From 38c8e52c1da080a2dbf3f6d3cea479dceecaae7d Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Sun, 23 Sep 2012 09:59:48 +0100 Subject: [PATCH 001/317] Beginnings of the "pass everything using a native type wrapper" console code. - ConsoleValue class is now the base value class. - ConsoleValueRef is now used to supply function parameters. Values are disposable. - Script functions return values instead of just strings where possible. - Variables can be disposable strings - Bytecode changed Fix the issues with console method parameters and fields which prevented missions from loading. --- Engine/source/T3D/gameBase/gameConnection.cpp | 8 +- Engine/source/app/net/net.cpp | 9 +- Engine/source/app/net/tcpObject.cpp | 2 +- Engine/source/app/net/tcpObject.h | 2 +- Engine/source/cinterface/c_scripting.cpp | 17 +- Engine/source/cinterface/cinterface.cpp | 18 +- .../dynamicConsoleMethodComponent.cpp | 8 +- .../component/dynamicConsoleMethodComponent.h | 4 +- Engine/source/component/simComponent.cpp | 2 +- Engine/source/component/simComponent.h | 2 +- Engine/source/console/ICallMethod.h | 2 +- Engine/source/console/SimXMLDocument.cpp | 2 +- Engine/source/console/SimXMLDocument.h | 2 +- Engine/source/console/astNodes.cpp | 73 ++- Engine/source/console/codeBlock.h | 6 +- Engine/source/console/compiledEval.cpp | 231 ++++++-- Engine/source/console/compiler.h | 9 +- Engine/source/console/console.cpp | 496 ++++++++++++++---- Engine/source/console/console.h | 231 ++++++-- Engine/source/console/consoleFunctions.cpp | 17 +- Engine/source/console/consoleInternal.cpp | 208 ++++++-- Engine/source/console/consoleInternal.h | 151 ++---- Engine/source/console/consoleLogger.cpp | 2 +- Engine/source/console/consoleLogger.h | 2 +- Engine/source/console/engineAPI.h | 133 ++--- Engine/source/console/engineDoc.cpp | 4 +- Engine/source/console/sim.cpp | 10 +- Engine/source/console/simEvents.cpp | 44 +- Engine/source/console/simEvents.h | 9 +- Engine/source/console/simObject.cpp | 2 +- Engine/source/console/simObject.h | 2 +- Engine/source/console/simObjectList.cpp | 2 +- Engine/source/console/simPersistSet.cpp | 2 +- Engine/source/console/simPersistSet.h | 2 +- Engine/source/console/simSet.cpp | 12 +- Engine/source/console/simSet.h | 4 +- Engine/source/console/stringStack.cpp | 176 ++++++- Engine/source/console/stringStack.h | 45 ++ Engine/source/core/fileObject.cpp | 4 +- .../editors/guiMeshRoadEditorCtrl.cpp | 2 +- .../editors/guiRiverEditorCtrl.cpp | 2 +- .../environment/editors/guiRoadEditorCtrl.cpp | 2 +- Engine/source/environment/waterObject.cpp | 2 +- Engine/source/environment/waterObject.h | 2 +- .../source/gui/controls/guiMaterialCtrl.cpp | 2 +- .../source/gui/controls/guiTreeViewCtrl.cpp | 2 +- Engine/source/gui/core/guiControl.cpp | 2 +- Engine/source/gui/core/guiControl.h | 2 +- Engine/source/gui/editor/guiFilterCtrl.cpp | 5 +- .../editor/inspector/variableInspector.cpp | 2 +- Engine/source/gui/worldEditor/creator.cpp | 2 +- .../gui/worldEditor/editorIconRegistry.cpp | 4 +- .../gui/worldEditor/guiDecalEditorCtrl.cpp | 4 +- .../source/gui/worldEditor/terrainEditor.cpp | 4 +- Engine/source/gui/worldEditor/worldEditor.cpp | 4 +- Engine/source/gui/worldEditor/worldEditor.h | 2 +- Engine/source/materials/materialManager.cpp | 4 +- Engine/source/sfx/sfxSource.cpp | 2 +- Engine/source/sfx/sfxSource.h | 2 +- Engine/source/sfx/sfxTrack.cpp | 2 +- Engine/source/sfx/sfxTrack.h | 2 +- Engine/source/sim/actionMap.cpp | 7 +- Engine/source/sim/netConnection.cpp | 3 + Engine/source/terrain/terrExport.cpp | 4 +- Engine/source/ts/collada/colladaImport.cpp | 2 +- Engine/source/ts/collada/colladaLights.cpp | 2 +- Engine/source/util/settings.cpp | 8 +- Engine/source/util/undo.cpp | 2 +- 68 files changed, 1511 insertions(+), 529 deletions(-) diff --git a/Engine/source/T3D/gameBase/gameConnection.cpp b/Engine/source/T3D/gameBase/gameConnection.cpp index afd1f9e25..2e1b68441 100644 --- a/Engine/source/T3D/gameBase/gameConnection.cpp +++ b/Engine/source/T3D/gameBase/gameConnection.cpp @@ -269,7 +269,8 @@ ConsoleMethod(GameConnection, setConnectArgs, void, 3, 17, "@see GameConnection::onConnect()\n\n") { - object->setConnectArgs(argc - 2, argv + 2); + StringStackWrapper args(argc - 2, argv + 2); + object->setConnectArgs(args.count(), args); } void GameConnection::onTimedOut() @@ -430,7 +431,7 @@ bool GameConnection::readConnectRequest(BitStream *stream, const char **errorStr *errorString = "CR_INVALID_ARGS"; return false; } - const char *connectArgv[MaxConnectArgs + 3]; + ConsoleValueRef connectArgv[MaxConnectArgs + 3]; for(U32 i = 0; i < mConnectArgc; i++) { char argString[256]; @@ -439,6 +440,7 @@ bool GameConnection::readConnectRequest(BitStream *stream, const char **errorStr connectArgv[i + 3] = mConnectArgv[i]; } connectArgv[0] = "onConnectRequest"; + connectArgv[1] = NULL; char buffer[256]; Net::addressToString(getNetAddress(), buffer); connectArgv[2] = buffer; @@ -944,7 +946,7 @@ bool GameConnection::readDemoStartBlock(BitStream *stream) void GameConnection::demoPlaybackComplete() { - static const char *demoPlaybackArgv[1] = { "demoPlaybackComplete" }; + static ConsoleValueRef demoPlaybackArgv[1] = { "demoPlaybackComplete" }; Sim::postCurrentEvent(Sim::getRootGroup(), new SimConsoleEvent(1, demoPlaybackArgv, false)); Parent::demoPlaybackComplete(); } diff --git a/Engine/source/app/net/net.cpp b/Engine/source/app/net/net.cpp index 7c5f47a0d..d24e698d4 100644 --- a/Engine/source/app/net/net.cpp +++ b/Engine/source/app/net/net.cpp @@ -237,7 +237,9 @@ ConsoleFunction( commandToServer, void, 2, RemoteCommandEvent::MaxRemoteCommandA NetConnection *conn = NetConnection::getConnectionToServer(); if(!conn) return; - sendRemoteCommand(conn, argc - 1, argv + 1); + + StringStackWrapper args(argc - 1, argv + 1); + sendRemoteCommand(conn, args.count(), args); } ConsoleFunction( commandToClient, void, 3, RemoteCommandEvent::MaxRemoteCommandArgs + 2, "(NetConnection client, string func, ...)" @@ -274,7 +276,8 @@ ConsoleFunction( commandToClient, void, 3, RemoteCommandEvent::MaxRemoteCommandA NetConnection *conn; if(!Sim::findObject(argv[1], conn)) return; - sendRemoteCommand(conn, argc - 2, argv + 2); + StringStackWrapper args(argc - 2, argv + 2); + sendRemoteCommand(conn, args.count(), args); } @@ -288,7 +291,7 @@ ConsoleFunction(removeTaggedString, void, 2, 2, "(int tag)" "@see getTaggedString()\n" "@ingroup Networking\n") { - gNetStringTable->removeString(dAtoi(argv[1]+1), true); + gNetStringTable->removeString(dAtoi(((const char*)argv[1])+1), true); } ConsoleFunction( addTaggedString, const char*, 2, 2, "(string str)" diff --git a/Engine/source/app/net/tcpObject.cpp b/Engine/source/app/net/tcpObject.cpp index 202934fb1..4f4396af7 100644 --- a/Engine/source/app/net/tcpObject.cpp +++ b/Engine/source/app/net/tcpObject.cpp @@ -226,7 +226,7 @@ TCPObject::~TCPObject() } } -bool TCPObject::processArguments(S32 argc, const char **argv) +bool TCPObject::processArguments(S32 argc, ConsoleValueRef *argv) { if(argc == 0) return true; diff --git a/Engine/source/app/net/tcpObject.h b/Engine/source/app/net/tcpObject.h index a438f35bc..9c4582eab 100644 --- a/Engine/source/app/net/tcpObject.h +++ b/Engine/source/app/net/tcpObject.h @@ -79,7 +79,7 @@ public: void disconnect(); State getState() { return mState; } - bool processArguments(S32 argc, const char **argv); + bool processArguments(S32 argc, ConsoleValueRef *argv); void send(const U8 *buffer, U32 bufferLen); void addToTable(NetSocket newTag); void removeFromTable(); diff --git a/Engine/source/cinterface/c_scripting.cpp b/Engine/source/cinterface/c_scripting.cpp index e6448089b..ebaa2e9b5 100644 --- a/Engine/source/cinterface/c_scripting.cpp +++ b/Engine/source/cinterface/c_scripting.cpp @@ -76,7 +76,7 @@ extern "C" { if (!entry) return ""; - const char* argv[] = {"consoleExportXML", 0}; + ConsoleValueRef argv[] = {"consoleExportXML"}; return entry->cb.mStringCallbackFunc(NULL, 1, argv); } @@ -215,7 +215,8 @@ extern "C" { return ""; } - return entry->cb.mStringCallbackFunc(o, argc, argv); + StringStackConsoleWrapper args(argc, argv); + return entry->cb.mStringCallbackFunc(o, args.count(), args); } bool script_call_namespace_entry_bool(Namespace::Entry* entry, S32 argc, const char** argv) @@ -233,7 +234,8 @@ extern "C" { return ""; } - return entry->cb.mBoolCallbackFunc(o, argc, argv); + StringStackConsoleWrapper args(argc, argv); + return entry->cb.mBoolCallbackFunc(o, args.count(), args); } S32 script_call_namespace_entry_int(Namespace::Entry* entry, S32 argc, const char** argv) @@ -251,7 +253,8 @@ extern "C" { return 0; } - return entry->cb.mIntCallbackFunc(o, argc, argv); + StringStackConsoleWrapper args(argc, argv); + return entry->cb.mIntCallbackFunc(o, args.count(), args); } F32 script_call_namespace_entry_float(Namespace::Entry* entry, S32 argc, const char** argv) @@ -269,7 +272,8 @@ extern "C" { return 0.0f; } - return entry->cb.mFloatCallbackFunc(o, argc, argv); + StringStackConsoleWrapper args(argc, argv); + return entry->cb.mFloatCallbackFunc(o, args.count(), args); } @@ -288,7 +292,8 @@ extern "C" { return; } - entry->cb.mVoidCallbackFunc(o, argc, argv); + StringStackConsoleWrapper args(argc, argv); + entry->cb.mVoidCallbackFunc(o, args.count(), args); } int script_simobject_get_id(SimObject* so) diff --git a/Engine/source/cinterface/cinterface.cpp b/Engine/source/cinterface/cinterface.cpp index f5b1c1412..54d795fd1 100644 --- a/Engine/source/cinterface/cinterface.cpp +++ b/Engine/source/cinterface/cinterface.cpp @@ -262,7 +262,8 @@ extern "C" { if (!entry) return; - entry->cb.mVoidCallbackFunc(NULL, argc, argv); + StringStackConsoleWrapper args(argc, argv); + entry->cb.mVoidCallbackFunc(NULL, args.count(), args); } F32 torque_callfloatfunction(const char* nameSpace, const char* name, S32 argc, const char ** argv) @@ -273,7 +274,8 @@ extern "C" { if (!entry) return 0.0f; - return entry->cb.mFloatCallbackFunc(NULL, argc, argv); + StringStackConsoleWrapper args(argc, argv); + return entry->cb.mFloatCallbackFunc(NULL, args.count(), args); } S32 torque_callintfunction(const char* nameSpace, const char* name, S32 argc, const char ** argv) @@ -284,7 +286,8 @@ extern "C" { if (!entry) return 0; - return entry->cb.mIntCallbackFunc(NULL, argc, argv); + StringStackConsoleWrapper args(argc, argv); + return entry->cb.mIntCallbackFunc(NULL, args.count(), args); } @@ -295,7 +298,8 @@ extern "C" { if (!entry) return ""; - return entry->cb.mStringCallbackFunc(NULL, argc, argv); + StringStackConsoleWrapper args(argc, argv); + return entry->cb.mStringCallbackFunc(NULL, args.count(), args); } bool torque_callboolfunction(const char* nameSpace, const char* name, S32 argc, const char ** argv) @@ -305,7 +309,8 @@ extern "C" { if (!entry) return ""; - return entry->cb.mBoolCallbackFunc(NULL, argc, argv); + StringStackConsoleWrapper args(argc, argv); + return entry->cb.mBoolCallbackFunc(NULL, args.count(), args); } @@ -319,7 +324,8 @@ extern "C" { if(!entry->mFunctionOffset) return ""; - const char* ret = entry->mCode->exec(entry->mFunctionOffset, StringTable->insert(name), entry->mNamespace, argc, argv, false, entry->mPackage); + StringStackConsoleWrapper args(argc, argv); + const char* ret = entry->mCode->exec(entry->mFunctionOffset, StringTable->insert(name), entry->mNamespace, args.count(), args, false, entry->mPackage); if (!ret || !dStrlen(ret)) return ""; diff --git a/Engine/source/component/dynamicConsoleMethodComponent.cpp b/Engine/source/component/dynamicConsoleMethodComponent.cpp index c76cae60a..c0a971405 100644 --- a/Engine/source/component/dynamicConsoleMethodComponent.cpp +++ b/Engine/source/component/dynamicConsoleMethodComponent.cpp @@ -90,7 +90,9 @@ const char *DynamicConsoleMethodComponent::callMethod( S32 argc, const char* met argv[1] = methodName; argv[2] = methodName; - return callMethodArgList( argc , argv ); + StringStackConsoleWrapper argsw(argc, argv); + + return callMethodArgList( argsw.count() , argsw ); } #ifdef TORQUE_DEBUG @@ -117,7 +119,7 @@ void DynamicConsoleMethodComponent::injectMethodCall( const char* method ) } #endif -const char* DynamicConsoleMethodComponent::callMethodArgList( U32 argc, const char *argv[], bool callThis /* = true */ ) +const char* DynamicConsoleMethodComponent::callMethodArgList( U32 argc, ConsoleValueRef argv[], bool callThis /* = true */ ) { #ifdef TORQUE_DEBUG injectMethodCall( argv[0] ); @@ -128,7 +130,7 @@ const char* DynamicConsoleMethodComponent::callMethodArgList( U32 argc, const ch // Call all components that implement methodName giving them a chance to operate // Components are called in reverse order of addition -const char *DynamicConsoleMethodComponent::_callMethod( U32 argc, const char *argv[], bool callThis /* = true */ ) +const char *DynamicConsoleMethodComponent::_callMethod( U32 argc, ConsoleValueRef argv[], bool callThis /* = true */ ) { // Set Owner SimObject *pThis = dynamic_cast( this ); diff --git a/Engine/source/component/dynamicConsoleMethodComponent.h b/Engine/source/component/dynamicConsoleMethodComponent.h index 479642f25..32e594cd6 100644 --- a/Engine/source/component/dynamicConsoleMethodComponent.h +++ b/Engine/source/component/dynamicConsoleMethodComponent.h @@ -62,7 +62,7 @@ protected: /// Internal callMethod : Actually does component notification and script method execution /// @attention This method does some magic to the argc argv to make Con::execute act properly /// as such it's internal and should not be exposed or used except by this class - virtual const char* _callMethod( U32 argc, const char *argv[], bool callThis = true ); + virtual const char* _callMethod( U32 argc, ConsoleValueRef argv[], bool callThis = true ); public: @@ -75,7 +75,7 @@ public: #endif /// Call Method - virtual const char* callMethodArgList( U32 argc, const char *argv[], bool callThis = true ); + virtual const char* callMethodArgList( U32 argc, ConsoleValueRef argv[], bool callThis = true ); /// Call Method format string const char* callMethod( S32 argc, const char* methodName, ... ); diff --git a/Engine/source/component/simComponent.cpp b/Engine/source/component/simComponent.cpp index f886a4a86..b1a1cf86d 100644 --- a/Engine/source/component/simComponent.cpp +++ b/Engine/source/component/simComponent.cpp @@ -171,7 +171,7 @@ void SimComponent::onRemove() ////////////////////////////////////////////////////////////////////////// -bool SimComponent::processArguments(S32 argc, const char **argv) +bool SimComponent::processArguments(S32 argc, ConsoleValueRef *argv) { for(S32 i = 0; i < argc; i++) { diff --git a/Engine/source/component/simComponent.h b/Engine/source/component/simComponent.h index 2e46451d3..70e41f69e 100644 --- a/Engine/source/component/simComponent.h +++ b/Engine/source/component/simComponent.h @@ -150,7 +150,7 @@ public: static void initPersistFields(); - virtual bool processArguments(S32 argc, const char **argv); + virtual bool processArguments(S32 argc, ConsoleValueRef *argv); bool isEnabled() const { return mEnabled; } diff --git a/Engine/source/console/ICallMethod.h b/Engine/source/console/ICallMethod.h index dc4904262..fdda77fa7 100644 --- a/Engine/source/console/ICallMethod.h +++ b/Engine/source/console/ICallMethod.h @@ -27,7 +27,7 @@ class ICallMethod { public: virtual const char* callMethod( S32 argc, const char* methodName, ... ) = 0; - virtual const char* callMethodArgList( U32 argc, const char *argv[], bool callThis = true ) = 0; + virtual const char* callMethodArgList( U32 argc, ConsoleValueRef argv[], bool callThis = true ) = 0; }; #endif \ No newline at end of file diff --git a/Engine/source/console/SimXMLDocument.cpp b/Engine/source/console/SimXMLDocument.cpp index 6076b9711..25920a4a0 100644 --- a/Engine/source/console/SimXMLDocument.cpp +++ b/Engine/source/console/SimXMLDocument.cpp @@ -158,7 +158,7 @@ SimXMLDocument::~SimXMLDocument() // ----------------------------------------------------------------------------- // Included for completeness. // ----------------------------------------------------------------------------- -bool SimXMLDocument::processArguments(S32 argc, const char** argv) +bool SimXMLDocument::processArguments(S32 argc, ConsoleValueRef *argv) { return argc == 0; } diff --git a/Engine/source/console/SimXMLDocument.h b/Engine/source/console/SimXMLDocument.h index 7154e68e3..3f6e2661c 100644 --- a/Engine/source/console/SimXMLDocument.h +++ b/Engine/source/console/SimXMLDocument.h @@ -57,7 +57,7 @@ class SimXMLDocument: public SimObject // tie in to the script language. The .cc file has more in depth // comments on these. //----------------------------------------------------------------------- - bool processArguments(S32 argc, const char** argv); + bool processArguments(S32 argc, ConsoleValueRef *argv); bool onAdd(); void onRemove(); static void initPersistFields(); diff --git a/Engine/source/console/astNodes.cpp b/Engine/source/console/astNodes.cpp index d055801a0..31b337c35 100644 --- a/Engine/source/console/astNodes.cpp +++ b/Engine/source/console/astNodes.cpp @@ -236,7 +236,14 @@ U32 ExprNode::compileStmt(U32 *codeStream, U32 ip, U32, U32) U32 ReturnStmtNode::precompileStmt(U32) { addBreakCount(); - return 1 + (expr ? expr->precompile(TypeReqString) : 0); + U32 size = 0; + if (expr) { + TypeReq walkType = expr->getPreferredType(); + if (walkType == TypeReqNone) walkType = TypeReqString; + size = expr->precompile(walkType); + } + + return 1 + size; } U32 ReturnStmtNode::compileStmt(U32 *codeStream, U32 ip, U32, U32) @@ -246,8 +253,22 @@ U32 ReturnStmtNode::compileStmt(U32 *codeStream, U32 ip, U32, U32) codeStream[ip++] = OP_RETURN_VOID; else { - ip = expr->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_RETURN; + TypeReq walkType = expr->getPreferredType(); + if (walkType == TypeReqNone) walkType = TypeReqString; + ip = expr->compile(codeStream, ip, walkType); + + // Return the correct type + switch (walkType) { + case TypeReqUInt: + codeStream[ip++] = OP_RETURN_UINT; + break; + case TypeReqFloat: + codeStream[ip++] = OP_RETURN_FLT; + break; + default: + codeStream[ip++] = OP_RETURN; + break; + } } return ip; } @@ -1347,8 +1368,11 @@ U32 FuncCallExprNode::precompile(TypeReq type) size++; precompileIdent(funcName); precompileIdent(nameSpace); - for(ExprNode *walk = args; walk; walk = (ExprNode *) walk->getNext()) - size += walk->precompile(TypeReqString) + 1; + for(ExprNode *walk = args; walk; walk = (ExprNode *) walk->getNext()) { + TypeReq walkType = walk->getPreferredType(); + if (walkType == TypeReqNone) walkType = TypeReqString; + size += walk->precompile(walkType) + 1; + } return size + 5; } @@ -1357,8 +1381,20 @@ U32 FuncCallExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) codeStream[ip++] = OP_PUSH_FRAME; for(ExprNode *walk = args; walk; walk = (ExprNode *) walk->getNext()) { - ip = walk->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_PUSH; + TypeReq walkType = walk->getPreferredType(); + if (walkType == TypeReqNone) walkType = TypeReqString; + ip = walk->compile(codeStream, ip, walkType); + switch (walk->getPreferredType()) { + case TypeReqFloat: + codeStream[ip++] = OP_PUSH_FLT; + break; + case TypeReqUInt: + codeStream[ip++] = OP_PUSH_UINT; + break; + default: + codeStream[ip++] = OP_PUSH; + break; + } } if(callType == MethodCall || callType == ParentCall) codeStream[ip++] = OP_CALLFUNC; @@ -1729,8 +1765,11 @@ U32 ObjectDeclNode::precompileSubObject(bool) U32 argSize = 0; precompileIdent(parentObject); - for(ExprNode *exprWalk = argList; exprWalk; exprWalk = (ExprNode *) exprWalk->getNext()) - argSize += exprWalk->precompile(TypeReqString) + 1; + for(ExprNode *exprWalk = argList; exprWalk; exprWalk = (ExprNode *) exprWalk->getNext()) { + TypeReq walkType = exprWalk->getPreferredType(); + if (walkType == TypeReqNone) walkType = TypeReqString; + argSize += exprWalk->precompile(walkType) + 1; + } argSize += classNameExpr->precompile(TypeReqString) + 1; U32 nameSize = objectNameExpr->precompile(TypeReqString) + 1; @@ -1775,8 +1814,20 @@ U32 ObjectDeclNode::compileSubObject(U32 *codeStream, U32 ip, bool root) codeStream[ip++] = OP_PUSH; for(ExprNode *exprWalk = argList; exprWalk; exprWalk = (ExprNode *) exprWalk->getNext()) { - ip = exprWalk->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_PUSH; + TypeReq walkType = exprWalk->getPreferredType(); + if (walkType == TypeReqNone) walkType = TypeReqString; + ip = exprWalk->compile(codeStream, ip, walkType); + switch (exprWalk->getPreferredType()) { + case TypeReqFloat: + codeStream[ip++] = OP_PUSH_FLT; + break; + case TypeReqUInt: + codeStream[ip++] = OP_PUSH_UINT; + break; + default: + codeStream[ip++] = OP_PUSH; + break; + } } codeStream[ip++] = OP_CREATE_OBJECT; codeStream[ip] = STEtoU32(parentObject, ip); diff --git a/Engine/source/console/codeBlock.h b/Engine/source/console/codeBlock.h index 98e581e46..2122210b4 100644 --- a/Engine/source/console/codeBlock.h +++ b/Engine/source/console/codeBlock.h @@ -27,6 +27,8 @@ #include "console/consoleParser.h" class Stream; +class ConsoleValue; +class ConsoleValueRef; /// Core TorqueScript code management class. /// @@ -146,8 +148,8 @@ public: /// -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, + ConsoleValueRef exec(U32 offset, const char *fnName, Namespace *ns, U32 argc, + ConsoleValueRef *argv, bool noCalls, StringTableEntry packageName, S32 setFrame = -1); }; diff --git a/Engine/source/console/compiledEval.cpp b/Engine/source/console/compiledEval.cpp index e2cfcf280..ca9e730fb 100644 --- a/Engine/source/console/compiledEval.cpp +++ b/Engine/source/console/compiledEval.cpp @@ -102,7 +102,11 @@ IterStackRecord iterStack[ MaxStackSize ]; F64 floatStack[MaxStackSize]; S64 intStack[MaxStackSize]; + + + StringStack STR; +ConsoleValueStack CSTK; U32 _FLT = 0; ///< Stack pointer for floatStack. U32 _UINT = 0; ///< Stack pointer for intStack. @@ -401,14 +405,15 @@ static void setFieldComponent( SimObject* object, StringTableEntry field, const } } -const char *CodeBlock::exec(U32 ip, const char *functionName, Namespace *thisNamespace, U32 argc, const char **argv, bool noCalls, StringTableEntry packageName, S32 setFrame) +ConsoleValueRef CodeBlock::exec(U32 ip, const char *functionName, Namespace *thisNamespace, U32 argc, ConsoleValueRef *argv, bool noCalls, StringTableEntry packageName, S32 setFrame) { #ifdef TORQUE_DEBUG U32 stackStart = STR.mStartStackSize; + U32 consoleStackStart = CSTK.mStackPos; #endif static char traceBuffer[1024]; - U32 i; + S32 i; U32 iterDepth = 0; @@ -424,7 +429,7 @@ const char *CodeBlock::exec(U32 ip, const char *functionName, Namespace *thisNam // assume this points into a function decl: U32 fnArgc = code[ip + 5]; thisFunctionName = U32toSTE(code[ip]); - argc = getMin(argc-1, fnArgc); // argv[0] is func name + S32 wantedArgc = getMin(argc-1, fnArgc); // argv[0] is func name if(gEvalState.traceOn) { traceBuffer[0] = 0; @@ -456,11 +461,22 @@ const char *CodeBlock::exec(U32 ip, const char *functionName, Namespace *thisNam } gEvalState.pushFrame(thisFunctionName, thisNamespace); popFrame = true; - for(i = 0; i < argc; i++) + + for(i = 0; i < wantedArgc; i++) { StringTableEntry var = U32toSTE(code[ip + i + 6]); gEvalState.setCurVarNameCreate(var); - gEvalState.setStringVariable(argv[i+1]); + + ConsoleValueRef ref = argv[i+1]; + + if (argv[i+1].isString()) + gEvalState.setStringVariable(argv[i+1]); + else if (argv[i+1].isInt()) + gEvalState.setIntVariable(argv[i+1]); + else if (argv[i+1].isFloat()) + gEvalState.setFloatVariable(argv[i+1]); + else + gEvalState.setStringVariable(argv[i+1]); } ip = ip + fnArgc + 6; curFloatTable = functionFloats; @@ -497,6 +513,11 @@ const char *CodeBlock::exec(U32 ip, const char *functionName, Namespace *thisNam } } + // jamesu - reset the console stack frame which at this point will contain + // either nothing or argv[] which we just copied + // NOTE: it might be better to do this when we are finished? + CSTK.resetFrame(); + // Grab the state of the telenet debugger here once // so that the push and pop frames are always balanced. const bool telDebuggerOn = TelDebugger && TelDebugger->isConnected(); @@ -530,7 +551,7 @@ const char *CodeBlock::exec(U32 ip, const char *functionName, Namespace *thisNam char nsDocBlockClass[nsDocLength]; U32 callArgc; - const char **callArgv; + ConsoleValueRef *callArgv; static char curFieldArray[256]; static char prevFieldArray[256]; @@ -543,6 +564,10 @@ const char *CodeBlock::exec(U32 ip, const char *functionName, Namespace *thisNam Con::gCurrentRoot = this->modPath; } const char * val; + const char *retValue; + + // note: anything returned is pushed to CSTK and will be invalidated on the next exec() + ConsoleValueRef returnValue; // The frame temp is used by the variable accessor ops (OP_SAVEFIELD_* and // OP_LOADFIELD_*) to store temporary values for the fields. @@ -615,8 +640,8 @@ breakContinue: objectCreationStack[ objectCreationStackIndex++ ].failJump = failJump; // Get the constructor information off the stack. - STR.getArgcArgv(NULL, &callArgc, &callArgv); - const char* objectName = callArgv[ 2 ]; + CSTK.getArgcArgv(NULL, &callArgc, &callArgv); + const char *objectName = callArgv[ 2 ]; // Con::printf("Creating object..."); @@ -638,6 +663,7 @@ breakContinue: Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare data block %s with a different class.", getFileLine(ip), objectName); ip = failJump; STR.popFrame(); + CSTK.popFrame(); break; } @@ -650,18 +676,19 @@ breakContinue: // IF we aren't looking at a local/internal object, then check if // this object already exists in the global space - SimObject *obj = Sim::findObject( objectName ); + SimObject *obj = Sim::findObject( (const char*)objectName ); if (obj /*&& !obj->isLocalName()*/) { if ( isSingleton ) { // Make sure we're not trying to change types - if ( dStricmp( obj->getClassName(), callArgv[1] ) != 0 ) + if ( dStricmp( obj->getClassName(), (const char*)callArgv[1] ) != 0 ) { Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare object [%s] with a different class [%s] - was [%s].", - getFileLine(ip), objectName, callArgv[1], obj->getClassName()); + getFileLine(ip), objectName, (const char*)callArgv[1], obj->getClassName()); ip = failJump; STR.popFrame(); + CSTK.popFrame(); break; } @@ -679,13 +706,29 @@ breakContinue: // string stack and may get stomped if deleteObject triggers // script execution. - const char* savedArgv[ StringStack::MaxArgs ]; - dMemcpy( savedArgv, callArgv, sizeof( savedArgv[ 0 ] ) * callArgc ); + ConsoleValueRef savedArgv[ StringStack::MaxArgs ]; + for (int i=0; ideleteObject(); obj = NULL; - dMemcpy( callArgv, savedArgv, sizeof( callArgv[ 0 ] ) * callArgc ); + // Prevent stack value corruption + CSTK.popFrame(); + STR.popFrame(); + // -- + + //dMemcpy( callArgv, savedArgv, sizeof( callArgv[ 0 ] ) * callArgc ); + for (int i=0; isetOriginalName( objectName ); } + // Prevent stack value corruption + CSTK.pushFrame(); + STR.pushFrame(); + // -- + // Do the constructor parameters. if(!currentNewObject->processArguments(callArgc-3, callArgv+3)) { delete currentNewObject; currentNewObject = NULL; ip = failJump; + + // Prevent stack value corruption + CSTK.popFrame(); + STR.popFrame(); + // -- break; } + // Prevent stack value corruption + CSTK.popFrame(); + STR.popFrame(); + // -- + // If it's not a datablock, allow people to modify bits of it. if(!isDataBlock) { @@ -854,6 +915,11 @@ breakContinue: // Con::printf("Adding object %s", currentNewObject->getName()); + // Prevent stack value corruption + CSTK.pushFrame(); + STR.pushFrame(); + // -- + // Make sure it wasn't already added, then add it. if(currentNewObject->isProperlyAdded() == false) { @@ -877,6 +943,10 @@ breakContinue: Con::warnf(ConsoleLogEntry::General, "%s: Register object failed for object %s of class %s.", getFileLine(ip), currentNewObject->getName(), currentNewObject->getClassName()); delete currentNewObject; ip = failJump; + // Prevent stack value corruption + CSTK.popFrame(); + STR.popFrame(); + // -- break; } } @@ -885,6 +955,8 @@ breakContinue: SimDataBlock *dataBlock = dynamic_cast(currentNewObject); static String errorStr; + + // If so, preload it. if(dataBlock && !dataBlock->preload(true, errorStr)) { @@ -892,6 +964,11 @@ breakContinue: currentNewObject->getName(), errorStr.c_str()); dataBlock->deleteObject(); ip = failJump; + + // Prevent stack value corruption + CSTK.popFrame(); + STR.popFrame(); + // -- break; } @@ -946,6 +1023,10 @@ breakContinue: else intStack[++_UINT] = currentNewObject->getId(); + // Prevent stack value corruption + CSTK.popFrame(); + STR.popFrame(); + // -- break; } @@ -1028,6 +1109,29 @@ breakContinue: // We're falling thru here on purpose. case OP_RETURN: + retValue = STR.getStringValue(); + + if( iterDepth > 0 ) + { + // Clear iterator state. + while( iterDepth > 0 ) + { + iterStack[ -- _ITER ].mIsStringIter = false; + -- iterDepth; + } + + STR.rewind(); + STR.setStringValue( retValue ); // Not nice but works. + retValue = STR.getStringValue(); + } + + // Previously the return value was on the stack and would be returned using STR.getStringValue(). + // Now though we need to wrap it in a ConsoleValueRef + returnValue.value = CSTK.pushStackString(retValue); + + goto execFinished; + + case OP_RETURN_FLT: if( iterDepth > 0 ) { @@ -1038,10 +1142,27 @@ breakContinue: -- iterDepth; } - const char* returnValue = STR.getStringValue(); - STR.rewind(); - STR.setStringValue( returnValue ); // Not nice but works. } + + returnValue.value = CSTK.pushFLT(floatStack[_FLT]); + _FLT--; + + goto execFinished; + + case OP_RETURN_UINT: + + if( iterDepth > 0 ) + { + // Clear iterator state. + while( iterDepth > 0 ) + { + iterStack[ -- _ITER ].mIsStringIter = false; + -- iterDepth; + } + } + + returnValue.value = CSTK.pushUINT(intStack[_UINT]); + _UINT--; goto execFinished; @@ -1514,6 +1635,7 @@ breakContinue: getFileLine(ip-4), fnNamespace ? fnNamespace : "", fnNamespace ? "::" : "", fnName); STR.popFrame(); + CSTK.popFrame(); break; } // Now fall through to OP_CALLFUNC... @@ -1538,7 +1660,7 @@ breakContinue: U32 callType = code[ip+2]; ip += 3; - STR.getArgcArgv(fnName, &callArgc, &callArgv); + CSTK.getArgcArgv(fnName, &callArgc, &callArgv); const char *componentReturnValue = ""; @@ -1555,14 +1677,15 @@ breakContinue: else if(callType == FuncCallExprNode::MethodCall) { saveObject = gEvalState.thisObject; - gEvalState.thisObject = Sim::findObject(callArgv[1]); + gEvalState.thisObject = Sim::findObject((const char*)callArgv[1]); if(!gEvalState.thisObject) { // Go back to the previous saved object. gEvalState.thisObject = saveObject; - Con::warnf(ConsoleLogEntry::General,"%s: Unable to find object: '%s' attempting to call function '%s'", getFileLine(ip-4), callArgv[1], fnName); + Con::warnf(ConsoleLogEntry::General,"%s: Unable to find object: '%s' attempting to call function '%s'", getFileLine(ip-4), (const char*)callArgv[1], fnName); STR.popFrame(); + CSTK.popFrame(); break; } @@ -1618,6 +1741,7 @@ breakContinue: } } STR.popFrame(); + CSTK.popFrame(); if( routingId == MethodOnComponent ) STR.setStringValue( componentReturnValue ); @@ -1628,12 +1752,30 @@ breakContinue: } if(nsEntry->mType == Namespace::Entry::ConsoleFunctionType) { - const char *ret = ""; + ConsoleValueRef ret; if(nsEntry->mFunctionOffset) ret = nsEntry->mCode->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage); - - STR.popFrame(); - STR.setStringValue(ret); + + STR.popFrame(); + // Functions are assumed to return strings, so look ahead to see if we can skip the conversion + if(code[ip] == OP_STR_TO_UINT) + { + ip++; + intStack[++_UINT] = (U32)((S32)ret); + } + else if(code[ip] == OP_STR_TO_FLT) + { + ip++; + floatStack[++_FLT] = (F32)ret; + } + else if(code[ip] == OP_STR_TO_NONE) + ip++; + else + STR.setStringValue((const char*)ret); + + // This will clear everything including returnValue + CSTK.popFrame(); + STR.clearFunctionOffset(); } else { @@ -1654,6 +1796,7 @@ breakContinue: callArgc, nsEntry->mMinArgs, nsEntry->mMaxArgs); Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", getFileLine(ip-4), nsEntry->mUsage); STR.popFrame(); + CSTK.popFrame(); } else { @@ -1663,16 +1806,18 @@ breakContinue: { const char *ret = nsEntry->cb.mStringCallbackFunc(gEvalState.thisObject, callArgc, callArgv); STR.popFrame(); + CSTK.popFrame(); if(ret != STR.getStringValue()) STR.setStringValue(ret); - else - STR.setLen(dStrlen(ret)); + //else + // STR.setLen(dStrlen(ret)); break; } case Namespace::Entry::IntCallbackType: { S32 result = nsEntry->cb.mIntCallbackFunc(gEvalState.thisObject, callArgc, callArgv); STR.popFrame(); + CSTK.popFrame(); if(code[ip] == OP_STR_TO_UINT) { ip++; @@ -1695,6 +1840,7 @@ breakContinue: { F64 result = nsEntry->cb.mFloatCallbackFunc(gEvalState.thisObject, callArgc, callArgv); STR.popFrame(); + CSTK.popFrame(); if(code[ip] == OP_STR_TO_UINT) { ip++; @@ -1719,12 +1865,14 @@ breakContinue: Con::warnf(ConsoleLogEntry::General, "%s: Call to %s in %s uses result of void function call.", getFileLine(ip-4), fnName, functionName); STR.popFrame(); + CSTK.popFrame(); STR.setStringValue(""); break; case Namespace::Entry::BoolCallbackType: { bool result = nsEntry->cb.mBoolCallbackFunc(gEvalState.thisObject, callArgc, callArgv); STR.popFrame(); + CSTK.popFrame(); if(code[ip] == OP_STR_TO_UINT) { ip++; @@ -1778,11 +1926,24 @@ breakContinue: intStack[++_UINT] = STR.compare(); break; case OP_PUSH: - STR.push(); + STR.push(); + //Con::printf("Pushing str: %s",STR.getPreviousStringValue()); + CSTK.pushString(STR.getPreviousStringValue()); + break; + case OP_PUSH_UINT: + //Con::printf("Pushing int: %i",(S32)intStack[_UINT]); + CSTK.pushUINT(intStack[_UINT]); + _UINT--; + break; + case OP_PUSH_FLT: + //Con::printf("Pushing float: %f",(F32)intStack[_UINT]); + CSTK.pushFLT(floatStack[_FLT]); + _FLT--; break; case OP_PUSH_FRAME: STR.pushFrame(); + CSTK.pushFrame(); break; case OP_ASSERT: @@ -1961,8 +2122,9 @@ execFinished: if ( telDebuggerOn && setFrame < 0 ) TelDebugger->popStackFrame(); - if ( popFrame ) + if ( popFrame ) { gEvalState.popFrame(); + } if(argv) { @@ -2012,7 +2174,8 @@ execFinished: AssertFatal(!(STR.mStartStackSize > stackStart), "String stack not popped enough in script exec"); AssertFatal(!(STR.mStartStackSize < stackStart), "String stack popped too much in script exec"); #endif - return STR.getStringValue(); + + return returnValue; } //------------------------------------------------------------ diff --git a/Engine/source/console/compiler.h b/Engine/source/console/compiler.h index 66e3966b9..5354ee029 100644 --- a/Engine/source/console/compiler.h +++ b/Engine/source/console/compiler.h @@ -54,6 +54,9 @@ namespace Compiler OP_RETURN, // fixes a bug when not explicitly returning a value OP_RETURN_VOID, + OP_RETURN_FLT, + OP_RETURN_UINT, + OP_CMPEQ, OP_CMPGR, OP_CMPGE, @@ -136,8 +139,10 @@ namespace Compiler OP_TERMINATE_REWIND_STR, OP_COMPARE_STR, - OP_PUSH, - OP_PUSH_FRAME, + OP_PUSH, // String + OP_PUSH_UINT, // Integer + OP_PUSH_FLT, // Float + OP_PUSH_FRAME, // Frame OP_ASSERT, OP_BREAK, diff --git a/Engine/source/console/console.cpp b/Engine/source/console/console.cpp index 0e7099ff2..efe658db6 100644 --- a/Engine/source/console/console.cpp +++ b/Engine/source/console/console.cpp @@ -42,6 +42,7 @@ extern StringStack STR; +extern ConsoleValueStack CSTK; ConsoleDocFragment* ConsoleDocFragment::smFirst; ExprEvalState gEvalState; @@ -709,10 +710,11 @@ void errorf(const char* fmt,...) //--------------------------------------------------------------------------- -void setVariable(const char *name, const char *value) +bool getVariableObjectField(const char *name, SimObject **object, const char **field) { // get the field info from the object.. - if(name[0] != '$' && dStrchr(name, '.') && !isFunction(name)) + const char *dot = dStrchr(name, '.'); + if(name[0] != '$' && dot) { S32 len = dStrlen(name); AssertFatal(len < sizeof(scratchBuffer)-1, "Sim::getVariable - name too long"); @@ -721,17 +723,17 @@ void setVariable(const char *name, const char *value) char * token = dStrtok(scratchBuffer, "."); SimObject * obj = Sim::findObject(token); if(!obj) - return; + return false; token = dStrtok(0, ".\0"); if(!token) - return; + return false; while(token != NULL) { const char * val = obj->getDataField(StringTable->insert(token), 0); if(!val) - return; + return false; char *fieldToken = token; token = dStrtok(0, ".\0"); @@ -739,17 +741,71 @@ void setVariable(const char *name, const char *value) { obj = Sim::findObject(token); if(!obj) - return; + return false; } else { - obj->setDataField(StringTable->insert(fieldToken), 0, value); + *object = obj; + *field = fieldToken; + return true; } } } + return false; +} + +Dictionary::Entry *getLocalVariableEntry(const char *name) +{ + name = prependPercent(name); + return gEvalState.getCurrentFrame().lookup(StringTable->insert(name)); +} + +Dictionary::Entry *getVariableEntry(const char *name) +{ name = prependDollar(name); - gEvalState.globalVars.setVariable(StringTable->insert(name), value); + return gEvalState.globalVars.lookup(StringTable->insert(name)); +} + +Dictionary::Entry *addVariableEntry(const char *name) +{ + name = prependDollar(name); + return gEvalState.globalVars.add(StringTable->insert(name)); +} + +Dictionary::Entry *getAddVariableEntry(const char *name) +{ + name = prependDollar(name); + StringTableEntry stName = StringTable->insert(name); + Dictionary::Entry *entry = gEvalState.globalVars.lookup(stName); + if (!entry) { + entry = gEvalState.globalVars.add(stName); + } + return entry; +} + +Dictionary::Entry *getAddLocalVariableEntry(const char *name) +{ + name = prependPercent(name); + StringTableEntry stName = StringTable->insert(name); + Dictionary::Entry *entry = gEvalState.getCurrentFrame().lookup(stName); + if (!entry) { + entry = gEvalState.getCurrentFrame().add(stName); + } + return entry; +} + +void setVariable(const char *name, const char *value) +{ + SimObject *obj = NULL; + const char *objField = NULL; + + if (getVariableObjectField(name, &obj, &objField)) { + obj->setDataField(StringTable->insert(objField), 0, value); + } else { + name = prependDollar(name); + gEvalState.globalVars.setVariable(StringTable->insert(name), value); + } } void setLocalVariable(const char *name, const char *value) @@ -760,21 +816,48 @@ void setLocalVariable(const char *name, const char *value) void setBoolVariable(const char *varName, bool value) { - setVariable(varName, value ? "1" : "0"); + SimObject *obj = NULL; + const char *objField = NULL; + + if (getVariableObjectField(varName, &obj, &objField)) { + obj->setDataField(StringTable->insert(objField), 0, value ? "1" : "0"); + } else { + varName = prependDollar(varName); + Dictionary::Entry *entry = getAddVariableEntry(varName); + entry->setStringValue(value ? "1" : "0"); + } } void setIntVariable(const char *varName, S32 value) { - char scratchBuffer[32]; - dSprintf(scratchBuffer, sizeof(scratchBuffer), "%d", value); - setVariable(varName, scratchBuffer); + SimObject *obj = NULL; + const char *objField = NULL; + + if (getVariableObjectField(varName, &obj, &objField)) { + char scratchBuffer[32]; + dSprintf(scratchBuffer, sizeof(scratchBuffer), "%d", value); + obj->setDataField(StringTable->insert(objField), 0, scratchBuffer); + } else { + varName = prependDollar(varName); + Dictionary::Entry *entry = getAddVariableEntry(varName); + entry->setIntValue(value); + } } void setFloatVariable(const char *varName, F32 value) { - char scratchBuffer[32]; - dSprintf(scratchBuffer, sizeof(scratchBuffer), "%g", value); - setVariable(varName, scratchBuffer); + SimObject *obj = NULL; + const char *objField = NULL; + + if (getVariableObjectField(varName, &obj, &objField)) { + char scratchBuffer[32]; + dSprintf(scratchBuffer, sizeof(scratchBuffer), "%g", value); + obj->setDataField(StringTable->insert(objField), 0, scratchBuffer); + } else { + varName = prependDollar(varName); + Dictionary::Entry *entry = getAddVariableEntry(varName); + entry->setFloatValue(value); + } } //--------------------------------------------------------------------------- @@ -825,13 +908,14 @@ void stripColorChars(char* line) } } -const char *getVariable(const char *name) +// +const char *getObjectTokenField(const char *name) { - // get the field info from the object.. - if(name[0] != '$' && dStrchr(name, '.') && !isFunction(name)) + const char *dot = dStrchr(name, '.'); + if(name[0] != '$' && dot) { S32 len = dStrlen(name); - AssertFatal(len < sizeof(scratchBuffer)-1, "Sim::getVariable - name too long"); + AssertFatal(len < sizeof(scratchBuffer)-1, "Sim::getVariable - object name too long"); dMemcpy(scratchBuffer, name, len+1); char * token = dStrtok(scratchBuffer, "."); @@ -861,8 +945,18 @@ const char *getVariable(const char *name) } } - name = prependDollar(name); - return gEvalState.globalVars.getVariable(StringTable->insert(name)); + return NULL; +} + +const char *getVariable(const char *name) +{ + const char *objField = getObjectTokenField(name); + if (objField) { + return objField; + } else { + Dictionary::Entry *entry = getVariableEntry(name); + return entry ? entry->getStringValue() : ""; + } } const char *getLocalVariable(const char *name) @@ -874,20 +968,36 @@ const char *getLocalVariable(const char *name) bool getBoolVariable(const char *varName, bool def) { - const char *value = getVariable(varName); - return *value ? dAtob(value) : def; + const char *objField = getObjectTokenField(varName); + if (objField) { + return *objField ? dAtob(objField) : def; + } else { + Dictionary::Entry *entry = getVariableEntry(varName); + objField = entry ? entry->getStringValue() : ""; + return *objField ? dAtob(objField) : def; + } } S32 getIntVariable(const char *varName, S32 def) { - const char *value = getVariable(varName); - return *value ? dAtoi(value) : def; + const char *objField = getObjectTokenField(varName); + if (objField) { + return *objField ? dAtoi(objField) : def; + } else { + Dictionary::Entry *entry = getVariableEntry(varName); + return entry ? entry->getIntValue() : def; + } } F32 getFloatVariable(const char *varName, F32 def) { - const char *value = getVariable(varName); - return *value ? dAtof(value) : def; + const char *objField = getObjectTokenField(varName); + if (objField) { + return *objField ? dAtof(objField) : def; + } else { + Dictionary::Entry *entry = getVariableEntry(varName); + return entry ? entry->getFloatValue() : def; + } } //--------------------------------------------------------------------------- @@ -1032,7 +1142,7 @@ const char *evaluatef(const char* string, ...) return newCodeBlock->compileExec(NULL, buffer, false, 0); } -const char *execute(S32 argc, const char *argv[]) +const char *execute(S32 argc, ConsoleValueRef argv[]) { #ifdef TORQUE_MULTITHREAD if(isMainThread()) @@ -1048,6 +1158,7 @@ const char *execute(S32 argc, const char *argv[]) // Clean up arg buffers, if any. STR.clearFunctionOffset(); + CSTK.resetFrame(); return ""; } return ent->execute(argc, argv, &gEvalState); @@ -1064,10 +1175,16 @@ const char *execute(S32 argc, const char *argv[]) #endif } -//------------------------------------------------------------------------------ -const char *execute(SimObject *object, S32 argc, const char *argv[], bool thisCallOnly) +const char *execute(S32 argc, const char *argv[]) { - static char idBuf[16]; + StringStackConsoleWrapper args(argc, argv); + return execute(args.count(), args); +} + +//------------------------------------------------------------------------------ +const char *execute(SimObject *object, S32 argc, ConsoleValueRef argv[], bool thisCallOnly) +{ + //static char idBuf[16]; if(argc < 2) return ""; @@ -1077,14 +1194,21 @@ const char *execute(SimObject *object, S32 argc, const char *argv[], bool thisCa if( !thisCallOnly ) { ICallMethod *com = dynamic_cast(object); - if(com) + if(com) { + STR.pushFrame(); + CSTK.pushFrame(); com->callMethodArgList(argc, argv, false); + STR.popFrame(); + CSTK.popFrame(); + } } if(object->getNamespace()) { - dSprintf(idBuf, sizeof(idBuf), "%d", object->getId()); - argv[1] = idBuf; + ConsoleValueRef internalArgv[StringStack::MaxArgs]; + + U32 ident = object->getId(); + ConsoleValueRef oldIdent = argv[1]; StringTableEntry funcName = StringTable->insert(argv[0]); Namespace::Entry *ent = object->getNamespace()->lookup(funcName); @@ -1094,14 +1218,14 @@ const char *execute(SimObject *object, S32 argc, const char *argv[], bool thisCa //warnf(ConsoleLogEntry::Script, "%s: undefined for object '%s' - id %d", funcName, object->getName(), object->getId()); // Clean up arg buffers, if any. + //CSTK. STR.clearFunctionOffset(); + CSTK.resetFrame(); return ""; } // Twiddle %this argument - const char *oldArg1 = argv[1]; - dSprintf(idBuf, sizeof(idBuf), "%d", object->getId()); - argv[1] = idBuf; + argv[1] = (S32)ident; SimObject *save = gEvalState.thisObject; gEvalState.thisObject = object; @@ -1109,7 +1233,7 @@ const char *execute(SimObject *object, S32 argc, const char *argv[], bool thisCa gEvalState.thisObject = save; // Twiddle it back - argv[1] = oldArg1; + argv[1] = oldIdent; return ret; } @@ -1117,82 +1241,54 @@ const char *execute(SimObject *object, S32 argc, const char *argv[], bool thisCa return ""; } -#define B( a ) const char* a = NULL -#define A const char* -inline const char*_executef(SimObject *obj, S32 checkArgc, S32 argc, - A a, B(b), B(c), B(d), B(e), B(f), B(g), B(h), B(i), B(j), B(k)) +const char *execute(SimObject *object, S32 argc, const char *argv[], bool thisCallOnly) +{ + StringStackConsoleWrapper args(argc, argv); + return execute(object, args.count(), args, thisCallOnly); +} + +inline const char*_executef(SimObject *obj, S32 checkArgc, S32 argc, ConsoleValueRef *argv) { -#undef A -#undef B const U32 maxArg = 12; AssertWarn(checkArgc == argc, "Incorrect arg count passed to Con::executef(SimObject*)"); AssertFatal(argc <= maxArg - 1, "Too many args passed to Con::_executef(SimObject*). Please update the function to handle more."); - const char* argv[maxArg]; - argv[0] = a; - argv[1] = a; - argv[2] = b; - argv[3] = c; - argv[4] = d; - argv[5] = e; - argv[6] = f; - argv[7] = g; - argv[8] = h; - argv[9] = i; - argv[10] = j; - argv[11] = k; - return execute(obj, argc+1, argv); + return execute(obj, argc, argv); // jamesu - argc should == argc } -#define A const char* +#define A ConsoleValueRef #define OBJ SimObject* obj -const char *executef(OBJ, A a) { return _executef(obj, 1, 1, a); } -const char *executef(OBJ, A a, A b) { return _executef(obj, 2, 2, a, b); } -const char *executef(OBJ, A a, A b, A c) { return _executef(obj, 3, 3, a, b, c); } -const char *executef(OBJ, A a, A b, A c, A d) { return _executef(obj, 4, 4, a, b, c, d); } -const char *executef(OBJ, A a, A b, A c, A d, A e) { return _executef(obj, 5, 5, a, b, c, d, e); } -const char *executef(OBJ, A a, A b, A c, A d, A e, A f) { return _executef(obj, 6, 6, a, b, c, d, e, f); } -const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g) { return _executef(obj, 7, 7, a, b, c, d, e, f, g); } -const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h) { return _executef(obj, 8, 8, a, b, c, d, e, f, g, h); } -const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h, A i) { return _executef(obj, 9, 9, a, b, c, d, e, f, g, h, i); } -const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h, A i, A j) { return _executef(obj,10,10, a, b, c, d, e, f, g, h, i, j); } -const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h, A i, A j, A k) { return _executef(obj,11,11, a, b, c, d, e, f, g, h, i, j, k); } -#undef A +const char *executef(OBJ, A a) { ConsoleValueRef params[] = {a,ConsoleValueRef()}; return _executef(obj, 2, 2, params); } +const char *executef(OBJ, A a, A b) { ConsoleValueRef params[] = {a,ConsoleValueRef(),b}; return _executef(obj, 3, 3, params); } +const char *executef(OBJ, A a, A b, A c) { ConsoleValueRef params[] = {a,ConsoleValueRef(),b,c}; return _executef(obj, 4, 4, params); } +const char *executef(OBJ, A a, A b, A c, A d) { ConsoleValueRef params[] = {a,ConsoleValueRef(),b,c,d}; return _executef(obj, 5, 5, params); } +const char *executef(OBJ, A a, A b, A c, A d, A e) { ConsoleValueRef params[] = {a,ConsoleValueRef(),b,c,d,e}; return _executef(obj, 6, 6, params); } +const char *executef(OBJ, A a, A b, A c, A d, A e, A f) { ConsoleValueRef params[] = {a,ConsoleValueRef(),b,c,d,e,f}; return _executef(obj, 7, 7, params); } +const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g) { ConsoleValueRef params[] = {a,ConsoleValueRef(),b,c,d,e,f,g}; return _executef(obj, 8, 8, params); } +const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h) { ConsoleValueRef params[] = {a,ConsoleValueRef(),b,c,d,e,f,g,h}; return _executef(obj, 9, 9, params); } +const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h, A i) { ConsoleValueRef params[] = {a,ConsoleValueRef(),b,c,d,e,f,g,h,i}; return _executef(obj, 10, 10, params); } +const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h, A i, A j) { ConsoleValueRef params[] = {a,ConsoleValueRef(),b,c,d,e,f,g,h,i,j}; return _executef(obj, 11, 11, params); } +const char *executef(OBJ, A a, A b, A c, A d, A e, A f, A g, A h, A i, A j, A k) { ConsoleValueRef params[] = {a,ConsoleValueRef(),b,c,d,e,f,g,h,i,j,k}; return _executef(obj, 12, 12, params); } //------------------------------------------------------------------------------ -#define B( a ) const char* a = NULL -#define A const char* -inline const char*_executef(S32 checkArgc, S32 argc, A a, B(b), B(c), B(d), B(e), B(f), B(g), B(h), B(i), B(j)) +inline const char*_executef(S32 checkArgc, S32 argc, ConsoleValueRef *argv) { -#undef A -#undef B const U32 maxArg = 10; AssertFatal(checkArgc == argc, "Incorrect arg count passed to Con::executef()"); AssertFatal(argc <= maxArg, "Too many args passed to Con::_executef(). Please update the function to handle more."); - const char* argv[maxArg]; - argv[0] = a; - argv[1] = b; - argv[2] = c; - argv[3] = d; - argv[4] = e; - argv[5] = f; - argv[6] = g; - argv[7] = h; - argv[8] = i; - argv[9] = j; return execute(argc, argv); } -#define A const char* -const char *executef(A a) { return _executef(1, 1, a); } -const char *executef(A a, A b) { return _executef(2, 2, a, b); } -const char *executef(A a, A b, A c) { return _executef(3, 3, a, b, c); } -const char *executef(A a, A b, A c, A d) { return _executef(4, 4, a, b, c, d); } -const char *executef(A a, A b, A c, A d, A e) { return _executef(5, 5, a, b, c, d, e); } -const char *executef(A a, A b, A c, A d, A e, A f) { return _executef(6, 6, a, b, c, d, e, f); } -const char *executef(A a, A b, A c, A d, A e, A f, A g) { return _executef(7, 7, a, b, c, d, e, f, g); } -const char *executef(A a, A b, A c, A d, A e, A f, A g, A h) { return _executef(8, 8, a, b, c, d, e, f, g, h); } -const char *executef(A a, A b, A c, A d, A e, A f, A g, A h, A i) { return _executef(9, 9, a, b, c, d, e, f, g, h, i); } -const char *executef(A a, A b, A c, A d, A e, A f, A g, A h, A i, A j) { return _executef(10,10,a, b, c, d, e, f, g, h, i, j); } +#define A ConsoleValueRef +const char *executef(A a) { ConsoleValueRef params[] = {a}; return _executef(1, 1, params); } +const char *executef(A a, A b) { ConsoleValueRef params[] = {a,b}; return _executef(2, 2, params); } +const char *executef(A a, A b, A c) { ConsoleValueRef params[] = {a,b,c}; return _executef(3, 3, params); } +const char *executef(A a, A b, A c, A d) { ConsoleValueRef params[] = {a,b,c,d}; return _executef(4, 4, params); } +const char *executef(A a, A b, A c, A d, A e) { ConsoleValueRef params[] = {a,b,c,d,e}; return _executef(5, 5, params); } +const char *executef(A a, A b, A c, A d, A e, A f) { ConsoleValueRef params[] = {a,b,c,d,e,f}; return _executef(1, 1, params); } +const char *executef(A a, A b, A c, A d, A e, A f, A g) { ConsoleValueRef params[] = {a,b,c,d,e,f,g}; return _executef(1, 1, params); } +const char *executef(A a, A b, A c, A d, A e, A f, A g, A h) { ConsoleValueRef params[] = {a,b,c,d,e,f,g,h}; return _executef(1, 1, params); } +const char *executef(A a, A b, A c, A d, A e, A f, A g, A h, A i) { ConsoleValueRef params[] = {a,b,c,d,e,f,g,h,i}; return _executef(1, 1, params); } +const char *executef(A a, A b, A c, A d, A e, A f, A g, A h, A i, A j) { ConsoleValueRef params[] = {a,b,c,d,e,f,g,h,i,j}; return _executef(1, 1, params); } #undef A @@ -1389,10 +1485,10 @@ StringTableEntry getModNameFromPath(const char *path) void postConsoleInput( RawData data ) { // Schedule this to happen at the next time event. - char *argv[2]; + ConsoleValueRef argv[2]; argv[0] = "eval"; - argv[1] = ( char* ) data.data; - Sim::postCurrentEvent(Sim::getRootGroup(), new SimConsoleEvent(2, const_cast(argv), false)); + argv[1] = ( const char* ) data.data; + Sim::postCurrentEvent(Sim::getRootGroup(), new SimConsoleEvent(2, argv, false)); } //------------------------------------------------------------------------------ @@ -1455,3 +1551,201 @@ DefineEngineFunction( logWarning, void, ( const char* message ),, { Con::warnf( "%s", message ); } + +ConsoleValueRef::ConsoleValueRef(const ConsoleValueRef &ref) +{ + value = ref.value; + stringStackValue = ref.stringStackValue; +} + +ConsoleValueRef::ConsoleValueRef(const char *newValue) : value(NULL) +{ + *this = newValue; +} + +ConsoleValueRef::ConsoleValueRef(const String &newValue) : value(NULL) +{ + *this = (const char*)(newValue.utf8()); +} + + +ConsoleValueRef::ConsoleValueRef(S32 newValue) : value(NULL) +{ + *this = newValue; +} + +ConsoleValueRef::ConsoleValueRef(F32 newValue) : value(NULL) +{ + *this = newValue; +} + +ConsoleValueRef::ConsoleValueRef(F64 newValue) : value(NULL) +{ + *this = newValue; +} + +StringStackWrapper::StringStackWrapper(int targc, ConsoleValueRef targv[]) +{ + argv = new const char*[targc]; + argc = targc; + + for (int i=0; i(val); + if(sval != typeValueEmpty) + { + if (type != TypeInternalStackString) dFree(sval); + sval = typeValueEmpty; + } + type = TypeInternalFloat; + } + else + { + const char *dptr = Con::getData(TypeF32, &val, 0); + Con::setData(type, dataPtr, 0, 1, &dptr, enumTable); + } + } + + + const char *ConsoleValueRef::getStringArgValue() + { + if (value) { + if (stringStackValue == NULL) { + stringStackValue = Con::getStringArg(value->getStringValue()); + } + return stringStackValue; + } else { + return ""; + } + } + + +extern ConsoleValueStack CSTK; + +ConsoleValueRef& ConsoleValueRef::operator=(const ConsoleValueRef &newValue) +{ + value = newValue.value; + stringStackValue = newValue.stringStackValue; + return *this; +} + +ConsoleValueRef& ConsoleValueRef::operator=(const char *newValue) +{ + value = CSTK.pushStackString(newValue); + stringStackValue = NULL; + return *this; +} + +ConsoleValueRef& ConsoleValueRef::operator=(S32 newValue) +{ + value = CSTK.pushUINT(newValue); + stringStackValue = NULL; + return *this; +} + +ConsoleValueRef& ConsoleValueRef::operator=(F32 newValue) +{ + value = CSTK.pushFLT(newValue); + stringStackValue = NULL; + return *this; +} + +ConsoleValueRef& ConsoleValueRef::operator=(F64 newValue) +{ + value = CSTK.pushFLT(newValue); + stringStackValue = NULL; + return *this; +} + +namespace Con +{ + void resetStackFrame() + { + CSTK.resetFrame(); + } +} \ No newline at end of file diff --git a/Engine/source/console/console.h b/Engine/source/console/console.h index 3c5718c48..8e2802d48 100644 --- a/Engine/source/console/console.h +++ b/Engine/source/console/console.h @@ -29,15 +29,18 @@ #ifndef _BITSET_H_ #include "core/bitSet.h" #endif +#ifndef _REFBASE_H_ + #include "core/util/refBase.h" +#endif #include +#include "core/util/str.h" #include "core/util/journal/journaledSignal.h" class SimObject; class Namespace; struct ConsoleFunctionHeader; - class EngineEnumTable; typedef EngineEnumTable EnumTable; @@ -110,6 +113,163 @@ struct ConsoleLogEntry }; typedef const char *StringTableEntry; +extern char *typeValueEmpty; + +class ConsoleValue +{ +public: + + enum + { + TypeInternalInt = -4, + TypeInternalFloat = -3, + TypeInternalStackString = -2, + TypeInternalString = -1, + }; + + S32 type; + +public: + + // NOTE: This is protected to ensure no one outside + // of this structure is messing with it. + +#pragma warning( push ) +#pragma warning( disable : 4201 ) // warning C4201: nonstandard extension used : nameless struct/union + + // An variable is either a real dynamic type or + // its one exposed from C++ using a data pointer. + // + // We use this nameless union and struct setup + // to optimize the memory usage. + union + { + struct + { + char *sval; + U32 ival; // doubles as strlen when type is TypeInternalString + F32 fval; + U32 bufferLen; + }; + + struct + { + /// The real data pointer. + void *dataPtr; + + /// The enum lookup table for enumerated types. + const EnumTable *enumTable; + }; + }; + + U32 getIntValue(); + + F32 getFloatValue(); + + const char *getStringValue(); + + void setIntValue(U32 val); + + void setFloatValue(F32 val); + + void setStringValue(const char *value); + void setStackStringValue(const char *value); + + void init() + { + ival = 0; + fval = 0; + sval = typeValueEmpty; + bufferLen = 0; + } + + void cleanup() + { + if (type <= TypeInternalString && + sval != typeValueEmpty && type != TypeInternalStackString ) + dFree(sval); + sval = typeValueEmpty; + type = ConsoleValue::TypeInternalString; + ival = 0; + fval = 0; + bufferLen = 0; + } +}; + +// Proxy class for console variables +// Can point to existing console variables +// or act like a free floating value +class ConsoleValueRef { +public: + ConsoleValue *value; + const char *stringStackValue; + + ConsoleValueRef() : value(0), stringStackValue(0) { ; } + ~ConsoleValueRef() { ; } + + ConsoleValueRef(const ConsoleValueRef &ref); + ConsoleValueRef(const char *value); + ConsoleValueRef(const String &ref); + ConsoleValueRef(S32 value); + ConsoleValueRef(F32 value); + ConsoleValueRef(F64 value); + + const char *getStringValue() { return value ? value->getStringValue() : ""; } + const char *getStringArgValue(); + + inline S32 getIntValue() { return value ? value->getIntValue() : 0; } + inline F32 getFloatValue() { return value ? value->getFloatValue() : 0.0f; } + //inline F64 getDoubleValue() { return value ? value->getDoubleValue() : 0.0; } + + inline operator const char*() { return getStringValue(); } + inline operator String() { return String(getStringValue()); } + inline operator S32() { return getIntValue(); } + inline operator F32() { return getFloatValue(); } + //inline operator F64() { return getDoubleValue(); } + + inline bool isString() { return value ? value->type >= ConsoleValue::TypeInternalStackString : true; } + inline bool isInt() { return value ? value->type == ConsoleValue::TypeInternalInt : false; } + inline bool isFloat() { return value ? value->type == ConsoleValue::TypeInternalFloat : false; } + + // Note: operators replace value + ConsoleValueRef& operator=(const ConsoleValueRef &other); + ConsoleValueRef& operator=(const char *newValue); + ConsoleValueRef& operator=(S32 newValue); + ConsoleValueRef& operator=(F32 newValue); + ConsoleValueRef& operator=(F64 newValue); +}; + +// Transparently converts ConsoleValue[] to const char** +class StringStackWrapper +{ +public: + const char **argv; + int argc; + + StringStackWrapper(int targc, ConsoleValueRef targv[]); + ~StringStackWrapper(); + + const char* operator[](int idx) { return argv[idx]; } + operator const char**() { return argv; } + + int count() { return argc; } +}; + +// Transparently converts const char** to ConsoleValue +class StringStackConsoleWrapper +{ +public: + ConsoleValueRef *argv; + int argc; + + StringStackConsoleWrapper(int targc, const char **targv); + ~StringStackConsoleWrapper(); + + ConsoleValueRef& operator[](int idx) { return argv[idx]; } + operator ConsoleValueRef*() { return argv; } + + int count() { return argc; } +}; /// @defgroup console_callbacks Scripting Engine Callbacks /// @@ -129,11 +289,11 @@ typedef const char *StringTableEntry; /// @{ /// -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 const char * (*StringCallback)(SimObject *obj, S32 argc, ConsoleValueRef argv[]); +typedef S32 (*IntCallback)(SimObject *obj, S32 argc, ConsoleValueRef argv[]); +typedef F32 (*FloatCallback)(SimObject *obj, S32 argc, ConsoleValueRef argv[]); +typedef void (*VoidCallback)(SimObject *obj, S32 argc, ConsoleValueRef argv[]); // We have it return a value so things don't break.. +typedef bool (*BoolCallback)(SimObject *obj, S32 argc, ConsoleValueRef argv[]); typedef void (*ConsumerCallback)(U32 level, const char *consoleLine); /// @} @@ -182,7 +342,7 @@ namespace Con /// 09/12/07 - CAF - 43->44 remove newmsg operator /// 09/27/07 - RDB - 44->45 Patch from Andreas Kirsch: Added opcode to support correct void return /// 01/13/09 - TMS - 45->46 Added script assert - DSOVersion = 46, + DSOVersion = 47, MaxLineLength = 512, ///< Maximum length of a line of console input. MaxDataTypes = 256 ///< Maximum number of registered data types. @@ -415,6 +575,11 @@ namespace Con /// @return The string value of the variable or "" if the variable does not exist. const char* getVariable(const char* name); + /// Retrieve the string value of an object field + /// @param name "object.field" string to query + /// @return The string value of the variable or NULL if no object is specified + const char* getObjectField(const char* name); + /// Same as setVariable(), but for bools. void setBoolVariable (const char* name,bool var); @@ -565,9 +730,11 @@ namespace Con /// char* result = execute(2, argv); /// @endcode const char *execute(S32 argc, const char* argv[]); + const char *execute(S32 argc, ConsoleValueRef argv[]); /// @see execute(S32 argc, const char* argv[]) -#define ARG const char* + // Note: this can't be ConsoleValueRef& since the compiler will confuse it with SimObject* +#define ARG ConsoleValueRef const char *executef( ARG); const char *executef( ARG, ARG); const char *executef( ARG, ARG, ARG); @@ -580,7 +747,6 @@ namespace Con const char *executef( ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG, ARG); #undef ARG - /// 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) @@ -594,9 +760,10 @@ namespace Con /// char* result = execute(mysimobject, 3, argv); /// @endcode const char *execute(SimObject *object, S32 argc, const char *argv[], bool thisCallOnly = false); + const char *execute(SimObject *object, S32 argc, ConsoleValueRef argv[], bool thisCallOnly = false); - /// @see execute(SimObject *, S32 argc, const char *argv[]) -#define ARG const char* + /// @see execute(SimObject *, S32 argc, ConsoleValueRef argv[]) +#define ARG ConsoleValueRef const char *executef(SimObject *, ARG); const char *executef(SimObject *, ARG, ARG); const char *executef(SimObject *, ARG, ARG, ARG); @@ -646,6 +813,8 @@ namespace Con char* getStringArg( const String& arg ); /// @} + void resetStackFrame(); + /// @name Namespaces /// @{ @@ -941,14 +1110,14 @@ struct ConsoleDocFragment static ConsoleConstructor cfg_ConsoleFunctionGroup_##groupName##_GroupBegin(NULL,#groupName,usage) # define ConsoleFunction(name,returnType,minArgs,maxArgs,usage1) \ - returnType cf_##name(SimObject *, S32, const char **argv); \ + returnType cf_##name(SimObject *, S32, ConsoleValueRef *argv); \ ConsoleConstructor cc_##name##_obj(NULL,#name,cf_##name,usage1,minArgs,maxArgs); \ - returnType cf_##name(SimObject *, S32 argc, const char **argv) + returnType cf_##name(SimObject *, S32 argc, ConsoleValueRef *argv) # define ConsoleToolFunction(name,returnType,minArgs,maxArgs,usage1) \ - returnType ctf_##name(SimObject *, S32, const char **argv); \ + returnType ctf_##name(SimObject *, S32, ConsoleValueRef *argv); \ ConsoleConstructor cc_##name##_obj(NULL,#name,ctf_##name,usage1,minArgs,maxArgs, true); \ - returnType ctf_##name(SimObject *, S32 argc, const char **argv) + returnType ctf_##name(SimObject *, S32 argc, ConsoleValueRef *argv) # define ConsoleFunctionGroupEnd(groupName) \ static ConsoleConstructor cfg_##groupName##_GroupEnd(NULL,#groupName,NULL) @@ -961,22 +1130,22 @@ struct ConsoleDocFragment static ConsoleConstructor cc_##className##_##groupName##_GroupBegin(#className,#groupName,usage) # define ConsoleMethod(className,name,returnType,minArgs,maxArgs,usage1) \ - inline returnType cm_##className##_##name(className *, S32, const char **argv); \ - returnType cm_##className##_##name##_caster(SimObject *object, S32 argc, const char **argv) { \ + inline returnType cm_##className##_##name(className *, S32, ConsoleValueRef *argv); \ + returnType cm_##className##_##name##_caster(SimObject *object, S32 argc, ConsoleValueRef *argv) { \ AssertFatal( dynamic_cast( object ), "Object passed to " #name " is not a " #className "!" ); \ conmethod_return_##returnType ) cm_##className##_##name(static_cast(object),argc,argv); \ }; \ ConsoleConstructor cc_##className##_##name##_obj(#className,#name,cm_##className##_##name##_caster,usage1,minArgs,maxArgs); \ - inline returnType cm_##className##_##name(className *object, S32 argc, const char **argv) + inline returnType cm_##className##_##name(className *object, S32 argc, ConsoleValueRef *argv) # define ConsoleStaticMethod(className,name,returnType,minArgs,maxArgs,usage1) \ - inline returnType cm_##className##_##name(S32, const char **); \ - returnType cm_##className##_##name##_caster(SimObject *object, S32 argc, const char **argv) { \ + inline returnType cm_##className##_##name(S32, ConsoleValueRef *); \ + returnType cm_##className##_##name##_caster(SimObject *object, S32 argc, ConsoleValueRef *argv) { \ conmethod_return_##returnType ) cm_##className##_##name(argc,argv); \ }; \ ConsoleConstructor \ cc_##className##_##name##_obj(#className,#name,cm_##className##_##name##_caster,usage1,minArgs,maxArgs); \ - inline returnType cm_##className##_##name(S32 argc, const char **argv) + inline returnType cm_##className##_##name(S32 argc, ConsoleValueRef *argv) # define ConsoleMethodGroupEnd(className, groupName) \ static ConsoleConstructor cc_##className##_##groupName##_GroupEnd(#className,#groupName,NULL) @@ -999,32 +1168,32 @@ struct ConsoleDocFragment // 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 returnType c##name(SimObject *, S32, ConsoleValueRef*); \ static ConsoleConstructor g##name##obj(NULL,#name,c##name,"",minArgs,maxArgs);\ - static returnType c##name(SimObject *, S32 argc, const char **argv) + static returnType c##name(SimObject *, S32 argc, ConsoleValueRef *argv) # define ConsoleToolFunction(name,returnType,minArgs,maxArgs,usage1) \ - static returnType c##name(SimObject *, S32, const char **); \ + static returnType c##name(SimObject *, S32, ConsoleValueRef*); \ static ConsoleConstructor g##name##obj(NULL,#name,c##name,"",minArgs,maxArgs, true);\ - static returnType c##name(SimObject *, S32 argc, const char **argv) + static returnType c##name(SimObject *, S32 argc, ConsoleValueRef *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) { \ + static inline returnType c##className##name(className *, S32, ConsoleValueRef *argv); \ + static returnType c##className##name##caster(SimObject *object, S32 argc, ConsoleValueRef *argv) { \ conmethod_return_##returnType ) c##className##name(static_cast(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) + static inline returnType c##className##name(className *object, S32 argc, ConsoleValueRef *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) { \ + static inline returnType c##className##name(S32, ConsoleValueRef*); \ + static returnType c##className##name##caster(SimObject *object, S32 argc, ConsoleValueRef *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) + static inline returnType c##className##name(S32 argc, ConsoleValueRef *argv) #define ConsoleDoc( text ) diff --git a/Engine/source/console/consoleFunctions.cpp b/Engine/source/console/consoleFunctions.cpp index a2a6b1137..bc3870cec 100644 --- a/Engine/source/console/consoleFunctions.cpp +++ b/Engine/source/console/consoleFunctions.cpp @@ -1207,7 +1207,9 @@ ConsoleFunction( nextToken, const char *, 4, 4, "( string str, string token, str "@endtsexample\n\n" "@ingroup Strings" ) { - char *str = (char *) argv[1]; + char buffer[4096]; + dStrncpy(buffer, argv[1], 4096); + char *str = buffer; const char *token = argv[2]; const char *delim = argv[3]; @@ -1240,7 +1242,9 @@ ConsoleFunction( nextToken, const char *, 4, 4, "( string str, string token, str str++; } - return str; + char *ret = Con::getReturnBuffer(dStrlen(str)+1); + dStrncpy(ret, str, dStrlen(str)+1); + return ret; } //============================================================================= @@ -1302,16 +1306,17 @@ ConsoleFunction(getTag, const char *, 2, 2, "(string textTagString)" TORQUE_UNUSED(argc); if(argv[1][0] == StringTagPrefixByte) { + const char *arg = argv[1]; const char * space = dStrchr(argv[1], ' '); U32 len; if(space) - len = space - argv[1]; + len = space - arg; else - len = dStrlen(argv[1]) + 1; + len = dStrlen(arg) + 1; char * ret = Con::getReturnBuffer(len); - dStrncpy(ret, argv[1] + 1, len - 1); + dStrncpy(ret, arg + 1, len - 1); ret[len - 1] = 0; return(ret); @@ -2394,7 +2399,7 @@ ConsoleFunction( pushInstantGroup, void, 1, 2, "([group])" "@internal") { if( argc > 1 ) - Con::pushInstantGroup( argv[ 1 ] ); + Con::pushInstantGroup( (const char*)argv[ 1 ] ); else Con::pushInstantGroup(); } diff --git a/Engine/source/console/consoleInternal.cpp b/Engine/source/console/consoleInternal.cpp index 250326460..208c998cc 100644 --- a/Engine/source/console/consoleInternal.cpp +++ b/Engine/source/console/consoleInternal.cpp @@ -35,6 +35,44 @@ //#define DEBUG_SPEW +Dictionary::Entry smLocalDictionaryEntryStack[4096*4]; +Dictionary::Entry *smLocalDictionaryEntryStackHead = NULL; + +void setupDictionaryStack() +{ + smLocalDictionaryEntryStackHead = &smLocalDictionaryEntryStack[0]; + + for (int i=0; i<4096*4; i++) { + (smLocalDictionaryEntryStackHead + i)->mNext = i == (4096*4)-1 ? NULL : smLocalDictionaryEntryStackHead + (i+1); + } +} + +Dictionary::Entry * getDictionaryStackEntry() +{ + Dictionary::Entry *entry = smLocalDictionaryEntryStackHead; + AssertFatal(entry, "No more local variables"); + + entry->reset(); + + Dictionary::Entry *next = entry->mNext; + + smLocalDictionaryEntryStackHead = next; + + entry->mNext = NULL; + + return entry; +} + +void disposeDictionaryStackEntry(Dictionary::Entry *entry) +{ + Dictionary::Entry *prevHead = smLocalDictionaryEntryStackHead; + smLocalDictionaryEntryStackHead = entry; + + smLocalDictionaryEntryStackHead->mNext = prevHead; +} + + + #define ST_INIT_SIZE 15 static char scratchBuffer[1024]; @@ -168,13 +206,13 @@ void Dictionary::exportVariables(const char *varString, const char *fileName, bo for(s = sortList.begin(); s != sortList.end(); s++) { - switch((*s)->type) + switch((*s)->value.type) { - case Entry::TypeInternalInt: - dSprintf(buffer, sizeof(buffer), "%s = %d;%s", (*s)->name, (*s)->ival, cat); + case ConsoleValue::TypeInternalInt: + dSprintf(buffer, sizeof(buffer), "%s = %d;%s", (*s)->name, (*s)->value.ival, cat); break; - case Entry::TypeInternalFloat: - dSprintf(buffer, sizeof(buffer), "%s = %g;%s", (*s)->name, (*s)->fval, cat); + case ConsoleValue::TypeInternalFloat: + dSprintf(buffer, sizeof(buffer), "%s = %g;%s", (*s)->name, (*s)->value.fval, cat); break; default: expandEscape(expandBuffer, (*s)->getStringValue()); @@ -228,13 +266,13 @@ void Dictionary::exportVariables( const char *varString, Vector *names, if ( values ) { - switch ( (*s)->type ) + switch ( (*s)->value.type ) { - case Entry::TypeInternalInt: - values->push_back( String::ToString( (*s)->ival ) ); + case ConsoleValue::TypeInternalInt: + values->push_back( String::ToString( (*s)->value.ival ) ); break; - case Entry::TypeInternalFloat: - values->push_back( String::ToString( (*s)->fval ) ); + case ConsoleValue::TypeInternalFloat: + values->push_back( String::ToString( (*s)->value.fval ) ); break; default: expandEscape( expandBuffer, (*s)->getStringValue() ); @@ -284,10 +322,13 @@ Dictionary::Entry *Dictionary::lookup(StringTableEntry name) Dictionary::Entry *Dictionary::add(StringTableEntry name) { // Try to find an existing match. + //printf("Add Variable %s\n", name); Entry* ret = lookup( name ); - if( ret ) + if( ret ) { + //printf("Found Variable %s (named %s)\n", name, ret->name); return ret; + } // Rehash if the table get's too crowded. Be aware that this might // modify a table that we don't own. @@ -296,6 +337,7 @@ Dictionary::Entry *Dictionary::add(StringTableEntry name) if( hashTable->count > hashTable->size * 2 ) { // Allocate a new table. + printf("Re-hashing dictionary...\n"); const U32 newTableSize = hashTable->size * 4 - 1; Entry** newTableData = new Entry*[ newTableSize ]; @@ -307,7 +349,10 @@ Dictionary::Entry *Dictionary::add(StringTableEntry name) for( Entry* entry = hashTable->data[ i ]; entry != NULL; ) { Entry* next = entry->nextEntry; - S32 index = HashPointer( entry->name ) % newTableSize; + U32 index = HashPointer( entry->name ) % newTableSize; + + + //printf(" Variable(%s) in bucket %i moved to bucket %i\n", entry->name, i, index); entry->nextEntry = newTableData[ index ]; newTableData[ index ] = entry; @@ -328,9 +373,10 @@ Dictionary::Entry *Dictionary::add(StringTableEntry name) // Add the new entry. - ret = hashTable->mChunker.alloc(); - constructInPlace( ret, name ); - S32 idx = HashPointer(name) % hashTable->size; + ret = getDictionaryStackEntry();//hashTable->mChunker.alloc(); + ret->name = name; + //constructInPlace( ret, name ); + U32 idx = HashPointer(name) % hashTable->size; ret->nextEntry = hashTable->data[idx]; hashTable->data[idx] = ret; @@ -350,8 +396,8 @@ void Dictionary::remove(Dictionary::Entry *ent) *walk = (ent->nextEntry); - destructInPlace( ent ); - hashTable->mChunker.free( ent ); + disposeDictionaryStackEntry( ent ); + //hashTable->mChunker.free( ent ); hashTable->count--; } @@ -412,13 +458,13 @@ void Dictionary::reset() while( walk ) { Entry* temp = walk->nextEntry; - destructInPlace( walk ); + disposeDictionaryStackEntry( walk ); walk = temp; } } dMemset( ownHashTable.data, 0, ownHashTable.size * sizeof( Entry* ) ); - ownHashTable.mChunker.freeBlocks( true ); + //ownHashTable.mChunker.freeBlocks( true ); ownHashTable.count = 0; hashTable = NULL; @@ -454,7 +500,7 @@ char *typeValueEmpty = ""; Dictionary::Entry::Entry(StringTableEntry in_name) { name = in_name; - type = TypeInternalString; + value.type = ConsoleValue::TypeInternalString; notify = NULL; nextEntry = NULL; mUsage = NULL; @@ -462,17 +508,12 @@ Dictionary::Entry::Entry(StringTableEntry in_name) // NOTE: This is data inside a nameless // union, so we don't need to init the rest. - ival = 0; - fval = 0; - sval = typeValueEmpty; - bufferLen = 0; + value.init(); } Dictionary::Entry::~Entry() { - if ( type <= TypeInternalString && - sval != typeValueEmpty ) - dFree(sval); + value.cleanup(); if ( notify ) delete notify; @@ -497,15 +538,11 @@ const char *Dictionary::getVariable(StringTableEntry name, bool *entValid) return ""; } -void Dictionary::Entry::setStringValue(const char * value) +void ConsoleValue::setStringValue(const char * value) { - if( mIsConstant ) - { - Con::errorf( "Cannot assign value to constant '%s'.", name ); - return; - } + if (value == NULL) value = typeValueEmpty; - if(type <= TypeInternalString) + if(type <= ConsoleValue::TypeInternalString) { // Let's not remove empty-string-valued global vars from the dict. // If we remove them, then they won't be exported, and sometimes @@ -519,6 +556,15 @@ void Dictionary::Entry::setStringValue(const char * value) return; } */ + if (value == typeValueEmpty) { + if (sval && sval != typeValueEmpty && type != TypeInternalStackString) dFree(sval); + sval = typeValueEmpty; + bufferLen = 0; + fval = 0.f; + ival = 0; + type = TypeInternalString; + return; + } U32 stringLen = dStrlen(value); @@ -537,25 +583,89 @@ void Dictionary::Entry::setStringValue(const char * value) ival = 0; } - type = TypeInternalString; - // may as well pad to the next cache line U32 newLen = ((stringLen + 1) + 15) & ~15; - - if(sval == typeValueEmpty) + + if(sval == typeValueEmpty || type == TypeInternalStackString) sval = (char *) dMalloc(newLen); else if(newLen > bufferLen) sval = (char *) dRealloc(sval, newLen); + type = TypeInternalString; + bufferLen = newLen; dStrcpy(sval, value); } else Con::setData(type, dataPtr, 0, 1, &value, enumTable); +} - // Fire off the notification if we have one. - if ( notify ) - notify->trigger(); + +void ConsoleValue::setStackStringValue(const char * value) +{ + if (value == NULL) value = typeValueEmpty; + + if(type <= ConsoleValue::TypeInternalString) + { + if (value == typeValueEmpty) { + if (sval && sval != typeValueEmpty && type != ConsoleValue::TypeInternalStackString) dFree(sval); + sval = typeValueEmpty; + bufferLen = 0; + fval = 0.f; + ival = 0; + type = TypeInternalString; + return; + } + + U32 stringLen = dStrlen(value); + if(stringLen < 256) + { + fval = dAtof(value); + ival = dAtoi(value); + } + else + { + fval = 0.f; + ival = 0; + } + + type = TypeInternalStackString; + sval = (char*)value; + bufferLen = stringLen; + } + else + Con::setData(type, dataPtr, 0, 1, &value, enumTable); +} + + +S32 Dictionary::getIntVariable(StringTableEntry name, bool *entValid) +{ + Entry *ent = lookup(name); + if(ent) + { + if(entValid) + *entValid = true; + return ent->getIntValue(); + } + if(entValid) + *entValid = false; + + return 0; +} + +F32 Dictionary::getFloatVariable(StringTableEntry name, bool *entValid) +{ + Entry *ent = lookup(name); + if(ent) + { + if(entValid) + *entValid = true; + return ent->getFloatValue(); + } + if(entValid) + *entValid = false; + + return 0; } void Dictionary::setVariable(StringTableEntry name, const char *value) @@ -582,19 +692,19 @@ Dictionary::Entry* Dictionary::addVariable( const char *name, Entry *ent = add(StringTable->insert(name)); - if ( ent->type <= Entry::TypeInternalString && - ent->sval != typeValueEmpty ) - dFree(ent->sval); + if ( ent->value.type <= ConsoleValue::TypeInternalString && + ent->value.sval != typeValueEmpty && ent->value.type != ConsoleValue::TypeInternalStackString ) + dFree(ent->value.sval); - ent->type = type; - ent->dataPtr = dataPtr; + ent->value.type = type; + ent->value.dataPtr = dataPtr; ent->mUsage = usage; // Fetch enum table, if any. ConsoleBaseType* conType = ConsoleBaseType::getType( type ); AssertFatal( conType, "Dictionary::addVariable - invalid console type" ); - ent->enumTable = conType->getEnumTable(); + ent->value.enumTable = conType->getEnumTable(); return ent; } @@ -1031,6 +1141,8 @@ void Namespace::init() mGlobalNamespace->mName = NULL; mGlobalNamespace->mNext = NULL; mNamespaceList = mGlobalNamespace; + + setupDictionaryStack(); } Namespace *Namespace::global() @@ -1268,7 +1380,7 @@ void Namespace::markGroup(const char* name, const char* usage) extern S32 executeBlock(StmtNode *block, ExprEvalState *state); -const char *Namespace::Entry::execute(S32 argc, const char **argv, ExprEvalState *state) +const char *Namespace::Entry::execute(S32 argc, ConsoleValueRef *argv, ExprEvalState *state) { if(mType == ConsoleFunctionType) { diff --git a/Engine/source/console/consoleInternal.h b/Engine/source/console/consoleInternal.h index 0a9c9060e..7893e6384 100644 --- a/Engine/source/console/consoleInternal.h +++ b/Engine/source/console/consoleInternal.h @@ -151,7 +151,7 @@ class Namespace void clear(); /// - const char *execute( S32 argc, const char** argv, ExprEvalState* state ); + const char *execute( S32 argc, ConsoleValueRef* argv, ExprEvalState* state ); /// Return a one-line documentation text string for the function. String getBriefDescription( String* outRemainingDocText = NULL ) const; @@ -275,7 +275,7 @@ class Namespace typedef VectorPtr::iterator NamespaceEntryListIterator; -extern char *typeValueEmpty; + class Dictionary { @@ -283,16 +283,9 @@ public: struct Entry { - enum - { - TypeInternalInt = -3, - TypeInternalFloat = -2, - TypeInternalString = -1, - }; - StringTableEntry name; + ConsoleValue value; Entry *nextEntry; - S32 type; typedef Signal NotifySignal; @@ -306,72 +299,42 @@ public: /// Whether this is a constant that cannot be assigned to. bool mIsConstant; - protected: - - // NOTE: This is protected to ensure no one outside - // of this structure is messing with it. - - #pragma warning( push ) - #pragma warning( disable : 4201 ) // warning C4201: nonstandard extension used : nameless struct/union - - // An variable is either a real dynamic type or - // its one exposed from C++ using a data pointer. - // - // We use this nameless union and struct setup - // to optimize the memory usage. - union - { - struct - { - char *sval; - U32 ival; // doubles as strlen when type is TypeInternalString - F32 fval; - U32 bufferLen; - }; - - struct - { - /// The real data pointer. - void *dataPtr; - - /// The enum lookup table for enumerated types. - const EnumTable *enumTable; - }; - }; - - #pragma warning( pop ) // C4201 - public: + Entry() { + name = NULL; + notify = NULL; + nextEntry = NULL; + mUsage = NULL; + mIsConstant = false; + value.init(); + } + Entry(StringTableEntry name); ~Entry(); - - U32 getIntValue() - { - if(type <= TypeInternalString) - return ival; - else - return dAtoi(Con::getData(type, dataPtr, 0, enumTable)); + + Entry *mNext; + + void reset() { + name = NULL; + value.cleanup(); + if ( notify ) + delete notify; } - F32 getFloatValue() + inline U32 getIntValue() { - if(type <= TypeInternalString) - return fval; - else - return dAtof(Con::getData(type, dataPtr, 0, enumTable)); + return value.getIntValue(); } - const char *getStringValue() + inline F32 getFloatValue() { - 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, enumTable); + return value.getFloatValue(); + } + + inline const char *getStringValue() + { + return value.getStringValue(); } void setIntValue(U32 val) @@ -381,23 +344,8 @@ public: Con::errorf( "Cannot assign value to constant '%s'.", name ); return; } - - if(type <= TypeInternalString) - { - fval = (F32)val; - ival = val; - if(sval != typeValueEmpty) - { - dFree(sval); - sval = typeValueEmpty; - } - type = TypeInternalInt; - } - else - { - const char *dptr = Con::getData(TypeS32, &val, 0); - Con::setData(type, dataPtr, 0, 1, &dptr, enumTable); - } + + value.setIntValue(val); // Fire off the notification if we have one. if ( notify ) @@ -411,30 +359,29 @@ public: Con::errorf( "Cannot assign value to constant '%s'.", name ); return; } - - if(type <= TypeInternalString) - { - fval = val; - ival = static_cast(val); - if(sval != typeValueEmpty) - { - dFree(sval); - sval = typeValueEmpty; - } - type = TypeInternalFloat; - } - else - { - const char *dptr = Con::getData(TypeF32, &val, 0); - Con::setData(type, dataPtr, 0, 1, &dptr, enumTable); - } + + value.setFloatValue(val); // Fire off the notification if we have one. if ( notify ) notify->trigger(); } - void setStringValue(const char *value); + void setStringValue(const char *newValue) + { + if( mIsConstant ) + { + Con::errorf( "Cannot assign value to constant '%s'.", name ); + return; + } + + value.setStringValue(newValue); + + + // Fire off the notification if we have one. + if ( notify ) + notify->trigger(); + } }; struct HashTableData @@ -473,6 +420,8 @@ public: void setVariable(StringTableEntry name, const char *value); const char *getVariable(StringTableEntry name, bool *valid = NULL); + S32 getIntVariable(StringTableEntry name, bool *valid = NULL); + F32 getFloatVariable(StringTableEntry name, bool *entValid = NULL); U32 getCount() const { diff --git a/Engine/source/console/consoleLogger.cpp b/Engine/source/console/consoleLogger.cpp index aedc9baab..277f2eaa7 100644 --- a/Engine/source/console/consoleLogger.cpp +++ b/Engine/source/console/consoleLogger.cpp @@ -79,7 +79,7 @@ void ConsoleLogger::initPersistFields() //----------------------------------------------------------------------------- -bool ConsoleLogger::processArguments( S32 argc, const char **argv ) +bool ConsoleLogger::processArguments( S32 argc, ConsoleValueRef *argv ) { if( argc == 0 ) return false; diff --git a/Engine/source/console/consoleLogger.h b/Engine/source/console/consoleLogger.h index dfcb14251..901f45b17 100644 --- a/Engine/source/console/consoleLogger.h +++ b/Engine/source/console/consoleLogger.h @@ -81,7 +81,7 @@ class ConsoleLogger : public SimObject /// // Example script constructor usage. /// %obj = new ConsoleLogger( objName, logFileName, [append = false] ); /// @endcode - bool processArguments( S32 argc, const char **argv ); + bool processArguments( S32 argc, ConsoleValueRef *argv ); /// Default constructor, make sure to initalize ConsoleLogger(); diff --git a/Engine/source/console/engineAPI.h b/Engine/source/console/engineAPI.h index 7a3886580..1383277ce 100644 --- a/Engine/source/console/engineAPI.h +++ b/Engine/source/console/engineAPI.h @@ -145,12 +145,12 @@ inline const char* EngineMarshallData( U32 value ) /// Marshal data from native into client form stored directly in /// client function invocation vector. template< typename T > -inline void EngineMarshallData( const T& arg, S32& argc, const char** argv ) +inline void EngineMarshallData( const T& arg, S32& argc, ConsoleValueRef *argv ) { argv[ argc ] = Con::getStringArg( castConsoleTypeToString( arg ) ); argc ++; } -inline void EngineMarshallData( bool arg, S32& argc, const char** argv ) +inline void EngineMarshallData( bool arg, S32& argc, ConsoleValueRef *argv ) { if( arg ) argv[ argc ] = "1"; @@ -158,33 +158,33 @@ inline void EngineMarshallData( bool arg, S32& argc, const char** argv ) argv[ argc ] = "0"; argc ++; } -inline void EngineMarshallData( S32 arg, S32& argc, const char** argv ) +inline void EngineMarshallData( S32 arg, S32& argc, ConsoleValueRef *argv ) { argv[ argc ] = Con::getIntArg( arg ); argc ++; } -inline void EngineMarshallData( U32 arg, S32& argc, const char** argv ) +inline void EngineMarshallData( U32 arg, S32& argc, ConsoleValueRef *argv ) { EngineMarshallData( S32( arg ), argc, argv ); } -inline void EngineMarshallData( F32 arg, S32& argc, const char** argv ) +inline void EngineMarshallData( F32 arg, S32& argc, ConsoleValueRef *argv ) { argv[ argc ] = Con::getFloatArg( arg ); argc ++; } -inline void EngineMarshallData( const char* arg, S32& argc, const char** argv ) +inline void EngineMarshallData( const char* arg, S32& argc, ConsoleValueRef *argv ) { argv[ argc ] = arg; argc ++; } template< typename T > -inline void EngineMarshallData( T* object, S32& argc, const char** argv ) +inline void EngineMarshallData( T* object, S32& argc, ConsoleValueRef *argv ) { argv[ argc ] = ( object ? object->getIdString() : "0" ); argc ++; } template< typename T > -inline void EngineMarshallData( const T* object, S32& argc, const char** argv ) +inline void EngineMarshallData( const T* object, S32& argc, ConsoleValueRef *argv ) { argv[ argc ] = ( object ? object->getIdString() : "0" ); argc ++; @@ -1391,12 +1391,12 @@ struct _EngineConsoleThunk< startArgc, R() > { typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; static const int NUM_ARGS = 0; - static ReturnType thunk( S32 argc, const char** argv, R ( *fn )(), const _EngineFunctionDefaultArguments< void() >& ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( *fn )(), const _EngineFunctionDefaultArguments< void() >& ) { return _EngineConsoleThunkReturnValue( fn() ); } template< typename Frame > - static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )() const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType* ) >& ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( Frame::*fn )() const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType* ) >& ) { return _EngineConsoleThunkReturnValue( ( frame->*fn )() ); } @@ -1406,12 +1406,12 @@ struct _EngineConsoleThunk< startArgc, void() > { typedef void ReturnType; static const int NUM_ARGS = 0; - static void thunk( S32 argc, const char** argv, void ( *fn )(), const _EngineFunctionDefaultArguments< void() >& ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( *fn )(), const _EngineFunctionDefaultArguments< void() >& ) { fn(); } template< typename Frame > - static void thunk( S32 argc, const char** argv, void ( Frame::*fn )() const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType* ) >& ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( Frame::*fn )() const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType* ) >& ) { return ( frame->*fn )(); } @@ -1422,13 +1422,13 @@ struct _EngineConsoleThunk< startArgc, R( A ) > { typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; static const int NUM_ARGS = 1 + startArgc; - static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A ), const _EngineFunctionDefaultArguments< void( A ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( *fn )( A ), const _EngineFunctionDefaultArguments< void( A ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); return _EngineConsoleThunkReturnValue( fn( a ) ); } template< typename Frame > - static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( Frame::*fn )( A ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); return _EngineConsoleThunkReturnValue( ( frame->*fn )( a ) ); @@ -1439,13 +1439,13 @@ struct _EngineConsoleThunk< startArgc, void( A ) > { typedef void ReturnType; static const int NUM_ARGS = 1 + startArgc; - static void thunk( S32 argc, const char** argv, void ( *fn )( A ), const _EngineFunctionDefaultArguments< void( A ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( *fn )( A ), const _EngineFunctionDefaultArguments< void( A ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); fn( a ); } template< typename Frame > - static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( Frame::*fn )( A ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); ( frame->*fn )( a ); @@ -1457,14 +1457,14 @@ struct _EngineConsoleThunk< startArgc, R( A, B ) > { typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; static const int NUM_ARGS = 2 + startArgc; - static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B ), const _EngineFunctionDefaultArguments< void( A, B ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( *fn )( A, B ), const _EngineFunctionDefaultArguments< void( A, B ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); return _EngineConsoleThunkReturnValue( fn( a, b ) ); } template< typename Frame > - static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( Frame::*fn )( A, B ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1476,14 +1476,14 @@ struct _EngineConsoleThunk< startArgc, void( A, B ) > { typedef void ReturnType; static const int NUM_ARGS = 2 + startArgc; - static void thunk( S32 argc, const char** argv, void ( *fn )( A, B ), const _EngineFunctionDefaultArguments< void( A, B ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( *fn )( A, B ), const _EngineFunctionDefaultArguments< void( A, B ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); fn( a, b ); } template< typename Frame > - static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( Frame::*fn )( A, B ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1496,7 +1496,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C ) > { typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; static const int NUM_ARGS = 3 + startArgc; - static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C ), const _EngineFunctionDefaultArguments< void( A, B, C ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( *fn )( A, B, C ), const _EngineFunctionDefaultArguments< void( A, B, C ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1504,7 +1504,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C ) > return _EngineConsoleThunkReturnValue( fn( a, b, c ) ); } template< typename Frame > - static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( Frame::*fn )( A, B, C ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1517,7 +1517,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C ) > { typedef void ReturnType; static const int NUM_ARGS = 3 + startArgc; - static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C ), const _EngineFunctionDefaultArguments< void( A, B, C ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( *fn )( A, B, C ), const _EngineFunctionDefaultArguments< void( A, B, C ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1525,7 +1525,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C ) > fn( a, b, c ); } template< typename Frame > - static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( Frame::*fn )( A, B, C ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1539,7 +1539,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D ) > { typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; static const int NUM_ARGS = 4 + startArgc; - static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D ), const _EngineFunctionDefaultArguments< void( A, B, C, D ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( *fn )( A, B, C, D ), const _EngineFunctionDefaultArguments< void( A, B, C, D ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1548,7 +1548,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D ) > return _EngineConsoleThunkReturnValue( fn( a, b, c, d ) ); } template< typename Frame > - static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( Frame::*fn )( A, B, C, D ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1562,7 +1562,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D ) > { typedef void ReturnType; static const int NUM_ARGS = 4 + startArgc; - static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D ), const _EngineFunctionDefaultArguments< void( A, B, C, D ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( *fn )( A, B, C, D ), const _EngineFunctionDefaultArguments< void( A, B, C, D ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1571,7 +1571,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D ) > fn( a, b, c, d ); } template< typename Frame > - static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( Frame::*fn )( A, B, C, D ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1586,7 +1586,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E ) > { typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; static const int NUM_ARGS = 5 + startArgc; - static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D, E ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( *fn )( A, B, C, D, E ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1596,7 +1596,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E ) > return _EngineConsoleThunkReturnValue( fn( a, b, c, d, e ) ); } template< typename Frame > - static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D, E ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( Frame::*fn )( A, B, C, D, E ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1611,7 +1611,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E ) > { typedef void ReturnType; static const int NUM_ARGS = 5 + startArgc; - static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D, E ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( *fn )( A, B, C, D, E ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1621,7 +1621,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E ) > fn( a, b, c, d, e ); } template< typename Frame > - static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D, E ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( Frame::*fn )( A, B, C, D, E ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1637,7 +1637,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F ) > { typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; static const int NUM_ARGS = 6 + startArgc; - static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D, E, F ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( *fn )( A, B, C, D, E, F ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1648,7 +1648,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F ) > return _EngineConsoleThunkReturnValue( fn( a, b, c, d, e, f ) ); } template< typename Frame > - static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D, E, F ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( Frame::*fn )( A, B, C, D, E, F ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1664,7 +1664,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F ) > { typedef void ReturnType; static const int NUM_ARGS = 6 + startArgc; - static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D, E, F ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( *fn )( A, B, C, D, E, F ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1675,7 +1675,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F ) > fn( a, b, c, d, e, f ); } template< typename Frame > - static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D, E, F ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( Frame::*fn )( A, B, C, D, E, F ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1692,7 +1692,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G ) > { typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; static const int NUM_ARGS = 7 + startArgc; - static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D, E, F, G ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( *fn )( A, B, C, D, E, F, G ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1704,7 +1704,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G ) > return _EngineConsoleThunkReturnValue( fn( a, b, c, d, e, f, g ) ); } template< typename Frame > - static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D, E, F, G ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( Frame::*fn )( A, B, C, D, E, F, G ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1721,7 +1721,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G ) > { typedef void ReturnType; static const int NUM_ARGS = 7 + startArgc; - static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D, E, F, G ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( *fn )( A, B, C, D, E, F, G ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1733,7 +1733,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G ) > fn( a, b, c, d, e, f, g ); } template< typename Frame > - static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D, E, F, G ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( Frame::*fn )( A, B, C, D, E, F, G ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1751,7 +1751,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G, H ) > { typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; static const int NUM_ARGS = 8 + startArgc; - static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D, E, F, G, H ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( *fn )( A, B, C, D, E, F, G, H ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1764,7 +1764,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G, H ) > return _EngineConsoleThunkReturnValue( fn( a, b, c, d, e, f, g, h ) ); } template< typename Frame > - static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D, E, F, G, H ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( Frame::*fn )( A, B, C, D, E, F, G, H ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1782,7 +1782,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H ) > { typedef void ReturnType; static const int NUM_ARGS = 8 + startArgc; - static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D, E, F, G, H ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( *fn )( A, B, C, D, E, F, G, H ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1795,7 +1795,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H ) > fn( a, b, c, d, e, f, g, h ); } template< typename Frame > - static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D, E, F, G, H ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( Frame::*fn )( A, B, C, D, E, F, G, H ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1814,7 +1814,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G, H, I ) > { typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; static const int NUM_ARGS = 9 + startArgc; - static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D, E, F, G, H, I ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( *fn )( A, B, C, D, E, F, G, H, I ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1828,7 +1828,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G, H, I ) > return _EngineConsoleThunkReturnValue( fn( a, b, c, d, e, f, g, h, i ) ); } template< typename Frame > - static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D, E, F, G, H, I ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( Frame::*fn )( A, B, C, D, E, F, G, H, I ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1847,7 +1847,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I ) > { typedef void ReturnType; static const int NUM_ARGS = 9 + startArgc; - static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D, E, F, G, H, I ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( *fn )( A, B, C, D, E, F, G, H, I ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1861,7 +1861,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I ) > fn( a, b, c, d, e, f, g, h, i ); } template< typename Frame > - static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D, E, F, G, H, I ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( Frame::*fn )( A, B, C, D, E, F, G, H, I ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1881,7 +1881,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G, H, I, J ) > { typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; static const int NUM_ARGS = 10 + startArgc; - static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D, E, F, G, H, I, J ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( *fn )( A, B, C, D, E, F, G, H, I, J ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1896,7 +1896,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G, H, I, J ) > return _EngineConsoleThunkReturnValue( fn( a, b, c, d, e, f, g, h, i, j ) ); } template< typename Frame > - static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D, E, F, G, H, I, J ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I, J ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( Frame::*fn )( A, B, C, D, E, F, G, H, I, J ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I, J ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1916,7 +1916,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I, J ) > { typedef void ReturnType; static const int NUM_ARGS = 10 + startArgc; - static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D, E, F, G, H, I, J ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( *fn )( A, B, C, D, E, F, G, H, I, J ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1931,7 +1931,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I, J ) > fn( a, b, c, d, e, f, g, h, i, j ); } template< typename Frame > - static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D, E, F, G, H, I, J ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I, J ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( Frame::*fn )( A, B, C, D, E, F, G, H, I, J ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I, J ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1951,7 +1951,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G, H, I, J, K ) > { typedef typename _EngineConsoleThunkType< R >::ReturnType ReturnType; static const int NUM_ARGS = 11 + startArgc; - static ReturnType thunk( S32 argc, const char** argv, R ( *fn )( A, B, C, D, E, F, G, H, I, J, K ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J, K ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( *fn )( A, B, C, D, E, F, G, H, I, J, K ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J, K ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -1967,7 +1967,7 @@ struct _EngineConsoleThunk< startArgc, R( A, B, C, D, E, F, G, H, I, J, K ) > return _EngineConsoleThunkReturnValue( fn( a, b, c, d, e, f, g, h, i, j, k ) ); } template< typename Frame > - static ReturnType thunk( S32 argc, const char** argv, R ( Frame::*fn )( A, B, C, D, E, F, G, H, I, J, K ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I, J, K ) >& defaultArgs ) + static ReturnType thunk( S32 argc, ConsoleValueRef *argv, R ( Frame::*fn )( A, B, C, D, E, F, G, H, I, J, K ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I, J, K ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -1988,7 +1988,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I, J, K ) > { typedef void ReturnType; static const int NUM_ARGS = 11 + startArgc; - static void thunk( S32 argc, const char** argv, void ( *fn )( A, B, C, D, E, F, G, H, I, J, K ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J, K ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( *fn )( A, B, C, D, E, F, G, H, I, J, K ), const _EngineFunctionDefaultArguments< void( A, B, C, D, E, F, G, H, I, J, K ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.a ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.b ) ); @@ -2004,7 +2004,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I, J, K ) > fn( a, b, c, d, e, f, g, h, i, j, k ); } template< typename Frame > - static void thunk( S32 argc, const char** argv, void ( Frame::*fn )( A, B, C, D, E, F, G, H, I, J, K ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I, J, K ) >& defaultArgs ) + static void thunk( S32 argc, ConsoleValueRef *argv, void ( Frame::*fn )( A, B, C, D, E, F, G, H, I, J, K ) const, Frame* frame, const _EngineFunctionDefaultArguments< void( typename Frame::ObjectType*, A, B, C, D, E, F, G, H, I, J, K ) >& defaultArgs ) { A a = ( startArgc < argc ? EngineUnmarshallData< A >()( argv[ startArgc ] ) : A( defaultArgs.b ) ); B b = ( startArgc + 1 < argc ? EngineUnmarshallData< B >()( argv[ startArgc + 1 ] ) : B( defaultArgs.c ) ); @@ -2088,7 +2088,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I, J, K ) > ( void* ) &fn ## name, \ 0 \ ); \ - static _EngineConsoleThunkType< returnType >::ReturnType _ ## name ## caster( SimObject*, S32 argc, const char** argv ) \ + static _EngineConsoleThunkType< returnType >::ReturnType _ ## name ## caster( SimObject*, S32 argc, ConsoleValueRef *argv ) \ { \ return _EngineConsoleThunkType< returnType >::ReturnType( _EngineConsoleThunk< 1, returnType args >::thunk( \ argc, argv, &_fn ## name ## impl, _fn ## name ## DefaultArgs \ @@ -2168,7 +2168,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I, J, K ) > ( void* ) &fn ## className ## _ ## name, \ 0 \ ); \ - static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject* object, S32 argc, const char** argv ) \ + static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject* object, S32 argc, ConsoleValueRef *argv ) \ { \ _ ## className ## name ## frame frame; \ frame.object = static_cast< className* >( object ); \ @@ -2225,7 +2225,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I, J, K ) > ( void* ) &fn ## className ## _ ## name, \ 0 \ ); \ - static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject*, S32 argc, const char** argv )\ + static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject*, S32 argc, ConsoleValueRef *argv )\ { \ return _EngineConsoleThunkType< returnType >::ReturnType( _EngineConsoleThunk< 1, returnType args >::thunk( \ argc, argv, &_fn ## className ## name ## impl, _fn ## className ## name ## DefaultArgs \ @@ -2249,7 +2249,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I, J, K ) > #define DefineConsoleFunction( name, returnType, args, defaultArgs, usage ) \ static inline returnType _fn ## name ## impl args; \ static _EngineFunctionDefaultArguments< void args > _fn ## name ## DefaultArgs defaultArgs; \ - static _EngineConsoleThunkType< returnType >::ReturnType _ ## name ## caster( SimObject*, S32 argc, const char** argv ) \ + static _EngineConsoleThunkType< returnType >::ReturnType _ ## name ## caster( SimObject*, S32 argc, ConsoleValueRef *argv ) \ { \ return _EngineConsoleThunkType< returnType >::ReturnType( _EngineConsoleThunk< 1, returnType args >::thunk( \ argc, argv, &_fn ## name ## impl, _fn ## name ## DefaultArgs \ @@ -2274,7 +2274,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I, J, K ) > }; \ static _EngineFunctionDefaultArguments< _EngineMethodTrampoline< _ ## className ## name ## frame, void args >::FunctionType > \ _fn ## className ## name ## DefaultArgs defaultArgs; \ - static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject* object, S32 argc, const char** argv ) \ + static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject* object, S32 argc, ConsoleValueRef *argv ) \ { \ _ ## className ## name ## frame frame; \ frame.object = static_cast< className* >( object ); \ @@ -2296,7 +2296,7 @@ struct _EngineConsoleThunk< startArgc, void( A, B, C, D, E, F, G, H, I, J, K ) > #define DefineConsoleStaticMethod( className, name, returnType, args, defaultArgs, usage ) \ static inline returnType _fn ## className ## name ## impl args; \ static _EngineFunctionDefaultArguments< void args > _fn ## className ## name ## DefaultArgs defaultArgs; \ - static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject*, S32 argc, const char** argv )\ + static _EngineConsoleThunkType< returnType >::ReturnType _ ## className ## name ## caster( SimObject*, S32 argc, ConsoleValueRef *argv )\ { \ return _EngineConsoleThunkType< returnType >::ReturnType( _EngineConsoleThunk< 1, returnType args >::thunk( \ argc, argv, &_fn ## className ## name ## impl, _fn ## className ## name ## DefaultArgs \ @@ -2619,14 +2619,19 @@ struct _EngineConsoleCallbackHelper SimObject* mThis; S32 mArgc; - const char* mArgv[ MAX_ARGUMENTS + 2 ]; + ConsoleValueRef mArgv[ MAX_ARGUMENTS + 2 ]; const char* _exec() { if( mThis ) { // Cannot invoke callback until object has been registered - return mThis->isProperlyAdded() ? Con::execute( mThis, mArgc, mArgv ) : ""; + if (mThis->isProperlyAdded()) { + return Con::execute( mThis, mArgc, mArgv ); + } else { + Con::resetStackFrame(); // jamesu - we might have pushed some vars here + return ""; + } } else return Con::execute( mArgc, mArgv ); diff --git a/Engine/source/console/engineDoc.cpp b/Engine/source/console/engineDoc.cpp index f049b6cc3..be8872ed9 100644 --- a/Engine/source/console/engineDoc.cpp +++ b/Engine/source/console/engineDoc.cpp @@ -114,7 +114,7 @@ static void dumpVariable( Stream& stream, { // Skip variables defined in script. - if( entry->type < 0 ) + if( entry->value.type < 0 ) return; // Skip internals... don't export them. @@ -149,7 +149,7 @@ static void dumpVariable( Stream& stream, // Skip variables for which we can't decipher their type. - ConsoleBaseType* type = ConsoleBaseType::getType( entry->type ); + ConsoleBaseType* type = ConsoleBaseType::getType( entry->value.type ); if( !type ) { Con::errorf( "Can't find type for variable '%s'", entry->name ); diff --git a/Engine/source/console/sim.cpp b/Engine/source/console/sim.cpp index 974d2a044..aa41316a2 100644 --- a/Engine/source/console/sim.cpp +++ b/Engine/source/console/sim.cpp @@ -138,20 +138,20 @@ ConsoleDocFragment _spawnObject1( ConsoleFunction(spawnObject, S32, 3, 6, "spawnObject(class [, dataBlock, name, properties, script])" "@hide") { - String spawnClass(argv[1]); + String spawnClass((String)argv[1]); String spawnDataBlock; String spawnName; String spawnProperties; String spawnScript; if (argc >= 3) - spawnDataBlock = argv[2]; + spawnDataBlock = (String)argv[2]; if (argc >= 4) - spawnName = argv[3]; + spawnName = (String)argv[3]; if (argc >= 5) - spawnProperties = argv[4]; + spawnProperties = (String)argv[4]; if (argc >= 6) - spawnScript = argv[5]; + spawnScript = (String)argv[5]; SimObject* spawnObject = Sim::spawnObject(spawnClass, spawnDataBlock, spawnName, spawnProperties, spawnScript); diff --git a/Engine/source/console/simEvents.cpp b/Engine/source/console/simEvents.cpp index bf73ea7df..3fe32b2a5 100644 --- a/Engine/source/console/simEvents.cpp +++ b/Engine/source/console/simEvents.cpp @@ -28,30 +28,26 @@ // Stupid globals not declared in a header extern ExprEvalState gEvalState; -SimConsoleEvent::SimConsoleEvent(S32 argc, const char **argv, bool onObject) +SimConsoleEvent::SimConsoleEvent(S32 argc, ConsoleValueRef *argv, bool onObject) { mOnObject = onObject; mArgc = argc; - U32 totalSize = 0; - S32 i; - for(i = 0; i < argc; i++) - totalSize += dStrlen(argv[i]) + 1; - totalSize += sizeof(char *) * argc; - mArgv = (char **) dMalloc(totalSize); - char *argBase = (char *) &mArgv[argc]; - - for(i = 0; i < argc; i++) - { - mArgv[i] = argBase; - dStrcpy(mArgv[i], argv[i]); - argBase += dStrlen(argv[i]) + 1; + mArgv = new ConsoleValueRef[argc]; + for (int i=0; itype = ConsoleValue::TypeInternalString; + mArgv[i].value->init(); + mArgv[i].value->setStringValue((const char*)argv[i]); } } SimConsoleEvent::~SimConsoleEvent() { - dFree(mArgv); + for (int i=0; i( mArgv )); + Con::execute(object, mArgc, mArgv ); else { // Grab the function name. If '::' doesn't exist, then the schedule is // on a global function. - char* func = dStrstr( mArgv[0], (char*)"::" ); + char funcName[256]; + dStrncpy(funcName, (const char*)mArgv[0], 256); + char* func = dStrstr( funcName, (char*)"::" ); if( func ) { // Set the first colon to NULL, so we can reference the namespace. @@ -77,18 +75,18 @@ void SimConsoleEvent::process(SimObject* object) func += 2; // Lookup the namespace and function entry. - Namespace* ns = Namespace::find( StringTable->insert( mArgv[0] ) ); + Namespace* ns = Namespace::find( StringTable->insert( funcName ) ); if( ns ) { Namespace::Entry* nse = ns->lookup( StringTable->insert( func ) ); if( nse ) // Execute. - nse->execute( mArgc, (const char**)mArgv, &gEvalState ); + nse->execute( mArgc, mArgv, &gEvalState ); } } else - Con::execute(mArgc, const_cast( mArgv )); + Con::execute(mArgc, mArgv ); } } @@ -122,7 +120,7 @@ const char *SimConsoleThreadExecCallback::waitForResult() //----------------------------------------------------------------------------- -SimConsoleThreadExecEvent::SimConsoleThreadExecEvent(S32 argc, const char **argv, bool onObject, SimConsoleThreadExecCallback *callback) : +SimConsoleThreadExecEvent::SimConsoleThreadExecEvent(S32 argc, ConsoleValueRef *argv, bool onObject, SimConsoleThreadExecCallback *callback) : SimConsoleEvent(argc, argv, onObject), cb(callback) { } @@ -131,9 +129,9 @@ void SimConsoleThreadExecEvent::process(SimObject* object) { const char *retVal; if(mOnObject) - retVal = Con::execute(object, mArgc, const_cast( mArgv )); + retVal = Con::execute(object, mArgc, mArgv); else - retVal = Con::execute(mArgc, const_cast( mArgv )); + retVal = Con::execute(mArgc, mArgv); if(cb) cb->handleCallback(retVal); diff --git a/Engine/source/console/simEvents.h b/Engine/source/console/simEvents.h index f325591db..360cf27ec 100644 --- a/Engine/source/console/simEvents.h +++ b/Engine/source/console/simEvents.h @@ -34,6 +34,7 @@ // Forward Refs class SimObject; class Semaphore; +class ConsoleValue; /// Represents a queued event in the sim. /// @@ -82,6 +83,8 @@ public: virtual void process(SimObject *object)=0; }; +class ConsoleValueRef; + /// Implementation of schedule() function. /// /// This allows you to set a console function to be @@ -90,7 +93,7 @@ class SimConsoleEvent : public SimEvent { protected: S32 mArgc; - char **mArgv; + ConsoleValueRef *mArgv; bool mOnObject; public: @@ -107,7 +110,7 @@ public: /// /// @see Con::execute(S32 argc, const char *argv[]) /// @see Con::execute(SimObject *object, S32 argc, const char *argv[]) - SimConsoleEvent(S32 argc, const char **argv, bool onObject); + SimConsoleEvent(S32 argc, ConsoleValueRef *argv, bool onObject); ~SimConsoleEvent(); virtual void process(SimObject *object); @@ -131,7 +134,7 @@ class SimConsoleThreadExecEvent : public SimConsoleEvent SimConsoleThreadExecCallback *cb; public: - SimConsoleThreadExecEvent(S32 argc, const char **argv, bool onObject, SimConsoleThreadExecCallback *callback); + SimConsoleThreadExecEvent(S32 argc, ConsoleValueRef *argv, bool onObject, SimConsoleThreadExecCallback *callback); virtual void process(SimObject *object); }; diff --git a/Engine/source/console/simObject.cpp b/Engine/source/console/simObject.cpp index 3385f3a91..dc46a6cb3 100644 --- a/Engine/source/console/simObject.cpp +++ b/Engine/source/console/simObject.cpp @@ -123,7 +123,7 @@ SimObject::~SimObject() //----------------------------------------------------------------------------- -bool SimObject::processArguments(S32 argc, const char**argv) +bool SimObject::processArguments(S32 argc, ConsoleValueRef *argv) { return argc == 0; } diff --git a/Engine/source/console/simObject.h b/Engine/source/console/simObject.h index 541c15bbc..58c69117a 100644 --- a/Engine/source/console/simObject.h +++ b/Engine/source/console/simObject.h @@ -540,7 +540,7 @@ class SimObject: public ConsoleObject virtual ~SimObject(); - virtual bool processArguments(S32 argc, const char **argv); ///< Process constructor options. (ie, new SimObject(1,2,3)) + virtual bool processArguments(S32 argc, ConsoleValueRef *argv); ///< Process constructor options. (ie, new SimObject(1,2,3)) /// @} diff --git a/Engine/source/console/simObjectList.cpp b/Engine/source/console/simObjectList.cpp index 4efc055a3..be4b10b22 100644 --- a/Engine/source/console/simObjectList.cpp +++ b/Engine/source/console/simObjectList.cpp @@ -117,7 +117,7 @@ S32 QSORT_CALLBACK SimObjectList::_callbackSort( const void *a, const void *b ) static char idB[64]; dSprintf( idB, sizeof( idB ), "%d", objB->getId() ); - return dAtoi( Con::executef( smSortScriptCallbackFn, idA, idB ) ); + return dAtoi( Con::executef( (const char*)smSortScriptCallbackFn, idA, idB ) ); } void SimObjectList::scriptSort( const String &scriptCallback ) diff --git a/Engine/source/console/simPersistSet.cpp b/Engine/source/console/simPersistSet.cpp index ec3151157..82dd6e856 100644 --- a/Engine/source/console/simPersistSet.cpp +++ b/Engine/source/console/simPersistSet.cpp @@ -46,7 +46,7 @@ SimPersistSet::SimPersistSet() //----------------------------------------------------------------------------- -bool SimPersistSet::processArguments( S32 argc, const char** argv ) +bool SimPersistSet::processArguments( S32 argc, ConsoleValueRef *argv ) { for( U32 i = 0; i < argc; ++ i ) { diff --git a/Engine/source/console/simPersistSet.h b/Engine/source/console/simPersistSet.h index 6ac534762..d1769cff0 100644 --- a/Engine/source/console/simPersistSet.h +++ b/Engine/source/console/simPersistSet.h @@ -58,7 +58,7 @@ class SimPersistSet : public SimSet // SimSet. virtual void addObject( SimObject* ); virtual void write( Stream &stream, U32 tabStop, U32 flags = 0 ); - virtual bool processArguments( S32 argc, const char** argv ); + virtual bool processArguments( S32 argc, ConsoleValueRef *argv ); DECLARE_CONOBJECT( SimPersistSet ); DECLARE_CATEGORY( "Console" ); diff --git a/Engine/source/console/simSet.cpp b/Engine/source/console/simSet.cpp index 441b8d164..6f72319fe 100644 --- a/Engine/source/console/simSet.cpp +++ b/Engine/source/console/simSet.cpp @@ -228,11 +228,11 @@ void SimSet::scriptSort( const String &scriptCallbackFn ) //----------------------------------------------------------------------------- -void SimSet::callOnChildren( const String &method, S32 argc, const char *argv[], bool executeOnChildGroups ) +void SimSet::callOnChildren( const String &method, S32 argc, ConsoleValueRef argv[], bool executeOnChildGroups ) { // Prep the arguments for the console exec... // Make sure and leave args[1] empty. - const char* args[21]; + ConsoleValueRef args[21]; args[0] = method.c_str(); for (S32 i = 0; i < argc; i++) args[i + 2] = argv[i]; @@ -834,7 +834,7 @@ SimGroup* SimGroup::deepClone() //----------------------------------------------------------------------------- -bool SimGroup::processArguments(S32, const char **) +bool SimGroup::processArguments(S32, ConsoleValueRef *argv) { return true; } @@ -973,7 +973,7 @@ ConsoleMethod( SimSet, callOnChildren, void, 3, 0, "@note This method recurses into all SimSets that are children to the set.\n\n" "@see callOnChildrenNoRecurse" ) { - object->callOnChildren( argv[2], argc - 3, argv + 3 ); + object->callOnChildren( (const char*)argv[2], argc - 3, argv + 3 ); } //----------------------------------------------------------------------------- @@ -985,7 +985,7 @@ ConsoleMethod( SimSet, callOnChildrenNoRecurse, void, 3, 0, "@note This method does not recurse into child SimSets.\n\n" "@see callOnChildren" ) { - object->callOnChildren( argv[2], argc - 3, argv + 3, false ); + object->callOnChildren( (const char*)argv[2], argc - 3, argv + 3, false ); } //----------------------------------------------------------------------------- @@ -1121,7 +1121,7 @@ DefineEngineMethod( SimSet, pushToBack, void, ( SimObject* obj ),, ConsoleMethod( SimSet, sort, void, 3, 3, "( string callbackFunction ) Sort the objects in the set using the given comparison function.\n" "@param callbackFunction Name of a function that takes two object arguments A and B and returns -1 if A is less, 1 if B is less, and 0 if both are equal." ) { - object->scriptSort( argv[2] ); + object->scriptSort( (const char*)argv[2] ); } //----------------------------------------------------------------------------- diff --git a/Engine/source/console/simSet.h b/Engine/source/console/simSet.h index baf39c787..f5e634262 100644 --- a/Engine/source/console/simSet.h +++ b/Engine/source/console/simSet.h @@ -214,7 +214,7 @@ class SimSet: public SimObject /// @} - void callOnChildren( const String &method, S32 argc, const char *argv[], bool executeOnChildGroups = true ); + void callOnChildren( const String &method, S32 argc, ConsoleValueRef argv[], bool executeOnChildGroups = true ); /// Return the number of objects in this set as well as all sets that are contained /// in this set and its children. @@ -434,7 +434,7 @@ class SimGroup: public SimSet virtual SimObject* findObject(const char* name); virtual void onRemove(); - virtual bool processArguments( S32 argc, const char** argv ); + virtual bool processArguments( S32 argc, ConsoleValueRef *argv ); DECLARE_CONOBJECT( SimGroup ); }; diff --git a/Engine/source/console/stringStack.cpp b/Engine/source/console/stringStack.cpp index 6061747d4..1473d614c 100644 --- a/Engine/source/console/stringStack.cpp +++ b/Engine/source/console/stringStack.cpp @@ -20,22 +20,180 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include +#include "console/consoleInternal.h" #include "console/stringStack.h" -void StringStack::getArgcArgv(StringTableEntry name, U32 *argc, const char ***in_argv, bool popStackFrame /* = false */) -{ - U32 startStack = mFrameOffsets[mNumFrames-1] + 1; - U32 argCount = getMin(mStartStackSize - startStack, (U32)MaxArgs - 1); - *in_argv = mArgV; - mArgV[0] = name; +void ConsoleValueStack::getArgcArgv(StringTableEntry name, U32 *argc, ConsoleValueRef **in_argv, bool popStackFrame /* = false */) +{ + U32 startStack = mStackFrames[mFrame-1]; + U32 argCount = getMin(mStackPos - startStack, (U32)MaxArgs - 1); + + *in_argv = mArgv; + mArgv[0] = name; - for(U32 i = 0; i < argCount; i++) - mArgV[i+1] = mBuffer + mStartOffsets[startStack + i]; + for(U32 i = 0; i < argCount; i++) { + ConsoleValueRef *ref = &mArgv[i+1]; + ref->value = &mStack[startStack + i]; + ref->stringStackValue = NULL; + } argCount++; *argc = argCount; if(popStackFrame) popFrame(); -} \ No newline at end of file +} + +ConsoleValueStack::ConsoleValueStack() : +mFrame(0), +mStackPos(0) +{ + for (int i=0; itype) + { + case ConsoleValue::TypeInternalInt: + mStack[mStackPos++].setIntValue((S32)variable->getIntValue()); + case ConsoleValue::TypeInternalFloat: + mStack[mStackPos++].setFloatValue((F32)variable->getFloatValue()); + default: + mStack[mStackPos++].setStackStringValue(variable->getStringValue()); + } +} + +void ConsoleValueStack::pushValue(ConsoleValue &variable) +{ + if (mStackPos == ConsoleValueStack::MaxStackDepth) { + AssertFatal(false, "Console Value Stack is empty"); + return; + } + + switch (variable.type) + { + case ConsoleValue::TypeInternalInt: + mStack[mStackPos++].setIntValue((S32)variable.getIntValue()); + case ConsoleValue::TypeInternalFloat: + mStack[mStackPos++].setFloatValue((F32)variable.getFloatValue()); + default: + mStack[mStackPos++].setStringValue(variable.getStringValue()); + } +} + +ConsoleValue *ConsoleValueStack::pushString(const char *value) +{ + if (mStackPos == ConsoleValueStack::MaxStackDepth) { + AssertFatal(false, "Console Value Stack is empty"); + return NULL; + } + + //Con::printf("[%i]CSTK pushString %s", mStackPos, value); + + mStack[mStackPos++].setStringValue(value); + return &mStack[mStackPos-1]; +} + +ConsoleValue *ConsoleValueStack::pushStackString(const char *value) +{ + if (mStackPos == ConsoleValueStack::MaxStackDepth) { + AssertFatal(false, "Console Value Stack is empty"); + return NULL; + } + + //Con::printf("[%i]CSTK pushString %s", mStackPos, value); + + mStack[mStackPos++].setStackStringValue(value); + return &mStack[mStackPos-1]; +} + +ConsoleValue *ConsoleValueStack::pushUINT(U32 value) +{ + if (mStackPos == ConsoleValueStack::MaxStackDepth) { + AssertFatal(false, "Console Value Stack is empty"); + return NULL; + } + + //Con::printf("[%i]CSTK pushUINT %i", mStackPos, value); + + mStack[mStackPos++].setIntValue(value); + return &mStack[mStackPos-1]; +} + +ConsoleValue *ConsoleValueStack::pushFLT(float value) +{ + if (mStackPos == ConsoleValueStack::MaxStackDepth) { + AssertFatal(false, "Console Value Stack is empty"); + return NULL; + } + + //Con::printf("[%i]CSTK pushFLT %f", mStackPos, value); + + mStack[mStackPos++].setFloatValue(value); + return &mStack[mStackPos-1]; +} + +static ConsoleValue gNothing; + +ConsoleValue* ConsoleValueStack::pop() +{ + if (mStackPos == 0) { + AssertFatal(false, "Console Value Stack is empty"); + return &gNothing; + } + + return &mStack[--mStackPos]; +} + +void ConsoleValueStack::pushFrame() +{ + //Con::printf("CSTK pushFrame"); + mStackFrames[mFrame++] = mStackPos; +} + +void ConsoleValueStack::resetFrame() +{ + if (mFrame == 0) { + mStackPos = 0; + return; + } + + U32 start = mStackFrames[mFrame-1]; + //for (U32 i=start; iwriteObject( obj, (const U8*)objName ); } diff --git a/Engine/source/environment/editors/guiMeshRoadEditorCtrl.cpp b/Engine/source/environment/editors/guiMeshRoadEditorCtrl.cpp index ac4e4edc5..24fd84405 100644 --- a/Engine/source/environment/editors/guiMeshRoadEditorCtrl.cpp +++ b/Engine/source/environment/editors/guiMeshRoadEditorCtrl.cpp @@ -1040,7 +1040,7 @@ void GuiMeshRoadEditorCtrl::setMode( String mode, bool sourceShortcut = false ) mMode = mode; if( sourceShortcut ) - Con::executef( this, "paletteSync", mode ); + Con::executef( this, "paletteSync", (const char*)mode ); } void GuiMeshRoadEditorCtrl::setSelectedRoad( MeshRoad *road ) diff --git a/Engine/source/environment/editors/guiRiverEditorCtrl.cpp b/Engine/source/environment/editors/guiRiverEditorCtrl.cpp index 527c53b3a..26fabc99b 100644 --- a/Engine/source/environment/editors/guiRiverEditorCtrl.cpp +++ b/Engine/source/environment/editors/guiRiverEditorCtrl.cpp @@ -1181,7 +1181,7 @@ void GuiRiverEditorCtrl::setMode( String mode, bool sourceShortcut = false ) mMode = mode; if( sourceShortcut ) - Con::executef( this, "paletteSync", mode ); + Con::executef( this, "paletteSync", mode.utf8() ); } void GuiRiverEditorCtrl::setSelectedRiver( River *river ) diff --git a/Engine/source/environment/editors/guiRoadEditorCtrl.cpp b/Engine/source/environment/editors/guiRoadEditorCtrl.cpp index a890c6ce2..a5c32abe5 100644 --- a/Engine/source/environment/editors/guiRoadEditorCtrl.cpp +++ b/Engine/source/environment/editors/guiRoadEditorCtrl.cpp @@ -945,7 +945,7 @@ void GuiRoadEditorCtrl::setMode( String mode, bool sourceShortcut = false ) mMode = mode; if( sourceShortcut ) - Con::executef( this, "paletteSync", mode ); + Con::executef( this, "paletteSync", mode.utf8() ); } void GuiRoadEditorCtrl::setSelectedRoad( DecalRoad *road ) diff --git a/Engine/source/environment/waterObject.cpp b/Engine/source/environment/waterObject.cpp index 470d27b8a..563596b6d 100644 --- a/Engine/source/environment/waterObject.cpp +++ b/Engine/source/environment/waterObject.cpp @@ -406,7 +406,7 @@ void WaterObject::inspectPostApply() setMaskBits( UpdateMask | WaveMask | TextureMask | SoundMask ); } -bool WaterObject::processArguments( S32 argc, const char **argv ) +bool WaterObject::processArguments( S32 argc, ConsoleValueRef *argv ) { if( typeid( *this ) == typeid( WaterObject ) ) { diff --git a/Engine/source/environment/waterObject.h b/Engine/source/environment/waterObject.h index 06c5e7dd0..fc094e225 100644 --- a/Engine/source/environment/waterObject.h +++ b/Engine/source/environment/waterObject.h @@ -156,7 +156,7 @@ public: virtual bool onAdd(); virtual void onRemove(); virtual void inspectPostApply(); - virtual bool processArguments(S32 argc, const char **argv); + virtual bool processArguments(S32 argc, ConsoleValueRef *argv); // NetObject virtual U32 packUpdate( NetConnection * conn, U32 mask, BitStream *stream ); diff --git a/Engine/source/gui/controls/guiMaterialCtrl.cpp b/Engine/source/gui/controls/guiMaterialCtrl.cpp index 124070b57..e5fe887f5 100644 --- a/Engine/source/gui/controls/guiMaterialCtrl.cpp +++ b/Engine/source/gui/controls/guiMaterialCtrl.cpp @@ -169,5 +169,5 @@ void GuiMaterialCtrl::onRender( Point2I offset, const RectI &updateRect ) ConsoleMethod( GuiMaterialCtrl, setMaterial, bool, 3, 3, "( string materialName )" "Set the material to be displayed in the control." ) { - return object->setMaterial( argv[2] ); + return object->setMaterial( (const char*)argv[2] ); } diff --git a/Engine/source/gui/controls/guiTreeViewCtrl.cpp b/Engine/source/gui/controls/guiTreeViewCtrl.cpp index 5e71b7188..390cbfcae 100644 --- a/Engine/source/gui/controls/guiTreeViewCtrl.cpp +++ b/Engine/source/gui/controls/guiTreeViewCtrl.cpp @@ -4928,7 +4928,7 @@ ConsoleMethod( GuiTreeViewCtrl, setItemTooltip, void, 4, 4, "( int id, string te return; } - item->mTooltip = argv[ 3 ]; + item->mTooltip = (String)argv[ 3 ]; } ConsoleMethod( GuiTreeViewCtrl, setItemImages, void, 5, 5, "( int id, int normalImage, int expandedImage ) - Sets the normal and expanded images to show for the given item." ) diff --git a/Engine/source/gui/core/guiControl.cpp b/Engine/source/gui/core/guiControl.cpp index 2f5f41d99..40293b710 100644 --- a/Engine/source/gui/core/guiControl.cpp +++ b/Engine/source/gui/core/guiControl.cpp @@ -308,7 +308,7 @@ void GuiControl::initPersistFields() //----------------------------------------------------------------------------- -bool GuiControl::processArguments(S32 argc, const char **argv) +bool GuiControl::processArguments(S32 argc, ConsoleValueRef *argv) { // argv[0] - The GuiGroup to add this control to when it's created. // this is an optional parameter that may be specified at diff --git a/Engine/source/gui/core/guiControl.h b/Engine/source/gui/core/guiControl.h index 0e798935f..afd436dc1 100644 --- a/Engine/source/gui/core/guiControl.h +++ b/Engine/source/gui/core/guiControl.h @@ -330,7 +330,7 @@ class GuiControl : public SimGroup GuiControl(); virtual ~GuiControl(); - virtual bool processArguments(S32 argc, const char **argv); + virtual bool processArguments(S32 argc, ConsoleValueRef *argv); static void initPersistFields(); static void consoleInit(); diff --git a/Engine/source/gui/editor/guiFilterCtrl.cpp b/Engine/source/gui/editor/guiFilterCtrl.cpp index 4555f3ee5..ae9a51cca 100644 --- a/Engine/source/gui/editor/guiFilterCtrl.cpp +++ b/Engine/source/gui/editor/guiFilterCtrl.cpp @@ -83,10 +83,9 @@ ConsoleMethod( GuiFilterCtrl, setValue, void, 3, 20, "(f1, f2, ...)" { Filter filter; - argc -= 2; - argv += 2; + StringStackWrapper args(argc - 2, argv + 2); - filter.set(argc, argv); + filter.set(args.count(), args); object->set(filter); } diff --git a/Engine/source/gui/editor/inspector/variableInspector.cpp b/Engine/source/gui/editor/inspector/variableInspector.cpp index 62993d600..1a061e8a0 100644 --- a/Engine/source/gui/editor/inspector/variableInspector.cpp +++ b/Engine/source/gui/editor/inspector/variableInspector.cpp @@ -63,5 +63,5 @@ void GuiVariableInspector::loadVars( String searchStr ) ConsoleMethod( GuiVariableInspector, loadVars, void, 3, 3, "loadVars( searchString )" ) { - object->loadVars( argv[2] ); + object->loadVars( (const char*)argv[2] ); } \ No newline at end of file diff --git a/Engine/source/gui/worldEditor/creator.cpp b/Engine/source/gui/worldEditor/creator.cpp index f1d786fb0..d2c8ce1de 100644 --- a/Engine/source/gui/worldEditor/creator.cpp +++ b/Engine/source/gui/worldEditor/creator.cpp @@ -264,7 +264,7 @@ ConsoleMethod( CreatorTree, fileNameMatch, bool, 5, 5, "(string world, string ty if(dToupper(argv[4][0]) != dToupper(argv[2][0])) return(false); - return(!dStrnicmp(argv[4]+1, argv[3], typeLen)); + return(!dStrnicmp(((const char*)argv[4])+1, argv[3], typeLen)); } ConsoleMethod( CreatorTree, getSelected, S32, 2, 2, "Return a handle to the currently selected item.") diff --git a/Engine/source/gui/worldEditor/editorIconRegistry.cpp b/Engine/source/gui/worldEditor/editorIconRegistry.cpp index 331240bc8..1a80dc725 100644 --- a/Engine/source/gui/worldEditor/editorIconRegistry.cpp +++ b/Engine/source/gui/worldEditor/editorIconRegistry.cpp @@ -175,7 +175,7 @@ ConsoleStaticMethod( EditorIconRegistry, add, void, 3, 4, "( String className, S if ( argc > 3 ) overwrite = dAtob( argv[3] ); - gEditorIcons.add( argv[1], argv[2], overwrite ); + gEditorIcons.add( (const char*)argv[1], (const char*)argv[2], overwrite ); } ConsoleStaticMethod( EditorIconRegistry, loadFromPath, void, 2, 3, "( String imagePath [, bool overwrite = true] )" @@ -185,7 +185,7 @@ ConsoleStaticMethod( EditorIconRegistry, loadFromPath, void, 2, 3, "( String ima if ( argc > 2 ) overwrite = dAtob( argv[2] ); - gEditorIcons.loadFromPath( argv[1], overwrite ); + gEditorIcons.loadFromPath( (const char*)argv[1], overwrite ); } ConsoleStaticMethod( EditorIconRegistry, clear, void, 1, 1, "" diff --git a/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp b/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp index 93192a155..2bcf0c822 100644 --- a/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp +++ b/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp @@ -782,7 +782,7 @@ void GuiDecalEditorCtrl::setMode( String mode, bool sourceShortcut = false ) mMode = mode; if( sourceShortcut ) - Con::executef( this, "paletteSync", mMode ); + Con::executef( this, "paletteSync", (const char*)mMode ); } ConsoleMethod( GuiDecalEditorCtrl, deleteSelectedDecal, void, 2, 2, "deleteSelectedDecal()" ) @@ -894,7 +894,7 @@ ConsoleMethod( GuiDecalEditorCtrl, getSelectionCount, S32, 2, 2, "" ) ConsoleMethod( GuiDecalEditorCtrl, retargetDecalDatablock, void, 4, 4, "" ) { if( dStrcmp( argv[2], "" ) != 0 && dStrcmp( argv[3], "" ) != 0 ) - object->retargetDecalDatablock( argv[2], argv[3] ); + object->retargetDecalDatablock( (const char*)argv[2], (const char*)argv[3] ); } void GuiDecalEditorCtrl::setGizmoFocus( DecalInstance * decalInstance ) diff --git a/Engine/source/gui/worldEditor/terrainEditor.cpp b/Engine/source/gui/worldEditor/terrainEditor.cpp index 1b2239911..ec032e5f9 100644 --- a/Engine/source/gui/worldEditor/terrainEditor.cpp +++ b/Engine/source/gui/worldEditor/terrainEditor.cpp @@ -2714,7 +2714,7 @@ ConsoleMethod(TerrainEditor, updateMaterial, bool, 4, 4, if ( index >= terr->getMaterialCount() ) return false; - terr->updateMaterial( index, argv[3] ); + terr->updateMaterial( index, (const char*)argv[3] ); object->setDirty(); @@ -2729,7 +2729,7 @@ ConsoleMethod(TerrainEditor, addMaterial, S32, 3, 3, if ( !terr ) return false; - terr->addMaterial( argv[2] ); + terr->addMaterial( (const char*)argv[2] ); object->setDirty(); diff --git a/Engine/source/gui/worldEditor/worldEditor.cpp b/Engine/source/gui/worldEditor/worldEditor.cpp index 9f4566944..21948ceff 100644 --- a/Engine/source/gui/worldEditor/worldEditor.cpp +++ b/Engine/source/gui/worldEditor/worldEditor.cpp @@ -2758,7 +2758,7 @@ void WorldEditor::initPersistFields() //------------------------------------------------------------------------------ // These methods are needed for the console interfaces. -void WorldEditor::ignoreObjClass( U32 argc, const char **argv ) +void WorldEditor::ignoreObjClass( U32 argc, ConsoleValueRef *argv ) { for(S32 i = 2; i < argc; i++) { @@ -3542,7 +3542,7 @@ void WorldEditor::colladaExportSelection( const String &path ) ConsoleMethod( WorldEditor, colladaExportSelection, void, 3, 3, "( String path ) - Export the combined geometry of all selected objects to the specified path in collada format." ) { - object->colladaExportSelection( argv[2] ); + object->colladaExportSelection( (const char*)argv[2] ); } void WorldEditor::makeSelectionPrefab( const char *filename ) diff --git a/Engine/source/gui/worldEditor/worldEditor.h b/Engine/source/gui/worldEditor/worldEditor.h index 387a2d810..add2e5d4f 100644 --- a/Engine/source/gui/worldEditor/worldEditor.h +++ b/Engine/source/gui/worldEditor/worldEditor.h @@ -76,7 +76,7 @@ class WorldEditor : public EditTSCtrl Point3F p2; }; - void ignoreObjClass(U32 argc, const char** argv); + void ignoreObjClass(U32 argc, ConsoleValueRef* argv); void clearIgnoreList(); static bool setObjectsUseBoxCenter( void *object, const char *index, const char *data ) { static_cast(object)->setObjectsUseBoxCenter( dAtob( data ) ); return false; }; diff --git a/Engine/source/materials/materialManager.cpp b/Engine/source/materials/materialManager.cpp index 7456b9f38..bc30791da 100644 --- a/Engine/source/materials/materialManager.cpp +++ b/Engine/source/materials/materialManager.cpp @@ -465,7 +465,7 @@ ConsoleFunction( addMaterialMapping, void, 3, 3, "(string texName, string matNam "block or interior surface using the associated texture.\n\n" "@ingroup Materials") { - MATMGR->mapMaterial(argv[1],argv[2]); + MATMGR->mapMaterial((const char*)argv[1],(const char*)argv[2]); } ConsoleFunction( getMaterialMapping, const char*, 2, 2, "(string texName)\n" @@ -474,7 +474,7 @@ ConsoleFunction( getMaterialMapping, const char*, 2, 2, "(string texName)\n" "@param texName Name of the texture\n\n" "@ingroup Materials") { - return MATMGR->getMapEntry(argv[1]).c_str(); + return MATMGR->getMapEntry((const char*)argv[1]).c_str(); } ConsoleFunction( dumpMaterialInstances, void, 1, 1, diff --git a/Engine/source/sfx/sfxSource.cpp b/Engine/source/sfx/sfxSource.cpp index f6f5bf600..e3e813e47 100644 --- a/Engine/source/sfx/sfxSource.cpp +++ b/Engine/source/sfx/sfxSource.cpp @@ -316,7 +316,7 @@ void SFXSource::initPersistFields() //----------------------------------------------------------------------------- -bool SFXSource::processArguments( S32 argc, const char **argv ) +bool SFXSource::processArguments( S32 argc, ConsoleValueRef *argv ) { // Don't allow subclasses of this to be created via script. Force // usage of the SFXSystem functions. diff --git a/Engine/source/sfx/sfxSource.h b/Engine/source/sfx/sfxSource.h index 83e31d0d2..c704ed7c5 100644 --- a/Engine/source/sfx/sfxSource.h +++ b/Engine/source/sfx/sfxSource.h @@ -382,7 +382,7 @@ class SFXSource : public SimGroup /// We overload this to disable creation of /// a source via script 'new'. - virtual bool processArguments( S32 argc, const char **argv ); + virtual bool processArguments( S32 argc, ConsoleValueRef *argv ); // Console getters/setters. static bool _setDescription( void *obj, const char *index, const char *data ); diff --git a/Engine/source/sfx/sfxTrack.cpp b/Engine/source/sfx/sfxTrack.cpp index 2461b39b7..dde11bb26 100644 --- a/Engine/source/sfx/sfxTrack.cpp +++ b/Engine/source/sfx/sfxTrack.cpp @@ -86,7 +86,7 @@ void SFXTrack::initPersistFields() //----------------------------------------------------------------------------- -bool SFXTrack::processArguments( S32 argc, const char **argv ) +bool SFXTrack::processArguments( S32 argc, ConsoleValueRef *argv ) { if( typeid( *this ) == typeid( SFXTrack ) ) { diff --git a/Engine/source/sfx/sfxTrack.h b/Engine/source/sfx/sfxTrack.h index 2a1d68614..13221dbb8 100644 --- a/Engine/source/sfx/sfxTrack.h +++ b/Engine/source/sfx/sfxTrack.h @@ -57,7 +57,7 @@ class SFXTrack : public SimDataBlock StringTableEntry mParameters[ MaxNumParameters ]; /// Overload this to disable direct instantiation of this class via script 'new'. - virtual bool processArguments( S32 argc, const char **argv ); + virtual bool processArguments( S32 argc, ConsoleValueRef *argv ); public: diff --git a/Engine/source/sim/actionMap.cpp b/Engine/source/sim/actionMap.cpp index 175ee875b..c7e7665a1 100644 --- a/Engine/source/sim/actionMap.cpp +++ b/Engine/source/sim/actionMap.cpp @@ -1746,7 +1746,8 @@ static ConsoleDocFragment _ActionMapbind2( ConsoleMethod( ActionMap, bind, bool, 5, 10, "actionMap.bind( device, action, [modifier spec, mod...], command )" "@hide") { - return object->processBind( argc - 2, argv + 2, NULL ); + StringStackWrapper args(argc - 2, argv + 2); + return object->processBind( args.count(), args, NULL ); } static ConsoleDocFragment _ActionMapbindObj1( @@ -1801,7 +1802,9 @@ ConsoleMethod( ActionMap, bindObj, bool, 6, 11, "(device, action, [modifier spec return false; } - return object->processBind( argc - 3, argv + 2, simObject ); + StringStackWrapper args(argc - 3, argv + 2); + + return object->processBind( args.count(), args, simObject ); } //------------------------------------------------------------------------------ diff --git a/Engine/source/sim/netConnection.cpp b/Engine/source/sim/netConnection.cpp index ee1815e82..469d20f60 100644 --- a/Engine/source/sim/netConnection.cpp +++ b/Engine/source/sim/netConnection.cpp @@ -433,6 +433,9 @@ NetConnection::NetConnection() // Disable starting a new journal recording or playback from here on Journal::Disable(); + + // jamesu - netAddress is not set + dMemset(&mNetAddress, '\0', sizeof(NetAddress)); } NetConnection::~NetConnection() diff --git a/Engine/source/terrain/terrExport.cpp b/Engine/source/terrain/terrExport.cpp index 378452085..09fbbca19 100644 --- a/Engine/source/terrain/terrExport.cpp +++ b/Engine/source/terrain/terrExport.cpp @@ -141,7 +141,7 @@ ConsoleMethod( TerrainBlock, exportHeightMap, bool, 3, 4, "(string filename, [st UTF8 fileName[1024]; String format = "png"; if( argc > 3 ) - format = argv[ 3 ]; + format = (String)argv[ 3 ]; Con::expandScriptFilename( fileName, sizeof( fileName ), argv[2] ); @@ -153,7 +153,7 @@ ConsoleMethod( TerrainBlock, exportLayerMaps, bool, 3, 4, "(string filePrefix, [ UTF8 filePrefix[1024]; String format = "png"; if( argc > 3 ) - format = argv[3]; + format = (String)argv[3]; Con::expandScriptFilename( filePrefix, sizeof( filePrefix ), argv[2] ); diff --git a/Engine/source/ts/collada/colladaImport.cpp b/Engine/source/ts/collada/colladaImport.cpp index 7692dcc77..f22ee4662 100644 --- a/Engine/source/ts/collada/colladaImport.cpp +++ b/Engine/source/ts/collada/colladaImport.cpp @@ -145,7 +145,7 @@ ConsoleFunction( enumColladaForImport, bool, 3, 3, // Check if a cached DTS is available => no need to import the collada file // if we can load the DTS instead - Torque::Path path(argv[1]); + Torque::Path path((const char*)argv[1]); if (ColladaShapeLoader::canLoadCachedDTS(path)) return false; diff --git a/Engine/source/ts/collada/colladaLights.cpp b/Engine/source/ts/collada/colladaLights.cpp index 1388278d0..62ea45c50 100644 --- a/Engine/source/ts/collada/colladaLights.cpp +++ b/Engine/source/ts/collada/colladaLights.cpp @@ -162,7 +162,7 @@ ConsoleFunction( loadColladaLights, bool, 2, 4, "@ingroup Editors\n" "@internal") { - Torque::Path path(argv[1]); + Torque::Path path((const char*)argv[1]); // Optional group to add the lights to. Create if it does not exist, and use // the MissionGroup if not specified. diff --git a/Engine/source/util/settings.cpp b/Engine/source/util/settings.cpp index 19de55673..bb4992d2a 100644 --- a/Engine/source/util/settings.cpp +++ b/Engine/source/util/settings.cpp @@ -488,9 +488,9 @@ ConsoleMethod(Settings, findFirstValue, const char*, 2, 5, "settingObj.findFirst if( argc == 3 ) return object->findFirstValue( argv[2] ); else if( argc == 4 ) - return object->findFirstValue( argv[2], argv[3] ); + return object->findFirstValue( argv[2], dAtob(argv[3]) ); else if( argc == 5 ) - return object->findFirstValue( argv[2], argv[3], argv[4] ); + return object->findFirstValue( argv[2], dAtob(argv[3]), dAtob(argv[4]) ); else return ""; } @@ -691,8 +691,8 @@ ConsoleMethod(Settings, remove, void, 3, 4, "settingObj.remove(settingName, incl } else if(argc == 4) { - object->remove( argv[2], argv[3] ); - object->remove( argv[2], argv[3] ); + object->remove( argv[2], dAtob(argv[3]) ); + object->remove( argv[2], dAtob(argv[3]) ); } } diff --git a/Engine/source/util/undo.cpp b/Engine/source/util/undo.cpp index d10cf6496..3a8e52cff 100644 --- a/Engine/source/util/undo.cpp +++ b/Engine/source/util/undo.cpp @@ -566,7 +566,7 @@ ConsoleMethod( UndoManager, pushCompound, const char*, 2, 3, "( string name=\"\" { String name; if( argc > 2 ) - name = argv[ 2 ]; + name = (String)argv[ 2 ]; CompoundUndoAction* action = object->pushCompound( name ); if( !action ) From e99eadd61fcf19a9e710e485bcf6f79e6a92f894 Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Thu, 4 Oct 2012 17:58:43 +0100 Subject: [PATCH 002/317] Optimize variable-to-variable assignment --- Engine/source/console/ast.h | 3 +- Engine/source/console/astNodes.cpp | 51 ++++++++++++++++++++++--- Engine/source/console/compiledEval.cpp | 40 +++++++++++++++++++ Engine/source/console/compiler.h | 8 +++- Engine/source/console/consoleInternal.h | 3 ++ 5 files changed, 96 insertions(+), 9 deletions(-) diff --git a/Engine/source/console/ast.h b/Engine/source/console/ast.h index 71cfbeec0..f6e81de4c 100644 --- a/Engine/source/console/ast.h +++ b/Engine/source/console/ast.h @@ -38,7 +38,8 @@ enum TypeReq TypeReqNone, TypeReqUInt, TypeReqFloat, - TypeReqString + TypeReqString, + TypeReqVar }; /// Representation of a node for the scripting language parser. diff --git a/Engine/source/console/astNodes.cpp b/Engine/source/console/astNodes.cpp index 31b337c35..ae09abbd2 100644 --- a/Engine/source/console/astNodes.cpp +++ b/Engine/source/console/astNodes.cpp @@ -134,6 +134,8 @@ static U32 conversionOp(TypeReq src, TypeReq dst) return OP_STR_TO_FLT; case TypeReqNone: return OP_STR_TO_NONE; + case TypeReqVar: + return OP_SAVEVAR_STR; default: break; } @@ -148,6 +150,8 @@ static U32 conversionOp(TypeReq src, TypeReq dst) return OP_FLT_TO_STR; case TypeReqNone: return OP_FLT_TO_NONE; + case TypeReqVar: + return OP_SAVEVAR_FLT; default: break; } @@ -162,6 +166,24 @@ static U32 conversionOp(TypeReq src, TypeReq dst) return OP_UINT_TO_STR; case TypeReqNone: return OP_UINT_TO_NONE; + case TypeReqVar: + return OP_SAVEVAR_UINT; + default: + break; + } + } + else if(src == TypeReqVar) + { + switch(dst) + { + case TypeReqUInt: + return OP_LOADVAR_UINT; + case TypeReqFloat: + return OP_LOADVAR_FLT; + case TypeReqString: + return OP_LOADVAR_STR; + case TypeReqNone: + return OP_COPYVAR_TO_NONE; default: break; } @@ -872,6 +894,7 @@ U32 VarNode::precompile(TypeReq type) return (arrayIndex ? arrayIndex->precompile(TypeReqString) + 6 : 3); } +// Puts value of VarNode onto StringStack/intStack/fltStack U32 VarNode::compile(U32 *codeStream, U32 ip, TypeReq type) { if(type == TypeReqNone) @@ -882,10 +905,11 @@ U32 VarNode::compile(U32 *codeStream, U32 ip, TypeReq type) ip++; if(arrayIndex) { + // NOTE: in this case we have the start value loaded into STR codeStream[ip++] = OP_ADVANCE_STR; - ip = arrayIndex->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_REWIND_STR; - codeStream[ip++] = OP_SETCURVAR_ARRAY; + ip = arrayIndex->compile(codeStream, ip, TypeReqString); // Add on extra bits + codeStream[ip++] = OP_REWIND_STR; // Go back to start + codeStream[ip++] = OP_SETCURVAR_ARRAY; // Set variable name } switch(type) { @@ -898,8 +922,13 @@ U32 VarNode::compile(U32 *codeStream, U32 ip, TypeReq type) case TypeReqString: codeStream[ip++] = OP_LOADVAR_STR; break; + case TypeReqVar: + codeStream[ip++] = OP_LOADVAR_VAR; + break; case TypeReqNone: break; + default: + break; } return ip; } @@ -1105,8 +1134,15 @@ U32 AssignExprNode::precompile(TypeReq type) subType = expr->getPreferredType(); if(subType == TypeReqNone) subType = type; - if(subType == TypeReqNone) - subType = TypeReqString; + if(subType == TypeReqNone) { + // jamesu - what we need to do in this case is turn it into a VarNode reference + if (dynamic_cast(expr) != NULL) { + // Sanity check passed + subType = TypeReqVar; + } else { + subType = TypeReqString; + } + } // if it's an array expr, the formula is: // eval expr // (push and pop if it's TypeReqString) OP_ADVANCE_STR @@ -1139,7 +1175,7 @@ U32 AssignExprNode::precompile(TypeReq type) U32 AssignExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) { - ip = expr->compile(codeStream, ip, subType); + ip = expr->compile(codeStream, ip, subType); // this is the value of VarNode if(arrayIndex) { if(subType == TypeReqString) @@ -1172,6 +1208,9 @@ U32 AssignExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) case TypeReqFloat: codeStream[ip++] = OP_SAVEVAR_FLT; break; + case TypeReqVar: + codeStream[ip++] = OP_SAVEVAR_VAR; + break; case TypeReqNone: break; } diff --git a/Engine/source/console/compiledEval.cpp b/Engine/source/console/compiledEval.cpp index ca9e730fb..612ddad12 100644 --- a/Engine/source/console/compiledEval.cpp +++ b/Engine/source/console/compiledEval.cpp @@ -285,6 +285,24 @@ inline void ExprEvalState::setStringVariable(const char *val) currentVariable->setStringValue(val); } +inline void ExprEvalState::setCopyVariable() +{ + if (copyVariable) { + switch (copyVariable->value.type) + { + case ConsoleValue::TypeInternalInt: + currentVariable->setIntValue(copyVariable->getIntValue()); + break; + case ConsoleValue::TypeInternalFloat: + currentVariable->setFloatValue(copyVariable->getFloatValue()); + break; + default: + currentVariable->setStringValue(copyVariable->getStringValue()); + break; + } + } +} + //------------------------------------------------------------ // Gets a component of an object's field value or a variable and returns it @@ -1362,6 +1380,11 @@ breakContinue: STR.setStringValue(val); break; + case OP_LOADVAR_VAR: + // Sets current source of OP_SAVEVAR_VAR + gEvalState.copyVariable = gEvalState.currentVariable; + break; + case OP_SAVEVAR_UINT: gEvalState.setIntVariable(intStack[_UINT]); break; @@ -1373,6 +1396,11 @@ breakContinue: case OP_SAVEVAR_STR: gEvalState.setStringVariable(STR.getStringValue()); break; + + case OP_SAVEVAR_VAR: + // this basically handles %var1 = %var2 + gEvalState.setCopyVariable(); + break; case OP_SETCUROBJECT: // Save the previous object for parsing vector fields. @@ -1560,6 +1588,11 @@ breakContinue: _UINT--; break; + case OP_COPYVAR_TO_NONE: + // nop + gEvalState.copyVariable = NULL; + break; + case OP_LOADIMMED_UINT: intStack[_UINT+1] = code[ip++]; _UINT++; @@ -1940,6 +1973,13 @@ breakContinue: CSTK.pushFLT(floatStack[_FLT]); _FLT--; break; + case OP_PUSH_VAR: + //Con::printf("Pushing variable: %s",gEvalState.getCurrentVariable()]); + if (gEvalState.currentVariable) + CSTK.pushValue(gEvalState.currentVariable->value); + else + CSTK.pushString(""); + break; case OP_PUSH_FRAME: STR.pushFrame(); diff --git a/Engine/source/console/compiler.h b/Engine/source/console/compiler.h index 5354ee029..150b92b2e 100644 --- a/Engine/source/console/compiler.h +++ b/Engine/source/console/compiler.h @@ -90,10 +90,12 @@ namespace Compiler OP_LOADVAR_UINT, OP_LOADVAR_FLT, OP_LOADVAR_STR, + OP_LOADVAR_VAR, OP_SAVEVAR_UINT, OP_SAVEVAR_FLT, OP_SAVEVAR_STR, + OP_SAVEVAR_VAR, OP_SETCUROBJECT, OP_SETCUROBJECT_NEW, @@ -120,6 +122,7 @@ namespace Compiler OP_UINT_TO_FLT, OP_UINT_TO_STR, OP_UINT_TO_NONE, + OP_COPYVAR_TO_NONE, OP_LOADIMMED_UINT, OP_LOADIMMED_FLT, @@ -140,8 +143,9 @@ namespace Compiler OP_COMPARE_STR, OP_PUSH, // String - OP_PUSH_UINT, // Integer - OP_PUSH_FLT, // Float + OP_PUSH_UINT, // Integer + OP_PUSH_FLT, // Float + OP_PUSH_VAR, // Variable OP_PUSH_FRAME, // Frame OP_ASSERT, diff --git a/Engine/source/console/consoleInternal.h b/Engine/source/console/consoleInternal.h index 7893e6384..da465d74f 100644 --- a/Engine/source/console/consoleInternal.h +++ b/Engine/source/console/consoleInternal.h @@ -464,6 +464,7 @@ public: /// SimObject *thisObject; Dictionary::Entry *currentVariable; + Dictionary::Entry *copyVariable; bool traceOn; U32 mStackDepth; @@ -485,12 +486,14 @@ public: 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 setCopyVariable(); void pushFrame(StringTableEntry frameName, Namespace *ns); void popFrame(); From 08d4f6ebc00a93bddeb575d2e2bf494369225d0e Mon Sep 17 00:00:00 2001 From: jamesu Date: Thu, 11 Oct 2012 21:29:39 +0100 Subject: [PATCH 003/317] Improvements to console refactor code - Prevent stack corruption in a few places - Use correct type in printfs - Reduce type conversions in EngineApi & dAto* - Fix compilation on GCC - Tidy up code --- Engine/source/T3D/lightBase.cpp | 4 +- Engine/source/T3D/missionMarker.cpp | 2 +- Engine/source/T3D/physics/physicsPlugin.cpp | 14 +- Engine/source/app/net/net.cpp | 2 +- .../dynamicConsoleMethodComponent.cpp | 23 ++ Engine/source/component/simComponent.cpp | 8 +- Engine/source/console/arrayObject.cpp | 10 +- Engine/source/console/astNodes.cpp | 104 ++++---- Engine/source/console/compiledEval.cpp | 150 +++++------ Engine/source/console/compiler.h | 4 +- Engine/source/console/console.cpp | 248 ++++++++++-------- Engine/source/console/console.h | 29 +- Engine/source/console/consoleFunctions.cpp | 6 +- Engine/source/console/consoleInternal.cpp | 140 ++++------ Engine/source/console/consoleInternal.h | 208 +++++++-------- Engine/source/console/engineAPI.h | 31 ++- Engine/source/console/fieldBrushObject.cpp | 2 +- Engine/source/console/persistenceManager.cpp | 18 +- Engine/source/console/sim.cpp | 10 +- Engine/source/console/sim.h | 9 + Engine/source/console/simEvents.cpp | 12 +- Engine/source/console/simManager.cpp | 5 + Engine/source/console/simPersistSet.cpp | 2 +- Engine/source/console/simSet.cpp | 4 +- Engine/source/console/stringStack.cpp | 180 ++++++------- Engine/source/console/stringStack.h | 37 +-- .../editors/guiMeshRoadEditorCtrl.cpp | 8 +- .../editors/guiRiverEditorCtrl.cpp | 8 +- .../environment/editors/guiRoadEditorCtrl.cpp | 4 +- Engine/source/forest/forest.cpp | 4 +- Engine/source/gui/controls/guiPopUpCtrl.cpp | 6 +- Engine/source/gui/controls/guiPopUpCtrlEx.cpp | 6 +- .../source/gui/controls/guiTreeViewCtrl.cpp | 2 +- Engine/source/gui/core/guiCanvas.cpp | 4 +- Engine/source/gui/editor/guiEditCtrl.cpp | 10 +- Engine/source/gui/editor/guiInspector.cpp | 6 +- .../source/gui/editor/guiInspectorTypes.cpp | 2 +- .../editor/inspector/variableInspector.cpp | 2 +- .../gui/worldEditor/editorIconRegistry.cpp | 6 +- .../gui/worldEditor/guiDecalEditorCtrl.cpp | 12 +- .../source/gui/worldEditor/terrainEditor.cpp | 6 +- Engine/source/gui/worldEditor/worldEditor.cpp | 6 +- .../gui/worldEditor/worldEditorSelection.cpp | 4 +- Engine/source/i18n/i18n.cpp | 2 +- Engine/source/materials/materialManager.cpp | 8 +- Engine/source/platformMac/macCarbFileio.mm | 4 +- Engine/source/platformWin32/winInput.cpp | 2 +- .../platformX86UNIX/x86UNIXInput.client.cpp | 2 +- Engine/source/platformX86UNIX/x86UNIXMath.cpp | 2 +- Engine/source/postFx/postEffectVis.cpp | 6 +- Engine/source/sfx/sfxSystem.cpp | 6 +- Engine/source/sim/actionMap.cpp | 17 +- Engine/source/sim/netConnection.cpp | 2 +- Engine/source/terrain/terrExport.cpp | 4 +- Engine/source/ts/collada/colladaImport.cpp | 2 +- Engine/source/ts/collada/colladaLights.cpp | 2 +- Engine/source/util/messaging/eventManager.cpp | 2 +- Engine/source/util/undo.cpp | 4 +- 58 files changed, 733 insertions(+), 690 deletions(-) diff --git a/Engine/source/T3D/lightBase.cpp b/Engine/source/T3D/lightBase.cpp index a3a5372cf..150f6e9cc 100644 --- a/Engine/source/T3D/lightBase.cpp +++ b/Engine/source/T3D/lightBase.cpp @@ -439,7 +439,7 @@ ConsoleMethod( LightBase, playAnimation, void, 2, 3, "( [LightAnimData anim] )\t LightAnimData *animData; if ( !Sim::findObject( argv[2], animData ) ) { - Con::errorf( "LightBase::playAnimation() - Invalid LightAnimData '%s'.", argv[2] ); + Con::errorf( "LightBase::playAnimation() - Invalid LightAnimData '%s'.", (const char*)argv[2] ); return; } @@ -481,4 +481,4 @@ void LightBase::pauseAnimation( void ) mAnimState.active = false; setMaskBits( UpdateMask ); } -} \ No newline at end of file +} diff --git a/Engine/source/T3D/missionMarker.cpp b/Engine/source/T3D/missionMarker.cpp index bff79e962..668054559 100644 --- a/Engine/source/T3D/missionMarker.cpp +++ b/Engine/source/T3D/missionMarker.cpp @@ -554,7 +554,7 @@ ConsoleMethod(SpawnSphere, spawnObject, S32, 2, 3, String additionalProps; if (argc == 3) - additionalProps = String(argv[2]); + additionalProps = (const char*)argv[2]; SimObject* obj = object->spawnObject(additionalProps); diff --git a/Engine/source/T3D/physics/physicsPlugin.cpp b/Engine/source/T3D/physics/physicsPlugin.cpp index afb06ccb8..79c5076e9 100644 --- a/Engine/source/T3D/physics/physicsPlugin.cpp +++ b/Engine/source/T3D/physics/physicsPlugin.cpp @@ -147,13 +147,13 @@ ConsoleFunction( physicsDestroy, void, 1, 1, "physicsDestroy()" ) ConsoleFunction( physicsInitWorld, bool, 2, 2, "physicsInitWorld( String worldName )" ) { - return PHYSICSMGR && PHYSICSMGR->createWorld( String( argv[1] ) ); + return PHYSICSMGR && PHYSICSMGR->createWorld( (const char*)argv[1] ); } ConsoleFunction( physicsDestroyWorld, void, 2, 2, "physicsDestroyWorld( String worldName )" ) { if ( PHYSICSMGR ) - PHYSICSMGR->destroyWorld( String( argv[1] ) ); + PHYSICSMGR->destroyWorld( (const char*)argv[1] ); } @@ -162,13 +162,13 @@ ConsoleFunction( physicsDestroyWorld, void, 2, 2, "physicsDestroyWorld( String w ConsoleFunction( physicsStartSimulation, void, 2, 2, "physicsStartSimulation( String worldName )" ) { if ( PHYSICSMGR ) - PHYSICSMGR->enableSimulation( String( argv[1] ), true ); + PHYSICSMGR->enableSimulation( (const char*)argv[1], true ); } ConsoleFunction( physicsStopSimulation, void, 2, 2, "physicsStopSimulation( String worldName )" ) { if ( PHYSICSMGR ) - PHYSICSMGR->enableSimulation( String( argv[1] ), false ); + PHYSICSMGR->enableSimulation( (const char*)argv[1], false ); } ConsoleFunction( physicsSimulationEnabled, bool, 1, 1, "physicsSimulationEnabled()" ) @@ -182,7 +182,7 @@ ConsoleFunction( physicsSimulationEnabled, bool, 1, 1, "physicsSimulationEnabled ConsoleFunction( physicsSetTimeScale, void, 2, 2, "physicsSetTimeScale( F32 scale )" ) { if ( PHYSICSMGR ) - PHYSICSMGR->setTimeScale( dAtof( argv[1] ) ); + PHYSICSMGR->setTimeScale( argv[1] ); } // Get the currently set time scale. @@ -212,5 +212,5 @@ ConsoleFunction( physicsRestoreState, void, 1, 1, "physicsRestoreState()" ) ConsoleFunction( physicsDebugDraw, void, 2, 2, "physicsDebugDraw( bool enable )" ) { if ( PHYSICSMGR ) - PHYSICSMGR->enableDebugDraw( dAtoi( argv[1] ) ); -} \ No newline at end of file + PHYSICSMGR->enableDebugDraw( (S32)argv[1] ); +} diff --git a/Engine/source/app/net/net.cpp b/Engine/source/app/net/net.cpp index d24e698d4..6166f981e 100644 --- a/Engine/source/app/net/net.cpp +++ b/Engine/source/app/net/net.cpp @@ -307,7 +307,7 @@ ConsoleFunction( addTaggedString, const char*, 2, 2, "(string str)" "@see getTaggedString()\n" "@ingroup Networking\n") { - NetStringHandle s(argv[1]); + NetStringHandle s((const char*)argv[1]); gNetStringTable->incStringRefScript(s.getIndex()); char *ret = Con::getReturnBuffer(10); diff --git a/Engine/source/component/dynamicConsoleMethodComponent.cpp b/Engine/source/component/dynamicConsoleMethodComponent.cpp index c0a971405..f889b5c39 100644 --- a/Engine/source/component/dynamicConsoleMethodComponent.cpp +++ b/Engine/source/component/dynamicConsoleMethodComponent.cpp @@ -21,6 +21,10 @@ //----------------------------------------------------------------------------- #include "component/dynamicConsoleMethodComponent.h" +#include "console/stringStack.h" + +extern StringStack STR; +extern ConsoleValueStack CSTK; IMPLEMENT_CO_NETOBJECT_V1(DynamicConsoleMethodComponent); @@ -152,23 +156,42 @@ const char *DynamicConsoleMethodComponent::_callMethod( U32 argc, ConsoleValueRe DynamicConsoleMethodComponent *pThisComponent = dynamic_cast( pComponent ); AssertFatal( pThisComponent, "DynamicConsoleMethodComponent::callMethod - Non DynamicConsoleMethodComponent component attempting to callback!"); + // Prevent stack corruption + STR.pushFrame(); + CSTK.pushFrame(); + // -- + // Only call on first depth components // Should isMethod check these calls? [11/22/2006 justind] if(pComponent->isEnabled()) Con::execute( pThisComponent, argc, argv ); + // Prevent stack corruption + STR.popFrame(); + CSTK.popFrame(); + // -- + // Bail if this was the first element //if( nItr == componentList.begin() ) // break; } unlockComponentList(); } + + // Prevent stack corruption + STR.pushFrame(); + CSTK.pushFrame(); + // -- // Set Owner Field const char* result = ""; if(callThis) result = Con::execute( pThis, argc, argv, true ); // true - exec method onThisOnly, not on DCMCs + // Prevent stack corruption + STR.popFrame(); + CSTK.popFrame(); + // -- return result; } diff --git a/Engine/source/component/simComponent.cpp b/Engine/source/component/simComponent.cpp index b1a1cf86d..f4e1f5c67 100644 --- a/Engine/source/component/simComponent.cpp +++ b/Engine/source/component/simComponent.cpp @@ -179,7 +179,7 @@ bool SimComponent::processArguments(S32 argc, ConsoleValueRef *argv) if(obj) addComponent(obj); else - Con::printf("SimComponent::processArguments - Invalid Component Object \"%s\"", argv[i]); + Con::printf("SimComponent::processArguments - Invalid Component Object \"%s\"", (const char*)argv[i]); } return true; } @@ -383,7 +383,7 @@ ConsoleMethod( SimComponent, addComponents, bool, 3, 64, "%obj.addComponents( %c if(obj) object->addComponent(obj); else - Con::printf("SimComponent::addComponents - Invalid Component Object \"%s\"", argv[i]); + Con::printf("SimComponent::addComponents - Invalid Component Object \"%s\"", (const char*)argv[i]); } return true; } @@ -399,7 +399,7 @@ ConsoleMethod( SimComponent, removeComponents, bool, 3, 64, "%obj.removeComponen if(obj) object->removeComponent(obj); else - Con::printf("SimComponent::removeComponents - Invalid Component Object \"%s\"", argv[i]); + Con::printf("SimComponent::removeComponents - Invalid Component Object \"%s\"", (const char*)argv[i]); } return true; } @@ -449,4 +449,4 @@ ConsoleMethod(SimComponent, getIsTemplate, bool, 2, 2, "() Check whether SimComp "@return true if is a template and false if not") { return object->getIsTemplate(); -} \ No newline at end of file +} diff --git a/Engine/source/console/arrayObject.cpp b/Engine/source/console/arrayObject.cpp index 180461cb1..ddcab509f 100644 --- a/Engine/source/console/arrayObject.cpp +++ b/Engine/source/console/arrayObject.cpp @@ -103,10 +103,7 @@ S32 QSORT_CALLBACK ArrayObject::_keyFunctionCompare( const void* a, const void* ArrayObject::Element* ea = ( ArrayObject::Element* )( a ); ArrayObject::Element* eb = ( ArrayObject::Element* )( b ); - const char* argv[ 3 ]; - argv[ 0 ] = smCompareFunction; - argv[ 1 ] = ea->key; - argv[ 2 ] = eb->key; + ConsoleValueRef argv[] = { smCompareFunction, ea->key, eb->key }; S32 result = dAtoi( Con::execute( 3, argv ) ); S32 res = result < 0 ? -1 : ( result > 0 ? 1 : 0 ); @@ -118,10 +115,7 @@ S32 QSORT_CALLBACK ArrayObject::_valueFunctionCompare( const void* a, const void ArrayObject::Element* ea = ( ArrayObject::Element* )( a ); ArrayObject::Element* eb = ( ArrayObject::Element* )( b ); - const char* argv[ 3 ]; - argv[ 0 ] = smCompareFunction; - argv[ 1 ] = ea->value; - argv[ 2 ] = eb->value; + ConsoleValueRef argv[] = { smCompareFunction, ea->value, eb->value }; S32 result = dAtoi( Con::execute( 3, argv ) ); S32 res = result < 0 ? -1 : ( result > 0 ? 1 : 0 ); diff --git a/Engine/source/console/astNodes.cpp b/Engine/source/console/astNodes.cpp index ae09abbd2..0d63a6ddb 100644 --- a/Engine/source/console/astNodes.cpp +++ b/Engine/source/console/astNodes.cpp @@ -134,8 +134,8 @@ static U32 conversionOp(TypeReq src, TypeReq dst) return OP_STR_TO_FLT; case TypeReqNone: return OP_STR_TO_NONE; - case TypeReqVar: - return OP_SAVEVAR_STR; + case TypeReqVar: + return OP_SAVEVAR_STR; default: break; } @@ -150,7 +150,7 @@ static U32 conversionOp(TypeReq src, TypeReq dst) return OP_FLT_TO_STR; case TypeReqNone: return OP_FLT_TO_NONE; - case TypeReqVar: + case TypeReqVar: return OP_SAVEVAR_FLT; default: break; @@ -166,7 +166,7 @@ static U32 conversionOp(TypeReq src, TypeReq dst) return OP_UINT_TO_STR; case TypeReqNone: return OP_UINT_TO_NONE; - case TypeReqVar: + case TypeReqVar: return OP_SAVEVAR_UINT; default: break; @@ -276,21 +276,21 @@ U32 ReturnStmtNode::compileStmt(U32 *codeStream, U32 ip, U32, U32) else { TypeReq walkType = expr->getPreferredType(); - if (walkType == TypeReqNone) walkType = TypeReqString; + if (walkType == TypeReqNone) walkType = TypeReqString; ip = expr->compile(codeStream, ip, walkType); - // Return the correct type - switch (walkType) { - case TypeReqUInt: - codeStream[ip++] = OP_RETURN_UINT; - break; - case TypeReqFloat: - codeStream[ip++] = OP_RETURN_FLT; - break; - default: - codeStream[ip++] = OP_RETURN; - break; - } + // Return the correct type + switch (walkType) { + case TypeReqUInt: + codeStream[ip++] = OP_RETURN_UINT; + break; + case TypeReqFloat: + codeStream[ip++] = OP_RETURN_FLT; + break; + default: + codeStream[ip++] = OP_RETURN; + break; + } } return ip; } @@ -1134,12 +1134,18 @@ U32 AssignExprNode::precompile(TypeReq type) subType = expr->getPreferredType(); if(subType == TypeReqNone) subType = type; - if(subType == TypeReqNone) { - // jamesu - what we need to do in this case is turn it into a VarNode reference - if (dynamic_cast(expr) != NULL) { - // Sanity check passed + if(subType == TypeReqNone) + { + // What we need to do in this case is turn it into a VarNode reference. + // Unfortunately other nodes such as field access (SlotAccessNode) + // cannot be optimized in the same manner as all fields are exposed + // and set as strings. + if (dynamic_cast(expr) != NULL) + { subType = TypeReqVar; - } else { + } + else + { subType = TypeReqString; } } @@ -1409,7 +1415,7 @@ U32 FuncCallExprNode::precompile(TypeReq type) precompileIdent(nameSpace); for(ExprNode *walk = args; walk; walk = (ExprNode *) walk->getNext()) { TypeReq walkType = walk->getPreferredType(); - if (walkType == TypeReqNone) walkType = TypeReqString; + if (walkType == TypeReqNone) walkType = TypeReqString; size += walk->precompile(walkType) + 1; } return size + 5; @@ -1421,19 +1427,20 @@ U32 FuncCallExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) for(ExprNode *walk = args; walk; walk = (ExprNode *) walk->getNext()) { TypeReq walkType = walk->getPreferredType(); - if (walkType == TypeReqNone) walkType = TypeReqString; + if (walkType == TypeReqNone) walkType = TypeReqString; ip = walk->compile(codeStream, ip, walkType); - switch (walk->getPreferredType()) { - case TypeReqFloat: - codeStream[ip++] = OP_PUSH_FLT; - break; - case TypeReqUInt: - codeStream[ip++] = OP_PUSH_UINT; - break; - default: - codeStream[ip++] = OP_PUSH; - break; - } + switch (walk->getPreferredType()) + { + case TypeReqFloat: + codeStream[ip++] = OP_PUSH_FLT; + break; + case TypeReqUInt: + codeStream[ip++] = OP_PUSH_UINT; + break; + default: + codeStream[ip++] = OP_PUSH; + break; + } } if(callType == MethodCall || callType == ParentCall) codeStream[ip++] = OP_CALLFUNC; @@ -1806,7 +1813,7 @@ U32 ObjectDeclNode::precompileSubObject(bool) precompileIdent(parentObject); for(ExprNode *exprWalk = argList; exprWalk; exprWalk = (ExprNode *) exprWalk->getNext()) { TypeReq walkType = exprWalk->getPreferredType(); - if (walkType == TypeReqNone) walkType = TypeReqString; + if (walkType == TypeReqNone) walkType = TypeReqString; argSize += exprWalk->precompile(walkType) + 1; } argSize += classNameExpr->precompile(TypeReqString) + 1; @@ -1854,19 +1861,20 @@ U32 ObjectDeclNode::compileSubObject(U32 *codeStream, U32 ip, bool root) for(ExprNode *exprWalk = argList; exprWalk; exprWalk = (ExprNode *) exprWalk->getNext()) { TypeReq walkType = exprWalk->getPreferredType(); - if (walkType == TypeReqNone) walkType = TypeReqString; + if (walkType == TypeReqNone) walkType = TypeReqString; ip = exprWalk->compile(codeStream, ip, walkType); - switch (exprWalk->getPreferredType()) { - case TypeReqFloat: - codeStream[ip++] = OP_PUSH_FLT; - break; - case TypeReqUInt: - codeStream[ip++] = OP_PUSH_UINT; - break; - default: - codeStream[ip++] = OP_PUSH; - break; - } + switch (exprWalk->getPreferredType()) + { + case TypeReqFloat: + codeStream[ip++] = OP_PUSH_FLT; + break; + case TypeReqUInt: + codeStream[ip++] = OP_PUSH_UINT; + break; + default: + codeStream[ip++] = OP_PUSH; + break; + } } codeStream[ip++] = OP_CREATE_OBJECT; codeStream[ip] = STEtoU32(parentObject, ip); diff --git a/Engine/source/console/compiledEval.cpp b/Engine/source/console/compiledEval.cpp index 612ddad12..5946d2445 100644 --- a/Engine/source/console/compiledEval.cpp +++ b/Engine/source/console/compiledEval.cpp @@ -192,18 +192,16 @@ namespace Con return STR.getArgBuffer(bufferSize); } - char *getFloatArg(F64 arg) + ConsoleValueRef getFloatArg(F64 arg) { - char *ret = STR.getArgBuffer(32); - dSprintf(ret, 32, "%g", arg); - return ret; + ConsoleValueRef ref = arg; + return ref; } - char *getIntArg(S32 arg) + ConsoleValueRef getIntArg(S32 arg) { - char *ret = STR.getArgBuffer(32); - dSprintf(ret, 32, "%d", arg); - return ret; + ConsoleValueRef ref = arg; + return ref; } char *getStringArg( const char *arg ) @@ -287,7 +285,8 @@ inline void ExprEvalState::setStringVariable(const char *val) inline void ExprEvalState::setCopyVariable() { - if (copyVariable) { + if (copyVariable) + { switch (copyVariable->value.type) { case ConsoleValue::TypeInternalInt: @@ -485,16 +484,16 @@ ConsoleValueRef CodeBlock::exec(U32 ip, const char *functionName, Namespace *thi StringTableEntry var = U32toSTE(code[ip + i + 6]); gEvalState.setCurVarNameCreate(var); - ConsoleValueRef ref = argv[i+1]; + ConsoleValueRef ref = argv[i+1]; - if (argv[i+1].isString()) - gEvalState.setStringVariable(argv[i+1]); - else if (argv[i+1].isInt()) - gEvalState.setIntVariable(argv[i+1]); - else if (argv[i+1].isFloat()) - gEvalState.setFloatVariable(argv[i+1]); - else - gEvalState.setStringVariable(argv[i+1]); + if (argv[i+1].isString()) + gEvalState.setStringVariable(argv[i+1]); + else if (argv[i+1].isInt()) + gEvalState.setIntVariable(argv[i+1]); + else if (argv[i+1].isFloat()) + gEvalState.setFloatVariable(argv[i+1]); + else + gEvalState.setStringVariable(argv[i+1]); } ip = ip + fnArgc + 6; curFloatTable = functionFloats; @@ -531,9 +530,8 @@ ConsoleValueRef CodeBlock::exec(U32 ip, const char *functionName, Namespace *thi } } - // jamesu - reset the console stack frame which at this point will contain + // Reset the console stack frame which at this point will contain // either nothing or argv[] which we just copied - // NOTE: it might be better to do this when we are finished? CSTK.resetFrame(); // Grab the state of the telenet debugger here once @@ -584,7 +582,7 @@ ConsoleValueRef CodeBlock::exec(U32 ip, const char *functionName, Namespace *thi const char * val; const char *retValue; - // note: anything returned is pushed to CSTK and will be invalidated on the next exec() + // note: anything returned is pushed to CSTK and will be invalidated on the next exec() ConsoleValueRef returnValue; // The frame temp is used by the variable accessor ops (OP_SAVEFIELD_* and @@ -681,7 +679,7 @@ breakContinue: Con::errorf(ConsoleLogEntry::General, "%s: Cannot re-declare data block %s with a different class.", getFileLine(ip), objectName); ip = failJump; STR.popFrame(); - CSTK.popFrame(); + CSTK.popFrame(); break; } @@ -730,18 +728,18 @@ breakContinue: } //dMemcpy( savedArgv, callArgv, sizeof( savedArgv[ 0 ] ) * callArgc ); - // Prevent stack value corruption - CSTK.pushFrame(); - STR.pushFrame(); - // -- + // Prevent stack value corruption + CSTK.pushFrame(); + STR.pushFrame(); + // -- obj->deleteObject(); obj = NULL; - // Prevent stack value corruption - CSTK.popFrame(); - STR.popFrame(); - // -- + // Prevent stack value corruption + CSTK.popFrame(); + STR.popFrame(); + // -- //dMemcpy( callArgv, savedArgv, sizeof( callArgv[ 0 ] ) * callArgc ); for (int i=0; isetOriginalName( objectName ); } - // Prevent stack value corruption - CSTK.pushFrame(); - STR.pushFrame(); - // -- + // Prevent stack value corruption + CSTK.pushFrame(); + STR.pushFrame(); + // -- // Do the constructor parameters. if(!currentNewObject->processArguments(callArgc-3, callArgv+3)) @@ -1041,10 +1039,10 @@ breakContinue: else intStack[++_UINT] = currentNewObject->getId(); - // Prevent stack value corruption - CSTK.popFrame(); - STR.popFrame(); - // -- + // Prevent stack value corruption + CSTK.popFrame(); + STR.popFrame(); + // -- break; } @@ -1127,7 +1125,7 @@ breakContinue: // We're falling thru here on purpose. case OP_RETURN: - retValue = STR.getStringValue(); + retValue = STR.getStringValue(); if( iterDepth > 0 ) { @@ -1140,12 +1138,12 @@ breakContinue: STR.rewind(); STR.setStringValue( retValue ); // Not nice but works. - retValue = STR.getStringValue(); + retValue = STR.getStringValue(); } - // Previously the return value was on the stack and would be returned using STR.getStringValue(). - // Now though we need to wrap it in a ConsoleValueRef - returnValue.value = CSTK.pushStackString(retValue); + // Previously the return value was on the stack and would be returned using STR.getStringValue(). + // Now though we need to wrap it in a ConsoleValueRef + returnValue.value = CSTK.pushStackString(retValue); goto execFinished; @@ -1179,8 +1177,8 @@ breakContinue: } } - returnValue.value = CSTK.pushUINT(intStack[_UINT]); - _UINT--; + returnValue.value = CSTK.pushUINT(intStack[_UINT]); + _UINT--; goto execFinished; @@ -1323,7 +1321,7 @@ breakContinue: var = U32toSTE(code[ip]); ip++; - // See OP_SETCURVAR + // See OP_SETCURVAR prevField = NULL; prevObject = NULL; curObject = NULL; @@ -1398,7 +1396,7 @@ breakContinue: break; case OP_SAVEVAR_VAR: - // this basically handles %var1 = %var2 + // this basically handles %var1 = %var2 gEvalState.setCopyVariable(); break; @@ -1589,7 +1587,6 @@ breakContinue: break; case OP_COPYVAR_TO_NONE: - // nop gEvalState.copyVariable = NULL; break; @@ -1668,7 +1665,7 @@ breakContinue: getFileLine(ip-4), fnNamespace ? fnNamespace : "", fnNamespace ? "::" : "", fnName); STR.popFrame(); - CSTK.popFrame(); + CSTK.popFrame(); break; } // Now fall through to OP_CALLFUNC... @@ -1718,7 +1715,7 @@ breakContinue: Con::warnf(ConsoleLogEntry::General,"%s: Unable to find object: '%s' attempting to call function '%s'", getFileLine(ip-4), (const char*)callArgv[1], fnName); STR.popFrame(); - CSTK.popFrame(); + CSTK.popFrame(); break; } @@ -1774,7 +1771,7 @@ breakContinue: } } STR.popFrame(); - CSTK.popFrame(); + CSTK.popFrame(); if( routingId == MethodOnComponent ) STR.setStringValue( componentReturnValue ); @@ -1789,9 +1786,9 @@ breakContinue: if(nsEntry->mFunctionOffset) ret = nsEntry->mCode->exec(nsEntry->mFunctionOffset, fnName, nsEntry->mNamespace, callArgc, callArgv, false, nsEntry->mPackage); - STR.popFrame(); - // Functions are assumed to return strings, so look ahead to see if we can skip the conversion - if(code[ip] == OP_STR_TO_UINT) + STR.popFrame(); + // Functions are assumed to return strings, so look ahead to see if we can skip the conversion + if(code[ip] == OP_STR_TO_UINT) { ip++; intStack[++_UINT] = (U32)((S32)ret); @@ -1806,9 +1803,9 @@ breakContinue: else STR.setStringValue((const char*)ret); - // This will clear everything including returnValue - CSTK.popFrame(); - STR.clearFunctionOffset(); + // This will clear everything including returnValue + CSTK.popFrame(); + STR.clearFunctionOffset(); } else { @@ -1829,7 +1826,7 @@ breakContinue: callArgc, nsEntry->mMinArgs, nsEntry->mMaxArgs); Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", getFileLine(ip-4), nsEntry->mUsage); STR.popFrame(); - CSTK.popFrame(); + CSTK.popFrame(); } else { @@ -1839,7 +1836,7 @@ breakContinue: { const char *ret = nsEntry->cb.mStringCallbackFunc(gEvalState.thisObject, callArgc, callArgv); STR.popFrame(); - CSTK.popFrame(); + CSTK.popFrame(); if(ret != STR.getStringValue()) STR.setStringValue(ret); //else @@ -1850,7 +1847,7 @@ breakContinue: { S32 result = nsEntry->cb.mIntCallbackFunc(gEvalState.thisObject, callArgc, callArgv); STR.popFrame(); - CSTK.popFrame(); + CSTK.popFrame(); if(code[ip] == OP_STR_TO_UINT) { ip++; @@ -1873,7 +1870,7 @@ breakContinue: { F64 result = nsEntry->cb.mFloatCallbackFunc(gEvalState.thisObject, callArgc, callArgv); STR.popFrame(); - CSTK.popFrame(); + CSTK.popFrame(); if(code[ip] == OP_STR_TO_UINT) { ip++; @@ -1898,14 +1895,14 @@ breakContinue: Con::warnf(ConsoleLogEntry::General, "%s: Call to %s in %s uses result of void function call.", getFileLine(ip-4), fnName, functionName); STR.popFrame(); - CSTK.popFrame(); + CSTK.popFrame(); STR.setStringValue(""); break; case Namespace::Entry::BoolCallbackType: { bool result = nsEntry->cb.mBoolCallbackFunc(gEvalState.thisObject, callArgc, callArgv); STR.popFrame(); - CSTK.popFrame(); + CSTK.popFrame(); if(code[ip] == OP_STR_TO_UINT) { ip++; @@ -1959,31 +1956,27 @@ breakContinue: intStack[++_UINT] = STR.compare(); break; case OP_PUSH: - STR.push(); - //Con::printf("Pushing str: %s",STR.getPreviousStringValue()); + STR.push(); CSTK.pushString(STR.getPreviousStringValue()); break; case OP_PUSH_UINT: - //Con::printf("Pushing int: %i",(S32)intStack[_UINT]); CSTK.pushUINT(intStack[_UINT]); - _UINT--; + _UINT--; break; case OP_PUSH_FLT: - //Con::printf("Pushing float: %f",(F32)intStack[_UINT]); CSTK.pushFLT(floatStack[_FLT]); - _FLT--; + _FLT--; break; case OP_PUSH_VAR: - //Con::printf("Pushing variable: %s",gEvalState.getCurrentVariable()]); if (gEvalState.currentVariable) - CSTK.pushValue(gEvalState.currentVariable->value); - else - CSTK.pushString(""); + CSTK.pushValue(gEvalState.currentVariable->value); + else + CSTK.pushString(""); break; case OP_PUSH_FRAME: STR.pushFrame(); - CSTK.pushFrame(); + CSTK.pushFrame(); break; case OP_ASSERT: @@ -2162,9 +2155,8 @@ execFinished: if ( telDebuggerOn && setFrame < 0 ) TelDebugger->popStackFrame(); - if ( popFrame ) { + if ( popFrame ) gEvalState.popFrame(); - } if(argv) { diff --git a/Engine/source/console/compiler.h b/Engine/source/console/compiler.h index 150b92b2e..1873d704d 100644 --- a/Engine/source/console/compiler.h +++ b/Engine/source/console/compiler.h @@ -54,8 +54,8 @@ namespace Compiler OP_RETURN, // fixes a bug when not explicitly returning a value OP_RETURN_VOID, - OP_RETURN_FLT, - OP_RETURN_UINT, + OP_RETURN_FLT, + OP_RETURN_UINT, OP_CMPEQ, OP_CMPGR, diff --git a/Engine/source/console/console.cpp b/Engine/source/console/console.cpp index efe658db6..8ef659891 100644 --- a/Engine/source/console/console.cpp +++ b/Engine/source/console/console.cpp @@ -745,9 +745,9 @@ bool getVariableObjectField(const char *name, SimObject **object, const char **f } else { - *object = obj; - *field = fieldToken; - return true; + *object = obj; + *field = fieldToken; + return true; } } } @@ -778,9 +778,8 @@ Dictionary::Entry *getAddVariableEntry(const char *name) name = prependDollar(name); StringTableEntry stName = StringTable->insert(name); Dictionary::Entry *entry = gEvalState.globalVars.lookup(stName); - if (!entry) { + if (!entry) entry = gEvalState.globalVars.add(stName); - } return entry; } @@ -789,9 +788,8 @@ Dictionary::Entry *getAddLocalVariableEntry(const char *name) name = prependPercent(name); StringTableEntry stName = StringTable->insert(name); Dictionary::Entry *entry = gEvalState.getCurrentFrame().lookup(stName); - if (!entry) { + if (!entry) entry = gEvalState.getCurrentFrame().add(stName); - } return entry; } @@ -800,9 +798,12 @@ void setVariable(const char *name, const char *value) SimObject *obj = NULL; const char *objField = NULL; - if (getVariableObjectField(name, &obj, &objField)) { + if (getVariableObjectField(name, &obj, &objField)) + { obj->setDataField(StringTable->insert(objField), 0, value); - } else { + } + else + { name = prependDollar(name); gEvalState.globalVars.setVariable(StringTable->insert(name), value); } @@ -819,9 +820,12 @@ void setBoolVariable(const char *varName, bool value) SimObject *obj = NULL; const char *objField = NULL; - if (getVariableObjectField(varName, &obj, &objField)) { + if (getVariableObjectField(varName, &obj, &objField)) + { obj->setDataField(StringTable->insert(objField), 0, value ? "1" : "0"); - } else { + } + else + { varName = prependDollar(varName); Dictionary::Entry *entry = getAddVariableEntry(varName); entry->setStringValue(value ? "1" : "0"); @@ -833,11 +837,14 @@ void setIntVariable(const char *varName, S32 value) SimObject *obj = NULL; const char *objField = NULL; - if (getVariableObjectField(varName, &obj, &objField)) { + if (getVariableObjectField(varName, &obj, &objField)) + { char scratchBuffer[32]; dSprintf(scratchBuffer, sizeof(scratchBuffer), "%d", value); obj->setDataField(StringTable->insert(objField), 0, scratchBuffer); - } else { + } + else + { varName = prependDollar(varName); Dictionary::Entry *entry = getAddVariableEntry(varName); entry->setIntValue(value); @@ -849,11 +856,14 @@ void setFloatVariable(const char *varName, F32 value) SimObject *obj = NULL; const char *objField = NULL; - if (getVariableObjectField(varName, &obj, &objField)) { + if (getVariableObjectField(varName, &obj, &objField)) + { char scratchBuffer[32]; dSprintf(scratchBuffer, sizeof(scratchBuffer), "%g", value); obj->setDataField(StringTable->insert(objField), 0, scratchBuffer); - } else { + } + else + { varName = prependDollar(varName); Dictionary::Entry *entry = getAddVariableEntry(varName); entry->setFloatValue(value); @@ -951,11 +961,14 @@ const char *getObjectTokenField(const char *name) const char *getVariable(const char *name) { const char *objField = getObjectTokenField(name); - if (objField) { + if (objField) + { return objField; - } else { + } + else + { Dictionary::Entry *entry = getVariableEntry(name); - return entry ? entry->getStringValue() : ""; + return entry ? entry->getStringValue() : ""; } } @@ -969,11 +982,14 @@ const char *getLocalVariable(const char *name) bool getBoolVariable(const char *varName, bool def) { const char *objField = getObjectTokenField(varName); - if (objField) { + if (objField) + { return *objField ? dAtob(objField) : def; - } else { + } + else + { Dictionary::Entry *entry = getVariableEntry(varName); - objField = entry ? entry->getStringValue() : ""; + objField = entry ? entry->getStringValue() : ""; return *objField ? dAtob(objField) : def; } } @@ -981,22 +997,28 @@ bool getBoolVariable(const char *varName, bool def) S32 getIntVariable(const char *varName, S32 def) { const char *objField = getObjectTokenField(varName); - if (objField) { + if (objField) + { return *objField ? dAtoi(objField) : def; - } else { + } + else + { Dictionary::Entry *entry = getVariableEntry(varName); - return entry ? entry->getIntValue() : def; + return entry ? entry->getIntValue() : def; } } F32 getFloatVariable(const char *varName, F32 def) { const char *objField = getObjectTokenField(varName); - if (objField) { + if (objField) + { return *objField ? dAtof(objField) : def; - } else { + } + else + { Dictionary::Entry *entry = getVariableEntry(varName); - return entry ? entry->getFloatValue() : def; + return entry ? entry->getFloatValue() : def; } } @@ -1154,11 +1176,11 @@ const char *execute(S32 argc, ConsoleValueRef argv[]) if(!ent) { - warnf(ConsoleLogEntry::Script, "%s: Unknown command.", argv[0]); + warnf(ConsoleLogEntry::Script, "%s: Unknown command.", (const char*)argv[0]); // Clean up arg buffers, if any. STR.clearFunctionOffset(); - CSTK.resetFrame(); + CSTK.resetFrame(); return ""; } return ent->execute(argc, argv, &gEvalState); @@ -1184,7 +1206,6 @@ const char *execute(S32 argc, const char *argv[]) //------------------------------------------------------------------------------ const char *execute(SimObject *object, S32 argc, ConsoleValueRef argv[], bool thisCallOnly) { - //static char idBuf[16]; if(argc < 2) return ""; @@ -1194,13 +1215,14 @@ const char *execute(SimObject *object, S32 argc, ConsoleValueRef argv[], bool th if( !thisCallOnly ) { ICallMethod *com = dynamic_cast(object); - if(com) { + if(com) + { STR.pushFrame(); CSTK.pushFrame(); com->callMethodArgList(argc, argv, false); STR.popFrame(); CSTK.popFrame(); - } + } } if(object->getNamespace()) @@ -1218,14 +1240,13 @@ const char *execute(SimObject *object, S32 argc, ConsoleValueRef argv[], bool th //warnf(ConsoleLogEntry::Script, "%s: undefined for object '%s' - id %d", funcName, object->getName(), object->getId()); // Clean up arg buffers, if any. - //CSTK. STR.clearFunctionOffset(); - CSTK.resetFrame(); + CSTK.resetFrame(); return ""; } // Twiddle %this argument - argv[1] = (S32)ident; + argv[1] = (S32)ident; SimObject *save = gEvalState.thisObject; gEvalState.thisObject = object; @@ -1237,7 +1258,7 @@ const char *execute(SimObject *object, S32 argc, ConsoleValueRef argv[], bool th return ret; } - warnf(ConsoleLogEntry::Script, "Con::execute - %d has no namespace: %s", object->getId(), argv[0]); + warnf(ConsoleLogEntry::Script, "Con::execute - %d has no namespace: %s", object->getId(), (const char*)argv[0]); return ""; } @@ -1252,7 +1273,7 @@ inline const char*_executef(SimObject *obj, S32 checkArgc, S32 argc, ConsoleValu const U32 maxArg = 12; AssertWarn(checkArgc == argc, "Incorrect arg count passed to Con::executef(SimObject*)"); AssertFatal(argc <= maxArg - 1, "Too many args passed to Con::_executef(SimObject*). Please update the function to handle more."); - return execute(obj, argc, argv); // jamesu - argc should == argc + return execute(obj, argc, argv); } #define A ConsoleValueRef @@ -1589,14 +1610,16 @@ StringStackWrapper::StringStackWrapper(int targc, ConsoleValueRef targv[]) argv = new const char*[targc]; argc = targc; - for (int i=0; i(val); - if(sval != typeValueEmpty) - { - if (type != TypeInternalStackString) dFree(sval); - sval = typeValueEmpty; - } - type = TypeInternalFloat; - } - else - { - const char *dptr = Con::getData(TypeF32, &val, 0); - Con::setData(type, dataPtr, 0, 1, &dptr, enumTable); - } + const char *dptr = Con::getData(TypeS32, &val, 0); + Con::setData(type, dataPtr, 0, 1, &dptr, enumTable); } +} + +void ConsoleValue::setFloatValue(F32 val) +{ + if(type <= TypeInternalString) + { + fval = val; + ival = static_cast(val); + if(sval != typeValueEmpty) + { + if (type != TypeInternalStackString) dFree(sval); + sval = typeValueEmpty; + } + type = TypeInternalFloat; + } + else + { + const char *dptr = Con::getData(TypeF32, &val, 0); + Con::setData(type, dataPtr, 0, 1, &dptr, enumTable); + } +} - const char *ConsoleValueRef::getStringArgValue() +const char *ConsoleValueRef::getStringArgValue() +{ + if (value) { - if (value) { - if (stringStackValue == NULL) { - stringStackValue = Con::getStringArg(value->getStringValue()); - } - return stringStackValue; - } else { - return ""; - } + if (stringStackValue == NULL) + stringStackValue = Con::getStringArg(value->getStringValue()); + return stringStackValue; } + else + { + return ""; + } +} extern ConsoleValueStack CSTK; @@ -1748,4 +1774,4 @@ namespace Con { CSTK.resetFrame(); } -} \ No newline at end of file +} diff --git a/Engine/source/console/console.h b/Engine/source/console/console.h index 8e2802d48..2783a048f 100644 --- a/Engine/source/console/console.h +++ b/Engine/source/console/console.h @@ -123,7 +123,7 @@ public: { TypeInternalInt = -4, TypeInternalFloat = -3, - TypeInternalStackString = -2, + TypeInternalStackString = -2, TypeInternalString = -1, }; @@ -197,9 +197,10 @@ public: }; // Proxy class for console variables -// Can point to existing console variables -// or act like a free floating value -class ConsoleValueRef { +// Can point to existing console variables, +// or act like a free floating value. +class ConsoleValueRef +{ public: ConsoleValue *value; const char *stringStackValue; @@ -219,13 +220,11 @@ public: inline S32 getIntValue() { return value ? value->getIntValue() : 0; } inline F32 getFloatValue() { return value ? value->getFloatValue() : 0.0f; } - //inline F64 getDoubleValue() { return value ? value->getDoubleValue() : 0.0; } inline operator const char*() { return getStringValue(); } inline operator String() { return String(getStringValue()); } inline operator S32() { return getIntValue(); } inline operator F32() { return getFloatValue(); } - //inline operator F64() { return getDoubleValue(); } inline bool isString() { return value ? value->type >= ConsoleValue::TypeInternalStackString : true; } inline bool isInt() { return value ? value->type == ConsoleValue::TypeInternalInt : false; } @@ -239,6 +238,19 @@ public: ConsoleValueRef& operator=(F64 newValue); }; +// Overrides to allow ConsoleValueRefs to be directly converted to S32&F32 + +inline S32 dAtoi(ConsoleValueRef &ref) +{ + return ref.getIntValue(); +} + +inline F32 dAtof(ConsoleValueRef &ref) +{ + return ref.getFloatValue(); +} + + // Transparently converts ConsoleValue[] to const char** class StringStackWrapper { @@ -342,6 +354,7 @@ namespace Con /// 09/12/07 - CAF - 43->44 remove newmsg operator /// 09/27/07 - RDB - 44->45 Patch from Andreas Kirsch: Added opcode to support correct void return /// 01/13/09 - TMS - 45->46 Added script assert + /// 10/11/12 - JU - 46->47 Added opcodes to reduce reliance on strings in function calls DSOVersion = 47, MaxLineLength = 512, ///< Maximum length of a line of console input. @@ -807,8 +820,8 @@ namespace Con char* getReturnBuffer( const StringBuilder& str ); char* getArgBuffer(U32 bufferSize); - char* getFloatArg(F64 arg); - char* getIntArg (S32 arg); + ConsoleValueRef getFloatArg(F64 arg); + ConsoleValueRef getIntArg (S32 arg); char* getStringArg( const char *arg ); char* getStringArg( const String& arg ); /// @} diff --git a/Engine/source/console/consoleFunctions.cpp b/Engine/source/console/consoleFunctions.cpp index bc3870cec..9e5208375 100644 --- a/Engine/source/console/consoleFunctions.cpp +++ b/Engine/source/console/consoleFunctions.cpp @@ -1306,7 +1306,7 @@ ConsoleFunction(getTag, const char *, 2, 2, "(string textTagString)" TORQUE_UNUSED(argc); if(argv[1][0] == StringTagPrefixByte) { - const char *arg = argv[1]; + const char *arg = argv[1]; const char * space = dStrchr(argv[1], ' '); U32 len; @@ -2364,7 +2364,7 @@ ConsoleFunction(isDefined, bool, 2, 3, "(string varName)" if (dStrcmp(argv[1], "0") && dStrcmp(argv[1], "") && (Sim::findObject(argv[1]) != NULL)) return true; else if (argc > 2) - Con::errorf("%s() - can't assign a value to a variable of the form \"%s\"", __FUNCTION__, argv[1]); + Con::errorf("%s() - can't assign a value to a variable of the form \"%s\"", __FUNCTION__, (const char*)argv[1]); } return false; @@ -2419,7 +2419,7 @@ ConsoleFunction(getPrefsPath, const char *, 1, 2, "([relativeFileName])" "@note Appears to be useless in Torque 3D, should be deprecated\n" "@internal") { - const char *filename = Platform::getPrefsPath(argc > 1 ? argv[1] : NULL); + const char *filename = Platform::getPrefsPath(argc > 1 ? (const char*)argv[1] : NULL); if(filename == NULL || *filename == 0) return ""; diff --git a/Engine/source/console/consoleInternal.cpp b/Engine/source/console/consoleInternal.cpp index 208c998cc..c8c497515 100644 --- a/Engine/source/console/consoleInternal.cpp +++ b/Engine/source/console/consoleInternal.cpp @@ -34,45 +34,6 @@ //#define DEBUG_SPEW - -Dictionary::Entry smLocalDictionaryEntryStack[4096*4]; -Dictionary::Entry *smLocalDictionaryEntryStackHead = NULL; - -void setupDictionaryStack() -{ - smLocalDictionaryEntryStackHead = &smLocalDictionaryEntryStack[0]; - - for (int i=0; i<4096*4; i++) { - (smLocalDictionaryEntryStackHead + i)->mNext = i == (4096*4)-1 ? NULL : smLocalDictionaryEntryStackHead + (i+1); - } -} - -Dictionary::Entry * getDictionaryStackEntry() -{ - Dictionary::Entry *entry = smLocalDictionaryEntryStackHead; - AssertFatal(entry, "No more local variables"); - - entry->reset(); - - Dictionary::Entry *next = entry->mNext; - - smLocalDictionaryEntryStackHead = next; - - entry->mNext = NULL; - - return entry; -} - -void disposeDictionaryStackEntry(Dictionary::Entry *entry) -{ - Dictionary::Entry *prevHead = smLocalDictionaryEntryStackHead; - smLocalDictionaryEntryStackHead = entry; - - smLocalDictionaryEntryStackHead->mNext = prevHead; -} - - - #define ST_INIT_SIZE 15 static char scratchBuffer[1024]; @@ -325,10 +286,8 @@ Dictionary::Entry *Dictionary::add(StringTableEntry name) //printf("Add Variable %s\n", name); Entry* ret = lookup( name ); - if( ret ) { - //printf("Found Variable %s (named %s)\n", name, ret->name); + if( ret ) return ret; - } // Rehash if the table get's too crowded. Be aware that this might // modify a table that we don't own. @@ -337,7 +296,6 @@ Dictionary::Entry *Dictionary::add(StringTableEntry name) if( hashTable->count > hashTable->size * 2 ) { // Allocate a new table. - printf("Re-hashing dictionary...\n"); const U32 newTableSize = hashTable->size * 4 - 1; Entry** newTableData = new Entry*[ newTableSize ]; @@ -351,9 +309,6 @@ Dictionary::Entry *Dictionary::add(StringTableEntry name) Entry* next = entry->nextEntry; U32 index = HashPointer( entry->name ) % newTableSize; - - //printf(" Variable(%s) in bucket %i moved to bucket %i\n", entry->name, i, index); - entry->nextEntry = newTableData[ index ]; newTableData[ index ] = entry; @@ -373,9 +328,8 @@ Dictionary::Entry *Dictionary::add(StringTableEntry name) // Add the new entry. - ret = getDictionaryStackEntry();//hashTable->mChunker.alloc(); - ret->name = name; - //constructInPlace( ret, name ); + ret = hashTable->mChunker.alloc(); + constructInPlace( ret, name ); U32 idx = HashPointer(name) % hashTable->size; ret->nextEntry = hashTable->data[idx]; hashTable->data[idx] = ret; @@ -396,8 +350,8 @@ void Dictionary::remove(Dictionary::Entry *ent) *walk = (ent->nextEntry); - disposeDictionaryStackEntry( ent ); - //hashTable->mChunker.free( ent ); + destructInPlace( ent ); + hashTable->mChunker.free( ent ); hashTable->count--; } @@ -458,13 +412,13 @@ void Dictionary::reset() while( walk ) { Entry* temp = walk->nextEntry; - disposeDictionaryStackEntry( walk ); + destructInPlace( walk ); walk = temp; } } dMemset( ownHashTable.data, 0, ownHashTable.size * sizeof( Entry* ) ); - //ownHashTable.mChunker.freeBlocks( true ); + ownHashTable.mChunker.freeBlocks( true ); ownHashTable.count = 0; hashTable = NULL; @@ -556,15 +510,16 @@ void ConsoleValue::setStringValue(const char * value) return; } */ - if (value == typeValueEmpty) { - if (sval && sval != typeValueEmpty && type != TypeInternalStackString) dFree(sval); - sval = typeValueEmpty; - bufferLen = 0; - fval = 0.f; - ival = 0; - type = TypeInternalString; - return; - } + if (value == typeValueEmpty) + { + if (sval && sval != typeValueEmpty && type != TypeInternalStackString) dFree(sval); + sval = typeValueEmpty; + bufferLen = 0; + fval = 0.f; + ival = 0; + type = TypeInternalString; + return; + } U32 stringLen = dStrlen(value); @@ -586,7 +541,7 @@ void ConsoleValue::setStringValue(const char * value) // may as well pad to the next cache line U32 newLen = ((stringLen + 1) + 15) & ~15; - if(sval == typeValueEmpty || type == TypeInternalStackString) + if(sval == typeValueEmpty || type == TypeInternalStackString) sval = (char *) dMalloc(newLen); else if(newLen > bufferLen) sval = (char *) dRealloc(sval, newLen); @@ -607,15 +562,16 @@ void ConsoleValue::setStackStringValue(const char * value) if(type <= ConsoleValue::TypeInternalString) { - if (value == typeValueEmpty) { - if (sval && sval != typeValueEmpty && type != ConsoleValue::TypeInternalStackString) dFree(sval); - sval = typeValueEmpty; - bufferLen = 0; + if (value == typeValueEmpty) + { + if (sval && sval != typeValueEmpty && type != ConsoleValue::TypeInternalStackString) dFree(sval); + sval = typeValueEmpty; + bufferLen = 0; fval = 0.f; ival = 0; - type = TypeInternalString; - return; - } + type = TypeInternalString; + return; + } U32 stringLen = dStrlen(value); if(stringLen < 256) @@ -640,32 +596,34 @@ void ConsoleValue::setStackStringValue(const char * value) S32 Dictionary::getIntVariable(StringTableEntry name, bool *entValid) { - Entry *ent = lookup(name); - if(ent) - { - if(entValid) - *entValid = true; - return ent->getIntValue(); - } - if(entValid) - *entValid = false; + Entry *ent = lookup(name); + if(ent) + { + if(entValid) + *entValid = true; + return ent->getIntValue(); + } + + if(entValid) + *entValid = false; return 0; } F32 Dictionary::getFloatVariable(StringTableEntry name, bool *entValid) { - Entry *ent = lookup(name); - if(ent) - { - if(entValid) - *entValid = true; - return ent->getFloatValue(); - } - if(entValid) - *entValid = false; + Entry *ent = lookup(name); + if(ent) + { + if(entValid) + *entValid = true; + return ent->getFloatValue(); + } - return 0; + if(entValid) + *entValid = false; + + return 0; } void Dictionary::setVariable(StringTableEntry name, const char *value) @@ -726,7 +684,7 @@ void Dictionary::addVariableNotify( const char *name, const Con::NotifyDelegate return; if ( !ent->notify ) - ent->notify = new Entry::NotifySignal(); + ent->notify = new Entry::NotifySignal(); ent->notify->notify( callback ); } @@ -1141,8 +1099,6 @@ void Namespace::init() mGlobalNamespace->mName = NULL; mGlobalNamespace->mNext = NULL; mNamespaceList = mGlobalNamespace; - - setupDictionaryStack(); } Namespace *Namespace::global() diff --git a/Engine/source/console/consoleInternal.h b/Engine/source/console/consoleInternal.h index da465d74f..e6dd6b99d 100644 --- a/Engine/source/console/consoleInternal.h +++ b/Engine/source/console/consoleInternal.h @@ -384,138 +384,138 @@ public: } }; - struct HashTableData - { - Dictionary* owner; - S32 size; - S32 count; - Entry **data; - FreeListChunker< Entry > mChunker; - - HashTableData( Dictionary* owner ) - : owner( owner ), size( 0 ), count( 0 ), data( NULL ) {} - }; + struct HashTableData + { + Dictionary* owner; + S32 size; + S32 count; + Entry **data; + FreeListChunker< Entry > mChunker; + + HashTableData( Dictionary* owner ) + : owner( owner ), size( 0 ), count( 0 ), data( NULL ) {} + }; - HashTableData* hashTable; - HashTableData ownHashTable; - ExprEvalState *exprState; - - StringTableEntry scopeName; - Namespace *scopeNamespace; - CodeBlock *code; - U32 ip; + HashTableData* hashTable; + HashTableData ownHashTable; + ExprEvalState *exprState; - Dictionary(); - ~Dictionary(); + StringTableEntry scopeName; + Namespace *scopeNamespace; + CodeBlock *code; + U32 ip; - Entry *lookup(StringTableEntry name); - Entry *add(StringTableEntry name); - void setState(ExprEvalState *state, Dictionary* ref=NULL); - void remove(Entry *); - void reset(); + Dictionary(); + ~Dictionary(); - void exportVariables( const char *varString, const char *fileName, bool append ); - void exportVariables( const char *varString, Vector *names, Vector *values ); - void deleteVariables( const char *varString ); + Entry *lookup(StringTableEntry name); + Entry *add(StringTableEntry name); + void setState(ExprEvalState *state, Dictionary* ref=NULL); + void remove(Entry *); + void reset(); - void setVariable(StringTableEntry name, const char *value); - const char *getVariable(StringTableEntry name, bool *valid = NULL); - S32 getIntVariable(StringTableEntry name, bool *valid = NULL); - F32 getFloatVariable(StringTableEntry name, bool *entValid = NULL); - - U32 getCount() const - { + void exportVariables( const char *varString, const char *fileName, bool append ); + void exportVariables( const char *varString, Vector *names, Vector *values ); + void deleteVariables( const char *varString ); + + void setVariable(StringTableEntry name, const char *value); + const char *getVariable(StringTableEntry name, bool *valid = NULL); + S32 getIntVariable(StringTableEntry name, bool *valid = NULL); + F32 getFloatVariable(StringTableEntry name, bool *entValid = NULL); + + U32 getCount() const + { return hashTable->count; - } - bool isOwner() const - { + } + bool isOwner() const + { return hashTable->owner; - } + } - /// @see Con::addVariable - Entry* addVariable( const char *name, - S32 type, - void *dataPtr, - const char* usage ); + /// @see Con::addVariable + Entry* addVariable( const char *name, + S32 type, + void *dataPtr, + const char* usage ); - /// @see Con::removeVariable - bool removeVariable(StringTableEntry name); + /// @see Con::removeVariable + bool removeVariable(StringTableEntry name); - /// @see Con::addVariableNotify - void addVariableNotify( const char *name, const Con::NotifyDelegate &callback ); + /// @see Con::addVariableNotify + void addVariableNotify( const char *name, const Con::NotifyDelegate &callback ); - /// @see Con::removeVariableNotify - void removeVariableNotify( const char *name, const Con::NotifyDelegate &callback ); + /// @see Con::removeVariableNotify + void removeVariableNotify( const char *name, const Con::NotifyDelegate &callback ); - /// 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); - - /// Run integrity checks for debugging. - void validate(); + /// 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); + + /// Run integrity checks for debugging. + void validate(); }; class ExprEvalState { public: - /// @name Expression Evaluation - /// @{ + /// @name Expression Evaluation + /// @{ - /// - SimObject *thisObject; - Dictionary::Entry *currentVariable; - Dictionary::Entry *copyVariable; - bool traceOn; - - U32 mStackDepth; + /// + SimObject *thisObject; + Dictionary::Entry *currentVariable; + Dictionary::Entry *copyVariable; + bool traceOn; - ExprEvalState(); - ~ExprEvalState(); + U32 mStackDepth; - /// @} + ExprEvalState(); + ~ExprEvalState(); - /// @name Stack Management - /// @{ + /// @} - /// The stack of callframes. The extra redirection is necessary since Dictionary holds - /// an interior pointer that will become invalid when the object changes address. - Vector< Dictionary* > stack; + /// @name Stack Management + /// @{ - /// - Dictionary globalVars; - - void setCurVarName(StringTableEntry name); - void setCurVarNameCreate(StringTableEntry name); + /// The stack of callframes. The extra redirection is necessary since Dictionary holds + /// an interior pointer that will become invalid when the object changes address. + Vector< Dictionary* > stack; - S32 getIntVariable(); - F64 getFloatVariable(); - const char *getStringVariable(); - void setIntVariable(S32 val); - void setFloatVariable(F64 val); - void setStringVariable(const char *str); - void setCopyVariable(); + /// + Dictionary globalVars; - void pushFrame(StringTableEntry frameName, Namespace *ns); - void popFrame(); + void setCurVarName(StringTableEntry name); + void setCurVarNameCreate(StringTableEntry name); - /// Puts a reference to an existing stack frame - /// on the top of the stack. - void pushFrameRef(S32 stackIndex); - - U32 getStackDepth() const - { - return mStackDepth; - } - - Dictionary& getCurrentFrame() - { + S32 getIntVariable(); + F64 getFloatVariable(); + const char *getStringVariable(); + void setIntVariable(S32 val); + void setFloatVariable(F64 val); + void setStringVariable(const char *str); + void setCopyVariable(); + + 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); + + U32 getStackDepth() const + { + return mStackDepth; + } + + Dictionary& getCurrentFrame() + { return *( stack[ mStackDepth - 1 ] ); - } + } - /// @} - - /// Run integrity checks for debugging. - void validate(); + /// @} + + /// Run integrity checks for debugging. + void validate(); }; namespace Con diff --git a/Engine/source/console/engineAPI.h b/Engine/source/console/engineAPI.h index 1383277ce..f69396e32 100644 --- a/Engine/source/console/engineAPI.h +++ b/Engine/source/console/engineAPI.h @@ -160,7 +160,7 @@ inline void EngineMarshallData( bool arg, S32& argc, ConsoleValueRef *argv ) } inline void EngineMarshallData( S32 arg, S32& argc, ConsoleValueRef *argv ) { - argv[ argc ] = Con::getIntArg( arg ); + argv[ argc ] = arg; argc ++; } inline void EngineMarshallData( U32 arg, S32& argc, ConsoleValueRef *argv ) @@ -169,7 +169,7 @@ inline void EngineMarshallData( U32 arg, S32& argc, ConsoleValueRef *argv ) } inline void EngineMarshallData( F32 arg, S32& argc, ConsoleValueRef *argv ) { - argv[ argc ] = Con::getFloatArg( arg ); + argv[ argc ] = arg; argc ++; } inline void EngineMarshallData( const char* arg, S32& argc, ConsoleValueRef *argv ) @@ -207,6 +207,11 @@ struct EngineUnmarshallData template<> struct EngineUnmarshallData< S32 > { + S32 operator()( ConsoleValueRef &ref ) const + { + return (S32)ref; + } + S32 operator()( const char* str ) const { return dAtoi( str ); @@ -215,6 +220,11 @@ struct EngineUnmarshallData< S32 > template<> struct EngineUnmarshallData< U32 > { + U32 operator()( ConsoleValueRef &ref ) const + { + return (U32)((S32)ref); + } + U32 operator()( const char* str ) const { return dAtoui( str ); @@ -223,6 +233,11 @@ struct EngineUnmarshallData< U32 > template<> struct EngineUnmarshallData< F32 > { + F32 operator()( ConsoleValueRef &ref ) const + { + return (F32)ref; + } + F32 operator()( const char* str ) const { return dAtof( str ); @@ -2626,12 +2641,12 @@ struct _EngineConsoleCallbackHelper if( mThis ) { // Cannot invoke callback until object has been registered - if (mThis->isProperlyAdded()) { - return Con::execute( mThis, mArgc, mArgv ); - } else { - Con::resetStackFrame(); // jamesu - we might have pushed some vars here - return ""; - } + if (mThis->isProperlyAdded()) { + return Con::execute( mThis, mArgc, mArgv ); + } else { + Con::resetStackFrame(); // We might have pushed some vars here + return ""; + } } else return Con::execute( mArgc, mArgv ); diff --git a/Engine/source/console/fieldBrushObject.cpp b/Engine/source/console/fieldBrushObject.cpp index 0d160ec7c..2bc6906eb 100644 --- a/Engine/source/console/fieldBrushObject.cpp +++ b/Engine/source/console/fieldBrushObject.cpp @@ -383,7 +383,7 @@ ConsoleMethod(FieldBrushObject, copyFields, void, 3, 4, "(simObject, [fieldList] } // Fetch field list. - const char* pFieldList = (argc > 3 ) ? argv[3] : NULL; + const char* pFieldList = (argc > 3 ) ? (const char*)argv[3] : NULL; // Copy Fields. object->copyFields( pSimObject, pFieldList ); diff --git a/Engine/source/console/persistenceManager.cpp b/Engine/source/console/persistenceManager.cpp index c269c44a8..23dcd754e 100644 --- a/Engine/source/console/persistenceManager.cpp +++ b/Engine/source/console/persistenceManager.cpp @@ -2204,7 +2204,7 @@ ConsoleMethod( PersistenceManager, setDirty, void, 3, 4, "(SimObject object, [fi { if (!Sim::findObject(argv[2], dirtyObject)) { - Con::printf("%s(): Invalid SimObject: %s", argv[0], argv[2]); + Con::printf("%s(): Invalid SimObject: %s", (const char*)argv[0], (const char*)argv[2]); return; } } @@ -2213,7 +2213,7 @@ ConsoleMethod( PersistenceManager, setDirty, void, 3, 4, "(SimObject object, [fi if( dirtyObject == Sim::getRootGroup() ) { - Con::errorf( "%s(): Cannot save RootGroup", argv[ 0 ] ); + Con::errorf( "%s(): Cannot save RootGroup", (const char*)argv[ 0 ] ); return; } @@ -2234,7 +2234,7 @@ ConsoleMethod( PersistenceManager, removeDirty, void, 3, 3, "(SimObject object)" { if (!Sim::findObject(argv[2], dirtyObject)) { - Con::printf("%s(): Invalid SimObject: %s", argv[0], argv[2]); + Con::printf("%s(): Invalid SimObject: %s", (const char*)argv[0], (const char*)argv[2]); return; } } @@ -2251,7 +2251,7 @@ ConsoleMethod( PersistenceManager, isDirty, bool, 3, 3, "(SimObject object)" { if (!Sim::findObject(argv[2], dirtyObject)) { - Con::printf("%s(): Invalid SimObject: %s", argv[0], argv[2]); + Con::printf("%s(): Invalid SimObject: %s", (const char*)argv[0], (const char*)argv[2]); return false; } } @@ -2280,7 +2280,7 @@ ConsoleMethod( PersistenceManager, getDirtyObject, S32, 3, 3, "( index )" const S32 index = dAtoi( argv[2] ); if ( index < 0 || index >= object->getDirtyList().size() ) { - Con::warnf( "PersistenceManager::getDirtyObject() - Index (%s) out of range.", argv[2] ); + Con::warnf( "PersistenceManager::getDirtyObject() - Index (%s) out of range.", (const char*)argv[2] ); return 0; } @@ -2333,7 +2333,7 @@ ConsoleMethod( PersistenceManager, saveDirtyObject, bool, 3, 3, "(SimObject obje { if (!Sim::findObject(argv[2], dirtyObject)) { - Con::printf("%s(): Invalid SimObject: %s", argv[0], argv[2]); + Con::printf("%s(): Invalid SimObject: %s", (const char*)argv[0], (const char*)argv[2]); return false; } } @@ -2358,7 +2358,7 @@ ConsoleMethod( PersistenceManager, removeObjectFromFile, void, 3, 4, "(SimObject { if (!Sim::findObject(argv[2], dirtyObject)) { - Con::printf("%s(): Invalid SimObject: %s", argv[0], argv[2]); + Con::printf("%s(): Invalid SimObject: %s", (const char*)argv[0], (const char*)argv[2]); return; } } @@ -2380,7 +2380,7 @@ ConsoleMethod( PersistenceManager, removeField, void, 4, 4, "(SimObject object, { if (!Sim::findObject(argv[2], dirtyObject)) { - Con::printf("%s(): Invalid SimObject: %s", argv[0], argv[2]); + Con::printf("%s(): Invalid SimObject: %s", (const char*)argv[0], (const char*)argv[2]); return; } } @@ -2390,4 +2390,4 @@ ConsoleMethod( PersistenceManager, removeField, void, 4, 4, "(SimObject object, if (argv[3][0]) object->addRemoveField(dirtyObject, argv[3]); } -} \ No newline at end of file +} diff --git a/Engine/source/console/sim.cpp b/Engine/source/console/sim.cpp index aa41316a2..918a69b79 100644 --- a/Engine/source/console/sim.cpp +++ b/Engine/source/console/sim.cpp @@ -138,20 +138,20 @@ ConsoleDocFragment _spawnObject1( ConsoleFunction(spawnObject, S32, 3, 6, "spawnObject(class [, dataBlock, name, properties, script])" "@hide") { - String spawnClass((String)argv[1]); + String spawnClass((const char*)argv[1]); String spawnDataBlock; String spawnName; String spawnProperties; String spawnScript; if (argc >= 3) - spawnDataBlock = (String)argv[2]; + spawnDataBlock = (const char*)argv[2]; if (argc >= 4) - spawnName = (String)argv[3]; + spawnName = (const char*)argv[3]; if (argc >= 5) - spawnProperties = (String)argv[4]; + spawnProperties = (const char*)argv[4]; if (argc >= 6) - spawnScript = (String)argv[5]; + spawnScript = (const char*)argv[5]; SimObject* spawnObject = Sim::spawnObject(spawnClass, spawnDataBlock, spawnName, spawnProperties, spawnScript); diff --git a/Engine/source/console/sim.h b/Engine/source/console/sim.h index c7cfa52fb..013721872 100644 --- a/Engine/source/console/sim.h +++ b/Engine/source/console/sim.h @@ -32,6 +32,9 @@ #ifndef _MODULE_H_ #include "core/module.h" #endif +#ifndef _CONSOLE_H_ +#include "console/console.h" +#endif // Forward Refs class SimSet; @@ -122,9 +125,15 @@ namespace Sim SimDataBlockGroup *getDataBlockGroup(); SimGroup* getRootGroup(); + SimObject* findObject(ConsoleValueRef&); SimObject* findObject(SimObjectId); SimObject* findObject(const char* name); SimObject* findObject(const char* fileName, S32 declarationLine); + template inline bool findObject(ConsoleValueRef &ref,T*&t) + { + t = dynamic_cast(findObject(ref)); + return t != NULL; + } template inline bool findObject(SimObjectId iD,T*&t) { t = dynamic_cast(findObject(iD)); diff --git a/Engine/source/console/simEvents.cpp b/Engine/source/console/simEvents.cpp index 3fe32b2a5..afb634bdf 100644 --- a/Engine/source/console/simEvents.cpp +++ b/Engine/source/console/simEvents.cpp @@ -34,17 +34,19 @@ SimConsoleEvent::SimConsoleEvent(S32 argc, ConsoleValueRef *argv, bool onObject) mArgc = argc; mArgv = new ConsoleValueRef[argc]; - for (int i=0; itype = ConsoleValue::TypeInternalString; - mArgv[i].value->init(); + for (int i=0; itype = ConsoleValue::TypeInternalString; + mArgv[i].value->init(); mArgv[i].value->setStringValue((const char*)argv[i]); } } SimConsoleEvent::~SimConsoleEvent() { - for (int i=0; ifindObjectByLineNumber(fileName, declarationLine, true); } +SimObject* findObject(ConsoleValueRef &ref) +{ + return findObject((const char*)ref); +} + SimObject* findObject(const char* name) { PROFILE_SCOPE(SimFindObject); diff --git a/Engine/source/console/simPersistSet.cpp b/Engine/source/console/simPersistSet.cpp index 82dd6e856..c8630002e 100644 --- a/Engine/source/console/simPersistSet.cpp +++ b/Engine/source/console/simPersistSet.cpp @@ -54,7 +54,7 @@ bool SimPersistSet::processArguments( S32 argc, ConsoleValueRef *argv ) Torque::UUID uuid; if( !uuid.fromString( argv[ i ] ) ) { - Con::errorf( "SimPersistSet::processArguments - could not read UUID at index %i: %s", i, argv[ i ] ); + Con::errorf( "SimPersistSet::processArguments - could not read UUID at index %i: %s", i, (const char*)argv[ i ] ); continue; } diff --git a/Engine/source/console/simSet.cpp b/Engine/source/console/simSet.cpp index 6f72319fe..d03f10248 100644 --- a/Engine/source/console/simSet.cpp +++ b/Engine/source/console/simSet.cpp @@ -909,7 +909,7 @@ ConsoleMethod( SimSet, add, void, 3, 0, if(obj) object->addObject( obj ); else - Con::printf("Set::add: Object \"%s\" doesn't exist", argv[ i ] ); + Con::printf("Set::add: Object \"%s\" doesn't exist", (const char*)argv[ i ] ); } } @@ -934,7 +934,7 @@ ConsoleMethod( SimSet, remove, void, 3, 0, if(obj && object->find(object->begin(),object->end(),obj) != object->end()) object->removeObject(obj); else - Con::printf("Set::remove: Object \"%s\" does not exist in set", argv[i]); + Con::printf("Set::remove: Object \"%s\" does not exist in set", (const char*)argv[i]); object->unlock(); } } diff --git a/Engine/source/console/stringStack.cpp b/Engine/source/console/stringStack.cpp index 1473d614c..e57445657 100644 --- a/Engine/source/console/stringStack.cpp +++ b/Engine/source/console/stringStack.cpp @@ -34,9 +34,9 @@ void ConsoleValueStack::getArgcArgv(StringTableEntry name, U32 *argc, ConsoleVal mArgv[0] = name; for(U32 i = 0; i < argCount; i++) { - ConsoleValueRef *ref = &mArgv[i+1]; - ref->value = &mStack[startStack + i]; - ref->stringStackValue = NULL; + ConsoleValueRef *ref = &mArgv[i+1]; + ref->value = &mStack[startStack + i]; + ref->stringStackValue = NULL; } argCount++; @@ -50,10 +50,10 @@ ConsoleValueStack::ConsoleValueStack() : mFrame(0), mStackPos(0) { - for (int i=0; itype) - { - case ConsoleValue::TypeInternalInt: - mStack[mStackPos++].setIntValue((S32)variable->getIntValue()); - case ConsoleValue::TypeInternalFloat: - mStack[mStackPos++].setFloatValue((F32)variable->getFloatValue()); - default: - mStack[mStackPos++].setStackStringValue(variable->getStringValue()); - } + switch (variable->type) + { + case ConsoleValue::TypeInternalInt: + mStack[mStackPos++].setIntValue((S32)variable->getIntValue()); + case ConsoleValue::TypeInternalFloat: + mStack[mStackPos++].setFloatValue((F32)variable->getFloatValue()); + default: + mStack[mStackPos++].setStackStringValue(variable->getStringValue()); + } } void ConsoleValueStack::pushValue(ConsoleValue &variable) { - if (mStackPos == ConsoleValueStack::MaxStackDepth) { - AssertFatal(false, "Console Value Stack is empty"); - return; - } + if (mStackPos == ConsoleValueStack::MaxStackDepth) { + AssertFatal(false, "Console Value Stack is empty"); + return; + } - switch (variable.type) - { - case ConsoleValue::TypeInternalInt: - mStack[mStackPos++].setIntValue((S32)variable.getIntValue()); - case ConsoleValue::TypeInternalFloat: - mStack[mStackPos++].setFloatValue((F32)variable.getFloatValue()); - default: - mStack[mStackPos++].setStringValue(variable.getStringValue()); - } + switch (variable.type) + { + case ConsoleValue::TypeInternalInt: + mStack[mStackPos++].setIntValue((S32)variable.getIntValue()); + case ConsoleValue::TypeInternalFloat: + mStack[mStackPos++].setFloatValue((F32)variable.getFloatValue()); + default: + mStack[mStackPos++].setStringValue(variable.getStringValue()); + } } ConsoleValue *ConsoleValueStack::pushString(const char *value) { - if (mStackPos == ConsoleValueStack::MaxStackDepth) { - AssertFatal(false, "Console Value Stack is empty"); - return NULL; - } + if (mStackPos == ConsoleValueStack::MaxStackDepth) { + AssertFatal(false, "Console Value Stack is empty"); + return NULL; + } - //Con::printf("[%i]CSTK pushString %s", mStackPos, value); + //Con::printf("[%i]CSTK pushString %s", mStackPos, value); - mStack[mStackPos++].setStringValue(value); - return &mStack[mStackPos-1]; + mStack[mStackPos++].setStringValue(value); + return &mStack[mStackPos-1]; } ConsoleValue *ConsoleValueStack::pushStackString(const char *value) { - if (mStackPos == ConsoleValueStack::MaxStackDepth) { - AssertFatal(false, "Console Value Stack is empty"); - return NULL; - } + if (mStackPos == ConsoleValueStack::MaxStackDepth) { + AssertFatal(false, "Console Value Stack is empty"); + return NULL; + } - //Con::printf("[%i]CSTK pushString %s", mStackPos, value); + //Con::printf("[%i]CSTK pushString %s", mStackPos, value); - mStack[mStackPos++].setStackStringValue(value); - return &mStack[mStackPos-1]; + mStack[mStackPos++].setStackStringValue(value); + return &mStack[mStackPos-1]; } ConsoleValue *ConsoleValueStack::pushUINT(U32 value) { - if (mStackPos == ConsoleValueStack::MaxStackDepth) { - AssertFatal(false, "Console Value Stack is empty"); - return NULL; - } + if (mStackPos == ConsoleValueStack::MaxStackDepth) { + AssertFatal(false, "Console Value Stack is empty"); + return NULL; + } - //Con::printf("[%i]CSTK pushUINT %i", mStackPos, value); + //Con::printf("[%i]CSTK pushUINT %i", mStackPos, value); - mStack[mStackPos++].setIntValue(value); - return &mStack[mStackPos-1]; + mStack[mStackPos++].setIntValue(value); + return &mStack[mStackPos-1]; } ConsoleValue *ConsoleValueStack::pushFLT(float value) { - if (mStackPos == ConsoleValueStack::MaxStackDepth) { - AssertFatal(false, "Console Value Stack is empty"); - return NULL; - } + if (mStackPos == ConsoleValueStack::MaxStackDepth) { + AssertFatal(false, "Console Value Stack is empty"); + return NULL; + } - //Con::printf("[%i]CSTK pushFLT %f", mStackPos, value); + //Con::printf("[%i]CSTK pushFLT %f", mStackPos, value); - mStack[mStackPos++].setFloatValue(value); - return &mStack[mStackPos-1]; + mStack[mStackPos++].setFloatValue(value); + return &mStack[mStackPos-1]; } static ConsoleValue gNothing; ConsoleValue* ConsoleValueStack::pop() { - if (mStackPos == 0) { - AssertFatal(false, "Console Value Stack is empty"); - return &gNothing; - } + if (mStackPos == 0) { + AssertFatal(false, "Console Value Stack is empty"); + return &gNothing; + } - return &mStack[--mStackPos]; + return &mStack[--mStackPos]; } void ConsoleValueStack::pushFrame() { - //Con::printf("CSTK pushFrame"); - mStackFrames[mFrame++] = mStackPos; + //Con::printf("CSTK pushFrame"); + mStackFrames[mFrame++] = mStackPos; } void ConsoleValueStack::resetFrame() { - if (mFrame == 0) { - mStackPos = 0; - return; - } + if (mFrame == 0) { + mStackPos = 0; + return; + } - U32 start = mStackFrames[mFrame-1]; - //for (U32 i=start; imatchTerrainToRoad(); -} \ No newline at end of file +} diff --git a/Engine/source/environment/editors/guiRiverEditorCtrl.cpp b/Engine/source/environment/editors/guiRiverEditorCtrl.cpp index 26fabc99b..cbd776618 100644 --- a/Engine/source/environment/editors/guiRiverEditorCtrl.cpp +++ b/Engine/source/environment/editors/guiRiverEditorCtrl.cpp @@ -1181,7 +1181,7 @@ void GuiRiverEditorCtrl::setMode( String mode, bool sourceShortcut = false ) mMode = mode; if( sourceShortcut ) - Con::executef( this, "paletteSync", mode.utf8() ); + Con::executef( this, "paletteSync", mode ); } void GuiRiverEditorCtrl::setSelectedRiver( River *river ) @@ -1444,7 +1444,7 @@ ConsoleMethod( GuiRiverEditorCtrl, setNodePosition, void, 3, 3, "" ) if ( (count != 3) ) { - Con::printf("Failed to parse node information \"px py pz\" from '%s'", argv[3]); + Con::printf("Failed to parse node information \"px py pz\" from '%s'", (const char*)argv[3]); return; } @@ -1470,7 +1470,7 @@ ConsoleMethod( GuiRiverEditorCtrl, setNodeNormal, void, 3, 3, "" ) if ( (count != 3) ) { - Con::printf("Failed to parse node information \"px py pz\" from '%s'", argv[3]); + Con::printf("Failed to parse node information \"px py pz\" from '%s'", (const char*)argv[3]); return; } @@ -1503,4 +1503,4 @@ ConsoleMethod( GuiRiverEditorCtrl, regenerate, void, 2, 2, "" ) River *river = object->getSelectedRiver(); if ( river ) river->regenerate(); -} \ No newline at end of file +} diff --git a/Engine/source/environment/editors/guiRoadEditorCtrl.cpp b/Engine/source/environment/editors/guiRoadEditorCtrl.cpp index a5c32abe5..627573691 100644 --- a/Engine/source/environment/editors/guiRoadEditorCtrl.cpp +++ b/Engine/source/environment/editors/guiRoadEditorCtrl.cpp @@ -945,7 +945,7 @@ void GuiRoadEditorCtrl::setMode( String mode, bool sourceShortcut = false ) mMode = mode; if( sourceShortcut ) - Con::executef( this, "paletteSync", mode.utf8() ); + Con::executef( this, "paletteSync", mode ); } void GuiRoadEditorCtrl::setSelectedRoad( DecalRoad *road ) @@ -1081,7 +1081,7 @@ ConsoleMethod( GuiRoadEditorCtrl, setNodePosition, void, 3, 3, "" ) if ( (count != 3) ) { - Con::printf("Failed to parse node information \"px py pz\" from '%s'", argv[3]); + Con::printf("Failed to parse node information \"px py pz\" from '%s'", (const char*)argv[3]); return; } diff --git a/Engine/source/forest/forest.cpp b/Engine/source/forest/forest.cpp index 41da38500..a61bf9f3b 100644 --- a/Engine/source/forest/forest.cpp +++ b/Engine/source/forest/forest.cpp @@ -356,7 +356,7 @@ void Forest::saveDataFile( const char *path ) ConsoleMethod( Forest, saveDataFile, bool, 2, 3, "saveDataFile( [path] )" ) { - object->saveDataFile( argc == 3 ? argv[2] : NULL ); + object->saveDataFile( argc == 3 ? (const char*)argv[2] : NULL ); return true; } @@ -373,4 +373,4 @@ ConsoleMethod(Forest, regenCells, void, 2, 2, "()") ConsoleMethod(Forest, clear, void, 2, 2, "()" ) { object->clear(); -} \ No newline at end of file +} diff --git a/Engine/source/gui/controls/guiPopUpCtrl.cpp b/Engine/source/gui/controls/guiPopUpCtrl.cpp index e4b2903f7..9f3775bd3 100644 --- a/Engine/source/gui/controls/guiPopUpCtrl.cpp +++ b/Engine/source/gui/controls/guiPopUpCtrl.cpp @@ -431,7 +431,7 @@ ConsoleMethod( GuiPopUpMenuCtrl, setEnumContent, void, 4, 4, "(string class, str // get it? if(!classRep) { - Con::warnf(ConsoleLogEntry::General, "failed to locate class rep for '%s'", argv[2]); + Con::warnf(ConsoleLogEntry::General, "failed to locate class rep for '%s'", (const char*)argv[2]); return; } @@ -444,7 +444,7 @@ ConsoleMethod( GuiPopUpMenuCtrl, setEnumContent, void, 4, 4, "(string class, str // found it? if(i == classRep->mFieldList.size()) { - Con::warnf(ConsoleLogEntry::General, "failed to locate field '%s' for class '%s'", argv[3], argv[2]); + Con::warnf(ConsoleLogEntry::General, "failed to locate field '%s' for class '%s'", (const char*)argv[3], (const char*)argv[2]); return; } @@ -454,7 +454,7 @@ ConsoleMethod( GuiPopUpMenuCtrl, setEnumContent, void, 4, 4, "(string class, str // check the type if( !conType->getEnumTable() ) { - Con::warnf(ConsoleLogEntry::General, "field '%s' is not an enumeration for class '%s'", argv[3], argv[2]); + Con::warnf(ConsoleLogEntry::General, "field '%s' is not an enumeration for class '%s'", (const char*)argv[3], (const char*)argv[2]); return; } diff --git a/Engine/source/gui/controls/guiPopUpCtrlEx.cpp b/Engine/source/gui/controls/guiPopUpCtrlEx.cpp index 275faf75c..108f37ea4 100644 --- a/Engine/source/gui/controls/guiPopUpCtrlEx.cpp +++ b/Engine/source/gui/controls/guiPopUpCtrlEx.cpp @@ -604,7 +604,7 @@ ConsoleMethod( GuiPopUpMenuCtrlEx, setEnumContent, void, 4, 4, // get it? if(!classRep) { - Con::warnf(ConsoleLogEntry::General, "failed to locate class rep for '%s'", argv[2]); + Con::warnf(ConsoleLogEntry::General, "failed to locate class rep for '%s'", (const char*)argv[2]); return; } @@ -617,7 +617,7 @@ ConsoleMethod( GuiPopUpMenuCtrlEx, setEnumContent, void, 4, 4, // found it? if(i == classRep->mFieldList.size()) { - Con::warnf(ConsoleLogEntry::General, "failed to locate field '%s' for class '%s'", argv[3], argv[2]); + Con::warnf(ConsoleLogEntry::General, "failed to locate field '%s' for class '%s'", (const char*)argv[3], (const char*)argv[2]); return; } @@ -627,7 +627,7 @@ ConsoleMethod( GuiPopUpMenuCtrlEx, setEnumContent, void, 4, 4, // check the type if( !conType->getEnumTable() ) { - Con::warnf(ConsoleLogEntry::General, "field '%s' is not an enumeration for class '%s'", argv[3], argv[2]); + Con::warnf(ConsoleLogEntry::General, "field '%s' is not an enumeration for class '%s'", (const char*)argv[3], (const char*)argv[2]); return; } diff --git a/Engine/source/gui/controls/guiTreeViewCtrl.cpp b/Engine/source/gui/controls/guiTreeViewCtrl.cpp index 390cbfcae..10001d006 100644 --- a/Engine/source/gui/controls/guiTreeViewCtrl.cpp +++ b/Engine/source/gui/controls/guiTreeViewCtrl.cpp @@ -4928,7 +4928,7 @@ ConsoleMethod( GuiTreeViewCtrl, setItemTooltip, void, 4, 4, "( int id, string te return; } - item->mTooltip = (String)argv[ 3 ]; + item->mTooltip = (const char*)argv[ 3 ]; } ConsoleMethod( GuiTreeViewCtrl, setItemImages, void, 5, 5, "( int id, int normalImage, int expandedImage ) - Sets the normal and expanded images to show for the given item." ) diff --git a/Engine/source/gui/core/guiCanvas.cpp b/Engine/source/gui/core/guiCanvas.cpp index 00a669646..36e1d1ed1 100644 --- a/Engine/source/gui/core/guiCanvas.cpp +++ b/Engine/source/gui/core/guiCanvas.cpp @@ -2007,7 +2007,7 @@ ConsoleMethod( GuiCanvas, pushDialog, void, 3, 5, "(GuiControl ctrl, int layer=0 if (! Sim::findObject(argv[2], gui)) { - Con::printf("%s(): Invalid control: %s", argv[0], argv[2]); + Con::printf("%s(): Invalid control: %s", (const char*)argv[0], (const char*)argv[2]); return; } @@ -2052,7 +2052,7 @@ ConsoleMethod( GuiCanvas, popDialog, void, 2, 3, "(GuiControl ctrl=NULL)" { if (!Sim::findObject(argv[2], gui)) { - Con::printf("%s(): Invalid control: %s", argv[0], argv[2]); + Con::printf("%s(): Invalid control: %s", (const char*)argv[0], (const char*)argv[2]); return; } } diff --git a/Engine/source/gui/editor/guiEditCtrl.cpp b/Engine/source/gui/editor/guiEditCtrl.cpp index 584c9b9e2..1710ec4f5 100644 --- a/Engine/source/gui/editor/guiEditCtrl.cpp +++ b/Engine/source/gui/editor/guiEditCtrl.cpp @@ -2540,7 +2540,7 @@ ConsoleMethod( GuiEditCtrl, setCurrentAddSet, void, 3, 3, "(GuiControl ctrl)") if (!Sim::findObject(argv[2], addSet)) { - Con::printf("%s(): Invalid control: %s", argv[0], argv[2]); + Con::printf("%s(): Invalid control: %s", (const char*)argv[0], (const char*)argv[2]); return; } object->setCurrentAddSet(addSet); @@ -2700,7 +2700,7 @@ ConsoleMethod( GuiEditCtrl, readGuides, void, 3, 4, "( GuiControl ctrl [, int ax GuiControl* ctrl; if( !Sim::findObject( argv[ 2 ], ctrl ) ) { - Con::errorf( "GuiEditCtrl::readGuides - no control '%s'", argv[ 2 ] ); + Con::errorf( "GuiEditCtrl::readGuides - no control '%s'", (const char*)argv[ 2 ] ); return; } @@ -2711,7 +2711,7 @@ ConsoleMethod( GuiEditCtrl, readGuides, void, 3, 4, "( GuiControl ctrl [, int ax S32 axis = dAtoi( argv[ 3 ] ); if( axis < 0 || axis > 1 ) { - Con::errorf( "GuiEditCtrl::readGuides - invalid axis '%s'", argv[ 3 ] ); + Con::errorf( "GuiEditCtrl::readGuides - invalid axis '%s'", (const char*)argv[ 3 ] ); return; } @@ -2733,7 +2733,7 @@ ConsoleMethod( GuiEditCtrl, writeGuides, void, 3, 4, "( GuiControl ctrl [, int a GuiControl* ctrl; if( !Sim::findObject( argv[ 2 ], ctrl ) ) { - Con::errorf( "GuiEditCtrl::writeGuides - no control '%i'", argv[ 2 ] ); + Con::errorf( "GuiEditCtrl::writeGuides - no control '%i'", (const char*)argv[ 2 ] ); return; } @@ -2744,7 +2744,7 @@ ConsoleMethod( GuiEditCtrl, writeGuides, void, 3, 4, "( GuiControl ctrl [, int a S32 axis = dAtoi( argv[ 3 ] ); if( axis < 0 || axis > 1 ) { - Con::errorf( "GuiEditCtrl::writeGuides - invalid axis '%s'", argv[ 3 ] ); + Con::errorf( "GuiEditCtrl::writeGuides - invalid axis '%s'", (const char*)argv[ 3 ] ); return; } diff --git a/Engine/source/gui/editor/guiInspector.cpp b/Engine/source/gui/editor/guiInspector.cpp index dab92cafb..d05e03c6d 100644 --- a/Engine/source/gui/editor/guiInspector.cpp +++ b/Engine/source/gui/editor/guiInspector.cpp @@ -777,7 +777,7 @@ ConsoleMethod( GuiInspector, inspect, void, 3, 3, "Inspect(Object)") if(!target) { if(dAtoi(argv[2]) > 0) - Con::warnf("%s::inspect(): invalid object: %s", argv[0], argv[2]); + Con::warnf("%s::inspect(): invalid object: %s", (const char*)argv[0], (const char*)argv[2]); object->clearInspectObjects(); return; @@ -793,7 +793,7 @@ ConsoleMethod( GuiInspector, addInspect, void, 3, 4, "( id object, (bool autoSyn SimObject* obj; if( !Sim::findObject( argv[ 2 ], obj ) ) { - Con::errorf( "%s::addInspect(): invalid object: %s", argv[ 0 ], argv[ 2 ] ); + Con::errorf( "%s::addInspect(): invalid object: %s", (const char*)argv[ 0 ], (const char*)argv[ 2 ] ); return; } @@ -810,7 +810,7 @@ ConsoleMethod( GuiInspector, removeInspect, void, 3, 3, "( id object ) - Remove SimObject* obj; if( !Sim::findObject( argv[ 2 ], obj ) ) { - Con::errorf( "%s::removeInspect(): invalid object: %s", argv[ 0 ], argv[ 2 ] ); + Con::errorf( "%s::removeInspect(): invalid object: %s", (const char*)argv[ 0 ], (const char*)argv[ 2 ] ); return; } diff --git a/Engine/source/gui/editor/guiInspectorTypes.cpp b/Engine/source/gui/editor/guiInspectorTypes.cpp index e1533134f..2482d3c2e 100644 --- a/Engine/source/gui/editor/guiInspectorTypes.cpp +++ b/Engine/source/gui/editor/guiInspectorTypes.cpp @@ -571,7 +571,7 @@ void GuiInspectorTypeFileName::updateValue() ConsoleMethod( GuiInspectorTypeFileName, apply, void, 3,3, "apply(newValue);" ) { - String path( argv[2] ); + String path( (const char*)argv[2] ); if ( path.isNotEmpty() ) path = Platform::makeRelativePathName( path, Platform::getMainDotCsDir() ); diff --git a/Engine/source/gui/editor/inspector/variableInspector.cpp b/Engine/source/gui/editor/inspector/variableInspector.cpp index 1a061e8a0..62993d600 100644 --- a/Engine/source/gui/editor/inspector/variableInspector.cpp +++ b/Engine/source/gui/editor/inspector/variableInspector.cpp @@ -63,5 +63,5 @@ void GuiVariableInspector::loadVars( String searchStr ) ConsoleMethod( GuiVariableInspector, loadVars, void, 3, 3, "loadVars( searchString )" ) { - object->loadVars( (const char*)argv[2] ); + object->loadVars( argv[2] ); } \ No newline at end of file diff --git a/Engine/source/gui/worldEditor/editorIconRegistry.cpp b/Engine/source/gui/worldEditor/editorIconRegistry.cpp index 1a80dc725..6dd1f2cc6 100644 --- a/Engine/source/gui/worldEditor/editorIconRegistry.cpp +++ b/Engine/source/gui/worldEditor/editorIconRegistry.cpp @@ -175,7 +175,7 @@ ConsoleStaticMethod( EditorIconRegistry, add, void, 3, 4, "( String className, S if ( argc > 3 ) overwrite = dAtob( argv[3] ); - gEditorIcons.add( (const char*)argv[1], (const char*)argv[2], overwrite ); + gEditorIcons.add( argv[1], argv[2], overwrite ); } ConsoleStaticMethod( EditorIconRegistry, loadFromPath, void, 2, 3, "( String imagePath [, bool overwrite = true] )" @@ -185,7 +185,7 @@ ConsoleStaticMethod( EditorIconRegistry, loadFromPath, void, 2, 3, "( String ima if ( argc > 2 ) overwrite = dAtob( argv[2] ); - gEditorIcons.loadFromPath( (const char*)argv[1], overwrite ); + gEditorIcons.loadFromPath( argv[1], overwrite ); } ConsoleStaticMethod( EditorIconRegistry, clear, void, 1, 1, "" @@ -212,7 +212,7 @@ ConsoleStaticMethod( EditorIconRegistry, findIconBySimObject, const char*, 2, 2, SimObject *obj = NULL; if ( !Sim::findObject( argv[1], obj ) ) { - Con::warnf( "EditorIconRegistry::findIcon, parameter %d was not a SimObject!", argv[1] ); + Con::warnf( "EditorIconRegistry::findIcon, parameter %d was not a SimObject!", (const char*)argv[1] ); return NULL; } diff --git a/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp b/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp index 2bcf0c822..79385948c 100644 --- a/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp +++ b/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp @@ -782,7 +782,7 @@ void GuiDecalEditorCtrl::setMode( String mode, bool sourceShortcut = false ) mMode = mode; if( sourceShortcut ) - Con::executef( this, "paletteSync", (const char*)mMode ); + Con::executef( this, "paletteSync", mMode ); } ConsoleMethod( GuiDecalEditorCtrl, deleteSelectedDecal, void, 2, 2, "deleteSelectedDecal()" ) @@ -792,7 +792,7 @@ ConsoleMethod( GuiDecalEditorCtrl, deleteSelectedDecal, void, 2, 2, "deleteSelec ConsoleMethod( GuiDecalEditorCtrl, deleteDecalDatablock, void, 3, 3, "deleteSelectedDecalDatablock( String datablock )" ) { - String lookupName( argv[2] ); + String lookupName( (const char*)argv[2] ); if( lookupName == String::EmptyString ) return; @@ -801,7 +801,7 @@ ConsoleMethod( GuiDecalEditorCtrl, deleteDecalDatablock, void, 3, 3, "deleteSele ConsoleMethod( GuiDecalEditorCtrl, setMode, void, 3, 3, "setMode( String mode )()" ) { - String newMode = ( argv[2] ); + String newMode = ( (const char*)argv[2] ); object->setMode( newMode ); } @@ -868,7 +868,7 @@ ConsoleMethod( GuiDecalEditorCtrl, editDecalDetails, void, 4, 4, "editDecalDetai if ( (count != 7) ) { - Con::printf("Failed to parse decal information \"px py pz tx ty tz s\" from '%s'", argv[3]); + Con::printf("Failed to parse decal information \"px py pz tx ty tz s\" from '%s'", (const char*)argv[3]); return; } @@ -894,7 +894,7 @@ ConsoleMethod( GuiDecalEditorCtrl, getSelectionCount, S32, 2, 2, "" ) ConsoleMethod( GuiDecalEditorCtrl, retargetDecalDatablock, void, 4, 4, "" ) { if( dStrcmp( argv[2], "" ) != 0 && dStrcmp( argv[3], "" ) != 0 ) - object->retargetDecalDatablock( (const char*)argv[2], (const char*)argv[3] ); + object->retargetDecalDatablock( argv[2], argv[3] ); } void GuiDecalEditorCtrl::setGizmoFocus( DecalInstance * decalInstance ) @@ -1253,4 +1253,4 @@ void DBRetargetUndoAction::redo() if ( mEditor->isMethod( "rebuildInstanceTree" ) ) Con::executef( mEditor, "rebuildInstanceTree" ); } -#endif \ No newline at end of file +#endif diff --git a/Engine/source/gui/worldEditor/terrainEditor.cpp b/Engine/source/gui/worldEditor/terrainEditor.cpp index ec032e5f9..a26314121 100644 --- a/Engine/source/gui/worldEditor/terrainEditor.cpp +++ b/Engine/source/gui/worldEditor/terrainEditor.cpp @@ -2500,7 +2500,7 @@ ConsoleMethod( TerrainEditor, attachTerrain, void, 2, 3, "(TerrainBlock terrain) terrains.push_back(terrBlock); if(terrains.size() == 0) - Con::errorf(ConsoleLogEntry::Script, "TerrainEditor::attach: failed to attach to object '%s'", argv[2]); + Con::errorf(ConsoleLogEntry::Script, "TerrainEditor::attach: failed to attach to object '%s'", (const char*)argv[2]); } if (terrains.size() > 0) @@ -2714,7 +2714,7 @@ ConsoleMethod(TerrainEditor, updateMaterial, bool, 4, 4, if ( index >= terr->getMaterialCount() ) return false; - terr->updateMaterial( index, (const char*)argv[3] ); + terr->updateMaterial( index, argv[3] ); object->setDirty(); @@ -2729,7 +2729,7 @@ ConsoleMethod(TerrainEditor, addMaterial, S32, 3, 3, if ( !terr ) return false; - terr->addMaterial( (const char*)argv[2] ); + terr->addMaterial( argv[2] ); object->setDirty(); diff --git a/Engine/source/gui/worldEditor/worldEditor.cpp b/Engine/source/gui/worldEditor/worldEditor.cpp index 21948ceff..9ac8b2576 100644 --- a/Engine/source/gui/worldEditor/worldEditor.cpp +++ b/Engine/source/gui/worldEditor/worldEditor.cpp @@ -3204,7 +3204,7 @@ ConsoleMethod( WorldEditor, setActiveSelection, void, 3, 3, "( id set ) - Set th WorldEditorSelection* selection; if( !Sim::findObject( argv[ 2 ], selection ) ) { - Con::errorf( "WorldEditor::setActiveSelectionSet - no selection set '%s'", argv[ 2 ] ); + Con::errorf( "WorldEditor::setActiveSelectionSet - no selection set '%s'", (const char*)argv[ 2 ] ); return; } @@ -3330,14 +3330,14 @@ ConsoleMethod( WorldEditor, alignByBounds, void, 3, 3, "(int boundsAxis)" "Align all selected objects against the given bounds axis.") { if(!object->alignByBounds(dAtoi(argv[2]))) - Con::warnf(ConsoleLogEntry::General, avar("worldEditor.alignByBounds: invalid bounds axis '%s'", argv[2])); + Con::warnf(ConsoleLogEntry::General, avar("worldEditor.alignByBounds: invalid bounds axis '%s'", (const char*)argv[2])); } ConsoleMethod( WorldEditor, alignByAxis, void, 3, 3, "(int axis)" "Align all selected objects along the given axis.") { if(!object->alignByAxis(dAtoi(argv[2]))) - Con::warnf(ConsoleLogEntry::General, avar("worldEditor.alignByAxis: invalid axis '%s'", argv[2])); + Con::warnf(ConsoleLogEntry::General, avar("worldEditor.alignByAxis: invalid axis '%s'", (const char*)argv[2])); } ConsoleMethod( WorldEditor, resetSelectedRotation, void, 2, 2, "") diff --git a/Engine/source/gui/worldEditor/worldEditorSelection.cpp b/Engine/source/gui/worldEditor/worldEditorSelection.cpp index ed2e1f5a3..c8b2e2680 100644 --- a/Engine/source/gui/worldEditor/worldEditorSelection.cpp +++ b/Engine/source/gui/worldEditor/worldEditorSelection.cpp @@ -682,7 +682,7 @@ ConsoleMethod( WorldEditorSelection, union, void, 3, 3, "( SimSet set ) - Add al SimSet* selection; if( !Sim::findObject( argv[ 2 ], selection ) ) { - Con::errorf( "WorldEditorSelection::union - no SimSet '%s'", argv[ 2 ] ); + Con::errorf( "WorldEditorSelection::union - no SimSet '%s'", (const char*)argv[ 2 ] ); return; } @@ -698,7 +698,7 @@ ConsoleMethod( WorldEditorSelection, subtract, void, 3, 3, "( SimSet ) - Remove SimSet* selection; if( !Sim::findObject( argv[ 2 ], selection ) ) { - Con::errorf( "WorldEditorSelection::subtract - no SimSet '%s'", argv[ 2 ] ); + Con::errorf( "WorldEditorSelection::subtract - no SimSet '%s'", (const char*)argv[ 2 ] ); return; } diff --git a/Engine/source/i18n/i18n.cpp b/Engine/source/i18n/i18n.cpp index a5cfaf9de..38b213361 100644 --- a/Engine/source/i18n/i18n.cpp +++ b/Engine/source/i18n/i18n.cpp @@ -78,7 +78,7 @@ ConsoleFunction(setCoreLangTable, void, 2, 2, "(string LangTable)" if(Sim::findObject(argv[1], lt)) gCoreLangTable = lt; else - Con::errorf("setCoreLangTable - Unable to find LanTable '%s'", argv[1]); + Con::errorf("setCoreLangTable - Unable to find LanTable '%s'", (const char*)argv[1]); } //----------------------------------------------------------------------------- diff --git a/Engine/source/materials/materialManager.cpp b/Engine/source/materials/materialManager.cpp index bc30791da..e0d04e9ad 100644 --- a/Engine/source/materials/materialManager.cpp +++ b/Engine/source/materials/materialManager.cpp @@ -465,7 +465,7 @@ ConsoleFunction( addMaterialMapping, void, 3, 3, "(string texName, string matNam "block or interior surface using the associated texture.\n\n" "@ingroup Materials") { - MATMGR->mapMaterial((const char*)argv[1],(const char*)argv[2]); + MATMGR->mapMaterial(argv[1], argv[2]); } ConsoleFunction( getMaterialMapping, const char*, 2, 2, "(string texName)\n" @@ -474,7 +474,7 @@ ConsoleFunction( getMaterialMapping, const char*, 2, 2, "(string texName)\n" "@param texName Name of the texture\n\n" "@ingroup Materials") { - return MATMGR->getMapEntry((const char*)argv[1]).c_str(); + return MATMGR->getMapEntry(argv[1]).c_str(); } ConsoleFunction( dumpMaterialInstances, void, 1, 1, @@ -487,5 +487,5 @@ ConsoleFunction( dumpMaterialInstances, void, 1, 1, ConsoleFunction( getMapEntry, const char *, 2, 2, "@hide") { - return MATMGR->getMapEntry( String(argv[1]) ); -} \ No newline at end of file + return MATMGR->getMapEntry( argv[1] ); +} diff --git a/Engine/source/platformMac/macCarbFileio.mm b/Engine/source/platformMac/macCarbFileio.mm index 9b95d67fc..03329593f 100644 --- a/Engine/source/platformMac/macCarbFileio.mm +++ b/Engine/source/platformMac/macCarbFileio.mm @@ -884,7 +884,7 @@ bool Platform::fileTimeToString(FileTime * time, char * string, U32 strLen) { re //----------------------------------------------------------------------------- #if defined(TORQUE_DEBUG) ConsoleFunction(testHasSubdir,void,2,2,"tests platform::hasSubDirectory") { - Con::printf("testing %s",argv[1]); + Con::printf("testing %s",(const char*)argv[1]); Platform::addExcludedDirectory(".svn"); if(Platform::hasSubDirectory(argv[1])) Con::printf(" has subdir"); @@ -901,7 +901,7 @@ ConsoleFunction(testDumpDirectories,void,4,4,"testDumpDirectories('path', int de Platform::dumpDirectories(argv[1], paths, depth, noBasePath); - Con::printf("Dumping directories starting from %s with depth %i", argv[1],depth); + Con::printf("Dumping directories starting from %s with depth %i", (const char*)argv[1],depth); for(Vector::iterator itr = paths.begin(); itr != paths.end(); itr++) { Con::printf(*itr); diff --git a/Engine/source/platformWin32/winInput.cpp b/Engine/source/platformWin32/winInput.cpp index f6ee3cc02..58586981d 100644 --- a/Engine/source/platformWin32/winInput.cpp +++ b/Engine/source/platformWin32/winInput.cpp @@ -509,7 +509,7 @@ void Input::log( const char* format, ... ) ConsoleFunction( inputLog, void, 2, 2, "inputLog( string )" ) { argc; - Input::log( "%s\n", argv[1] ); + Input::log( "%s\n", (const char*)argv[1] ); } #endif // LOG_INPUT diff --git a/Engine/source/platformX86UNIX/x86UNIXInput.client.cpp b/Engine/source/platformX86UNIX/x86UNIXInput.client.cpp index 0db1d7a0f..e05e910d5 100644 --- a/Engine/source/platformX86UNIX/x86UNIXInput.client.cpp +++ b/Engine/source/platformX86UNIX/x86UNIXInput.client.cpp @@ -335,7 +335,7 @@ void Input::log( const char* format, ... ) ConsoleFunction( inputLog, void, 2, 2, "inputLog( string )" ) { argc; - Input::log( "%s\n", argv[1] ); + Input::log( "%s\n", (const char*)argv[1] ); } #endif // LOG_INPUT diff --git a/Engine/source/platformX86UNIX/x86UNIXMath.cpp b/Engine/source/platformX86UNIX/x86UNIXMath.cpp index 300515083..2a9abf547 100644 --- a/Engine/source/platformX86UNIX/x86UNIXMath.cpp +++ b/Engine/source/platformX86UNIX/x86UNIXMath.cpp @@ -70,7 +70,7 @@ ConsoleFunction( MathInit, void, 1, 10, "(detect|C|FPU|MMX|3DNOW|SSE|...)") properties |= CPU_PROP_SSE; continue; } - Con::printf("Error: MathInit(): ignoring unknown math extension '%s'", *argv); + Con::printf("Error: MathInit(): ignoring unknown math extension '%s'", (const char*)argv[0]); } Math::init(properties); } diff --git a/Engine/source/postFx/postEffectVis.cpp b/Engine/source/postFx/postEffectVis.cpp index bf4e40057..c938e9a93 100644 --- a/Engine/source/postFx/postEffectVis.cpp +++ b/Engine/source/postFx/postEffectVis.cpp @@ -400,7 +400,7 @@ ConsoleStaticMethod( PfxVis, open, void, 2, 3, "( PostEffect, [bool clear = fals PostEffect *pfx; if ( !Sim::findObject( argv[1], pfx ) ) { - Con::errorf( "PfxVis::add, argument %s was not a PostEffect", argv[1] ); + Con::errorf( "PfxVis::add, argument %s was not a PostEffect", (const char*)argv[1] ); return; } @@ -450,9 +450,9 @@ ConsoleStaticMethod( PfxVis, onWindowClosed, void, 2, 2, "( GuiWindowCtrl )" GuiWindowCtrl *ctrl; if ( !Sim::findObject( argv[1], ctrl ) ) { - Con::errorf( "PfxVis::onWindowClosed, argument %s was not a GuiWindowCtrl", argv[1] ); + Con::errorf( "PfxVis::onWindowClosed, argument %s was not a GuiWindowCtrl", (const char*)argv[1] ); return; } PFXVIS->onWindowClosed( ctrl ); -} \ No newline at end of file +} diff --git a/Engine/source/sfx/sfxSystem.cpp b/Engine/source/sfx/sfxSystem.cpp index c43a44b2f..f016136f9 100644 --- a/Engine/source/sfx/sfxSystem.cpp +++ b/Engine/source/sfx/sfxSystem.cpp @@ -1454,7 +1454,7 @@ ConsoleFunction( sfxCreateSource, S32, 2, 6, description = dynamic_cast< SFXDescription* >( Sim::findObject( argv[1] ) ); if ( !description ) { - Con::printf( "Unable to locate sound track/description '%s'", argv[1] ); + Con::printf( "Unable to locate sound track/description '%s'", (const char*)argv[1] ); return 0; } } @@ -1560,7 +1560,7 @@ ConsoleFunction( sfxPlay, S32, 2, 5, "( SFXSource source | ( SFXTrack track [, f SFXTrack* track = dynamic_cast( Sim::findObject( argv[1] ) ); if ( !track ) { - Con::printf( "Unable to locate sfx track '%s'", argv[1] ); + Con::printf( "Unable to locate sfx track '%s'", (const char*)argv[1] ); return 0; } @@ -1663,7 +1663,7 @@ ConsoleFunction( sfxPlayOnce, S32, 2, 6, description = dynamic_cast< SFXDescription* >( Sim::findObject( argv[1] ) ); if( !description ) { - Con::errorf( "sfxPlayOnce - Unable to locate sound track/description '%s'", argv[1] ); + Con::errorf( "sfxPlayOnce - Unable to locate sound track/description '%s'", (const char*)argv[1] ); return 0; } } diff --git a/Engine/source/sim/actionMap.cpp b/Engine/source/sim/actionMap.cpp index c7e7665a1..43e50b568 100644 --- a/Engine/source/sim/actionMap.cpp +++ b/Engine/source/sim/actionMap.cpp @@ -1795,16 +1795,15 @@ static ConsoleDocFragment _ActionMapbindObj2( ConsoleMethod( ActionMap, bindObj, bool, 6, 11, "(device, action, [modifier spec, mod...], command, object)" "@hide") { - SimObject* simObject = Sim::findObject(argv[argc - 1]); - if ( simObject == NULL ) - { - Con::warnf("ActionMap::bindObj() - Cannot bind, specified object was not found!"); - return false; - } + SimObject* simObject = Sim::findObject(argv[argc - 1]); + if ( simObject == NULL ) + { + Con::warnf("ActionMap::bindObj() - Cannot bind, specified object was not found!"); + return false; + } - StringStackWrapper args(argc - 3, argv + 2); - - return object->processBind( args.count(), args, simObject ); + StringStackWrapper args(argc - 3, argv + 2); + return object->processBind( args.count(), args, simObject ); } //------------------------------------------------------------------------------ diff --git a/Engine/source/sim/netConnection.cpp b/Engine/source/sim/netConnection.cpp index 469d20f60..b16265b82 100644 --- a/Engine/source/sim/netConnection.cpp +++ b/Engine/source/sim/netConnection.cpp @@ -434,7 +434,7 @@ NetConnection::NetConnection() // Disable starting a new journal recording or playback from here on Journal::Disable(); - // jamesu - netAddress is not set + // Ensure NetAddress is cleared dMemset(&mNetAddress, '\0', sizeof(NetAddress)); } diff --git a/Engine/source/terrain/terrExport.cpp b/Engine/source/terrain/terrExport.cpp index 09fbbca19..1a9b3d76a 100644 --- a/Engine/source/terrain/terrExport.cpp +++ b/Engine/source/terrain/terrExport.cpp @@ -141,7 +141,7 @@ ConsoleMethod( TerrainBlock, exportHeightMap, bool, 3, 4, "(string filename, [st UTF8 fileName[1024]; String format = "png"; if( argc > 3 ) - format = (String)argv[ 3 ]; + format = (const char*)argv[ 3 ]; Con::expandScriptFilename( fileName, sizeof( fileName ), argv[2] ); @@ -153,7 +153,7 @@ ConsoleMethod( TerrainBlock, exportLayerMaps, bool, 3, 4, "(string filePrefix, [ UTF8 filePrefix[1024]; String format = "png"; if( argc > 3 ) - format = (String)argv[3]; + format = (const char*)argv[3]; Con::expandScriptFilename( filePrefix, sizeof( filePrefix ), argv[2] ); diff --git a/Engine/source/ts/collada/colladaImport.cpp b/Engine/source/ts/collada/colladaImport.cpp index f22ee4662..ca50a8f55 100644 --- a/Engine/source/ts/collada/colladaImport.cpp +++ b/Engine/source/ts/collada/colladaImport.cpp @@ -139,7 +139,7 @@ ConsoleFunction( enumColladaForImport, bool, 3, 3, GuiTreeViewCtrl* tree; if (!Sim::findObject(argv[2], tree)) { - Con::errorf("enumColladaScene::Could not find GuiTreeViewCtrl '%s'", argv[2]); + Con::errorf("enumColladaScene::Could not find GuiTreeViewCtrl '%s'", (const char*)argv[2]); return false; } diff --git a/Engine/source/ts/collada/colladaLights.cpp b/Engine/source/ts/collada/colladaLights.cpp index 62ea45c50..a13bed1ef 100644 --- a/Engine/source/ts/collada/colladaLights.cpp +++ b/Engine/source/ts/collada/colladaLights.cpp @@ -172,7 +172,7 @@ ConsoleFunction( loadColladaLights, bool, 2, 4, if (!Sim::findObject(argv[2], group)) { // Create the group if it could not be found group = new SimGroup; - if (group->registerObject(argv[2])) { + if (group->registerObject((const char*)argv[2])) { if (missionGroup) missionGroup->addObject(group); } diff --git a/Engine/source/util/messaging/eventManager.cpp b/Engine/source/util/messaging/eventManager.cpp index 328fe2d6d..e7c7d1d8a 100644 --- a/Engine/source/util/messaging/eventManager.cpp +++ b/Engine/source/util/messaging/eventManager.cpp @@ -471,7 +471,7 @@ ConsoleMethod( EventManager, subscribe, bool, 4, 5, "( SimObject listener, Strin return false; } - return object->subscribe( cbObj, argv[3], argc > 4 ? argv[4] : NULL ); + return object->subscribe( cbObj, argv[3], argc > 4 ? (const char*)argv[4] : NULL ); } ConsoleMethod( EventManager, remove, void, 4, 4, "( SimObject listener, String event )\n\n" diff --git a/Engine/source/util/undo.cpp b/Engine/source/util/undo.cpp index 3a8e52cff..0a04a8ca2 100644 --- a/Engine/source/util/undo.cpp +++ b/Engine/source/util/undo.cpp @@ -566,7 +566,7 @@ ConsoleMethod( UndoManager, pushCompound, const char*, 2, 3, "( string name=\"\" { String name; if( argc > 2 ) - name = (String)argv[ 2 ]; + name = (const char*)argv[ 2 ]; CompoundUndoAction* action = object->pushCompound( name ); if( !action ) @@ -584,7 +584,7 @@ ConsoleMethod( UndoManager, popCompound, void, 2, 3, "( bool discard=false ) - P { if( !object->getCompoundStackDepth() ) { - Con::errorf( "%s::popCompound - no compound on stack", argv[ 0 ] ); + Con::errorf( "%s::popCompound - no compound on stack", (const char*)argv[ 0 ] ); return; } From 8a15133a61b69ba5526d62f95bf4416d0748517c Mon Sep 17 00:00:00 2001 From: jamesu Date: Fri, 19 Oct 2012 23:44:59 +0100 Subject: [PATCH 004/317] Add new instructions to the instruction dumper --- Engine/source/console/codeBlock.cpp | 56 +++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) diff --git a/Engine/source/console/codeBlock.cpp b/Engine/source/console/codeBlock.cpp index 896d2a4cf..e3feab1bc 100644 --- a/Engine/source/console/codeBlock.cpp +++ b/Engine/source/console/codeBlock.cpp @@ -823,6 +823,26 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) break; } + case OP_RETURN_UINT: + { + Con::printf( "%i: OP_RETURNUINT", ip - 1 ); + + if( upToReturn ) + return; + + break; + } + + case OP_RETURN_FLT: + { + Con::printf( "%i: OP_RETURNFLT", ip - 1 ); + + if( upToReturn ) + return; + + break; + } + case OP_CMPEQ: { Con::printf( "%i: OP_CMPEQ", ip - 1 ); @@ -1003,6 +1023,12 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) break; } + case OP_LOADVAR_VAR: + { + Con::printf( "%i: OP_LOADVAR_VAR", ip - 1 ); + break; + } + case OP_SAVEVAR_UINT: { Con::printf( "%i: OP_SAVEVAR_UINT", ip - 1 ); @@ -1021,6 +1047,12 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) break; } + case OP_SAVEVAR_VAR: + { + Con::printf( "%i: OP_SAVEVAR_VAR", ip - 1 ); + break; + } + case OP_SETCUROBJECT: { Con::printf( "%i: OP_SETCUROBJECT", ip - 1 ); @@ -1151,6 +1183,12 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) break; } + case OP_COPYVAR_TO_NONE: + { + Con::printf( "%i: OP_COPYVAR_TO_NONE", ip - 1 ); + break; + } + case OP_LOADIMMED_UINT: { U32 val = code[ ip ]; @@ -1277,6 +1315,24 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) break; } + case OP_PUSH_UINT: + { + Con::printf( "%i: OP_PUSH_UINT", ip - 1 ); + break; + } + + case OP_PUSH_FLT: + { + Con::printf( "%i: OP_PUSH_FLT", ip - 1 ); + break; + } + + case OP_PUSH_VAR: + { + Con::printf( "%i: OP_PUSH_VAR", ip - 1 ); + break; + } + case OP_PUSH_FRAME: { Con::printf( "%i: OP_PUSH_FRAME", ip - 1 ); From 98b92360f9a27262a4d0f234d1a730f7f348a740 Mon Sep 17 00:00:00 2001 From: jamesu Date: Fri, 9 Nov 2012 23:00:46 +0000 Subject: [PATCH 005/317] Signed values now set/get from the float value. Also add a bool helper. --- Engine/source/console/console.cpp | 43 ++++++++++++++++++++++++++++++- Engine/source/console/console.h | 24 ++++++++++++----- 2 files changed, 59 insertions(+), 8 deletions(-) diff --git a/Engine/source/console/console.cpp b/Engine/source/console/console.cpp index 8ef659891..b292fc315 100644 --- a/Engine/source/console/console.cpp +++ b/Engine/source/console/console.cpp @@ -1589,6 +1589,10 @@ ConsoleValueRef::ConsoleValueRef(const String &newValue) : value(NULL) *this = (const char*)(newValue.utf8()); } +ConsoleValueRef::ConsoleValueRef(U32 newValue) : value(NULL) +{ + *this = newValue; +} ConsoleValueRef::ConsoleValueRef(S32 newValue) : value(NULL) { @@ -1645,7 +1649,13 @@ StringStackConsoleWrapper::~StringStackConsoleWrapper() delete[] argv; } - +S32 ConsoleValue::getSignedIntValue() +{ + if(type <= TypeInternalString) + return (S32)fval; + else + return dAtoi(Con::getData(type, dataPtr, 0, enumTable)); +} U32 ConsoleValue::getIntValue() { @@ -1675,6 +1685,25 @@ const char *ConsoleValue::getStringValue() return Con::getData(type, dataPtr, 0, enumTable); } +bool ConsoleValue::getBoolValue() +{ + if(type == TypeInternalString || type == TypeInternalStackString) + return dAtob(sval); + if(type == TypeInternalFloat) + return fval > 0; + else if(type == TypeInternalInt) + return ival > 0; + else { + const char *value = Con::getData(type, dataPtr, 0, enumTable); + return dAtob(value); + } +} + +void ConsoleValue::setIntValue(S32 val) +{ + setFloatValue(val); +} + void ConsoleValue::setIntValue(U32 val) { if(type <= TypeInternalString) @@ -1695,6 +1724,11 @@ void ConsoleValue::setIntValue(U32 val) } } +void ConsoleValue::setBoolValue(bool val) +{ + return setIntValue(val ? 1 : 0); +} + void ConsoleValue::setFloatValue(F32 val) { if(type <= TypeInternalString) @@ -1748,6 +1782,13 @@ ConsoleValueRef& ConsoleValueRef::operator=(const char *newValue) } ConsoleValueRef& ConsoleValueRef::operator=(S32 newValue) +{ + value = CSTK.pushFLT(newValue); + stringStackValue = NULL; + return *this; +} + +ConsoleValueRef& ConsoleValueRef::operator=(U32 newValue) { value = CSTK.pushUINT(newValue); stringStackValue = NULL; diff --git a/Engine/source/console/console.h b/Engine/source/console/console.h index 2783a048f..edba83ea7 100644 --- a/Engine/source/console/console.h +++ b/Engine/source/console/console.h @@ -163,17 +163,17 @@ public: }; U32 getIntValue(); - + S32 getSignedIntValue(); F32 getFloatValue(); - const char *getStringValue(); + bool getBoolValue(); void setIntValue(U32 val); - + void setIntValue(S32 val); void setFloatValue(F32 val); - void setStringValue(const char *value); void setStackStringValue(const char *value); + void setBoolValue(bool val); void init() { @@ -211,6 +211,7 @@ public: ConsoleValueRef(const ConsoleValueRef &ref); ConsoleValueRef(const char *value); ConsoleValueRef(const String &ref); + ConsoleValueRef(U32 value); ConsoleValueRef(S32 value); ConsoleValueRef(F32 value); ConsoleValueRef(F64 value); @@ -218,12 +219,15 @@ public: const char *getStringValue() { return value ? value->getStringValue() : ""; } const char *getStringArgValue(); - inline S32 getIntValue() { return value ? value->getIntValue() : 0; } + inline U32 getIntValue() { return value ? value->getIntValue() : 0; } + inline S32 getSignedIntValue() { return value ? value->getSignedIntValue() : 0; } inline F32 getFloatValue() { return value ? value->getFloatValue() : 0.0f; } + inline bool getBoolValue() { return value ? value->getBoolValue() : false; } inline operator const char*() { return getStringValue(); } inline operator String() { return String(getStringValue()); } - inline operator S32() { return getIntValue(); } + inline operator U32() { return getIntValue(); } + inline operator S32() { return getSignedIntValue(); } inline operator F32() { return getFloatValue(); } inline bool isString() { return value ? value->type >= ConsoleValue::TypeInternalStackString : true; } @@ -233,6 +237,7 @@ public: // Note: operators replace value ConsoleValueRef& operator=(const ConsoleValueRef &other); ConsoleValueRef& operator=(const char *newValue); + ConsoleValueRef& operator=(U32 newValue); ConsoleValueRef& operator=(S32 newValue); ConsoleValueRef& operator=(F32 newValue); ConsoleValueRef& operator=(F64 newValue); @@ -242,7 +247,7 @@ public: inline S32 dAtoi(ConsoleValueRef &ref) { - return ref.getIntValue(); + return ref.getSignedIntValue(); } inline F32 dAtof(ConsoleValueRef &ref) @@ -250,6 +255,11 @@ inline F32 dAtof(ConsoleValueRef &ref) return ref.getFloatValue(); } +inline bool dAtob(ConsoleValue &ref) +{ + return ref.getBoolValue(); +} + // Transparently converts ConsoleValue[] to const char** class StringStackWrapper From 46f140710a7c80e508ab98735078b848bde6732e Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 20 Nov 2012 08:18:14 +1100 Subject: [PATCH 006/317] Prevented looking up incorrect object handles. Previously, dAtoi would be called on arbitrary strings delimited only by the / character. Now, Sim::findObject actually checks that object handles (strings starting with a digit) actually only contain digits or slashes. --- Engine/source/console/simManager.cpp | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Engine/source/console/simManager.cpp b/Engine/source/console/simManager.cpp index 0f00d1e33..2347219aa 100644 --- a/Engine/source/console/simManager.cpp +++ b/Engine/source/console/simManager.cpp @@ -367,6 +367,8 @@ SimObject* findObject(const char* name) return NULL; return obj->findObject(temp); } + else if (c < '0' || c > '9') + return NULL; } } S32 len; From d4b189e175d1148d9669f811571adb063ef013a5 Mon Sep 17 00:00:00 2001 From: rextimmy Date: Fri, 28 Feb 2014 12:11:26 +1000 Subject: [PATCH 007/317] PhysX3 basic plugin. --- Engine/source/T3D/physics/physx3/px3.h | 53 ++ Engine/source/T3D/physics/physx3/px3Body.cpp | 421 +++++++++++++ Engine/source/T3D/physics/physx3/px3Body.h | 121 ++++ Engine/source/T3D/physics/physx3/px3Cast.h | 137 +++++ .../T3D/physics/physx3/px3Collision.cpp | 212 +++++++ .../source/T3D/physics/physx3/px3Collision.h | 87 +++ .../source/T3D/physics/physx3/px3Player.cpp | 328 ++++++++++ Engine/source/T3D/physics/physx3/px3Player.h | 104 ++++ .../source/T3D/physics/physx3/px3Plugin.cpp | 227 +++++++ Engine/source/T3D/physics/physx3/px3Plugin.h | 58 ++ .../source/T3D/physics/physx3/px3Stream.cpp | 92 +++ Engine/source/T3D/physics/physx3/px3Stream.h | 77 +++ Engine/source/T3D/physics/physx3/px3Utils.cpp | 32 + Engine/source/T3D/physics/physx3/px3Utils.h | 35 ++ Engine/source/T3D/physics/physx3/px3World.cpp | 567 ++++++++++++++++++ Engine/source/T3D/physics/physx3/px3World.h | 110 ++++ Tools/projectGenerator/modules/physX3.inc | 106 ++++ 17 files changed, 2767 insertions(+) create mode 100644 Engine/source/T3D/physics/physx3/px3.h create mode 100644 Engine/source/T3D/physics/physx3/px3Body.cpp create mode 100644 Engine/source/T3D/physics/physx3/px3Body.h create mode 100644 Engine/source/T3D/physics/physx3/px3Cast.h create mode 100644 Engine/source/T3D/physics/physx3/px3Collision.cpp create mode 100644 Engine/source/T3D/physics/physx3/px3Collision.h create mode 100644 Engine/source/T3D/physics/physx3/px3Player.cpp create mode 100644 Engine/source/T3D/physics/physx3/px3Player.h create mode 100644 Engine/source/T3D/physics/physx3/px3Plugin.cpp create mode 100644 Engine/source/T3D/physics/physx3/px3Plugin.h create mode 100644 Engine/source/T3D/physics/physx3/px3Stream.cpp create mode 100644 Engine/source/T3D/physics/physx3/px3Stream.h create mode 100644 Engine/source/T3D/physics/physx3/px3Utils.cpp create mode 100644 Engine/source/T3D/physics/physx3/px3Utils.h create mode 100644 Engine/source/T3D/physics/physx3/px3World.cpp create mode 100644 Engine/source/T3D/physics/physx3/px3World.h create mode 100644 Tools/projectGenerator/modules/physX3.inc diff --git a/Engine/source/T3D/physics/physx3/px3.h b/Engine/source/T3D/physics/physx3/px3.h new file mode 100644 index 000000000..e7c8b6555 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3.h @@ -0,0 +1,53 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSX3_H_ +#define _PHYSX3_H_ + +//------------------------------------------------------------------------- +//defines to keep PhysX happy and compiling +#if defined(TORQUE_OS_MAC) && !defined(__APPLE__) + #define __APPLE__ +#elif defined(TORQUE_OS_LINUX) && !defined(LINUX) + #define LINUX +#elif defined(TORQUE_OS_WIN32) && !defined(WIN32) + #define WIN32 +#endif + +//------------------------------------------------------------------------- + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +extern physx::PxPhysics* gPhysics3SDK; + +#endif \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx3/px3Body.cpp b/Engine/source/T3D/physics/physx3/px3Body.cpp new file mode 100644 index 000000000..e4f437225 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Body.cpp @@ -0,0 +1,421 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physx3/px3Body.h" + +#include "T3D/physics/physx3/px3.h" +#include "T3D/physics/physx3/px3Cast.h" +#include "T3D/physics/physx3/px3World.h" +#include "T3D/physics/physx3/px3Collision.h" + +#include "console/console.h" +#include "console/consoleTypes.h" + + +Px3Body::Px3Body() : + mActor( NULL ), + mMaterial( NULL ), + mWorld( NULL ), + mBodyFlags( 0 ), + mIsEnabled( true ), + mIsStatic(false) +{ +} + +Px3Body::~Px3Body() +{ + _releaseActor(); +} + +void Px3Body::_releaseActor() +{ + if ( !mActor ) + return; + + mWorld->releaseWriteLock(); + + mActor->userData = NULL; + + mActor->release(); + mActor = NULL; + mBodyFlags = 0; + + if ( mMaterial ) + { + mMaterial->release(); + } + + mColShape = NULL; +} + +bool Px3Body::init( PhysicsCollision *shape, + F32 mass, + U32 bodyFlags, + SceneObject *obj, + PhysicsWorld *world ) +{ + AssertFatal( obj, "Px3Body::init - Got a null scene object!" ); + AssertFatal( world, "Px3Body::init - Got a null world!" ); + AssertFatal( dynamic_cast( world ), "Px3Body::init - The world is the wrong type!" ); + AssertFatal( shape, "Px3Body::init - Got a null collision shape!" ); + AssertFatal( dynamic_cast( shape ), "Px3Body::init - The collision shape is the wrong type!" ); + AssertFatal( !((Px3Collision*)shape)->getShapes().empty(), "Px3Body::init - Got empty collision shape!" ); + + // Cleanup any previous actor. + _releaseActor(); + + mWorld = (Px3World*)world; + mColShape = (Px3Collision*)shape; + mBodyFlags = bodyFlags; + + const bool isKinematic = mBodyFlags & BF_KINEMATIC; + const bool isTrigger = mBodyFlags & BF_TRIGGER; + const bool isDebris = mBodyFlags & BF_DEBRIS; + + if ( isKinematic ) + { + mActor = gPhysics3SDK->createRigidDynamic(physx::PxTransform(physx::PxIDENTITY())); + physx::PxRigidDynamic *actor = mActor->is(); + actor->setRigidDynamicFlag(physx::PxRigidDynamicFlag::eKINEMATIC, true); + actor->setMass(getMax( mass, 1.0f )); + } + else if ( mass > 0.0f ) + { + mActor = gPhysics3SDK->createRigidDynamic(physx::PxTransform(physx::PxIDENTITY())); + } + else + { + mActor = gPhysics3SDK->createRigidStatic(physx::PxTransform(physx::PxIDENTITY())); + mIsStatic = true; + } + + mMaterial = gPhysics3SDK->createMaterial(0.6f,0.4f,0.1f); + + // Add all the shapes. + const Vector &shapes = mColShape->getShapes(); + for ( U32 i=0; i < shapes.size(); i++ ) + { + Px3CollisionDesc* desc = shapes[i]; + if( mass > 0.0f ) + { + if(desc->pGeometry->getType() == physx::PxGeometryType::eTRIANGLEMESH) + { + Con::errorf("PhysX3 Dynamic Triangle Mesh is not supported."); + } + } + physx::PxShape * pShape = mActor->createShape(*desc->pGeometry,*mMaterial,desc->pose); + physx::PxFilterData colData; + if(isDebris) + colData.word0 = PX3_DEBRIS; + else if(isTrigger) + { + //We don't want trigger shapes taking part in shape pair intersection tests + pShape->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE, false); + colData.word0 = PX3_TRIGGER; + } + else + colData.word0 = PX3_DEFAULT; + + //set the skin width + pShape->setContactOffset(0.01f); + pShape->setFlag(physx::PxShapeFlag::eSCENE_QUERY_SHAPE,true); + pShape->setSimulationFilterData(colData); + pShape->setQueryFilterData(colData); + } + + //mass & intertia has to be set after creating the shape + if ( mass > 0.0f ) + { + physx::PxRigidDynamic *actor = mActor->is(); + physx::PxRigidBodyExt::setMassAndUpdateInertia(*actor,mass); + } + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + + mWorld->getScene()->addActor(*mActor); + mIsEnabled = true; + + if ( isDebris ) + mActor->setDominanceGroup( 31 ); + + mUserData.setObject( obj ); + mUserData.setBody( this ); + mActor->userData = &mUserData; + + return true; +} + +void Px3Body::setMaterial( F32 restitution, + F32 friction, + F32 staticFriction ) +{ + AssertFatal( mActor, "Px3Body::setMaterial - The actor is null!" ); + + if ( isDynamic() ) + { + physx::PxRigidDynamic *actor = mActor->is(); + actor->wakeUp(); + } + + mMaterial->setRestitution(restitution); + mMaterial->setStaticFriction(staticFriction); + mMaterial->setDynamicFriction(friction); + +} + +void Px3Body::setSleepThreshold( F32 linear, F32 angular ) +{ + AssertFatal( mActor, "Px3Body::setSleepThreshold - The actor is null!" ); + + if(mIsStatic) + return; + + physx::PxRigidDynamic *actor = mActor->is(); + physx::PxF32 massNormalized= (linear*linear+angular*angular)/2.0f; + actor->setSleepThreshold(massNormalized); +} + +void Px3Body::setDamping( F32 linear, F32 angular ) +{ + AssertFatal( mActor, "Px3Body::setDamping - The actor is null!" ); + if(mIsStatic) + return; + + physx::PxRigidDynamic *actor = mActor->is(); + actor->setLinearDamping( linear ); + actor->setAngularDamping( angular ); +} + +void Px3Body::getState( PhysicsState *outState ) +{ + AssertFatal( mActor, "Px3Body::getState - The actor is null!" ); + AssertFatal( isDynamic(), "Px3Body::getState - This call is only for dynamics!" ); + + outState->position = px3Cast( mActor->getGlobalPose().p ); + outState->orientation = px3Cast( mActor->getGlobalPose().q ); + + physx::PxRigidDynamic *actor = mActor->is(); + outState->linVelocity = px3Cast( actor->getLinearVelocity() ); + outState->angVelocity = px3Cast( actor->getAngularVelocity() ); + outState->sleeping = actor->isSleeping(); + outState->momentum = px3Cast( (1.0f/actor->getMass()) * actor->getLinearVelocity() );//?? + +} + +F32 Px3Body::getMass() const +{ + AssertFatal( mActor, "PxBody::getCMassPosition - The actor is null!" ); + if(mIsStatic) + return 0; + + const physx::PxRigidDynamic *actor = mActor->is(); + return actor->getMass(); +} + +Point3F Px3Body::getCMassPosition() const +{ + AssertFatal( mActor, "Px3Body::getCMassPosition - The actor is null!" ); + if(mIsStatic) + return px3Cast(mActor->getGlobalPose().p); + + physx::PxRigidDynamic *actor = mActor->is(); + physx::PxTransform pose = actor->getGlobalPose() * actor->getCMassLocalPose(); + return px3Cast(pose.p); +} + +void Px3Body::setLinVelocity( const Point3F &vel ) +{ + AssertFatal( mActor, "Px3Body::setLinVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "Px3Body::setLinVelocity - This call is only for dynamics!" ); + + physx::PxRigidDynamic *actor = mActor->is(); + actor->setLinearVelocity( px3Cast( vel ) ); +} + +void Px3Body::setAngVelocity( const Point3F &vel ) +{ + AssertFatal( mActor, "Px3Body::setAngVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "Px3Body::setAngVelocity - This call is only for dynamics!" ); + + physx::PxRigidDynamic *actor = mActor->is(); + actor->setAngularVelocity(px3Cast( vel ) ); +} + +Point3F Px3Body::getLinVelocity() const +{ + AssertFatal( mActor, "Px3Body::getLinVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "Px3Body::getLinVelocity - This call is only for dynamics!" ); + + physx::PxRigidDynamic *actor = mActor->is(); + return px3Cast( actor->getLinearVelocity() ); +} + +Point3F Px3Body::getAngVelocity() const +{ + AssertFatal( mActor, "Px3Body::getAngVelocity - The actor is null!" ); + AssertFatal( isDynamic(), "Px3Body::getAngVelocity - This call is only for dynamics!" ); + + physx::PxRigidDynamic *actor = mActor->is(); + return px3Cast( actor->getAngularVelocity() ); +} + +void Px3Body::setSleeping( bool sleeping ) +{ + AssertFatal( mActor, "Px3Body::setSleeping - The actor is null!" ); + AssertFatal( isDynamic(), "Px3Body::setSleeping - This call is only for dynamics!" ); + + physx::PxRigidDynamic *actor = mActor->is(); + if ( sleeping ) + actor->putToSleep(); + else + actor->wakeUp(); +} + +bool Px3Body::isDynamic() const +{ + AssertFatal( mActor, "PxBody::isDynamic - The actor is null!" ); + return !mIsStatic && ( mBodyFlags & BF_KINEMATIC ) == 0; +} + +PhysicsWorld* Px3Body::getWorld() +{ + return mWorld; +} + +PhysicsCollision* Px3Body::getColShape() +{ + return mColShape; +} + +MatrixF& Px3Body::getTransform( MatrixF *outMatrix ) +{ + AssertFatal( mActor, "Px3Body::getTransform - The actor is null!" ); + + *outMatrix = px3Cast(mActor->getGlobalPose()); + return *outMatrix; +} + +Box3F Px3Body::getWorldBounds() +{ + AssertFatal( mActor, "Px3Body::getTransform - The actor is null!" ); + + physx::PxBounds3 bounds; + bounds.setEmpty(); + physx::PxBounds3 shapeBounds; + + + U32 shapeCount = mActor->getNbShapes(); + physx::PxShape **shapes = new physx::PxShape*[shapeCount]; + mActor->getShapes(shapes, shapeCount); + for ( U32 i = 0; i < shapeCount; i++ ) + { + // Get the shape's bounds. + shapeBounds = physx::PxShapeExt::getWorldBounds(*shapes[i],*mActor); + // Combine them into the total bounds. + bounds.include( shapeBounds ); + } + + delete [] shapes; + + return px3Cast( bounds ); +} + +void Px3Body::setSimulationEnabled( bool enabled ) +{ + if ( mIsEnabled == enabled ) + return; + + //Don't need to enable/disable eSIMULATION_SHAPE for trigger,it's disabled permanently + if(mBodyFlags & BF_TRIGGER) + return; + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + + U32 shapeCount = mActor->getNbShapes(); + physx::PxShape **shapes = new physx::PxShape*[shapeCount]; + mActor->getShapes(shapes, shapeCount); + for ( S32 i = 0; i < mActor->getNbShapes(); i++ ) + { + shapes[i]->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE,!mIsEnabled);//????? + } + + delete [] shapes; +} + +void Px3Body::setTransform( const MatrixF &transform ) +{ + AssertFatal( mActor, "Px3Body::setTransform - The actor is null!" ); + + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + + + mActor->setGlobalPose(px3Cast(transform),false); + + if(mIsStatic) + return; + + physx::PxRigidDynamic *actor = mActor->is(); + bool kinematic = actor->getRigidDynamicFlags() & physx::PxRigidDynamicFlag::eKINEMATIC; + // If its dynamic we have more to do. + if ( isDynamic() && !kinematic ) + { + actor->setLinearVelocity( physx::PxVec3(0) ); + actor->setAngularVelocity( physx::PxVec3(0) ); + actor->wakeUp(); + } +} + +void Px3Body::applyCorrection( const MatrixF &transform ) +{ + AssertFatal( mActor, "Px3Body::applyCorrection - The actor is null!" ); + AssertFatal( isDynamic(), "Px3Body::applyCorrection - This call is only for dynamics!" ); + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + + mActor->setGlobalPose( px3Cast(transform) ); +} + +void Px3Body::applyImpulse( const Point3F &origin, const Point3F &force ) +{ + AssertFatal( mActor, "Px3Body::applyImpulse - The actor is null!" ); + + // This sucks, but it has to happen if we want + // to avoid write lock errors from PhysX right now. + mWorld->releaseWriteLock(); + physx::PxRigidDynamic *actor = mActor->is(); + if ( mIsEnabled && isDynamic() ) + physx::PxRigidBodyExt::addForceAtPos(*actor,px3Cast(force), + px3Cast(origin), + physx::PxForceMode::eIMPULSE); + +} + diff --git a/Engine/source/T3D/physics/physx3/px3Body.h b/Engine/source/T3D/physics/physx3/px3Body.h new file mode 100644 index 000000000..379d5b4cd --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Body.h @@ -0,0 +1,121 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PX3BODY_H_ +#define _T3D_PHYSICS_PX3BODY_H_ + +#ifndef _T3D_PHYSICS_PHYSICSBODY_H_ +#include "T3D/physics/physicsBody.h" +#endif +#ifndef _PHYSICS_PHYSICSUSERDATA_H_ +#include "T3D/physics/physicsUserData.h" +#endif +#ifndef _REFBASE_H_ +#include "core/util/refBase.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif + +class Px3World; +class Px3Collision; +struct Px3CollisionDesc; + +namespace physx{ + class PxRigidActor; + class PxMaterial; + class PxShape; +} + + +class Px3Body : public PhysicsBody +{ +protected: + + /// The physics world we are in. + Px3World *mWorld; + + /// The physics actor. + physx::PxRigidActor *mActor; + + /// The unshared local material used on all the + /// shapes on this actor. + physx::PxMaterial *mMaterial; + + /// We hold the collision reference as it contains + /// allocated objects that we own and must free. + StrongRefPtr mColShape; + + /// + MatrixF mInternalTransform; + + /// The body flags set at creation time. + U32 mBodyFlags; + + /// Is true if this body is enabled and active + /// in the simulation of the scene. + bool mIsEnabled; + bool mIsStatic; + + /// + void _releaseActor(); + + +public: + + Px3Body(); + virtual ~Px3Body(); + + // PhysicsObject + virtual PhysicsWorld* getWorld(); + virtual void setTransform( const MatrixF &xfm ); + virtual MatrixF& getTransform( MatrixF *outMatrix ); + virtual Box3F getWorldBounds(); + virtual void setSimulationEnabled( bool enabled ); + virtual bool isSimulationEnabled() { return mIsEnabled; } + + // PhysicsBody + virtual bool init( PhysicsCollision *shape, + F32 mass, + U32 bodyFlags, + SceneObject *obj, + PhysicsWorld *world ); + virtual bool isDynamic() const; + virtual PhysicsCollision* getColShape(); + virtual void setSleepThreshold( F32 linear, F32 angular ); + virtual void setDamping( F32 linear, F32 angular ); + virtual void getState( PhysicsState *outState ); + virtual F32 getMass() const; + virtual Point3F getCMassPosition() const; + virtual void setLinVelocity( const Point3F &vel ); + virtual void setAngVelocity( const Point3F &vel ); + virtual Point3F getLinVelocity() const; + virtual Point3F getAngVelocity() const; + virtual void setSleeping( bool sleeping ); + virtual void setMaterial( F32 restitution, + F32 friction, + F32 staticFriction ); + virtual void applyCorrection( const MatrixF &xfm ); + virtual void applyImpulse( const Point3F &origin, const Point3F &force ); +}; + +#endif diff --git a/Engine/source/T3D/physics/physx3/px3Cast.h b/Engine/source/T3D/physics/physx3/px3Cast.h new file mode 100644 index 000000000..971e830c8 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Cast.h @@ -0,0 +1,137 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSX3_CASTS_H_ +#define _PHYSX3_CASTS_H_ + +#ifndef _MPOINT3_H_ +#include "math/mPoint3.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +#ifndef _MBOX_H_ +#include "math/mBox.h" +#endif +#ifndef _MQUAT_H_ +#include "math/mQuat.h" +#endif +#ifndef _MTRANSFORM_H_ +#include "math/mTransform.h" +#endif + + +template inline T px3Cast( const F &from ); + +//------------------------------------------------------------------------- + +template<> +inline Point3F px3Cast( const physx::PxVec3 &vec ) +{ + return Point3F( vec.x, vec.y, vec.z ); +} + +template<> +inline physx::PxVec3 px3Cast( const Point3F &point ) +{ + return physx::PxVec3( point.x, point.y, point.z ); +} +//------------------------------------------------------------------------- +template<> +inline QuatF px3Cast( const physx::PxQuat &quat ) +{ + /// The Torque quat has the opposite winding order. + return QuatF( -quat.x, -quat.y, -quat.z, quat.w ); +} + +template<> +inline physx::PxQuat px3Cast( const QuatF &quat ) +{ + /// The Torque quat has the opposite winding order. + physx::PxQuat result( -quat.x, -quat.y, -quat.z, quat.w ); + return result; +} +//------------------------------------------------------------------------- + +template<> +inline physx::PxExtendedVec3 px3Cast( const Point3F &point ) +{ + return physx::PxExtendedVec3( point.x, point.y, point.z ); +} + +template<> +inline Point3F px3Cast( const physx::PxExtendedVec3 &xvec ) +{ + return Point3F( xvec.x, xvec.y, xvec.z ); +} + +//------------------------------------------------------------------------- + +template<> +inline physx::PxBounds3 px3Cast( const Box3F &box ) +{ + physx::PxBounds3 bounds(px3Cast(box.minExtents), + px3Cast(box.maxExtents)); + return bounds; +} + +template<> +inline Box3F px3Cast( const physx::PxBounds3 &bounds ) +{ + return Box3F( bounds.minimum.x, + bounds.minimum.y, + bounds.minimum.z, + bounds.maximum.x, + bounds.maximum.y, + bounds.maximum.z ); +} + +//------------------------------------------------------------------------- + +template<> +inline physx::PxTransform px3Cast( const MatrixF &xfm ) +{ + physx::PxTransform out; + QuatF q; + q.set(xfm); + out.q = px3Cast(q); + out.p = px3Cast(xfm.getPosition()); + return out; +} + +template<> +inline TransformF px3Cast(const physx::PxTransform &xfm) +{ + TransformF out(px3Cast(xfm.p),AngAxisF(px3Cast(xfm.q))); + return out; +} + +template<> +inline MatrixF px3Cast( const physx::PxTransform &xfm ) +{ + MatrixF out; + TransformF t = px3Cast(xfm); + out = t.getMatrix(); + return out; +} + +#endif diff --git a/Engine/source/T3D/physics/physx3/px3Collision.cpp b/Engine/source/T3D/physics/physx3/px3Collision.cpp new file mode 100644 index 000000000..fb11a4400 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Collision.cpp @@ -0,0 +1,212 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physx3/px3Collision.h" + +#include "math/mPoint3.h" +#include "math/mMatrix.h" +#include "T3D/physics/physx3/px3.h" +#include "T3D/physics/physx3/px3Cast.h" +#include "T3D/physics/physx3/px3World.h" +#include "T3D/physics/physx3/px3Stream.h" + + +Px3Collision::Px3Collision() +{ +} + +Px3Collision::~Px3Collision() +{ + + for ( U32 i=0; i < mColShapes.size(); i++ ) + { + Px3CollisionDesc *desc = mColShapes[i]; + delete desc->pGeometry; + // Delete the descriptor. + delete desc; + } + + mColShapes.clear(); +} + +void Px3Collision::addPlane( const PlaneF &plane ) +{ + physx::PxVec3 pos = px3Cast(plane.getPosition()); + Px3CollisionDesc *desc = new Px3CollisionDesc; + desc->pGeometry = new physx::PxPlaneGeometry(); + desc->pose = physx::PxTransform(pos, physx::PxQuat(physx::PxHalfPi, physx::PxVec3(0.0f, -1.0f, 0.0f))); + mColShapes.push_back(desc); +} + +void Px3Collision::addBox( const Point3F &halfWidth,const MatrixF &localXfm ) +{ + Px3CollisionDesc *desc = new Px3CollisionDesc; + desc->pGeometry = new physx::PxBoxGeometry(px3Cast(halfWidth)); + desc->pose = px3Cast(localXfm); + mColShapes.push_back(desc); +} + +void Px3Collision::addSphere( F32 radius, + const MatrixF &localXfm ) +{ + Px3CollisionDesc *desc = new Px3CollisionDesc; + desc->pGeometry = new physx::PxSphereGeometry(radius); + desc->pose = px3Cast(localXfm); + mColShapes.push_back(desc); +} + +void Px3Collision::addCapsule( F32 radius, + F32 height, + const MatrixF &localXfm ) +{ + Px3CollisionDesc *desc = new Px3CollisionDesc; + desc->pGeometry = new physx::PxCapsuleGeometry(radius,height*0.5);//uses half height + desc->pose = px3Cast(localXfm); + mColShapes.push_back(desc); +} + +bool Px3Collision::addConvex( const Point3F *points, + U32 count, + const MatrixF &localXfm ) +{ + physx::PxCooking *cooking = Px3World::getCooking(); + physx::PxConvexMeshDesc convexDesc; + convexDesc.points.data = points; + convexDesc.points.stride = sizeof(Point3F); + convexDesc.points.count = count; + convexDesc.flags = physx::PxConvexFlag::eFLIPNORMALS|physx::PxConvexFlag::eCOMPUTE_CONVEX | physx::PxConvexFlag::eINFLATE_CONVEX; + + Px3MemOutStream stream; + if(!cooking->cookConvexMesh(convexDesc,stream)) + return false; + + physx::PxConvexMesh* convexMesh; + Px3MemInStream in(stream.getData(), stream.getSize()); + convexMesh = gPhysics3SDK->createConvexMesh(in); + + Px3CollisionDesc *desc = new Px3CollisionDesc; + desc->pGeometry = new physx::PxConvexMeshGeometry(convexMesh); + desc->pose = px3Cast(localXfm); + mColShapes.push_back(desc); + return true; +} + +bool Px3Collision::addTriangleMesh( const Point3F *vert, + U32 vertCount, + const U32 *index, + U32 triCount, + const MatrixF &localXfm ) +{ + physx::PxCooking *cooking = Px3World::getCooking(); + physx::PxTriangleMeshDesc meshDesc; + meshDesc.points.count = vertCount; + meshDesc.points.data = vert; + meshDesc.points.stride = sizeof(Point3F); + + meshDesc.triangles.count = triCount; + meshDesc.triangles.data = index; + meshDesc.triangles.stride = 3*sizeof(U32); + meshDesc.flags = physx::PxMeshFlag::eFLIPNORMALS; + + Px3MemOutStream stream; + if(!cooking->cookTriangleMesh(meshDesc,stream)) + return false; + + physx::PxTriangleMesh *mesh; + Px3MemInStream in(stream.getData(), stream.getSize()); + mesh = gPhysics3SDK->createTriangleMesh(in); + + Px3CollisionDesc *desc = new Px3CollisionDesc; + desc->pGeometry = new physx::PxTriangleMeshGeometry(mesh); + desc->pose = px3Cast(localXfm); + mColShapes.push_back(desc); + return true; +} + +bool Px3Collision::addHeightfield( const U16 *heights, + const bool *holes, + U32 blockSize, + F32 metersPerSample, + const MatrixF &localXfm ) +{ + const F32 heightScale = 0.03125f; + physx::PxHeightFieldSample* samples = (physx::PxHeightFieldSample*) new physx::PxHeightFieldSample[blockSize*blockSize]; + memset(samples,0,blockSize*blockSize*sizeof(physx::PxHeightFieldSample)); + + physx::PxHeightFieldDesc heightFieldDesc; + heightFieldDesc.nbColumns = blockSize; + heightFieldDesc.nbRows = blockSize; + heightFieldDesc.thickness = -10.f; + heightFieldDesc.convexEdgeThreshold = 0; + heightFieldDesc.format = physx::PxHeightFieldFormat::eS16_TM; + heightFieldDesc.samples.data = samples; + heightFieldDesc.samples.stride = sizeof(physx::PxHeightFieldSample); + + physx::PxU8 *currentByte = (physx::PxU8*)heightFieldDesc.samples.data; + for ( U32 row = 0; row < blockSize; row++ ) + { + const U32 tess = ( row + 1 ) % 2; + + for ( U32 column = 0; column < blockSize; column++ ) + { + physx::PxHeightFieldSample *currentSample = (physx::PxHeightFieldSample*)currentByte; + + U32 index = ( blockSize - row - 1 ) + ( column * blockSize ); + currentSample->height = (physx::PxI16)heights[ index ]; + + + if ( holes && holes[ getMax( (S32)index - 1, 0 ) ] ) // row index for holes adjusted so PhysX collision shape better matches rendered terrain + { + currentSample->materialIndex0 = 0; + currentSample->materialIndex1 = 0; + } + else + { + currentSample->materialIndex0 = 1; + currentSample->materialIndex1 = 1; + } + + int flag = ( column + tess ) % 2; + if(flag) + currentSample->setTessFlag(); + else + currentSample->clearTessFlag(); + + currentByte += heightFieldDesc.samples.stride; + } + } + + physx::PxHeightField * hf = gPhysics3SDK->createHeightField(heightFieldDesc); + physx::PxHeightFieldGeometry *geom = new physx::PxHeightFieldGeometry(hf,physx::PxMeshGeometryFlags(),heightScale,metersPerSample,metersPerSample); + + physx::PxTransform pose= physx::PxTransform(physx::PxQuat(Float_HalfPi, physx::PxVec3(1, 0, 0 ))); + physx::PxTransform pose1= physx::PxTransform(physx::PxQuat(Float_Pi, physx::PxVec3(0, 0, 1 ))); + physx::PxTransform pose2 = pose1 * pose; + pose2.p = physx::PxVec3(( blockSize - 1 ) * metersPerSample, 0, 0 ); + Px3CollisionDesc *desc = new Px3CollisionDesc; + desc->pGeometry = geom; + desc->pose = pose2; + + mColShapes.push_back(desc); + return true; +} diff --git a/Engine/source/T3D/physics/physx3/px3Collision.h b/Engine/source/T3D/physics/physx3/px3Collision.h new file mode 100644 index 000000000..857f4f5e3 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Collision.h @@ -0,0 +1,87 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PX3COLLISION_H_ +#define _T3D_PHYSICS_PX3COLLISION_H_ + +#ifndef _T3D_PHYSICS_PHYSICSCOLLISION_H_ +#include "T3D/physics/physicsCollision.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif +#ifndef _MMATRIX_H_ +#include "math/mMatrix.h" +#endif +//nasty hate doing this! +#include + +//forward declare +namespace physx{class PxGeometry;} + + +struct Px3CollisionDesc +{ + physx::PxGeometry *pGeometry; + physx::PxTransform pose; +}; + +class Px3Collision : public PhysicsCollision +{ + typedef Vector Px3CollisionList; +protected: + /// The collision representation. + Px3CollisionList mColShapes; + +public: + + Px3Collision(); + virtual ~Px3Collision(); + + /// Return the PhysX shape descriptions. + const Px3CollisionList& getShapes() const { return mColShapes; } + + // PhysicsCollision + virtual void addPlane( const PlaneF &plane ); + virtual void addBox( const Point3F &halfWidth, + const MatrixF &localXfm ); + virtual void addSphere( F32 radius, + const MatrixF &localXfm ); + virtual void addCapsule( F32 radius, + F32 height, + const MatrixF &localXfm ); + virtual bool addConvex( const Point3F *points, + U32 count, + const MatrixF &localXfm ); + virtual bool addTriangleMesh( const Point3F *vert, + U32 vertCount, + const U32 *index, + U32 triCount, + const MatrixF &localXfm ); + virtual bool addHeightfield( const U16 *heights, + const bool *holes, + U32 blockSize, + F32 metersPerSample, + const MatrixF &localXfm ); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx3/px3Player.cpp b/Engine/source/T3D/physics/physx3/px3Player.cpp new file mode 100644 index 000000000..1c44068e9 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Player.cpp @@ -0,0 +1,328 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physx3/px3Player.h" +#include "T3D/physics/physicsPlugin.h" +#include "T3D/physics/physx3/px3World.h" +#include "T3D/physics/physx3/px3Cast.h" +#include "T3D/physics/physx3/px3Utils.h" +#include "collision/collision.h" + + +Px3Player::Px3Player() + : PhysicsPlayer(), + mController( NULL ), + mWorld( NULL ), + mObject( NULL ), + mSkinWidth( 0.05f ), + mOriginOffset( 0.0f ), + mElapsed(0) +{ + PHYSICSMGR->getPhysicsResetSignal().notify( this, &Px3Player::_onPhysicsReset ); +} + +Px3Player::~Px3Player() +{ + _releaseController(); + PHYSICSMGR->getPhysicsResetSignal().remove( this, &Px3Player::_onPhysicsReset ); +} + +void Px3Player::_releaseController() +{ + if ( mController ) + { + mController->getActor()->userData = NULL; + mWorld->getStaticChangedSignal().remove( this, &Px3Player::_onStaticChanged ); + mController->release(); + } +} + +void Px3Player::init( const char *type, + const Point3F &size, + F32 runSurfaceCos, + F32 stepHeight, + SceneObject *obj, + PhysicsWorld *world ) +{ + AssertFatal( obj, "Px3Player::init - Got a null scene object!" ); + AssertFatal( world, "Px3Player::init - Got a null world!" ); + AssertFatal( dynamic_cast( world ), "Px3Player::init - The world is the wrong type!" ); + + // Cleanup any previous controller. + _releaseController(); + + mObject = obj; + mWorld = (Px3World*)world; + mOriginOffset = size.z * 0.5f; + + physx::PxCapsuleControllerDesc desc; + desc.contactOffset = mSkinWidth; + desc.radius = getMax( size.x, size.y ) * 0.5f; + desc.radius -= mSkinWidth; + desc.height = size.z - ( desc.radius * 2.0f ); + desc.height -= mSkinWidth * 2.0f; + desc.climbingMode = physx::PxCapsuleClimbingMode::eCONSTRAINED; + desc.position.set( 0, 0, 0 ); + desc.upDirection = physx::PxVec3(0,0,1); + desc.reportCallback = this; + desc.slopeLimit = runSurfaceCos; + desc.stepOffset = stepHeight; + desc.behaviorCallback = NULL; + desc.material = gPhysics3SDK->createMaterial(0.1f, 0.1f, 0.2f); + + mController = mWorld->createController( desc ); + + mWorld->getStaticChangedSignal().notify( this, &Px3Player::_onStaticChanged ); + physx::PxRigidDynamic *kineActor = mController->getActor(); + + //player only has one shape + physx::PxShape *shape = px3GetFirstShape(kineActor); + physx::PxFilterData colData; + colData.word0 = PX3_PLAYER; + shape->setSimulationFilterData(colData); + shape->setQueryFilterData(colData); + + //store geometry for later use in findContact calls + shape->getCapsuleGeometry(mGeometry); + + mUserData.setObject( obj ); + kineActor->userData = &mUserData; + +} + +void Px3Player::_onStaticChanged() +{ + if(mController) + mController->invalidateCache(); +} + +void Px3Player::_onPhysicsReset( PhysicsResetEvent reset ) +{ + if(mController) + mController->invalidateCache(); +} + +Point3F Px3Player::move( const VectorF &disp, CollisionList &outCol ) +{ + AssertFatal( mController, "Px3Player::move - The controller is null!" ); + + // Return the last position if the simulation is stopped. + // + // See PxPlayer::_onPhysicsReset + if ( !mWorld->isEnabled() ) + { + Point3F newPos = px3Cast( mController->getPosition() ); + newPos.z -= mOriginOffset; + return newPos; + } + + mWorld->releaseWriteLock(); + + mCollisionList = &outCol; + + physx::PxVec3 dispNx( disp.x, disp.y, disp.z ); + if (mIsZero(disp.z)) + dispNx.z = 0.0f; + + U32 groups = 0xffffffff; + groups &= ~( PX3_TRIGGER ); // No trigger shapes! + groups &= ~( PX3_DEBRIS); + physx::PxControllerFilters filter; + physx::PxFilterData data; + data.word0=groups; + filter.mFilterData = &data; + filter.mFilterFlags = physx::PxSceneQueryFilterFlags(physx::PxControllerFlag::eCOLLISION_DOWN|physx::PxControllerFlag::eCOLLISION_SIDES|physx::PxControllerFlag::eCOLLISION_UP); + + mController->move( dispNx,0.0001f,0, filter ); + + Point3F newPos = px3Cast( mController->getPosition() ); + newPos.z -= mOriginOffset; + + mCollisionList = NULL; + + return newPos; +} + +void Px3Player::onShapeHit( const physx::PxControllerShapeHit& hit ) +{ + if (!mCollisionList || mCollisionList->getCount() >= CollisionList::MaxCollisions) + return; + + physx::PxRigidActor *actor = hit.actor; + PhysicsUserData *userData = PhysicsUserData::cast( actor->userData ); + + // Fill out the Collision + // structure for use later. + Collision &col = mCollisionList->increment(); + dMemset( &col, 0, sizeof( col ) ); + + col.normal = px3Cast( hit.worldNormal ); + col.point = px3Cast( hit.worldPos ); + col.distance = hit.length; + if ( userData ) + col.object = userData->getObject(); + + if (mIsZero(hit.dir.z)) + { + if (col.normal.z > 0.0f) + { + col.normal.z = 0.0f; + col.normal.normalizeSafe(); + } + } + else + { + col.normal.set(0.0f, 0.0f, 1.0f); + } + + +} + +void Px3Player::onControllerHit( const physx::PxControllersHit& hit ) +{ + if (!mCollisionList || mCollisionList->getCount() >= CollisionList::MaxCollisions) + return; + + physx::PxRigidActor *actor = hit.other->getActor(); + PhysicsUserData *userData = PhysicsUserData::cast( actor->userData ); + + // For controller-to-controller hit we don't have an actual hit point, so all + // we can do is set the hit object. + Collision &col = mCollisionList->increment(); + dMemset( &col, 0, sizeof( col ) ); + if ( userData ) + col.object = userData->getObject(); + +} + +void Px3Player::findContact( SceneObject **contactObject, + VectorF *contactNormal, + Vector *outOverlapObjects ) const +{ + // Calculate the sweep motion... + F32 halfCapSize = mOriginOffset; + F32 halfSmallCapSize = halfCapSize * 0.8f; + F32 diff = halfCapSize - halfSmallCapSize; + + F32 distance = diff + mSkinWidth + 0.01f; + physx::PxVec3 dir(0,0,-1); + + physx::PxScene *scene = mWorld->getScene(); + physx::PxHitFlags hitFlags(physx::PxHitFlag::eDEFAULT); + physx::PxQueryFilterData filterData(physx::PxQueryFlag::eDYNAMIC|physx::PxQueryFlag::eSTATIC); + filterData.data.word0 = PX3_DEFAULT; + physx::PxSweepHit sweepHit; + physx::PxRigidDynamic *actor= mController->getActor(); + physx::PxU32 shapeIndex; + + bool hit = physx::PxRigidBodyExt::linearSweepSingle(*actor,*scene,dir,distance,hitFlags,sweepHit,shapeIndex,filterData); + if ( hit ) + { + PhysicsUserData *data = PhysicsUserData::cast( sweepHit.actor->userData); + if ( data ) + { + *contactObject = data->getObject(); + *contactNormal = px3Cast( sweepHit.normal ); + } + } + + // Check for overlapped objects ( triggers ) + + if ( !outOverlapObjects ) + return; + + filterData.data.word0 = PX3_TRIGGER; + + const physx::PxU32 bufferSize = 10; + physx::PxOverlapBufferN hitBuffer; + hit = scene->overlap(mGeometry,actor->getGlobalPose(),hitBuffer,filterData); + if(hit) + { + for ( U32 i = 0; i < hitBuffer.nbTouches; i++ ) + { + PhysicsUserData *data = PhysicsUserData::cast( hitBuffer.touches[i].actor->userData ); + if ( data ) + outOverlapObjects->push_back( data->getObject() ); + } + } + +} + +void Px3Player::enableCollision() +{ + AssertFatal( mController, "Px3Player::enableCollision - The controller is null!" ); + + mWorld->releaseWriteLock(); + px3GetFirstShape(mController->getActor())->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE,true); +} + +void Px3Player::disableCollision() +{ + AssertFatal( mController, "Px3Player::disableCollision - The controller is null!" ); + + mWorld->releaseWriteLock(); + px3GetFirstShape(mController->getActor())->setFlag(physx::PxShapeFlag::eSIMULATION_SHAPE,false); +} + +PhysicsWorld* Px3Player::getWorld() +{ + return mWorld; +} + +void Px3Player::setTransform( const MatrixF &transform ) +{ + AssertFatal( mController, "Px3Player::setTransform - The controller is null!" ); + + mWorld->releaseWriteLock(); + + Point3F newPos = transform.getPosition(); + newPos.z += mOriginOffset; + + const Point3F &curPos = px3Cast(mController->getPosition()); + + if ( !(newPos - curPos ).isZero() ) + mController->setPosition( px3Cast(newPos) ); +} + +MatrixF& Px3Player::getTransform( MatrixF *outMatrix ) +{ + AssertFatal( mController, "Px3Player::getTransform - The controller is null!" ); + + Point3F newPos = px3Cast( mController->getPosition() ); + newPos.z -= mOriginOffset; + outMatrix->setPosition( newPos ); + + return *outMatrix; +} + +void Px3Player::setScale( const Point3F &scale ) +{ + //Ignored +} + +Box3F Px3Player::getWorldBounds() +{ + Con::warnf( "Px3Player::getWorldBounds - not implemented" ); + return Box3F::Invalid; +} + diff --git a/Engine/source/T3D/physics/physx3/px3Player.h b/Engine/source/T3D/physics/physx3/px3Player.h new file mode 100644 index 000000000..c8dee08e6 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Player.h @@ -0,0 +1,104 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PX3PLAYER_H +#define _PX3PLAYER_H + +#ifndef _PHYSX3_H_ +#include "T3D/physics/physx3/px3.h" +#endif +#ifndef _T3D_PHYSICS_PHYSICSPLAYER_H_ +#include "T3D/physics/physicsPlayer.h" +#endif +#ifndef _T3D_PHYSICSCOMMON_H_ +#include "T3D/physics/physicsCommon.h" +#endif + +class Px3World; + +class Px3Player : public PhysicsPlayer, public physx::PxUserControllerHitReport +{ +protected: + + physx::PxController *mController; + physx::PxCapsuleGeometry mGeometry; + F32 mSkinWidth; + + Px3World *mWorld; + + SceneObject *mObject; + + /// Used to get collision info out of the + /// PxUserControllerHitReport callbacks. + CollisionList *mCollisionList; + + /// + F32 mOriginOffset; + + /// + F32 mStepHeight; + U32 mElapsed; + /// + void _releaseController(); + + + virtual void onShapeHit( const physx::PxControllerShapeHit &hit ); + virtual void onControllerHit( const physx::PxControllersHit &hit ); + virtual void onObstacleHit(const physx::PxControllerObstacleHit &){} + + void _findContact( SceneObject **contactObject, VectorF *contactNormal ) const; + + void _onPhysicsReset( PhysicsResetEvent reset ); + + void _onStaticChanged(); + +public: + + Px3Player(); + virtual ~Px3Player(); + + // PhysicsObject + virtual PhysicsWorld* getWorld(); + virtual void setTransform( const MatrixF &transform ); + virtual MatrixF& getTransform( MatrixF *outMatrix ); + virtual void setScale( const Point3F &scale ); + virtual Box3F getWorldBounds(); + virtual void setSimulationEnabled( bool enabled ) {} + virtual bool isSimulationEnabled() { return true; } + + // PhysicsPlayer + virtual void init( const char *type, + const Point3F &size, + F32 runSurfaceCos, + F32 stepHeight, + SceneObject *obj, + PhysicsWorld *world ); + virtual Point3F move( const VectorF &displacement, CollisionList &outCol ); + virtual void findContact( SceneObject **contactObject, VectorF *contactNormal, Vector *outOverlapObjects ) const; + virtual bool testSpacials( const Point3F &nPos, const Point3F &nSize ) const { return true; } + virtual void setSpacials( const Point3F &nPos, const Point3F &nSize ) {} + virtual void enableCollision(); + virtual void disableCollision(); +}; + + +#endif // _PXPLAYER_H \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx3/px3Plugin.cpp b/Engine/source/T3D/physics/physx3/px3Plugin.cpp new file mode 100644 index 000000000..9da0ca026 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Plugin.cpp @@ -0,0 +1,227 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "console/consoleTypes.h" +#include "T3D/physics/physx3/px3World.h" +#include "T3D/physics/physx3/px3Plugin.h" +#include "T3D/physics/physx3/px3Collision.h" +#include "T3D/physics/physx3/px3Body.h" +#include "T3D/physics/physx3/px3Player.h" + +#include "T3D/physics/physicsShape.h" +#include "T3D/gameBase/gameProcess.h" +#include "core/util/tNamedFactory.h" + + +AFTER_MODULE_INIT( Sim ) +{ + NamedFactory::add( "PhysX3", &Px3Plugin::create ); + + #if defined(TORQUE_OS_WIN32) || defined(TORQUE_OS_XBOX) || defined(TORQUE_OS_XENON) + NamedFactory::add( "default", &Px3Plugin::create ); + #endif +} + +PhysicsPlugin* Px3Plugin::create() +{ + // Only create the plugin if it hasn't been set up AND + // the PhysX world is successfully initialized. + bool success = Px3World::restartSDK( false ); + if ( success ) + return new Px3Plugin(); + + return NULL; +} + +Px3Plugin::Px3Plugin() +{ +} + +Px3Plugin::~Px3Plugin() +{ +} + +void Px3Plugin::destroyPlugin() +{ + // Cleanup any worlds that are still kicking. + Map::Iterator iter = mPhysicsWorldLookup.begin(); + for ( ; iter != mPhysicsWorldLookup.end(); iter++ ) + { + iter->value->destroyWorld(); + delete iter->value; + } + mPhysicsWorldLookup.clear(); + + Px3World::restartSDK( true ); + + delete this; +} + +void Px3Plugin::reset() +{ + // First delete all the cleanup objects. + if ( getPhysicsCleanup() ) + getPhysicsCleanup()->deleteAllObjects(); + + getPhysicsResetSignal().trigger( PhysicsResetEvent_Restore ); + + // Now let each world reset itself. + Map::Iterator iter = mPhysicsWorldLookup.begin(); + for ( ; iter != mPhysicsWorldLookup.end(); iter++ ) + iter->value->reset(); +} + +PhysicsCollision* Px3Plugin::createCollision() +{ + return new Px3Collision(); +} + +PhysicsBody* Px3Plugin::createBody() +{ + return new Px3Body(); +} + +PhysicsPlayer* Px3Plugin::createPlayer() +{ + return new Px3Player(); +} + +bool Px3Plugin::isSimulationEnabled() const +{ + bool ret = false; + Px3World *world = static_cast( getWorld( smClientWorldName ) ); + if ( world ) + { + ret = world->isEnabled(); + return ret; + } + + world = static_cast( getWorld( smServerWorldName ) ); + if ( world ) + { + ret = world->isEnabled(); + return ret; + } + + return ret; +} + +void Px3Plugin::enableSimulation( const String &worldName, bool enable ) +{ + Px3World *world = static_cast( getWorld( worldName ) ); + if ( world ) + world->setEnabled( enable ); +} + +void Px3Plugin::setTimeScale( const F32 timeScale ) +{ + // Grab both the client and + // server worlds and set their time + // scales to the passed value. + Px3World *world = static_cast( getWorld( smClientWorldName ) ); + if ( world ) + world->setEditorTimeScale( timeScale ); + + world = static_cast( getWorld( smServerWorldName ) ); + if ( world ) + world->setEditorTimeScale( timeScale ); +} + +const F32 Px3Plugin::getTimeScale() const +{ + // Grab both the client and + // server worlds and call + // setEnabled( true ) on them. + Px3World *world = static_cast( getWorld( smClientWorldName ) ); + if ( !world ) + { + world = static_cast( getWorld( smServerWorldName ) ); + if ( !world ) + return 0.0f; + } + + return world->getEditorTimeScale(); +} + +bool Px3Plugin::createWorld( const String &worldName ) +{ + Map::Iterator iter = mPhysicsWorldLookup.find( worldName ); + PhysicsWorld *world = NULL; + + iter != mPhysicsWorldLookup.end() ? world = (*iter).value : world = NULL; + + if ( world ) + { + Con::errorf( "Px3Plugin::createWorld - %s world already exists!", worldName.c_str() ); + return false; + } + + world = new Px3World(); + + if ( worldName.equal( smClientWorldName, String::NoCase ) ) + world->initWorld( false, ClientProcessList::get() ); + else + world->initWorld( true, ServerProcessList::get() ); + + mPhysicsWorldLookup.insert( worldName, world ); + + return world != NULL; +} + +void Px3Plugin::destroyWorld( const String &worldName ) +{ + Map::Iterator iter = mPhysicsWorldLookup.find( worldName ); + if ( iter == mPhysicsWorldLookup.end() ) + return; + + PhysicsWorld *world = (*iter).value; + world->destroyWorld(); + delete world; + + mPhysicsWorldLookup.erase( iter ); +} + +PhysicsWorld* Px3Plugin::getWorld( const String &worldName ) const +{ + if ( mPhysicsWorldLookup.isEmpty() ) + return NULL; + + Map::ConstIterator iter = mPhysicsWorldLookup.find( worldName ); + + return iter != mPhysicsWorldLookup.end() ? (*iter).value : NULL; +} + +PhysicsWorld* Px3Plugin::getWorld() const +{ + if ( mPhysicsWorldLookup.size() == 0 ) + return NULL; + + Map::ConstIterator iter = mPhysicsWorldLookup.begin(); + return iter->value; +} + +U32 Px3Plugin::getWorldCount() const +{ + return mPhysicsWorldLookup.size(); +} + diff --git a/Engine/source/T3D/physics/physx3/px3Plugin.h b/Engine/source/T3D/physics/physx3/px3Plugin.h new file mode 100644 index 000000000..7972fe59a --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Plugin.h @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PX3PLUGIN_H_ +#define _T3D_PHYSICS_PX3PLUGIN_H_ + +#ifndef _T3D_PHYSICS_PHYSICSPLUGIN_H_ +#include "T3D/physics/physicsPlugin.h" +#endif + +class Px3Plugin : public PhysicsPlugin +{ +public: + + Px3Plugin(); + ~Px3Plugin(); + + /// Create function for factory. + static PhysicsPlugin* create(); + + // PhysicsPlugin + virtual void destroyPlugin(); + virtual void reset(); + virtual PhysicsCollision* createCollision(); + virtual PhysicsBody* createBody(); + virtual PhysicsPlayer* createPlayer(); + virtual bool isSimulationEnabled() const; + virtual void enableSimulation( const String &worldName, bool enable ); + virtual void setTimeScale( const F32 timeScale ); + virtual const F32 getTimeScale() const; + virtual bool createWorld( const String &worldName ); + virtual void destroyWorld( const String &worldName ); + virtual PhysicsWorld* getWorld( const String &worldName ) const; + virtual PhysicsWorld* getWorld() const; + virtual U32 getWorldCount() const; + +}; + +#endif \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx3/px3Stream.cpp b/Engine/source/T3D/physics/physx3/px3Stream.cpp new file mode 100644 index 000000000..f8374cf89 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Stream.cpp @@ -0,0 +1,92 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physx3/px3Stream.h" + +#include "console/console.h" +#include "console/consoleTypes.h" +#include "core/strings/stringFunctions.h" + + +Px3MemOutStream::Px3MemOutStream() : mMemStream(1024) +{ +} + +Px3MemOutStream::~Px3MemOutStream() +{ +} + +physx::PxU32 Px3MemOutStream::write(const void *src, physx::PxU32 count) +{ + physx::PxU32 out=0; + if(!mMemStream.write(count,src)) + return out; + + out = count; + return out; +} + +Px3MemInStream::Px3MemInStream(physx::PxU8* data, physx::PxU32 length):mMemStream(length,data) +{ +} + +physx::PxU32 Px3MemInStream::read(void* dest, physx::PxU32 count) +{ + physx::PxU32 read =0; + if(!mMemStream.read(count,dest)) + return read; + + read = count; + return read; +} + +void Px3MemInStream::seek(physx::PxU32 pos) +{ + mMemStream.setPosition(pos); +} + +physx::PxU32 Px3MemInStream::getLength() const +{ + return mMemStream.getStreamSize(); +} + +physx::PxU32 Px3MemInStream::tell() const +{ + return mMemStream.getPosition(); +} + +Px3ConsoleStream::Px3ConsoleStream() +{ +} + +Px3ConsoleStream::~Px3ConsoleStream() +{ +} + +void Px3ConsoleStream::reportError( physx::PxErrorCode code, const char *message, const char* file, int line ) +{ + UTF8 info[1024]; + dSprintf( info, 1024, "File: %s\nLine: %d\n%s", file, line, message ); + Platform::AlertOK( "PhysX Error", info ); + // Con::printf( "PhysX Error:\n %s(%d) : %s\n", file, line, message ); +} \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx3/px3Stream.h b/Engine/source/T3D/physics/physx3/px3Stream.h new file mode 100644 index 000000000..155165881 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Stream.h @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _T3D_PHYSICS_PX3STREAM_H_ +#define _T3D_PHYSICS_PX3STREAM_H_ + +#ifndef _PHYSX3_H_ +#include "T3D/physics/physx3/px3.h" +#endif +#ifndef _MEMSTREAM_H_ +#include "core/stream/memStream.h" +#endif + + +class Px3MemOutStream : public physx::PxOutputStream +{ +public: + + Px3MemOutStream(); + virtual ~Px3MemOutStream(); + + void resetPosition(); + + virtual physx::PxU32 write(const void *src, physx::PxU32 count); + physx::PxU32 getSize() const {return mMemStream.getStreamSize();} + physx::PxU8* getData() const {return (physx::PxU8*)mMemStream.getBuffer();} + +protected: + + mutable MemStream mMemStream; +}; + +class Px3MemInStream: public physx::PxInputData +{ + public: + Px3MemInStream(physx::PxU8* data, physx::PxU32 length); + virtual physx::PxU32 read(void* dest, physx::PxU32 count); + physx::PxU32 getLength() const; + virtual void seek(physx::PxU32 pos); + virtual physx::PxU32 tell() const; +protected: + mutable MemStream mMemStream; + + }; + +class Px3ConsoleStream : public physx::PxDefaultErrorCallback +{ +protected: + + virtual void reportError( physx::PxErrorCode code, const char *message, const char* file, int line ); + +public: + + Px3ConsoleStream(); + virtual ~Px3ConsoleStream(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx3/px3Utils.cpp b/Engine/source/T3D/physics/physx3/px3Utils.cpp new file mode 100644 index 000000000..ced52198c --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Utils.cpp @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physx3/px3Utils.h" +#include "T3D/physics/physx3/px3.h" + +physx::PxShape* px3GetFirstShape(physx::PxRigidActor *actor) +{ + physx::PxShape *shapes[1]; + actor->getShapes(shapes, 1); + return shapes[0]; +} diff --git a/Engine/source/T3D/physics/physx3/px3Utils.h b/Engine/source/T3D/physics/physx3/px3Utils.h new file mode 100644 index 000000000..d80b5acce --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3Utils.h @@ -0,0 +1,35 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSX3_UTILS_H_ +#define _PHYSX3_UTILS_H_ + +namespace physx +{ + class PxRigidActor; + class PxShape; +} + +extern physx::PxShape* px3GetFirstShape(physx::PxRigidActor *actor); + + +#endif \ No newline at end of file diff --git a/Engine/source/T3D/physics/physx3/px3World.cpp b/Engine/source/T3D/physics/physx3/px3World.cpp new file mode 100644 index 000000000..67fd78cf0 --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3World.cpp @@ -0,0 +1,567 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "platform/platform.h" +#include "T3D/physics/physx3/px3World.h" + +#include "T3D/physics/physx3/px3.h" +#include "T3D/physics/physx3/px3Plugin.h" +#include "T3D/physics/physx3/px3Cast.h" +#include "T3D/physics/physx3/px3Stream.h" +#include "T3D/physics/physicsUserData.h" + +#include "console/engineAPI.h" +#include "core/stream/bitStream.h" +#include "platform/profiler.h" +#include "sim/netConnection.h" +#include "console/console.h" +#include "console/consoleTypes.h" +#include "core/util/safeDelete.h" +#include "collision/collision.h" +#include "T3D/gameBase/gameProcess.h" +#include "gfx/sim/debugDraw.h" +#include "gfx/primBuilder.h" + + +physx::PxPhysics* gPhysics3SDK = NULL; +physx::PxCooking* Px3World::smCooking = NULL; +physx::PxFoundation* Px3World::smFoundation = NULL; +physx::PxProfileZoneManager* Px3World::smProfileZoneManager = NULL; +physx::PxDefaultCpuDispatcher* Px3World::smCpuDispatcher=NULL; +Px3ConsoleStream* Px3World::smErrorCallback = NULL; +physx::PxVisualDebuggerConnection* Px3World::smPvdConnection=NULL; +physx::PxDefaultAllocator Px3World::smMemoryAlloc; +//Physics timing +F32 Px3World::smPhysicsStepTime = 1.0f/(F32)TickMs; +U32 Px3World::smPhysicsMaxIterations = 4; + +Px3World::Px3World(): mScene( NULL ), + mProcessList( NULL ), + mIsSimulating( false ), + mErrorReport( false ), + mTickCount( 0 ), + mIsEnabled( false ), + mEditorTimeScale( 1.0f ), + mAccumulator( 0 ), + mControllerManager( NULL ) +{ +} + +Px3World::~Px3World() +{ +} + +physx::PxCooking *Px3World::getCooking() +{ + return smCooking; +} + +void Px3World::setTiming(F32 stepTime,U32 maxIterations) +{ + smPhysicsStepTime = stepTime; + smPhysicsMaxIterations = maxIterations; +} + +bool Px3World::restartSDK( bool destroyOnly, Px3World *clientWorld, Px3World *serverWorld) +{ + // If either the client or the server still exist + // then we cannot reset the SDK. + if ( clientWorld || serverWorld ) + return false; + + if(smPvdConnection) + smPvdConnection->release(); + + if(smCooking) + smCooking->release(); + + if(smCpuDispatcher) + smCpuDispatcher->release(); + + // Destroy the existing SDK. + if ( gPhysics3SDK ) + { + PxCloseExtensions(); + gPhysics3SDK->release(); + } + + if(smErrorCallback) + { + SAFE_DELETE(smErrorCallback); + } + + if(smFoundation) + { + smFoundation->release(); + SAFE_DELETE(smErrorCallback); + } + + // If we're not supposed to restart... return. + if ( destroyOnly ) + return true; + + bool memTrack = false; + #ifdef TORQUE_DEBUG + memTrack = true; + #endif + + smErrorCallback = new Px3ConsoleStream; + smFoundation = PxCreateFoundation(PX_PHYSICS_VERSION, smMemoryAlloc, *smErrorCallback); + smProfileZoneManager = &physx::PxProfileZoneManager::createProfileZoneManager(smFoundation); + gPhysics3SDK = PxCreatePhysics(PX_PHYSICS_VERSION, *smFoundation, physx::PxTolerancesScale(),memTrack,smProfileZoneManager); + + if ( !gPhysics3SDK ) + { + Con::errorf( "PhysX3 failed to initialize!" ); + Platform::messageBox( Con::getVariable( "$appName" ), + avar("PhysX3 could not be started!\r\n"), + MBOk, MIStop ); + Platform::forceShutdown( -1 ); + + // We shouldn't get here, but this shuts up + // source diagnostic tools. + return false; + } + + if(!PxInitExtensions(*gPhysics3SDK)) + { + Con::errorf( "PhysX3 failed to initialize extensions!" ); + Platform::messageBox( Con::getVariable( "$appName" ), + avar("PhysX3 could not be started!\r\n"), + MBOk, MIStop ); + Platform::forceShutdown( -1 ); + return false; + } + + smCooking = PxCreateCooking(PX_PHYSICS_VERSION, *smFoundation, physx::PxCookingParams(physx::PxTolerancesScale())); + if(!smCooking) + { + Con::errorf( "PhysX3 failed to initialize cooking!" ); + Platform::messageBox( Con::getVariable( "$appName" ), + avar("PhysX3 could not be started!\r\n"), + MBOk, MIStop ); + Platform::forceShutdown( -1 ); + return false; + } + + //just for testing-must remove, should really be enabled via console like physx 2 plugin +#ifdef TORQUE_DEBUG + physx::PxVisualDebuggerConnectionFlags connectionFlags(physx::PxVisualDebuggerExt::getAllConnectionFlags()); + smPvdConnection = physx::PxVisualDebuggerExt::createConnection(gPhysics3SDK->getPvdConnectionManager(), + "localhost", 5425, 100, connectionFlags); +#endif + + return true; +} + +void Px3World::destroyWorld() +{ + getPhysicsResults(); + + // Release the tick processing signals. + if ( mProcessList ) + { + mProcessList->preTickSignal().remove( this, &Px3World::getPhysicsResults ); + mProcessList->postTickSignal().remove( this, &Px3World::tickPhysics ); + mProcessList = NULL; + } + + if(mControllerManager) + { + mControllerManager->release(); + mControllerManager = NULL; + } + + // Destroy the scene. + if ( mScene ) + { + // Release the scene. + mScene->release(); + mScene = NULL; + } + +} + +bool Px3World::initWorld( bool isServer, ProcessList *processList ) +{ + if ( !gPhysics3SDK ) + { + Con::errorf( "Physx3World::init - PhysXSDK not initialized!" ); + return false; + } + + mIsServer = isServer; + + physx::PxSceneDesc sceneDesc(gPhysics3SDK->getTolerancesScale()); + + sceneDesc.gravity = px3Cast(mGravity); + sceneDesc.userData = this; + if(!sceneDesc.cpuDispatcher) + { + smCpuDispatcher = physx::PxDefaultCpuDispatcherCreate(PHYSICSMGR->getThreadCount()); + sceneDesc.cpuDispatcher = smCpuDispatcher; + Con::printf("PhysX3 using Cpu: %d workers", smCpuDispatcher->getWorkerCount()); + } + + sceneDesc.flags |= physx::PxSceneFlag::eENABLE_ACTIVETRANSFORMS; + + sceneDesc.filterShader = physx::PxDefaultSimulationFilterShader; + + mScene = gPhysics3SDK->createScene(sceneDesc); + + physx::PxDominanceGroupPair debrisDominance( 0.0f, 1.0f ); + mScene->setDominanceGroupPair(0,31,debrisDominance); + + mControllerManager = PxCreateControllerManager(*mScene); + + AssertFatal( processList, "Px3World::init() - We need a process list to create the world!" ); + mProcessList = processList; + mProcessList->preTickSignal().notify( this, &Px3World::getPhysicsResults ); + mProcessList->postTickSignal().notify( this, &Px3World::tickPhysics, 1000.0f ); + + return true; +} +// Most of this borrowed from bullet physics library, see btDiscreteDynamicsWorld.cpp +bool Px3World::_simulate(const F32 dt) +{ + int numSimulationSubSteps = 0; + //fixed timestep with interpolation + mAccumulator += dt; + if (mAccumulator >= smPhysicsStepTime) + { + numSimulationSubSteps = int(mAccumulator / smPhysicsStepTime); + mAccumulator -= numSimulationSubSteps * smPhysicsStepTime; + } + if (numSimulationSubSteps) + { + //clamp the number of substeps, to prevent simulation grinding spiralling down to a halt + int clampedSimulationSteps = (numSimulationSubSteps > smPhysicsMaxIterations)? smPhysicsMaxIterations : numSimulationSubSteps; + + for (int i=0;ifetchResults(true); + mScene->simulate(smPhysicsStepTime); + } + } + + mIsSimulating = true; + + return true; +} + +void Px3World::tickPhysics( U32 elapsedMs ) +{ + if ( !mScene || !mIsEnabled ) + return; + + // Did we forget to call getPhysicsResults somewhere? + AssertFatal( !mIsSimulating, "PhysX3World::tickPhysics() - Already simulating!" ); + + // The elapsed time should be non-zero and + // a multiple of TickMs! + AssertFatal( elapsedMs != 0 && + ( elapsedMs % TickMs ) == 0 , "PhysX3World::tickPhysics() - Got bad elapsed time!" ); + + PROFILE_SCOPE(Px3World_TickPhysics); + + // Convert it to seconds. + const F32 elapsedSec = (F32)elapsedMs * 0.001f; + mIsSimulating = _simulate(elapsedSec * mEditorTimeScale); + + //Con::printf( "%s PhysX3World::tickPhysics!", mIsServer ? "Client" : "Server" ); +} + +void Px3World::getPhysicsResults() +{ + if ( !mScene || !mIsSimulating ) + return; + + PROFILE_SCOPE(Px3World_GetPhysicsResults); + + // Get results from scene. + mScene->fetchResults(true); + mIsSimulating = false; + mTickCount++; + + // Con::printf( "%s PhysXWorld::getPhysicsResults!", this == smClientWorld ? "Client" : "Server" ); +} + +void Px3World::releaseWriteLocks() +{ + Px3World *world = dynamic_cast( PHYSICSMGR->getWorld( "server" ) ); + + if ( world ) + world->releaseWriteLock(); + + world = dynamic_cast( PHYSICSMGR->getWorld( "client" ) ); + + if ( world ) + world->releaseWriteLock(); +} + +void Px3World::releaseWriteLock() +{ + if ( !mScene || !mIsSimulating ) + return; + + PROFILE_SCOPE(PxWorld_ReleaseWriteLock); + + // We use checkResults here to release the write lock + // but we do not change the simulation flag or increment + // the tick count... we may have gotten results, but the + // simulation hasn't really ticked! + mScene->checkResults( true ); + //AssertFatal( mScene->isWritable(), "PhysX3World::releaseWriteLock() - We should have been writable now!" ); +} + +bool Px3World::castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse ) +{ + + physx::PxVec3 orig = px3Cast( startPnt ); + physx::PxVec3 dir = px3Cast( endPnt - startPnt ); + physx::PxF32 maxDist = dir.magnitude(); + dir.normalize(); + + U32 groups = 0xffffffff; + groups &= ~( PX3_TRIGGER ); // No trigger shapes! + + physx::PxHitFlags outFlags(physx::PxHitFlag::eDISTANCE | physx::PxHitFlag::eIMPACT | physx::PxHitFlag::eNORMAL); + physx::PxQueryFilterData filterData(physx::PxQueryFlag::eSTATIC|physx::PxQueryFlag::eDYNAMIC); + filterData.data.word0 = groups; + physx::PxRaycastBuffer buf; + + if(!mScene->raycast(orig,dir,maxDist,buf,outFlags,filterData)) + return false; + if(!buf.hasBlock) + return false; + + const physx::PxRaycastHit hit = buf.block; + physx::PxRigidActor *actor = hit.actor; + PhysicsUserData *userData = PhysicsUserData::cast( actor->userData ); + + if ( ri ) + { + ri->object = ( userData != NULL ) ? userData->getObject() : NULL; + + if ( ri->object == NULL ) + + ri->distance = hit.distance; + ri->normal = px3Cast( hit.normal ); + ri->point = px3Cast( hit.position ); + ri->t = maxDist / hit.distance; + } + + if ( impulse.isZero() || + !actor->isRigidDynamic() || + actor->is()->getRigidDynamicFlags() & physx::PxRigidDynamicFlag::eKINEMATIC ) + return true; + + physx::PxRigidBody *body = actor->is(); + physx::PxVec3 force = px3Cast( impulse ); + physx::PxRigidBodyExt::addForceAtPos(*body,force,hit.position,physx::PxForceMode::eIMPULSE); + + return true; +} + +PhysicsBody* Px3World::castRay( const Point3F &start, const Point3F &end, U32 bodyTypes ) +{ + physx::PxVec3 orig = px3Cast( start ); + physx::PxVec3 dir = px3Cast( end - start ); + physx::PxF32 maxDist = dir.magnitude(); + dir.normalize(); + + U32 groups = 0xFFFFFFFF; + if ( !( bodyTypes & BT_Player ) ) + groups &= ~( PX3_PLAYER ); + + // TODO: For now always skip triggers and debris, + // but we should consider how game specifc this API + // should be in the future. + groups &= ~( PX3_TRIGGER ); // triggers + groups &= ~( PX3_DEBRIS ); // debris + + physx::PxHitFlags outFlags(physx::PxHitFlag::eDISTANCE | physx::PxHitFlag::eIMPACT | physx::PxHitFlag::eNORMAL); + physx::PxQueryFilterData filterData; + if(bodyTypes & BT_Static) + filterData.flags |= physx::PxQueryFlag::eSTATIC; + if(bodyTypes & BT_Dynamic) + filterData.flags |= physx::PxQueryFlag::eDYNAMIC; + + filterData.data.word0 = groups; + physx::PxRaycastBuffer buf; + + if( !mScene->raycast(orig,dir,maxDist,buf,outFlags,filterData) ) + return NULL; + if(!buf.hasBlock) + return NULL; + + physx::PxRigidActor *actor = buf.block.actor; + PhysicsUserData *userData = PhysicsUserData::cast( actor->userData ); + if( !userData ) + return NULL; + + return userData->getBody(); +} + +void Px3World::explosion( const Point3F &pos, F32 radius, F32 forceMagnitude ) +{ + physx::PxVec3 nxPos = px3Cast( pos ); + const physx::PxU32 bufferSize = 10; + physx::PxSphereGeometry worldSphere(radius); + physx::PxTransform pose(nxPos); + physx::PxOverlapBufferN buffer; + + if(!mScene->overlap(worldSphere,pose,buffer)) + return; + + for ( physx::PxU32 i = 0; i < buffer.nbTouches; i++ ) + { + physx::PxRigidActor *actor = buffer.touches[i].actor; + + bool dynamic = actor->isRigidDynamic(); + + if ( !dynamic ) + continue; + + bool kinematic = actor->is()->getRigidDynamicFlags() & physx::PxRigidDynamicFlag::eKINEMATIC; + + if ( kinematic ) + continue; + + physx::PxVec3 force = actor->getGlobalPose().p - nxPos; + force.normalize(); + force *= forceMagnitude; + + physx::PxRigidBody *body = actor->is(); + physx::PxRigidBodyExt::addForceAtPos(*body,force,nxPos,physx::PxForceMode::eIMPULSE); + } +} + +void Px3World::setEnabled( bool enabled ) +{ + mIsEnabled = enabled; + + if ( !mIsEnabled ) + getPhysicsResults(); +} + +physx::PxController* Px3World::createController( physx::PxControllerDesc &desc ) +{ + if ( !mScene ) + return NULL; + + // We need the writelock! + releaseWriteLock(); + physx::PxController* pController = mControllerManager->createController(desc); + AssertFatal( pController, "Px3World::createController - Got a null!" ); + return pController; +} + +static ColorI getDebugColor( physx::PxU32 packed ) +{ + ColorI col; + col.blue = (packed)&0xff; + col.green = (packed>>8)&0xff; + col.red = (packed>>16)&0xff; + col.alpha = 255; + + return col; +} + +void Px3World::onDebugDraw( const SceneRenderState *state ) +{ + if ( !mScene ) + return; + + mScene->setVisualizationParameter(physx::PxVisualizationParameter::eSCALE,1.0f); + mScene->setVisualizationParameter(physx::PxVisualizationParameter::eBODY_AXES,1.0f); + mScene->setVisualizationParameter(physx::PxVisualizationParameter::eCOLLISION_SHAPES,1.0f); + + const physx::PxRenderBuffer *renderBuffer = &mScene->getRenderBuffer(); + + if(!renderBuffer) + return; + + // Render points + { + physx::PxU32 numPoints = renderBuffer->getNbPoints(); + const physx::PxDebugPoint *points = renderBuffer->getPoints(); + + PrimBuild::begin( GFXPointList, numPoints ); + + while ( numPoints-- ) + { + PrimBuild::color( getDebugColor(points->color) ); + PrimBuild::vertex3fv(px3Cast(points->pos)); + points++; + } + + PrimBuild::end(); + } + + // Render lines + { + physx::PxU32 numLines = renderBuffer->getNbLines(); + const physx::PxDebugLine *lines = renderBuffer->getLines(); + + PrimBuild::begin( GFXLineList, numLines * 2 ); + + while ( numLines-- ) + { + PrimBuild::color( getDebugColor( lines->color0 ) ); + PrimBuild::vertex3fv( px3Cast(lines->pos0)); + PrimBuild::color( getDebugColor( lines->color1 ) ); + PrimBuild::vertex3fv( px3Cast(lines->pos1)); + lines++; + } + + PrimBuild::end(); + } + + // Render triangles + { + physx::PxU32 numTris = renderBuffer->getNbTriangles(); + const physx::PxDebugTriangle *triangles = renderBuffer->getTriangles(); + + PrimBuild::begin( GFXTriangleList, numTris * 3 ); + + while ( numTris-- ) + { + PrimBuild::color( getDebugColor( triangles->color0 ) ); + PrimBuild::vertex3fv( px3Cast(triangles->pos0) ); + PrimBuild::color( getDebugColor( triangles->color1 ) ); + PrimBuild::vertex3fv( px3Cast(triangles->pos1)); + PrimBuild::color( getDebugColor( triangles->color2 ) ); + PrimBuild::vertex3fv( px3Cast(triangles->pos2) ); + triangles++; + } + + PrimBuild::end(); + } + +} + +//set simulation timing via script +DefineEngineFunction( physx3SetSimulationTiming, void, ( F32 stepTime, U32 maxSteps ),, "Set simulation timing of the PhysX 3 plugin" ) +{ + Px3World::setTiming(stepTime,maxSteps); +} diff --git a/Engine/source/T3D/physics/physx3/px3World.h b/Engine/source/T3D/physics/physx3/px3World.h new file mode 100644 index 000000000..faf51a41a --- /dev/null +++ b/Engine/source/T3D/physics/physx3/px3World.h @@ -0,0 +1,110 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _PHYSX3_WORLD_H_ +#define _PHYSX3_WORLD_H_ + +#ifndef _T3D_PHYSICS_PHYSICSWORLD_H_ +#include "T3D/physics/physicsWorld.h" +#endif +#ifndef _MMATH_H_ +#include "math/mMath.h" +#endif +#ifndef _PHYSX3_H_ +#include "T3D/physics/physx3/px3.h" +#endif +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif + +class Px3ConsoleStream; +class Px3ContactReporter; +class FixedStepper; + +enum Px3CollisionGroup +{ + PX3_DEFAULT = BIT(0), + PX3_PLAYER = BIT(1), + PX3_DEBRIS = BIT(2), + PX3_TRIGGER = BIT(3), +}; + +class Px3World : public PhysicsWorld +{ +protected: + + physx::PxScene* mScene; + + bool mIsEnabled; + bool mIsSimulating; + bool mIsServer; + U32 mTickCount; + ProcessList *mProcessList; + F32 mEditorTimeScale; + bool mErrorReport; + physx::PxControllerManager* mControllerManager; + static Px3ConsoleStream *smErrorCallback; + static physx::PxDefaultAllocator smMemoryAlloc; + static physx::PxFoundation* smFoundation; + static physx::PxCooking *smCooking; + static physx::PxProfileZoneManager* smProfileZoneManager; + static physx::PxDefaultCpuDispatcher* smCpuDispatcher; + static physx::PxVisualDebuggerConnection* smPvdConnection; + static F32 smPhysicsStepTime; + static U32 smPhysicsMaxIterations; + + F32 mAccumulator; + bool _simulate(const F32 dt); + +public: + + Px3World(); + virtual ~Px3World(); + + virtual bool initWorld( bool isServer, ProcessList *processList ); + virtual void destroyWorld(); + virtual void onDebugDraw( const SceneRenderState *state ); + virtual void reset() {} + virtual bool castRay( const Point3F &startPnt, const Point3F &endPnt, RayInfo *ri, const Point3F &impulse ); + virtual PhysicsBody* castRay( const Point3F &start, const Point3F &end, U32 bodyTypes = BT_All ); + virtual void explosion( const Point3F &pos, F32 radius, F32 forceMagnitude ); + virtual bool isEnabled() const { return mIsEnabled; } + physx::PxScene* getScene(){ return mScene;} + void setEnabled( bool enabled ); + U32 getTick() { return mTickCount; } + void tickPhysics( U32 elapsedMs ); + void getPhysicsResults(); + void setEditorTimeScale( F32 timeScale ) { mEditorTimeScale = timeScale; } + const F32 getEditorTimeScale() const { return mEditorTimeScale; } + void releaseWriteLock(); + bool isServer(){return mIsServer;} + physx::PxController* createController( physx::PxControllerDesc &desc ); + //static + static bool restartSDK( bool destroyOnly = false, Px3World *clientWorld = NULL, Px3World *serverWorld = NULL ); + static void releaseWriteLocks(); + static physx::PxCooking *getCooking(); + static void setTiming(F32 stepTime,U32 maxIterations); +}; + + + +#endif \ No newline at end of file diff --git a/Tools/projectGenerator/modules/physX3.inc b/Tools/projectGenerator/modules/physX3.inc new file mode 100644 index 000000000..42eb48b5a --- /dev/null +++ b/Tools/projectGenerator/modules/physX3.inc @@ -0,0 +1,106 @@ + From 5303fa4973be374b0bc1711dfe613544a2a44810 Mon Sep 17 00:00:00 2001 From: rextimmy Date: Fri, 28 Feb 2014 12:19:02 +1000 Subject: [PATCH 008/317] Readme update. --- README.md | 177 +++++++++++++++--------------------------------------- 1 file changed, 48 insertions(+), 129 deletions(-) diff --git a/README.md b/README.md index 016232d4f..c4b6649e9 100644 --- a/README.md +++ b/README.md @@ -1,138 +1,57 @@ -Torque 3D v3.5 -============== +Torque 3D v3.5 - PhysX 3.3 Basic Plugin +========================== -MIT Licensed Open Source version of [Torque 3D](http://www.garagegames.com/products/torque-3d) from [GarageGames](http://www.garagegames.com) +This is a basic PhysX 3.3 plugin that does not contain any added features like cloth,particles and CCD. This plugin provides no more features than the bullet plugin and can therefore be used as a direct alternative. It does not modify any files outside of the physx3 folder. A far more advanced physx3 plugin with CCD, cloth and particles can be found on this repository under the physx branch. -More Information ----------------- +Setting up PhysX 3.3 using the Torque 3D Project Manager +------------------------------------------ + - You can find a pre compiled binary of the Torque3D Project Manager that supports PhysX 3.3 here: http://www.narivtech.com/downloads/T3DProjectManager-2-1-devel.zip and source code here: https://github.com/rextimmy/Torque3D-ProjectManager/tree/development + - For the Project Manager to find PhysX 3.3 SDK you have two options 1)Create an environment variable called TORQUE_PHYSX3_PATH and have that pointing to the location you installed the SDK 2)Place the SDK into a folder called "Program Files"/NVIDIA Corporation/NVIDIA PhysX SDK/v3.3.0_win + - Simply choose PhysX 3.3 physics from the modules list in the project manager and everything should be automatically taken care of. -* Torque 3D [main repository](https://github.com/GarageGames/Torque3D) -* Torque 3D [GitHub Wiki](https://github.com/GarageGames/Torque3D/wiki) -* Documentation is in the [Torque3D-Documentation](https://github.com/GarageGames/Torque3D-Documentation) GitHub repo. -* Project Manager is in the [Torque3D-ProjectManager](https://github.com/GarageGames/Torque3D-ProjectManager) GitHub repo. -* T3D [Beginner's Forum](http://www.garagegames.com/community/forums/73) -* T3D [Professional Forum](http://www.garagegames.com/community/forums/63) -* Torque 3D [FPS Tutorial](http://www.garagegames.com/products/torque-3d/fps#/1-setup/1) -* GarageGames [Store](http://www.garagegames.com/products) -* GarageGames [Professional Services](http://services.garagegames.com/) - -Pre-compiled Version --------------------- - -In addition to GitHub we also have a couple of pre-packaged files for you to download if you would prefer to not compile the code yourself: - -* [Complete Torque 3D 3.0 zip package](http://mit.garagegames.com/Torque3D-3-0.zip) with updated TorqueScript documentation, the *Project Manager*, and compiled versions of the templates. -* [Torque 3D Project Manager v2.0](http://mit.garagegames.com/T3DProjectManager-2-0.zip) on its own for use in your T3D forks. - -If you're looking for an older release see the [Torque 3D Archive](https://github.com/GarageGames/Torque3D/wiki/Torque-3D-Archive) - -Creating a New Project Based on a Template +Setting up PhysX 3.3 manually ------------------------------------------ -The templates included with Torque 3D provide a starting point for your project. Once we have created our own project based on a template we may then compile an executable and begin work on our game. The following templates are included in this version of Torque 3D: + - You will need the latest SDK from NVIDIA. This requires signing up for their developer program. If you don't already have access to their developer site then sign up now as access is not immediate. + - Set up a standard Torque3D project, don't include any PhysX or Bullet, just regular Torque Physics in project manager options (if you're using it) + - Generate Projects and open the source code in Visual Studio ( or the IDE of your choice ) + - In the solution explorer in the DLL for your project you should find Source Files -> Engine -> T3D -> physics + - Add a new filter "physx3" and then right click on it and add existing item + - Add all the files found under Engine\Source\T3D\physics\physx3\ + - Now you need to add the PhysX SDK. + - Under the properties for the DLL project, under Linker -> Additional Library Directories add the lib\win32 directory for the PhysX 3.3 SDK. For example, mine is in: C:\Program Files (x86)\NVIDIA Corporation\NVIDIA PhysX SDK\v3.3.0_win\Lib\win32 + - In the same window under C/C++ you should see Additional Include Directories, you need to add the Include directory for the PhysX 3.3 SDK. For example, mine is in: C:\Program Files %28x86%29\NVIDIA Corporation\NVIDIA PhysX SDK\v3.3.0_win\Include + - You should now be able to compile now without any issues. -* Empty -* Full +The following libraries will also be needed: + +Release , Debug -### Using PhysX ### + - PhysX3_x86.lib,PhysX3CHECKED_x86.lib + - PhysX3Common_x86.lib,PhysX3CommonCHECKED_x86.lib + - PhysX3Extensions.lib,PhysX3ExtensionsCHECKED.lib + - PhysX3Cooking_x86.lib,PhysX3CookingCHECKED_x86.lib + - PxTask.lib,PxTaskCHECKED.lib + - PhysX3CharacterKinematic_x86.lib,PhysX3CharacterKinematicCHECKED_x86.lib + - PhysXVisualDebuggerSDK.lib, PhysXVisualDebuggerSDKCHECKED.lib + - PhysXProfileSDK.lib, PhysXProfileSDKCHECKED.lib -If you plan on creating a project that uses PhysX you will first need to have the PhysX SDK intalled on your computer. Without the PhysX SDK in place the project generation step will fail when using either the *Project Manager* or manual project generation methods. +With debug build feel free to change CHECKED to DEBUG if you prefer but it will still require the CHECKED dll's though. + +Running a project +------------------------------------------ -PhysX SDK version 2.8.4.6 is required for Torque 3D's PhysX templates. The following steps are used to install this SDK: - -1. In a web browser, go to the [NVidia Support Center](http://supportcenteronline.com/ics/support/default.asp?deptID=1949) -2. If you do not have an account, you will need to register with them to have the support staff create an account for you. -3. If you have an account, login. -4. On the middle of the page, on the right, click on Downloads. -5. On the far right column, under Old downloads, click More. -6. Download the Windows 2.8.4.6 version. -7. Run the installer and follow the steps to install it in the default location. -8. Depending on your operating system version, you may need to reboot after the installation. - -### Using the Project Manager to Create a Project ### - -The *Project Manager* may be used to create a new game project based on one of the templates that are included with Torque 3D. If you are using Torque 3D directly from the [GitHub](https://github.com/GarageGames/Torque3D) repository then you will need to get the *Project Manager* from the [Torque3D-ProjectManager](https://github.com/GarageGames/Torque3D-ProjectManager) repo, or download a pre-compiled version from the [Project Manager Wiki Page](https://github.com/GarageGames/Torque3D/wiki/Project-Manager). - -The following steps use the *Project Manager* to create a new project. This is a quick summary and more detailed instructions may be found on the [Project Manager Wiki Page](https://github.com/GarageGames/Torque3D/wiki/Project-Manager) - -1. Run the *Project Manager*. -2. Click the *New Project* button. -3. Choose a template from the drop down on the right. -4. Pick which modules you want to compile into your project by using the *Choose Modules* button. -5. Give the project a name. -6. Click the *Create* button to create the project. This will open a new dialog window that shows the progress. -7. When it finishes, click the *Finished* button. -8. You may click on the *Open Folder* button to go to the project's directory. - -### Manually Creating a Project ### - -We may also manually create a project based on a template. The following steps outline how to do this: - -1. Open the *Templates* directory. -2. Right-click on the template you would like to use and choose *Copy*. -3. Go to the *My Projects* directory and paste the template there. -4. Rename the pasted template to the name of your project/game. -5. Go into your project's *game* directory and rename all executables, DLL files and the .torsion file (and maybe .torsion.opt) from the template name to that of your project (these files may not be present at this time). -6. Open the .torsion file in a text editor and replace all references to the template's name with that of your project (you only need to do this if you plan on using Torsion). You will need to also do this with the .torsion.opt if it exists. -7. Open you project's *source/torqueConfig.h* file in a text editor and change the `TORQUE_APP_NAME` define to the name of your project. -8. In your project's *buildFiles/config* directory open each .conf file and find each reference to the template's name and replace it with the name of your project. -9. Open your project's *game/main.cs* file in a text editor and change the `$appName` assignment to the name of your project. -10. Go to your project's directory and double click on the *generateProjects.bat* to create your project's solution files. - -Compiling Torque 3D (Windows) ------------------------------ -If this is the first time you will compile Torque 3D, or if you have added or removed any files to either the standard Torque 3D *Engine/source* directory or your project's *source* directory, you will need to run your project's *generateProjects.bat* file. This will rebuild your project's solution and project files. Now follow these steps to compile Torque 3D: - -1. Navigate to your project's *buildFiles/VisualStudio 2010* directory (or the *2008* directory if that is the version of Visual Studio you are using). -2. Double click on your project's .sln file. This will open Visual Studio. -3. When Visual Studio has fully loaded, press `F7` to start compiling your project. - -Compiling Torque 3D (Linux) ------------------------------ -This version of Torque 3D supports being run as a dedicated server under Linux. As with a Windows build you will need to run your project's *generateProjects.command* file to properly generate the required make file. - -Prior to compiling Torque 3D under Linux, you will need to make sure you have the appropriate libraries and tools installed. The exact packages will depend on which Linux distribution you are using. For example, under Ubuntu you will need: - -* build-essential -* nasm -* git -* php5-cli -* libsdl-dev -* libogg-dev - -With everything in place you may now follow these steps to compile Torque 3D: - -1. Change to you project's *buildFiles/Make_Ded* directory. -2. Enter the `make clean` command. -3. Enter the either the `make debug` or `make release` command depending on the type of build you wish to make. -4. Go to your project's *game* directory. -5. To start your game enter the following command (we'll use the name *MyGame* as the example project name): - `./MyGame -dedicated -mission "levels/Empty Terrain.mis"` - where the argument after the `-mission` switch is the path to the mission to load. - -Accessing the Editors ---------------------- -From the Main Menu or while in-game, pressing F10 opens the GUI Editor and pressing F11 opens the World Editor. - -License -------- - -Copyright (c) 2012 GarageGames, LLC - -Permission is hereby granted, free of charge, to any person obtaining a copy -of this software and associated documentation files (the "Software"), to -deal in the Software without restriction, including without limitation the -rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -sell copies of the Software, and to permit persons to whom the Software is -furnished to do so, subject to the following conditions: - -The above copyright notice and this permission notice shall be included in -all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -IN THE SOFTWARE. + - To run a release project you will need the following from the SDK bin folder: + 1. PhysX3_x86.dll + 2. PhysX3CharacterKinematic_x86.dll + 3. PhysX3Common_x86.dll + 4. PhysX3Cooking_x86.dll + + - To run a debug project you will need the following from the SDK bin folder: + 1. PhysX3CHECKED_x86.dll + 2. nvToolsExt32_1.dll + 3. PhysX3CookingCHECKED_x86.dll + 4. PhysX3CommonCHECKED_x86.dll + 5. PhysX3CharacterKinematicCHECKED_x86.dll + +Place these files along side the exe and this should get you up and running. From c187c92edd152d272db994d7e1b2f1e6cd34dace Mon Sep 17 00:00:00 2001 From: rextimmy Date: Fri, 28 Feb 2014 12:21:10 +1000 Subject: [PATCH 009/317] Readme update #2 --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index c4b6649e9..93e4b09b4 100644 --- a/README.md +++ b/README.md @@ -1,7 +1,7 @@ Torque 3D v3.5 - PhysX 3.3 Basic Plugin ========================== -This is a basic PhysX 3.3 plugin that does not contain any added features like cloth,particles and CCD. This plugin provides no more features than the bullet plugin and can therefore be used as a direct alternative. It does not modify any files outside of the physx3 folder. A far more advanced physx3 plugin with CCD, cloth and particles can be found on this repository under the physx branch. +This is a basic PhysX 3.3 plugin that does not contain any added features like cloth,particles and CCD. This plugin provides no more features than the bullet plugin and can therefore be used as a drop in alternative. It does not modify any files outside of the physx3 folder. A far more advanced physx3 plugin with CCD, cloth and particles can be found on this repository under the physx3 branch. Setting up PhysX 3.3 using the Torque 3D Project Manager ------------------------------------------ From 33742599b380e052ca32b6c0d6bd79aa6ab965c0 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 13 Apr 2014 17:55:02 +0200 Subject: [PATCH 010/317] Use GFXDevice::setupGenericShaders for support non Fixed Fuction Pipelines. OpenGL and DirectX11 not support FFP, and GFDevice::disableShaders has not the necessary information to decide the shader to be used. GFDevice::SetupGenericShaders is used instead of GFDevice::disableShaders. GFDevice::disableShaders will be deprecated on T3D 4.0 --- Engine/source/T3D/cameraSpline.cpp | 2 +- Engine/source/T3D/fx/precipitation.cpp | 4 ++-- Engine/source/environment/waterObject.cpp | 2 +- Engine/source/gfx/gfxDevice.h | 2 +- Engine/source/gfx/gfxFontRenderBatcher.cpp | 5 ++--- Engine/source/gfx/sim/debugDraw.cpp | 3 +-- Engine/source/gui/worldEditor/guiTerrPreviewCtrl.cpp | 2 +- Engine/source/lighting/common/blobShadow.cpp | 2 +- Engine/source/materials/processedCustomMaterial.cpp | 2 +- Engine/source/materials/processedShaderMaterial.cpp | 2 +- Engine/source/postFx/postEffect.cpp | 2 +- Engine/source/renderInstance/renderPassManager.cpp | 2 +- 12 files changed, 14 insertions(+), 16 deletions(-) diff --git a/Engine/source/T3D/cameraSpline.cpp b/Engine/source/T3D/cameraSpline.cpp index 94d0055f5..10e777868 100644 --- a/Engine/source/T3D/cameraSpline.cpp +++ b/Engine/source/T3D/cameraSpline.cpp @@ -214,7 +214,7 @@ void CameraSpline::renderTimeMap() // Render the buffer GFX->pushWorldMatrix(); - GFX->disableShaders(); + GFX->setupGenericShaders(); GFX->setVertexBuffer(vb); GFX->drawPrimitive(GFXLineStrip,0,index); GFX->popWorldMatrix(); diff --git a/Engine/source/T3D/fx/precipitation.cpp b/Engine/source/T3D/fx/precipitation.cpp index 9205238e0..10d619931 100644 --- a/Engine/source/T3D/fx/precipitation.cpp +++ b/Engine/source/T3D/fx/precipitation.cpp @@ -1668,7 +1668,7 @@ void Precipitation::renderObject(ObjectRenderInst *ri, SceneRenderState *state, } else { - GFX->disableShaders(); + GFX->setupGenericShaders(GFXDevice::GSTexture); // We don't support distance fade or lighting without shaders. GFX->setStateBlock(mDistantSB); @@ -1801,7 +1801,7 @@ void Precipitation::renderObject(ObjectRenderInst *ri, SceneRenderState *state, GFX->setShaderConstBuffer(mSplashShaderConsts); } else - GFX->disableShaders(); + GFX->setupGenericShaders(GFXDevice::GSTexture); while (curr) { diff --git a/Engine/source/environment/waterObject.cpp b/Engine/source/environment/waterObject.cpp index 51e737717..bdc92632d 100644 --- a/Engine/source/environment/waterObject.cpp +++ b/Engine/source/environment/waterObject.cpp @@ -775,7 +775,7 @@ void WaterObject::drawUnderwaterFilter( SceneRenderState *state ) GFX->setWorldMatrix( newMat ); // set up render states - GFX->disableShaders(); + GFX->setupGenericShaders(); GFX->setStateBlock( mUnderwaterSB ); /* diff --git a/Engine/source/gfx/gfxDevice.h b/Engine/source/gfx/gfxDevice.h index 44a889f66..d83acfa76 100644 --- a/Engine/source/gfx/gfxDevice.h +++ b/Engine/source/gfx/gfxDevice.h @@ -720,7 +720,7 @@ public: virtual U32 getNumRenderTargets() const = 0; virtual void setShader( GFXShader *shader ) {} - virtual void disableShaders() {} + virtual void disableShaders() {} // TODO Remove when T3D 4.0 /// Set the buffer! (Actual set happens on the next draw call, just like textures, state blocks, etc) void setShaderConstBuffer(GFXShaderConstBuffer* buffer); diff --git a/Engine/source/gfx/gfxFontRenderBatcher.cpp b/Engine/source/gfx/gfxFontRenderBatcher.cpp index e782268d0..a9761e7fc 100644 --- a/Engine/source/gfx/gfxFontRenderBatcher.cpp +++ b/Engine/source/gfx/gfxFontRenderBatcher.cpp @@ -60,7 +60,6 @@ void FontRenderBatcher::render( F32 rot, const Point2F &offset ) return; GFX->setStateBlock(mFontSB); - GFX->disableShaders(); for(U32 i = 0; i < GFX->getNumSamplers(); i++) GFX->setTexture(i, NULL); @@ -177,6 +176,7 @@ void FontRenderBatcher::render( F32 rot, const Point2F &offset ) AssertFatal(currentPt <= mLength * 6, "FontRenderBatcher::render - too many verts for length of string!"); GFX->setVertexBuffer(verts); + GFX->setupGenericShaders( GFXDevice::GSAddColorTexture ); // Now do an optimal render! for( S32 i = 0; i < mSheets.size(); i++ ) @@ -186,8 +186,7 @@ void FontRenderBatcher::render( F32 rot, const Point2F &offset ) if(!mSheets[i]->numChars ) continue; - - GFX->setupGenericShaders( GFXDevice::GSAddColorTexture ); + GFX->setTexture( 0, mFont->getTextureHandle(i) ); GFX->drawPrimitive(GFXTriangleList, mSheets[i]->startVertex, mSheets[i]->numChars * 2); } diff --git a/Engine/source/gfx/sim/debugDraw.cpp b/Engine/source/gfx/sim/debugDraw.cpp index 2dfe00141..68b8c835d 100644 --- a/Engine/source/gfx/sim/debugDraw.cpp +++ b/Engine/source/gfx/sim/debugDraw.cpp @@ -150,11 +150,10 @@ void DebugDrawer::render() } SimTime curTime = Sim::getCurrentTime(); - - GFX->disableShaders(); for(DebugPrim **walk = &mHead; *walk; ) { + GFX->setupGenericShaders(); DebugPrim *p = *walk; // Set up the state block... diff --git a/Engine/source/gui/worldEditor/guiTerrPreviewCtrl.cpp b/Engine/source/gui/worldEditor/guiTerrPreviewCtrl.cpp index 3477d974e..28462fec6 100644 --- a/Engine/source/gui/worldEditor/guiTerrPreviewCtrl.cpp +++ b/Engine/source/gui/worldEditor/guiTerrPreviewCtrl.cpp @@ -245,7 +245,7 @@ void GuiTerrPreviewCtrl::onRender(Point2I offset, const RectI &updateRect) for(U32 i = 0; i < GFX->getNumSamplers(); i++) GFX->setTexture(i, NULL); - GFX->disableShaders(); + GFX->setupGenericShaders(GFXDevice::GSModColorTexture); Point2F terrPos(terrBlock->getPosition().x, terrBlock->getPosition().y); diff --git a/Engine/source/lighting/common/blobShadow.cpp b/Engine/source/lighting/common/blobShadow.cpp index 0dfce8b15..9756757e9 100644 --- a/Engine/source/lighting/common/blobShadow.cpp +++ b/Engine/source/lighting/common/blobShadow.cpp @@ -331,7 +331,7 @@ void BlobShadow::render( F32 camDist, const TSRenderState &rdata ) world.mul(mLightToWorld); GFX->setWorldMatrix(world); - GFX->disableShaders(); + GFX->setupGenericShaders(GFXDevice::GSModColorTexture); GFX->setStateBlock(mShadowSB); GFX->setTexture(0, smGenericShadowTexture); diff --git a/Engine/source/materials/processedCustomMaterial.cpp b/Engine/source/materials/processedCustomMaterial.cpp index 513ead314..2c26b14da 100644 --- a/Engine/source/materials/processedCustomMaterial.cpp +++ b/Engine/source/materials/processedCustomMaterial.cpp @@ -273,7 +273,7 @@ bool ProcessedCustomMaterial::setupPass( SceneRenderState *state, const SceneDat if ( rpd->shader ) GFX->setShader( rpd->shader ); else - GFX->disableShaders(); + GFX->setupGenericShaders(); // Set our textures setTextureStages( state, sgData, pass ); diff --git a/Engine/source/materials/processedShaderMaterial.cpp b/Engine/source/materials/processedShaderMaterial.cpp index 01741f496..bbf9f0f18 100644 --- a/Engine/source/materials/processedShaderMaterial.cpp +++ b/Engine/source/materials/processedShaderMaterial.cpp @@ -688,7 +688,7 @@ bool ProcessedShaderMaterial::setupPass( SceneRenderState *state, const SceneDat } else { - GFX->disableShaders(); + GFX->setupGenericShaders(); GFX->setShaderConstBuffer(NULL); } diff --git a/Engine/source/postFx/postEffect.cpp b/Engine/source/postFx/postEffect.cpp index aff1e6f2c..6ee7ccbb0 100644 --- a/Engine/source/postFx/postEffect.cpp +++ b/Engine/source/postFx/postEffect.cpp @@ -1215,7 +1215,7 @@ void PostEffect::process( const SceneRenderState *state, GFX->setShaderConstBuffer( mShaderConsts ); } else - GFX->disableShaders(); + GFX->setupGenericShaders(); Frustum frustum; if ( state ) diff --git a/Engine/source/renderInstance/renderPassManager.cpp b/Engine/source/renderInstance/renderPassManager.cpp index 33e74839e..236cccd9b 100644 --- a/Engine/source/renderInstance/renderPassManager.cpp +++ b/Engine/source/renderInstance/renderPassManager.cpp @@ -255,7 +255,7 @@ void RenderPassManager::render(SceneRenderState * state) GFX->setProjectionMatrix( proj ); // Restore a clean state for subsequent rendering. - GFX->disableShaders(); + GFX->setupGenericShaders(); for(S32 i = 0; i < GFX->getNumSamplers(); ++i) GFX->setTexture(i, NULL); } From a8462b7fc3dd7f5a0a56d137c20613e2eb026197 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 13 Apr 2014 19:30:35 +0200 Subject: [PATCH 011/317] Case-sensitive fixes on template script files for Linux. --- .../Empty/game/core/scripts/gui/messageBoxes/messageBox.ed.cs | 4 ++-- Templates/Empty/game/tools/gui/profiles.ed.cs | 2 +- Templates/Empty/game/tools/riverEditor/main.cs | 4 ++-- Templates/Empty/game/tools/roadEditor/main.cs | 4 ++-- Templates/Empty/game/tools/shapeEditor/gui/Profiles.ed.cs | 2 +- Templates/Empty/game/tools/shapeEditor/main.cs | 2 +- Templates/Empty/game/tools/worldEditor/main.cs | 2 +- .../Full/game/core/scripts/gui/messageBoxes/messageBox.ed.cs | 4 ++-- Templates/Full/game/scripts/client/init.cs | 4 ++-- Templates/Full/game/tools/gui/profiles.ed.cs | 2 +- Templates/Full/game/tools/riverEditor/main.cs | 4 ++-- Templates/Full/game/tools/roadEditor/main.cs | 4 ++-- Templates/Full/game/tools/shapeEditor/gui/Profiles.ed.cs | 2 +- Templates/Full/game/tools/shapeEditor/main.cs | 2 +- Templates/Full/game/tools/worldEditor/main.cs | 2 +- 15 files changed, 22 insertions(+), 22 deletions(-) diff --git a/Templates/Empty/game/core/scripts/gui/messageBoxes/messageBox.ed.cs b/Templates/Empty/game/core/scripts/gui/messageBoxes/messageBox.ed.cs index d778ae7ee..7c970c341 100644 --- a/Templates/Empty/game/core/scripts/gui/messageBoxes/messageBox.ed.cs +++ b/Templates/Empty/game/core/scripts/gui/messageBoxes/messageBox.ed.cs @@ -42,8 +42,8 @@ if( isObject( IODropdownDlg ) ) exec("./messageBoxOk.ed.gui"); exec("./messageBoxYesNo.ed.gui"); exec("./messageBoxYesNoCancel.ed.gui"); -exec("./messageBoxOKCancel.ed.gui"); -exec("./messageBoxOKCancelDetailsDlg.ed.gui"); +exec("./messageBoxOkCancel.ed.gui"); +exec("./MessageBoxOKCancelDetailsDlg.ed.gui"); exec("./messagePopup.ed.gui"); exec("./IODropdownDlg.ed.gui"); diff --git a/Templates/Empty/game/tools/gui/profiles.ed.cs b/Templates/Empty/game/tools/gui/profiles.ed.cs index 8d6718601..b3a5b2295 100644 --- a/Templates/Empty/game/tools/gui/profiles.ed.cs +++ b/Templates/Empty/game/tools/gui/profiles.ed.cs @@ -764,7 +764,7 @@ singleton GuiControlProfile( GuiInspectorGroupProfile ) opaque = false; border = false; - bitmap = "tools/editorclasses/gui/images/rollout"; + bitmap = "tools/editorClasses/gui/images/rollout"; textOffset = "20 0"; diff --git a/Templates/Empty/game/tools/riverEditor/main.cs b/Templates/Empty/game/tools/riverEditor/main.cs index b51bd5273..eafb3c3c8 100644 --- a/Templates/Empty/game/tools/riverEditor/main.cs +++ b/Templates/Empty/game/tools/riverEditor/main.cs @@ -25,8 +25,8 @@ function initializeRiverEditor() echo(" % - Initializing River Editor"); exec( "./riverEditor.cs" ); - exec( "./riverEditorGui.gui" ); - exec( "./riverEditorToolbar.gui" ); + exec( "./RiverEditorGui.gui" ); + exec( "./RiverEditorToolbar.gui" ); exec( "./riverEditorGui.cs" ); // Add ourselves to EditorGui, where all the other tools reside diff --git a/Templates/Empty/game/tools/roadEditor/main.cs b/Templates/Empty/game/tools/roadEditor/main.cs index cb7ea052c..f45823670 100644 --- a/Templates/Empty/game/tools/roadEditor/main.cs +++ b/Templates/Empty/game/tools/roadEditor/main.cs @@ -25,8 +25,8 @@ function initializeRoadEditor() echo( " - Initializing Road and Path Editor" ); exec( "./roadEditor.cs" ); - exec( "./roadEditorGui.gui" ); - exec( "./roadEditorToolbar.gui"); + exec( "./RoadEditorGui.gui" ); + exec( "./RoadEditorToolbar.gui"); exec( "./roadEditorGui.cs" ); // Add ourselves to EditorGui, where all the other tools reside diff --git a/Templates/Empty/game/tools/shapeEditor/gui/Profiles.ed.cs b/Templates/Empty/game/tools/shapeEditor/gui/Profiles.ed.cs index 0b92a30e2..cb42ef169 100644 --- a/Templates/Empty/game/tools/shapeEditor/gui/Profiles.ed.cs +++ b/Templates/Empty/game/tools/shapeEditor/gui/Profiles.ed.cs @@ -41,7 +41,7 @@ singleton GuiControlProfile(GuiShapeEdTextListProfile : ToolsGuiTextListProfile) singleton GuiControlProfile(GuiShapeEdRolloutProfile : GuiInspectorRolloutProfile0) { - bitmap = "tools/editorclasses/gui/images/rollout"; + bitmap = "tools/editorClasses/gui/images/rollout"; category = "Editor"; }; diff --git a/Templates/Empty/game/tools/shapeEditor/main.cs b/Templates/Empty/game/tools/shapeEditor/main.cs index f033bae86..721313e95 100644 --- a/Templates/Empty/game/tools/shapeEditor/main.cs +++ b/Templates/Empty/game/tools/shapeEditor/main.cs @@ -33,7 +33,7 @@ function initializeShapeEditor() exec("./gui/shapeEdPreviewWindow.ed.gui"); exec("./gui/shapeEdAnimWindow.ed.gui"); exec("./gui/shapeEdAdvancedWindow.ed.gui"); - exec("./gui/shapeEditorToolbar.ed.gui"); + exec("./gui/ShapeEditorToolbar.ed.gui"); exec("./gui/shapeEdSelectWindow.ed.gui"); exec("./gui/shapeEdPropWindow.ed.gui"); diff --git a/Templates/Empty/game/tools/worldEditor/main.cs b/Templates/Empty/game/tools/worldEditor/main.cs index 1b20201e0..773dbac17 100644 --- a/Templates/Empty/game/tools/worldEditor/main.cs +++ b/Templates/Empty/game/tools/worldEditor/main.cs @@ -29,7 +29,7 @@ function initializeWorldEditor() exec("./scripts/cursors.ed.cs"); exec("./gui/guiCreateNewTerrainGui.gui" ); - exec("./gui/genericPromptDialog.ed.gui" ); + exec("./gui/GenericPromptDialog.ed.gui" ); exec("./gui/guiTerrainImportGui.gui" ); exec("./gui/guiTerrainExportGui.gui" ); exec("./gui/EditorGui.ed.gui"); diff --git a/Templates/Full/game/core/scripts/gui/messageBoxes/messageBox.ed.cs b/Templates/Full/game/core/scripts/gui/messageBoxes/messageBox.ed.cs index d778ae7ee..7c970c341 100644 --- a/Templates/Full/game/core/scripts/gui/messageBoxes/messageBox.ed.cs +++ b/Templates/Full/game/core/scripts/gui/messageBoxes/messageBox.ed.cs @@ -42,8 +42,8 @@ if( isObject( IODropdownDlg ) ) exec("./messageBoxOk.ed.gui"); exec("./messageBoxYesNo.ed.gui"); exec("./messageBoxYesNoCancel.ed.gui"); -exec("./messageBoxOKCancel.ed.gui"); -exec("./messageBoxOKCancelDetailsDlg.ed.gui"); +exec("./messageBoxOkCancel.ed.gui"); +exec("./MessageBoxOKCancelDetailsDlg.ed.gui"); exec("./messagePopup.ed.gui"); exec("./IODropdownDlg.ed.gui"); diff --git a/Templates/Full/game/scripts/client/init.cs b/Templates/Full/game/scripts/client/init.cs index 3f7f2d96a..56da1c945 100644 --- a/Templates/Full/game/scripts/client/init.cs +++ b/Templates/Full/game/scripts/client/init.cs @@ -73,8 +73,8 @@ function initClient() // Load up the Game GUIs exec("art/gui/defaultGameProfiles.cs"); - exec("art/gui/PlayGui.gui"); - exec("art/gui/ChatHud.gui"); + exec("art/gui/playGui.gui"); + exec("art/gui/chatHud.gui"); exec("art/gui/playerList.gui"); exec("art/gui/hudlessGui.gui"); diff --git a/Templates/Full/game/tools/gui/profiles.ed.cs b/Templates/Full/game/tools/gui/profiles.ed.cs index 8d6718601..b3a5b2295 100644 --- a/Templates/Full/game/tools/gui/profiles.ed.cs +++ b/Templates/Full/game/tools/gui/profiles.ed.cs @@ -764,7 +764,7 @@ singleton GuiControlProfile( GuiInspectorGroupProfile ) opaque = false; border = false; - bitmap = "tools/editorclasses/gui/images/rollout"; + bitmap = "tools/editorClasses/gui/images/rollout"; textOffset = "20 0"; diff --git a/Templates/Full/game/tools/riverEditor/main.cs b/Templates/Full/game/tools/riverEditor/main.cs index b51bd5273..eafb3c3c8 100644 --- a/Templates/Full/game/tools/riverEditor/main.cs +++ b/Templates/Full/game/tools/riverEditor/main.cs @@ -25,8 +25,8 @@ function initializeRiverEditor() echo(" % - Initializing River Editor"); exec( "./riverEditor.cs" ); - exec( "./riverEditorGui.gui" ); - exec( "./riverEditorToolbar.gui" ); + exec( "./RiverEditorGui.gui" ); + exec( "./RiverEditorToolbar.gui" ); exec( "./riverEditorGui.cs" ); // Add ourselves to EditorGui, where all the other tools reside diff --git a/Templates/Full/game/tools/roadEditor/main.cs b/Templates/Full/game/tools/roadEditor/main.cs index cb7ea052c..f45823670 100644 --- a/Templates/Full/game/tools/roadEditor/main.cs +++ b/Templates/Full/game/tools/roadEditor/main.cs @@ -25,8 +25,8 @@ function initializeRoadEditor() echo( " - Initializing Road and Path Editor" ); exec( "./roadEditor.cs" ); - exec( "./roadEditorGui.gui" ); - exec( "./roadEditorToolbar.gui"); + exec( "./RoadEditorGui.gui" ); + exec( "./RoadEditorToolbar.gui"); exec( "./roadEditorGui.cs" ); // Add ourselves to EditorGui, where all the other tools reside diff --git a/Templates/Full/game/tools/shapeEditor/gui/Profiles.ed.cs b/Templates/Full/game/tools/shapeEditor/gui/Profiles.ed.cs index 0b92a30e2..cb42ef169 100644 --- a/Templates/Full/game/tools/shapeEditor/gui/Profiles.ed.cs +++ b/Templates/Full/game/tools/shapeEditor/gui/Profiles.ed.cs @@ -41,7 +41,7 @@ singleton GuiControlProfile(GuiShapeEdTextListProfile : ToolsGuiTextListProfile) singleton GuiControlProfile(GuiShapeEdRolloutProfile : GuiInspectorRolloutProfile0) { - bitmap = "tools/editorclasses/gui/images/rollout"; + bitmap = "tools/editorClasses/gui/images/rollout"; category = "Editor"; }; diff --git a/Templates/Full/game/tools/shapeEditor/main.cs b/Templates/Full/game/tools/shapeEditor/main.cs index f033bae86..721313e95 100644 --- a/Templates/Full/game/tools/shapeEditor/main.cs +++ b/Templates/Full/game/tools/shapeEditor/main.cs @@ -33,7 +33,7 @@ function initializeShapeEditor() exec("./gui/shapeEdPreviewWindow.ed.gui"); exec("./gui/shapeEdAnimWindow.ed.gui"); exec("./gui/shapeEdAdvancedWindow.ed.gui"); - exec("./gui/shapeEditorToolbar.ed.gui"); + exec("./gui/ShapeEditorToolbar.ed.gui"); exec("./gui/shapeEdSelectWindow.ed.gui"); exec("./gui/shapeEdPropWindow.ed.gui"); diff --git a/Templates/Full/game/tools/worldEditor/main.cs b/Templates/Full/game/tools/worldEditor/main.cs index 1b20201e0..773dbac17 100644 --- a/Templates/Full/game/tools/worldEditor/main.cs +++ b/Templates/Full/game/tools/worldEditor/main.cs @@ -29,7 +29,7 @@ function initializeWorldEditor() exec("./scripts/cursors.ed.cs"); exec("./gui/guiCreateNewTerrainGui.gui" ); - exec("./gui/genericPromptDialog.ed.gui" ); + exec("./gui/GenericPromptDialog.ed.gui" ); exec("./gui/guiTerrainImportGui.gui" ); exec("./gui/guiTerrainExportGui.gui" ); exec("./gui/EditorGui.ed.gui"); From 6aea37b407392e5dcf74dae9ad6b91274ff3d8ae Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 13 Apr 2014 19:48:51 +0200 Subject: [PATCH 012/317] Changes to GLSL files for OpenGL --- .../Empty/game/shaders/common/gl/blurP.glsl | 10 +- .../Empty/game/shaders/common/gl/blurV.glsl | 11 +- .../game/shaders/common/gl/cloudLayerP.glsl | 44 ++- .../game/shaders/common/gl/cloudLayerV.glsl | 74 ++-- .../Empty/game/shaders/common/gl/foliage.glsl | 44 +-- .../common/gl/fxFoliageReplicatorP.glsl | 10 +- .../common/gl/fxFoliageReplicatorV.glsl | 34 +- .../game/shaders/common/gl/guiMaterialV.glsl | 12 +- .../game/shaders/common/gl/hlslCompat.glsl | 82 +++- .../game/shaders/common/gl/lighting.glsl | 194 +++++++--- .../shaders/common/gl/particleCompositeP.glsl | 18 +- .../shaders/common/gl/particleCompositeV.glsl | 27 +- .../game/shaders/common/gl/particlesP.glsl | 73 ++-- .../game/shaders/common/gl/particlesV.glsl | 31 +- .../shaders/common/gl/planarReflectBumpP.glsl | 12 +- .../shaders/common/gl/planarReflectBumpV.glsl | 13 +- .../shaders/common/gl/planarReflectP.glsl | 10 +- .../shaders/common/gl/planarReflectV.glsl | 11 +- .../Empty/game/shaders/common/gl/precipP.glsl | 6 +- .../Empty/game/shaders/common/gl/precipV.glsl | 14 +- .../shaders/common/gl/projectedShadowP.glsl | 23 +- .../shaders/common/gl/projectedShadowV.glsl | 27 +- .../game/shaders/common/gl/scatterSkyP.glsl | 46 ++- .../game/shaders/common/gl/scatterSkyV.glsl | 108 +++--- .../Empty/game/shaders/common/gl/torque.glsl | 68 ++-- .../Empty/game/shaders/common/gl/wavesP.glsl | 16 +- .../lighting/advanced/gl/convexGeometryV.glsl | 27 +- .../advanced/gl/dbgDepthVisualizeP.glsl | 5 +- .../advanced/gl/dbgLightColorVisualizeP.glsl | 9 +- .../gl/dbgLightSpecularVisualizeP.glsl | 9 +- .../advanced/gl/dbgNormalVisualizeP.glsl | 10 +- .../advanced/gl/dbgShadowVisualizeP.glsl | 7 +- .../lighting/advanced/gl/farFrustumQuad.glsl | 2 +- .../lighting/advanced/gl/farFrustumQuadV.glsl | 34 +- .../lighting/advanced/gl/pointLightP.glsl | 210 +++++------ .../lighting/advanced/gl/softShadow.glsl | 242 ++++++------ .../lighting/advanced/gl/spotLightP.glsl | 160 ++++---- .../lighting/advanced/gl/vectorLightP.glsl | 288 +++++++------- .../lighting/basic/gl/shadowFilterP.glsl | 34 +- .../lighting/basic/gl/shadowFilterV.glsl | 11 +- .../lighting/shadowMap/gl/boxFilterP.glsl | 6 +- .../lighting/shadowMap/gl/boxFilterV.glsl | 9 +- .../game/shaders/common/postFx/postFxV.glsl | 25 +- .../shaders/common/terrain/gl/blendP.glsl | 12 +- .../shaders/common/terrain/gl/blendV.glsl | 15 +- .../shaders/common/water/gl/waterBasicP.glsl | 97 +++-- .../shaders/common/water/gl/waterBasicV.glsl | 212 +++++------ .../game/shaders/common/water/gl/waterP.glsl | 356 ++++++++---------- .../game/shaders/common/water/gl/waterV.glsl | 228 +++++++---- .../Full/game/shaders/common/gl/blurP.glsl | 10 +- .../Full/game/shaders/common/gl/blurV.glsl | 11 +- .../game/shaders/common/gl/cloudLayerP.glsl | 44 ++- .../game/shaders/common/gl/cloudLayerV.glsl | 74 ++-- .../Full/game/shaders/common/gl/foliage.glsl | 44 +-- .../common/gl/fxFoliageReplicatorP.glsl | 10 +- .../common/gl/fxFoliageReplicatorV.glsl | 34 +- .../game/shaders/common/gl/guiMaterialV.glsl | 12 +- .../game/shaders/common/gl/hlslCompat.glsl | 82 +++- .../Full/game/shaders/common/gl/lighting.glsl | 194 +++++++--- .../shaders/common/gl/particleCompositeP.glsl | 18 +- .../shaders/common/gl/particleCompositeV.glsl | 27 +- .../game/shaders/common/gl/particlesP.glsl | 73 ++-- .../game/shaders/common/gl/particlesV.glsl | 31 +- .../shaders/common/gl/planarReflectBumpP.glsl | 12 +- .../shaders/common/gl/planarReflectBumpV.glsl | 13 +- .../shaders/common/gl/planarReflectP.glsl | 10 +- .../shaders/common/gl/planarReflectV.glsl | 11 +- .../Full/game/shaders/common/gl/precipP.glsl | 6 +- .../Full/game/shaders/common/gl/precipV.glsl | 14 +- .../shaders/common/gl/projectedShadowP.glsl | 23 +- .../shaders/common/gl/projectedShadowV.glsl | 27 +- .../game/shaders/common/gl/scatterSkyP.glsl | 46 ++- .../game/shaders/common/gl/scatterSkyV.glsl | 108 +++--- .../Full/game/shaders/common/gl/torque.glsl | 68 ++-- .../Full/game/shaders/common/gl/wavesP.glsl | 16 +- .../lighting/advanced/gl/convexGeometryV.glsl | 27 +- .../advanced/gl/dbgDepthVisualizeP.glsl | 5 +- .../advanced/gl/dbgLightColorVisualizeP.glsl | 9 +- .../gl/dbgLightSpecularVisualizeP.glsl | 9 +- .../advanced/gl/dbgNormalVisualizeP.glsl | 10 +- .../advanced/gl/dbgShadowVisualizeP.glsl | 7 +- .../lighting/advanced/gl/farFrustumQuad.glsl | 2 +- .../lighting/advanced/gl/farFrustumQuadV.glsl | 34 +- .../lighting/advanced/gl/pointLightP.glsl | 210 +++++------ .../lighting/advanced/gl/softShadow.glsl | 242 ++++++------ .../lighting/advanced/gl/spotLightP.glsl | 160 ++++---- .../lighting/advanced/gl/vectorLightP.glsl | 288 +++++++------- .../lighting/basic/gl/shadowFilterP.glsl | 34 +- .../lighting/basic/gl/shadowFilterV.glsl | 11 +- .../lighting/shadowMap/gl/boxFilterP.glsl | 6 +- .../lighting/shadowMap/gl/boxFilterV.glsl | 9 +- .../game/shaders/common/postFx/postFxV.glsl | 25 +- .../shaders/common/terrain/gl/blendP.glsl | 12 +- .../shaders/common/terrain/gl/blendV.glsl | 15 +- .../shaders/common/water/gl/waterBasicP.glsl | 97 +++-- .../shaders/common/water/gl/waterBasicV.glsl | 212 +++++------ .../game/shaders/common/water/gl/waterP.glsl | 356 ++++++++---------- .../game/shaders/common/water/gl/waterV.glsl | 228 +++++++---- 98 files changed, 3366 insertions(+), 2686 deletions(-) diff --git a/Templates/Empty/game/shaders/common/gl/blurP.glsl b/Templates/Empty/game/shaders/common/gl/blurP.glsl index bc05b992f..5c37ebc6b 100644 --- a/Templates/Empty/game/shaders/common/gl/blurP.glsl +++ b/Templates/Empty/game/shaders/common/gl/blurP.glsl @@ -26,12 +26,12 @@ uniform vec4 kernel; uniform sampler2D diffuseMap; -varying vec2 texc0, texc1, texc2, texc3; +in vec2 texc0, texc1, texc2, texc3; void main() { - gl_FragColor = texture2D(diffuseMap, texc0) * kernel.x; - gl_FragColor += texture2D(diffuseMap, texc1) * kernel.y; - gl_FragColor += texture2D(diffuseMap, texc2) * kernel.z; - gl_FragColor += texture2D(diffuseMap, texc3) * kernel.w; + OUT_FragColor0 = texture(diffuseMap, texc0) * kernel.x; + OUT_FragColor0 += texture(diffuseMap, texc1) * kernel.y; + OUT_FragColor0 += texture(diffuseMap, texc2) * kernel.z; + OUT_FragColor0 += texture(diffuseMap, texc3) * kernel.w; } diff --git a/Templates/Empty/game/shaders/common/gl/blurV.glsl b/Templates/Empty/game/shaders/common/gl/blurV.glsl index d5d615fb9..1bfb0cd1b 100644 --- a/Templates/Empty/game/shaders/common/gl/blurV.glsl +++ b/Templates/Empty/game/shaders/common/gl/blurV.glsl @@ -24,20 +24,25 @@ // Glow shader //***************************************************************************** +in vec4 vPosition; +in vec4 vColor; +in vec2 vTexCoord0; + uniform mat4 modelview; uniform vec2 offset0, offset1, offset2, offset3; -varying vec2 texc0, texc1, texc2, texc3; +out vec2 texc0, texc1, texc2, texc3; void main() { - gl_Position = modelview * gl_Vertex; + gl_Position = modelview * vPosition; - vec2 tc = gl_MultiTexCoord0.st; + vec2 tc = vTexCoord0.st; tc.y = 1.0 - tc.y; texc0 = tc + offset0; texc1 = tc + offset1; texc2 = tc + offset2; texc3 = tc + offset3; + gl_Position.y *= -1; } \ No newline at end of file diff --git a/Templates/Empty/game/shaders/common/gl/cloudLayerP.glsl b/Templates/Empty/game/shaders/common/gl/cloudLayerP.glsl index 326f2d3c6..da3996d58 100644 --- a/Templates/Empty/game/shaders/common/gl/cloudLayerP.glsl +++ b/Templates/Empty/game/shaders/common/gl/cloudLayerP.glsl @@ -22,12 +22,20 @@ #include "hlslCompat.glsl" -varying vec4 texCoord12; -varying vec4 texCoord34; -varying vec3 vLightTS; // light vector in tangent space, denormalized -varying vec3 vViewTS; // view vector in tangent space, denormalized -varying vec3 vNormalWS; // Normal vector in world space -varying float worldDist; +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- +//ConnectData +in vec4 texCoord12; +#define IN_texCoord12 texCoord12 +in vec4 texCoord34; +#define IN_texCoord34 texCoord34 +in vec3 vLightTS; // light vector in tangent space, denormalized +#define IN_vLightTS vLightTS +in vec3 vViewTS; // view vector in tangent space, denormalized +#define IN_vViewTS vViewTS +in float worldDist; +#define IN_worldDist worldDist //----------------------------------------------------------------------------- // Uniforms @@ -37,6 +45,7 @@ uniform vec3 ambientColor; uniform vec3 sunColor; uniform float cloudCoverage; uniform vec3 cloudBaseColor; +uniform float cloudExposure; //----------------------------------------------------------------------------- // Globals @@ -97,26 +106,25 @@ void main() // Normalize the interpolated vectors: vec3 vViewTS = normalize( vViewTS ); vec3 vLightTS = normalize( vLightTS ); - vec3 vNormalWS = normalize( vNormalWS ); - vec4 cResultColor = float4( 0, 0, 0, 1 ); + vec4 cResultColor = vec4( 0, 0, 0, 1 ); - vec2 texSample = texCoord12.xy; + vec2 texSample = IN_texCoord12.xy; - vec4 noise1 = texture2D( normalHeightMap, texCoord12.zw ); + vec4 noise1 = texture( normalHeightMap, IN_texCoord12.zw ); noise1 = normalize( ( noise1 - 0.5 ) * 2.0 ); //return noise1; - vec4 noise2 = texture2D( normalHeightMap, texCoord34.xy ); + vec4 noise2 = texture( normalHeightMap, IN_texCoord34.xy ); noise2 = normalize( ( noise2 - 0.5 ) * 2.0 ); //return noise2; vec3 noiseNormal = normalize( noise1 + noise2 ).xyz; - //return float4( noiseNormal, 1.0 ); + //return vec4( noiseNormal, 1.0 ); float noiseHeight = noise1.a * noise2.a * ( cloudCoverage / 2.0 + 0.5 ); - vec3 vNormalTS = normalize( texture2D( normalHeightMap, texSample ).xyz * 2.0 - 1.0 ); + vec3 vNormalTS = normalize( texture( normalHeightMap, texSample ).xyz * 2.0 - 1.0 ); vNormalTS += noiseNormal; vNormalTS = normalize( vNormalTS ); @@ -124,16 +132,14 @@ void main() cResultColor.rgb = ComputeIllumination( texSample, vLightTS, vViewTS, vNormalTS ); float coverage = ( cloudCoverage - 0.5 ) * 2.0; - cResultColor.a = texture2D( normalHeightMap, texSample ).a + coverage + noiseHeight; + cResultColor.a = texture( normalHeightMap, texSample ).a + coverage + noiseHeight; if ( cloudCoverage > -1.0 ) cResultColor.a /= 1.0 + coverage; - cResultColor.a = saturate( cResultColor.a * pow( saturate(cloudCoverage), 0.25 ) ); + cResultColor.a = clamp( cResultColor.a * pow( saturate(cloudCoverage), 0.25 ), 0.0, 1.0 ); - cResultColor.a = mix( cResultColor.a, 0.0, 1.0 - pow(worldDist,2.0) ); + cResultColor.a = mix( cResultColor.a, 0.0, 1.0 - pow(IN_worldDist,2.0) ); - // If using HDR rendering, make sure to tonemap the resuld color prior to outputting it. - // But since this example isn't doing that, we just output the computed result color here: - gl_FragColor = cResultColor; + OUT_FragColor0 = cResultColor; } diff --git a/Templates/Empty/game/shaders/common/gl/cloudLayerV.glsl b/Templates/Empty/game/shaders/common/gl/cloudLayerV.glsl index 39a6f4ba8..395c6f286 100644 --- a/Templates/Empty/game/shaders/common/gl/cloudLayerV.glsl +++ b/Templates/Empty/game/shaders/common/gl/cloudLayerV.glsl @@ -20,12 +20,24 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -varying vec4 texCoord12; -varying vec4 texCoord34; -varying vec3 vLightTS; // light vector in tangent space, denormalized -varying vec3 vViewTS; // view vector in tangent space, denormalized -varying vec3 vNormalWS; // Normal vector in world space -varying float worldDist; +#include "hlslCompat.glsl" + +in vec4 vPosition; +in vec3 vNormal; +in vec3 vBinormal; +in vec3 vTangent; +in vec2 vTexCoord0; + +out vec4 texCoord12; +#define OUT_texCoord12 texCoord12 +out vec4 texCoord34; +#define OUT_texCoord34 texCoord34 +out vec3 vLightTS; // light vector in tangent space, denormalized +#define OUT_vLightTS vLightTS +out vec3 vViewTS; // view vector in tangent space, denormalized +#define OUT_vViewTS vViewTS +out float worldDist; +#define OUT_worldDist worldDist //----------------------------------------------------------------------------- // Uniforms @@ -43,37 +55,37 @@ uniform vec3 texScale; //----------------------------------------------------------------------------- void main() { - vec4 pos = gl_Vertex; - vec3 normal = gl_Normal; - vec3 binormal = gl_MultiTexCoord0.xyz; - vec3 tangent = gl_MultiTexCoord1.xyz; - vec2 uv0 = gl_MultiTexCoord2.st; + vec4 IN_pos = vPosition; + vec3 IN_normal = vNormal; + vec3 IN_binormal = vBinormal; + vec3 IN_tangent = vTangent; + vec2 IN_uv0 = vTexCoord0.st; - gl_Position = modelview * pos; + gl_Position = modelview * IN_pos; // Offset the uv so we don't have a seam directly over our head. - vec2 uv = uv0 + vec2( 0.5, 0.5 ); + vec2 uv = IN_uv0 + vec2( 0.5, 0.5 ); - texCoord12.xy = uv * texScale.x; - texCoord12.xy += texOffset0; + OUT_texCoord12.xy = uv * texScale.x; + OUT_texCoord12.xy += texOffset0; - texCoord12.zw = uv * texScale.y; - texCoord12.zw += texOffset1; + OUT_texCoord12.zw = uv * texScale.y; + OUT_texCoord12.zw += texOffset1; - texCoord34.xy = uv * texScale.z; - texCoord34.xy += texOffset2; + OUT_texCoord34.xy = uv * texScale.z; + OUT_texCoord34.xy += texOffset2; - texCoord34.z = pos.z; - texCoord34.w = 0.0; + OUT_texCoord34.z = IN_pos.z; + OUT_texCoord34.w = 0.0; // Transform the normal, tangent and binormal vectors from object space to // homogeneous projection space: - vNormalWS = -normal; - vec3 vTangentWS = -tangent; - vec3 vBinormalWS = -binormal; + vec3 vNormalWS = -IN_normal; + vec3 vTangentWS = -IN_tangent; + vec3 vBinormalWS = -IN_binormal; // Compute position in world space: - vec4 vPositionWS = pos + vec4( eyePosWorld, 1 ); //mul( pos, objTrans ); + vec4 vPositionWS = IN_pos + vec4( eyePosWorld, 1 ); //tMul( IN_pos, objTrans ); // Compute and output the world view vector (unnormalized): vec3 vViewWS = eyePosWorld - vPositionWS.xyz; @@ -81,12 +93,14 @@ void main() // Compute denormalized light vector in world space: vec3 vLightWS = -sunVec; - // Normalize the light and view vectors and transform it to the tangent space: + // Normalize the light and view vectors and transform it to the IN_tangent space: mat3 mWorldToTangent = mat3( vTangentWS, vBinormalWS, vNormalWS ); // Propagate the view and the light vectors (in tangent space): - vLightTS = mWorldToTangent * vLightWS; - vViewTS = vViewWS * mWorldToTangent; - - worldDist = clamp( pow( pos.z, 2.0 ), 0.0, 1.0 ); + OUT_vLightTS = vLightWS * mWorldToTangent; + OUT_vViewTS = mWorldToTangent * vViewWS; + + OUT_worldDist = clamp( pow( max( IN_pos.z, 0 ), 2 ), 0.0, 1.0 ); + + correctSSP(gl_Position); } diff --git a/Templates/Empty/game/shaders/common/gl/foliage.glsl b/Templates/Empty/game/shaders/common/gl/foliage.glsl index 2fee902e3..38b66e767 100644 --- a/Templates/Empty/game/shaders/common/gl/foliage.glsl +++ b/Templates/Empty/game/shaders/common/gl/foliage.glsl @@ -46,7 +46,19 @@ uniform vec3 gc_gustInfo; uniform vec2 gc_turbInfo; -//static float sMovableCorner[4] = { 0.0, 0.0, 1.0, 1.0 }; +const float sCornerRight[4] = float[]( -0.5, 0.5, 0.5, -0.5 ); + +const float sCornerUp[4] = float[]( 0, 0, 1, 1 ); + +const float sMovableCorner[4] = float[]( 0, 0, 1, 1 ); + +const vec2 sUVCornerExtent[4] = vec2[] +( + vec2( 0, 1 ), + vec2( 1, 1 ), + vec2( 1, 0 ), + vec2( 0, 0 ) +); /////////////////////////////////////////////////////////////////////////////// @@ -106,34 +118,13 @@ vec2 windEffect( float bbPhase, void foliageProcessVert( inout vec3 position, inout vec4 diffuse, - in vec4 texCoord, - out vec2 outTexCoord, + inout vec4 texCoord, inout vec3 normal, inout vec3 T, in vec3 eyePos ) { - - float sCornerRight[4]; - sCornerRight[0] = -0.5; - sCornerRight[1] = 0.5; - sCornerRight[2] = 0.5; - sCornerRight[3] = -0.5; - - float sCornerUp[4]; - sCornerUp[0] = 0.0; - sCornerUp[1] = 0.0; - sCornerUp[2] = 1.0; - sCornerUp[3] = 1.0; - - vec2 sUVCornerExtent[4]; - sUVCornerExtent[0] = vec2( 0.0, 1.0 ); - sUVCornerExtent[1] = vec2( 1.0, 1.0 ); - sUVCornerExtent[2] = vec2( 1.0, 0.0 ); - sUVCornerExtent[3] = vec2( 0.0, 0.0 ); - - // Assign the normal and tagent values. - //normal = cross( gc_camUp, gc_camRight ); + //normal = vec3( 0, 0, 1 );//cross( gc_camUp, gc_camRight ); T = gc_camRight; // Pull out local vars we need for work. @@ -172,8 +163,8 @@ void foliageProcessVert( inout vec3 position, // Grab the uv set and setup the texture coord. vec4 uvSet = gc_typeRects[type]; - outTexCoord.x = uvSet.x + ( uvSet.z * sUVCornerExtent[corner].x ); - outTexCoord.y = uvSet.y + ( uvSet.w * sUVCornerExtent[corner].y ); + texCoord.x = uvSet.x + ( uvSet.z * sUVCornerExtent[corner].x ); + texCoord.y = uvSet.y + ( uvSet.w * sUVCornerExtent[corner].y ); // Animate the normal to get lighting changes // across the the wind swept foliage. @@ -184,7 +175,6 @@ void foliageProcessVert( inout vec3 position, normal.xy += wind.xy * ( 10.0 * texCoord.w ); normal = normalize( normal ); - // Get the alpha fade value. float fadeStart = gc_fadeParams.x; diff --git a/Templates/Empty/game/shaders/common/gl/fxFoliageReplicatorP.glsl b/Templates/Empty/game/shaders/common/gl/fxFoliageReplicatorP.glsl index 9e5b34caa..fb5abb91e 100644 --- a/Templates/Empty/game/shaders/common/gl/fxFoliageReplicatorP.glsl +++ b/Templates/Empty/game/shaders/common/gl/fxFoliageReplicatorP.glsl @@ -26,15 +26,15 @@ uniform sampler2D diffuseMap, alphaMap; uniform vec4 groundAlpha; -varying vec4 color, groundAlphaCoeff; -varying vec2 outTexCoord, alphaLookup; +in vec4 color, groundAlphaCoeff; +in vec2 outTexCoord, alphaLookup; //----------------------------------------------------------------------------- // Main //----------------------------------------------------------------------------- void main() { - vec4 alpha = texture2D(alphaMap, alphaLookup); - gl_FragColor = color * texture2D(diffuseMap, outTexCoord); - gl_FragColor.a = gl_FragColor.a * min(alpha, groundAlpha + groundAlphaCoeff.x).x; + vec4 alpha = texture(alphaMap, alphaLookup); + OUT_FragColor0 = color * texture(diffuseMap, outTexCoord); + OUT_FragColor0.a = OUT_FragColor0.a * min(alpha, groundAlpha + groundAlphaCoeff.x).x; } diff --git a/Templates/Empty/game/shaders/common/gl/fxFoliageReplicatorV.glsl b/Templates/Empty/game/shaders/common/gl/fxFoliageReplicatorV.glsl index 94a7af2b0..c8dcf1ddb 100644 --- a/Templates/Empty/game/shaders/common/gl/fxFoliageReplicatorV.glsl +++ b/Templates/Empty/game/shaders/common/gl/fxFoliageReplicatorV.glsl @@ -23,13 +23,20 @@ //----------------------------------------------------------------------------- // Data //----------------------------------------------------------------------------- +in vec4 vPosition; +in vec3 vNormal; +in vec4 vColor; +in vec2 vTexCoord0; +in vec2 vTexCoord1; +in vec2 vTexCoord2; + uniform mat4 projection, world; uniform vec3 CameraPos; uniform float GlobalSwayPhase, SwayMagnitudeSide, SwayMagnitudeFront, GlobalLightPhase, LuminanceMagnitude, LuminanceMidpoint, DistanceRange; -varying vec4 color, groundAlphaCoeff; -varying vec2 outTexCoord, alphaLookup; +out vec4 color, groundAlphaCoeff; +out vec2 outTexCoord, alphaLookup; //----------------------------------------------------------------------------- // Main @@ -42,9 +49,9 @@ void main() trans[1][1] = 1.0; trans[2][2] = 1.0; trans[3][3] = 1.0; - trans[3][0] = gl_Vertex.x; - trans[3][1] = gl_Vertex.y; - trans[3][2] = gl_Vertex.z; + trans[3][0] = vPosition.x; + trans[3][1] = vPosition.y; + trans[3][2] = vPosition.z; // Billboard transform * world matrix mat4 o = world; @@ -64,28 +71,29 @@ void main() // Handle sway. Sway is stored in a texture coord. The x coordinate is the sway phase multiplier, // the y coordinate determines if this vertex actually sways or not. float xSway, ySway; - float wavePhase = GlobalSwayPhase * gl_MultiTexCoord1.x; + float wavePhase = GlobalSwayPhase * vTexCoord1.x; ySway = sin(wavePhase); xSway = cos(wavePhase); - xSway = xSway * gl_MultiTexCoord1.y * SwayMagnitudeSide; - ySway = ySway * gl_MultiTexCoord1.y * SwayMagnitudeFront; + xSway = xSway * vTexCoord1.y * SwayMagnitudeSide; + ySway = ySway * vTexCoord1.y * SwayMagnitudeFront; vec4 p; - p = o * vec4(gl_Normal.x + xSway, ySway, gl_Normal.z, 1.0); + p = o * vec4(vNormal.x + xSway, ySway, vNormal.z, 1.0); // Project the point gl_Position = projection * p; // Lighting - float Luminance = LuminanceMidpoint + LuminanceMagnitude * cos(GlobalLightPhase + gl_Normal.y); + float Luminance = LuminanceMidpoint + LuminanceMagnitude * cos(GlobalLightPhase + vNormal.y); // Alpha - vec3 worldPos = vec3(gl_Vertex.x, gl_Vertex.y, gl_Vertex.z); + vec3 worldPos = vec3(vPosition.x, vPosition.y, vPosition.z); float alpha = abs(distance(worldPos, CameraPos)) / DistanceRange; alpha = clamp(alpha, 0.0, 1.0); //pass it through alphaLookup = vec2(alpha, 0.0); - bool alphaCoeff = bool(gl_Normal.z); + bool alphaCoeff = bool(vNormal.z); groundAlphaCoeff = vec4(float(alphaCoeff)); - outTexCoord = gl_MultiTexCoord0.st; + outTexCoord = vTexCoord0.st; color = vec4(Luminance, Luminance, Luminance, 1.0); + gl_Position.y *= -1; } \ No newline at end of file diff --git a/Templates/Empty/game/shaders/common/gl/guiMaterialV.glsl b/Templates/Empty/game/shaders/common/gl/guiMaterialV.glsl index cd44de2f2..de3845ee7 100644 --- a/Templates/Empty/game/shaders/common/gl/guiMaterialV.glsl +++ b/Templates/Empty/game/shaders/common/gl/guiMaterialV.glsl @@ -20,16 +20,20 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +in vec4 vPosition; +in vec2 vTexCoord0; + uniform mat4x4 modelview; -varying vec4 hpos; -varying vec2 uv0; +out vec4 hpos; +out vec2 uv0; void main() { - hpos = vec4( modelview * gl_Vertex ); + hpos = vec4( modelview * vPosition ); gl_Position = hpos; - uv0 = gl_MultiTexCoord0.st; + uv0 = vTexCoord0.st; + gl_Position.y *= -1; } diff --git a/Templates/Empty/game/shaders/common/gl/hlslCompat.glsl b/Templates/Empty/game/shaders/common/gl/hlslCompat.glsl index be5c63340..0815df51f 100644 --- a/Templates/Empty/game/shaders/common/gl/hlslCompat.glsl +++ b/Templates/Empty/game/shaders/common/gl/hlslCompat.glsl @@ -27,17 +27,79 @@ #define float3 vec3 #define float2 vec2 -#define texCUBE textureCube -#define tex2D texture2D +#define half float +#define half2 vec2 +#define half3 vec3 +#define half4 vec4 + +#define float4x4 mat4 +#define float3x3 mat3 +#define float2x2 mat2 + +#define texCUBE texture +#define tex2D texture +#define tex1D texture +#define tex2Dproj textureProj +#define tex2Dlod( sampler, texCoord ) textureLod(sampler, texCoord.xy, texCoord.w) + +#define samplerCUBE samplerCube + +#define frac fract #define lerp mix -float saturate( float val ) { return clamp( val, 0.0, 1.0 ); } -vec2 saturate( vec2 val ) { return clamp( val, 0.0, 1.0 ); } -vec3 saturate( vec3 val ) { return clamp( val, 0.0, 1.0 ); } -vec4 saturate( vec4 val ) { return clamp( val, 0.0, 1.0 ); } +void tSetMatrixRow(out float3x3 m, int row, float3 value) +{ + m[0][row] = value.x; + m[1][row] = value.y; + m[2][row] = value.z; +} -float round( float n ) { return sign( n ) * floor( abs( n ) + 0.5 ); } -vec2 round( vec2 n ) { return sign( n ) * floor( abs( n ) + 0.5 ); } -vec3 round( vec3 n ) { return sign( n ) * floor( abs( n ) + 0.5 ); } -vec4 round( vec4 n ) { return sign( n ) * floor( abs( n ) + 0.5 ); } +void tSetMatrixRow(out float4x4 m, int row, float4 value) +{ + m[0][row] = value.x; + m[1][row] = value.y; + m[2][row] = value.z; + m[3][row] = value.w; +} + +#define tGetMatrix3Row(matrix, row) float3(matrix[0][row], matrix[1][row], matrix[2][row]) +#define tGetMatrix4Row(matrix, row) float4(matrix[0][row], matrix[1][row], matrix[2][row], matrix[3][row]) + +float3x3 float4x4to3x3(float4x4 m) +{ + return float3x3( vec3(m[0]).xyz, m[1].xyz, m[2].xyz); +} + +float3x3 float4x4to3x3_(float4x4 m) +{ + return float3x3( vec3(m[0]), m[1].xyz, m[2].xyz); +} + +mat4 mat4FromRow( float r0c0, float r0c1, float r0c2, float r0c3, + float r1c0, float r1c1, float r1c2, float r1c3, + float r2c0, float r2c1, float r2c2, float r2c3, + float r3c0, float r3c1, float r3c2, float r3c3 ) +{ + return mat4( r0c0, r1c0, r2c0, r3c0, + r0c1, r1c1, r2c1, r3c1, + r0c2, r1c2, r2c2, r3c2, + r0c3, r1c3, r2c3, r3c3 ); +} + + +#define saturate( val ) clamp( val, 0.0, 1.0 ) + +#define round( n ) (sign( n ) * floor( abs( n ) + 0.5 )) + +#define tMul(a, b) (a*b) + +#define inversesqrt( n ) inversesqrt( n ) + +#define correctSSP(vec) vec.y *= -1 + +#ifdef TORQUE_PIXEL_SHADER + void clip(float a) { if(a < 0) discard;} + + out vec4 OUT_FragColor0; +#endif diff --git a/Templates/Empty/game/shaders/common/gl/lighting.glsl b/Templates/Empty/game/shaders/common/gl/lighting.glsl index 3f8867d3b..4483c7526 100644 --- a/Templates/Empty/game/shaders/common/gl/lighting.glsl +++ b/Templates/Empty/game/shaders/common/gl/lighting.glsl @@ -20,73 +20,181 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- + +#ifndef TORQUE_SHADERGEN + // These are the uniforms used by most lighting shaders. -uniform vec3 inLightPos[4]; +uniform vec4 inLightPos[3]; uniform vec4 inLightInvRadiusSq; uniform vec4 inLightColor[4]; + +#ifndef TORQUE_BL_NOSPOTLIGHT + uniform vec4 inLightSpotDir[3]; + uniform vec4 inLightSpotAngle; + uniform vec4 inLightSpotFalloff; +#endif + uniform vec4 ambient; uniform float specularPower; uniform vec4 specularColor; - -// This is used to limit the maximum processed -// lights in the compute4Lights down for really -// low end GPUs. -// -// NOTE: If you want to support 10.5.x, this needs to be changed to 2. -#define C4L_MAX_LIGHTS 4 +#endif // !TORQUE_SHADERGEN void compute4Lights( vec3 wsView, vec3 wsPosition, - vec3 wsNormal, + vec3 wsNormal, + vec4 shadowMask, + + #ifdef TORQUE_SHADERGEN + + vec4 inLightPos[3], + vec4 inLightInvRadiusSq, + vec4 inLightColor[4], + vec4 inLightSpotDir[3], + vec4 inLightSpotAngle, + vec4 inLightSpotFalloff, + float specularPower, + vec4 specularColor, + + #endif // TORQUE_SHADERGEN + out vec4 outDiffuse, out vec4 outSpecular ) { - #ifdef PHONG_SPECULAR - // (R.V)^c - float reflected = reflect( wsView, wsNormal ); - #endif - - vec4 nDotL = vec4( 0.0 ); - vec4 rDotL = vec4( 0.0 ); - vec4 sqDists = vec4( 0.0 ); + // NOTE: The light positions and spotlight directions + // are stored in SoA order, so inLightPos[0] is the + // x coord for all 4 lights... inLightPos[1] is y... etc. + // + // This is the key to fully utilizing the vector units and + // saving a huge amount of instructions. + // + // For example this change saved more than 10 instructions + // over a simple for loop for each light. + int i; - for ( i = 0; i < C4L_MAX_LIGHTS; ++i ) - { - vec3 lightVector = inLightPos[i] - wsPosition; - vec3 lightDirection = normalize( lightVector ); + vec4 lightVectors[3]; + for ( i = 0; i < 3; i++ ) + lightVectors[i] = wsPosition[i] - inLightPos[i]; - nDotL[i] = max( dot( lightDirection, wsNormal ), 0.0 ); + vec4 squareDists = vec4(0); + for ( i = 0; i < 3; i++ ) + squareDists += lightVectors[i] * lightVectors[i]; - #ifdef PHONG_SPECULAR - rDotL[i] = saturate( dot( lightDirection, reflected ) ); - #else - // (N.H)^c [Blinn-Phong, TGEA style, default] - rDotL[i] = dot( wsNormal, normalize( lightDirection + wsView ) ); - #endif + // Accumulate the dot product between the light + // vector and the normal. + // + // The normal is negated because it faces away from + // the surface and the light faces towards the + // surface... this keeps us from needing to flip + // the light vector direction which complicates + // the spot light calculations. + // + // We normalize the result a little later. + // + vec4 nDotL = vec4(0); + for ( i = 0; i < 3; i++ ) + nDotL += lightVectors[i] * -wsNormal[i]; - sqDists[i] = dot( lightVector, lightVector ); - } + vec4 rDotL = vec4(0); + #ifndef TORQUE_BL_NOSPECULAR - // Attenuation - vec4 atten = vec4( 1.0 ) - ( sqDists * inLightInvRadiusSq ); + // We're using the Phong specular reflection model + // here where traditionally Torque has used Blinn-Phong + // which has proven to be more accurate to real materials. + // + // We do so because its cheaper as do not need to + // calculate the half angle for all 4 lights. + // + // Advanced Lighting still uses Blinn-Phong, but the + // specular reconstruction it does looks fairly similar + // to this. + // + vec3 R = reflect( wsView, -wsNormal ); + + for ( i = 0; i < 3; i++ ) + rDotL += lightVectors[i] * R[i]; + + #endif + + // Normalize the dots. + // + // Notice we're using the half type here to get a + // much faster sqrt via the rsq_pp instruction at + // the loss of some precision. + // + // Unless we have some extremely large point lights + // i don't believe the precision loss will matter. + // + half4 correction = half4(inversesqrt( squareDists )); + nDotL = saturate( nDotL * correction ); + rDotL = clamp( rDotL * correction, 0.00001, 1.0 ); + + // First calculate a simple point light linear + // attenuation factor. + // + // If this is a directional light the inverse + // radius should be greater than the distance + // causing the attenuation to have no affect. + // + vec4 atten = saturate( 1.0 - ( squareDists * inLightInvRadiusSq ) ); + + #ifndef TORQUE_BL_NOSPOTLIGHT + + // The spotlight attenuation factor. This is really + // fast for what it does... 6 instructions for 4 spots. + + vec4 spotAtten = vec4(0); + for ( i = 0; i < 3; i++ ) + spotAtten += lightVectors[i] * inLightSpotDir[i]; + + vec4 cosAngle = ( spotAtten * correction ) - inLightSpotAngle; + atten *= saturate( cosAngle * inLightSpotFalloff ); + + #endif + + // Finally apply the shadow masking on the attenuation. + atten *= shadowMask; + + // Get the final light intensity. + vec4 intensity = nDotL * atten; // Combine the light colors for output. - vec4 diffuse = clamp( nDotL * atten, vec4( 0.0 ), vec4( 1.0 ) ); - outDiffuse = vec4( 0.0 ); - for ( i = 0; i < C4L_MAX_LIGHTS; ++i ) - outDiffuse += vec4( diffuse[i] ) * inLightColor[i]; + outDiffuse = vec4(0); + for ( i = 0; i < 4; i++ ) + outDiffuse += intensity[i] * inLightColor[i]; // Output the specular power. - rDotL = max( rDotL, vec4( 0.00001 ) ); - outSpecular = pow( rDotL, vec4( specularPower ) ); + vec4 specularIntensity = pow( rDotL, vec4(specularPower) ) * atten; + + // Apply the per-light specular attenuation. + vec4 specular = vec4(0,0,0,1); + for ( i = 0; i < 4; i++ ) + specular += vec4( inLightColor[i].rgb * inLightColor[i].a * specularIntensity[i], 1 ); + + // Add the final specular intensity values together + // using a single dot product operation then get the + // final specular lighting color. + outSpecular = specularColor * specular; } -/// The standard specular calculation. +// This value is used in AL as a constant power to raise specular values +// to, before storing them into the light info buffer. The per-material +// specular value is then computer by using the integer identity of +// exponentiation: +// +// (a^m)^n = a^(m*n) +// +// or +// +// (specular^constSpecular)^(matSpecular/constSpecular) = specular^(matSpecular*constSpecular) +// +#define AL_ConstantSpecularPower 12.0f + +/// The specular calculation used in Advanced Lighting. /// /// @param toLight Normalized vector representing direction from the pixel /// being lit, to the light source, in world space. @@ -96,11 +204,7 @@ void compute4Lights( vec3 wsView, /// @param toEye The normalized vector representing direction from the pixel /// being lit to the camera. /// -/// @param specPwr The specular exponent. -/// -/// @param specScale A scalar on the specular output used in RGB accumulation. -/// -float calcSpecular( vec3 toLight, vec3 normal, vec3 toEye, float specPwr ) +float AL_CalcSpecular( vec3 toLight, vec3 normal, vec3 toEye ) { #ifdef PHONG_SPECULAR // (R.V)^c @@ -111,5 +215,5 @@ float calcSpecular( vec3 toLight, vec3 normal, vec3 toEye, float specPwr ) #endif // Return the specular factor. - return pow( max( specVal, 0.00001f ), specPwr ); + return pow( max( specVal, 0.00001f ), AL_ConstantSpecularPower ); } diff --git a/Templates/Empty/game/shaders/common/gl/particleCompositeP.glsl b/Templates/Empty/game/shaders/common/gl/particleCompositeP.glsl index 4f2d9b359..6971e1013 100644 --- a/Templates/Empty/game/shaders/common/gl/particleCompositeP.glsl +++ b/Templates/Empty/game/shaders/common/gl/particleCompositeP.glsl @@ -21,6 +21,13 @@ //----------------------------------------------------------------------------- #include "torque.glsl" +#include "hlslCompat.glsl" + +in vec4 offscreenPos; +in vec4 backbufferPos; + +#define IN_offscreenPos offscreenPos +#define IN_backbufferPos backbufferPos uniform sampler2D colorSource; uniform vec4 offscreenTargetParams; @@ -31,8 +38,6 @@ uniform sampler2D edgeSource; uniform vec4 edgeTargetParams; #endif -varying vec4 backbufferPos; -varying vec4 offscreenPos; void main() { @@ -47,11 +52,10 @@ void main() #ifdef REJECT_EDGES // Cut out particles along the edges, this will create the stencil mask uvScene.zw = viewportCoordToRenderTarget(uvScene.zw, edgeTargetParams); - float edge = texture2D( edgeSource, uvScene.zw ).r; - if (-edge < 0.0) - discard; + float edge = texture( edgeSource, uvScene.zw ).r; + clip( -edge ); #endif // Sample offscreen target and return - gl_FragColor = texture2D( colorSource, uvScene.xy ); -} + OUT_FragColor0 = texture( colorSource, uvScene.xy ); +} \ No newline at end of file diff --git a/Templates/Empty/game/shaders/common/gl/particleCompositeV.glsl b/Templates/Empty/game/shaders/common/gl/particleCompositeV.glsl index 88a9431d1..8c8f840d1 100644 --- a/Templates/Empty/game/shaders/common/gl/particleCompositeV.glsl +++ b/Templates/Empty/game/shaders/common/gl/particleCompositeV.glsl @@ -20,16 +20,29 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -uniform mat4 modelViewProj; -uniform mat4 targetModelViewProj; +#include "hlslCompat.glsl" -varying vec4 offscreenPos; -varying vec4 backbufferPos; +in vec2 vTexCoord0; +#define uvCoord vTexCoord0 + +out vec4 offscreenPos; +out vec4 backbufferPos; + +#define OUT_hpos gl_Position +#define OUT_offscreenPos offscreenPos +#define OUT_backbufferPos backbufferPos + +uniform vec4 screenRect; // point, extent void main() { - gl_Position = modelViewProj * gl_Vertex; - backbufferPos = gl_Position; - offscreenPos = targetModelViewProj * gl_Vertex; + OUT_hpos = vec4(uvCoord.xy, 1.0, 1.0); + OUT_hpos.xy *= screenRect.zw; + OUT_hpos.xy += screenRect.xy; + + OUT_backbufferPos = OUT_hpos; + OUT_offscreenPos = OUT_hpos; + + correctSSP(gl_Position); } diff --git a/Templates/Empty/game/shaders/common/gl/particlesP.glsl b/Templates/Empty/game/shaders/common/gl/particlesP.glsl index 66a3fee28..efd930302 100644 --- a/Templates/Empty/game/shaders/common/gl/particlesP.glsl +++ b/Templates/Empty/game/shaders/common/gl/particlesP.glsl @@ -20,63 +20,92 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -#include "hlslCompat.glsl" #include "torque.glsl" - +#include "hlslCompat.glsl" + // With advanced lighting we get soft particles. #ifdef TORQUE_LINEAR_DEPTH #define SOFTPARTICLES #endif -#define CLIP_Z // TODO: Make this a proper macro - -uniform sampler2D diffuseMap; - #ifdef SOFTPARTICLES #include "shadergen:/autogenConditioners.h" uniform float oneOverSoftness; uniform float oneOverFar; - uniform sampler2D prepassTex; + uniform sampler2D prepassTex; //uniform vec3 vEye; uniform vec4 prePassTargetParams; #endif +#define CLIP_Z // TODO: Make this a proper macro + +in vec4 color; +in vec2 uv0; +in vec4 pos; + +#define IN_color color +#define IN_uv0 uv0 +#define IN_pos pos + +uniform sampler2D diffuseMap; + +uniform sampler2D paraboloidLightMap; + +vec4 lmSample( vec3 nrm ) +{ + bool calcBack = (nrm.z < 0.0); + if ( calcBack ) + nrm.z = nrm.z * -1.0; + + vec2 lmCoord; + lmCoord.x = (nrm.x / (2*(1 + nrm.z))) + 0.5; + lmCoord.y = 1-((nrm.y / (2*(1 + nrm.z))) + 0.5); + + + // If this is the back, offset in the atlas + if ( calcBack ) + lmCoord.x += 1.0; + + // Atlasing front and back maps, so scale + lmCoord.x *= 0.5; + + return texture(paraboloidLightMap, lmCoord); +} + + uniform float alphaFactor; uniform float alphaScale; -varying vec4 color; -varying vec2 uv0; -varying vec4 pos; - - void main() { - float softBlend = 1.0; + float softBlend = 1; #ifdef SOFTPARTICLES - float2 tc = pos.xy * vec2(1.0, -1.0 ) / pos.w; + vec2 tc = IN_pos.xy * vec2(1.0, -1.0) / IN_pos.w; tc = viewportCoordToRenderTarget(saturate( ( tc + 1.0 ) * 0.5 ), prePassTargetParams); float sceneDepth = prepassUncondition( prepassTex, tc ).w; - float depth = pos.w * oneOverFar; - float diff = sceneDepth - depth; + float depth = IN_pos.w * oneOverFar; + float diff = sceneDepth - depth; #ifdef CLIP_Z // If drawing offscreen, this acts as the depth test, since we don't line up with the z-buffer // When drawing high-res, though, we want to be able to take advantage of hi-z // so this is #ifdef'd out - if (diff < 0.0) - discard; + //clip(diff); #endif softBlend = saturate( diff * oneOverSoftness ); #endif - - vec4 diffuse = texture2D( diffuseMap, uv0 ); + + vec4 diffuse = texture( diffuseMap, IN_uv0 ); + + //OUT_FragColor0 = vec4( lmSample(vec3(0, 0, -1)).rgb, IN_color.a * diffuse.a * softBlend * alphaScale); // Scale output color by the alpha factor (turn LerpAlpha into pre-multiplied alpha) - vec3 colorScale = ( alphaFactor < 0.0 ? color.rgb * diffuse.rgb : ( alphaFactor > 0.0 ? vec3(color.a * alphaFactor * diffuse.a * softBlend) : vec3(softBlend) ) ); + vec3 colorScale = ( alphaFactor < 0.0 ? IN_color.rgb * diffuse.rgb : vec3( alphaFactor > 0.0 ? IN_color.a * diffuse.a * alphaFactor * softBlend : softBlend ) ); - gl_FragColor = hdrEncode( vec4(color.rgb * diffuse.rgb * colorScale, softBlend * color.a * diffuse.a * alphaScale) ); + OUT_FragColor0 = hdrEncode( vec4( IN_color.rgb * diffuse.rgb * colorScale, + IN_color.a * diffuse.a * softBlend * alphaScale ) ); } diff --git a/Templates/Empty/game/shaders/common/gl/particlesV.glsl b/Templates/Empty/game/shaders/common/gl/particlesV.glsl index da896431f..3d75a6fb6 100644 --- a/Templates/Empty/game/shaders/common/gl/particlesV.glsl +++ b/Templates/Empty/game/shaders/common/gl/particlesV.glsl @@ -20,18 +20,35 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -varying vec4 color; -varying vec2 uv0; -varying vec4 pos; +#include "hlslCompat.glsl" + +in vec4 vPosition; +in vec4 vColor; +in vec2 vTexCoord0; + +#define In_pos vPosition +#define In_color vColor +#define In_uv0 vTexCoord0 + +out vec4 color; +out vec2 uv0; +out vec4 pos; + +#define OUT_hpos gl_Position +#define OUT_color color +#define OUT_uv0 uv0 +#define OUT_pos pos uniform mat4 modelViewProj; uniform mat4 fsModelViewProj; void main() { - gl_Position = modelViewProj * gl_Vertex; - pos = fsModelViewProj * gl_Vertex; - color = gl_Color; - uv0 = gl_MultiTexCoord0.st; + OUT_hpos = tMul( modelViewProj, In_pos ); + OUT_pos = tMul( fsModelViewProj, In_pos ); + OUT_color = In_color; + OUT_uv0 = In_uv0; + + correctSSP(gl_Position); } diff --git a/Templates/Empty/game/shaders/common/gl/planarReflectBumpP.glsl b/Templates/Empty/game/shaders/common/gl/planarReflectBumpP.glsl index 00413aa3b..334c70a70 100644 --- a/Templates/Empty/game/shaders/common/gl/planarReflectBumpP.glsl +++ b/Templates/Empty/game/shaders/common/gl/planarReflectBumpP.glsl @@ -26,8 +26,8 @@ uniform sampler2D diffuseMap, refractMap, bumpMap; uniform vec4 shadeColor; -varying vec2 TEX0; -varying vec4 TEX1; +in vec2 TEX0; +in vec4 TEX1; //----------------------------------------------------------------------------- // Fade edges of axis for texcoord passed in @@ -49,7 +49,7 @@ float fadeAxis( float val ) //----------------------------------------------------------------------------- void main() { - vec3 bumpNorm = texture2D( bumpMap, TEX0 ).rgb * 2.0 - 1.0; + vec3 bumpNorm = texture( bumpMap, TEX0 ).rgb * 2.0 - 1.0; vec2 offset = vec2( bumpNorm.x, bumpNorm.y ); vec4 texIndex = TEX1; @@ -61,8 +61,8 @@ void main() const float distortion = 0.2; texIndex.xy += offset * distortion * fadeVal; - vec4 diffuseColor = texture2D( diffuseMap, TEX0 ); - vec4 reflectColor = texture2DProj( refractMap, texIndex ); + vec4 diffuseColor = texture( diffuseMap, TEX0 ); + vec4 reflectColor = textureProj( refractMap, texIndex ); - gl_FragColor = diffuseColor + reflectColor * diffuseColor.a; + OUT_FragColor0 = diffuseColor + reflectColor * diffuseColor.a; } diff --git a/Templates/Empty/game/shaders/common/gl/planarReflectBumpV.glsl b/Templates/Empty/game/shaders/common/gl/planarReflectBumpV.glsl index 820bdf698..90bcd27d8 100644 --- a/Templates/Empty/game/shaders/common/gl/planarReflectBumpV.glsl +++ b/Templates/Empty/game/shaders/common/gl/planarReflectBumpV.glsl @@ -23,10 +23,13 @@ //----------------------------------------------------------------------------- // Data //----------------------------------------------------------------------------- +in vec4 vPosition; +in vec2 vTexCoord0; + uniform mat4 modelview; -varying vec2 TEX0; -varying vec4 TEX1; +out vec2 TEX0; +out vec4 TEX1; //----------------------------------------------------------------------------- // Main @@ -38,11 +41,11 @@ void main() 0.0, 0.0, 1.0, 0.0, 0.5, 0.5, 0.0, 1.0); - gl_Position = modelview * gl_Vertex; + gl_Position = modelview * vPosition; - TEX0 = gl_MultiTexCoord0.st; + TEX0 = vTexCoord0.st; TEX1 = texGenTest * gl_Position; TEX1.y = -TEX1.y; - + gl_Position.y *= -1; } diff --git a/Templates/Empty/game/shaders/common/gl/planarReflectP.glsl b/Templates/Empty/game/shaders/common/gl/planarReflectP.glsl index b4f4fab39..77914b80e 100644 --- a/Templates/Empty/game/shaders/common/gl/planarReflectP.glsl +++ b/Templates/Empty/game/shaders/common/gl/planarReflectP.glsl @@ -26,16 +26,16 @@ uniform sampler2D diffuseMap, refractMap; uniform vec4 shadeColor; -varying vec2 TEX0; -varying vec4 TEX1; +in vec2 TEX0; +in vec4 TEX1; //----------------------------------------------------------------------------- // Main //----------------------------------------------------------------------------- void main() { - vec4 diffuseColor = texture2D( diffuseMap, TEX0 ); - vec4 reflectColor = texture2DProj( refractMap, TEX1 ); + vec4 diffuseColor = texture( diffuseMap, TEX0 ); + vec4 reflectColor = textureProj( refractMap, TEX1 ); - gl_FragColor = diffuseColor + reflectColor * diffuseColor.a; + OUT_FragColor0 = diffuseColor + reflectColor * diffuseColor.a; } diff --git a/Templates/Empty/game/shaders/common/gl/planarReflectV.glsl b/Templates/Empty/game/shaders/common/gl/planarReflectV.glsl index 820bdf698..ba2484f66 100644 --- a/Templates/Empty/game/shaders/common/gl/planarReflectV.glsl +++ b/Templates/Empty/game/shaders/common/gl/planarReflectV.glsl @@ -23,10 +23,13 @@ //----------------------------------------------------------------------------- // Data //----------------------------------------------------------------------------- +in vec4 vPosition; +in vec2 vTexCoord0; + uniform mat4 modelview; -varying vec2 TEX0; -varying vec4 TEX1; +out vec2 TEX0; +out vec4 TEX1; //----------------------------------------------------------------------------- // Main @@ -38,9 +41,9 @@ void main() 0.0, 0.0, 1.0, 0.0, 0.5, 0.5, 0.0, 1.0); - gl_Position = modelview * gl_Vertex; + gl_Position = modelview * vPosition; - TEX0 = gl_MultiTexCoord0.st; + TEX0 = vTexCoord0; TEX1 = texGenTest * gl_Position; TEX1.y = -TEX1.y; diff --git a/Templates/Empty/game/shaders/common/gl/precipP.glsl b/Templates/Empty/game/shaders/common/gl/precipP.glsl index a04f16e4b..3c669517d 100644 --- a/Templates/Empty/game/shaders/common/gl/precipP.glsl +++ b/Templates/Empty/game/shaders/common/gl/precipP.glsl @@ -25,13 +25,13 @@ //----------------------------------------------------------------------------- uniform sampler2D diffuseMap; -varying vec4 color; -varying vec2 texCoord; +in vec4 color; +in vec2 texCoord; //----------------------------------------------------------------------------- // Main //----------------------------------------------------------------------------- void main() { - gl_FragColor = texture2D(diffuseMap, texCoord) * color; + OUT_FragColor0 = texture(diffuseMap, texCoord) * color; } diff --git a/Templates/Empty/game/shaders/common/gl/precipV.glsl b/Templates/Empty/game/shaders/common/gl/precipV.glsl index 3535f2f38..29f921630 100644 --- a/Templates/Empty/game/shaders/common/gl/precipV.glsl +++ b/Templates/Empty/game/shaders/common/gl/precipV.glsl @@ -23,28 +23,32 @@ //----------------------------------------------------------------------------- // Data //----------------------------------------------------------------------------- +in vec4 vPosition; +in vec2 vTexCoord0; + uniform mat4 modelview; uniform vec3 cameraPos, ambient; uniform vec2 fadeStartEnd; -varying vec4 color; -varying vec2 texCoord; +out vec4 color; +out vec2 texCoord; //----------------------------------------------------------------------------- // Main //----------------------------------------------------------------------------- void main() { - gl_Position = modelview * gl_Vertex; - texCoord = gl_MultiTexCoord0.st; + gl_Position = modelview * vPosition; + texCoord = vTexCoord0.st; color = vec4( ambient.r, ambient.g, ambient.b, 1.0 ); // Do we need to do a distance fade? if ( fadeStartEnd.x < fadeStartEnd.y ) { - float distance = length( cameraPos - gl_Vertex.xyz ); + float distance = length( cameraPos - vPosition.xyz ); color.a = abs( clamp( ( distance - fadeStartEnd.x ) / ( fadeStartEnd.y - fadeStartEnd.x ), 0.0, 1.0 ) - 1.0 ); } + gl_Position.y *= -1; } diff --git a/Templates/Empty/game/shaders/common/gl/projectedShadowP.glsl b/Templates/Empty/game/shaders/common/gl/projectedShadowP.glsl index 1dc963a4f..8ce0fba13 100644 --- a/Templates/Empty/game/shaders/common/gl/projectedShadowP.glsl +++ b/Templates/Empty/game/shaders/common/gl/projectedShadowP.glsl @@ -20,9 +20,11 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -varying vec2 texCoord; -varying vec4 color; -varying float fade; +in vec2 texCoord; +in vec4 color; +in float fade; + +out vec4 OUT_FragColor0; uniform sampler2D inputTex; uniform vec4 ambient; @@ -30,17 +32,6 @@ uniform vec4 ambient; void main() { - vec3 LUMINANCE_VECTOR = vec3(0.2125f, 0.4154f, 0.1721f); - float esmFactor = 200.0; - - float lum = dot( ambient.rgb, LUMINANCE_VECTOR ); - - gl_FragColor.rgb = ambient.rgb * lum; - gl_FragColor.a = 0.0; - float depth = texture2D(inputTex, texCoord).a; - - depth = depth * exp(depth - 10.0); - depth = exp(esmFactor * depth) - 1.0; - - gl_FragColor.a = clamp(depth * 300.0, 0.0, 1.0) * (1.0 - lum) * fade * color.a; + float shadow = texture( inputTex, texCoord ).a * color.a; + OUT_FragColor0 = ( ambient * shadow ) + ( 1 - shadow ); } diff --git a/Templates/Empty/game/shaders/common/gl/projectedShadowV.glsl b/Templates/Empty/game/shaders/common/gl/projectedShadowV.glsl index c8abde742..b5de84181 100644 --- a/Templates/Empty/game/shaders/common/gl/projectedShadowV.glsl +++ b/Templates/Empty/game/shaders/common/gl/projectedShadowV.glsl @@ -20,13 +20,16 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -//***************************************************************************** -// Precipitation vertex shader -//***************************************************************************** +#include "hlslCompat.glsl" -varying vec2 texCoord; -varying vec4 color; -varying float fade; +in vec4 vPosition; +in vec4 vColor; +in vec2 vTexCoord0; +in vec2 vTexCoord1; + +out vec2 texCoord; +out vec4 color; +out float fade; uniform mat4 modelview; uniform float shadowLength; @@ -34,11 +37,13 @@ uniform vec3 shadowCasterPosition; void main() { - gl_Position = modelview * vec4(gl_Vertex.xyz, 1.0); + gl_Position = modelview * vec4(vPosition.xyz, 1.0); - color = gl_Color; - texCoord = gl_MultiTexCoord1.st; + color = vColor; + texCoord = vTexCoord1.st; - float fromCasterDist = length(gl_Vertex.xyz - shadowCasterPosition) - shadowLength; - fade = 1.0 - clamp(fromCasterDist/shadowLength, 0.0, 1.0); + float fromCasterDist = length(vPosition.xyz - shadowCasterPosition) - shadowLength; + fade = 1.0 - clamp( fromCasterDist / shadowLength , 0.0, 1.0 ); + + correctSSP(gl_Position); } diff --git a/Templates/Empty/game/shaders/common/gl/scatterSkyP.glsl b/Templates/Empty/game/shaders/common/gl/scatterSkyP.glsl index 6a6b9b97c..691567a37 100644 --- a/Templates/Empty/game/shaders/common/gl/scatterSkyP.glsl +++ b/Templates/Empty/game/shaders/common/gl/scatterSkyP.glsl @@ -21,36 +21,32 @@ //----------------------------------------------------------------------------- #include "torque.glsl" +#include "hlslCompat.glsl" -// Calculates the Mie phase function -float getMiePhase(float fCos, float fCos2, float g, float g2) -{ - return 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos2) / pow(abs(1.0 + g2 - 2.0*g*fCos), 1.5); -} -// Calculates the Rayleigh phase function -float getRayleighPhase(float fCos2) -{ - //return 1.0; - return 0.75 + 0.75*fCos2; -} +// Conn +in vec4 rayleighColor; +#define IN_rayleighColor rayleighColor +in vec4 mieColor; +#define IN_mieColor mieColor +in vec3 v3Direction; +#define IN_v3Direction v3Direction +in float zPosition; +#define IN_zPosition zPosition +in vec3 pos; +#define IN_pos pos -varying vec4 rayleighColor; -varying vec4 mieColor; -varying vec3 v3Direction; -varying float zPosition; -varying vec3 pos; - -uniform samplerCube nightSky; +uniform samplerCube nightSky ; uniform vec4 nightColor; uniform vec2 nightInterpAndExposure; uniform float useCubemap; uniform vec3 lightDir; uniform vec3 sunDir; -void main() +void main() { - float fCos = dot( lightDir, v3Direction ) / length(v3Direction); + + float fCos = dot( lightDir, IN_v3Direction ) / length(IN_v3Direction); float fCos2 = fCos*fCos; float g = -0.991; @@ -58,15 +54,15 @@ void main() float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos2) / pow(abs(1.0 + g2 - 2.0*g*fCos), 1.5); - vec4 color = rayleighColor + fMiePhase * mieColor; + vec4 color = IN_rayleighColor + fMiePhase * IN_mieColor; color.a = color.b; - vec4 nightSkyColor = textureCube(nightSky, -v3Direction); + vec4 nightSkyColor = texture(nightSky, -v3Direction); nightSkyColor = mix(nightColor, nightSkyColor, useCubemap); float fac = dot( normalize( pos ), sunDir ); fac = max( nightInterpAndExposure.y, pow( clamp( fac, 0.0, 1.0 ), 2 ) ); - gl_FragColor = mix( color, nightSkyColor, nightInterpAndExposure.y ); + OUT_FragColor0 = mix( color, nightSkyColor, nightInterpAndExposure.y ); // Clip based on the camera-relative // z position of the vertex, passed through @@ -74,6 +70,6 @@ void main() if(zPosition < 0.0) discard; - gl_FragColor.a = 1; - gl_FragColor = hdrEncode( gl_FragColor ); + OUT_FragColor0.a = 1; + OUT_FragColor0 = hdrEncode( OUT_FragColor0 ); } diff --git a/Templates/Empty/game/shaders/common/gl/scatterSkyV.glsl b/Templates/Empty/game/shaders/common/gl/scatterSkyV.glsl index 53abd5a6b..61580d785 100644 --- a/Templates/Empty/game/shaders/common/gl/scatterSkyV.glsl +++ b/Templates/Empty/game/shaders/common/gl/scatterSkyV.glsl @@ -20,12 +20,7 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -const int nSamples = 4; -const float fSamples = 4.0; - -// The scale depth (the altitude at which the average atmospheric density is found) -const float fScaleDepth = 0.25; -const float fInvScaleDepth = 1.0 / 0.25; +#include "hlslCompat.glsl" // The scale equation calculated by Vernier's Graphical Analysis float vernierScale(float fCos) @@ -40,12 +35,27 @@ float vernierScale(float fCos) return 0.25 * outx; } +in vec4 vPosition; +in vec3 vNormal; +in vec4 vColor; +in vec2 vTexCoord0; + +// This is the shader input vertex structure. +#define IN_position vPosition +#define IN_normal vNormal +#define IN_color vColor + // This is the shader output data. -varying vec4 rayleighColor; -varying vec4 mieColor; -varying vec3 v3Direction; -varying float zPosition; -varying vec3 pos; +out vec4 rayleighColor; +#define OUT_rayleighColor rayleighColor +out vec4 mieColor; +#define OUT_mieColor mieColor +out vec3 v3Direction; +#define OUT_v3Direction v3Direction +out float zPosition; +#define OUT_zPosition zPosition +out vec3 pos; +#define OUT_pos pos uniform mat4 modelView; uniform vec4 misc; @@ -54,13 +64,16 @@ uniform vec4 scatteringCoeffs; uniform vec3 camPos; uniform vec3 lightDir; uniform vec4 invWaveLength; - -void main() -{ - vec4 position = gl_Vertex.xyzw; - vec3 normal = gl_Normal.xyz; - vec4 color = gl_MultiTexCoord0.xyzw; +uniform vec4 colorize; +vec3 desaturate(const vec3 color, const float desaturation) +{ + const vec3 gray_conv = vec3 (0.30, 0.59, 0.11); + return mix(color, vec3(dot(gray_conv , color)), desaturation); +} + +void main() +{ // Pull some variables out: float camHeight = misc.x; float camHeightSqr = misc.y; @@ -83,7 +96,7 @@ void main() // Get the ray from the camera to the vertex, // and its length (which is the far point of the ray // passing through the atmosphere). - vec3 v3Pos = position.xyz / 6378000.0;// / outerRadius; + vec3 v3Pos = vec3(IN_position / 6378000.0);// / outerRadius; vec3 newCamPos = vec3( 0, 0, camHeight ); v3Pos.z += innerRadius; vec3 v3Ray = v3Pos.xyz - newCamPos; @@ -97,16 +110,7 @@ void main() float fDepth = exp(scaleOverScaleDepth * (innerRadius - camHeight)); float fStartAngle = dot(v3Ray, v3Start) / fHeight; - float x = 1.0 - fStartAngle; - float x5 = x * 5.25; - float x5p6 = (-6.80 + x5); - float xnew = (3.83 + x * x5p6); - float xfinal = (0.459 + x * xnew); - float xfinal2 = -0.00287 + x * xfinal; - float othx = exp( xfinal2 ); - float vscale1 = 0.25 * othx; - - float fStartOffset = fDepth * vscale1;//vernierScale(fStartAngle); + float fStartOffset = fDepth * vernierScale( fStartAngle ); // Initialize the scattering loop variables. float fSampleLength = fFar / 2.0; @@ -123,24 +127,8 @@ void main() float fLightAngle = dot(lightDir, v3SamplePoint) / fHeight; float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight; - x = 1.0 - fCameraAngle; - x5 = x * 5.25; - x5p6 = (-6.80 + x5); - xnew = (3.83 + x * x5p6); - xfinal = (0.459 + x * xnew); - xfinal2 = -0.00287 + x * xfinal; - othx = exp( xfinal2 ); - float vscale3 = 0.25 * othx; - - - x = 1.0 - fLightAngle; - x5 = x * 5.25; - x5p6 = (-6.80 + x5); - xnew = (3.83 + x * x5p6); - xfinal = (0.459 + x * xnew); - xfinal2 = -0.00287 + x * xfinal; - othx = exp( xfinal2 ); - float vscale2 = 0.25 * othx; + float vscale3 = vernierScale( fCameraAngle ); + float vscale2 = vernierScale( fLightAngle ); float fScatter = (fStartOffset + fDepth*(vscale2 - vscale3)); vec3 v3Attenuate = exp(-fScatter * (invWaveLength.xyz * rayleigh4PI + mie4PI)); @@ -150,16 +138,24 @@ void main() // Finally, scale the Mie and Rayleigh colors // and set up the varying variables for the pixel shader. - gl_Position = modelView * position; - mieColor.rgb = v3FrontColor * mieBrightness; - mieColor.a = 1.0; - rayleighColor.rgb = v3FrontColor * (invWaveLength.xyz * rayleighBrightness); - rayleighColor.a = 1.0; - v3Direction = newCamPos - v3Pos.xyz; + gl_Position = modelView * IN_position; + OUT_mieColor.rgb = v3FrontColor * mieBrightness; + OUT_mieColor.a = 1.0; + OUT_rayleighColor.rgb = v3FrontColor * (invWaveLength.xyz * rayleighBrightness); + OUT_rayleighColor.a = 1.0; + OUT_v3Direction = newCamPos - v3Pos.xyz; + OUT_pos = IN_position.xyz; - // This offset is to get rid of the black line between the atmosky and the waterPlane - // along the horizon. - zPosition = position.z + 4000.0; - pos = position.xyz; +#ifdef USE_COLORIZE + + OUT_rayleighColor.rgb = desaturate(OUT_rayleighColor.rgb, 1) * colorize.a; + + OUT_rayleighColor.r *= colorize.r; + OUT_rayleighColor.g *= colorize.g; + OUT_rayleighColor.b *= colorize.b; + +#endif + + correctSSP(gl_Position); } diff --git a/Templates/Empty/game/shaders/common/gl/torque.glsl b/Templates/Empty/game/shaders/common/gl/torque.glsl index dc73a706f..a98ef859f 100644 --- a/Templates/Empty/game/shaders/common/gl/torque.glsl +++ b/Templates/Empty/game/shaders/common/gl/torque.glsl @@ -117,6 +117,7 @@ mat3x3 quatToMat( vec4 quat ) return mat; } + /// The number of additional substeps we take when refining /// the results of the offset parallax mapping function below. /// @@ -129,19 +130,20 @@ mat3x3 quatToMat( vec4 quat ) /// Performs fast parallax offset mapping using /// multiple refinement steps. -////// @param texMap The texture map whos alpha channel we sample the parallax depth. +/// +/// @param texMap The texture map whos alpha channel we sample the parallax depth. /// @param texCoord The incoming texture coordinate for sampling the parallax depth. /// @param negViewTS The negative view vector in tangent space. /// @param depthScale The parallax factor used to scale the depth result. /// vec2 parallaxOffset( sampler2D texMap, vec2 texCoord, vec3 negViewTS, float depthScale ) { - float depth = texture2D( texMap, texCoord ).a; + float depth = texture( texMap, texCoord ).a; vec2 offset = negViewTS.xy * ( depth * depthScale ); for ( int i=0; i < PARALLAX_REFINE_STEPS; i++ ) { - depth = ( depth + texture2D( texMap, texCoord + offset ).a ) * 0.5; + depth = ( depth + texture( texMap, texCoord + offset ).a ) * 0.5; offset = negViewTS.xy * ( depth * depthScale ); } @@ -151,59 +153,61 @@ vec2 parallaxOffset( sampler2D texMap, vec2 texCoord, vec3 negViewTS, float dept /// The maximum value for 16bit per component integer HDR encoding. const float HDR_RGB16_MAX = 100.0; -/// The maximum value for 10bit per component integer HDR encoding.const float HDR_RGB10_MAX = 4.0; +/// The maximum value for 10bit per component integer HDR encoding. +const float HDR_RGB10_MAX = 4.0; /// Encodes an HDR color for storage into a target. -vec3 hdrEncode( vec3 sample ){ +vec3 hdrEncode( vec3 _sample ) +{ #if defined( TORQUE_HDR_RGB16 ) - return sample / HDR_RGB16_MAX; + return _sample / HDR_RGB16_MAX; #elif defined( TORQUE_HDR_RGB10 ) - return sample / HDR_RGB10_MAX; + return _sample / HDR_RGB10_MAX; #else // No encoding. - return sample; + return _sample; #endif } /// Encodes an HDR color for storage into a target. -vec4 hdrEncode( vec4 sample ) +vec4 hdrEncode( vec4 _sample ) { - return vec4( hdrEncode( sample.rgb ), sample.a ); + return vec4( hdrEncode( _sample.rgb ), _sample.a ); } /// Decodes an HDR color from a target. -vec3 hdrDecode( vec3 sample ) +vec3 hdrDecode( vec3 _sample ) { #if defined( TORQUE_HDR_RGB16 ) - return sample * HDR_RGB16_MAX; + return _sample * HDR_RGB16_MAX; #elif defined( TORQUE_HDR_RGB10 ) - return sample * HDR_RGB10_MAX; + return _sample * HDR_RGB10_MAX; #else // No encoding. - return sample; + return _sample; #endif } /// Decodes an HDR color from a target. -vec4 hdrDecode( vec4 sample ) +vec4 hdrDecode( vec4 _sample ) { - return vec4( hdrDecode( sample.rgb ), sample.a ); + return vec4( hdrDecode( _sample.rgb ), _sample.a ); } /// Returns the luminance for an HDR pixel. -float hdrLuminance( vec3 sample ) +float hdrLuminance( vec3 _sample ) { // There are quite a few different ways to // calculate luminance from an rgb value. @@ -216,7 +220,7 @@ float hdrLuminance( vec3 sample ) // // Max component luminance. // - //float lum = max( sample.r, max( sample.g, sample.b ) ); + //float lum = max( _sample.r, max( _sample.g, _sample.b ) ); //////////////////////////////////////////////////////////////////////////// // The perceptual relative luminance. @@ -224,23 +228,45 @@ float hdrLuminance( vec3 sample ) // See http://en.wikipedia.org/wiki/Luminance_(relative) // const vec3 RELATIVE_LUMINANCE = vec3( 0.2126, 0.7152, 0.0722 ); - float lum = dot( sample, RELATIVE_LUMINANCE ); + float lum = dot( _sample, RELATIVE_LUMINANCE ); //////////////////////////////////////////////////////////////////////////// // // The average component luminance. // //const vec3 AVERAGE_LUMINANCE = vec3( 0.3333, 0.3333, 0.3333 ); - //float lum = dot( sample, AVERAGE_LUMINANCE ); + //float lum = dot( _sample, AVERAGE_LUMINANCE ); return lum; } +#ifdef TORQUE_PIXEL_SHADER +/// Called from the visibility feature to do screen +/// door transparency for fading of objects. +void fizzle(vec2 vpos, float visibility) +{ + // NOTE: The magic values below are what give us + // the nice even pattern during the fizzle. + // + // These values can be changed to get different + // patterns... some better than others. + // + // Horizontal Blinds - { vpos.x, 0.916, vpos.y, 0 } + // Vertical Lines - { vpos.x, 12.9898, vpos.y, 78.233 } + // + // I'm sure there are many more patterns here to + // discover for different effects. + + mat2x2 m = mat2x2( vpos.x, vpos.y, 0.916, 0.350 ); + if( (visibility - fract( determinant( m ) )) < 0 ) //if(a < 0) discard; + discard; +} +#endif //TORQUE_PIXEL_SHADER /// Basic assert macro. If the condition fails, then the shader will output color. /// @param condition This should be a bvec[2-4]. If any items is false, condition is considered to fail. /// @param color The color that should be outputted if the condition fails. /// @note This macro will only work in the void main() method of a pixel shader. -#define assert(condition, color) { if(!any(condition)) { gl_FragColor = color; return; } } +#define assert(condition, color) { if(!any(condition)) { OUT_FragColor0 = color; return; } } #endif // _TORQUE_GLSL_ diff --git a/Templates/Empty/game/shaders/common/gl/wavesP.glsl b/Templates/Empty/game/shaders/common/gl/wavesP.glsl index 24bd8cbb4..ddb683947 100644 --- a/Templates/Empty/game/shaders/common/gl/wavesP.glsl +++ b/Templates/Empty/game/shaders/common/gl/wavesP.glsl @@ -28,10 +28,10 @@ uniform float specularPower; uniform vec4 ambient; uniform float accumTime; -varying vec2 TEX0; -varying vec4 outLightVec; -varying vec3 outPos; -varying vec3 outEyePos; +in vec2 TEX0; +in vec4 outLightVec; +in vec3 outPos; +in vec3 outEyePos; void main() { @@ -42,14 +42,14 @@ void main() texOffset.x = TEX0.x + sinOffset1 + sinOffset2; texOffset.y = TEX0.y + cos( accumTime * 3.0 + TEX0.x * 6.28319 * 2.0 ) * 0.05; - vec4 bumpNorm = texture2D(bumpMap, texOffset) * 2.0 - 1.0; - vec4 diffuse = texture2D(diffMap, texOffset); + vec4 bumpNorm = texture(bumpMap, texOffset) * 2.0 - 1.0; + vec4 diffuse = texture(diffMap, texOffset); - gl_FragColor = diffuse * (clamp(dot(outLightVec.xyz, bumpNorm.xyz), 0.0, 1.0) + ambient); + OUT_FragColor0 = diffuse * (clamp(dot(outLightVec.xyz, bumpNorm.xyz), 0.0, 1.0) + ambient); vec3 eyeVec = normalize(outEyePos - outPos); vec3 halfAng = normalize(eyeVec + outLightVec.xyz); float specular = clamp(dot(bumpNorm.xyz, halfAng), 0.0, 1.0) * outLightVec.w; specular = pow(specular, specularPower); - gl_FragColor += specularColor * specular; + OUT_FragColor0 += specularColor * specular; } diff --git a/Templates/Empty/game/shaders/common/lighting/advanced/gl/convexGeometryV.glsl b/Templates/Empty/game/shaders/common/lighting/advanced/gl/convexGeometryV.glsl index 6d3e36e7e..1807ac43f 100644 --- a/Templates/Empty/game/shaders/common/lighting/advanced/gl/convexGeometryV.glsl +++ b/Templates/Empty/game/shaders/common/lighting/advanced/gl/convexGeometryV.glsl @@ -20,16 +20,33 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -varying vec4 wsEyeDir; -varying vec4 ssPos; +#include "../../../gl/hlslCompat.glsl" + +in vec4 vPosition; + +#define IN_pos vPosition + +out vec4 wsEyeDir; +out vec4 ssPos; +out vec4 vsEyeDir; + +#define OUT_hpos gl_Position +#define OUT_wsEyeDir wsEyeDir +#define OUT_ssPos ssPos +#define OUT_vsEyeDir vsEyeDir uniform mat4 modelview; uniform mat4 objTrans; +uniform mat4 worldViewOnly; uniform vec3 eyePosWorld; void main() { - gl_Position = modelview * gl_Vertex; - wsEyeDir = objTrans * gl_Vertex - vec4( eyePosWorld, 0.0 ); - ssPos = gl_Position; + OUT_hpos = tMul( modelview, IN_pos ); + OUT_wsEyeDir = tMul( objTrans, IN_pos ) - vec4( eyePosWorld, 0.0 ); + OUT_vsEyeDir = tMul( worldViewOnly, IN_pos ); + OUT_ssPos = OUT_hpos; + + correctSSP(gl_Position); } + diff --git a/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgDepthVisualizeP.glsl b/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgDepthVisualizeP.glsl index 4a954ae84..e3ee59c8a 100644 --- a/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgDepthVisualizeP.glsl +++ b/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgDepthVisualizeP.glsl @@ -20,14 +20,15 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" #include "shadergen:/autogenConditioners.h" -varying vec2 uv0; +in vec2 uv0; uniform sampler2D prepassBuffer; uniform sampler1D depthViz; void main() { float depth = prepassUncondition( prepassBuffer, uv0 ).w; - gl_FragColor = vec4( texture1D( depthViz, depth ).rgb, 1 ); + OUT_FragColor0 = vec4( texture( depthViz, depth ).rgb, 1.0 ); } \ No newline at end of file diff --git a/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl b/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl index 580204487..aa17df4d6 100644 --- a/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl +++ b/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl @@ -20,15 +20,16 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" #include "shadergen:/autogenConditioners.h" -varying vec2 uv0; +in vec2 uv0; uniform sampler2D lightInfoBuffer; void main() { - vec3 lightcolor; + vec3 lightcolor; float nl_Att, specular; - lightinfoUncondition( texture2DLod( lightInfoBuffer, uv0 ), lightcolor, nl_Att, specular ); - gl_FragColor = vec4( lightcolor, 1.0 ); + lightinfoUncondition( texture( lightInfoBuffer, uv0 ), lightcolor, nl_Att, specular ); + OUT_FragColor0 = vec4( lightcolor, 1.0 ); } \ No newline at end of file diff --git a/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl b/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl index 6f7c52486..e549922ff 100644 --- a/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl +++ b/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl @@ -20,15 +20,16 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" #include "shadergen:/autogenConditioners.h" -varying vec2 uv0; +in vec2 uv0; uniform sampler2D lightInfoBuffer; void main() { - vec3 lightcolor; + vec3 lightcolor; float nl_Att, specular; - lightinfoUncondition( texture2DLod( lightInfoBuffer, uv0 ), lightcolor, nl_Att, specular ); - gl_FragColor = vec4( specular, specular, specular, 1.0 ); + lightinfoUncondition( texture( lightInfoBuffer, uv0 ), lightcolor, nl_Att, specular ); + OUT_FragColor0 = vec4( specular, specular, specular, 1.0 ); } \ No newline at end of file diff --git a/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgNormalVisualizeP.glsl b/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgNormalVisualizeP.glsl index 96b645524..f61706393 100644 --- a/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgNormalVisualizeP.glsl +++ b/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgNormalVisualizeP.glsl @@ -20,14 +20,14 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" #include "shadergen:/autogenConditioners.h" - -varying vec2 uv0; -uniform sampler2D prepassTex; +in vec2 uv0; +uniform sampler2D prepassBuffer; void main() { - vec3 normal = prepassUncondition( prepassTex, uv0 ).xyz; - gl_FragColor = vec4( ( normal + 1.0 ) * 0.5, 1.0 ); + vec3 normal = prepassUncondition( prepassBuffer, uv0 ).xyz; + OUT_FragColor0 = vec4( ( normal + 1.0 ) * 0.5, 1.0 ); } \ No newline at end of file diff --git a/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgShadowVisualizeP.glsl b/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgShadowVisualizeP.glsl index 51609fc6b..e997950ab 100644 --- a/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgShadowVisualizeP.glsl +++ b/Templates/Empty/game/shaders/common/lighting/advanced/gl/dbgShadowVisualizeP.glsl @@ -19,13 +19,14 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" -varying vec2 uv0; +in vec2 uv0; uniform sampler2D shadowMap; uniform sampler1D depthViz; void main() { - float depth = clamp( texture2DLod( shadowMap, uv0, 0 ).r, 0.0, 1.0 ); - gl_FragColor = vec4( texture1D( depthViz, depth ).rgb, 1.0 ); + float depth = saturate( texture( shadowMap, uv0 ).r ); + OUT_FragColor0 = vec4( texture( depthViz, depth ).rgb, 1 ); } \ No newline at end of file diff --git a/Templates/Empty/game/shaders/common/lighting/advanced/gl/farFrustumQuad.glsl b/Templates/Empty/game/shaders/common/lighting/advanced/gl/farFrustumQuad.glsl index 866a39b0b..76054eb09 100644 --- a/Templates/Empty/game/shaders/common/lighting/advanced/gl/farFrustumQuad.glsl +++ b/Templates/Empty/game/shaders/common/lighting/advanced/gl/farFrustumQuad.glsl @@ -24,7 +24,7 @@ vec2 getUVFromSSPos( vec3 ssPos, vec4 rtParams ) { vec2 outPos = ( ssPos.xy + 1.0 ) / 2.0; + outPos.y = 1.0 - outPos.y; outPos = ( outPos * rtParams.zw ) + rtParams.xy; - //outPos.y = 1.0 - outPos.y; return outPos; } diff --git a/Templates/Empty/game/shaders/common/lighting/advanced/gl/farFrustumQuadV.glsl b/Templates/Empty/game/shaders/common/lighting/advanced/gl/farFrustumQuadV.glsl index ce5c2ad81..a80e856ed 100644 --- a/Templates/Empty/game/shaders/common/lighting/advanced/gl/farFrustumQuadV.glsl +++ b/Templates/Empty/game/shaders/common/lighting/advanced/gl/farFrustumQuadV.glsl @@ -20,24 +20,32 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" #include "farFrustumQuad.glsl" -uniform vec4 renderTargetParams; - -varying vec4 hpos; -varying vec2 uv0; -varying vec3 wsEyeRay; +in vec4 vPosition; +in vec3 vNormal; +in vec3 vTangent; +in vec2 vTexCoord0; +uniform vec4 rtParams0; +out vec4 hpos; +out vec2 uv0; +out vec3 wsEyeRay; +out vec3 vsEyeRay; void main() -{ - // Expand the SS coordinate (stored in uv0) - hpos = vec4( gl_MultiTexCoord0.st * 2.0 - 1.0, 1.0, 1.0 ); - gl_Position = hpos; - +{ + hpos = vec4( vTexCoord0, 0, 1 ); + // Get a RT-corrected UV from the SS coord - uv0 = getUVFromSSPos( hpos.xyz, renderTargetParams ); + uv0 = getUVFromSSPos( hpos.xyz, rtParams0 ); + gl_Position = hpos; - // Interpolators will generate eye ray from far-frustum corners - wsEyeRay = gl_Vertex.xyz; + // Interpolators will generate eye rays the + // from far-frustum corners. + wsEyeRay = vTangent; + vsEyeRay = vNormal; + + correctSSP(gl_Position); } diff --git a/Templates/Empty/game/shaders/common/lighting/advanced/gl/pointLightP.glsl b/Templates/Empty/game/shaders/common/lighting/advanced/gl/pointLightP.glsl index b135f1aa8..c0610508b 100644 --- a/Templates/Empty/game/shaders/common/lighting/advanced/gl/pointLightP.glsl +++ b/Templates/Empty/game/shaders/common/lighting/advanced/gl/pointLightP.glsl @@ -21,34 +21,26 @@ //----------------------------------------------------------------------------- #include "../../../gl/hlslCompat.glsl" -#include "farFrustumQuad.glsl" -#include "lightingUtils.glsl" -#include "../../shadowMap/shadowMapIO_GLSL.h" #include "shadergen:/autogenConditioners.h" +#include "farFrustumQuad.glsl" +#include "lightingUtils.glsl" +#include "../../../gl/lighting.glsl" +#include "../../shadowMap/shadowMapIO_GLSL.h" +#include "softShadow.glsl" -#if TORQUE_SM >= 30 +in vec4 wsEyeDir; +in vec4 ssPos; +in vec4 vsEyeDir; - // Enables high quality soft shadow - // filtering for SM3.0 and above. - #define SOFTSHADOW_SM3 - - #include "softShadow.glsl" - -#else +#ifdef USE_COOKIE_TEX +/// The texture for cookie rendering. +uniform samplerCube cookieMap ; #endif - -// I am not sure if we should do this in a better way -//#define SHADOW_CUBE -//#define SHADOW_PARABOLOID -#define SHADOW_DUALPARABOLOID -#define SHADOW_DUALPARABOLOID_SINGLE_PASS - - #ifdef SHADOW_CUBE vec3 decodeShadowCoord( vec3 shadowCoord ) @@ -56,39 +48,47 @@ return shadowCoord; } - vec4 shadowSample( samplerCUBE shadowMap, vec3 shadowCoord ) + vec4 shadowSample( samplerCube shadowMap, vec3 shadowCoord ) { - return textureCUBE( shadowMap, shadowCoord ); + return texture( shadowMap, shadowCoord ); } - -#elif defined( SHADOW_DUALPARABOLOID ) + +#else vec3 decodeShadowCoord( vec3 paraVec ) { - // Swizzle z and y + // Flip y and z paraVec = paraVec.xzy; - #ifdef SHADOW_DUALPARABOLOID_SINGLE_PASS + #ifndef SHADOW_PARABOLOID bool calcBack = (paraVec.z < 0.0); - if(calcBack) + if ( calcBack ) + { paraVec.z = paraVec.z * -1.0; + + #ifdef SHADOW_DUALPARABOLOID + paraVec.x = -paraVec.x; + #endif + } #endif vec3 shadowCoord; - shadowCoord.x = (paraVec.x / (2.0*(1.0 + paraVec.z))) + 0.5; - shadowCoord.y = ((paraVec.y / (2.0*(1.0 + paraVec.z))) + 0.5); + shadowCoord.x = (paraVec.x / (2*(1 + paraVec.z))) + 0.5; + shadowCoord.y = 1-((paraVec.y / (2*(1 + paraVec.z))) + 0.5); shadowCoord.z = 0; // adjust the co-ordinate slightly if it is near the extent of the paraboloid // this value was found via experementation - shadowCoord.xy *= 0.997; + // NOTE: this is wrong, it only biases in one direction, not towards the uv + // center ( 0.5 0.5 ). + //shadowCoord.xy *= 0.997; - #ifdef SHADOW_DUALPARABOLOID_SINGLE_PASS + #ifndef SHADOW_PARABOLOID // If this is the back, offset in the atlas - if(calcBack) + if ( calcBack ) shadowCoord.x += 1.0; // Atlasing front and back maps, so scale @@ -99,51 +99,35 @@ return shadowCoord; } -#else - - #error Unknown shadow type! - #endif -varying vec4 wsEyeDir; -varying vec4 ssPos; - - uniform sampler2D prePassBuffer; #ifdef SHADOW_CUBE - uniform samplerCube shadowMap; + uniform samplerCube shadowMap; #else - uniform sampler2D shadowMap; -#endif -#ifdef ACCUMULATE_LUV - uniform sampler2D scratchTarget; + uniform sampler2D shadowMap; #endif -uniform vec4 renderTargetParams; +uniform vec4 rtParams0; uniform vec3 lightPosition; uniform vec4 lightColor; -uniform float lightBrightness; -uniform float lightRange; +uniform float lightBrightness; +uniform float lightRange; uniform vec2 lightAttenuation; uniform vec4 lightMapParams; - -uniform vec3 eyePosWorld; -uniform vec4 farPlane; -uniform float negFarPlaneDotEye; -uniform mat3x3 worldToLightProj; - +uniform vec4 vsFarPlane; +uniform mat3 viewToLightProj; uniform vec4 lightParams; uniform float shadowSoftness; -uniform float constantSpecularPower; + - -void main() -{ +void main() +{ // Compute scene UV - vec3 ssPosP = ssPos.xyz / ssPos.w; - vec2 uvScene = getUVFromSSPos( ssPosP, renderTargetParams ); + vec3 ssPos = ssPos.xyz / ssPos.w; + vec2 uvScene = getUVFromSSPos( ssPos, rtParams0 ); // Sample/unpack the normal/z data vec4 prepassSample = prepassUncondition( prePassBuffer, uvScene ); @@ -151,21 +135,17 @@ void main() float depth = prepassSample.a; // Eye ray - Eye -> Pixel - vec3 eyeRay = getDistanceVectorToPlane( negFarPlaneDotEye, wsEyeDir.xyz / wsEyeDir.w , farPlane ); - - // Get world space pixel position - vec3 worldPos = eyePosWorld + eyeRay * depth; + vec3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, vsEyeDir.xyz, vsFarPlane ); + vec3 viewSpacePos = eyeRay * depth; // Build light vec, get length, clip pixel if needed - vec3 lightVec = lightPosition - worldPos; + vec3 lightVec = lightPosition - viewSpacePos; float lenLightV = length( lightVec ); - if ( lightRange - lenLightV < 0.0 ) - discard; - + clip( lightRange - lenLightV ); + // Get the attenuated falloff. float atten = attenuate( lightColor, lightAttenuation, lenLightV ); - if ( atten - 1e-6 < 0.0 ) - discard; + clip( atten - 1e-6 ); // Normalize lightVec lightVec /= lenLightV; @@ -181,61 +161,73 @@ void main() #else - // Convert the light vector into a shadow map - // here once instead of in the filtering loop. - vec4 shadowCoord = vec4(0.0); - #ifdef SHADOW_CUBE - shadowCoord.xy = decodeShadowCoord( -lightVec ); - #else - shadowCoord.xy = decodeShadowCoord( worldToLightProj * -lightVec ).xy; - #endif - // Get a linear depth from the light source. - float distToLight = lenLightV / lightRange; + float distToLight = lenLightV / lightRange; - #ifdef SOFTSHADOW_SM3 + #ifdef SHADOW_CUBE + + // TODO: We need to fix shadow cube to handle soft shadows! + float occ = texture( shadowMap, tMul( viewToLightProj, -lightVec ) ).r; + float shadowed = saturate( exp( lightParams.y * ( occ - distToLight ) ) ); + + #else + vec2 shadowCoord = decodeShadowCoord( tMul( viewToLightProj, -lightVec ) ).xy; + float shadowed = softShadow_filter( shadowMap, - gTapRotationTex, - ssPosP.xy, - shadowCoord.xy, + ssPos.xy, + shadowCoord, shadowSoftness, distToLight, nDotL, lightParams.y ); - - #else // !SOFTSHADOW_SM3 - - // TODO: Implement the SM2 lower quality - // shadow filtering method. #endif #endif // !NO_SHADOW - + + #ifdef USE_COOKIE_TEX + + // Lookup the cookie sample. + vec4 cookie = texture( cookieMap, tMul( viewToLightProj, -lightVec ) ); + + // Multiply the light with the cookie tex. + lightColor.rgb *= cookie.rgb; + + // Use a maximum channel luminance to attenuate + // the lighting else we get specular in the dark + // regions of the cookie texture. + atten *= max( cookie.r, max( cookie.g, cookie.b ) ); + + #endif + // NOTE: Do not clip on fully shadowed pixels as it would // cause the hardware occlusion query to disable the shadow. // Specular term - float specular = calcSpecular( lightVec, - normal, - normalize( -eyeRay ), - constantSpecularPower, - shadowed * atten * lightBrightness ); - - // N.L * Attenuation - float Sat_NL_Att = clamp( nDotL * atten * shadowed, 0.0, 1.0 ); - - // In LUV color mode we need to blend in the - // output from the previous target. - vec4 previousPix = vec4(0.0); - #ifdef ACCUMULATE_LUV - previousPix = texture2DLod( scratchTarget, uvScene, 0 ); - #endif + float specular = AL_CalcSpecular( lightVec, + normal, + normalize( -eyeRay ) ) * lightBrightness * atten * shadowed; - // Output - gl_FragColor = lightinfoCondition( lightColor.rgb * lightBrightness, - Sat_NL_Att, - specular, - previousPix ) * lightMapParams; + float Sat_NL_Att = saturate( nDotL * atten * shadowed ) * lightBrightness; + vec3 lightColorOut = lightMapParams.rgb * lightColor.rgb; + vec4 addToResult = vec4(0.0); + + // TODO: This needs to be removed when lightmapping is disabled + // as its extra work per-pixel on dynamic lit scenes. + // + // Special lightmapping pass. + if ( lightMapParams.a < 0.0 ) + { + // This disables shadows on the backsides of objects. + shadowed = nDotL < 0.0f ? 1.0f : shadowed; + + Sat_NL_Att = 1.0f; + shadowed = mix( 1.0f, shadowed, atten ); + lightColorOut = vec3(shadowed); + specular *= lightBrightness; + addToResult = ( 1.0 - shadowed ) * abs(lightMapParams); + } + + OUT_FragColor0 = lightinfoCondition( lightColorOut, Sat_NL_Att, specular, addToResult ); } diff --git a/Templates/Empty/game/shaders/common/lighting/advanced/gl/softShadow.glsl b/Templates/Empty/game/shaders/common/lighting/advanced/gl/softShadow.glsl index 8ef09ed2f..a14213946 100644 --- a/Templates/Empty/game/shaders/common/lighting/advanced/gl/softShadow.glsl +++ b/Templates/Empty/game/shaders/common/lighting/advanced/gl/softShadow.glsl @@ -19,113 +19,141 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- - - -#define NUM_TAPS 12 - -#define NUM_PRE_TAPS 4 - -/// The non-uniform poisson disk used in the -/// high quality shadow filtering. -vec2 sNonUniformTaps[NUM_TAPS]; -void initNonUniformTaps() + +#if defined( SOFTSHADOW ) && defined( SOFTSHADOW_HIGH_QUALITY ) + +#define NUM_PRE_TAPS 4 +#define NUM_TAPS 12 + +/// The non-uniform poisson disk used in the +/// high quality shadow filtering. +vec2 sNonUniformTaps[NUM_TAPS] = vec2[] +( + // These first 4 taps are located around the edges + // of the disk and are used to predict fully shadowed + // or unshadowed areas. + vec2( 0.992833, 0.979309 ), + vec2( -0.998585, 0.985853 ), + vec2( 0.949299, -0.882562 ), + vec2( -0.941358, -0.893924 ), + + // The rest of the samples. + vec2( 0.545055, -0.589072 ), + vec2( 0.346526, 0.385821 ), + vec2( -0.260183, 0.334412 ), + vec2( 0.248676, -0.679605 ), + vec2( -0.569502, -0.390637 ), + vec2( -0.614096, 0.212577 ), + vec2( -0.259178, 0.876272 ), + vec2( 0.649526, 0.864333 ) +); + +#else + +#define NUM_PRE_TAPS 5 + +/// The non-uniform poisson disk used in the +/// high quality shadow filtering. +vec2 sNonUniformTaps[NUM_PRE_TAPS] = vec2[] +( + vec2( 0.892833, 0.959309 ), + vec2( -0.941358, -0.873924 ), + vec2( -0.260183, 0.334412 ), + vec2( 0.348676, -0.679605 ), + vec2( -0.569502, -0.390637 ) +); + +#endif + + +/// The texture used to do per-pixel pseudorandom +/// rotations of the filter taps. +uniform sampler2D gTapRotationTex ; + + +float softShadow_sampleTaps( sampler2D shadowMap, + vec2 sinCos, + vec2 shadowPos, + float filterRadius, + float distToLight, + float esmFactor, + int startTap, + int endTap ) { - // These first 4 taps are located around the edges - // of the disk and are used to predict fully shadowed - // or unshadowed areas. - sNonUniformTaps[0] = vec2( 0.992833, 0.979309 ); - sNonUniformTaps[1] = vec2( -0.998585, 0.985853 ); - sNonUniformTaps[2] = vec2( 0.949299, -0.882562 ); - sNonUniformTaps[3] = vec2( -0.941358, -0.893924 ); - - // The rest of the samples. - sNonUniformTaps[4] = vec2( 0.545055, -0.589072 ); - sNonUniformTaps[5] = vec2( 0.346526, 0.385821 ); - sNonUniformTaps[6] = vec2( -0.260183, 0.334412 ); - sNonUniformTaps[7] = vec2( 0.248676, -0.679605 ); - sNonUniformTaps[8] = vec2( -0.569502, -0.390637 ); - sNonUniformTaps[9] = vec2( -0.014096, 0.012577 ); - sNonUniformTaps[10] = vec2( -0.259178, 0.876272 ); - sNonUniformTaps[11] = vec2( 0.649526, 0.664333 ); + float shadow = 0; + + vec2 tap = vec2(0); + for ( int t = startTap; t < endTap; t++ ) + { + tap.x = ( sNonUniformTaps[t].x * sinCos.y - sNonUniformTaps[t].y * sinCos.x ) * filterRadius; + tap.y = ( sNonUniformTaps[t].y * sinCos.y + sNonUniformTaps[t].x * sinCos.x ) * filterRadius; + float occluder = tex2Dlod( shadowMap, vec4( shadowPos + tap, 0, 0 ) ).r; + + float esm = saturate( exp( esmFactor * ( occluder - distToLight ) ) ); + shadow += esm / float( endTap - startTap ); + } + + return shadow; } - -/// The texture used to do per-pixel pseudorandom -/// rotations of the filter taps. -uniform sampler2D gTapRotationTex; - - -float softShadow_sampleTaps( sampler2D shadowMap, - vec2 sinCos, - vec2 shadowPos, - float filterRadius, - float distToLight, - float esmFactor, - int startTap, - int endTap ) -{ - initNonUniformTaps(); - float shadow = 0.0; - - vec2 tap = vec2(0.0); - for ( int t = startTap; t < endTap; t++ ) - { - tap.x = ( sNonUniformTaps[t].x * sinCos.y - sNonUniformTaps[t].y * sinCos.x ) * filterRadius; - tap.y = ( sNonUniformTaps[t].y * sinCos.y + sNonUniformTaps[t].x * sinCos.x ) * filterRadius; - float occluder = texture2DLod( shadowMap, shadowPos + tap, 0.0 ).r; - - float esm = clamp( exp( esmFactor * ( occluder - distToLight ) ), 0.0, 1.0 ); - shadow += esm / float( endTap - startTap ); - } - - return shadow; -} - - -// HACK! HACK! HACK! -// We take the noise texture directly as the second parameter to ensure that it -// is the "last used" sampler, and thus doesn't collide with the prepass buffer -// or shadow map. If we use gTapRotationTex directly here, then it is the first -// used sampler and will collide with the prepass buffer. -float softShadow_filter( sampler2D shadowMap, - sampler2D noiseTexture, - vec2 vpos, - vec2 shadowPos, - float filterRadius, - float distToLight, - float dotNL, - float esmFactor ) -{ - // Lookup the random rotation for this screen pixel. - vec2 sinCos = ( texture2DLod( noiseTexture, vpos * 16.0, 0.0 ).rg - 0.5 ) * 2.0; - - // Do the prediction taps first. - float shadow = softShadow_sampleTaps( shadowMap, - sinCos, - shadowPos, - filterRadius, - distToLight, - esmFactor, - 0, - NUM_PRE_TAPS ); - - // Only do the expensive filtering if we're really - // in a partially shadowed area. - if ( shadow * ( 1.0 - shadow ) * max( dotNL, 0.0 ) > 0.06 ) - { - shadow += softShadow_sampleTaps( shadowMap, - sinCos, - shadowPos, - filterRadius, - distToLight, - esmFactor, - NUM_PRE_TAPS, - NUM_TAPS ); - - // This averages the taps above with the results - // of the prediction samples. - shadow *= 0.5; - } - - return shadow; -} \ No newline at end of file + + +float softShadow_filter( sampler2D shadowMap, + vec2 vpos, + vec2 shadowPos, + float filterRadius, + float distToLight, + float dotNL, + float esmFactor ) +{ + #ifndef SOFTSHADOW + + // If softshadow is undefined then we skip any complex + // filtering... just do a single sample ESM. + + float occluder = tex2Dlod( shadowMap, vec4( shadowPos, 0, 0 ) ).r; + float shadow = saturate( exp( esmFactor * ( occluder - distToLight ) ) ); + + #else + + // Lookup the random rotation for this screen pixel. + vec2 sinCos = ( tex2Dlod( gTapRotationTex, vec4( vpos * 16, 0, 0 ) ).rg - 0.5 ) * 2; + + // Do the prediction taps first. + float shadow = softShadow_sampleTaps( shadowMap, + sinCos, + shadowPos, + filterRadius, + distToLight, + esmFactor, + 0, + NUM_PRE_TAPS ); + + // We live with only the pretap results if we don't + // have high quality shadow filtering enabled. + #ifdef SOFTSHADOW_HIGH_QUALITY + + // Only do the expensive filtering if we're really + // in a partially shadowed area. + if ( shadow * ( 1.0 - shadow ) * max( dotNL, 0 ) > 0.06 ) + { + shadow += softShadow_sampleTaps( shadowMap, + sinCos, + shadowPos, + filterRadius, + distToLight, + esmFactor, + NUM_PRE_TAPS, + NUM_TAPS ); + + // This averages the taps above with the results + // of the prediction samples. + shadow *= 0.5; + } + + #endif // SOFTSHADOW_HIGH_QUALITY + + #endif // SOFTSHADOW + + return shadow; +} \ No newline at end of file diff --git a/Templates/Empty/game/shaders/common/lighting/advanced/gl/spotLightP.glsl b/Templates/Empty/game/shaders/common/lighting/advanced/gl/spotLightP.glsl index d29f5edb0..c07be1f34 100644 --- a/Templates/Empty/game/shaders/common/lighting/advanced/gl/spotLightP.glsl +++ b/Templates/Empty/game/shaders/common/lighting/advanced/gl/spotLightP.glsl @@ -25,58 +25,49 @@ #include "lightingUtils.glsl" #include "../../shadowMap/shadowMapIO_GLSL.h" #include "shadergen:/autogenConditioners.h" +#include "softShadow.glsl" +#include "../../../gl/lighting.glsl" +in vec4 wsEyeDir; +in vec4 ssPos; +in vec4 vsEyeDir; -#if TORQUE_SM >= 30 +#define IN_wsEyeDir wsEyeDir +#define IN_ssPos ssPos +#define IN_vsEyeDir vsEyeDir - // Enables high quality soft shadow - // filtering for SM3.0 and above. - #define SOFTSHADOW_SM3 - - #include "softShadow.glsl" - -#else +#ifdef USE_COOKIE_TEX +/// The texture for cookie rendering. +uniform sampler2D cookieMap; #endif - -varying vec4 ssPos; -varying vec4 wsEyeDir; - - uniform sampler2D prePassBuffer; uniform sampler2D shadowMap; -#ifdef ACCUMULATE_LUV - uniform sampler2D scratchTarget; -#endif -uniform vec4 renderTargetParams; +uniform vec4 rtParams0; uniform vec3 lightPosition; uniform vec4 lightColor; -uniform float lightBrightness; -uniform float lightRange; +uniform float lightBrightness; +uniform float lightRange; uniform vec2 lightAttenuation; uniform vec3 lightDirection; uniform vec4 lightSpotParams; uniform vec4 lightMapParams; -uniform vec3 eyePosWorld; -uniform vec4 farPlane; -uniform float negFarPlaneDotEye; -uniform mat4x4 worldToLightProj; +uniform vec4 vsFarPlane; +uniform mat4 viewToLightProj; uniform vec4 lightParams; uniform float shadowSoftness; -uniform float constantSpecularPower; - void main() -{ +{ // Compute scene UV - vec3 ssPosP = ssPos.xyz / ssPos.w; - vec2 uvScene = getUVFromSSPos( ssPosP, renderTargetParams ); + vec3 ssPos = IN_ssPos.xyz / IN_ssPos.w; + vec2 uvScene = getUVFromSSPos( ssPos, rtParams0 ); // Sample/unpack the normal/z data vec4 prepassSample = prepassUncondition( prePassBuffer, uvScene ); @@ -84,85 +75,92 @@ void main() float depth = prepassSample.a; // Eye ray - Eye -> Pixel - vec3 eyeRay = getDistanceVectorToPlane( negFarPlaneDotEye, wsEyeDir.xyz / wsEyeDir.w , farPlane ); - - // Get world space pixel position - vec3 worldPos = eyePosWorld + eyeRay * depth; + vec3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, IN_vsEyeDir.xyz, vsFarPlane ); + vec3 viewSpacePos = eyeRay * depth; // Build light vec, get length, clip pixel if needed - vec3 lightToPxlVec = worldPos - lightPosition; + vec3 lightToPxlVec = viewSpacePos - lightPosition; float lenLightV = length( lightToPxlVec ); lightToPxlVec /= lenLightV; - //lightDirection = float3( -lightDirection.xy, lightDirection.z ); //float3( 0, 0, -1 ); + //lightDirection = vec3( -lightDirection.xy, lightDirection.z ); //vec3( 0, 0, -1 ); float cosAlpha = dot( lightDirection, lightToPxlVec ); - if ( cosAlpha - lightSpotParams.x < 0.0 ) discard; - if ( lightRange - lenLightV < 0.0 ) discard; + clip( cosAlpha - lightSpotParams.x ); + clip( lightRange - lenLightV ); float atten = attenuate( lightColor, lightAttenuation, lenLightV ); atten *= ( cosAlpha - lightSpotParams.x ) / lightSpotParams.y; - if ( atten - 1e-6 < 0.0 ) discard; + clip( atten - 1e-6 ); + atten = saturate( atten ); float nDotL = dot( normal, -lightToPxlVec ); + // Get the shadow texture coordinate + vec4 pxlPosLightProj = tMul( viewToLightProj, vec4( viewSpacePos, 1 ) ); + vec2 shadowCoord = ( ( pxlPosLightProj.xy / pxlPosLightProj.w ) * 0.5 ) + vec2( 0.5, 0.5 ); + shadowCoord.y = 1.0f - shadowCoord.y; + #ifdef NO_SHADOW float shadowed = 1.0; #else - // Find Shadow coordinate - vec4 pxlPosLightProj = vec4( worldToLightProj * vec4( worldPos, 1.0 ) ); - vec2 shadowCoord = ( ( pxlPosLightProj.xy / pxlPosLightProj.w ) * 0.5 ) + vec2( 0.5, 0.5 ); - // Get a linear depth from the light source. float distToLight = pxlPosLightProj.z / lightRange; - #ifdef SOFTSHADOW_SM3 - - float shadowed = softShadow_filter( shadowMap, - gTapRotationTex, - ssPosP.xy, - shadowCoord, - shadowSoftness, - distToLight, - nDotL, - lightParams.y ); - - #else // !SOFTSHADOW_SM3 - - // Simple exponential shadow map. - float occluder = decodeShadowMap( texture2DLod( shadowMap, shadowCoord, 0.0 ) ); - float esmFactor = lightParams.y; - float shadowed = clamp( exp( esmFactor * ( occluder - distToLight ) ), 0.0, 1.0 ); - - #endif + float shadowed = softShadow_filter( shadowMap, + ssPos.xy, + shadowCoord, + shadowSoftness, + distToLight, + nDotL, + lightParams.y ); #endif // !NO_SHADOW - + + #ifdef USE_COOKIE_TEX + + // Lookup the cookie sample. + vec4 cookie = texture( cookieMap, shadowCoord ); + + // Multiply the light with the cookie tex. + lightColor.rgb *= cookie.rgb; + + // Use a maximum channel luminance to attenuate + // the lighting else we get specular in the dark + // regions of the cookie texture. + atten *= max( cookie.r, max( cookie.g, cookie.b ) ); + + #endif + // NOTE: Do not clip on fully shadowed pixels as it would // cause the hardware occlusion query to disable the shadow. // Specular term - float specular = calcSpecular( -lightToPxlVec, - normal, - normalize( -eyeRay ), - constantSpecularPower, - shadowed * atten * lightBrightness ); - - // N.L * Attenuation - float Sat_NL_Att = clamp( nDotL * atten * shadowed, 0.0, 1.0 ); - - // In LUV color mode we need to blend in the - // output from the previous target. - vec4 previousPix = vec4(0.0); - #ifdef ACCUMULATE_LUV - previousPix = texture2DLod( scratchTarget, uvScene, 0.0 ); - #endif + float specular = AL_CalcSpecular( -lightToPxlVec, + normal, + normalize( -eyeRay ) ) * lightBrightness * atten * shadowed; - // Output - gl_FragColor = lightinfoCondition( lightColor.rgb * lightBrightness, - Sat_NL_Att, - specular, - previousPix ) * lightMapParams; + float Sat_NL_Att = saturate( nDotL * atten * shadowed ) * lightBrightness; + vec3 lightColorOut = lightMapParams.rgb * lightColor.rgb; + vec4 addToResult = vec4(0.0); + + // TODO: This needs to be removed when lightmapping is disabled + // as its extra work per-pixel on dynamic lit scenes. + // + // Special lightmapping pass. + if ( lightMapParams.a < 0.0 ) + { + // This disables shadows on the backsides of objects. + shadowed = nDotL < 0.0f ? 1.0f : shadowed; + + Sat_NL_Att = 1.0f; + shadowed = mix( 1.0f, shadowed, atten ); + lightColorOut = vec3(shadowed); + specular *= lightBrightness; + addToResult = ( 1.0 - shadowed ) * abs(lightMapParams); + } + + OUT_FragColor0 = lightinfoCondition( lightColorOut, Sat_NL_Att, specular, addToResult ); } diff --git a/Templates/Empty/game/shaders/common/lighting/advanced/gl/vectorLightP.glsl b/Templates/Empty/game/shaders/common/lighting/advanced/gl/vectorLightP.glsl index bbd567fd0..0178c35ea 100644 --- a/Templates/Empty/game/shaders/common/lighting/advanced/gl/vectorLightP.glsl +++ b/Templates/Empty/game/shaders/common/lighting/advanced/gl/vectorLightP.glsl @@ -22,40 +22,32 @@ #include "../../../gl/hlslCompat.glsl" #include "shadergen:/autogenConditioners.h" +#include "farFrustumQuad.glsl" +#include "../../../gl/torque.glsl" +#include "../../../gl/lighting.glsl" #include "lightingUtils.glsl" #include "../../shadowMap/shadowMapIO_GLSL.h" +#include "softShadow.glsl" -varying vec2 uv0; -varying vec3 wsEyeRay; +in vec4 hpos; +in vec2 uv0; +in vec3 wsEyeRay; +in vec3 vsEyeRay; -uniform sampler2D prePassBuffer; -uniform sampler2D ShadowMap; +uniform sampler2D ShadowMap ; -#if TORQUE_SM >= 30 - - // Enables high quality soft shadow - // filtering for SM3.0 and above. - #define SOFTSHADOW_SM3 - - #include "softShadow.glsl" - -#else - - - +#ifdef USE_SSAO_MASK +uniform sampler2D ssaoMask ; +uniform vec4 rtParams2; #endif - + +uniform sampler2D prePassBuffer; uniform vec3 lightDirection; uniform vec4 lightColor; -uniform float lightBrightness; -uniform vec4 lightAmbient; -uniform vec4 lightTrilight; - -uniform vec3 eyePosWorld; - -uniform mat4 worldToLightProj; -uniform vec4 splitDistStart; -uniform vec4 splitDistEnd; +uniform float lightBrightness; +uniform vec4 lightAmbient; +uniform vec3 eyePosWorld; +uniform mat4x4 worldToLightProj; uniform vec4 scaleX; uniform vec4 scaleY; uniform vec4 offsetX; @@ -65,16 +57,12 @@ uniform vec4 atlasYOffset; uniform vec2 atlasScale; uniform vec4 zNearFarInvNearFar; uniform vec4 lightMapParams; - -uniform float constantSpecularPower; uniform vec2 fadeStartLength; uniform vec4 farPlaneScalePSSM; -uniform vec4 splitFade; uniform vec4 overDarkPSSM; uniform float shadowSoftness; - -void main() +void main() { // Sample/unpack the normal/z data vec4 prepassSample = prepassUncondition( prePassBuffer, uv0 ); @@ -83,148 +71,162 @@ void main() // Use eye ray to get ws pos vec4 worldPos = vec4(eyePosWorld + wsEyeRay * depth, 1.0f); - + // Get the light attenuation. float dotNL = dot(-lightDirection, normal); + + #ifdef PSSM_DEBUG_RENDER + vec3 debugColor = vec3(0); + #endif #ifdef NO_SHADOW // Fully unshadowed. float shadowed = 1.0; + #ifdef PSSM_DEBUG_RENDER + debugColor = vec3(1.0); + #endif + #else - + // Compute shadow map coordinate - vec4 pxlPosLightProj = worldToLightProj * worldPos; + vec4 pxlPosLightProj = tMul(worldToLightProj, worldPos); vec2 baseShadowCoord = pxlPosLightProj.xy / pxlPosLightProj.w; - - float distOffset = 0.0; - float shadowed = 0.0; - float fadeAmt = 0.0; - vec4 zDist = vec4(zNearFarInvNearFar.x + zNearFarInvNearFar.y * depth); - - // Calculate things dependant on the shadowmap split - for ( int i = 0; i < 2; i++ ) - { - float zDistSplit = zDist.x + distOffset; - vec4 mask0; - mask0.x = float(zDistSplit >= splitDistStart.x); - mask0.y = float(zDistSplit >= splitDistStart.y); - mask0.z = float(zDistSplit >= splitDistStart.z); - mask0.w = float(zDistSplit >= splitDistStart.w); + + // Distance to light, in shadowmap space + float distToLight = pxlPosLightProj.z / pxlPosLightProj.w; - vec4 mask1; - mask1.x = float(zDistSplit < splitDistEnd.x); - mask1.y = float(zDistSplit < splitDistEnd.y); - mask1.z = float(zDistSplit < splitDistEnd.z); - mask1.w = float(zDistSplit < splitDistEnd.w); + // Figure out which split to sample from. Basically, we compute the shadowmap sample coord + // for all of the splits and then check if its valid. + vec4 shadowCoordX = vec4( baseShadowCoord.x ); + vec4 shadowCoordY = vec4( baseShadowCoord.y ); + vec4 farPlaneDists = vec4( distToLight ); + shadowCoordX *= scaleX; + shadowCoordY *= scaleY; + shadowCoordX += offsetX; + shadowCoordY += offsetY; + farPlaneDists *= farPlaneScalePSSM; + + // If the shadow sample is within -1..1 and the distance + // to the light for this pixel is less than the far plane + // of the split, use it. + vec4 finalMask; + if ( shadowCoordX.x > -0.99 && shadowCoordX.x < 0.99 && + shadowCoordY.x > -0.99 && shadowCoordY.x < 0.99 && + farPlaneDists.x < 1.0 ) + finalMask = vec4(1, 0, 0, 0); + + else if ( shadowCoordX.y > -0.99 && shadowCoordX.y < 0.99 && + shadowCoordY.y > -0.99 && shadowCoordY.y < 0.99 && + farPlaneDists.y < 1.0 ) + finalMask = vec4(0, 1, 0, 0); + + else if ( shadowCoordX.z > -0.99 && shadowCoordX.z < 0.99 && + shadowCoordY.z > -0.99 && shadowCoordY.z < 0.99 && + farPlaneDists.z < 1.0 ) + finalMask = vec4(0, 0, 1, 0); - vec4 finalMask = mask0 * mask1; + else + finalMask = vec4(0, 0, 0, 1); - float splitFadeDist = dot( finalMask, splitFade ); - vec2 finalScale; - finalScale.x = dot(finalMask, scaleX); - finalScale.y = dot(finalMask, scaleY); + #ifdef PSSM_DEBUG_RENDER + if ( finalMask.x > 0 ) + debugColor += vec3( 1, 0, 0 ); + else if ( finalMask.y > 0 ) + debugColor += vec3( 0, 1, 0 ); + else if ( finalMask.z > 0 ) + debugColor += vec3( 0, 0, 1 ); + else if ( finalMask.w > 0 ) + debugColor += vec3( 1, 1, 0 ); + #endif - vec2 finalOffset; - finalOffset.x = dot(finalMask, offsetX); - finalOffset.y = dot(finalMask, offsetY); - - vec2 shadowCoord; - shadowCoord = baseShadowCoord * finalScale; - shadowCoord += finalOffset; + // Here we know what split we're sampling from, so recompute the texcoord location + // Yes, we could just use the result from above, but doing it this way actually saves + // shader instructions. + vec2 finalScale; + finalScale.x = dot(finalMask, scaleX); + finalScale.y = dot(finalMask, scaleY); - // Convert to texcoord space - shadowCoord = 0.5 * shadowCoord + vec2(0.5, 0.5); - //shadowCoord.y = 1.0f - shadowCoord.y; + vec2 finalOffset; + finalOffset.x = dot(finalMask, offsetX); + finalOffset.y = dot(finalMask, offsetY); - // Move around inside of atlas - vec2 aOffset; - aOffset.x = dot(finalMask, atlasXOffset); - aOffset.y = dot(finalMask, atlasYOffset); + vec2 shadowCoord; + shadowCoord = baseShadowCoord * finalScale; + shadowCoord += finalOffset; - shadowCoord *= atlasScale; - shadowCoord += aOffset; - - // Distance to light, in shadowmap space - float distToLight = pxlPosLightProj.z / pxlPosLightProj.w; - - // Each split has a different far plane, take this into account. - float farPlaneScale = dot( farPlaneScalePSSM, finalMask ); - distToLight *= farPlaneScale; - - #ifdef SOFTSHADOW_SM3 + // Convert to texcoord space + shadowCoord = 0.5 * shadowCoord + vec2(0.5, 0.5); + shadowCoord.y = 1.0f - shadowCoord.y; - float esmShadow = softShadow_filter( ShadowMap, - gTapRotationTex, - uv0.xy, - shadowCoord, - farPlaneScale * shadowSoftness, - distToLight, - dotNL, - dot( finalMask, overDarkPSSM ) ); - - #else // !SOFTSHADOW_SM3 + // Move around inside of atlas + vec2 aOffset; + aOffset.x = dot(finalMask, atlasXOffset); + aOffset.y = dot(finalMask, atlasYOffset); - float occluder = decodeShadowMap( texture2DLod( ShadowMap, shadowCoord, 0.0 ) ); - float overDark = dot( finalMask, overDarkPSSM ); - float esmShadow = saturate( exp( esmFactor * ( occluder - distToLight ) ) ); - - #endif - - if ( i == 0 ) - { - float endDist = dot(splitDistEnd, finalMask); - fadeAmt = smoothstep(endDist - splitFadeDist, endDist, zDist).x; - shadowed = esmShadow * ( 1.0 - fadeAmt ); - } - else - shadowed += esmShadow * fadeAmt; - - distOffset += splitFadeDist; - } + shadowCoord *= atlasScale; + shadowCoord += aOffset; + + // Each split has a different far plane, take this into account. + float farPlaneScale = dot( farPlaneScalePSSM, finalMask ); + distToLight *= farPlaneScale; + + float shadowed = softShadow_filter( ShadowMap, + uv0.xy, + shadowCoord, + farPlaneScale * shadowSoftness, + distToLight, + dotNL, + dot( finalMask, overDarkPSSM ) ); // Fade out the shadow at the end of the range. + vec4 zDist = vec4(zNearFarInvNearFar.x + zNearFarInvNearFar.y * depth); float fadeOutAmt = ( zDist.x - fadeStartLength.x ) * fadeStartLength.y; - shadowed = mix( shadowed, 1.0, clamp( fadeOutAmt, 0.0, 1.0 ) ); + shadowed = mix( shadowed, 1.0, saturate( fadeOutAmt ) ); + + #ifdef PSSM_DEBUG_RENDER + if ( fadeOutAmt > 1.0 ) + debugColor = vec3(1.0); + #endif #endif // !NO_SHADOW - - // Calc lighting coefficents - float specular = calcSpecular( -lightDirection, - normal, - normalize(-wsEyeRay), - constantSpecularPower, - shadowed * lightBrightness ); - - float Sat_NL_Att = clamp(dotNL, 0.0, 1.0) * shadowed; - - // Trilight, described by Tom Forsyth - // http://home.comcast.net/~tom_forsyth/papers/trilight/trilight.html -#ifdef ACCUMULATE_LUV - // In LUV multiply in the brightness of the light color (normaly done in the attenuate function) - Sat_NL_Att *= lightColor.a; + // Specular term + float specular = AL_CalcSpecular( -lightDirection, + normal, + normalize(-vsEyeRay) ) * lightBrightness * shadowed; - vec4 ambientBlend = lightAmbient; - ambientBlend.b *= clamp(-dotNL, 0.0, 1.0); - - vec3 trilight = lightTrilight.rgb; - trilight.b *= clamp(1.0 - abs(dotNL), 0.0, 1.0); - - ambientBlend.rg = mix(ambientBlend.rg, trilight.rg, clamp(0.5 * trilight.b / lightAmbient.b, 0.0, 1.0)); - ambientBlend.b += trilight.b; + float Sat_NL_Att = saturate( dotNL * shadowed ) * lightBrightness; + vec3 lightColorOut = lightMapParams.rgb * lightColor.rgb; + vec4 addToResult = lightAmbient; -#else + // TODO: This needs to be removed when lightmapping is disabled + // as its extra work per-pixel on dynamic lit scenes. + // + // Special lightmapping pass. + if ( lightMapParams.a < 0.0 ) + { + // This disables shadows on the backsides of objects. + shadowed = dotNL < 0.0f ? 1.0f : shadowed; - // RGB - // TODO: Trilight seems broken... it does lighting in shadows! - //vec4 ambientBlend = vec4(lightTrilight.rgb * clamp(1.0 - abs(dotNL), 0.0, 1.0) + lightAmbient.rgb * clamp(-dotNL, 0.0, 1.0), 0.0); - vec4 ambientBlend = vec4(lightAmbient.rgb, 0.0); + Sat_NL_Att = 1.0f; + lightColorOut = vec3(shadowed); + specular *= lightBrightness; + addToResult = ( 1.0 - shadowed ) * abs(lightMapParams); + } -#endif + // Sample the AO texture. + #ifdef USE_SSAO_MASK + float ao = 1.0 - texture( ssaoMask, viewportCoordToRenderTarget( uv0.xy, rtParams2 ) ).r; + addToResult *= ao; + #endif + + #ifdef PSSM_DEBUG_RENDER + lightColorOut = debugColor; + #endif + + OUT_FragColor0 = lightinfoCondition( lightColorOut, Sat_NL_Att, specular, addToResult ); - // Output - gl_FragColor = lightinfoCondition( lightColor.rgb * lightBrightness, Sat_NL_Att, specular, ambientBlend) * lightMapParams; } diff --git a/Templates/Empty/game/shaders/common/lighting/basic/gl/shadowFilterP.glsl b/Templates/Empty/game/shaders/common/lighting/basic/gl/shadowFilterP.glsl index 238721e5e..4e97fa347 100644 --- a/Templates/Empty/game/shaders/common/lighting/basic/gl/shadowFilterP.glsl +++ b/Templates/Empty/game/shaders/common/lighting/basic/gl/shadowFilterP.glsl @@ -20,35 +20,27 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" + uniform sampler2D diffuseMap; -varying vec2 uv; +in vec2 uv; uniform vec2 oneOverTargetSize; +const float offset[3] = float[]( 0.0, 1.3846153846, 3.2307692308 ); +const float weight[3] = float[]( 0.2270270270, 0.3162162162, 0.0702702703 ); + void main() { - vec2 sNonUniformTaps[8]; - - sNonUniformTaps[0] = vec2(0.992833, 0.979309); - sNonUniformTaps[1] = vec2(-0.998585, 0.985853); - sNonUniformTaps[2] = vec2(0.949299, -0.882562); - sNonUniformTaps[3] = vec2(-0.941358, -0.893924); - sNonUniformTaps[4] = vec2(0.545055, -0.589072); - sNonUniformTaps[5] = vec2(0.346526, 0.385821); - sNonUniformTaps[6] = vec2(-0.260183, 0.334412); - sNonUniformTaps[7] = vec2(0.248676, -0.679605); + vec4 OUT = texture( diffuseMap, uv ) * weight[0]; - gl_FragColor = vec4(0.0); - - vec2 texScale = vec2(1.0); - - for ( int i=0; i < 4; i++ ) + for ( int i=1; i < 3; i++ ) { - vec2 offset = (oneOverTargetSize * texScale) * sNonUniformTaps[i]; - gl_FragColor += texture2D( diffuseMap, uv + offset ); + vec2 _sample = (BLUR_DIR * offset[i]) * oneOverTargetSize; + OUT += texture( diffuseMap, uv + _sample ) * weight[i]; + OUT += texture( diffuseMap, uv - _sample ) * weight[i]; } - - gl_FragColor /= vec4(4.0); - gl_FragColor.rgb = vec3(0.0); + + OUT_FragColor0 = OUT; } diff --git a/Templates/Empty/game/shaders/common/lighting/basic/gl/shadowFilterV.glsl b/Templates/Empty/game/shaders/common/lighting/basic/gl/shadowFilterV.glsl index cbf3696be..0eeb2e0fd 100644 --- a/Templates/Empty/game/shaders/common/lighting/basic/gl/shadowFilterV.glsl +++ b/Templates/Empty/game/shaders/common/lighting/basic/gl/shadowFilterV.glsl @@ -22,13 +22,16 @@ #include "../../../../../../shaders/common/gl/torque.glsl" -uniform vec2 oneOverTargetSize; +in vec4 vPosition; +in vec2 vTexCoord0; + uniform vec4 rtParams0; -varying vec2 uv; +out vec2 uv; void main() { - gl_Position = gl_Vertex; - uv = viewportCoordToRenderTarget( gl_MultiTexCoord0.st, rtParams0 ); + gl_Position = vPosition; + uv = viewportCoordToRenderTarget( vTexCoord0.st, rtParams0 ); + gl_Position.y *= -1; //correct ssp } diff --git a/Templates/Empty/game/shaders/common/lighting/shadowMap/gl/boxFilterP.glsl b/Templates/Empty/game/shaders/common/lighting/shadowMap/gl/boxFilterP.glsl index 2800a3f17..0f568c716 100644 --- a/Templates/Empty/game/shaders/common/lighting/shadowMap/gl/boxFilterP.glsl +++ b/Templates/Empty/game/shaders/common/lighting/shadowMap/gl/boxFilterP.glsl @@ -26,7 +26,7 @@ uniform sampler2D diffuseMap0; uniform float texSize; uniform vec2 blurDimension; -varying vec2 tex0; +in vec2 tex0; void main() { @@ -40,8 +40,8 @@ void main() vec4 accum = vec4(0.0, 0.0, 0.0, 0.0); for(int i = 0; i < int(blurSamples); i++) { - accum += texture2D(diffuseMap0, BaseTexCoord + float(i) * SampleOffset); + accum += texture(diffuseMap0, BaseTexCoord + float(i) * SampleOffset); } accum /= blurSamples; - gl_FragColor = accum; + OUT_FragColor0 = accum; } \ No newline at end of file diff --git a/Templates/Empty/game/shaders/common/lighting/shadowMap/gl/boxFilterV.glsl b/Templates/Empty/game/shaders/common/lighting/shadowMap/gl/boxFilterV.glsl index 3850f83c7..9fc436f6c 100644 --- a/Templates/Empty/game/shaders/common/lighting/shadowMap/gl/boxFilterV.glsl +++ b/Templates/Empty/game/shaders/common/lighting/shadowMap/gl/boxFilterV.glsl @@ -20,12 +20,15 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +in vec4 vPosition; +in vec2 vTexCoord0; + uniform mat4 modelview; -varying vec2 tex0; +out vec2 tex0; void main() { - gl_Position = modelview * gl_Vertex; - tex0 = gl_MultiTexCoord0.st; + gl_Position = modelview * vPosition; + tex0 = vTexCoord0.st; } \ No newline at end of file diff --git a/Templates/Empty/game/shaders/common/postFx/postFxV.glsl b/Templates/Empty/game/shaders/common/postFx/postFxV.glsl index 3aded2e0f..96a5ec819 100644 --- a/Templates/Empty/game/shaders/common/postFx/postFxV.glsl +++ b/Templates/Empty/game/shaders/common/postFx/postFxV.glsl @@ -22,27 +22,30 @@ #include "./../gl/torque.glsl" +in vec4 vPosition; +in vec2 vTexCoord0; +in vec3 vTexCoord1; uniform vec4 rtParams0; uniform vec4 rtParams1; uniform vec4 rtParams2; uniform vec4 rtParams3; -varying vec2 uv0; -varying vec2 uv1; -varying vec2 uv2; -varying vec2 uv3; -varying vec3 wsEyeRay; +out vec2 uv0; +out vec2 uv1; +out vec2 uv2; +out vec2 uv3; +out vec3 wsEyeRay; void main() { - gl_Position = gl_Vertex; + gl_Position = vPosition; - uv0 = viewportCoordToRenderTarget( gl_MultiTexCoord0.st, rtParams0 ); - uv1 = viewportCoordToRenderTarget( gl_MultiTexCoord0.st, rtParams1 ); - uv2 = viewportCoordToRenderTarget( gl_MultiTexCoord0.st, rtParams2 ); - uv3 = viewportCoordToRenderTarget( gl_MultiTexCoord0.st, rtParams3 ); + uv0 = viewportCoordToRenderTarget( vTexCoord0, rtParams0 ); + uv1 = viewportCoordToRenderTarget( vTexCoord0, rtParams1 ); + uv2 = viewportCoordToRenderTarget( vTexCoord0, rtParams2 ); + uv3 = viewportCoordToRenderTarget( vTexCoord0, rtParams3 ); - wsEyeRay = gl_MultiTexCoord1.xyz; + wsEyeRay = vTexCoord1; } diff --git a/Templates/Empty/game/shaders/common/terrain/gl/blendP.glsl b/Templates/Empty/game/shaders/common/terrain/gl/blendP.glsl index 3817e1de2..a2e4af787 100644 --- a/Templates/Empty/game/shaders/common/terrain/gl/blendP.glsl +++ b/Templates/Empty/game/shaders/common/terrain/gl/blendP.glsl @@ -23,8 +23,10 @@ #include "../terrain.glsl" #include "../../gl/hlslCompat.glsl" -varying vec2 layerCoord; -varying vec2 texCoord; +in vec2 layerCoord; +#define IN_layerCoord layerCoord +in vec2 texCoord; +#define IN_texCoord texCoord uniform sampler2D layerTex; uniform sampler2D textureMap; @@ -33,12 +35,12 @@ uniform float layerSize; void main() { - vec4 layerSample = round(texture2D( layerTex, layerCoord ) * 255.0); + vec4 layerSample = round(texture( layerTex, IN_layerCoord ) * 255.0); - float blend = calcBlend( texId, layerCoord, layerSize, layerSample ); + float blend = calcBlend( texId, IN_layerCoord, layerSize, layerSample ); if(blend - 0.0001 < 0.0) discard; - gl_FragColor = vec4( texture2D( textureMap, texCoord ).rgb, blend ); + OUT_FragColor0 = vec4( texture( textureMap, IN_texCoord ).rgb, blend ); } diff --git a/Templates/Empty/game/shaders/common/terrain/gl/blendV.glsl b/Templates/Empty/game/shaders/common/terrain/gl/blendV.glsl index 44362085b..dc7b7befa 100644 --- a/Templates/Empty/game/shaders/common/terrain/gl/blendV.glsl +++ b/Templates/Empty/game/shaders/common/terrain/gl/blendV.glsl @@ -23,14 +23,19 @@ /// The vertex shader used in the generation and caching of the /// base terrain texture. -varying vec2 layerCoord; -varying vec2 texCoord; +in vec4 vPosition; +in vec2 vTexCoord0; + +out vec2 layerCoord; +out vec2 texCoord; uniform vec2 texScale; void main() { - gl_Position = vec4(gl_Vertex.xyz, 1.0); - layerCoord = gl_MultiTexCoord0.st; - texCoord = gl_MultiTexCoord0.st * texScale; + gl_Position = vec4(vPosition.xyz, 1.0); + layerCoord = vTexCoord0.st; + texCoord = vTexCoord0.st * texScale; + + gl_Position.y *= -1; } diff --git a/Templates/Empty/game/shaders/common/water/gl/waterBasicP.glsl b/Templates/Empty/game/shaders/common/water/gl/waterBasicP.glsl index 72232622a..44207dea9 100644 --- a/Templates/Empty/game/shaders/common/water/gl/waterBasicP.glsl +++ b/Templates/Empty/game/shaders/common/water/gl/waterBasicP.glsl @@ -31,7 +31,7 @@ #define FRESNEL_BIAS miscParams[0] #define FRESNEL_POWER miscParams[1] #define CLARITY miscParams[2] -#define ISRIVER miscParams[3] +#define ISRIVER miscParams[3] // reflectParams #define REFLECT_PLANE_Z reflectParams[0] @@ -45,40 +45,49 @@ #define DISTORT_FULL_DEPTH distortionParams[2] // ConnectData.misc -#define LIGHT_VEC misc.xyz -#define WORLD_Z objPos.w +#define LIGHT_VEC IN_misc.xyz +#define WORLD_Z IN_objPos.w // specularParams #define SPEC_POWER specularParams[3] #define SPEC_COLOR specularParams.xyz +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + // TexCoord 0 and 1 (xy,zw) for ripple texture lookup -varying vec4 rippleTexCoord01; +in vec4 rippleTexCoord01; +#define IN_rippleTexCoord01 rippleTexCoord01 // TexCoord 2 for ripple texture lookup -varying vec2 rippleTexCoord2; +in vec2 rippleTexCoord2; +#define IN_rippleTexCoord2 rippleTexCoord2 // Screenspace vert position BEFORE wave transformation -varying vec4 posPreWave; +in vec4 posPreWave; +#define IN_posPreWave posPreWave // Screenspace vert position AFTER wave transformation -varying vec4 posPostWave; +in vec4 posPostWave; +#define IN_posPostWave posPostWave // Worldspace unit distance/depth of this vertex/pixel -varying float pixelDist; - -// Objectspace vert position BEFORE wave transformation -// w coord is world space z position. -varying vec4 objPos; +in float pixelDist; +#define IN_pixelDist pixelDist -varying vec3 misc; +in vec4 objPos; +#define IN_objPos objPos + +in vec3 misc; +#define IN_misc misc //----------------------------------------------------------------------------- // approximate Fresnel function //----------------------------------------------------------------------------- float fresnel(float NdotV, float bias, float power) { - return bias + (1.0-bias)*pow(abs(1.0 - max(NdotV, 0.0)), power); + return bias + (1.0-bias)*pow(abs(1.0 - max(NdotV, 0)), power); } //----------------------------------------------------------------------------- @@ -89,7 +98,7 @@ uniform sampler2D bumpMap; uniform sampler2D reflectMap; uniform sampler2D refractBuff; uniform samplerCube skyMap; -//uniform sampler foamMap; +//uniform sampler2D foamMap; uniform vec4 baseColor; uniform vec4 miscParams; uniform vec4 reflectParams; @@ -98,8 +107,9 @@ uniform vec3 eyePos; uniform vec3 distortionParams; uniform vec3 fogData; uniform vec4 fogColor; -uniform vec3 rippleMagnitude; +uniform vec4 rippleMagnitude; uniform vec4 specularParams; +uniform mat4 modelMat; //----------------------------------------------------------------------------- // Main @@ -107,31 +117,35 @@ uniform vec4 specularParams; void main() { // Modulate baseColor by the ambientColor. - vec4 waterBaseColor = baseColor * vec4( ambientColor.rgb, 1.0 ); + vec4 waterBaseColor = baseColor * vec4( ambientColor.rgb, 1 ); // Get the bumpNorm... - vec3 bumpNorm = ( texture2D( bumpMap, rippleTexCoord01.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.x; - bumpNorm += ( texture2D( bumpMap, rippleTexCoord01.zw ).rgb * 2.0 - 1.0 ) * rippleMagnitude.y; - bumpNorm += ( texture2D( bumpMap, rippleTexCoord2 ).rgb * 2.0 - 1.0 ) * rippleMagnitude.z; - + vec3 bumpNorm = ( texture( bumpMap, IN_rippleTexCoord01.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.x; + bumpNorm += ( texture( bumpMap, IN_rippleTexCoord01.zw ).rgb * 2.0 - 1.0 ) * rippleMagnitude.y; + bumpNorm += ( texture( bumpMap, IN_rippleTexCoord2 ).rgb * 2.0 - 1.0 ) * rippleMagnitude.z; + + bumpNorm = normalize( bumpNorm ); + bumpNorm = mix( bumpNorm, vec3(0,0,1), 1.0 - rippleMagnitude.w ); + // We subtract a little from it so that we don't // distort where the water surface intersects the // camera near plane. - float distortAmt = saturate( pixelDist / 1.0 ) * 0.8; + float distortAmt = saturate( IN_pixelDist / 1.0 ) * 0.8; - vec4 distortPos = posPostWave; + vec4 distortPos = IN_posPostWave; distortPos.xy += bumpNorm.xy * distortAmt; #ifdef UNDERWATER - gl_FragColor = texture2DProj( refractBuff, distortPos.xyz ); + OUT_FragColor0 = hdrEncode( textureProj( refractBuff, distortPos ) ); #else - vec3 eyeVec = objPos.xyz - eyePos; - vec3 reflectionVec = reflect( eyeVec, normalize(bumpNorm) ); + vec3 eyeVec = IN_objPos.xyz - eyePos; + eyeVec = tMul( mat3(modelMat), eyeVec ); + vec3 reflectionVec = reflect( eyeVec, bumpNorm ); // Color that replaces the reflection color when we do not // have one that is appropriate. - vec4 fakeColor = vec4(ambientColor,1.0); + vec4 fakeColor = vec4(ambientColor,1); // Use fakeColor for ripple-normals that are angled towards the camera eyeVec = -eyeVec; @@ -140,58 +154,61 @@ void main() float fakeColorAmt = ang; // Get reflection map color - vec4 refMapColor = texture2DProj( reflectMap, distortPos ); + vec4 refMapColor = hdrDecode( textureProj( reflectMap, distortPos ) ); // If we do not have a reflection texture then we use the cubemap. - refMapColor = mix( refMapColor, textureCube( skyMap, -reflectionVec ), NO_REFLECT ); + refMapColor = mix( refMapColor, texture( skyMap, reflectionVec ), NO_REFLECT ); // Combine reflection color and fakeColor. vec4 reflectColor = mix( refMapColor, fakeColor, fakeColorAmt ); //return refMapColor; // Get refract color - vec4 refractColor = texture2DProj( refractBuff, distortPos.xyz ); + vec4 refractColor = hdrDecode( textureProj( refractBuff, distortPos ) ); // calc "diffuse" color by lerping from the water color // to refraction image based on the water clarity. - vec4 diffuseColor = mix( refractColor, waterBaseColor, 1.0 - CLARITY ); + vec4 diffuseColor = mix( refractColor, waterBaseColor, 1.0f - CLARITY ); // fresnel calculation float fresnelTerm = fresnel( ang, FRESNEL_BIAS, FRESNEL_POWER ); + //return vec4( fresnelTerm.rrr, 1 ); // Also scale the frensel by our distance to the // water surface. This removes the hard reflection // when really close to the water surface. - fresnelTerm *= saturate( pixelDist - 0.1 ); + fresnelTerm *= saturate( IN_pixelDist - 0.1 ); // Combine the diffuse color and reflection image via the // fresnel term and set out output color. - gl_FragColor = mix( diffuseColor, reflectColor, fresnelTerm ); - + vec4 OUT = mix( diffuseColor, reflectColor, fresnelTerm ); + #ifdef WATER_SPEC // Get some specular reflection. vec3 newbump = bumpNorm; newbump.xy *= 3.5; newbump = normalize( bumpNorm ); - vec3 halfAng = normalize( eyeVec + -LIGHT_VEC ); + half3 halfAng = normalize( eyeVec + -LIGHT_VEC ); float specular = saturate( dot( newbump, halfAng ) ); specular = pow( specular, SPEC_POWER ); - gl_FragColor.rgb = gl_FragColor.rgb + ( SPEC_COLOR * specular.xxx ); + OUT.rgb = OUT.rgb + ( SPEC_COLOR * specular.xxx ); #else // Disable fogging if spec is on because otherwise we run out of instructions. // Fog it. float factor = computeSceneFog( eyePos, - objPos.xyz, + IN_objPos.xyz, WORLD_Z, fogData.x, fogData.y, fogData.z ); - gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor.rgb, 1.0 - saturate( factor ) ); - - #endif + //OUT.rgb = mix( OUT.rgb, fogColor.rgb, 1.0 - saturate( factor ) ); + #endif + + OUT_FragColor0 = OUT; + #endif } diff --git a/Templates/Empty/game/shaders/common/water/gl/waterBasicV.glsl b/Templates/Empty/game/shaders/common/water/gl/waterBasicV.glsl index bb2a0c954..1634fd2de 100644 --- a/Templates/Empty/game/shaders/common/water/gl/waterBasicV.glsl +++ b/Templates/Empty/game/shaders/common/water/gl/waterBasicV.glsl @@ -27,23 +27,30 @@ //----------------------------------------------------------------------------- // TexCoord 0 and 1 (xy,zw) for ripple texture lookup -varying vec4 rippleTexCoord01; +out vec4 rippleTexCoord01; +#define OUT_rippleTexCoord01 rippleTexCoord01 // TexCoord 2 for ripple texture lookup -varying vec2 rippleTexCoord2; +out vec2 rippleTexCoord2; +#define OUT_rippleTexCoord2 rippleTexCoord2 // Screenspace vert position BEFORE wave transformation -varying vec4 posPreWave; +out vec4 posPreWave; +#define OUT_posPreWave posPreWave // Screenspace vert position AFTER wave transformation -varying vec4 posPostWave; +out vec4 posPostWave; +#define OUT_posPostWave posPostWave // Worldspace unit distance/depth of this vertex/pixel -varying float pixelDist; +out float pixelDist; +#define OUT_pixelDist pixelDist -varying vec4 objPos; +out vec4 objPos; +#define OUT_objPos objPos -varying vec3 misc; +out vec3 misc; +#define OUT_misc misc //----------------------------------------------------------------------------- // Uniforms @@ -63,49 +70,56 @@ uniform float gridElementSize; uniform float elapsedTime; uniform float undulateMaxDist; +in vec4 vPosition; +in vec3 vNormal; +in vec4 vColor; +in vec2 vTexCoord0; +in vec4 vTexCoord1; + //----------------------------------------------------------------------------- // Main //----------------------------------------------------------------------------- void main() { - vec4 position = gl_Vertex; - vec3 normal = gl_Normal; - vec2 undulateData = gl_MultiTexCoord0.st; - vec4 horizonFactor = gl_MultiTexCoord1; + vec4 IN_position = vPosition; + vec3 IN_normal = vNormal; + vec2 IN_undulateData = vTexCoord0; + vec4 IN_horizonFactor = vTexCoord1; + vec4 OUT_hpos = vec4(0); // use projection matrix for reflection / refraction texture coords - mat4 texGen = mat4(0.5, 0.0, 0.0, 0.0, - 0.0, 0.5, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.5, 0.5, 0.0, 1.0); + mat4 texGen = mat4FromRow( 0.5, 0.0, 0.0, 0.5, + 0.0, -0.5, 0.0, 0.5, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 ); // Move the vertex based on the horizonFactor if specified to do so for this vert. - //if ( horizonFactor.z > 0.0 ) - //{ - //vec2 offsetXY = eyePos.xy - mod(eyePos.xy, gridElementSize); - //position.xy += offsetXY; - //undulateData += offsetXY; - //} + // if ( IN_horizonFactor.z > 0 ) + // { + // vec2 offsetXY = eyePos.xy - eyePos.xy % gridElementSize; + // IN_position.xy += offsetXY; + // IN_undulateData += offsetXY; + // } - vec4 worldPos = modelMat * position; - //fogPos = position.xyz; - position.z = mix( position.z, eyePos.z, horizonFactor.x ); - - objPos.xyz = position.xyz; - objPos.w = worldPos.z; + vec4 worldPos = tMul( modelMat, IN_position ); + + IN_position.z = mix( IN_position.z, eyePos.z, IN_horizonFactor.x ); + + //OUT_objPos = worldPos; + OUT_objPos.xyz = IN_position.xyz; + OUT_objPos.w = worldPos.z; // Send pre-undulation screenspace position - posPreWave = modelview * position; - posPreWave = texGen * posPreWave; + OUT_posPreWave = tMul( modelview, IN_position ); + OUT_posPreWave = tMul( texGen, OUT_posPreWave ); // Calculate the undulation amount for this vertex. - vec2 undulatePos = (modelMat * vec4( undulateData.xy, 0, 1 )).xy; - - //if ( undulatePos.x < 0.0 ) - //undulatePos = position.xy; - - float undulateAmt = 0.0; + vec2 undulatePos = tMul( modelMat, vec4( IN_undulateData.xy, 0, 1 ) ).xy; + //if ( undulatePos.x < 0 ) + // undulatePos = IN_position.xy; + float undulateAmt = 0.0; + undulateAmt += waveData[0].y * sin( elapsedTime * waveData[0].x + undulatePos.x * waveDir[0].x + undulatePos.y * waveDir[0].y ); @@ -114,118 +128,84 @@ void main() undulatePos.y * waveDir[1].y ); undulateAmt += waveData[2].y * sin( elapsedTime * waveData[2].x + undulatePos.x * waveDir[2].x + - undulatePos.y * waveDir[2].y ); - - float undulateFade = 1.0; - - // Scale down wave magnitude amount based on distance from the camera. - float dist = length( position.xyz - eyePos ); + undulatePos.y * waveDir[2].y ); + + float undulateFade = 1; + + // Scale down wave magnitude amount based on distance from the camera. + float dist = distance( IN_position.xyz, eyePos ); dist = clamp( dist, 1.0, undulateMaxDist ); - undulateFade *= ( 1.0 - dist / undulateMaxDist ); + undulateFade *= ( 1 - dist / undulateMaxDist ); // Also scale down wave magnitude if the camera is very very close. - undulateFade *= saturate( ( length( position.xyz - eyePos ) - 0.5 ) / 10.0 ); - + undulateFade *= saturate( ( distance( IN_position.xyz, eyePos ) - 0.5 ) / 10.0 ); + undulateAmt *= undulateFade; + //#endif //undulateAmt = 0; // Apply wave undulation to the vertex. - posPostWave = position; - posPostWave.xyz += normal.xyz * undulateAmt; + OUT_posPostWave = IN_position; + OUT_posPostWave.xyz += IN_normal.xyz * undulateAmt; // Save worldSpace position of this pixel/vert - //worldPos = posPostWave.xyz; + //OUT_worldPos = OUT_posPostWave.xyz; + //OUT_worldPos = tMul( modelMat, OUT_posPostWave.xyz ); + //OUT_worldPos.z += objTrans[2][2]; //91.16; - //worldSpaceZ = ( modelMat * vec4(fogPos,1.0) ).z; - //if ( horizonFactor.x > 0.0 ) - //{ - //vec3 awayVec = normalize( fogPos.xyz - eyePos ); - //fogPos.xy += awayVec.xy * 1000.0; - //} + // OUT_misc.w = tMul( modelMat, OUT_fogPos ).z; + // if ( IN_horizonFactor.x > 0 ) + // { + // vec3 awayVec = normalize( OUT_fogPos.xyz - eyePos ); + // OUT_fogPos.xy += awayVec.xy * 1000.0; + // } // Convert to screen - posPostWave = modelview * posPostWave; + OUT_posPostWave = tMul( modelview, OUT_posPostWave ); // tMul( modelview, vec4( OUT_posPostWave.xyz, 1 ) ); // Setup the OUT position symantic variable - gl_Position = posPostWave; - //gl_Position.z = mix(gl_Position.z, gl_Position.w, horizonFactor.x); + OUT_hpos = OUT_posPostWave; // tMul( modelview, vec4( IN_position.xyz, 1 ) ); //vec4( OUT_posPostWave.xyz, 1 ); + //OUT_hpos.z = mix( OUT_hpos.z, OUT_hpos.w, IN_horizonFactor.x ); // Save world space camera dist/depth of the outgoing pixel - pixelDist = gl_Position.z; + OUT_pixelDist = OUT_hpos.z; // Convert to reflection texture space - posPostWave = texGen * posPostWave; + OUT_posPostWave = tMul( texGen, OUT_posPostWave ); vec2 txPos = undulatePos; - if ( horizonFactor.x > 0.0 ) + if ( bool(IN_horizonFactor.x) ) txPos = normalize( txPos ) * 50000.0; - - - // set up tex coordinates for the 3 interacting normal maps - rippleTexCoord01.xy = txPos * rippleTexScale[0]; - rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; + + // set up tex coordinates for the 3 interacting normal maps + OUT_rippleTexCoord01.xy = txPos * rippleTexScale[0]; + OUT_rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; mat2 texMat; texMat[0][0] = rippleMat[0].x; texMat[1][0] = rippleMat[0].y; texMat[0][1] = rippleMat[0].z; texMat[1][1] = rippleMat[0].w; - rippleTexCoord01.xy = texMat * rippleTexCoord01.xy ; + OUT_rippleTexCoord01.xy = tMul( texMat, OUT_rippleTexCoord01.xy ); - rippleTexCoord01.zw = txPos * rippleTexScale[1]; - rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; + OUT_rippleTexCoord01.zw = txPos * rippleTexScale[1]; + OUT_rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; texMat[0][0] = rippleMat[1].x; texMat[1][0] = rippleMat[1].y; texMat[0][1] = rippleMat[1].z; texMat[1][1] = rippleMat[1].w; - rippleTexCoord01.zw = texMat * rippleTexCoord01.zw ; + OUT_rippleTexCoord01.zw = tMul( texMat, OUT_rippleTexCoord01.zw ); - rippleTexCoord2.xy = txPos * rippleTexScale[2]; - rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; + OUT_rippleTexCoord2.xy = txPos * rippleTexScale[2]; + OUT_rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; texMat[0][0] = rippleMat[2].x; texMat[1][0] = rippleMat[2].y; texMat[0][1] = rippleMat[2].z; texMat[1][1] = rippleMat[2].w; - rippleTexCoord2.xy = texMat * rippleTexCoord2.xy ; - - - /*rippleTexCoord01.xy = mix( position.xy * rippleTexScale[0], txPos.xy * rippleTexScale[0], horizonFactor.x ); - rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; - - rippleTexCoord01.zw = mix( position.xy * rippleTexScale[1], txPos.xy * rippleTexScale[1], horizonFactor.x ); - rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; - - rippleTexCoord2.xy = mix( position.xy * rippleTexScale[2], txPos.xy * rippleTexScale[2], horizonFactor.x ); - rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; */ - - - /*rippleTexCoord01.xy = mix( position.xy * rippleTexScale[0], txPos.xy * rippleTexScale[0], horizonFactor.x ); - rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; - mat2 texMat; - texMat[0][0] = rippleMat[0].x; - texMat[1][0] = rippleMat[0].y; - texMat[0][1] = rippleMat[0].z; - texMat[1][1] = rippleMat[0].w; - rippleTexCoord01.xy = texMat * rippleTexCoord01.xy ; - - rippleTexCoord01.zw = mix( position.xy * rippleTexScale[1], txPos.xy * rippleTexScale[1], horizonFactor.x ); - rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; - texMat[0][0] = rippleMat[1].x; - texMat[1][0] = rippleMat[1].y; - texMat[0][1] = rippleMat[1].z; - texMat[1][1] = rippleMat[1].w; - rippleTexCoord01.zw = texMat * rippleTexCoord01.zw ; - - rippleTexCoord2.xy = mix( position.xy * rippleTexScale[2], txPos.xy * rippleTexScale[2], horizonFactor.x ); - rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; - texMat[0][0] = rippleMat[2].x; - texMat[1][0] = rippleMat[2].y; - texMat[0][1] = rippleMat[2].z; - texMat[1][1] = rippleMat[2].w; - rippleTexCoord2.xy = texMat * rippleTexCoord2.xy ;*/ + OUT_rippleTexCoord2.xy = tMul( texMat, OUT_rippleTexCoord2.xy ); #ifdef WATER_SPEC @@ -234,8 +214,8 @@ void main() vec3 normal; for ( int i = 0; i < 3; i++ ) { - binormal.z += undulateFade * waveDir[i].x * waveData[i].y * cos( waveDir[i].x * undulateData.x + waveDir[i].y * undulateData.y + elapsedTime * waveData[i].x ); - tangent.z += undulateFade * waveDir[i].y * waveData[i].y * cos( waveDir[i].x * undulateData.x + waveDir[i].y * undulateData.y + elapsedTime * waveData[i].x ); + binormal.z += undulateFade * waveDir[i].x * waveData[i].y * cos( waveDir[i].x * IN_undulateData.x + waveDir[i].y * IN_undulateData.y + elapsedTime * waveData[i].x ); + tangent.z += undulateFade * waveDir[i].y * waveData[i].y * cos( waveDir[i].x * IN_undulateData.x + waveDir[i].y * IN_undulateData.y + elapsedTime * waveData[i].x ); } binormal = normalize( binormal ); @@ -246,15 +226,19 @@ void main() worldToTangent[0] = binormal; worldToTangent[1] = tangent; worldToTangent[2] = normal; + + worldToTangent = transpose(worldToTangent); - misc.xyz = inLightVec * modelMat; - misc.xyz = worldToTangent * misc.xyz; + OUT_misc.xyz = tMul( inLightVec, modelMat ); + OUT_misc.xyz = tMul( worldToTangent, OUT_misc.xyz ); #else - misc.xyz = inLightVec; - + OUT_misc.xyz = inLightVec; + #endif - + + gl_Position = OUT_hpos; + correctSSP(gl_Position); } diff --git a/Templates/Empty/game/shaders/common/water/gl/waterP.glsl b/Templates/Empty/game/shaders/common/water/gl/waterP.glsl index bf482d724..3e15fe576 100644 --- a/Templates/Empty/game/shaders/common/water/gl/waterP.glsl +++ b/Templates/Empty/game/shaders/common/water/gl/waterP.glsl @@ -20,6 +20,7 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../gl/hlslCompat.glsl" #include "shadergen:/autogenConditioners.h" #include "../../gl/torque.glsl" @@ -27,10 +28,7 @@ // Defines //----------------------------------------------------------------------------- -#ifdef TORQUE_BASIC_LIGHTING - #define BASIC -#endif - +#define PIXEL_DIST IN_rippleTexCoord2.z // miscParams #define FRESNEL_BIAS miscParams[0] #define FRESNEL_POWER miscParams[1] @@ -57,33 +55,54 @@ #define DISTORT_FULL_DEPTH distortionParams[2] // foamParams -#define FOAM_SCALE foamParams[0] +#define FOAM_OPACITY foamParams[0] #define FOAM_MAX_DEPTH foamParams[1] +#define FOAM_AMBIENT_LERP foamParams[2] +#define FOAM_RIPPLE_INFLUENCE foamParams[3] -// Incoming data -// Worldspace position of this pixel -varying vec3 worldPos; +// specularParams +#define SPEC_POWER specularParams[3] +#define SPEC_COLOR specularParams.xyz + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- + +//ConnectData IN + +in vec4 hpos; // TexCoord 0 and 1 (xy,zw) for ripple texture lookup -varying vec4 rippleTexCoord01; +in vec4 rippleTexCoord01; -// TexCoord 2 for ripple texture lookup -varying vec2 rippleTexCoord2; +// xy is TexCoord 2 for ripple texture lookup +// z is the Worldspace unit distance/depth of this vertex/pixel +// w is amount of the crestFoam ( more at crest of waves ). +in vec4 rippleTexCoord2; // Screenspace vert position BEFORE wave transformation -varying vec4 posPreWave; +in vec4 posPreWave; // Screenspace vert position AFTER wave transformation -varying vec4 posPostWave; +in vec4 posPostWave; -// Worldspace unit distance/depth of this vertex/pixel -varying float pixelDist; +// Objectspace vert position BEFORE wave transformation +// w coord is world space z position. +in vec4 objPos; -varying vec3 fogPos; +in vec4 foamTexCoords; -varying float worldSpaceZ; +in mat3 tangentMat; -varying vec4 foamTexCoords; + +#define IN_hpos hpos +#define IN_rippleTexCoord01 rippleTexCoord01 +#define IN_rippleTexCoord2 rippleTexCoord2 +#define IN_posPreWave posPreWave +#define IN_posPostWave posPostWave +#define IN_objPos objPos +#define IN_foamTexCoords foamTexCoords +#define IN_tangentMat tangentMat //----------------------------------------------------------------------------- // approximate Fresnel function @@ -100,10 +119,10 @@ uniform sampler2D bumpMap; uniform sampler2D prepassTex; uniform sampler2D reflectMap; uniform sampler2D refractBuff; -uniform samplerCUBE skyMap; +uniform samplerCube skyMap; uniform sampler2D foamMap; -uniform vec4 specularColor; -uniform float specularPower; +uniform sampler1D depthGradMap; +uniform vec4 specularParams; uniform vec4 baseColor; uniform vec4 miscParams; uniform vec2 fogParams; @@ -112,64 +131,45 @@ uniform vec3 reflectNormal; uniform vec2 wetnessParams; uniform float farPlaneDist; uniform vec3 distortionParams; -//uniform vec4 renderTargetParams; -uniform vec2 foamParams; -uniform vec3 foamColorMod; +uniform vec4 foamParams; uniform vec3 ambientColor; -uniform vec3 eyePos; -uniform vec3 inLightVec; +uniform vec3 eyePos; // This is in object space! uniform vec3 fogData; uniform vec4 fogColor; -//uniform vec4 rtParams; -uniform vec2 rtScale; -uniform vec2 rtHalfPixel; -uniform vec4 rtOffset; -uniform vec3 rippleMagnitude; +uniform vec4 rippleMagnitude; +uniform vec4 rtParams1; +uniform float depthGradMax; +uniform vec3 inLightVec; +uniform mat4 modelMat; +uniform vec4 sunColor; +uniform float sunBrightness; +uniform float reflectivity; //----------------------------------------------------------------------------- // Main //----------------------------------------------------------------------------- void main() { - vec4 rtParams = vec4( rtOffset.x / rtOffset.z + rtHalfPixel.x, - rtOffset.y / rtOffset.w + rtHalfPixel.x, - rtScale ); - - // Modulate baseColor by the ambientColor. - vec4 waterBaseColor = baseColor * vec4( ambientColor.rgb, 1 ); - // Get the bumpNorm... - vec3 bumpNorm = ( tex2D( bumpMap, IN.rippleTexCoord01.xy ) * 2.0 - 1.0 ) * rippleMagnitude.x; - bumpNorm += ( tex2D( bumpMap, IN.rippleTexCoord01.zw ) * 2.0 - 1.0 ) * rippleMagnitude.y; - bumpNorm += ( tex2D( bumpMap, IN.rippleTexCoord2 ) * 2.0 - 1.0 ) * rippleMagnitude.z; - - // JCF: this was here, but seems to make the dot product against the bump - // normal we use below for cubeMap fade-in to be less reliable. - //bumpNorm.xy *= 0.75; - //bumpNorm = normalize( bumpNorm ); - //return vec4( bumpNorm, 1 ); + vec3 bumpNorm = ( texture( bumpMap, IN_rippleTexCoord01.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.x; + bumpNorm += ( texture( bumpMap, IN_rippleTexCoord01.zw ).rgb * 2.0 - 1.0 ) * rippleMagnitude.y; + bumpNorm += ( texture( bumpMap, IN_rippleTexCoord2.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.z; + + bumpNorm = normalize( bumpNorm ); + bumpNorm = mix( bumpNorm, vec3(0,0,1), 1.0 - rippleMagnitude.w ); + bumpNorm = tMul( bumpNorm, IN_tangentMat ); // Get depth of the water surface (this pixel). // Convert from WorldSpace to EyeSpace. - float pixelDepth = IN.pixelDist / farPlaneDist; + float pixelDepth = PIXEL_DIST / farPlaneDist; - // Get prepass depth at the undistorted pixel. - //vec4 prepassCoord = IN.posPostWave; - //prepassCoord.xy += renderTargetParams.xy; - vec2 prepassCoord = viewportCoordToRenderTarget( IN.posPostWave, rtParams ); - //vec2 prepassCoord = IN.posPostWave.xy; + vec2 prepassCoord = viewportCoordToRenderTarget( IN_posPostWave, rtParams1 ); - float startDepth = prepassUncondition( tex2D( prepassTex, prepassCoord ) ).w; - //return vec4( startDepth.rrr, 1 ); + float startDepth = prepassUncondition( prepassTex, prepassCoord ).w; // The water depth in world units of the undistorted pixel. float startDelta = ( startDepth - pixelDepth ); - if ( startDelta <= 0.0 ) - { - //return vec4( 1, 0, 0, 1 ); - startDelta = 0; - } - + startDelta = max( startDelta, 0.0 ); startDelta *= farPlaneDist; // Calculate the distortion amount for the water surface. @@ -177,23 +177,22 @@ void main() // We subtract a little from it so that we don't // distort where the water surface intersects the // camera near plane. - float distortAmt = saturate( ( IN.pixelDist - DISTORT_START_DIST ) / DISTORT_END_DIST ); + float distortAmt = saturate( ( PIXEL_DIST - DISTORT_START_DIST ) / DISTORT_END_DIST ); // Scale down distortion in shallow water. distortAmt *= saturate( startDelta / DISTORT_FULL_DEPTH ); - //distortAmt = 0; // Do the intial distortion... we might remove it below. vec2 distortDelta = bumpNorm.xy * distortAmt; - vec4 distortPos = IN.posPostWave; + vec4 distortPos = IN_posPostWave; distortPos.xy += distortDelta; - prepassCoord = viewportCoordToRenderTarget( distortPos, rtParams ); - //prepassCoord = distortPos; - //prepassCoord.xy += renderTargetParams.xy; + prepassCoord = viewportCoordToRenderTarget( distortPos, rtParams1 ); // Get prepass depth at the position of this distorted pixel. - float prepassDepth = prepassUncondition( tex2D( prepassTex, prepassCoord ) ).w; + float prepassDepth = prepassUncondition( prepassTex, prepassCoord ).w; + if ( prepassDepth > 0.99 ) + prepassDepth = 5.0; float delta = ( prepassDepth - pixelDepth ) * farPlaneDist; @@ -202,7 +201,7 @@ void main() // If we got a negative delta then the distorted // sample is above the water surface. Mask it out // by removing the distortion. - distortPos = IN.posPostWave; + distortPos = IN_posPostWave; delta = startDelta; distortAmt = 0; } @@ -212,20 +211,20 @@ void main() if ( diff < 0 ) { - distortAmt = saturate( ( IN.pixelDist - DISTORT_START_DIST ) / DISTORT_END_DIST ); + distortAmt = saturate( ( PIXEL_DIST - DISTORT_START_DIST ) / DISTORT_END_DIST ); distortAmt *= saturate( delta / DISTORT_FULL_DEPTH ); distortDelta = bumpNorm.xy * distortAmt; - distortPos = IN.posPostWave; + distortPos = IN_posPostWave; distortPos.xy += distortDelta; - prepassCoord = viewportCoordToRenderTarget( distortPos, rtParams ); - //prepassCoord = distortPos; - //prepassCoord.xy += renderTargetParams.xy; + prepassCoord = viewportCoordToRenderTarget( distortPos, rtParams1 ); // Get prepass depth at the position of this distorted pixel. - prepassDepth = prepassUncondition( tex2D( prepassTex, prepassCoord ) ).w; + prepassDepth = prepassUncondition( prepassTex, prepassCoord ).w; + if ( prepassDepth > 0.99 ) + prepassDepth = 5.0; delta = ( prepassDepth - pixelDepth ) * farPlaneDist; } @@ -234,133 +233,78 @@ void main() // If we got a negative delta then the distorted // sample is above the water surface. Mask it out // by removing the distortion. - distortPos = IN.posPostWave; + distortPos = IN_posPostWave; delta = startDelta; distortAmt = 0; } } - //return vec4( prepassDepth.rrr, 1 ); - - vec4 temp = IN.posPreWave; + vec4 temp = IN_posPreWave; temp.xy += bumpNorm.xy * distortAmt; - vec2 reflectCoord = viewportCoordToRenderTarget( temp, rtParams ); + vec2 reflectCoord = viewportCoordToRenderTarget( temp, rtParams1 ); - vec2 refractCoord = viewportCoordToRenderTarget( distortPos, rtParams ); + vec2 refractCoord = viewportCoordToRenderTarget( distortPos, rtParams1 ); - // Use cubemap colors instead of reflection colors in several cases... - - // First lookup the CubeMap color - // JCF: which do we want to use here, the reflectNormal or the bumpNormal - // neithor of them is exactly right and how can we combine the two together? - //bumpNorm = reflectNormal; - vec3 eyeVec = IN.worldPos - eyePos; + vec4 fakeColor = vec4(ambientColor,1); + vec3 eyeVec = IN_objPos.xyz - eyePos; + eyeVec = tMul( mat3(modelMat), eyeVec ); + eyeVec = tMul( IN_tangentMat, eyeVec ); vec3 reflectionVec = reflect( eyeVec, bumpNorm ); - //vec4 cubeColor = texCUBE( skyMap, reflectionVec ); - //return cubeColor; - // JCF: using ambient color instead of cubeColor for waterPlane, how do we still use the cubemap for rivers? - vec4 cubeColor = vec4(ambientColor,1); - //cubeColor.rgb = vec3( 0, 0, 1 ); - // Use cubeColor for waves that are angled towards camera + // Use fakeColor for ripple-normals that are angled towards the camera eyeVec = -eyeVec; eyeVec = normalize( eyeVec ); float ang = saturate( dot( eyeVec, bumpNorm ) ); - float cubeAmt = ang; + float fakeColorAmt = ang; - //float rplaneDist = (reflectPlane.x * IN.pos.x + reflectPlane.y * IN.pos.y + reflectPlane.z * IN.pos.z) + reflectPlane.w; - //rplaneDist = saturate( abs( rplaneDist ) / 0.5 ); - - -//#ifdef RIVER // for verts far from the reflect plane z position - float rplaneDist = abs( REFLECT_PLANE_Z - IN.worldPos.z ); + float rplaneDist = abs( REFLECT_PLANE_Z - IN_objPos.w ); rplaneDist = saturate( ( rplaneDist - 1.0 ) / 2.0 ); - //rplaneDist = REFLECT_PLANE_Z / eyePos.z; rplaneDist *= ISRIVER; - cubeAmt = max( cubeAmt, rplaneDist ); -//#endif - - //rplaneDist = IN.worldPos.z / eyePos.z; - - //return vec4( rplaneDist.rrr, 1 ); - //return vec4( (reflectParams[REFLECT_PLANE_Z] / 86.0 ).rrr, 1 ); - - // and for verts farther from the camera - //float cubeAmt = ( eyeDist - reflectParams[REFLECT_MIN_DIST] ) / ( reflectParams[REFLECT_MAX_DIST] - reflectParams[REFLECT_MIN_DIST] ); - //cubeAmt = saturate ( cubeAmt ); - - //float temp = ( eyeDist - reflectParams[REFLECT_MIN_DIST] ) / ( reflectParams[REFLECT_MAX_DIST] - reflectParams[REFLECT_MIN_DIST] ); - //temp = saturate ( temp ); - - // If the camera is very very close to the reflect plane. - //float eyeToPlaneDist = eyePos.z - REFLECT_PLANE_Z; // dot( reflectNormal, eyePos ) + REFLECT_PLANE_Z; - //eyeToPlaneDist = abs( eyeToPlaneDist ); - //eyeToPlaneDist = 1.0 - saturate( abs( eyeToPlaneDist ) / 1 ); - - //return vec4( eyeToPlaneDist.rrr, 1 ); - - //cubeAmt = max( cubeAmt, eyeToPlaneDist ); - //cubeAmt = max( cubeAmt, rplaneDist ); - //cubeAmt = max( cubeAmt, ang ); - //cubeAmt = max( cubeAmt, rplaneDist ); - //cubeAmt = max( cubeAmt, IN.depth.w ); - - // All cubemap if fullReflect is specifically user disabled - cubeAmt = max( cubeAmt, NO_REFLECT ); + fakeColorAmt = max( fakeColorAmt, rplaneDist ); #ifndef UNDERWATER - // Get foam color and amount - IN.foamTexCoords.xy += distortDelta * 0.5; - IN.foamTexCoords.zw += distortDelta * 0.5; + vec2 foamRippleOffset = bumpNorm.xy * FOAM_RIPPLE_INFLUENCE; + vec4 IN_foamTexCoords = IN_foamTexCoords; + IN_foamTexCoords.xy += foamRippleOffset; + IN_foamTexCoords.zw += foamRippleOffset; - vec4 foamColor = tex2D( foamMap, IN.foamTexCoords.xy ); - foamColor += tex2D( foamMap, IN.foamTexCoords.zw ); - //foamColor += tex2D( foamMap, IN.rippleTexCoord2 ) * 0.3; + vec4 foamColor = texture( foamMap, IN_foamTexCoords.xy ); + foamColor += texture( foamMap, IN_foamTexCoords.zw ); foamColor = saturate( foamColor ); - // Modulate foam color by ambient color so we don't have glowing white - // foam at night. - foamColor.rgb = lerp( foamColor.rgb, ambientColor.rgb, foamColorMod.rgb ); + + // Modulate foam color by ambient color + // so we don't have glowing white foam at night. + foamColor.rgb = mix( foamColor.rgb, ambientColor.rgb, FOAM_AMBIENT_LERP ); float foamDelta = saturate( delta / FOAM_MAX_DEPTH ); - float foamAmt = 1.0 - foamDelta; + float foamAmt = 1 - pow( foamDelta, 2 ); // Fade out the foam in very very low depth, // this improves the shoreline a lot. float diff = 0.8 - foamAmt; if ( diff < 0.0 ) - { - //return vec4( 1,0,0,1 ); foamAmt -= foamAmt * abs( diff ) / 0.2; - } - //return vec4( foamAmt.rrr, 1 ); - - foamAmt *= FOAM_SCALE * foamColor.a; - //return vec4( foamAmt.rrr, 1 ); - // Get reflection map color - vec4 refMapColor = tex2D( reflectMap, reflectCoord ); + foamAmt *= FOAM_OPACITY * foamColor.a; - //cubeAmt = 0; + foamColor.rgb *= FOAM_OPACITY * foamAmt * foamColor.a; - // Combine cube and foam colors into reflect color - vec4 reflectColor = lerp( refMapColor, cubeColor, cubeAmt ); - //return refMapColor; + // Get reflection map color. + vec4 refMapColor = hdrDecode( texture( reflectMap, reflectCoord ) ); - // This doesn't work because REFLECT_PLANE_Z is in worldSpace - // while eyePos is actually in objectSpace! + // If we do not have a reflection texture then we use the cubemap. + refMapColor = mix( refMapColor, texture( skyMap, reflectionVec ), NO_REFLECT ); - //float eyeToPlaneDist = eyePos.z - REFLECT_PLANE_Z; // dot( reflectNormal, eyePos ) + REFLECT_PLANE_Z; - //float transitionFactor = 1.0 - saturate( ( abs( eyeToPlaneDist ) - 0.5 ) / 5 ); - //reflectColor = lerp( reflectColor, waterBaseColor, transitionFactor ); - - //return reflectColor; + fakeColor = ( texture( skyMap, reflectionVec ) ); + fakeColor.a = 1; + // Combine reflection color and fakeColor. + vec4 reflectColor = mix( refMapColor, fakeColor, fakeColorAmt ); // Get refract color - vec4 refractColor = tex2D( refractBuff, refractCoord ); - //return refractColor; + vec4 refractColor = hdrDecode( texture( refractBuff, refractCoord ) ); // We darken the refraction color a bit to make underwater // elements look wet. We fade out this darkening near the @@ -371,86 +315,80 @@ void main() // Add Water fog/haze. float fogDelta = delta - FOG_DENSITY_OFFSET; - //return vec4( fogDelta.rrr, 1 ); + if ( fogDelta < 0.0 ) fogDelta = 0.0; float fogAmt = 1.0 - saturate( exp( -FOG_DENSITY * fogDelta ) ); - //return vec4( fogAmt.rrr, 1 ); + + // Calculate the water "base" color based on depth. + vec4 waterBaseColor = baseColor * texture( depthGradMap, saturate( delta / depthGradMax ) ); + + // Modulate baseColor by the ambientColor. + waterBaseColor *= vec4( ambientColor.rgb, 1 ); // calc "diffuse" color by lerping from the water color // to refraction image based on the water clarity. - vec4 diffuseColor = lerp( refractColor, waterBaseColor, fogAmt ); + vec4 diffuseColor = mix( refractColor, waterBaseColor, fogAmt ); // fresnel calculation float fresnelTerm = fresnel( ang, FRESNEL_BIAS, FRESNEL_POWER ); - //return vec4( fresnelTerm.rrr, 1 ); // Scale the frensel strength by fog amount // so that parts that are very clear get very little reflection. fresnelTerm *= fogAmt; - //return vec4( fresnelTerm.rrr, 1 ); // Also scale the frensel by our distance to the // water surface. This removes the hard reflection // when really close to the water surface. - fresnelTerm *= saturate( IN.pixelDist - 0.1 ); + fresnelTerm *= saturate( PIXEL_DIST - 0.1 ); + + fresnelTerm *= reflectivity; // Combine the diffuse color and reflection image via the // fresnel term and set out output color. - vec4 gl_FragColor = lerp( diffuseColor, reflectColor, fresnelTerm ); + vec4 OUT = mix( diffuseColor, reflectColor, fresnelTerm ); - //float brightness = saturate( 1.0 - ( waterHeight - eyePosWorld.z - 5.0 ) / 50.0 ); - //gl_FragColor.rgb *= brightness; + vec3 lightVec = inLightVec; + + // Get some specular reflection. + vec3 newbump = bumpNorm; + newbump.xy *= 3.5; + newbump = normalize( bumpNorm ); + vec3 halfAng = normalize( eyeVec + -lightVec ); + float specular = saturate( dot( newbump, halfAng ) ); + specular = pow( specular, SPEC_POWER ); + + // Scale down specularity in very shallow water to improve the transparency of the shoreline. + specular *= saturate( delta / 2 ); + OUT.rgb = OUT.rgb + ( SPEC_COLOR * vec3(specular) ); #else - vec4 refractColor = tex2D( refractBuff, refractCoord ); - vec4 gl_FragColor = refractColor; + + vec4 refractColor = hdrDecode( texture( refractBuff, refractCoord ) ); + vec4 OUT = refractColor; + #endif #ifndef UNDERWATER - gl_FragColor.rgb = lerp( gl_FragColor.rgb, foamColor.rgb, foamAmt ); -#endif - gl_FragColor.a = 1.0; - - // specular experiments - -// 1: -/* - float fDot = dot( bumpNorm, inLightVec ); - vec3 reflect = normalize( 2.0 * bumpNorm * fDot - eyeVec ); - // float specular = saturate(dot( reflect, inLightVec ) ); - float specular = pow( reflect, specularPower ); - gl_FragColor += specularColor * specular; -*/ - - -// 2: This almost looks good -/* - bumpNorm.xy *= 2.0; - bumpNorm = normalize( bumpNorm ); - - vec3 halfAng = normalize( eyeVec + inLightVec ); - float specular = saturate( dot( bumpNorm, halfAng) ); - specular = pow(specular, specularPower); - gl_FragColor += specularColor * specular; -*/ - -#ifndef UNDERWATER + OUT.rgb = OUT.rgb + foamColor.rgb; float factor = computeSceneFog( eyePos, - IN.fogPos, - IN.worldSpaceZ, + IN_objPos.xyz, + IN_objPos.w, fogData.x, fogData.y, fogData.z ); - gl_FragColor.rgb = lerp( gl_FragColor.rgb, fogColor.rgb, 1.0 - saturate( factor ) ); + OUT.rgb = mix( OUT.rgb, fogColor.rgb, 1.0 - saturate( factor ) ); + + //OUT.rgb = fogColor.rgb; #endif - //return vec4( refMapColor.rgb, 1 ); - gl_FragColor.a = 1.0; + OUT.a = 1.0; + + //return OUT; - return gl_FragColor; + OUT_FragColor0 = hdrEncode( OUT ); } diff --git a/Templates/Empty/game/shaders/common/water/gl/waterV.glsl b/Templates/Empty/game/shaders/common/water/gl/waterV.glsl index d4337476f..490af63a7 100644 --- a/Templates/Empty/game/shaders/common/water/gl/waterV.glsl +++ b/Templates/Empty/game/shaders/common/water/gl/waterV.glsl @@ -20,58 +20,86 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../gl/hlslCompat.glsl" #include "shadergen:/autogenConditioners.h" +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- +struct VertData +{ + vec4 position ;// POSITION; + vec3 normal ;// NORMAL; + vec2 undulateData ;// TEXCOORD0; + vec4 horizonFactor ;// TEXCOORD1; +}; + //----------------------------------------------------------------------------- // Defines //----------------------------------------------------------------------------- +//VertData IN +in vec4 vPosition; +in vec3 vNormal; +in vec2 vTexCoord0; +in vec4 vTexCoord1; -// waveData -#define WAVE_SPEED(i) waveData[i].x -#define WAVE_MAGNITUDE(i) waveData[i].y +#define IN_position_ vPosition +#define IN_normal vNormal +#define IN_undulateData vTexCoord0 +#define IN_horizonFactor vTexCoord1 -// Outgoing data -// Worldspace position of this pixel -varying vec3 worldPos; +//ConnectData OUT +// + out vec4 hpos ; // TexCoord 0 and 1 (xy,zw) for ripple texture lookup -varying vec4 rippleTexCoord01; +out vec4 rippleTexCoord01; -// TexCoord 2 for ripple texture lookup -varying vec2 rippleTexCoord2; + // xy is TexCoord 2 for ripple texture lookup + // z is the Worldspace unit distance/depth of this vertex/pixel + // w is amount of the crestFoam ( more at crest of waves ). + out vec4 rippleTexCoord2 ; // Screenspace vert position BEFORE wave transformation -varying vec4 posPreWave; +out vec4 posPreWave; // Screenspace vert position AFTER wave transformation -varying vec4 posPostWave; +out vec4 posPostWave; -// Worldspace unit distance/depth of this vertex/pixel -varying float pixelDist; + // Objectspace vert position BEFORE wave transformation + // w coord is world space z position. + out vec4 objPos ; -varying vec3 fogPos; + out vec4 foamTexCoords ; -varying float worldSpaceZ; + out mat3 tangentMat ; +// -varying vec4 foamTexCoords; +#define OUT_hpos hpos +#define OUT_rippleTexCoord01 rippleTexCoord01 +#define OUT_rippleTexCoord2 rippleTexCoord2 +#define OUT_posPreWave posPreWave +#define OUT_posPostWave posPostWave +#define OUT_objPos objPos +#define OUT_foamTexCoords foamTexCoords +#define OUT_tangentMat tangentMat //----------------------------------------------------------------------------- // Uniforms //----------------------------------------------------------------------------- uniform mat4 modelMat; uniform mat4 modelview; -uniform mat3 cubeTrans; -uniform mat4 objTrans; -uniform vec3 cubeEyePos; +uniform vec4 rippleMat[3]; uniform vec3 eyePos; uniform vec2 waveDir[3]; uniform vec2 waveData[3]; uniform vec2 rippleDir[3]; uniform vec2 rippleTexScale[3]; uniform vec3 rippleSpeed; -uniform vec2 reflectTexSize; +uniform vec4 foamDir; +uniform vec4 foamTexScale; +uniform vec2 foamSpeed; uniform vec3 inLightVec; -uniform vec3 reflectNormal; uniform float gridElementSize; uniform float elapsedTime; uniform float undulateMaxDist; @@ -81,97 +109,133 @@ uniform float undulateMaxDist; //----------------------------------------------------------------------------- void main() { - // Copy incoming attributes into locals so we can modify them in place. - vec4 position = gl_Vertex.xyzw; - vec3 normal = gl_Normal.xyz; - vec2 undulateData = gl_MultiTexCoord0.st; - vec4 horizonFactor = gl_MultiTexCoord1.xyzw; + vec4 IN_position = IN_position_; // use projection matrix for reflection / refraction texture coords - mat4 texGen = { 0.5, 0.0, 0.0, 0.5, //+ 0.5 / reflectTexSize.x, - 0.0, 0.5, 0.0, 0.5, //+ 1.0 / reflectTexSize.y, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0 }; + mat4 texGen = mat4FromRow( 0.5, 0.0, 0.0, 0.5, + 0.0, -0.5, 0.0, 0.5, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 ); - // Move the vertex based on the horizonFactor if specified to do so for this vert. - if ( horizonFactor.z > 0 ) - { - vec2 offsetXY = eyePos.xy - eyePos.xy % gridElementSize; - position.xy += offsetXY; - undulateData += offsetXY; - } + IN_position.z = mix( IN_position.z, eyePos.z, IN_horizonFactor.x ); - fogPos = position; - position.z = mix( position.z, eyePos.z, horizonFactor.x ); + OUT_objPos = IN_position; + OUT_objPos.w = tMul( modelMat, IN_position ).z; // Send pre-undulation screenspace position - posPreWave = modelview * position; - posPreWave = texGen * posPreWave; + OUT_posPreWave = tMul( modelview, IN_position ); + OUT_posPreWave = tMul( texGen, OUT_posPreWave ); // Calculate the undulation amount for this vertex. - vec2 undulatePos = undulateData; - float undulateAmt = 0; + vec2 undulatePos = tMul( modelMat, vec4 ( IN_undulateData.xy, 0, 1 ) ).xy; + float undulateAmt = 0.0; - for ( int i = 0; i < 3; i++ ) - { - undulateAmt += WAVE_MAGNITUDE(i) * sin( elapsedTime * WAVE_SPEED(i) + - undulatePos.x * waveDir[i].x + - undulatePos.y * waveDir[i].y ); - } + undulateAmt += waveData[0].y * sin( elapsedTime * waveData[0].x + + undulatePos.x * waveDir[0].x + + undulatePos.y * waveDir[0].y ); + undulateAmt += waveData[1].y * sin( elapsedTime * waveData[1].x + + undulatePos.x * waveDir[1].x + + undulatePos.y * waveDir[1].y ); + undulateAmt += waveData[2].y * sin( elapsedTime * waveData[2].x + + undulatePos.x * waveDir[2].x + + undulatePos.y * waveDir[2].y ); + + float undulateFade = 1; // Scale down wave magnitude amount based on distance from the camera. - float dist = distance( position, eyePos ); + float dist = distance( IN_position.xyz, eyePos ); dist = clamp( dist, 1.0, undulateMaxDist ); - undulateAmt *= ( 1 - dist / undulateMaxDist ); + undulateFade *= ( 1 - dist / undulateMaxDist ); // Also scale down wave magnitude if the camera is very very close. - undulateAmt *= clamp( ( distance( IN.position, eyePos ) - 0.5 ) / 10.0, 0.0, 1.0 ); + undulateFade *= saturate( ( distance( IN_position.xyz, eyePos ) - 0.5 ) / 10.0 ); + + undulateAmt *= undulateFade; + + OUT_rippleTexCoord2.w = undulateAmt / ( waveData[0].y + waveData[1].y + waveData[2].y ); + OUT_rippleTexCoord2.w = saturate( OUT_rippleTexCoord2.w - 0.2 ) / 0.8; // Apply wave undulation to the vertex. - posPostWave = position; - posPostWave.xyz += normal.xyz * undulateAmt; - - // Save worldSpace position of this pixel/vert - worldPos = posPostWave.xyz; + OUT_posPostWave = IN_position; + OUT_posPostWave.xyz += IN_normal.xyz * undulateAmt; // Convert to screen - posPostWave = modelview * posPostWave; + OUT_posPostWave = tMul( modelview, OUT_posPostWave ); // Setup the OUT position symantic variable - gl_Position = posPostWave; - gl_Position.z = mix(gl_Position.z, gl_Position.w, horizonFactor.x); + OUT_hpos = OUT_posPostWave; + //OUT_hpos.z = mix( OUT_hpos.z, OUT_hpos.w, IN_horizonFactor.x ); - worldSpaceZ = modelMat * vec4(fogPos, 1.0) ).z; - if ( horizonFactor.x > 0.0 ) - { - vec3 awayVec = normalize( fogPos.xyz - eyePos ); - fogPos.xy += awayVec.xy * 1000.0; - } + // if ( IN_horizonFactor.x > 0 ) + // { + // vec3 awayVec = normalize( OUT_objPos.xyz - eyePos ); + // OUT_objPos.xy += awayVec.xy * 1000.0; + // } // Save world space camera dist/depth of the outgoing pixel - pixelDist = gl_Position.z; + OUT_rippleTexCoord2.z = OUT_hpos.z; // Convert to reflection texture space - posPostWave = texGen * posPostWave; + OUT_posPostWave = tMul( texGen, OUT_posPostWave ); - float2 ripplePos = undulateData; - float2 txPos = normalize( ripplePos ); - txPos *= 50000.0; - ripplePos = mix( ripplePos, txPos, IN.horizonFactor.x ); + vec2 txPos = undulatePos; + if ( bool(IN_horizonFactor.x) ) + txPos = normalize( txPos ) * 50000.0; // set up tex coordinates for the 3 interacting normal maps - rippleTexCoord01.xy = mix( ripplePos * rippleTexScale[0], txPos.xy * rippleTexScale[0], IN.horizonFactor.x ); - rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; + OUT_rippleTexCoord01.xy = txPos * rippleTexScale[0]; + OUT_rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; - rippleTexCoord01.zw = mix( ripplePos * rippleTexScale[1], txPos.xy * rippleTexScale[1], IN.horizonFactor.x ); - rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; + mat2 texMat; + texMat[0][0] = rippleMat[0].x; + texMat[1][0] = rippleMat[0].y; + texMat[0][1] = rippleMat[0].z; + texMat[1][1] = rippleMat[0].w; + OUT_rippleTexCoord01.xy = tMul( texMat, OUT_rippleTexCoord01.xy ); - rippleTexCoord2.xy = mix( ripplePos * rippleTexScale[2], txPos.xy * rippleTexScale[2], IN.horizonFactor.x ); - rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; + OUT_rippleTexCoord01.zw = txPos * rippleTexScale[1]; + OUT_rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; + + texMat[0][0] = rippleMat[1].x; + texMat[1][0] = rippleMat[1].y; + texMat[0][1] = rippleMat[1].z; + texMat[1][1] = rippleMat[1].w; + OUT_rippleTexCoord01.zw = tMul( texMat, OUT_rippleTexCoord01.zw ); - foamTexCoords.xy = mix( ripplePos * 0.2, txPos.xy * rippleTexScale[0], IN.horizonFactor.x ); - foamTexCoords.xy += rippleDir[0] * sin( ( elapsedTime + 500.0 ) * -0.4 ) * 0.15; + OUT_rippleTexCoord2.xy = txPos * rippleTexScale[2]; + OUT_rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; - foamTexCoords.zw = mix( ripplePos * 0.3, txPos.xy * rippleTexScale[1], IN.horizonFactor.x ); - foamTexCoords.zw += rippleDir[1] * sin( elapsedTime * 0.4 ) * 0.15; + texMat[0][0] = rippleMat[2].x; + texMat[1][0] = rippleMat[2].y; + texMat[0][1] = rippleMat[2].z; + texMat[1][1] = rippleMat[2].w; + OUT_rippleTexCoord2.xy = tMul( texMat, OUT_rippleTexCoord2.xy ); + + OUT_foamTexCoords.xy = txPos * foamTexScale.xy + foamDir.xy * foamSpeed.x * elapsedTime; + OUT_foamTexCoords.zw = txPos * foamTexScale.zw + foamDir.zw * foamSpeed.y * elapsedTime; + + + vec3 binormal = vec3 ( 1, 0, 0 ); + vec3 tangent = vec3 ( 0, 1, 0 ); + vec3 normal; + for ( int i = 0; i < 3; i++ ) + { + binormal.z += undulateFade * waveDir[i].x * waveData[i].y * cos( waveDir[i].x * undulatePos.x + waveDir[i].y * undulatePos.y + elapsedTime * waveData[i].x ); + tangent.z += undulateFade * waveDir[i].y * waveData[i].y * cos( waveDir[i].x * undulatePos.x + waveDir[i].y * undulatePos.y + elapsedTime * waveData[i].x ); + } + + binormal = binormal; + tangent = tangent; + normal = cross( binormal, tangent ); + + mat3 worldToTangent; + worldToTangent[0] = binormal; + worldToTangent[1] = tangent; + worldToTangent[2] = normal; + + OUT_tangentMat = transpose(worldToTangent); + + gl_Position = OUT_hpos; + correctSSP(gl_Position); } + diff --git a/Templates/Full/game/shaders/common/gl/blurP.glsl b/Templates/Full/game/shaders/common/gl/blurP.glsl index bc05b992f..5c37ebc6b 100644 --- a/Templates/Full/game/shaders/common/gl/blurP.glsl +++ b/Templates/Full/game/shaders/common/gl/blurP.glsl @@ -26,12 +26,12 @@ uniform vec4 kernel; uniform sampler2D diffuseMap; -varying vec2 texc0, texc1, texc2, texc3; +in vec2 texc0, texc1, texc2, texc3; void main() { - gl_FragColor = texture2D(diffuseMap, texc0) * kernel.x; - gl_FragColor += texture2D(diffuseMap, texc1) * kernel.y; - gl_FragColor += texture2D(diffuseMap, texc2) * kernel.z; - gl_FragColor += texture2D(diffuseMap, texc3) * kernel.w; + OUT_FragColor0 = texture(diffuseMap, texc0) * kernel.x; + OUT_FragColor0 += texture(diffuseMap, texc1) * kernel.y; + OUT_FragColor0 += texture(diffuseMap, texc2) * kernel.z; + OUT_FragColor0 += texture(diffuseMap, texc3) * kernel.w; } diff --git a/Templates/Full/game/shaders/common/gl/blurV.glsl b/Templates/Full/game/shaders/common/gl/blurV.glsl index d5d615fb9..1bfb0cd1b 100644 --- a/Templates/Full/game/shaders/common/gl/blurV.glsl +++ b/Templates/Full/game/shaders/common/gl/blurV.glsl @@ -24,20 +24,25 @@ // Glow shader //***************************************************************************** +in vec4 vPosition; +in vec4 vColor; +in vec2 vTexCoord0; + uniform mat4 modelview; uniform vec2 offset0, offset1, offset2, offset3; -varying vec2 texc0, texc1, texc2, texc3; +out vec2 texc0, texc1, texc2, texc3; void main() { - gl_Position = modelview * gl_Vertex; + gl_Position = modelview * vPosition; - vec2 tc = gl_MultiTexCoord0.st; + vec2 tc = vTexCoord0.st; tc.y = 1.0 - tc.y; texc0 = tc + offset0; texc1 = tc + offset1; texc2 = tc + offset2; texc3 = tc + offset3; + gl_Position.y *= -1; } \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/gl/cloudLayerP.glsl b/Templates/Full/game/shaders/common/gl/cloudLayerP.glsl index 326f2d3c6..da3996d58 100644 --- a/Templates/Full/game/shaders/common/gl/cloudLayerP.glsl +++ b/Templates/Full/game/shaders/common/gl/cloudLayerP.glsl @@ -22,12 +22,20 @@ #include "hlslCompat.glsl" -varying vec4 texCoord12; -varying vec4 texCoord34; -varying vec3 vLightTS; // light vector in tangent space, denormalized -varying vec3 vViewTS; // view vector in tangent space, denormalized -varying vec3 vNormalWS; // Normal vector in world space -varying float worldDist; +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- +//ConnectData +in vec4 texCoord12; +#define IN_texCoord12 texCoord12 +in vec4 texCoord34; +#define IN_texCoord34 texCoord34 +in vec3 vLightTS; // light vector in tangent space, denormalized +#define IN_vLightTS vLightTS +in vec3 vViewTS; // view vector in tangent space, denormalized +#define IN_vViewTS vViewTS +in float worldDist; +#define IN_worldDist worldDist //----------------------------------------------------------------------------- // Uniforms @@ -37,6 +45,7 @@ uniform vec3 ambientColor; uniform vec3 sunColor; uniform float cloudCoverage; uniform vec3 cloudBaseColor; +uniform float cloudExposure; //----------------------------------------------------------------------------- // Globals @@ -97,26 +106,25 @@ void main() // Normalize the interpolated vectors: vec3 vViewTS = normalize( vViewTS ); vec3 vLightTS = normalize( vLightTS ); - vec3 vNormalWS = normalize( vNormalWS ); - vec4 cResultColor = float4( 0, 0, 0, 1 ); + vec4 cResultColor = vec4( 0, 0, 0, 1 ); - vec2 texSample = texCoord12.xy; + vec2 texSample = IN_texCoord12.xy; - vec4 noise1 = texture2D( normalHeightMap, texCoord12.zw ); + vec4 noise1 = texture( normalHeightMap, IN_texCoord12.zw ); noise1 = normalize( ( noise1 - 0.5 ) * 2.0 ); //return noise1; - vec4 noise2 = texture2D( normalHeightMap, texCoord34.xy ); + vec4 noise2 = texture( normalHeightMap, IN_texCoord34.xy ); noise2 = normalize( ( noise2 - 0.5 ) * 2.0 ); //return noise2; vec3 noiseNormal = normalize( noise1 + noise2 ).xyz; - //return float4( noiseNormal, 1.0 ); + //return vec4( noiseNormal, 1.0 ); float noiseHeight = noise1.a * noise2.a * ( cloudCoverage / 2.0 + 0.5 ); - vec3 vNormalTS = normalize( texture2D( normalHeightMap, texSample ).xyz * 2.0 - 1.0 ); + vec3 vNormalTS = normalize( texture( normalHeightMap, texSample ).xyz * 2.0 - 1.0 ); vNormalTS += noiseNormal; vNormalTS = normalize( vNormalTS ); @@ -124,16 +132,14 @@ void main() cResultColor.rgb = ComputeIllumination( texSample, vLightTS, vViewTS, vNormalTS ); float coverage = ( cloudCoverage - 0.5 ) * 2.0; - cResultColor.a = texture2D( normalHeightMap, texSample ).a + coverage + noiseHeight; + cResultColor.a = texture( normalHeightMap, texSample ).a + coverage + noiseHeight; if ( cloudCoverage > -1.0 ) cResultColor.a /= 1.0 + coverage; - cResultColor.a = saturate( cResultColor.a * pow( saturate(cloudCoverage), 0.25 ) ); + cResultColor.a = clamp( cResultColor.a * pow( saturate(cloudCoverage), 0.25 ), 0.0, 1.0 ); - cResultColor.a = mix( cResultColor.a, 0.0, 1.0 - pow(worldDist,2.0) ); + cResultColor.a = mix( cResultColor.a, 0.0, 1.0 - pow(IN_worldDist,2.0) ); - // If using HDR rendering, make sure to tonemap the resuld color prior to outputting it. - // But since this example isn't doing that, we just output the computed result color here: - gl_FragColor = cResultColor; + OUT_FragColor0 = cResultColor; } diff --git a/Templates/Full/game/shaders/common/gl/cloudLayerV.glsl b/Templates/Full/game/shaders/common/gl/cloudLayerV.glsl index 39a6f4ba8..395c6f286 100644 --- a/Templates/Full/game/shaders/common/gl/cloudLayerV.glsl +++ b/Templates/Full/game/shaders/common/gl/cloudLayerV.glsl @@ -20,12 +20,24 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -varying vec4 texCoord12; -varying vec4 texCoord34; -varying vec3 vLightTS; // light vector in tangent space, denormalized -varying vec3 vViewTS; // view vector in tangent space, denormalized -varying vec3 vNormalWS; // Normal vector in world space -varying float worldDist; +#include "hlslCompat.glsl" + +in vec4 vPosition; +in vec3 vNormal; +in vec3 vBinormal; +in vec3 vTangent; +in vec2 vTexCoord0; + +out vec4 texCoord12; +#define OUT_texCoord12 texCoord12 +out vec4 texCoord34; +#define OUT_texCoord34 texCoord34 +out vec3 vLightTS; // light vector in tangent space, denormalized +#define OUT_vLightTS vLightTS +out vec3 vViewTS; // view vector in tangent space, denormalized +#define OUT_vViewTS vViewTS +out float worldDist; +#define OUT_worldDist worldDist //----------------------------------------------------------------------------- // Uniforms @@ -43,37 +55,37 @@ uniform vec3 texScale; //----------------------------------------------------------------------------- void main() { - vec4 pos = gl_Vertex; - vec3 normal = gl_Normal; - vec3 binormal = gl_MultiTexCoord0.xyz; - vec3 tangent = gl_MultiTexCoord1.xyz; - vec2 uv0 = gl_MultiTexCoord2.st; + vec4 IN_pos = vPosition; + vec3 IN_normal = vNormal; + vec3 IN_binormal = vBinormal; + vec3 IN_tangent = vTangent; + vec2 IN_uv0 = vTexCoord0.st; - gl_Position = modelview * pos; + gl_Position = modelview * IN_pos; // Offset the uv so we don't have a seam directly over our head. - vec2 uv = uv0 + vec2( 0.5, 0.5 ); + vec2 uv = IN_uv0 + vec2( 0.5, 0.5 ); - texCoord12.xy = uv * texScale.x; - texCoord12.xy += texOffset0; + OUT_texCoord12.xy = uv * texScale.x; + OUT_texCoord12.xy += texOffset0; - texCoord12.zw = uv * texScale.y; - texCoord12.zw += texOffset1; + OUT_texCoord12.zw = uv * texScale.y; + OUT_texCoord12.zw += texOffset1; - texCoord34.xy = uv * texScale.z; - texCoord34.xy += texOffset2; + OUT_texCoord34.xy = uv * texScale.z; + OUT_texCoord34.xy += texOffset2; - texCoord34.z = pos.z; - texCoord34.w = 0.0; + OUT_texCoord34.z = IN_pos.z; + OUT_texCoord34.w = 0.0; // Transform the normal, tangent and binormal vectors from object space to // homogeneous projection space: - vNormalWS = -normal; - vec3 vTangentWS = -tangent; - vec3 vBinormalWS = -binormal; + vec3 vNormalWS = -IN_normal; + vec3 vTangentWS = -IN_tangent; + vec3 vBinormalWS = -IN_binormal; // Compute position in world space: - vec4 vPositionWS = pos + vec4( eyePosWorld, 1 ); //mul( pos, objTrans ); + vec4 vPositionWS = IN_pos + vec4( eyePosWorld, 1 ); //tMul( IN_pos, objTrans ); // Compute and output the world view vector (unnormalized): vec3 vViewWS = eyePosWorld - vPositionWS.xyz; @@ -81,12 +93,14 @@ void main() // Compute denormalized light vector in world space: vec3 vLightWS = -sunVec; - // Normalize the light and view vectors and transform it to the tangent space: + // Normalize the light and view vectors and transform it to the IN_tangent space: mat3 mWorldToTangent = mat3( vTangentWS, vBinormalWS, vNormalWS ); // Propagate the view and the light vectors (in tangent space): - vLightTS = mWorldToTangent * vLightWS; - vViewTS = vViewWS * mWorldToTangent; - - worldDist = clamp( pow( pos.z, 2.0 ), 0.0, 1.0 ); + OUT_vLightTS = vLightWS * mWorldToTangent; + OUT_vViewTS = mWorldToTangent * vViewWS; + + OUT_worldDist = clamp( pow( max( IN_pos.z, 0 ), 2 ), 0.0, 1.0 ); + + correctSSP(gl_Position); } diff --git a/Templates/Full/game/shaders/common/gl/foliage.glsl b/Templates/Full/game/shaders/common/gl/foliage.glsl index 2fee902e3..38b66e767 100644 --- a/Templates/Full/game/shaders/common/gl/foliage.glsl +++ b/Templates/Full/game/shaders/common/gl/foliage.glsl @@ -46,7 +46,19 @@ uniform vec3 gc_gustInfo; uniform vec2 gc_turbInfo; -//static float sMovableCorner[4] = { 0.0, 0.0, 1.0, 1.0 }; +const float sCornerRight[4] = float[]( -0.5, 0.5, 0.5, -0.5 ); + +const float sCornerUp[4] = float[]( 0, 0, 1, 1 ); + +const float sMovableCorner[4] = float[]( 0, 0, 1, 1 ); + +const vec2 sUVCornerExtent[4] = vec2[] +( + vec2( 0, 1 ), + vec2( 1, 1 ), + vec2( 1, 0 ), + vec2( 0, 0 ) +); /////////////////////////////////////////////////////////////////////////////// @@ -106,34 +118,13 @@ vec2 windEffect( float bbPhase, void foliageProcessVert( inout vec3 position, inout vec4 diffuse, - in vec4 texCoord, - out vec2 outTexCoord, + inout vec4 texCoord, inout vec3 normal, inout vec3 T, in vec3 eyePos ) { - - float sCornerRight[4]; - sCornerRight[0] = -0.5; - sCornerRight[1] = 0.5; - sCornerRight[2] = 0.5; - sCornerRight[3] = -0.5; - - float sCornerUp[4]; - sCornerUp[0] = 0.0; - sCornerUp[1] = 0.0; - sCornerUp[2] = 1.0; - sCornerUp[3] = 1.0; - - vec2 sUVCornerExtent[4]; - sUVCornerExtent[0] = vec2( 0.0, 1.0 ); - sUVCornerExtent[1] = vec2( 1.0, 1.0 ); - sUVCornerExtent[2] = vec2( 1.0, 0.0 ); - sUVCornerExtent[3] = vec2( 0.0, 0.0 ); - - // Assign the normal and tagent values. - //normal = cross( gc_camUp, gc_camRight ); + //normal = vec3( 0, 0, 1 );//cross( gc_camUp, gc_camRight ); T = gc_camRight; // Pull out local vars we need for work. @@ -172,8 +163,8 @@ void foliageProcessVert( inout vec3 position, // Grab the uv set and setup the texture coord. vec4 uvSet = gc_typeRects[type]; - outTexCoord.x = uvSet.x + ( uvSet.z * sUVCornerExtent[corner].x ); - outTexCoord.y = uvSet.y + ( uvSet.w * sUVCornerExtent[corner].y ); + texCoord.x = uvSet.x + ( uvSet.z * sUVCornerExtent[corner].x ); + texCoord.y = uvSet.y + ( uvSet.w * sUVCornerExtent[corner].y ); // Animate the normal to get lighting changes // across the the wind swept foliage. @@ -184,7 +175,6 @@ void foliageProcessVert( inout vec3 position, normal.xy += wind.xy * ( 10.0 * texCoord.w ); normal = normalize( normal ); - // Get the alpha fade value. float fadeStart = gc_fadeParams.x; diff --git a/Templates/Full/game/shaders/common/gl/fxFoliageReplicatorP.glsl b/Templates/Full/game/shaders/common/gl/fxFoliageReplicatorP.glsl index 9e5b34caa..fb5abb91e 100644 --- a/Templates/Full/game/shaders/common/gl/fxFoliageReplicatorP.glsl +++ b/Templates/Full/game/shaders/common/gl/fxFoliageReplicatorP.glsl @@ -26,15 +26,15 @@ uniform sampler2D diffuseMap, alphaMap; uniform vec4 groundAlpha; -varying vec4 color, groundAlphaCoeff; -varying vec2 outTexCoord, alphaLookup; +in vec4 color, groundAlphaCoeff; +in vec2 outTexCoord, alphaLookup; //----------------------------------------------------------------------------- // Main //----------------------------------------------------------------------------- void main() { - vec4 alpha = texture2D(alphaMap, alphaLookup); - gl_FragColor = color * texture2D(diffuseMap, outTexCoord); - gl_FragColor.a = gl_FragColor.a * min(alpha, groundAlpha + groundAlphaCoeff.x).x; + vec4 alpha = texture(alphaMap, alphaLookup); + OUT_FragColor0 = color * texture(diffuseMap, outTexCoord); + OUT_FragColor0.a = OUT_FragColor0.a * min(alpha, groundAlpha + groundAlphaCoeff.x).x; } diff --git a/Templates/Full/game/shaders/common/gl/fxFoliageReplicatorV.glsl b/Templates/Full/game/shaders/common/gl/fxFoliageReplicatorV.glsl index 94a7af2b0..c8dcf1ddb 100644 --- a/Templates/Full/game/shaders/common/gl/fxFoliageReplicatorV.glsl +++ b/Templates/Full/game/shaders/common/gl/fxFoliageReplicatorV.glsl @@ -23,13 +23,20 @@ //----------------------------------------------------------------------------- // Data //----------------------------------------------------------------------------- +in vec4 vPosition; +in vec3 vNormal; +in vec4 vColor; +in vec2 vTexCoord0; +in vec2 vTexCoord1; +in vec2 vTexCoord2; + uniform mat4 projection, world; uniform vec3 CameraPos; uniform float GlobalSwayPhase, SwayMagnitudeSide, SwayMagnitudeFront, GlobalLightPhase, LuminanceMagnitude, LuminanceMidpoint, DistanceRange; -varying vec4 color, groundAlphaCoeff; -varying vec2 outTexCoord, alphaLookup; +out vec4 color, groundAlphaCoeff; +out vec2 outTexCoord, alphaLookup; //----------------------------------------------------------------------------- // Main @@ -42,9 +49,9 @@ void main() trans[1][1] = 1.0; trans[2][2] = 1.0; trans[3][3] = 1.0; - trans[3][0] = gl_Vertex.x; - trans[3][1] = gl_Vertex.y; - trans[3][2] = gl_Vertex.z; + trans[3][0] = vPosition.x; + trans[3][1] = vPosition.y; + trans[3][2] = vPosition.z; // Billboard transform * world matrix mat4 o = world; @@ -64,28 +71,29 @@ void main() // Handle sway. Sway is stored in a texture coord. The x coordinate is the sway phase multiplier, // the y coordinate determines if this vertex actually sways or not. float xSway, ySway; - float wavePhase = GlobalSwayPhase * gl_MultiTexCoord1.x; + float wavePhase = GlobalSwayPhase * vTexCoord1.x; ySway = sin(wavePhase); xSway = cos(wavePhase); - xSway = xSway * gl_MultiTexCoord1.y * SwayMagnitudeSide; - ySway = ySway * gl_MultiTexCoord1.y * SwayMagnitudeFront; + xSway = xSway * vTexCoord1.y * SwayMagnitudeSide; + ySway = ySway * vTexCoord1.y * SwayMagnitudeFront; vec4 p; - p = o * vec4(gl_Normal.x + xSway, ySway, gl_Normal.z, 1.0); + p = o * vec4(vNormal.x + xSway, ySway, vNormal.z, 1.0); // Project the point gl_Position = projection * p; // Lighting - float Luminance = LuminanceMidpoint + LuminanceMagnitude * cos(GlobalLightPhase + gl_Normal.y); + float Luminance = LuminanceMidpoint + LuminanceMagnitude * cos(GlobalLightPhase + vNormal.y); // Alpha - vec3 worldPos = vec3(gl_Vertex.x, gl_Vertex.y, gl_Vertex.z); + vec3 worldPos = vec3(vPosition.x, vPosition.y, vPosition.z); float alpha = abs(distance(worldPos, CameraPos)) / DistanceRange; alpha = clamp(alpha, 0.0, 1.0); //pass it through alphaLookup = vec2(alpha, 0.0); - bool alphaCoeff = bool(gl_Normal.z); + bool alphaCoeff = bool(vNormal.z); groundAlphaCoeff = vec4(float(alphaCoeff)); - outTexCoord = gl_MultiTexCoord0.st; + outTexCoord = vTexCoord0.st; color = vec4(Luminance, Luminance, Luminance, 1.0); + gl_Position.y *= -1; } \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/gl/guiMaterialV.glsl b/Templates/Full/game/shaders/common/gl/guiMaterialV.glsl index cd44de2f2..de3845ee7 100644 --- a/Templates/Full/game/shaders/common/gl/guiMaterialV.glsl +++ b/Templates/Full/game/shaders/common/gl/guiMaterialV.glsl @@ -20,16 +20,20 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +in vec4 vPosition; +in vec2 vTexCoord0; + uniform mat4x4 modelview; -varying vec4 hpos; -varying vec2 uv0; +out vec4 hpos; +out vec2 uv0; void main() { - hpos = vec4( modelview * gl_Vertex ); + hpos = vec4( modelview * vPosition ); gl_Position = hpos; - uv0 = gl_MultiTexCoord0.st; + uv0 = vTexCoord0.st; + gl_Position.y *= -1; } diff --git a/Templates/Full/game/shaders/common/gl/hlslCompat.glsl b/Templates/Full/game/shaders/common/gl/hlslCompat.glsl index be5c63340..0815df51f 100644 --- a/Templates/Full/game/shaders/common/gl/hlslCompat.glsl +++ b/Templates/Full/game/shaders/common/gl/hlslCompat.glsl @@ -27,17 +27,79 @@ #define float3 vec3 #define float2 vec2 -#define texCUBE textureCube -#define tex2D texture2D +#define half float +#define half2 vec2 +#define half3 vec3 +#define half4 vec4 + +#define float4x4 mat4 +#define float3x3 mat3 +#define float2x2 mat2 + +#define texCUBE texture +#define tex2D texture +#define tex1D texture +#define tex2Dproj textureProj +#define tex2Dlod( sampler, texCoord ) textureLod(sampler, texCoord.xy, texCoord.w) + +#define samplerCUBE samplerCube + +#define frac fract #define lerp mix -float saturate( float val ) { return clamp( val, 0.0, 1.0 ); } -vec2 saturate( vec2 val ) { return clamp( val, 0.0, 1.0 ); } -vec3 saturate( vec3 val ) { return clamp( val, 0.0, 1.0 ); } -vec4 saturate( vec4 val ) { return clamp( val, 0.0, 1.0 ); } +void tSetMatrixRow(out float3x3 m, int row, float3 value) +{ + m[0][row] = value.x; + m[1][row] = value.y; + m[2][row] = value.z; +} -float round( float n ) { return sign( n ) * floor( abs( n ) + 0.5 ); } -vec2 round( vec2 n ) { return sign( n ) * floor( abs( n ) + 0.5 ); } -vec3 round( vec3 n ) { return sign( n ) * floor( abs( n ) + 0.5 ); } -vec4 round( vec4 n ) { return sign( n ) * floor( abs( n ) + 0.5 ); } +void tSetMatrixRow(out float4x4 m, int row, float4 value) +{ + m[0][row] = value.x; + m[1][row] = value.y; + m[2][row] = value.z; + m[3][row] = value.w; +} + +#define tGetMatrix3Row(matrix, row) float3(matrix[0][row], matrix[1][row], matrix[2][row]) +#define tGetMatrix4Row(matrix, row) float4(matrix[0][row], matrix[1][row], matrix[2][row], matrix[3][row]) + +float3x3 float4x4to3x3(float4x4 m) +{ + return float3x3( vec3(m[0]).xyz, m[1].xyz, m[2].xyz); +} + +float3x3 float4x4to3x3_(float4x4 m) +{ + return float3x3( vec3(m[0]), m[1].xyz, m[2].xyz); +} + +mat4 mat4FromRow( float r0c0, float r0c1, float r0c2, float r0c3, + float r1c0, float r1c1, float r1c2, float r1c3, + float r2c0, float r2c1, float r2c2, float r2c3, + float r3c0, float r3c1, float r3c2, float r3c3 ) +{ + return mat4( r0c0, r1c0, r2c0, r3c0, + r0c1, r1c1, r2c1, r3c1, + r0c2, r1c2, r2c2, r3c2, + r0c3, r1c3, r2c3, r3c3 ); +} + + +#define saturate( val ) clamp( val, 0.0, 1.0 ) + +#define round( n ) (sign( n ) * floor( abs( n ) + 0.5 )) + +#define tMul(a, b) (a*b) + +#define inversesqrt( n ) inversesqrt( n ) + +#define correctSSP(vec) vec.y *= -1 + +#ifdef TORQUE_PIXEL_SHADER + void clip(float a) { if(a < 0) discard;} + + out vec4 OUT_FragColor0; +#endif diff --git a/Templates/Full/game/shaders/common/gl/lighting.glsl b/Templates/Full/game/shaders/common/gl/lighting.glsl index 3f8867d3b..4483c7526 100644 --- a/Templates/Full/game/shaders/common/gl/lighting.glsl +++ b/Templates/Full/game/shaders/common/gl/lighting.glsl @@ -20,73 +20,181 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- + +#ifndef TORQUE_SHADERGEN + // These are the uniforms used by most lighting shaders. -uniform vec3 inLightPos[4]; +uniform vec4 inLightPos[3]; uniform vec4 inLightInvRadiusSq; uniform vec4 inLightColor[4]; + +#ifndef TORQUE_BL_NOSPOTLIGHT + uniform vec4 inLightSpotDir[3]; + uniform vec4 inLightSpotAngle; + uniform vec4 inLightSpotFalloff; +#endif + uniform vec4 ambient; uniform float specularPower; uniform vec4 specularColor; - -// This is used to limit the maximum processed -// lights in the compute4Lights down for really -// low end GPUs. -// -// NOTE: If you want to support 10.5.x, this needs to be changed to 2. -#define C4L_MAX_LIGHTS 4 +#endif // !TORQUE_SHADERGEN void compute4Lights( vec3 wsView, vec3 wsPosition, - vec3 wsNormal, + vec3 wsNormal, + vec4 shadowMask, + + #ifdef TORQUE_SHADERGEN + + vec4 inLightPos[3], + vec4 inLightInvRadiusSq, + vec4 inLightColor[4], + vec4 inLightSpotDir[3], + vec4 inLightSpotAngle, + vec4 inLightSpotFalloff, + float specularPower, + vec4 specularColor, + + #endif // TORQUE_SHADERGEN + out vec4 outDiffuse, out vec4 outSpecular ) { - #ifdef PHONG_SPECULAR - // (R.V)^c - float reflected = reflect( wsView, wsNormal ); - #endif - - vec4 nDotL = vec4( 0.0 ); - vec4 rDotL = vec4( 0.0 ); - vec4 sqDists = vec4( 0.0 ); + // NOTE: The light positions and spotlight directions + // are stored in SoA order, so inLightPos[0] is the + // x coord for all 4 lights... inLightPos[1] is y... etc. + // + // This is the key to fully utilizing the vector units and + // saving a huge amount of instructions. + // + // For example this change saved more than 10 instructions + // over a simple for loop for each light. + int i; - for ( i = 0; i < C4L_MAX_LIGHTS; ++i ) - { - vec3 lightVector = inLightPos[i] - wsPosition; - vec3 lightDirection = normalize( lightVector ); + vec4 lightVectors[3]; + for ( i = 0; i < 3; i++ ) + lightVectors[i] = wsPosition[i] - inLightPos[i]; - nDotL[i] = max( dot( lightDirection, wsNormal ), 0.0 ); + vec4 squareDists = vec4(0); + for ( i = 0; i < 3; i++ ) + squareDists += lightVectors[i] * lightVectors[i]; - #ifdef PHONG_SPECULAR - rDotL[i] = saturate( dot( lightDirection, reflected ) ); - #else - // (N.H)^c [Blinn-Phong, TGEA style, default] - rDotL[i] = dot( wsNormal, normalize( lightDirection + wsView ) ); - #endif + // Accumulate the dot product between the light + // vector and the normal. + // + // The normal is negated because it faces away from + // the surface and the light faces towards the + // surface... this keeps us from needing to flip + // the light vector direction which complicates + // the spot light calculations. + // + // We normalize the result a little later. + // + vec4 nDotL = vec4(0); + for ( i = 0; i < 3; i++ ) + nDotL += lightVectors[i] * -wsNormal[i]; - sqDists[i] = dot( lightVector, lightVector ); - } + vec4 rDotL = vec4(0); + #ifndef TORQUE_BL_NOSPECULAR - // Attenuation - vec4 atten = vec4( 1.0 ) - ( sqDists * inLightInvRadiusSq ); + // We're using the Phong specular reflection model + // here where traditionally Torque has used Blinn-Phong + // which has proven to be more accurate to real materials. + // + // We do so because its cheaper as do not need to + // calculate the half angle for all 4 lights. + // + // Advanced Lighting still uses Blinn-Phong, but the + // specular reconstruction it does looks fairly similar + // to this. + // + vec3 R = reflect( wsView, -wsNormal ); + + for ( i = 0; i < 3; i++ ) + rDotL += lightVectors[i] * R[i]; + + #endif + + // Normalize the dots. + // + // Notice we're using the half type here to get a + // much faster sqrt via the rsq_pp instruction at + // the loss of some precision. + // + // Unless we have some extremely large point lights + // i don't believe the precision loss will matter. + // + half4 correction = half4(inversesqrt( squareDists )); + nDotL = saturate( nDotL * correction ); + rDotL = clamp( rDotL * correction, 0.00001, 1.0 ); + + // First calculate a simple point light linear + // attenuation factor. + // + // If this is a directional light the inverse + // radius should be greater than the distance + // causing the attenuation to have no affect. + // + vec4 atten = saturate( 1.0 - ( squareDists * inLightInvRadiusSq ) ); + + #ifndef TORQUE_BL_NOSPOTLIGHT + + // The spotlight attenuation factor. This is really + // fast for what it does... 6 instructions for 4 spots. + + vec4 spotAtten = vec4(0); + for ( i = 0; i < 3; i++ ) + spotAtten += lightVectors[i] * inLightSpotDir[i]; + + vec4 cosAngle = ( spotAtten * correction ) - inLightSpotAngle; + atten *= saturate( cosAngle * inLightSpotFalloff ); + + #endif + + // Finally apply the shadow masking on the attenuation. + atten *= shadowMask; + + // Get the final light intensity. + vec4 intensity = nDotL * atten; // Combine the light colors for output. - vec4 diffuse = clamp( nDotL * atten, vec4( 0.0 ), vec4( 1.0 ) ); - outDiffuse = vec4( 0.0 ); - for ( i = 0; i < C4L_MAX_LIGHTS; ++i ) - outDiffuse += vec4( diffuse[i] ) * inLightColor[i]; + outDiffuse = vec4(0); + for ( i = 0; i < 4; i++ ) + outDiffuse += intensity[i] * inLightColor[i]; // Output the specular power. - rDotL = max( rDotL, vec4( 0.00001 ) ); - outSpecular = pow( rDotL, vec4( specularPower ) ); + vec4 specularIntensity = pow( rDotL, vec4(specularPower) ) * atten; + + // Apply the per-light specular attenuation. + vec4 specular = vec4(0,0,0,1); + for ( i = 0; i < 4; i++ ) + specular += vec4( inLightColor[i].rgb * inLightColor[i].a * specularIntensity[i], 1 ); + + // Add the final specular intensity values together + // using a single dot product operation then get the + // final specular lighting color. + outSpecular = specularColor * specular; } -/// The standard specular calculation. +// This value is used in AL as a constant power to raise specular values +// to, before storing them into the light info buffer. The per-material +// specular value is then computer by using the integer identity of +// exponentiation: +// +// (a^m)^n = a^(m*n) +// +// or +// +// (specular^constSpecular)^(matSpecular/constSpecular) = specular^(matSpecular*constSpecular) +// +#define AL_ConstantSpecularPower 12.0f + +/// The specular calculation used in Advanced Lighting. /// /// @param toLight Normalized vector representing direction from the pixel /// being lit, to the light source, in world space. @@ -96,11 +204,7 @@ void compute4Lights( vec3 wsView, /// @param toEye The normalized vector representing direction from the pixel /// being lit to the camera. /// -/// @param specPwr The specular exponent. -/// -/// @param specScale A scalar on the specular output used in RGB accumulation. -/// -float calcSpecular( vec3 toLight, vec3 normal, vec3 toEye, float specPwr ) +float AL_CalcSpecular( vec3 toLight, vec3 normal, vec3 toEye ) { #ifdef PHONG_SPECULAR // (R.V)^c @@ -111,5 +215,5 @@ float calcSpecular( vec3 toLight, vec3 normal, vec3 toEye, float specPwr ) #endif // Return the specular factor. - return pow( max( specVal, 0.00001f ), specPwr ); + return pow( max( specVal, 0.00001f ), AL_ConstantSpecularPower ); } diff --git a/Templates/Full/game/shaders/common/gl/particleCompositeP.glsl b/Templates/Full/game/shaders/common/gl/particleCompositeP.glsl index 4f2d9b359..6971e1013 100644 --- a/Templates/Full/game/shaders/common/gl/particleCompositeP.glsl +++ b/Templates/Full/game/shaders/common/gl/particleCompositeP.glsl @@ -21,6 +21,13 @@ //----------------------------------------------------------------------------- #include "torque.glsl" +#include "hlslCompat.glsl" + +in vec4 offscreenPos; +in vec4 backbufferPos; + +#define IN_offscreenPos offscreenPos +#define IN_backbufferPos backbufferPos uniform sampler2D colorSource; uniform vec4 offscreenTargetParams; @@ -31,8 +38,6 @@ uniform sampler2D edgeSource; uniform vec4 edgeTargetParams; #endif -varying vec4 backbufferPos; -varying vec4 offscreenPos; void main() { @@ -47,11 +52,10 @@ void main() #ifdef REJECT_EDGES // Cut out particles along the edges, this will create the stencil mask uvScene.zw = viewportCoordToRenderTarget(uvScene.zw, edgeTargetParams); - float edge = texture2D( edgeSource, uvScene.zw ).r; - if (-edge < 0.0) - discard; + float edge = texture( edgeSource, uvScene.zw ).r; + clip( -edge ); #endif // Sample offscreen target and return - gl_FragColor = texture2D( colorSource, uvScene.xy ); -} + OUT_FragColor0 = texture( colorSource, uvScene.xy ); +} \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/gl/particleCompositeV.glsl b/Templates/Full/game/shaders/common/gl/particleCompositeV.glsl index 88a9431d1..8c8f840d1 100644 --- a/Templates/Full/game/shaders/common/gl/particleCompositeV.glsl +++ b/Templates/Full/game/shaders/common/gl/particleCompositeV.glsl @@ -20,16 +20,29 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -uniform mat4 modelViewProj; -uniform mat4 targetModelViewProj; +#include "hlslCompat.glsl" -varying vec4 offscreenPos; -varying vec4 backbufferPos; +in vec2 vTexCoord0; +#define uvCoord vTexCoord0 + +out vec4 offscreenPos; +out vec4 backbufferPos; + +#define OUT_hpos gl_Position +#define OUT_offscreenPos offscreenPos +#define OUT_backbufferPos backbufferPos + +uniform vec4 screenRect; // point, extent void main() { - gl_Position = modelViewProj * gl_Vertex; - backbufferPos = gl_Position; - offscreenPos = targetModelViewProj * gl_Vertex; + OUT_hpos = vec4(uvCoord.xy, 1.0, 1.0); + OUT_hpos.xy *= screenRect.zw; + OUT_hpos.xy += screenRect.xy; + + OUT_backbufferPos = OUT_hpos; + OUT_offscreenPos = OUT_hpos; + + correctSSP(gl_Position); } diff --git a/Templates/Full/game/shaders/common/gl/particlesP.glsl b/Templates/Full/game/shaders/common/gl/particlesP.glsl index 66a3fee28..efd930302 100644 --- a/Templates/Full/game/shaders/common/gl/particlesP.glsl +++ b/Templates/Full/game/shaders/common/gl/particlesP.glsl @@ -20,63 +20,92 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -#include "hlslCompat.glsl" #include "torque.glsl" - +#include "hlslCompat.glsl" + // With advanced lighting we get soft particles. #ifdef TORQUE_LINEAR_DEPTH #define SOFTPARTICLES #endif -#define CLIP_Z // TODO: Make this a proper macro - -uniform sampler2D diffuseMap; - #ifdef SOFTPARTICLES #include "shadergen:/autogenConditioners.h" uniform float oneOverSoftness; uniform float oneOverFar; - uniform sampler2D prepassTex; + uniform sampler2D prepassTex; //uniform vec3 vEye; uniform vec4 prePassTargetParams; #endif +#define CLIP_Z // TODO: Make this a proper macro + +in vec4 color; +in vec2 uv0; +in vec4 pos; + +#define IN_color color +#define IN_uv0 uv0 +#define IN_pos pos + +uniform sampler2D diffuseMap; + +uniform sampler2D paraboloidLightMap; + +vec4 lmSample( vec3 nrm ) +{ + bool calcBack = (nrm.z < 0.0); + if ( calcBack ) + nrm.z = nrm.z * -1.0; + + vec2 lmCoord; + lmCoord.x = (nrm.x / (2*(1 + nrm.z))) + 0.5; + lmCoord.y = 1-((nrm.y / (2*(1 + nrm.z))) + 0.5); + + + // If this is the back, offset in the atlas + if ( calcBack ) + lmCoord.x += 1.0; + + // Atlasing front and back maps, so scale + lmCoord.x *= 0.5; + + return texture(paraboloidLightMap, lmCoord); +} + + uniform float alphaFactor; uniform float alphaScale; -varying vec4 color; -varying vec2 uv0; -varying vec4 pos; - - void main() { - float softBlend = 1.0; + float softBlend = 1; #ifdef SOFTPARTICLES - float2 tc = pos.xy * vec2(1.0, -1.0 ) / pos.w; + vec2 tc = IN_pos.xy * vec2(1.0, -1.0) / IN_pos.w; tc = viewportCoordToRenderTarget(saturate( ( tc + 1.0 ) * 0.5 ), prePassTargetParams); float sceneDepth = prepassUncondition( prepassTex, tc ).w; - float depth = pos.w * oneOverFar; - float diff = sceneDepth - depth; + float depth = IN_pos.w * oneOverFar; + float diff = sceneDepth - depth; #ifdef CLIP_Z // If drawing offscreen, this acts as the depth test, since we don't line up with the z-buffer // When drawing high-res, though, we want to be able to take advantage of hi-z // so this is #ifdef'd out - if (diff < 0.0) - discard; + //clip(diff); #endif softBlend = saturate( diff * oneOverSoftness ); #endif - - vec4 diffuse = texture2D( diffuseMap, uv0 ); + + vec4 diffuse = texture( diffuseMap, IN_uv0 ); + + //OUT_FragColor0 = vec4( lmSample(vec3(0, 0, -1)).rgb, IN_color.a * diffuse.a * softBlend * alphaScale); // Scale output color by the alpha factor (turn LerpAlpha into pre-multiplied alpha) - vec3 colorScale = ( alphaFactor < 0.0 ? color.rgb * diffuse.rgb : ( alphaFactor > 0.0 ? vec3(color.a * alphaFactor * diffuse.a * softBlend) : vec3(softBlend) ) ); + vec3 colorScale = ( alphaFactor < 0.0 ? IN_color.rgb * diffuse.rgb : vec3( alphaFactor > 0.0 ? IN_color.a * diffuse.a * alphaFactor * softBlend : softBlend ) ); - gl_FragColor = hdrEncode( vec4(color.rgb * diffuse.rgb * colorScale, softBlend * color.a * diffuse.a * alphaScale) ); + OUT_FragColor0 = hdrEncode( vec4( IN_color.rgb * diffuse.rgb * colorScale, + IN_color.a * diffuse.a * softBlend * alphaScale ) ); } diff --git a/Templates/Full/game/shaders/common/gl/particlesV.glsl b/Templates/Full/game/shaders/common/gl/particlesV.glsl index da896431f..3d75a6fb6 100644 --- a/Templates/Full/game/shaders/common/gl/particlesV.glsl +++ b/Templates/Full/game/shaders/common/gl/particlesV.glsl @@ -20,18 +20,35 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -varying vec4 color; -varying vec2 uv0; -varying vec4 pos; +#include "hlslCompat.glsl" + +in vec4 vPosition; +in vec4 vColor; +in vec2 vTexCoord0; + +#define In_pos vPosition +#define In_color vColor +#define In_uv0 vTexCoord0 + +out vec4 color; +out vec2 uv0; +out vec4 pos; + +#define OUT_hpos gl_Position +#define OUT_color color +#define OUT_uv0 uv0 +#define OUT_pos pos uniform mat4 modelViewProj; uniform mat4 fsModelViewProj; void main() { - gl_Position = modelViewProj * gl_Vertex; - pos = fsModelViewProj * gl_Vertex; - color = gl_Color; - uv0 = gl_MultiTexCoord0.st; + OUT_hpos = tMul( modelViewProj, In_pos ); + OUT_pos = tMul( fsModelViewProj, In_pos ); + OUT_color = In_color; + OUT_uv0 = In_uv0; + + correctSSP(gl_Position); } diff --git a/Templates/Full/game/shaders/common/gl/planarReflectBumpP.glsl b/Templates/Full/game/shaders/common/gl/planarReflectBumpP.glsl index 00413aa3b..334c70a70 100644 --- a/Templates/Full/game/shaders/common/gl/planarReflectBumpP.glsl +++ b/Templates/Full/game/shaders/common/gl/planarReflectBumpP.glsl @@ -26,8 +26,8 @@ uniform sampler2D diffuseMap, refractMap, bumpMap; uniform vec4 shadeColor; -varying vec2 TEX0; -varying vec4 TEX1; +in vec2 TEX0; +in vec4 TEX1; //----------------------------------------------------------------------------- // Fade edges of axis for texcoord passed in @@ -49,7 +49,7 @@ float fadeAxis( float val ) //----------------------------------------------------------------------------- void main() { - vec3 bumpNorm = texture2D( bumpMap, TEX0 ).rgb * 2.0 - 1.0; + vec3 bumpNorm = texture( bumpMap, TEX0 ).rgb * 2.0 - 1.0; vec2 offset = vec2( bumpNorm.x, bumpNorm.y ); vec4 texIndex = TEX1; @@ -61,8 +61,8 @@ void main() const float distortion = 0.2; texIndex.xy += offset * distortion * fadeVal; - vec4 diffuseColor = texture2D( diffuseMap, TEX0 ); - vec4 reflectColor = texture2DProj( refractMap, texIndex ); + vec4 diffuseColor = texture( diffuseMap, TEX0 ); + vec4 reflectColor = textureProj( refractMap, texIndex ); - gl_FragColor = diffuseColor + reflectColor * diffuseColor.a; + OUT_FragColor0 = diffuseColor + reflectColor * diffuseColor.a; } diff --git a/Templates/Full/game/shaders/common/gl/planarReflectBumpV.glsl b/Templates/Full/game/shaders/common/gl/planarReflectBumpV.glsl index 820bdf698..90bcd27d8 100644 --- a/Templates/Full/game/shaders/common/gl/planarReflectBumpV.glsl +++ b/Templates/Full/game/shaders/common/gl/planarReflectBumpV.glsl @@ -23,10 +23,13 @@ //----------------------------------------------------------------------------- // Data //----------------------------------------------------------------------------- +in vec4 vPosition; +in vec2 vTexCoord0; + uniform mat4 modelview; -varying vec2 TEX0; -varying vec4 TEX1; +out vec2 TEX0; +out vec4 TEX1; //----------------------------------------------------------------------------- // Main @@ -38,11 +41,11 @@ void main() 0.0, 0.0, 1.0, 0.0, 0.5, 0.5, 0.0, 1.0); - gl_Position = modelview * gl_Vertex; + gl_Position = modelview * vPosition; - TEX0 = gl_MultiTexCoord0.st; + TEX0 = vTexCoord0.st; TEX1 = texGenTest * gl_Position; TEX1.y = -TEX1.y; - + gl_Position.y *= -1; } diff --git a/Templates/Full/game/shaders/common/gl/planarReflectP.glsl b/Templates/Full/game/shaders/common/gl/planarReflectP.glsl index b4f4fab39..77914b80e 100644 --- a/Templates/Full/game/shaders/common/gl/planarReflectP.glsl +++ b/Templates/Full/game/shaders/common/gl/planarReflectP.glsl @@ -26,16 +26,16 @@ uniform sampler2D diffuseMap, refractMap; uniform vec4 shadeColor; -varying vec2 TEX0; -varying vec4 TEX1; +in vec2 TEX0; +in vec4 TEX1; //----------------------------------------------------------------------------- // Main //----------------------------------------------------------------------------- void main() { - vec4 diffuseColor = texture2D( diffuseMap, TEX0 ); - vec4 reflectColor = texture2DProj( refractMap, TEX1 ); + vec4 diffuseColor = texture( diffuseMap, TEX0 ); + vec4 reflectColor = textureProj( refractMap, TEX1 ); - gl_FragColor = diffuseColor + reflectColor * diffuseColor.a; + OUT_FragColor0 = diffuseColor + reflectColor * diffuseColor.a; } diff --git a/Templates/Full/game/shaders/common/gl/planarReflectV.glsl b/Templates/Full/game/shaders/common/gl/planarReflectV.glsl index 820bdf698..ba2484f66 100644 --- a/Templates/Full/game/shaders/common/gl/planarReflectV.glsl +++ b/Templates/Full/game/shaders/common/gl/planarReflectV.glsl @@ -23,10 +23,13 @@ //----------------------------------------------------------------------------- // Data //----------------------------------------------------------------------------- +in vec4 vPosition; +in vec2 vTexCoord0; + uniform mat4 modelview; -varying vec2 TEX0; -varying vec4 TEX1; +out vec2 TEX0; +out vec4 TEX1; //----------------------------------------------------------------------------- // Main @@ -38,9 +41,9 @@ void main() 0.0, 0.0, 1.0, 0.0, 0.5, 0.5, 0.0, 1.0); - gl_Position = modelview * gl_Vertex; + gl_Position = modelview * vPosition; - TEX0 = gl_MultiTexCoord0.st; + TEX0 = vTexCoord0; TEX1 = texGenTest * gl_Position; TEX1.y = -TEX1.y; diff --git a/Templates/Full/game/shaders/common/gl/precipP.glsl b/Templates/Full/game/shaders/common/gl/precipP.glsl index a04f16e4b..3c669517d 100644 --- a/Templates/Full/game/shaders/common/gl/precipP.glsl +++ b/Templates/Full/game/shaders/common/gl/precipP.glsl @@ -25,13 +25,13 @@ //----------------------------------------------------------------------------- uniform sampler2D diffuseMap; -varying vec4 color; -varying vec2 texCoord; +in vec4 color; +in vec2 texCoord; //----------------------------------------------------------------------------- // Main //----------------------------------------------------------------------------- void main() { - gl_FragColor = texture2D(diffuseMap, texCoord) * color; + OUT_FragColor0 = texture(diffuseMap, texCoord) * color; } diff --git a/Templates/Full/game/shaders/common/gl/precipV.glsl b/Templates/Full/game/shaders/common/gl/precipV.glsl index 3535f2f38..29f921630 100644 --- a/Templates/Full/game/shaders/common/gl/precipV.glsl +++ b/Templates/Full/game/shaders/common/gl/precipV.glsl @@ -23,28 +23,32 @@ //----------------------------------------------------------------------------- // Data //----------------------------------------------------------------------------- +in vec4 vPosition; +in vec2 vTexCoord0; + uniform mat4 modelview; uniform vec3 cameraPos, ambient; uniform vec2 fadeStartEnd; -varying vec4 color; -varying vec2 texCoord; +out vec4 color; +out vec2 texCoord; //----------------------------------------------------------------------------- // Main //----------------------------------------------------------------------------- void main() { - gl_Position = modelview * gl_Vertex; - texCoord = gl_MultiTexCoord0.st; + gl_Position = modelview * vPosition; + texCoord = vTexCoord0.st; color = vec4( ambient.r, ambient.g, ambient.b, 1.0 ); // Do we need to do a distance fade? if ( fadeStartEnd.x < fadeStartEnd.y ) { - float distance = length( cameraPos - gl_Vertex.xyz ); + float distance = length( cameraPos - vPosition.xyz ); color.a = abs( clamp( ( distance - fadeStartEnd.x ) / ( fadeStartEnd.y - fadeStartEnd.x ), 0.0, 1.0 ) - 1.0 ); } + gl_Position.y *= -1; } diff --git a/Templates/Full/game/shaders/common/gl/projectedShadowP.glsl b/Templates/Full/game/shaders/common/gl/projectedShadowP.glsl index 1dc963a4f..8ce0fba13 100644 --- a/Templates/Full/game/shaders/common/gl/projectedShadowP.glsl +++ b/Templates/Full/game/shaders/common/gl/projectedShadowP.glsl @@ -20,9 +20,11 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -varying vec2 texCoord; -varying vec4 color; -varying float fade; +in vec2 texCoord; +in vec4 color; +in float fade; + +out vec4 OUT_FragColor0; uniform sampler2D inputTex; uniform vec4 ambient; @@ -30,17 +32,6 @@ uniform vec4 ambient; void main() { - vec3 LUMINANCE_VECTOR = vec3(0.2125f, 0.4154f, 0.1721f); - float esmFactor = 200.0; - - float lum = dot( ambient.rgb, LUMINANCE_VECTOR ); - - gl_FragColor.rgb = ambient.rgb * lum; - gl_FragColor.a = 0.0; - float depth = texture2D(inputTex, texCoord).a; - - depth = depth * exp(depth - 10.0); - depth = exp(esmFactor * depth) - 1.0; - - gl_FragColor.a = clamp(depth * 300.0, 0.0, 1.0) * (1.0 - lum) * fade * color.a; + float shadow = texture( inputTex, texCoord ).a * color.a; + OUT_FragColor0 = ( ambient * shadow ) + ( 1 - shadow ); } diff --git a/Templates/Full/game/shaders/common/gl/projectedShadowV.glsl b/Templates/Full/game/shaders/common/gl/projectedShadowV.glsl index c8abde742..b5de84181 100644 --- a/Templates/Full/game/shaders/common/gl/projectedShadowV.glsl +++ b/Templates/Full/game/shaders/common/gl/projectedShadowV.glsl @@ -20,13 +20,16 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -//***************************************************************************** -// Precipitation vertex shader -//***************************************************************************** +#include "hlslCompat.glsl" -varying vec2 texCoord; -varying vec4 color; -varying float fade; +in vec4 vPosition; +in vec4 vColor; +in vec2 vTexCoord0; +in vec2 vTexCoord1; + +out vec2 texCoord; +out vec4 color; +out float fade; uniform mat4 modelview; uniform float shadowLength; @@ -34,11 +37,13 @@ uniform vec3 shadowCasterPosition; void main() { - gl_Position = modelview * vec4(gl_Vertex.xyz, 1.0); + gl_Position = modelview * vec4(vPosition.xyz, 1.0); - color = gl_Color; - texCoord = gl_MultiTexCoord1.st; + color = vColor; + texCoord = vTexCoord1.st; - float fromCasterDist = length(gl_Vertex.xyz - shadowCasterPosition) - shadowLength; - fade = 1.0 - clamp(fromCasterDist/shadowLength, 0.0, 1.0); + float fromCasterDist = length(vPosition.xyz - shadowCasterPosition) - shadowLength; + fade = 1.0 - clamp( fromCasterDist / shadowLength , 0.0, 1.0 ); + + correctSSP(gl_Position); } diff --git a/Templates/Full/game/shaders/common/gl/scatterSkyP.glsl b/Templates/Full/game/shaders/common/gl/scatterSkyP.glsl index 6a6b9b97c..691567a37 100644 --- a/Templates/Full/game/shaders/common/gl/scatterSkyP.glsl +++ b/Templates/Full/game/shaders/common/gl/scatterSkyP.glsl @@ -21,36 +21,32 @@ //----------------------------------------------------------------------------- #include "torque.glsl" +#include "hlslCompat.glsl" -// Calculates the Mie phase function -float getMiePhase(float fCos, float fCos2, float g, float g2) -{ - return 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos2) / pow(abs(1.0 + g2 - 2.0*g*fCos), 1.5); -} -// Calculates the Rayleigh phase function -float getRayleighPhase(float fCos2) -{ - //return 1.0; - return 0.75 + 0.75*fCos2; -} +// Conn +in vec4 rayleighColor; +#define IN_rayleighColor rayleighColor +in vec4 mieColor; +#define IN_mieColor mieColor +in vec3 v3Direction; +#define IN_v3Direction v3Direction +in float zPosition; +#define IN_zPosition zPosition +in vec3 pos; +#define IN_pos pos -varying vec4 rayleighColor; -varying vec4 mieColor; -varying vec3 v3Direction; -varying float zPosition; -varying vec3 pos; - -uniform samplerCube nightSky; +uniform samplerCube nightSky ; uniform vec4 nightColor; uniform vec2 nightInterpAndExposure; uniform float useCubemap; uniform vec3 lightDir; uniform vec3 sunDir; -void main() +void main() { - float fCos = dot( lightDir, v3Direction ) / length(v3Direction); + + float fCos = dot( lightDir, IN_v3Direction ) / length(IN_v3Direction); float fCos2 = fCos*fCos; float g = -0.991; @@ -58,15 +54,15 @@ void main() float fMiePhase = 1.5 * ((1.0 - g2) / (2.0 + g2)) * (1.0 + fCos2) / pow(abs(1.0 + g2 - 2.0*g*fCos), 1.5); - vec4 color = rayleighColor + fMiePhase * mieColor; + vec4 color = IN_rayleighColor + fMiePhase * IN_mieColor; color.a = color.b; - vec4 nightSkyColor = textureCube(nightSky, -v3Direction); + vec4 nightSkyColor = texture(nightSky, -v3Direction); nightSkyColor = mix(nightColor, nightSkyColor, useCubemap); float fac = dot( normalize( pos ), sunDir ); fac = max( nightInterpAndExposure.y, pow( clamp( fac, 0.0, 1.0 ), 2 ) ); - gl_FragColor = mix( color, nightSkyColor, nightInterpAndExposure.y ); + OUT_FragColor0 = mix( color, nightSkyColor, nightInterpAndExposure.y ); // Clip based on the camera-relative // z position of the vertex, passed through @@ -74,6 +70,6 @@ void main() if(zPosition < 0.0) discard; - gl_FragColor.a = 1; - gl_FragColor = hdrEncode( gl_FragColor ); + OUT_FragColor0.a = 1; + OUT_FragColor0 = hdrEncode( OUT_FragColor0 ); } diff --git a/Templates/Full/game/shaders/common/gl/scatterSkyV.glsl b/Templates/Full/game/shaders/common/gl/scatterSkyV.glsl index 53abd5a6b..61580d785 100644 --- a/Templates/Full/game/shaders/common/gl/scatterSkyV.glsl +++ b/Templates/Full/game/shaders/common/gl/scatterSkyV.glsl @@ -20,12 +20,7 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -const int nSamples = 4; -const float fSamples = 4.0; - -// The scale depth (the altitude at which the average atmospheric density is found) -const float fScaleDepth = 0.25; -const float fInvScaleDepth = 1.0 / 0.25; +#include "hlslCompat.glsl" // The scale equation calculated by Vernier's Graphical Analysis float vernierScale(float fCos) @@ -40,12 +35,27 @@ float vernierScale(float fCos) return 0.25 * outx; } +in vec4 vPosition; +in vec3 vNormal; +in vec4 vColor; +in vec2 vTexCoord0; + +// This is the shader input vertex structure. +#define IN_position vPosition +#define IN_normal vNormal +#define IN_color vColor + // This is the shader output data. -varying vec4 rayleighColor; -varying vec4 mieColor; -varying vec3 v3Direction; -varying float zPosition; -varying vec3 pos; +out vec4 rayleighColor; +#define OUT_rayleighColor rayleighColor +out vec4 mieColor; +#define OUT_mieColor mieColor +out vec3 v3Direction; +#define OUT_v3Direction v3Direction +out float zPosition; +#define OUT_zPosition zPosition +out vec3 pos; +#define OUT_pos pos uniform mat4 modelView; uniform vec4 misc; @@ -54,13 +64,16 @@ uniform vec4 scatteringCoeffs; uniform vec3 camPos; uniform vec3 lightDir; uniform vec4 invWaveLength; - -void main() -{ - vec4 position = gl_Vertex.xyzw; - vec3 normal = gl_Normal.xyz; - vec4 color = gl_MultiTexCoord0.xyzw; +uniform vec4 colorize; +vec3 desaturate(const vec3 color, const float desaturation) +{ + const vec3 gray_conv = vec3 (0.30, 0.59, 0.11); + return mix(color, vec3(dot(gray_conv , color)), desaturation); +} + +void main() +{ // Pull some variables out: float camHeight = misc.x; float camHeightSqr = misc.y; @@ -83,7 +96,7 @@ void main() // Get the ray from the camera to the vertex, // and its length (which is the far point of the ray // passing through the atmosphere). - vec3 v3Pos = position.xyz / 6378000.0;// / outerRadius; + vec3 v3Pos = vec3(IN_position / 6378000.0);// / outerRadius; vec3 newCamPos = vec3( 0, 0, camHeight ); v3Pos.z += innerRadius; vec3 v3Ray = v3Pos.xyz - newCamPos; @@ -97,16 +110,7 @@ void main() float fDepth = exp(scaleOverScaleDepth * (innerRadius - camHeight)); float fStartAngle = dot(v3Ray, v3Start) / fHeight; - float x = 1.0 - fStartAngle; - float x5 = x * 5.25; - float x5p6 = (-6.80 + x5); - float xnew = (3.83 + x * x5p6); - float xfinal = (0.459 + x * xnew); - float xfinal2 = -0.00287 + x * xfinal; - float othx = exp( xfinal2 ); - float vscale1 = 0.25 * othx; - - float fStartOffset = fDepth * vscale1;//vernierScale(fStartAngle); + float fStartOffset = fDepth * vernierScale( fStartAngle ); // Initialize the scattering loop variables. float fSampleLength = fFar / 2.0; @@ -123,24 +127,8 @@ void main() float fLightAngle = dot(lightDir, v3SamplePoint) / fHeight; float fCameraAngle = dot(v3Ray, v3SamplePoint) / fHeight; - x = 1.0 - fCameraAngle; - x5 = x * 5.25; - x5p6 = (-6.80 + x5); - xnew = (3.83 + x * x5p6); - xfinal = (0.459 + x * xnew); - xfinal2 = -0.00287 + x * xfinal; - othx = exp( xfinal2 ); - float vscale3 = 0.25 * othx; - - - x = 1.0 - fLightAngle; - x5 = x * 5.25; - x5p6 = (-6.80 + x5); - xnew = (3.83 + x * x5p6); - xfinal = (0.459 + x * xnew); - xfinal2 = -0.00287 + x * xfinal; - othx = exp( xfinal2 ); - float vscale2 = 0.25 * othx; + float vscale3 = vernierScale( fCameraAngle ); + float vscale2 = vernierScale( fLightAngle ); float fScatter = (fStartOffset + fDepth*(vscale2 - vscale3)); vec3 v3Attenuate = exp(-fScatter * (invWaveLength.xyz * rayleigh4PI + mie4PI)); @@ -150,16 +138,24 @@ void main() // Finally, scale the Mie and Rayleigh colors // and set up the varying variables for the pixel shader. - gl_Position = modelView * position; - mieColor.rgb = v3FrontColor * mieBrightness; - mieColor.a = 1.0; - rayleighColor.rgb = v3FrontColor * (invWaveLength.xyz * rayleighBrightness); - rayleighColor.a = 1.0; - v3Direction = newCamPos - v3Pos.xyz; + gl_Position = modelView * IN_position; + OUT_mieColor.rgb = v3FrontColor * mieBrightness; + OUT_mieColor.a = 1.0; + OUT_rayleighColor.rgb = v3FrontColor * (invWaveLength.xyz * rayleighBrightness); + OUT_rayleighColor.a = 1.0; + OUT_v3Direction = newCamPos - v3Pos.xyz; + OUT_pos = IN_position.xyz; - // This offset is to get rid of the black line between the atmosky and the waterPlane - // along the horizon. - zPosition = position.z + 4000.0; - pos = position.xyz; +#ifdef USE_COLORIZE + + OUT_rayleighColor.rgb = desaturate(OUT_rayleighColor.rgb, 1) * colorize.a; + + OUT_rayleighColor.r *= colorize.r; + OUT_rayleighColor.g *= colorize.g; + OUT_rayleighColor.b *= colorize.b; + +#endif + + correctSSP(gl_Position); } diff --git a/Templates/Full/game/shaders/common/gl/torque.glsl b/Templates/Full/game/shaders/common/gl/torque.glsl index dc73a706f..a98ef859f 100644 --- a/Templates/Full/game/shaders/common/gl/torque.glsl +++ b/Templates/Full/game/shaders/common/gl/torque.glsl @@ -117,6 +117,7 @@ mat3x3 quatToMat( vec4 quat ) return mat; } + /// The number of additional substeps we take when refining /// the results of the offset parallax mapping function below. /// @@ -129,19 +130,20 @@ mat3x3 quatToMat( vec4 quat ) /// Performs fast parallax offset mapping using /// multiple refinement steps. -////// @param texMap The texture map whos alpha channel we sample the parallax depth. +/// +/// @param texMap The texture map whos alpha channel we sample the parallax depth. /// @param texCoord The incoming texture coordinate for sampling the parallax depth. /// @param negViewTS The negative view vector in tangent space. /// @param depthScale The parallax factor used to scale the depth result. /// vec2 parallaxOffset( sampler2D texMap, vec2 texCoord, vec3 negViewTS, float depthScale ) { - float depth = texture2D( texMap, texCoord ).a; + float depth = texture( texMap, texCoord ).a; vec2 offset = negViewTS.xy * ( depth * depthScale ); for ( int i=0; i < PARALLAX_REFINE_STEPS; i++ ) { - depth = ( depth + texture2D( texMap, texCoord + offset ).a ) * 0.5; + depth = ( depth + texture( texMap, texCoord + offset ).a ) * 0.5; offset = negViewTS.xy * ( depth * depthScale ); } @@ -151,59 +153,61 @@ vec2 parallaxOffset( sampler2D texMap, vec2 texCoord, vec3 negViewTS, float dept /// The maximum value for 16bit per component integer HDR encoding. const float HDR_RGB16_MAX = 100.0; -/// The maximum value for 10bit per component integer HDR encoding.const float HDR_RGB10_MAX = 4.0; +/// The maximum value for 10bit per component integer HDR encoding. +const float HDR_RGB10_MAX = 4.0; /// Encodes an HDR color for storage into a target. -vec3 hdrEncode( vec3 sample ){ +vec3 hdrEncode( vec3 _sample ) +{ #if defined( TORQUE_HDR_RGB16 ) - return sample / HDR_RGB16_MAX; + return _sample / HDR_RGB16_MAX; #elif defined( TORQUE_HDR_RGB10 ) - return sample / HDR_RGB10_MAX; + return _sample / HDR_RGB10_MAX; #else // No encoding. - return sample; + return _sample; #endif } /// Encodes an HDR color for storage into a target. -vec4 hdrEncode( vec4 sample ) +vec4 hdrEncode( vec4 _sample ) { - return vec4( hdrEncode( sample.rgb ), sample.a ); + return vec4( hdrEncode( _sample.rgb ), _sample.a ); } /// Decodes an HDR color from a target. -vec3 hdrDecode( vec3 sample ) +vec3 hdrDecode( vec3 _sample ) { #if defined( TORQUE_HDR_RGB16 ) - return sample * HDR_RGB16_MAX; + return _sample * HDR_RGB16_MAX; #elif defined( TORQUE_HDR_RGB10 ) - return sample * HDR_RGB10_MAX; + return _sample * HDR_RGB10_MAX; #else // No encoding. - return sample; + return _sample; #endif } /// Decodes an HDR color from a target. -vec4 hdrDecode( vec4 sample ) +vec4 hdrDecode( vec4 _sample ) { - return vec4( hdrDecode( sample.rgb ), sample.a ); + return vec4( hdrDecode( _sample.rgb ), _sample.a ); } /// Returns the luminance for an HDR pixel. -float hdrLuminance( vec3 sample ) +float hdrLuminance( vec3 _sample ) { // There are quite a few different ways to // calculate luminance from an rgb value. @@ -216,7 +220,7 @@ float hdrLuminance( vec3 sample ) // // Max component luminance. // - //float lum = max( sample.r, max( sample.g, sample.b ) ); + //float lum = max( _sample.r, max( _sample.g, _sample.b ) ); //////////////////////////////////////////////////////////////////////////// // The perceptual relative luminance. @@ -224,23 +228,45 @@ float hdrLuminance( vec3 sample ) // See http://en.wikipedia.org/wiki/Luminance_(relative) // const vec3 RELATIVE_LUMINANCE = vec3( 0.2126, 0.7152, 0.0722 ); - float lum = dot( sample, RELATIVE_LUMINANCE ); + float lum = dot( _sample, RELATIVE_LUMINANCE ); //////////////////////////////////////////////////////////////////////////// // // The average component luminance. // //const vec3 AVERAGE_LUMINANCE = vec3( 0.3333, 0.3333, 0.3333 ); - //float lum = dot( sample, AVERAGE_LUMINANCE ); + //float lum = dot( _sample, AVERAGE_LUMINANCE ); return lum; } +#ifdef TORQUE_PIXEL_SHADER +/// Called from the visibility feature to do screen +/// door transparency for fading of objects. +void fizzle(vec2 vpos, float visibility) +{ + // NOTE: The magic values below are what give us + // the nice even pattern during the fizzle. + // + // These values can be changed to get different + // patterns... some better than others. + // + // Horizontal Blinds - { vpos.x, 0.916, vpos.y, 0 } + // Vertical Lines - { vpos.x, 12.9898, vpos.y, 78.233 } + // + // I'm sure there are many more patterns here to + // discover for different effects. + + mat2x2 m = mat2x2( vpos.x, vpos.y, 0.916, 0.350 ); + if( (visibility - fract( determinant( m ) )) < 0 ) //if(a < 0) discard; + discard; +} +#endif //TORQUE_PIXEL_SHADER /// Basic assert macro. If the condition fails, then the shader will output color. /// @param condition This should be a bvec[2-4]. If any items is false, condition is considered to fail. /// @param color The color that should be outputted if the condition fails. /// @note This macro will only work in the void main() method of a pixel shader. -#define assert(condition, color) { if(!any(condition)) { gl_FragColor = color; return; } } +#define assert(condition, color) { if(!any(condition)) { OUT_FragColor0 = color; return; } } #endif // _TORQUE_GLSL_ diff --git a/Templates/Full/game/shaders/common/gl/wavesP.glsl b/Templates/Full/game/shaders/common/gl/wavesP.glsl index 24bd8cbb4..ddb683947 100644 --- a/Templates/Full/game/shaders/common/gl/wavesP.glsl +++ b/Templates/Full/game/shaders/common/gl/wavesP.glsl @@ -28,10 +28,10 @@ uniform float specularPower; uniform vec4 ambient; uniform float accumTime; -varying vec2 TEX0; -varying vec4 outLightVec; -varying vec3 outPos; -varying vec3 outEyePos; +in vec2 TEX0; +in vec4 outLightVec; +in vec3 outPos; +in vec3 outEyePos; void main() { @@ -42,14 +42,14 @@ void main() texOffset.x = TEX0.x + sinOffset1 + sinOffset2; texOffset.y = TEX0.y + cos( accumTime * 3.0 + TEX0.x * 6.28319 * 2.0 ) * 0.05; - vec4 bumpNorm = texture2D(bumpMap, texOffset) * 2.0 - 1.0; - vec4 diffuse = texture2D(diffMap, texOffset); + vec4 bumpNorm = texture(bumpMap, texOffset) * 2.0 - 1.0; + vec4 diffuse = texture(diffMap, texOffset); - gl_FragColor = diffuse * (clamp(dot(outLightVec.xyz, bumpNorm.xyz), 0.0, 1.0) + ambient); + OUT_FragColor0 = diffuse * (clamp(dot(outLightVec.xyz, bumpNorm.xyz), 0.0, 1.0) + ambient); vec3 eyeVec = normalize(outEyePos - outPos); vec3 halfAng = normalize(eyeVec + outLightVec.xyz); float specular = clamp(dot(bumpNorm.xyz, halfAng), 0.0, 1.0) * outLightVec.w; specular = pow(specular, specularPower); - gl_FragColor += specularColor * specular; + OUT_FragColor0 += specularColor * specular; } diff --git a/Templates/Full/game/shaders/common/lighting/advanced/gl/convexGeometryV.glsl b/Templates/Full/game/shaders/common/lighting/advanced/gl/convexGeometryV.glsl index 6d3e36e7e..1807ac43f 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/gl/convexGeometryV.glsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/gl/convexGeometryV.glsl @@ -20,16 +20,33 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -varying vec4 wsEyeDir; -varying vec4 ssPos; +#include "../../../gl/hlslCompat.glsl" + +in vec4 vPosition; + +#define IN_pos vPosition + +out vec4 wsEyeDir; +out vec4 ssPos; +out vec4 vsEyeDir; + +#define OUT_hpos gl_Position +#define OUT_wsEyeDir wsEyeDir +#define OUT_ssPos ssPos +#define OUT_vsEyeDir vsEyeDir uniform mat4 modelview; uniform mat4 objTrans; +uniform mat4 worldViewOnly; uniform vec3 eyePosWorld; void main() { - gl_Position = modelview * gl_Vertex; - wsEyeDir = objTrans * gl_Vertex - vec4( eyePosWorld, 0.0 ); - ssPos = gl_Position; + OUT_hpos = tMul( modelview, IN_pos ); + OUT_wsEyeDir = tMul( objTrans, IN_pos ) - vec4( eyePosWorld, 0.0 ); + OUT_vsEyeDir = tMul( worldViewOnly, IN_pos ); + OUT_ssPos = OUT_hpos; + + correctSSP(gl_Position); } + diff --git a/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgDepthVisualizeP.glsl b/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgDepthVisualizeP.glsl index 4a954ae84..e3ee59c8a 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgDepthVisualizeP.glsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgDepthVisualizeP.glsl @@ -20,14 +20,15 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" #include "shadergen:/autogenConditioners.h" -varying vec2 uv0; +in vec2 uv0; uniform sampler2D prepassBuffer; uniform sampler1D depthViz; void main() { float depth = prepassUncondition( prepassBuffer, uv0 ).w; - gl_FragColor = vec4( texture1D( depthViz, depth ).rgb, 1 ); + OUT_FragColor0 = vec4( texture( depthViz, depth ).rgb, 1.0 ); } \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl b/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl index 580204487..aa17df4d6 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl @@ -20,15 +20,16 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" #include "shadergen:/autogenConditioners.h" -varying vec2 uv0; +in vec2 uv0; uniform sampler2D lightInfoBuffer; void main() { - vec3 lightcolor; + vec3 lightcolor; float nl_Att, specular; - lightinfoUncondition( texture2DLod( lightInfoBuffer, uv0 ), lightcolor, nl_Att, specular ); - gl_FragColor = vec4( lightcolor, 1.0 ); + lightinfoUncondition( texture( lightInfoBuffer, uv0 ), lightcolor, nl_Att, specular ); + OUT_FragColor0 = vec4( lightcolor, 1.0 ); } \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl b/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl index 6f7c52486..e549922ff 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl @@ -20,15 +20,16 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" #include "shadergen:/autogenConditioners.h" -varying vec2 uv0; +in vec2 uv0; uniform sampler2D lightInfoBuffer; void main() { - vec3 lightcolor; + vec3 lightcolor; float nl_Att, specular; - lightinfoUncondition( texture2DLod( lightInfoBuffer, uv0 ), lightcolor, nl_Att, specular ); - gl_FragColor = vec4( specular, specular, specular, 1.0 ); + lightinfoUncondition( texture( lightInfoBuffer, uv0 ), lightcolor, nl_Att, specular ); + OUT_FragColor0 = vec4( specular, specular, specular, 1.0 ); } \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgNormalVisualizeP.glsl b/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgNormalVisualizeP.glsl index 96b645524..f61706393 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgNormalVisualizeP.glsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgNormalVisualizeP.glsl @@ -20,14 +20,14 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" #include "shadergen:/autogenConditioners.h" - -varying vec2 uv0; -uniform sampler2D prepassTex; +in vec2 uv0; +uniform sampler2D prepassBuffer; void main() { - vec3 normal = prepassUncondition( prepassTex, uv0 ).xyz; - gl_FragColor = vec4( ( normal + 1.0 ) * 0.5, 1.0 ); + vec3 normal = prepassUncondition( prepassBuffer, uv0 ).xyz; + OUT_FragColor0 = vec4( ( normal + 1.0 ) * 0.5, 1.0 ); } \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgShadowVisualizeP.glsl b/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgShadowVisualizeP.glsl index 51609fc6b..e997950ab 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgShadowVisualizeP.glsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/gl/dbgShadowVisualizeP.glsl @@ -19,13 +19,14 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" -varying vec2 uv0; +in vec2 uv0; uniform sampler2D shadowMap; uniform sampler1D depthViz; void main() { - float depth = clamp( texture2DLod( shadowMap, uv0, 0 ).r, 0.0, 1.0 ); - gl_FragColor = vec4( texture1D( depthViz, depth ).rgb, 1.0 ); + float depth = saturate( texture( shadowMap, uv0 ).r ); + OUT_FragColor0 = vec4( texture( depthViz, depth ).rgb, 1 ); } \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/lighting/advanced/gl/farFrustumQuad.glsl b/Templates/Full/game/shaders/common/lighting/advanced/gl/farFrustumQuad.glsl index 866a39b0b..76054eb09 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/gl/farFrustumQuad.glsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/gl/farFrustumQuad.glsl @@ -24,7 +24,7 @@ vec2 getUVFromSSPos( vec3 ssPos, vec4 rtParams ) { vec2 outPos = ( ssPos.xy + 1.0 ) / 2.0; + outPos.y = 1.0 - outPos.y; outPos = ( outPos * rtParams.zw ) + rtParams.xy; - //outPos.y = 1.0 - outPos.y; return outPos; } diff --git a/Templates/Full/game/shaders/common/lighting/advanced/gl/farFrustumQuadV.glsl b/Templates/Full/game/shaders/common/lighting/advanced/gl/farFrustumQuadV.glsl index ce5c2ad81..a80e856ed 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/gl/farFrustumQuadV.glsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/gl/farFrustumQuadV.glsl @@ -20,24 +20,32 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" #include "farFrustumQuad.glsl" -uniform vec4 renderTargetParams; - -varying vec4 hpos; -varying vec2 uv0; -varying vec3 wsEyeRay; +in vec4 vPosition; +in vec3 vNormal; +in vec3 vTangent; +in vec2 vTexCoord0; +uniform vec4 rtParams0; +out vec4 hpos; +out vec2 uv0; +out vec3 wsEyeRay; +out vec3 vsEyeRay; void main() -{ - // Expand the SS coordinate (stored in uv0) - hpos = vec4( gl_MultiTexCoord0.st * 2.0 - 1.0, 1.0, 1.0 ); - gl_Position = hpos; - +{ + hpos = vec4( vTexCoord0, 0, 1 ); + // Get a RT-corrected UV from the SS coord - uv0 = getUVFromSSPos( hpos.xyz, renderTargetParams ); + uv0 = getUVFromSSPos( hpos.xyz, rtParams0 ); + gl_Position = hpos; - // Interpolators will generate eye ray from far-frustum corners - wsEyeRay = gl_Vertex.xyz; + // Interpolators will generate eye rays the + // from far-frustum corners. + wsEyeRay = vTangent; + vsEyeRay = vNormal; + + correctSSP(gl_Position); } diff --git a/Templates/Full/game/shaders/common/lighting/advanced/gl/pointLightP.glsl b/Templates/Full/game/shaders/common/lighting/advanced/gl/pointLightP.glsl index b135f1aa8..c0610508b 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/gl/pointLightP.glsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/gl/pointLightP.glsl @@ -21,34 +21,26 @@ //----------------------------------------------------------------------------- #include "../../../gl/hlslCompat.glsl" -#include "farFrustumQuad.glsl" -#include "lightingUtils.glsl" -#include "../../shadowMap/shadowMapIO_GLSL.h" #include "shadergen:/autogenConditioners.h" +#include "farFrustumQuad.glsl" +#include "lightingUtils.glsl" +#include "../../../gl/lighting.glsl" +#include "../../shadowMap/shadowMapIO_GLSL.h" +#include "softShadow.glsl" -#if TORQUE_SM >= 30 +in vec4 wsEyeDir; +in vec4 ssPos; +in vec4 vsEyeDir; - // Enables high quality soft shadow - // filtering for SM3.0 and above. - #define SOFTSHADOW_SM3 - - #include "softShadow.glsl" - -#else +#ifdef USE_COOKIE_TEX +/// The texture for cookie rendering. +uniform samplerCube cookieMap ; #endif - -// I am not sure if we should do this in a better way -//#define SHADOW_CUBE -//#define SHADOW_PARABOLOID -#define SHADOW_DUALPARABOLOID -#define SHADOW_DUALPARABOLOID_SINGLE_PASS - - #ifdef SHADOW_CUBE vec3 decodeShadowCoord( vec3 shadowCoord ) @@ -56,39 +48,47 @@ return shadowCoord; } - vec4 shadowSample( samplerCUBE shadowMap, vec3 shadowCoord ) + vec4 shadowSample( samplerCube shadowMap, vec3 shadowCoord ) { - return textureCUBE( shadowMap, shadowCoord ); + return texture( shadowMap, shadowCoord ); } - -#elif defined( SHADOW_DUALPARABOLOID ) + +#else vec3 decodeShadowCoord( vec3 paraVec ) { - // Swizzle z and y + // Flip y and z paraVec = paraVec.xzy; - #ifdef SHADOW_DUALPARABOLOID_SINGLE_PASS + #ifndef SHADOW_PARABOLOID bool calcBack = (paraVec.z < 0.0); - if(calcBack) + if ( calcBack ) + { paraVec.z = paraVec.z * -1.0; + + #ifdef SHADOW_DUALPARABOLOID + paraVec.x = -paraVec.x; + #endif + } #endif vec3 shadowCoord; - shadowCoord.x = (paraVec.x / (2.0*(1.0 + paraVec.z))) + 0.5; - shadowCoord.y = ((paraVec.y / (2.0*(1.0 + paraVec.z))) + 0.5); + shadowCoord.x = (paraVec.x / (2*(1 + paraVec.z))) + 0.5; + shadowCoord.y = 1-((paraVec.y / (2*(1 + paraVec.z))) + 0.5); shadowCoord.z = 0; // adjust the co-ordinate slightly if it is near the extent of the paraboloid // this value was found via experementation - shadowCoord.xy *= 0.997; + // NOTE: this is wrong, it only biases in one direction, not towards the uv + // center ( 0.5 0.5 ). + //shadowCoord.xy *= 0.997; - #ifdef SHADOW_DUALPARABOLOID_SINGLE_PASS + #ifndef SHADOW_PARABOLOID // If this is the back, offset in the atlas - if(calcBack) + if ( calcBack ) shadowCoord.x += 1.0; // Atlasing front and back maps, so scale @@ -99,51 +99,35 @@ return shadowCoord; } -#else - - #error Unknown shadow type! - #endif -varying vec4 wsEyeDir; -varying vec4 ssPos; - - uniform sampler2D prePassBuffer; #ifdef SHADOW_CUBE - uniform samplerCube shadowMap; + uniform samplerCube shadowMap; #else - uniform sampler2D shadowMap; -#endif -#ifdef ACCUMULATE_LUV - uniform sampler2D scratchTarget; + uniform sampler2D shadowMap; #endif -uniform vec4 renderTargetParams; +uniform vec4 rtParams0; uniform vec3 lightPosition; uniform vec4 lightColor; -uniform float lightBrightness; -uniform float lightRange; +uniform float lightBrightness; +uniform float lightRange; uniform vec2 lightAttenuation; uniform vec4 lightMapParams; - -uniform vec3 eyePosWorld; -uniform vec4 farPlane; -uniform float negFarPlaneDotEye; -uniform mat3x3 worldToLightProj; - +uniform vec4 vsFarPlane; +uniform mat3 viewToLightProj; uniform vec4 lightParams; uniform float shadowSoftness; -uniform float constantSpecularPower; + - -void main() -{ +void main() +{ // Compute scene UV - vec3 ssPosP = ssPos.xyz / ssPos.w; - vec2 uvScene = getUVFromSSPos( ssPosP, renderTargetParams ); + vec3 ssPos = ssPos.xyz / ssPos.w; + vec2 uvScene = getUVFromSSPos( ssPos, rtParams0 ); // Sample/unpack the normal/z data vec4 prepassSample = prepassUncondition( prePassBuffer, uvScene ); @@ -151,21 +135,17 @@ void main() float depth = prepassSample.a; // Eye ray - Eye -> Pixel - vec3 eyeRay = getDistanceVectorToPlane( negFarPlaneDotEye, wsEyeDir.xyz / wsEyeDir.w , farPlane ); - - // Get world space pixel position - vec3 worldPos = eyePosWorld + eyeRay * depth; + vec3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, vsEyeDir.xyz, vsFarPlane ); + vec3 viewSpacePos = eyeRay * depth; // Build light vec, get length, clip pixel if needed - vec3 lightVec = lightPosition - worldPos; + vec3 lightVec = lightPosition - viewSpacePos; float lenLightV = length( lightVec ); - if ( lightRange - lenLightV < 0.0 ) - discard; - + clip( lightRange - lenLightV ); + // Get the attenuated falloff. float atten = attenuate( lightColor, lightAttenuation, lenLightV ); - if ( atten - 1e-6 < 0.0 ) - discard; + clip( atten - 1e-6 ); // Normalize lightVec lightVec /= lenLightV; @@ -181,61 +161,73 @@ void main() #else - // Convert the light vector into a shadow map - // here once instead of in the filtering loop. - vec4 shadowCoord = vec4(0.0); - #ifdef SHADOW_CUBE - shadowCoord.xy = decodeShadowCoord( -lightVec ); - #else - shadowCoord.xy = decodeShadowCoord( worldToLightProj * -lightVec ).xy; - #endif - // Get a linear depth from the light source. - float distToLight = lenLightV / lightRange; + float distToLight = lenLightV / lightRange; - #ifdef SOFTSHADOW_SM3 + #ifdef SHADOW_CUBE + + // TODO: We need to fix shadow cube to handle soft shadows! + float occ = texture( shadowMap, tMul( viewToLightProj, -lightVec ) ).r; + float shadowed = saturate( exp( lightParams.y * ( occ - distToLight ) ) ); + + #else + vec2 shadowCoord = decodeShadowCoord( tMul( viewToLightProj, -lightVec ) ).xy; + float shadowed = softShadow_filter( shadowMap, - gTapRotationTex, - ssPosP.xy, - shadowCoord.xy, + ssPos.xy, + shadowCoord, shadowSoftness, distToLight, nDotL, lightParams.y ); - - #else // !SOFTSHADOW_SM3 - - // TODO: Implement the SM2 lower quality - // shadow filtering method. #endif #endif // !NO_SHADOW - + + #ifdef USE_COOKIE_TEX + + // Lookup the cookie sample. + vec4 cookie = texture( cookieMap, tMul( viewToLightProj, -lightVec ) ); + + // Multiply the light with the cookie tex. + lightColor.rgb *= cookie.rgb; + + // Use a maximum channel luminance to attenuate + // the lighting else we get specular in the dark + // regions of the cookie texture. + atten *= max( cookie.r, max( cookie.g, cookie.b ) ); + + #endif + // NOTE: Do not clip on fully shadowed pixels as it would // cause the hardware occlusion query to disable the shadow. // Specular term - float specular = calcSpecular( lightVec, - normal, - normalize( -eyeRay ), - constantSpecularPower, - shadowed * atten * lightBrightness ); - - // N.L * Attenuation - float Sat_NL_Att = clamp( nDotL * atten * shadowed, 0.0, 1.0 ); - - // In LUV color mode we need to blend in the - // output from the previous target. - vec4 previousPix = vec4(0.0); - #ifdef ACCUMULATE_LUV - previousPix = texture2DLod( scratchTarget, uvScene, 0 ); - #endif + float specular = AL_CalcSpecular( lightVec, + normal, + normalize( -eyeRay ) ) * lightBrightness * atten * shadowed; - // Output - gl_FragColor = lightinfoCondition( lightColor.rgb * lightBrightness, - Sat_NL_Att, - specular, - previousPix ) * lightMapParams; + float Sat_NL_Att = saturate( nDotL * atten * shadowed ) * lightBrightness; + vec3 lightColorOut = lightMapParams.rgb * lightColor.rgb; + vec4 addToResult = vec4(0.0); + + // TODO: This needs to be removed when lightmapping is disabled + // as its extra work per-pixel on dynamic lit scenes. + // + // Special lightmapping pass. + if ( lightMapParams.a < 0.0 ) + { + // This disables shadows on the backsides of objects. + shadowed = nDotL < 0.0f ? 1.0f : shadowed; + + Sat_NL_Att = 1.0f; + shadowed = mix( 1.0f, shadowed, atten ); + lightColorOut = vec3(shadowed); + specular *= lightBrightness; + addToResult = ( 1.0 - shadowed ) * abs(lightMapParams); + } + + OUT_FragColor0 = lightinfoCondition( lightColorOut, Sat_NL_Att, specular, addToResult ); } diff --git a/Templates/Full/game/shaders/common/lighting/advanced/gl/softShadow.glsl b/Templates/Full/game/shaders/common/lighting/advanced/gl/softShadow.glsl index 8ef09ed2f..a14213946 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/gl/softShadow.glsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/gl/softShadow.glsl @@ -19,113 +19,141 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- - - -#define NUM_TAPS 12 - -#define NUM_PRE_TAPS 4 - -/// The non-uniform poisson disk used in the -/// high quality shadow filtering. -vec2 sNonUniformTaps[NUM_TAPS]; -void initNonUniformTaps() + +#if defined( SOFTSHADOW ) && defined( SOFTSHADOW_HIGH_QUALITY ) + +#define NUM_PRE_TAPS 4 +#define NUM_TAPS 12 + +/// The non-uniform poisson disk used in the +/// high quality shadow filtering. +vec2 sNonUniformTaps[NUM_TAPS] = vec2[] +( + // These first 4 taps are located around the edges + // of the disk and are used to predict fully shadowed + // or unshadowed areas. + vec2( 0.992833, 0.979309 ), + vec2( -0.998585, 0.985853 ), + vec2( 0.949299, -0.882562 ), + vec2( -0.941358, -0.893924 ), + + // The rest of the samples. + vec2( 0.545055, -0.589072 ), + vec2( 0.346526, 0.385821 ), + vec2( -0.260183, 0.334412 ), + vec2( 0.248676, -0.679605 ), + vec2( -0.569502, -0.390637 ), + vec2( -0.614096, 0.212577 ), + vec2( -0.259178, 0.876272 ), + vec2( 0.649526, 0.864333 ) +); + +#else + +#define NUM_PRE_TAPS 5 + +/// The non-uniform poisson disk used in the +/// high quality shadow filtering. +vec2 sNonUniformTaps[NUM_PRE_TAPS] = vec2[] +( + vec2( 0.892833, 0.959309 ), + vec2( -0.941358, -0.873924 ), + vec2( -0.260183, 0.334412 ), + vec2( 0.348676, -0.679605 ), + vec2( -0.569502, -0.390637 ) +); + +#endif + + +/// The texture used to do per-pixel pseudorandom +/// rotations of the filter taps. +uniform sampler2D gTapRotationTex ; + + +float softShadow_sampleTaps( sampler2D shadowMap, + vec2 sinCos, + vec2 shadowPos, + float filterRadius, + float distToLight, + float esmFactor, + int startTap, + int endTap ) { - // These first 4 taps are located around the edges - // of the disk and are used to predict fully shadowed - // or unshadowed areas. - sNonUniformTaps[0] = vec2( 0.992833, 0.979309 ); - sNonUniformTaps[1] = vec2( -0.998585, 0.985853 ); - sNonUniformTaps[2] = vec2( 0.949299, -0.882562 ); - sNonUniformTaps[3] = vec2( -0.941358, -0.893924 ); - - // The rest of the samples. - sNonUniformTaps[4] = vec2( 0.545055, -0.589072 ); - sNonUniformTaps[5] = vec2( 0.346526, 0.385821 ); - sNonUniformTaps[6] = vec2( -0.260183, 0.334412 ); - sNonUniformTaps[7] = vec2( 0.248676, -0.679605 ); - sNonUniformTaps[8] = vec2( -0.569502, -0.390637 ); - sNonUniformTaps[9] = vec2( -0.014096, 0.012577 ); - sNonUniformTaps[10] = vec2( -0.259178, 0.876272 ); - sNonUniformTaps[11] = vec2( 0.649526, 0.664333 ); + float shadow = 0; + + vec2 tap = vec2(0); + for ( int t = startTap; t < endTap; t++ ) + { + tap.x = ( sNonUniformTaps[t].x * sinCos.y - sNonUniformTaps[t].y * sinCos.x ) * filterRadius; + tap.y = ( sNonUniformTaps[t].y * sinCos.y + sNonUniformTaps[t].x * sinCos.x ) * filterRadius; + float occluder = tex2Dlod( shadowMap, vec4( shadowPos + tap, 0, 0 ) ).r; + + float esm = saturate( exp( esmFactor * ( occluder - distToLight ) ) ); + shadow += esm / float( endTap - startTap ); + } + + return shadow; } - -/// The texture used to do per-pixel pseudorandom -/// rotations of the filter taps. -uniform sampler2D gTapRotationTex; - - -float softShadow_sampleTaps( sampler2D shadowMap, - vec2 sinCos, - vec2 shadowPos, - float filterRadius, - float distToLight, - float esmFactor, - int startTap, - int endTap ) -{ - initNonUniformTaps(); - float shadow = 0.0; - - vec2 tap = vec2(0.0); - for ( int t = startTap; t < endTap; t++ ) - { - tap.x = ( sNonUniformTaps[t].x * sinCos.y - sNonUniformTaps[t].y * sinCos.x ) * filterRadius; - tap.y = ( sNonUniformTaps[t].y * sinCos.y + sNonUniformTaps[t].x * sinCos.x ) * filterRadius; - float occluder = texture2DLod( shadowMap, shadowPos + tap, 0.0 ).r; - - float esm = clamp( exp( esmFactor * ( occluder - distToLight ) ), 0.0, 1.0 ); - shadow += esm / float( endTap - startTap ); - } - - return shadow; -} - - -// HACK! HACK! HACK! -// We take the noise texture directly as the second parameter to ensure that it -// is the "last used" sampler, and thus doesn't collide with the prepass buffer -// or shadow map. If we use gTapRotationTex directly here, then it is the first -// used sampler and will collide with the prepass buffer. -float softShadow_filter( sampler2D shadowMap, - sampler2D noiseTexture, - vec2 vpos, - vec2 shadowPos, - float filterRadius, - float distToLight, - float dotNL, - float esmFactor ) -{ - // Lookup the random rotation for this screen pixel. - vec2 sinCos = ( texture2DLod( noiseTexture, vpos * 16.0, 0.0 ).rg - 0.5 ) * 2.0; - - // Do the prediction taps first. - float shadow = softShadow_sampleTaps( shadowMap, - sinCos, - shadowPos, - filterRadius, - distToLight, - esmFactor, - 0, - NUM_PRE_TAPS ); - - // Only do the expensive filtering if we're really - // in a partially shadowed area. - if ( shadow * ( 1.0 - shadow ) * max( dotNL, 0.0 ) > 0.06 ) - { - shadow += softShadow_sampleTaps( shadowMap, - sinCos, - shadowPos, - filterRadius, - distToLight, - esmFactor, - NUM_PRE_TAPS, - NUM_TAPS ); - - // This averages the taps above with the results - // of the prediction samples. - shadow *= 0.5; - } - - return shadow; -} \ No newline at end of file + + +float softShadow_filter( sampler2D shadowMap, + vec2 vpos, + vec2 shadowPos, + float filterRadius, + float distToLight, + float dotNL, + float esmFactor ) +{ + #ifndef SOFTSHADOW + + // If softshadow is undefined then we skip any complex + // filtering... just do a single sample ESM. + + float occluder = tex2Dlod( shadowMap, vec4( shadowPos, 0, 0 ) ).r; + float shadow = saturate( exp( esmFactor * ( occluder - distToLight ) ) ); + + #else + + // Lookup the random rotation for this screen pixel. + vec2 sinCos = ( tex2Dlod( gTapRotationTex, vec4( vpos * 16, 0, 0 ) ).rg - 0.5 ) * 2; + + // Do the prediction taps first. + float shadow = softShadow_sampleTaps( shadowMap, + sinCos, + shadowPos, + filterRadius, + distToLight, + esmFactor, + 0, + NUM_PRE_TAPS ); + + // We live with only the pretap results if we don't + // have high quality shadow filtering enabled. + #ifdef SOFTSHADOW_HIGH_QUALITY + + // Only do the expensive filtering if we're really + // in a partially shadowed area. + if ( shadow * ( 1.0 - shadow ) * max( dotNL, 0 ) > 0.06 ) + { + shadow += softShadow_sampleTaps( shadowMap, + sinCos, + shadowPos, + filterRadius, + distToLight, + esmFactor, + NUM_PRE_TAPS, + NUM_TAPS ); + + // This averages the taps above with the results + // of the prediction samples. + shadow *= 0.5; + } + + #endif // SOFTSHADOW_HIGH_QUALITY + + #endif // SOFTSHADOW + + return shadow; +} \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/lighting/advanced/gl/spotLightP.glsl b/Templates/Full/game/shaders/common/lighting/advanced/gl/spotLightP.glsl index d29f5edb0..c07be1f34 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/gl/spotLightP.glsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/gl/spotLightP.glsl @@ -25,58 +25,49 @@ #include "lightingUtils.glsl" #include "../../shadowMap/shadowMapIO_GLSL.h" #include "shadergen:/autogenConditioners.h" +#include "softShadow.glsl" +#include "../../../gl/lighting.glsl" +in vec4 wsEyeDir; +in vec4 ssPos; +in vec4 vsEyeDir; -#if TORQUE_SM >= 30 +#define IN_wsEyeDir wsEyeDir +#define IN_ssPos ssPos +#define IN_vsEyeDir vsEyeDir - // Enables high quality soft shadow - // filtering for SM3.0 and above. - #define SOFTSHADOW_SM3 - - #include "softShadow.glsl" - -#else +#ifdef USE_COOKIE_TEX +/// The texture for cookie rendering. +uniform sampler2D cookieMap; #endif - -varying vec4 ssPos; -varying vec4 wsEyeDir; - - uniform sampler2D prePassBuffer; uniform sampler2D shadowMap; -#ifdef ACCUMULATE_LUV - uniform sampler2D scratchTarget; -#endif -uniform vec4 renderTargetParams; +uniform vec4 rtParams0; uniform vec3 lightPosition; uniform vec4 lightColor; -uniform float lightBrightness; -uniform float lightRange; +uniform float lightBrightness; +uniform float lightRange; uniform vec2 lightAttenuation; uniform vec3 lightDirection; uniform vec4 lightSpotParams; uniform vec4 lightMapParams; -uniform vec3 eyePosWorld; -uniform vec4 farPlane; -uniform float negFarPlaneDotEye; -uniform mat4x4 worldToLightProj; +uniform vec4 vsFarPlane; +uniform mat4 viewToLightProj; uniform vec4 lightParams; uniform float shadowSoftness; -uniform float constantSpecularPower; - void main() -{ +{ // Compute scene UV - vec3 ssPosP = ssPos.xyz / ssPos.w; - vec2 uvScene = getUVFromSSPos( ssPosP, renderTargetParams ); + vec3 ssPos = IN_ssPos.xyz / IN_ssPos.w; + vec2 uvScene = getUVFromSSPos( ssPos, rtParams0 ); // Sample/unpack the normal/z data vec4 prepassSample = prepassUncondition( prePassBuffer, uvScene ); @@ -84,85 +75,92 @@ void main() float depth = prepassSample.a; // Eye ray - Eye -> Pixel - vec3 eyeRay = getDistanceVectorToPlane( negFarPlaneDotEye, wsEyeDir.xyz / wsEyeDir.w , farPlane ); - - // Get world space pixel position - vec3 worldPos = eyePosWorld + eyeRay * depth; + vec3 eyeRay = getDistanceVectorToPlane( -vsFarPlane.w, IN_vsEyeDir.xyz, vsFarPlane ); + vec3 viewSpacePos = eyeRay * depth; // Build light vec, get length, clip pixel if needed - vec3 lightToPxlVec = worldPos - lightPosition; + vec3 lightToPxlVec = viewSpacePos - lightPosition; float lenLightV = length( lightToPxlVec ); lightToPxlVec /= lenLightV; - //lightDirection = float3( -lightDirection.xy, lightDirection.z ); //float3( 0, 0, -1 ); + //lightDirection = vec3( -lightDirection.xy, lightDirection.z ); //vec3( 0, 0, -1 ); float cosAlpha = dot( lightDirection, lightToPxlVec ); - if ( cosAlpha - lightSpotParams.x < 0.0 ) discard; - if ( lightRange - lenLightV < 0.0 ) discard; + clip( cosAlpha - lightSpotParams.x ); + clip( lightRange - lenLightV ); float atten = attenuate( lightColor, lightAttenuation, lenLightV ); atten *= ( cosAlpha - lightSpotParams.x ) / lightSpotParams.y; - if ( atten - 1e-6 < 0.0 ) discard; + clip( atten - 1e-6 ); + atten = saturate( atten ); float nDotL = dot( normal, -lightToPxlVec ); + // Get the shadow texture coordinate + vec4 pxlPosLightProj = tMul( viewToLightProj, vec4( viewSpacePos, 1 ) ); + vec2 shadowCoord = ( ( pxlPosLightProj.xy / pxlPosLightProj.w ) * 0.5 ) + vec2( 0.5, 0.5 ); + shadowCoord.y = 1.0f - shadowCoord.y; + #ifdef NO_SHADOW float shadowed = 1.0; #else - // Find Shadow coordinate - vec4 pxlPosLightProj = vec4( worldToLightProj * vec4( worldPos, 1.0 ) ); - vec2 shadowCoord = ( ( pxlPosLightProj.xy / pxlPosLightProj.w ) * 0.5 ) + vec2( 0.5, 0.5 ); - // Get a linear depth from the light source. float distToLight = pxlPosLightProj.z / lightRange; - #ifdef SOFTSHADOW_SM3 - - float shadowed = softShadow_filter( shadowMap, - gTapRotationTex, - ssPosP.xy, - shadowCoord, - shadowSoftness, - distToLight, - nDotL, - lightParams.y ); - - #else // !SOFTSHADOW_SM3 - - // Simple exponential shadow map. - float occluder = decodeShadowMap( texture2DLod( shadowMap, shadowCoord, 0.0 ) ); - float esmFactor = lightParams.y; - float shadowed = clamp( exp( esmFactor * ( occluder - distToLight ) ), 0.0, 1.0 ); - - #endif + float shadowed = softShadow_filter( shadowMap, + ssPos.xy, + shadowCoord, + shadowSoftness, + distToLight, + nDotL, + lightParams.y ); #endif // !NO_SHADOW - + + #ifdef USE_COOKIE_TEX + + // Lookup the cookie sample. + vec4 cookie = texture( cookieMap, shadowCoord ); + + // Multiply the light with the cookie tex. + lightColor.rgb *= cookie.rgb; + + // Use a maximum channel luminance to attenuate + // the lighting else we get specular in the dark + // regions of the cookie texture. + atten *= max( cookie.r, max( cookie.g, cookie.b ) ); + + #endif + // NOTE: Do not clip on fully shadowed pixels as it would // cause the hardware occlusion query to disable the shadow. // Specular term - float specular = calcSpecular( -lightToPxlVec, - normal, - normalize( -eyeRay ), - constantSpecularPower, - shadowed * atten * lightBrightness ); - - // N.L * Attenuation - float Sat_NL_Att = clamp( nDotL * atten * shadowed, 0.0, 1.0 ); - - // In LUV color mode we need to blend in the - // output from the previous target. - vec4 previousPix = vec4(0.0); - #ifdef ACCUMULATE_LUV - previousPix = texture2DLod( scratchTarget, uvScene, 0.0 ); - #endif + float specular = AL_CalcSpecular( -lightToPxlVec, + normal, + normalize( -eyeRay ) ) * lightBrightness * atten * shadowed; - // Output - gl_FragColor = lightinfoCondition( lightColor.rgb * lightBrightness, - Sat_NL_Att, - specular, - previousPix ) * lightMapParams; + float Sat_NL_Att = saturate( nDotL * atten * shadowed ) * lightBrightness; + vec3 lightColorOut = lightMapParams.rgb * lightColor.rgb; + vec4 addToResult = vec4(0.0); + + // TODO: This needs to be removed when lightmapping is disabled + // as its extra work per-pixel on dynamic lit scenes. + // + // Special lightmapping pass. + if ( lightMapParams.a < 0.0 ) + { + // This disables shadows on the backsides of objects. + shadowed = nDotL < 0.0f ? 1.0f : shadowed; + + Sat_NL_Att = 1.0f; + shadowed = mix( 1.0f, shadowed, atten ); + lightColorOut = vec3(shadowed); + specular *= lightBrightness; + addToResult = ( 1.0 - shadowed ) * abs(lightMapParams); + } + + OUT_FragColor0 = lightinfoCondition( lightColorOut, Sat_NL_Att, specular, addToResult ); } diff --git a/Templates/Full/game/shaders/common/lighting/advanced/gl/vectorLightP.glsl b/Templates/Full/game/shaders/common/lighting/advanced/gl/vectorLightP.glsl index bbd567fd0..0178c35ea 100644 --- a/Templates/Full/game/shaders/common/lighting/advanced/gl/vectorLightP.glsl +++ b/Templates/Full/game/shaders/common/lighting/advanced/gl/vectorLightP.glsl @@ -22,40 +22,32 @@ #include "../../../gl/hlslCompat.glsl" #include "shadergen:/autogenConditioners.h" +#include "farFrustumQuad.glsl" +#include "../../../gl/torque.glsl" +#include "../../../gl/lighting.glsl" #include "lightingUtils.glsl" #include "../../shadowMap/shadowMapIO_GLSL.h" +#include "softShadow.glsl" -varying vec2 uv0; -varying vec3 wsEyeRay; +in vec4 hpos; +in vec2 uv0; +in vec3 wsEyeRay; +in vec3 vsEyeRay; -uniform sampler2D prePassBuffer; -uniform sampler2D ShadowMap; +uniform sampler2D ShadowMap ; -#if TORQUE_SM >= 30 - - // Enables high quality soft shadow - // filtering for SM3.0 and above. - #define SOFTSHADOW_SM3 - - #include "softShadow.glsl" - -#else - - - +#ifdef USE_SSAO_MASK +uniform sampler2D ssaoMask ; +uniform vec4 rtParams2; #endif - + +uniform sampler2D prePassBuffer; uniform vec3 lightDirection; uniform vec4 lightColor; -uniform float lightBrightness; -uniform vec4 lightAmbient; -uniform vec4 lightTrilight; - -uniform vec3 eyePosWorld; - -uniform mat4 worldToLightProj; -uniform vec4 splitDistStart; -uniform vec4 splitDistEnd; +uniform float lightBrightness; +uniform vec4 lightAmbient; +uniform vec3 eyePosWorld; +uniform mat4x4 worldToLightProj; uniform vec4 scaleX; uniform vec4 scaleY; uniform vec4 offsetX; @@ -65,16 +57,12 @@ uniform vec4 atlasYOffset; uniform vec2 atlasScale; uniform vec4 zNearFarInvNearFar; uniform vec4 lightMapParams; - -uniform float constantSpecularPower; uniform vec2 fadeStartLength; uniform vec4 farPlaneScalePSSM; -uniform vec4 splitFade; uniform vec4 overDarkPSSM; uniform float shadowSoftness; - -void main() +void main() { // Sample/unpack the normal/z data vec4 prepassSample = prepassUncondition( prePassBuffer, uv0 ); @@ -83,148 +71,162 @@ void main() // Use eye ray to get ws pos vec4 worldPos = vec4(eyePosWorld + wsEyeRay * depth, 1.0f); - + // Get the light attenuation. float dotNL = dot(-lightDirection, normal); + + #ifdef PSSM_DEBUG_RENDER + vec3 debugColor = vec3(0); + #endif #ifdef NO_SHADOW // Fully unshadowed. float shadowed = 1.0; + #ifdef PSSM_DEBUG_RENDER + debugColor = vec3(1.0); + #endif + #else - + // Compute shadow map coordinate - vec4 pxlPosLightProj = worldToLightProj * worldPos; + vec4 pxlPosLightProj = tMul(worldToLightProj, worldPos); vec2 baseShadowCoord = pxlPosLightProj.xy / pxlPosLightProj.w; - - float distOffset = 0.0; - float shadowed = 0.0; - float fadeAmt = 0.0; - vec4 zDist = vec4(zNearFarInvNearFar.x + zNearFarInvNearFar.y * depth); - - // Calculate things dependant on the shadowmap split - for ( int i = 0; i < 2; i++ ) - { - float zDistSplit = zDist.x + distOffset; - vec4 mask0; - mask0.x = float(zDistSplit >= splitDistStart.x); - mask0.y = float(zDistSplit >= splitDistStart.y); - mask0.z = float(zDistSplit >= splitDistStart.z); - mask0.w = float(zDistSplit >= splitDistStart.w); + + // Distance to light, in shadowmap space + float distToLight = pxlPosLightProj.z / pxlPosLightProj.w; - vec4 mask1; - mask1.x = float(zDistSplit < splitDistEnd.x); - mask1.y = float(zDistSplit < splitDistEnd.y); - mask1.z = float(zDistSplit < splitDistEnd.z); - mask1.w = float(zDistSplit < splitDistEnd.w); + // Figure out which split to sample from. Basically, we compute the shadowmap sample coord + // for all of the splits and then check if its valid. + vec4 shadowCoordX = vec4( baseShadowCoord.x ); + vec4 shadowCoordY = vec4( baseShadowCoord.y ); + vec4 farPlaneDists = vec4( distToLight ); + shadowCoordX *= scaleX; + shadowCoordY *= scaleY; + shadowCoordX += offsetX; + shadowCoordY += offsetY; + farPlaneDists *= farPlaneScalePSSM; + + // If the shadow sample is within -1..1 and the distance + // to the light for this pixel is less than the far plane + // of the split, use it. + vec4 finalMask; + if ( shadowCoordX.x > -0.99 && shadowCoordX.x < 0.99 && + shadowCoordY.x > -0.99 && shadowCoordY.x < 0.99 && + farPlaneDists.x < 1.0 ) + finalMask = vec4(1, 0, 0, 0); + + else if ( shadowCoordX.y > -0.99 && shadowCoordX.y < 0.99 && + shadowCoordY.y > -0.99 && shadowCoordY.y < 0.99 && + farPlaneDists.y < 1.0 ) + finalMask = vec4(0, 1, 0, 0); + + else if ( shadowCoordX.z > -0.99 && shadowCoordX.z < 0.99 && + shadowCoordY.z > -0.99 && shadowCoordY.z < 0.99 && + farPlaneDists.z < 1.0 ) + finalMask = vec4(0, 0, 1, 0); - vec4 finalMask = mask0 * mask1; + else + finalMask = vec4(0, 0, 0, 1); - float splitFadeDist = dot( finalMask, splitFade ); - vec2 finalScale; - finalScale.x = dot(finalMask, scaleX); - finalScale.y = dot(finalMask, scaleY); + #ifdef PSSM_DEBUG_RENDER + if ( finalMask.x > 0 ) + debugColor += vec3( 1, 0, 0 ); + else if ( finalMask.y > 0 ) + debugColor += vec3( 0, 1, 0 ); + else if ( finalMask.z > 0 ) + debugColor += vec3( 0, 0, 1 ); + else if ( finalMask.w > 0 ) + debugColor += vec3( 1, 1, 0 ); + #endif - vec2 finalOffset; - finalOffset.x = dot(finalMask, offsetX); - finalOffset.y = dot(finalMask, offsetY); - - vec2 shadowCoord; - shadowCoord = baseShadowCoord * finalScale; - shadowCoord += finalOffset; + // Here we know what split we're sampling from, so recompute the texcoord location + // Yes, we could just use the result from above, but doing it this way actually saves + // shader instructions. + vec2 finalScale; + finalScale.x = dot(finalMask, scaleX); + finalScale.y = dot(finalMask, scaleY); - // Convert to texcoord space - shadowCoord = 0.5 * shadowCoord + vec2(0.5, 0.5); - //shadowCoord.y = 1.0f - shadowCoord.y; + vec2 finalOffset; + finalOffset.x = dot(finalMask, offsetX); + finalOffset.y = dot(finalMask, offsetY); - // Move around inside of atlas - vec2 aOffset; - aOffset.x = dot(finalMask, atlasXOffset); - aOffset.y = dot(finalMask, atlasYOffset); + vec2 shadowCoord; + shadowCoord = baseShadowCoord * finalScale; + shadowCoord += finalOffset; - shadowCoord *= atlasScale; - shadowCoord += aOffset; - - // Distance to light, in shadowmap space - float distToLight = pxlPosLightProj.z / pxlPosLightProj.w; - - // Each split has a different far plane, take this into account. - float farPlaneScale = dot( farPlaneScalePSSM, finalMask ); - distToLight *= farPlaneScale; - - #ifdef SOFTSHADOW_SM3 + // Convert to texcoord space + shadowCoord = 0.5 * shadowCoord + vec2(0.5, 0.5); + shadowCoord.y = 1.0f - shadowCoord.y; - float esmShadow = softShadow_filter( ShadowMap, - gTapRotationTex, - uv0.xy, - shadowCoord, - farPlaneScale * shadowSoftness, - distToLight, - dotNL, - dot( finalMask, overDarkPSSM ) ); - - #else // !SOFTSHADOW_SM3 + // Move around inside of atlas + vec2 aOffset; + aOffset.x = dot(finalMask, atlasXOffset); + aOffset.y = dot(finalMask, atlasYOffset); - float occluder = decodeShadowMap( texture2DLod( ShadowMap, shadowCoord, 0.0 ) ); - float overDark = dot( finalMask, overDarkPSSM ); - float esmShadow = saturate( exp( esmFactor * ( occluder - distToLight ) ) ); - - #endif - - if ( i == 0 ) - { - float endDist = dot(splitDistEnd, finalMask); - fadeAmt = smoothstep(endDist - splitFadeDist, endDist, zDist).x; - shadowed = esmShadow * ( 1.0 - fadeAmt ); - } - else - shadowed += esmShadow * fadeAmt; - - distOffset += splitFadeDist; - } + shadowCoord *= atlasScale; + shadowCoord += aOffset; + + // Each split has a different far plane, take this into account. + float farPlaneScale = dot( farPlaneScalePSSM, finalMask ); + distToLight *= farPlaneScale; + + float shadowed = softShadow_filter( ShadowMap, + uv0.xy, + shadowCoord, + farPlaneScale * shadowSoftness, + distToLight, + dotNL, + dot( finalMask, overDarkPSSM ) ); // Fade out the shadow at the end of the range. + vec4 zDist = vec4(zNearFarInvNearFar.x + zNearFarInvNearFar.y * depth); float fadeOutAmt = ( zDist.x - fadeStartLength.x ) * fadeStartLength.y; - shadowed = mix( shadowed, 1.0, clamp( fadeOutAmt, 0.0, 1.0 ) ); + shadowed = mix( shadowed, 1.0, saturate( fadeOutAmt ) ); + + #ifdef PSSM_DEBUG_RENDER + if ( fadeOutAmt > 1.0 ) + debugColor = vec3(1.0); + #endif #endif // !NO_SHADOW - - // Calc lighting coefficents - float specular = calcSpecular( -lightDirection, - normal, - normalize(-wsEyeRay), - constantSpecularPower, - shadowed * lightBrightness ); - - float Sat_NL_Att = clamp(dotNL, 0.0, 1.0) * shadowed; - - // Trilight, described by Tom Forsyth - // http://home.comcast.net/~tom_forsyth/papers/trilight/trilight.html -#ifdef ACCUMULATE_LUV - // In LUV multiply in the brightness of the light color (normaly done in the attenuate function) - Sat_NL_Att *= lightColor.a; + // Specular term + float specular = AL_CalcSpecular( -lightDirection, + normal, + normalize(-vsEyeRay) ) * lightBrightness * shadowed; - vec4 ambientBlend = lightAmbient; - ambientBlend.b *= clamp(-dotNL, 0.0, 1.0); - - vec3 trilight = lightTrilight.rgb; - trilight.b *= clamp(1.0 - abs(dotNL), 0.0, 1.0); - - ambientBlend.rg = mix(ambientBlend.rg, trilight.rg, clamp(0.5 * trilight.b / lightAmbient.b, 0.0, 1.0)); - ambientBlend.b += trilight.b; + float Sat_NL_Att = saturate( dotNL * shadowed ) * lightBrightness; + vec3 lightColorOut = lightMapParams.rgb * lightColor.rgb; + vec4 addToResult = lightAmbient; -#else + // TODO: This needs to be removed when lightmapping is disabled + // as its extra work per-pixel on dynamic lit scenes. + // + // Special lightmapping pass. + if ( lightMapParams.a < 0.0 ) + { + // This disables shadows on the backsides of objects. + shadowed = dotNL < 0.0f ? 1.0f : shadowed; - // RGB - // TODO: Trilight seems broken... it does lighting in shadows! - //vec4 ambientBlend = vec4(lightTrilight.rgb * clamp(1.0 - abs(dotNL), 0.0, 1.0) + lightAmbient.rgb * clamp(-dotNL, 0.0, 1.0), 0.0); - vec4 ambientBlend = vec4(lightAmbient.rgb, 0.0); + Sat_NL_Att = 1.0f; + lightColorOut = vec3(shadowed); + specular *= lightBrightness; + addToResult = ( 1.0 - shadowed ) * abs(lightMapParams); + } -#endif + // Sample the AO texture. + #ifdef USE_SSAO_MASK + float ao = 1.0 - texture( ssaoMask, viewportCoordToRenderTarget( uv0.xy, rtParams2 ) ).r; + addToResult *= ao; + #endif + + #ifdef PSSM_DEBUG_RENDER + lightColorOut = debugColor; + #endif + + OUT_FragColor0 = lightinfoCondition( lightColorOut, Sat_NL_Att, specular, addToResult ); - // Output - gl_FragColor = lightinfoCondition( lightColor.rgb * lightBrightness, Sat_NL_Att, specular, ambientBlend) * lightMapParams; } diff --git a/Templates/Full/game/shaders/common/lighting/basic/gl/shadowFilterP.glsl b/Templates/Full/game/shaders/common/lighting/basic/gl/shadowFilterP.glsl index 238721e5e..4e97fa347 100644 --- a/Templates/Full/game/shaders/common/lighting/basic/gl/shadowFilterP.glsl +++ b/Templates/Full/game/shaders/common/lighting/basic/gl/shadowFilterP.glsl @@ -20,35 +20,27 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../../gl/hlslCompat.glsl" + uniform sampler2D diffuseMap; -varying vec2 uv; +in vec2 uv; uniform vec2 oneOverTargetSize; +const float offset[3] = float[]( 0.0, 1.3846153846, 3.2307692308 ); +const float weight[3] = float[]( 0.2270270270, 0.3162162162, 0.0702702703 ); + void main() { - vec2 sNonUniformTaps[8]; - - sNonUniformTaps[0] = vec2(0.992833, 0.979309); - sNonUniformTaps[1] = vec2(-0.998585, 0.985853); - sNonUniformTaps[2] = vec2(0.949299, -0.882562); - sNonUniformTaps[3] = vec2(-0.941358, -0.893924); - sNonUniformTaps[4] = vec2(0.545055, -0.589072); - sNonUniformTaps[5] = vec2(0.346526, 0.385821); - sNonUniformTaps[6] = vec2(-0.260183, 0.334412); - sNonUniformTaps[7] = vec2(0.248676, -0.679605); + vec4 OUT = texture( diffuseMap, uv ) * weight[0]; - gl_FragColor = vec4(0.0); - - vec2 texScale = vec2(1.0); - - for ( int i=0; i < 4; i++ ) + for ( int i=1; i < 3; i++ ) { - vec2 offset = (oneOverTargetSize * texScale) * sNonUniformTaps[i]; - gl_FragColor += texture2D( diffuseMap, uv + offset ); + vec2 _sample = (BLUR_DIR * offset[i]) * oneOverTargetSize; + OUT += texture( diffuseMap, uv + _sample ) * weight[i]; + OUT += texture( diffuseMap, uv - _sample ) * weight[i]; } - - gl_FragColor /= vec4(4.0); - gl_FragColor.rgb = vec3(0.0); + + OUT_FragColor0 = OUT; } diff --git a/Templates/Full/game/shaders/common/lighting/basic/gl/shadowFilterV.glsl b/Templates/Full/game/shaders/common/lighting/basic/gl/shadowFilterV.glsl index cbf3696be..0eeb2e0fd 100644 --- a/Templates/Full/game/shaders/common/lighting/basic/gl/shadowFilterV.glsl +++ b/Templates/Full/game/shaders/common/lighting/basic/gl/shadowFilterV.glsl @@ -22,13 +22,16 @@ #include "../../../../../../shaders/common/gl/torque.glsl" -uniform vec2 oneOverTargetSize; +in vec4 vPosition; +in vec2 vTexCoord0; + uniform vec4 rtParams0; -varying vec2 uv; +out vec2 uv; void main() { - gl_Position = gl_Vertex; - uv = viewportCoordToRenderTarget( gl_MultiTexCoord0.st, rtParams0 ); + gl_Position = vPosition; + uv = viewportCoordToRenderTarget( vTexCoord0.st, rtParams0 ); + gl_Position.y *= -1; //correct ssp } diff --git a/Templates/Full/game/shaders/common/lighting/shadowMap/gl/boxFilterP.glsl b/Templates/Full/game/shaders/common/lighting/shadowMap/gl/boxFilterP.glsl index 2800a3f17..0f568c716 100644 --- a/Templates/Full/game/shaders/common/lighting/shadowMap/gl/boxFilterP.glsl +++ b/Templates/Full/game/shaders/common/lighting/shadowMap/gl/boxFilterP.glsl @@ -26,7 +26,7 @@ uniform sampler2D diffuseMap0; uniform float texSize; uniform vec2 blurDimension; -varying vec2 tex0; +in vec2 tex0; void main() { @@ -40,8 +40,8 @@ void main() vec4 accum = vec4(0.0, 0.0, 0.0, 0.0); for(int i = 0; i < int(blurSamples); i++) { - accum += texture2D(diffuseMap0, BaseTexCoord + float(i) * SampleOffset); + accum += texture(diffuseMap0, BaseTexCoord + float(i) * SampleOffset); } accum /= blurSamples; - gl_FragColor = accum; + OUT_FragColor0 = accum; } \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/lighting/shadowMap/gl/boxFilterV.glsl b/Templates/Full/game/shaders/common/lighting/shadowMap/gl/boxFilterV.glsl index 3850f83c7..9fc436f6c 100644 --- a/Templates/Full/game/shaders/common/lighting/shadowMap/gl/boxFilterV.glsl +++ b/Templates/Full/game/shaders/common/lighting/shadowMap/gl/boxFilterV.glsl @@ -20,12 +20,15 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +in vec4 vPosition; +in vec2 vTexCoord0; + uniform mat4 modelview; -varying vec2 tex0; +out vec2 tex0; void main() { - gl_Position = modelview * gl_Vertex; - tex0 = gl_MultiTexCoord0.st; + gl_Position = modelview * vPosition; + tex0 = vTexCoord0.st; } \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/postFx/postFxV.glsl b/Templates/Full/game/shaders/common/postFx/postFxV.glsl index 3aded2e0f..96a5ec819 100644 --- a/Templates/Full/game/shaders/common/postFx/postFxV.glsl +++ b/Templates/Full/game/shaders/common/postFx/postFxV.glsl @@ -22,27 +22,30 @@ #include "./../gl/torque.glsl" +in vec4 vPosition; +in vec2 vTexCoord0; +in vec3 vTexCoord1; uniform vec4 rtParams0; uniform vec4 rtParams1; uniform vec4 rtParams2; uniform vec4 rtParams3; -varying vec2 uv0; -varying vec2 uv1; -varying vec2 uv2; -varying vec2 uv3; -varying vec3 wsEyeRay; +out vec2 uv0; +out vec2 uv1; +out vec2 uv2; +out vec2 uv3; +out vec3 wsEyeRay; void main() { - gl_Position = gl_Vertex; + gl_Position = vPosition; - uv0 = viewportCoordToRenderTarget( gl_MultiTexCoord0.st, rtParams0 ); - uv1 = viewportCoordToRenderTarget( gl_MultiTexCoord0.st, rtParams1 ); - uv2 = viewportCoordToRenderTarget( gl_MultiTexCoord0.st, rtParams2 ); - uv3 = viewportCoordToRenderTarget( gl_MultiTexCoord0.st, rtParams3 ); + uv0 = viewportCoordToRenderTarget( vTexCoord0, rtParams0 ); + uv1 = viewportCoordToRenderTarget( vTexCoord0, rtParams1 ); + uv2 = viewportCoordToRenderTarget( vTexCoord0, rtParams2 ); + uv3 = viewportCoordToRenderTarget( vTexCoord0, rtParams3 ); - wsEyeRay = gl_MultiTexCoord1.xyz; + wsEyeRay = vTexCoord1; } diff --git a/Templates/Full/game/shaders/common/terrain/gl/blendP.glsl b/Templates/Full/game/shaders/common/terrain/gl/blendP.glsl index 3817e1de2..a2e4af787 100644 --- a/Templates/Full/game/shaders/common/terrain/gl/blendP.glsl +++ b/Templates/Full/game/shaders/common/terrain/gl/blendP.glsl @@ -23,8 +23,10 @@ #include "../terrain.glsl" #include "../../gl/hlslCompat.glsl" -varying vec2 layerCoord; -varying vec2 texCoord; +in vec2 layerCoord; +#define IN_layerCoord layerCoord +in vec2 texCoord; +#define IN_texCoord texCoord uniform sampler2D layerTex; uniform sampler2D textureMap; @@ -33,12 +35,12 @@ uniform float layerSize; void main() { - vec4 layerSample = round(texture2D( layerTex, layerCoord ) * 255.0); + vec4 layerSample = round(texture( layerTex, IN_layerCoord ) * 255.0); - float blend = calcBlend( texId, layerCoord, layerSize, layerSample ); + float blend = calcBlend( texId, IN_layerCoord, layerSize, layerSample ); if(blend - 0.0001 < 0.0) discard; - gl_FragColor = vec4( texture2D( textureMap, texCoord ).rgb, blend ); + OUT_FragColor0 = vec4( texture( textureMap, IN_texCoord ).rgb, blend ); } diff --git a/Templates/Full/game/shaders/common/terrain/gl/blendV.glsl b/Templates/Full/game/shaders/common/terrain/gl/blendV.glsl index 44362085b..dc7b7befa 100644 --- a/Templates/Full/game/shaders/common/terrain/gl/blendV.glsl +++ b/Templates/Full/game/shaders/common/terrain/gl/blendV.glsl @@ -23,14 +23,19 @@ /// The vertex shader used in the generation and caching of the /// base terrain texture. -varying vec2 layerCoord; -varying vec2 texCoord; +in vec4 vPosition; +in vec2 vTexCoord0; + +out vec2 layerCoord; +out vec2 texCoord; uniform vec2 texScale; void main() { - gl_Position = vec4(gl_Vertex.xyz, 1.0); - layerCoord = gl_MultiTexCoord0.st; - texCoord = gl_MultiTexCoord0.st * texScale; + gl_Position = vec4(vPosition.xyz, 1.0); + layerCoord = vTexCoord0.st; + texCoord = vTexCoord0.st * texScale; + + gl_Position.y *= -1; } diff --git a/Templates/Full/game/shaders/common/water/gl/waterBasicP.glsl b/Templates/Full/game/shaders/common/water/gl/waterBasicP.glsl index 72232622a..44207dea9 100644 --- a/Templates/Full/game/shaders/common/water/gl/waterBasicP.glsl +++ b/Templates/Full/game/shaders/common/water/gl/waterBasicP.glsl @@ -31,7 +31,7 @@ #define FRESNEL_BIAS miscParams[0] #define FRESNEL_POWER miscParams[1] #define CLARITY miscParams[2] -#define ISRIVER miscParams[3] +#define ISRIVER miscParams[3] // reflectParams #define REFLECT_PLANE_Z reflectParams[0] @@ -45,40 +45,49 @@ #define DISTORT_FULL_DEPTH distortionParams[2] // ConnectData.misc -#define LIGHT_VEC misc.xyz -#define WORLD_Z objPos.w +#define LIGHT_VEC IN_misc.xyz +#define WORLD_Z IN_objPos.w // specularParams #define SPEC_POWER specularParams[3] #define SPEC_COLOR specularParams.xyz +//----------------------------------------------------------------------------- +// Defines +//----------------------------------------------------------------------------- + // TexCoord 0 and 1 (xy,zw) for ripple texture lookup -varying vec4 rippleTexCoord01; +in vec4 rippleTexCoord01; +#define IN_rippleTexCoord01 rippleTexCoord01 // TexCoord 2 for ripple texture lookup -varying vec2 rippleTexCoord2; +in vec2 rippleTexCoord2; +#define IN_rippleTexCoord2 rippleTexCoord2 // Screenspace vert position BEFORE wave transformation -varying vec4 posPreWave; +in vec4 posPreWave; +#define IN_posPreWave posPreWave // Screenspace vert position AFTER wave transformation -varying vec4 posPostWave; +in vec4 posPostWave; +#define IN_posPostWave posPostWave // Worldspace unit distance/depth of this vertex/pixel -varying float pixelDist; - -// Objectspace vert position BEFORE wave transformation -// w coord is world space z position. -varying vec4 objPos; +in float pixelDist; +#define IN_pixelDist pixelDist -varying vec3 misc; +in vec4 objPos; +#define IN_objPos objPos + +in vec3 misc; +#define IN_misc misc //----------------------------------------------------------------------------- // approximate Fresnel function //----------------------------------------------------------------------------- float fresnel(float NdotV, float bias, float power) { - return bias + (1.0-bias)*pow(abs(1.0 - max(NdotV, 0.0)), power); + return bias + (1.0-bias)*pow(abs(1.0 - max(NdotV, 0)), power); } //----------------------------------------------------------------------------- @@ -89,7 +98,7 @@ uniform sampler2D bumpMap; uniform sampler2D reflectMap; uniform sampler2D refractBuff; uniform samplerCube skyMap; -//uniform sampler foamMap; +//uniform sampler2D foamMap; uniform vec4 baseColor; uniform vec4 miscParams; uniform vec4 reflectParams; @@ -98,8 +107,9 @@ uniform vec3 eyePos; uniform vec3 distortionParams; uniform vec3 fogData; uniform vec4 fogColor; -uniform vec3 rippleMagnitude; +uniform vec4 rippleMagnitude; uniform vec4 specularParams; +uniform mat4 modelMat; //----------------------------------------------------------------------------- // Main @@ -107,31 +117,35 @@ uniform vec4 specularParams; void main() { // Modulate baseColor by the ambientColor. - vec4 waterBaseColor = baseColor * vec4( ambientColor.rgb, 1.0 ); + vec4 waterBaseColor = baseColor * vec4( ambientColor.rgb, 1 ); // Get the bumpNorm... - vec3 bumpNorm = ( texture2D( bumpMap, rippleTexCoord01.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.x; - bumpNorm += ( texture2D( bumpMap, rippleTexCoord01.zw ).rgb * 2.0 - 1.0 ) * rippleMagnitude.y; - bumpNorm += ( texture2D( bumpMap, rippleTexCoord2 ).rgb * 2.0 - 1.0 ) * rippleMagnitude.z; - + vec3 bumpNorm = ( texture( bumpMap, IN_rippleTexCoord01.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.x; + bumpNorm += ( texture( bumpMap, IN_rippleTexCoord01.zw ).rgb * 2.0 - 1.0 ) * rippleMagnitude.y; + bumpNorm += ( texture( bumpMap, IN_rippleTexCoord2 ).rgb * 2.0 - 1.0 ) * rippleMagnitude.z; + + bumpNorm = normalize( bumpNorm ); + bumpNorm = mix( bumpNorm, vec3(0,0,1), 1.0 - rippleMagnitude.w ); + // We subtract a little from it so that we don't // distort where the water surface intersects the // camera near plane. - float distortAmt = saturate( pixelDist / 1.0 ) * 0.8; + float distortAmt = saturate( IN_pixelDist / 1.0 ) * 0.8; - vec4 distortPos = posPostWave; + vec4 distortPos = IN_posPostWave; distortPos.xy += bumpNorm.xy * distortAmt; #ifdef UNDERWATER - gl_FragColor = texture2DProj( refractBuff, distortPos.xyz ); + OUT_FragColor0 = hdrEncode( textureProj( refractBuff, distortPos ) ); #else - vec3 eyeVec = objPos.xyz - eyePos; - vec3 reflectionVec = reflect( eyeVec, normalize(bumpNorm) ); + vec3 eyeVec = IN_objPos.xyz - eyePos; + eyeVec = tMul( mat3(modelMat), eyeVec ); + vec3 reflectionVec = reflect( eyeVec, bumpNorm ); // Color that replaces the reflection color when we do not // have one that is appropriate. - vec4 fakeColor = vec4(ambientColor,1.0); + vec4 fakeColor = vec4(ambientColor,1); // Use fakeColor for ripple-normals that are angled towards the camera eyeVec = -eyeVec; @@ -140,58 +154,61 @@ void main() float fakeColorAmt = ang; // Get reflection map color - vec4 refMapColor = texture2DProj( reflectMap, distortPos ); + vec4 refMapColor = hdrDecode( textureProj( reflectMap, distortPos ) ); // If we do not have a reflection texture then we use the cubemap. - refMapColor = mix( refMapColor, textureCube( skyMap, -reflectionVec ), NO_REFLECT ); + refMapColor = mix( refMapColor, texture( skyMap, reflectionVec ), NO_REFLECT ); // Combine reflection color and fakeColor. vec4 reflectColor = mix( refMapColor, fakeColor, fakeColorAmt ); //return refMapColor; // Get refract color - vec4 refractColor = texture2DProj( refractBuff, distortPos.xyz ); + vec4 refractColor = hdrDecode( textureProj( refractBuff, distortPos ) ); // calc "diffuse" color by lerping from the water color // to refraction image based on the water clarity. - vec4 diffuseColor = mix( refractColor, waterBaseColor, 1.0 - CLARITY ); + vec4 diffuseColor = mix( refractColor, waterBaseColor, 1.0f - CLARITY ); // fresnel calculation float fresnelTerm = fresnel( ang, FRESNEL_BIAS, FRESNEL_POWER ); + //return vec4( fresnelTerm.rrr, 1 ); // Also scale the frensel by our distance to the // water surface. This removes the hard reflection // when really close to the water surface. - fresnelTerm *= saturate( pixelDist - 0.1 ); + fresnelTerm *= saturate( IN_pixelDist - 0.1 ); // Combine the diffuse color and reflection image via the // fresnel term and set out output color. - gl_FragColor = mix( diffuseColor, reflectColor, fresnelTerm ); - + vec4 OUT = mix( diffuseColor, reflectColor, fresnelTerm ); + #ifdef WATER_SPEC // Get some specular reflection. vec3 newbump = bumpNorm; newbump.xy *= 3.5; newbump = normalize( bumpNorm ); - vec3 halfAng = normalize( eyeVec + -LIGHT_VEC ); + half3 halfAng = normalize( eyeVec + -LIGHT_VEC ); float specular = saturate( dot( newbump, halfAng ) ); specular = pow( specular, SPEC_POWER ); - gl_FragColor.rgb = gl_FragColor.rgb + ( SPEC_COLOR * specular.xxx ); + OUT.rgb = OUT.rgb + ( SPEC_COLOR * specular.xxx ); #else // Disable fogging if spec is on because otherwise we run out of instructions. // Fog it. float factor = computeSceneFog( eyePos, - objPos.xyz, + IN_objPos.xyz, WORLD_Z, fogData.x, fogData.y, fogData.z ); - gl_FragColor.rgb = mix( gl_FragColor.rgb, fogColor.rgb, 1.0 - saturate( factor ) ); - - #endif + //OUT.rgb = mix( OUT.rgb, fogColor.rgb, 1.0 - saturate( factor ) ); + #endif + + OUT_FragColor0 = OUT; + #endif } diff --git a/Templates/Full/game/shaders/common/water/gl/waterBasicV.glsl b/Templates/Full/game/shaders/common/water/gl/waterBasicV.glsl index bb2a0c954..1634fd2de 100644 --- a/Templates/Full/game/shaders/common/water/gl/waterBasicV.glsl +++ b/Templates/Full/game/shaders/common/water/gl/waterBasicV.glsl @@ -27,23 +27,30 @@ //----------------------------------------------------------------------------- // TexCoord 0 and 1 (xy,zw) for ripple texture lookup -varying vec4 rippleTexCoord01; +out vec4 rippleTexCoord01; +#define OUT_rippleTexCoord01 rippleTexCoord01 // TexCoord 2 for ripple texture lookup -varying vec2 rippleTexCoord2; +out vec2 rippleTexCoord2; +#define OUT_rippleTexCoord2 rippleTexCoord2 // Screenspace vert position BEFORE wave transformation -varying vec4 posPreWave; +out vec4 posPreWave; +#define OUT_posPreWave posPreWave // Screenspace vert position AFTER wave transformation -varying vec4 posPostWave; +out vec4 posPostWave; +#define OUT_posPostWave posPostWave // Worldspace unit distance/depth of this vertex/pixel -varying float pixelDist; +out float pixelDist; +#define OUT_pixelDist pixelDist -varying vec4 objPos; +out vec4 objPos; +#define OUT_objPos objPos -varying vec3 misc; +out vec3 misc; +#define OUT_misc misc //----------------------------------------------------------------------------- // Uniforms @@ -63,49 +70,56 @@ uniform float gridElementSize; uniform float elapsedTime; uniform float undulateMaxDist; +in vec4 vPosition; +in vec3 vNormal; +in vec4 vColor; +in vec2 vTexCoord0; +in vec4 vTexCoord1; + //----------------------------------------------------------------------------- // Main //----------------------------------------------------------------------------- void main() { - vec4 position = gl_Vertex; - vec3 normal = gl_Normal; - vec2 undulateData = gl_MultiTexCoord0.st; - vec4 horizonFactor = gl_MultiTexCoord1; + vec4 IN_position = vPosition; + vec3 IN_normal = vNormal; + vec2 IN_undulateData = vTexCoord0; + vec4 IN_horizonFactor = vTexCoord1; + vec4 OUT_hpos = vec4(0); // use projection matrix for reflection / refraction texture coords - mat4 texGen = mat4(0.5, 0.0, 0.0, 0.0, - 0.0, 0.5, 0.0, 0.0, - 0.0, 0.0, 1.0, 0.0, - 0.5, 0.5, 0.0, 1.0); + mat4 texGen = mat4FromRow( 0.5, 0.0, 0.0, 0.5, + 0.0, -0.5, 0.0, 0.5, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 ); // Move the vertex based on the horizonFactor if specified to do so for this vert. - //if ( horizonFactor.z > 0.0 ) - //{ - //vec2 offsetXY = eyePos.xy - mod(eyePos.xy, gridElementSize); - //position.xy += offsetXY; - //undulateData += offsetXY; - //} + // if ( IN_horizonFactor.z > 0 ) + // { + // vec2 offsetXY = eyePos.xy - eyePos.xy % gridElementSize; + // IN_position.xy += offsetXY; + // IN_undulateData += offsetXY; + // } - vec4 worldPos = modelMat * position; - //fogPos = position.xyz; - position.z = mix( position.z, eyePos.z, horizonFactor.x ); - - objPos.xyz = position.xyz; - objPos.w = worldPos.z; + vec4 worldPos = tMul( modelMat, IN_position ); + + IN_position.z = mix( IN_position.z, eyePos.z, IN_horizonFactor.x ); + + //OUT_objPos = worldPos; + OUT_objPos.xyz = IN_position.xyz; + OUT_objPos.w = worldPos.z; // Send pre-undulation screenspace position - posPreWave = modelview * position; - posPreWave = texGen * posPreWave; + OUT_posPreWave = tMul( modelview, IN_position ); + OUT_posPreWave = tMul( texGen, OUT_posPreWave ); // Calculate the undulation amount for this vertex. - vec2 undulatePos = (modelMat * vec4( undulateData.xy, 0, 1 )).xy; - - //if ( undulatePos.x < 0.0 ) - //undulatePos = position.xy; - - float undulateAmt = 0.0; + vec2 undulatePos = tMul( modelMat, vec4( IN_undulateData.xy, 0, 1 ) ).xy; + //if ( undulatePos.x < 0 ) + // undulatePos = IN_position.xy; + float undulateAmt = 0.0; + undulateAmt += waveData[0].y * sin( elapsedTime * waveData[0].x + undulatePos.x * waveDir[0].x + undulatePos.y * waveDir[0].y ); @@ -114,118 +128,84 @@ void main() undulatePos.y * waveDir[1].y ); undulateAmt += waveData[2].y * sin( elapsedTime * waveData[2].x + undulatePos.x * waveDir[2].x + - undulatePos.y * waveDir[2].y ); - - float undulateFade = 1.0; - - // Scale down wave magnitude amount based on distance from the camera. - float dist = length( position.xyz - eyePos ); + undulatePos.y * waveDir[2].y ); + + float undulateFade = 1; + + // Scale down wave magnitude amount based on distance from the camera. + float dist = distance( IN_position.xyz, eyePos ); dist = clamp( dist, 1.0, undulateMaxDist ); - undulateFade *= ( 1.0 - dist / undulateMaxDist ); + undulateFade *= ( 1 - dist / undulateMaxDist ); // Also scale down wave magnitude if the camera is very very close. - undulateFade *= saturate( ( length( position.xyz - eyePos ) - 0.5 ) / 10.0 ); - + undulateFade *= saturate( ( distance( IN_position.xyz, eyePos ) - 0.5 ) / 10.0 ); + undulateAmt *= undulateFade; + //#endif //undulateAmt = 0; // Apply wave undulation to the vertex. - posPostWave = position; - posPostWave.xyz += normal.xyz * undulateAmt; + OUT_posPostWave = IN_position; + OUT_posPostWave.xyz += IN_normal.xyz * undulateAmt; // Save worldSpace position of this pixel/vert - //worldPos = posPostWave.xyz; + //OUT_worldPos = OUT_posPostWave.xyz; + //OUT_worldPos = tMul( modelMat, OUT_posPostWave.xyz ); + //OUT_worldPos.z += objTrans[2][2]; //91.16; - //worldSpaceZ = ( modelMat * vec4(fogPos,1.0) ).z; - //if ( horizonFactor.x > 0.0 ) - //{ - //vec3 awayVec = normalize( fogPos.xyz - eyePos ); - //fogPos.xy += awayVec.xy * 1000.0; - //} + // OUT_misc.w = tMul( modelMat, OUT_fogPos ).z; + // if ( IN_horizonFactor.x > 0 ) + // { + // vec3 awayVec = normalize( OUT_fogPos.xyz - eyePos ); + // OUT_fogPos.xy += awayVec.xy * 1000.0; + // } // Convert to screen - posPostWave = modelview * posPostWave; + OUT_posPostWave = tMul( modelview, OUT_posPostWave ); // tMul( modelview, vec4( OUT_posPostWave.xyz, 1 ) ); // Setup the OUT position symantic variable - gl_Position = posPostWave; - //gl_Position.z = mix(gl_Position.z, gl_Position.w, horizonFactor.x); + OUT_hpos = OUT_posPostWave; // tMul( modelview, vec4( IN_position.xyz, 1 ) ); //vec4( OUT_posPostWave.xyz, 1 ); + //OUT_hpos.z = mix( OUT_hpos.z, OUT_hpos.w, IN_horizonFactor.x ); // Save world space camera dist/depth of the outgoing pixel - pixelDist = gl_Position.z; + OUT_pixelDist = OUT_hpos.z; // Convert to reflection texture space - posPostWave = texGen * posPostWave; + OUT_posPostWave = tMul( texGen, OUT_posPostWave ); vec2 txPos = undulatePos; - if ( horizonFactor.x > 0.0 ) + if ( bool(IN_horizonFactor.x) ) txPos = normalize( txPos ) * 50000.0; - - - // set up tex coordinates for the 3 interacting normal maps - rippleTexCoord01.xy = txPos * rippleTexScale[0]; - rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; + + // set up tex coordinates for the 3 interacting normal maps + OUT_rippleTexCoord01.xy = txPos * rippleTexScale[0]; + OUT_rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; mat2 texMat; texMat[0][0] = rippleMat[0].x; texMat[1][0] = rippleMat[0].y; texMat[0][1] = rippleMat[0].z; texMat[1][1] = rippleMat[0].w; - rippleTexCoord01.xy = texMat * rippleTexCoord01.xy ; + OUT_rippleTexCoord01.xy = tMul( texMat, OUT_rippleTexCoord01.xy ); - rippleTexCoord01.zw = txPos * rippleTexScale[1]; - rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; + OUT_rippleTexCoord01.zw = txPos * rippleTexScale[1]; + OUT_rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; texMat[0][0] = rippleMat[1].x; texMat[1][0] = rippleMat[1].y; texMat[0][1] = rippleMat[1].z; texMat[1][1] = rippleMat[1].w; - rippleTexCoord01.zw = texMat * rippleTexCoord01.zw ; + OUT_rippleTexCoord01.zw = tMul( texMat, OUT_rippleTexCoord01.zw ); - rippleTexCoord2.xy = txPos * rippleTexScale[2]; - rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; + OUT_rippleTexCoord2.xy = txPos * rippleTexScale[2]; + OUT_rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; texMat[0][0] = rippleMat[2].x; texMat[1][0] = rippleMat[2].y; texMat[0][1] = rippleMat[2].z; texMat[1][1] = rippleMat[2].w; - rippleTexCoord2.xy = texMat * rippleTexCoord2.xy ; - - - /*rippleTexCoord01.xy = mix( position.xy * rippleTexScale[0], txPos.xy * rippleTexScale[0], horizonFactor.x ); - rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; - - rippleTexCoord01.zw = mix( position.xy * rippleTexScale[1], txPos.xy * rippleTexScale[1], horizonFactor.x ); - rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; - - rippleTexCoord2.xy = mix( position.xy * rippleTexScale[2], txPos.xy * rippleTexScale[2], horizonFactor.x ); - rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; */ - - - /*rippleTexCoord01.xy = mix( position.xy * rippleTexScale[0], txPos.xy * rippleTexScale[0], horizonFactor.x ); - rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; - mat2 texMat; - texMat[0][0] = rippleMat[0].x; - texMat[1][0] = rippleMat[0].y; - texMat[0][1] = rippleMat[0].z; - texMat[1][1] = rippleMat[0].w; - rippleTexCoord01.xy = texMat * rippleTexCoord01.xy ; - - rippleTexCoord01.zw = mix( position.xy * rippleTexScale[1], txPos.xy * rippleTexScale[1], horizonFactor.x ); - rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; - texMat[0][0] = rippleMat[1].x; - texMat[1][0] = rippleMat[1].y; - texMat[0][1] = rippleMat[1].z; - texMat[1][1] = rippleMat[1].w; - rippleTexCoord01.zw = texMat * rippleTexCoord01.zw ; - - rippleTexCoord2.xy = mix( position.xy * rippleTexScale[2], txPos.xy * rippleTexScale[2], horizonFactor.x ); - rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; - texMat[0][0] = rippleMat[2].x; - texMat[1][0] = rippleMat[2].y; - texMat[0][1] = rippleMat[2].z; - texMat[1][1] = rippleMat[2].w; - rippleTexCoord2.xy = texMat * rippleTexCoord2.xy ;*/ + OUT_rippleTexCoord2.xy = tMul( texMat, OUT_rippleTexCoord2.xy ); #ifdef WATER_SPEC @@ -234,8 +214,8 @@ void main() vec3 normal; for ( int i = 0; i < 3; i++ ) { - binormal.z += undulateFade * waveDir[i].x * waveData[i].y * cos( waveDir[i].x * undulateData.x + waveDir[i].y * undulateData.y + elapsedTime * waveData[i].x ); - tangent.z += undulateFade * waveDir[i].y * waveData[i].y * cos( waveDir[i].x * undulateData.x + waveDir[i].y * undulateData.y + elapsedTime * waveData[i].x ); + binormal.z += undulateFade * waveDir[i].x * waveData[i].y * cos( waveDir[i].x * IN_undulateData.x + waveDir[i].y * IN_undulateData.y + elapsedTime * waveData[i].x ); + tangent.z += undulateFade * waveDir[i].y * waveData[i].y * cos( waveDir[i].x * IN_undulateData.x + waveDir[i].y * IN_undulateData.y + elapsedTime * waveData[i].x ); } binormal = normalize( binormal ); @@ -246,15 +226,19 @@ void main() worldToTangent[0] = binormal; worldToTangent[1] = tangent; worldToTangent[2] = normal; + + worldToTangent = transpose(worldToTangent); - misc.xyz = inLightVec * modelMat; - misc.xyz = worldToTangent * misc.xyz; + OUT_misc.xyz = tMul( inLightVec, modelMat ); + OUT_misc.xyz = tMul( worldToTangent, OUT_misc.xyz ); #else - misc.xyz = inLightVec; - + OUT_misc.xyz = inLightVec; + #endif - + + gl_Position = OUT_hpos; + correctSSP(gl_Position); } diff --git a/Templates/Full/game/shaders/common/water/gl/waterP.glsl b/Templates/Full/game/shaders/common/water/gl/waterP.glsl index bf482d724..3e15fe576 100644 --- a/Templates/Full/game/shaders/common/water/gl/waterP.glsl +++ b/Templates/Full/game/shaders/common/water/gl/waterP.glsl @@ -20,6 +20,7 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../gl/hlslCompat.glsl" #include "shadergen:/autogenConditioners.h" #include "../../gl/torque.glsl" @@ -27,10 +28,7 @@ // Defines //----------------------------------------------------------------------------- -#ifdef TORQUE_BASIC_LIGHTING - #define BASIC -#endif - +#define PIXEL_DIST IN_rippleTexCoord2.z // miscParams #define FRESNEL_BIAS miscParams[0] #define FRESNEL_POWER miscParams[1] @@ -57,33 +55,54 @@ #define DISTORT_FULL_DEPTH distortionParams[2] // foamParams -#define FOAM_SCALE foamParams[0] +#define FOAM_OPACITY foamParams[0] #define FOAM_MAX_DEPTH foamParams[1] +#define FOAM_AMBIENT_LERP foamParams[2] +#define FOAM_RIPPLE_INFLUENCE foamParams[3] -// Incoming data -// Worldspace position of this pixel -varying vec3 worldPos; +// specularParams +#define SPEC_POWER specularParams[3] +#define SPEC_COLOR specularParams.xyz + +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- + +//ConnectData IN + +in vec4 hpos; // TexCoord 0 and 1 (xy,zw) for ripple texture lookup -varying vec4 rippleTexCoord01; +in vec4 rippleTexCoord01; -// TexCoord 2 for ripple texture lookup -varying vec2 rippleTexCoord2; +// xy is TexCoord 2 for ripple texture lookup +// z is the Worldspace unit distance/depth of this vertex/pixel +// w is amount of the crestFoam ( more at crest of waves ). +in vec4 rippleTexCoord2; // Screenspace vert position BEFORE wave transformation -varying vec4 posPreWave; +in vec4 posPreWave; // Screenspace vert position AFTER wave transformation -varying vec4 posPostWave; +in vec4 posPostWave; -// Worldspace unit distance/depth of this vertex/pixel -varying float pixelDist; +// Objectspace vert position BEFORE wave transformation +// w coord is world space z position. +in vec4 objPos; -varying vec3 fogPos; +in vec4 foamTexCoords; -varying float worldSpaceZ; +in mat3 tangentMat; -varying vec4 foamTexCoords; + +#define IN_hpos hpos +#define IN_rippleTexCoord01 rippleTexCoord01 +#define IN_rippleTexCoord2 rippleTexCoord2 +#define IN_posPreWave posPreWave +#define IN_posPostWave posPostWave +#define IN_objPos objPos +#define IN_foamTexCoords foamTexCoords +#define IN_tangentMat tangentMat //----------------------------------------------------------------------------- // approximate Fresnel function @@ -100,10 +119,10 @@ uniform sampler2D bumpMap; uniform sampler2D prepassTex; uniform sampler2D reflectMap; uniform sampler2D refractBuff; -uniform samplerCUBE skyMap; +uniform samplerCube skyMap; uniform sampler2D foamMap; -uniform vec4 specularColor; -uniform float specularPower; +uniform sampler1D depthGradMap; +uniform vec4 specularParams; uniform vec4 baseColor; uniform vec4 miscParams; uniform vec2 fogParams; @@ -112,64 +131,45 @@ uniform vec3 reflectNormal; uniform vec2 wetnessParams; uniform float farPlaneDist; uniform vec3 distortionParams; -//uniform vec4 renderTargetParams; -uniform vec2 foamParams; -uniform vec3 foamColorMod; +uniform vec4 foamParams; uniform vec3 ambientColor; -uniform vec3 eyePos; -uniform vec3 inLightVec; +uniform vec3 eyePos; // This is in object space! uniform vec3 fogData; uniform vec4 fogColor; -//uniform vec4 rtParams; -uniform vec2 rtScale; -uniform vec2 rtHalfPixel; -uniform vec4 rtOffset; -uniform vec3 rippleMagnitude; +uniform vec4 rippleMagnitude; +uniform vec4 rtParams1; +uniform float depthGradMax; +uniform vec3 inLightVec; +uniform mat4 modelMat; +uniform vec4 sunColor; +uniform float sunBrightness; +uniform float reflectivity; //----------------------------------------------------------------------------- // Main //----------------------------------------------------------------------------- void main() { - vec4 rtParams = vec4( rtOffset.x / rtOffset.z + rtHalfPixel.x, - rtOffset.y / rtOffset.w + rtHalfPixel.x, - rtScale ); - - // Modulate baseColor by the ambientColor. - vec4 waterBaseColor = baseColor * vec4( ambientColor.rgb, 1 ); - // Get the bumpNorm... - vec3 bumpNorm = ( tex2D( bumpMap, IN.rippleTexCoord01.xy ) * 2.0 - 1.0 ) * rippleMagnitude.x; - bumpNorm += ( tex2D( bumpMap, IN.rippleTexCoord01.zw ) * 2.0 - 1.0 ) * rippleMagnitude.y; - bumpNorm += ( tex2D( bumpMap, IN.rippleTexCoord2 ) * 2.0 - 1.0 ) * rippleMagnitude.z; - - // JCF: this was here, but seems to make the dot product against the bump - // normal we use below for cubeMap fade-in to be less reliable. - //bumpNorm.xy *= 0.75; - //bumpNorm = normalize( bumpNorm ); - //return vec4( bumpNorm, 1 ); + vec3 bumpNorm = ( texture( bumpMap, IN_rippleTexCoord01.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.x; + bumpNorm += ( texture( bumpMap, IN_rippleTexCoord01.zw ).rgb * 2.0 - 1.0 ) * rippleMagnitude.y; + bumpNorm += ( texture( bumpMap, IN_rippleTexCoord2.xy ).rgb * 2.0 - 1.0 ) * rippleMagnitude.z; + + bumpNorm = normalize( bumpNorm ); + bumpNorm = mix( bumpNorm, vec3(0,0,1), 1.0 - rippleMagnitude.w ); + bumpNorm = tMul( bumpNorm, IN_tangentMat ); // Get depth of the water surface (this pixel). // Convert from WorldSpace to EyeSpace. - float pixelDepth = IN.pixelDist / farPlaneDist; + float pixelDepth = PIXEL_DIST / farPlaneDist; - // Get prepass depth at the undistorted pixel. - //vec4 prepassCoord = IN.posPostWave; - //prepassCoord.xy += renderTargetParams.xy; - vec2 prepassCoord = viewportCoordToRenderTarget( IN.posPostWave, rtParams ); - //vec2 prepassCoord = IN.posPostWave.xy; + vec2 prepassCoord = viewportCoordToRenderTarget( IN_posPostWave, rtParams1 ); - float startDepth = prepassUncondition( tex2D( prepassTex, prepassCoord ) ).w; - //return vec4( startDepth.rrr, 1 ); + float startDepth = prepassUncondition( prepassTex, prepassCoord ).w; // The water depth in world units of the undistorted pixel. float startDelta = ( startDepth - pixelDepth ); - if ( startDelta <= 0.0 ) - { - //return vec4( 1, 0, 0, 1 ); - startDelta = 0; - } - + startDelta = max( startDelta, 0.0 ); startDelta *= farPlaneDist; // Calculate the distortion amount for the water surface. @@ -177,23 +177,22 @@ void main() // We subtract a little from it so that we don't // distort where the water surface intersects the // camera near plane. - float distortAmt = saturate( ( IN.pixelDist - DISTORT_START_DIST ) / DISTORT_END_DIST ); + float distortAmt = saturate( ( PIXEL_DIST - DISTORT_START_DIST ) / DISTORT_END_DIST ); // Scale down distortion in shallow water. distortAmt *= saturate( startDelta / DISTORT_FULL_DEPTH ); - //distortAmt = 0; // Do the intial distortion... we might remove it below. vec2 distortDelta = bumpNorm.xy * distortAmt; - vec4 distortPos = IN.posPostWave; + vec4 distortPos = IN_posPostWave; distortPos.xy += distortDelta; - prepassCoord = viewportCoordToRenderTarget( distortPos, rtParams ); - //prepassCoord = distortPos; - //prepassCoord.xy += renderTargetParams.xy; + prepassCoord = viewportCoordToRenderTarget( distortPos, rtParams1 ); // Get prepass depth at the position of this distorted pixel. - float prepassDepth = prepassUncondition( tex2D( prepassTex, prepassCoord ) ).w; + float prepassDepth = prepassUncondition( prepassTex, prepassCoord ).w; + if ( prepassDepth > 0.99 ) + prepassDepth = 5.0; float delta = ( prepassDepth - pixelDepth ) * farPlaneDist; @@ -202,7 +201,7 @@ void main() // If we got a negative delta then the distorted // sample is above the water surface. Mask it out // by removing the distortion. - distortPos = IN.posPostWave; + distortPos = IN_posPostWave; delta = startDelta; distortAmt = 0; } @@ -212,20 +211,20 @@ void main() if ( diff < 0 ) { - distortAmt = saturate( ( IN.pixelDist - DISTORT_START_DIST ) / DISTORT_END_DIST ); + distortAmt = saturate( ( PIXEL_DIST - DISTORT_START_DIST ) / DISTORT_END_DIST ); distortAmt *= saturate( delta / DISTORT_FULL_DEPTH ); distortDelta = bumpNorm.xy * distortAmt; - distortPos = IN.posPostWave; + distortPos = IN_posPostWave; distortPos.xy += distortDelta; - prepassCoord = viewportCoordToRenderTarget( distortPos, rtParams ); - //prepassCoord = distortPos; - //prepassCoord.xy += renderTargetParams.xy; + prepassCoord = viewportCoordToRenderTarget( distortPos, rtParams1 ); // Get prepass depth at the position of this distorted pixel. - prepassDepth = prepassUncondition( tex2D( prepassTex, prepassCoord ) ).w; + prepassDepth = prepassUncondition( prepassTex, prepassCoord ).w; + if ( prepassDepth > 0.99 ) + prepassDepth = 5.0; delta = ( prepassDepth - pixelDepth ) * farPlaneDist; } @@ -234,133 +233,78 @@ void main() // If we got a negative delta then the distorted // sample is above the water surface. Mask it out // by removing the distortion. - distortPos = IN.posPostWave; + distortPos = IN_posPostWave; delta = startDelta; distortAmt = 0; } } - //return vec4( prepassDepth.rrr, 1 ); - - vec4 temp = IN.posPreWave; + vec4 temp = IN_posPreWave; temp.xy += bumpNorm.xy * distortAmt; - vec2 reflectCoord = viewportCoordToRenderTarget( temp, rtParams ); + vec2 reflectCoord = viewportCoordToRenderTarget( temp, rtParams1 ); - vec2 refractCoord = viewportCoordToRenderTarget( distortPos, rtParams ); + vec2 refractCoord = viewportCoordToRenderTarget( distortPos, rtParams1 ); - // Use cubemap colors instead of reflection colors in several cases... - - // First lookup the CubeMap color - // JCF: which do we want to use here, the reflectNormal or the bumpNormal - // neithor of them is exactly right and how can we combine the two together? - //bumpNorm = reflectNormal; - vec3 eyeVec = IN.worldPos - eyePos; + vec4 fakeColor = vec4(ambientColor,1); + vec3 eyeVec = IN_objPos.xyz - eyePos; + eyeVec = tMul( mat3(modelMat), eyeVec ); + eyeVec = tMul( IN_tangentMat, eyeVec ); vec3 reflectionVec = reflect( eyeVec, bumpNorm ); - //vec4 cubeColor = texCUBE( skyMap, reflectionVec ); - //return cubeColor; - // JCF: using ambient color instead of cubeColor for waterPlane, how do we still use the cubemap for rivers? - vec4 cubeColor = vec4(ambientColor,1); - //cubeColor.rgb = vec3( 0, 0, 1 ); - // Use cubeColor for waves that are angled towards camera + // Use fakeColor for ripple-normals that are angled towards the camera eyeVec = -eyeVec; eyeVec = normalize( eyeVec ); float ang = saturate( dot( eyeVec, bumpNorm ) ); - float cubeAmt = ang; + float fakeColorAmt = ang; - //float rplaneDist = (reflectPlane.x * IN.pos.x + reflectPlane.y * IN.pos.y + reflectPlane.z * IN.pos.z) + reflectPlane.w; - //rplaneDist = saturate( abs( rplaneDist ) / 0.5 ); - - -//#ifdef RIVER // for verts far from the reflect plane z position - float rplaneDist = abs( REFLECT_PLANE_Z - IN.worldPos.z ); + float rplaneDist = abs( REFLECT_PLANE_Z - IN_objPos.w ); rplaneDist = saturate( ( rplaneDist - 1.0 ) / 2.0 ); - //rplaneDist = REFLECT_PLANE_Z / eyePos.z; rplaneDist *= ISRIVER; - cubeAmt = max( cubeAmt, rplaneDist ); -//#endif - - //rplaneDist = IN.worldPos.z / eyePos.z; - - //return vec4( rplaneDist.rrr, 1 ); - //return vec4( (reflectParams[REFLECT_PLANE_Z] / 86.0 ).rrr, 1 ); - - // and for verts farther from the camera - //float cubeAmt = ( eyeDist - reflectParams[REFLECT_MIN_DIST] ) / ( reflectParams[REFLECT_MAX_DIST] - reflectParams[REFLECT_MIN_DIST] ); - //cubeAmt = saturate ( cubeAmt ); - - //float temp = ( eyeDist - reflectParams[REFLECT_MIN_DIST] ) / ( reflectParams[REFLECT_MAX_DIST] - reflectParams[REFLECT_MIN_DIST] ); - //temp = saturate ( temp ); - - // If the camera is very very close to the reflect plane. - //float eyeToPlaneDist = eyePos.z - REFLECT_PLANE_Z; // dot( reflectNormal, eyePos ) + REFLECT_PLANE_Z; - //eyeToPlaneDist = abs( eyeToPlaneDist ); - //eyeToPlaneDist = 1.0 - saturate( abs( eyeToPlaneDist ) / 1 ); - - //return vec4( eyeToPlaneDist.rrr, 1 ); - - //cubeAmt = max( cubeAmt, eyeToPlaneDist ); - //cubeAmt = max( cubeAmt, rplaneDist ); - //cubeAmt = max( cubeAmt, ang ); - //cubeAmt = max( cubeAmt, rplaneDist ); - //cubeAmt = max( cubeAmt, IN.depth.w ); - - // All cubemap if fullReflect is specifically user disabled - cubeAmt = max( cubeAmt, NO_REFLECT ); + fakeColorAmt = max( fakeColorAmt, rplaneDist ); #ifndef UNDERWATER - // Get foam color and amount - IN.foamTexCoords.xy += distortDelta * 0.5; - IN.foamTexCoords.zw += distortDelta * 0.5; + vec2 foamRippleOffset = bumpNorm.xy * FOAM_RIPPLE_INFLUENCE; + vec4 IN_foamTexCoords = IN_foamTexCoords; + IN_foamTexCoords.xy += foamRippleOffset; + IN_foamTexCoords.zw += foamRippleOffset; - vec4 foamColor = tex2D( foamMap, IN.foamTexCoords.xy ); - foamColor += tex2D( foamMap, IN.foamTexCoords.zw ); - //foamColor += tex2D( foamMap, IN.rippleTexCoord2 ) * 0.3; + vec4 foamColor = texture( foamMap, IN_foamTexCoords.xy ); + foamColor += texture( foamMap, IN_foamTexCoords.zw ); foamColor = saturate( foamColor ); - // Modulate foam color by ambient color so we don't have glowing white - // foam at night. - foamColor.rgb = lerp( foamColor.rgb, ambientColor.rgb, foamColorMod.rgb ); + + // Modulate foam color by ambient color + // so we don't have glowing white foam at night. + foamColor.rgb = mix( foamColor.rgb, ambientColor.rgb, FOAM_AMBIENT_LERP ); float foamDelta = saturate( delta / FOAM_MAX_DEPTH ); - float foamAmt = 1.0 - foamDelta; + float foamAmt = 1 - pow( foamDelta, 2 ); // Fade out the foam in very very low depth, // this improves the shoreline a lot. float diff = 0.8 - foamAmt; if ( diff < 0.0 ) - { - //return vec4( 1,0,0,1 ); foamAmt -= foamAmt * abs( diff ) / 0.2; - } - //return vec4( foamAmt.rrr, 1 ); - - foamAmt *= FOAM_SCALE * foamColor.a; - //return vec4( foamAmt.rrr, 1 ); - // Get reflection map color - vec4 refMapColor = tex2D( reflectMap, reflectCoord ); + foamAmt *= FOAM_OPACITY * foamColor.a; - //cubeAmt = 0; + foamColor.rgb *= FOAM_OPACITY * foamAmt * foamColor.a; - // Combine cube and foam colors into reflect color - vec4 reflectColor = lerp( refMapColor, cubeColor, cubeAmt ); - //return refMapColor; + // Get reflection map color. + vec4 refMapColor = hdrDecode( texture( reflectMap, reflectCoord ) ); - // This doesn't work because REFLECT_PLANE_Z is in worldSpace - // while eyePos is actually in objectSpace! + // If we do not have a reflection texture then we use the cubemap. + refMapColor = mix( refMapColor, texture( skyMap, reflectionVec ), NO_REFLECT ); - //float eyeToPlaneDist = eyePos.z - REFLECT_PLANE_Z; // dot( reflectNormal, eyePos ) + REFLECT_PLANE_Z; - //float transitionFactor = 1.0 - saturate( ( abs( eyeToPlaneDist ) - 0.5 ) / 5 ); - //reflectColor = lerp( reflectColor, waterBaseColor, transitionFactor ); - - //return reflectColor; + fakeColor = ( texture( skyMap, reflectionVec ) ); + fakeColor.a = 1; + // Combine reflection color and fakeColor. + vec4 reflectColor = mix( refMapColor, fakeColor, fakeColorAmt ); // Get refract color - vec4 refractColor = tex2D( refractBuff, refractCoord ); - //return refractColor; + vec4 refractColor = hdrDecode( texture( refractBuff, refractCoord ) ); // We darken the refraction color a bit to make underwater // elements look wet. We fade out this darkening near the @@ -371,86 +315,80 @@ void main() // Add Water fog/haze. float fogDelta = delta - FOG_DENSITY_OFFSET; - //return vec4( fogDelta.rrr, 1 ); + if ( fogDelta < 0.0 ) fogDelta = 0.0; float fogAmt = 1.0 - saturate( exp( -FOG_DENSITY * fogDelta ) ); - //return vec4( fogAmt.rrr, 1 ); + + // Calculate the water "base" color based on depth. + vec4 waterBaseColor = baseColor * texture( depthGradMap, saturate( delta / depthGradMax ) ); + + // Modulate baseColor by the ambientColor. + waterBaseColor *= vec4( ambientColor.rgb, 1 ); // calc "diffuse" color by lerping from the water color // to refraction image based on the water clarity. - vec4 diffuseColor = lerp( refractColor, waterBaseColor, fogAmt ); + vec4 diffuseColor = mix( refractColor, waterBaseColor, fogAmt ); // fresnel calculation float fresnelTerm = fresnel( ang, FRESNEL_BIAS, FRESNEL_POWER ); - //return vec4( fresnelTerm.rrr, 1 ); // Scale the frensel strength by fog amount // so that parts that are very clear get very little reflection. fresnelTerm *= fogAmt; - //return vec4( fresnelTerm.rrr, 1 ); // Also scale the frensel by our distance to the // water surface. This removes the hard reflection // when really close to the water surface. - fresnelTerm *= saturate( IN.pixelDist - 0.1 ); + fresnelTerm *= saturate( PIXEL_DIST - 0.1 ); + + fresnelTerm *= reflectivity; // Combine the diffuse color and reflection image via the // fresnel term and set out output color. - vec4 gl_FragColor = lerp( diffuseColor, reflectColor, fresnelTerm ); + vec4 OUT = mix( diffuseColor, reflectColor, fresnelTerm ); - //float brightness = saturate( 1.0 - ( waterHeight - eyePosWorld.z - 5.0 ) / 50.0 ); - //gl_FragColor.rgb *= brightness; + vec3 lightVec = inLightVec; + + // Get some specular reflection. + vec3 newbump = bumpNorm; + newbump.xy *= 3.5; + newbump = normalize( bumpNorm ); + vec3 halfAng = normalize( eyeVec + -lightVec ); + float specular = saturate( dot( newbump, halfAng ) ); + specular = pow( specular, SPEC_POWER ); + + // Scale down specularity in very shallow water to improve the transparency of the shoreline. + specular *= saturate( delta / 2 ); + OUT.rgb = OUT.rgb + ( SPEC_COLOR * vec3(specular) ); #else - vec4 refractColor = tex2D( refractBuff, refractCoord ); - vec4 gl_FragColor = refractColor; + + vec4 refractColor = hdrDecode( texture( refractBuff, refractCoord ) ); + vec4 OUT = refractColor; + #endif #ifndef UNDERWATER - gl_FragColor.rgb = lerp( gl_FragColor.rgb, foamColor.rgb, foamAmt ); -#endif - gl_FragColor.a = 1.0; - - // specular experiments - -// 1: -/* - float fDot = dot( bumpNorm, inLightVec ); - vec3 reflect = normalize( 2.0 * bumpNorm * fDot - eyeVec ); - // float specular = saturate(dot( reflect, inLightVec ) ); - float specular = pow( reflect, specularPower ); - gl_FragColor += specularColor * specular; -*/ - - -// 2: This almost looks good -/* - bumpNorm.xy *= 2.0; - bumpNorm = normalize( bumpNorm ); - - vec3 halfAng = normalize( eyeVec + inLightVec ); - float specular = saturate( dot( bumpNorm, halfAng) ); - specular = pow(specular, specularPower); - gl_FragColor += specularColor * specular; -*/ - -#ifndef UNDERWATER + OUT.rgb = OUT.rgb + foamColor.rgb; float factor = computeSceneFog( eyePos, - IN.fogPos, - IN.worldSpaceZ, + IN_objPos.xyz, + IN_objPos.w, fogData.x, fogData.y, fogData.z ); - gl_FragColor.rgb = lerp( gl_FragColor.rgb, fogColor.rgb, 1.0 - saturate( factor ) ); + OUT.rgb = mix( OUT.rgb, fogColor.rgb, 1.0 - saturate( factor ) ); + + //OUT.rgb = fogColor.rgb; #endif - //return vec4( refMapColor.rgb, 1 ); - gl_FragColor.a = 1.0; + OUT.a = 1.0; + + //return OUT; - return gl_FragColor; + OUT_FragColor0 = hdrEncode( OUT ); } diff --git a/Templates/Full/game/shaders/common/water/gl/waterV.glsl b/Templates/Full/game/shaders/common/water/gl/waterV.glsl index d4337476f..490af63a7 100644 --- a/Templates/Full/game/shaders/common/water/gl/waterV.glsl +++ b/Templates/Full/game/shaders/common/water/gl/waterV.glsl @@ -20,58 +20,86 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- +#include "../../gl/hlslCompat.glsl" #include "shadergen:/autogenConditioners.h" +//----------------------------------------------------------------------------- +// Structures +//----------------------------------------------------------------------------- +struct VertData +{ + vec4 position ;// POSITION; + vec3 normal ;// NORMAL; + vec2 undulateData ;// TEXCOORD0; + vec4 horizonFactor ;// TEXCOORD1; +}; + //----------------------------------------------------------------------------- // Defines //----------------------------------------------------------------------------- +//VertData IN +in vec4 vPosition; +in vec3 vNormal; +in vec2 vTexCoord0; +in vec4 vTexCoord1; -// waveData -#define WAVE_SPEED(i) waveData[i].x -#define WAVE_MAGNITUDE(i) waveData[i].y +#define IN_position_ vPosition +#define IN_normal vNormal +#define IN_undulateData vTexCoord0 +#define IN_horizonFactor vTexCoord1 -// Outgoing data -// Worldspace position of this pixel -varying vec3 worldPos; +//ConnectData OUT +// + out vec4 hpos ; // TexCoord 0 and 1 (xy,zw) for ripple texture lookup -varying vec4 rippleTexCoord01; +out vec4 rippleTexCoord01; -// TexCoord 2 for ripple texture lookup -varying vec2 rippleTexCoord2; + // xy is TexCoord 2 for ripple texture lookup + // z is the Worldspace unit distance/depth of this vertex/pixel + // w is amount of the crestFoam ( more at crest of waves ). + out vec4 rippleTexCoord2 ; // Screenspace vert position BEFORE wave transformation -varying vec4 posPreWave; +out vec4 posPreWave; // Screenspace vert position AFTER wave transformation -varying vec4 posPostWave; +out vec4 posPostWave; -// Worldspace unit distance/depth of this vertex/pixel -varying float pixelDist; + // Objectspace vert position BEFORE wave transformation + // w coord is world space z position. + out vec4 objPos ; -varying vec3 fogPos; + out vec4 foamTexCoords ; -varying float worldSpaceZ; + out mat3 tangentMat ; +// -varying vec4 foamTexCoords; +#define OUT_hpos hpos +#define OUT_rippleTexCoord01 rippleTexCoord01 +#define OUT_rippleTexCoord2 rippleTexCoord2 +#define OUT_posPreWave posPreWave +#define OUT_posPostWave posPostWave +#define OUT_objPos objPos +#define OUT_foamTexCoords foamTexCoords +#define OUT_tangentMat tangentMat //----------------------------------------------------------------------------- // Uniforms //----------------------------------------------------------------------------- uniform mat4 modelMat; uniform mat4 modelview; -uniform mat3 cubeTrans; -uniform mat4 objTrans; -uniform vec3 cubeEyePos; +uniform vec4 rippleMat[3]; uniform vec3 eyePos; uniform vec2 waveDir[3]; uniform vec2 waveData[3]; uniform vec2 rippleDir[3]; uniform vec2 rippleTexScale[3]; uniform vec3 rippleSpeed; -uniform vec2 reflectTexSize; +uniform vec4 foamDir; +uniform vec4 foamTexScale; +uniform vec2 foamSpeed; uniform vec3 inLightVec; -uniform vec3 reflectNormal; uniform float gridElementSize; uniform float elapsedTime; uniform float undulateMaxDist; @@ -81,97 +109,133 @@ uniform float undulateMaxDist; //----------------------------------------------------------------------------- void main() { - // Copy incoming attributes into locals so we can modify them in place. - vec4 position = gl_Vertex.xyzw; - vec3 normal = gl_Normal.xyz; - vec2 undulateData = gl_MultiTexCoord0.st; - vec4 horizonFactor = gl_MultiTexCoord1.xyzw; + vec4 IN_position = IN_position_; // use projection matrix for reflection / refraction texture coords - mat4 texGen = { 0.5, 0.0, 0.0, 0.5, //+ 0.5 / reflectTexSize.x, - 0.0, 0.5, 0.0, 0.5, //+ 1.0 / reflectTexSize.y, - 0.0, 0.0, 1.0, 0.0, - 0.0, 0.0, 0.0, 1.0 }; + mat4 texGen = mat4FromRow( 0.5, 0.0, 0.0, 0.5, + 0.0, -0.5, 0.0, 0.5, + 0.0, 0.0, 1.0, 0.0, + 0.0, 0.0, 0.0, 1.0 ); - // Move the vertex based on the horizonFactor if specified to do so for this vert. - if ( horizonFactor.z > 0 ) - { - vec2 offsetXY = eyePos.xy - eyePos.xy % gridElementSize; - position.xy += offsetXY; - undulateData += offsetXY; - } + IN_position.z = mix( IN_position.z, eyePos.z, IN_horizonFactor.x ); - fogPos = position; - position.z = mix( position.z, eyePos.z, horizonFactor.x ); + OUT_objPos = IN_position; + OUT_objPos.w = tMul( modelMat, IN_position ).z; // Send pre-undulation screenspace position - posPreWave = modelview * position; - posPreWave = texGen * posPreWave; + OUT_posPreWave = tMul( modelview, IN_position ); + OUT_posPreWave = tMul( texGen, OUT_posPreWave ); // Calculate the undulation amount for this vertex. - vec2 undulatePos = undulateData; - float undulateAmt = 0; + vec2 undulatePos = tMul( modelMat, vec4 ( IN_undulateData.xy, 0, 1 ) ).xy; + float undulateAmt = 0.0; - for ( int i = 0; i < 3; i++ ) - { - undulateAmt += WAVE_MAGNITUDE(i) * sin( elapsedTime * WAVE_SPEED(i) + - undulatePos.x * waveDir[i].x + - undulatePos.y * waveDir[i].y ); - } + undulateAmt += waveData[0].y * sin( elapsedTime * waveData[0].x + + undulatePos.x * waveDir[0].x + + undulatePos.y * waveDir[0].y ); + undulateAmt += waveData[1].y * sin( elapsedTime * waveData[1].x + + undulatePos.x * waveDir[1].x + + undulatePos.y * waveDir[1].y ); + undulateAmt += waveData[2].y * sin( elapsedTime * waveData[2].x + + undulatePos.x * waveDir[2].x + + undulatePos.y * waveDir[2].y ); + + float undulateFade = 1; // Scale down wave magnitude amount based on distance from the camera. - float dist = distance( position, eyePos ); + float dist = distance( IN_position.xyz, eyePos ); dist = clamp( dist, 1.0, undulateMaxDist ); - undulateAmt *= ( 1 - dist / undulateMaxDist ); + undulateFade *= ( 1 - dist / undulateMaxDist ); // Also scale down wave magnitude if the camera is very very close. - undulateAmt *= clamp( ( distance( IN.position, eyePos ) - 0.5 ) / 10.0, 0.0, 1.0 ); + undulateFade *= saturate( ( distance( IN_position.xyz, eyePos ) - 0.5 ) / 10.0 ); + + undulateAmt *= undulateFade; + + OUT_rippleTexCoord2.w = undulateAmt / ( waveData[0].y + waveData[1].y + waveData[2].y ); + OUT_rippleTexCoord2.w = saturate( OUT_rippleTexCoord2.w - 0.2 ) / 0.8; // Apply wave undulation to the vertex. - posPostWave = position; - posPostWave.xyz += normal.xyz * undulateAmt; - - // Save worldSpace position of this pixel/vert - worldPos = posPostWave.xyz; + OUT_posPostWave = IN_position; + OUT_posPostWave.xyz += IN_normal.xyz * undulateAmt; // Convert to screen - posPostWave = modelview * posPostWave; + OUT_posPostWave = tMul( modelview, OUT_posPostWave ); // Setup the OUT position symantic variable - gl_Position = posPostWave; - gl_Position.z = mix(gl_Position.z, gl_Position.w, horizonFactor.x); + OUT_hpos = OUT_posPostWave; + //OUT_hpos.z = mix( OUT_hpos.z, OUT_hpos.w, IN_horizonFactor.x ); - worldSpaceZ = modelMat * vec4(fogPos, 1.0) ).z; - if ( horizonFactor.x > 0.0 ) - { - vec3 awayVec = normalize( fogPos.xyz - eyePos ); - fogPos.xy += awayVec.xy * 1000.0; - } + // if ( IN_horizonFactor.x > 0 ) + // { + // vec3 awayVec = normalize( OUT_objPos.xyz - eyePos ); + // OUT_objPos.xy += awayVec.xy * 1000.0; + // } // Save world space camera dist/depth of the outgoing pixel - pixelDist = gl_Position.z; + OUT_rippleTexCoord2.z = OUT_hpos.z; // Convert to reflection texture space - posPostWave = texGen * posPostWave; + OUT_posPostWave = tMul( texGen, OUT_posPostWave ); - float2 ripplePos = undulateData; - float2 txPos = normalize( ripplePos ); - txPos *= 50000.0; - ripplePos = mix( ripplePos, txPos, IN.horizonFactor.x ); + vec2 txPos = undulatePos; + if ( bool(IN_horizonFactor.x) ) + txPos = normalize( txPos ) * 50000.0; // set up tex coordinates for the 3 interacting normal maps - rippleTexCoord01.xy = mix( ripplePos * rippleTexScale[0], txPos.xy * rippleTexScale[0], IN.horizonFactor.x ); - rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; + OUT_rippleTexCoord01.xy = txPos * rippleTexScale[0]; + OUT_rippleTexCoord01.xy += rippleDir[0] * elapsedTime * rippleSpeed.x; - rippleTexCoord01.zw = mix( ripplePos * rippleTexScale[1], txPos.xy * rippleTexScale[1], IN.horizonFactor.x ); - rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; + mat2 texMat; + texMat[0][0] = rippleMat[0].x; + texMat[1][0] = rippleMat[0].y; + texMat[0][1] = rippleMat[0].z; + texMat[1][1] = rippleMat[0].w; + OUT_rippleTexCoord01.xy = tMul( texMat, OUT_rippleTexCoord01.xy ); - rippleTexCoord2.xy = mix( ripplePos * rippleTexScale[2], txPos.xy * rippleTexScale[2], IN.horizonFactor.x ); - rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; + OUT_rippleTexCoord01.zw = txPos * rippleTexScale[1]; + OUT_rippleTexCoord01.zw += rippleDir[1] * elapsedTime * rippleSpeed.y; + + texMat[0][0] = rippleMat[1].x; + texMat[1][0] = rippleMat[1].y; + texMat[0][1] = rippleMat[1].z; + texMat[1][1] = rippleMat[1].w; + OUT_rippleTexCoord01.zw = tMul( texMat, OUT_rippleTexCoord01.zw ); - foamTexCoords.xy = mix( ripplePos * 0.2, txPos.xy * rippleTexScale[0], IN.horizonFactor.x ); - foamTexCoords.xy += rippleDir[0] * sin( ( elapsedTime + 500.0 ) * -0.4 ) * 0.15; + OUT_rippleTexCoord2.xy = txPos * rippleTexScale[2]; + OUT_rippleTexCoord2.xy += rippleDir[2] * elapsedTime * rippleSpeed.z; - foamTexCoords.zw = mix( ripplePos * 0.3, txPos.xy * rippleTexScale[1], IN.horizonFactor.x ); - foamTexCoords.zw += rippleDir[1] * sin( elapsedTime * 0.4 ) * 0.15; + texMat[0][0] = rippleMat[2].x; + texMat[1][0] = rippleMat[2].y; + texMat[0][1] = rippleMat[2].z; + texMat[1][1] = rippleMat[2].w; + OUT_rippleTexCoord2.xy = tMul( texMat, OUT_rippleTexCoord2.xy ); + + OUT_foamTexCoords.xy = txPos * foamTexScale.xy + foamDir.xy * foamSpeed.x * elapsedTime; + OUT_foamTexCoords.zw = txPos * foamTexScale.zw + foamDir.zw * foamSpeed.y * elapsedTime; + + + vec3 binormal = vec3 ( 1, 0, 0 ); + vec3 tangent = vec3 ( 0, 1, 0 ); + vec3 normal; + for ( int i = 0; i < 3; i++ ) + { + binormal.z += undulateFade * waveDir[i].x * waveData[i].y * cos( waveDir[i].x * undulatePos.x + waveDir[i].y * undulatePos.y + elapsedTime * waveData[i].x ); + tangent.z += undulateFade * waveDir[i].y * waveData[i].y * cos( waveDir[i].x * undulatePos.x + waveDir[i].y * undulatePos.y + elapsedTime * waveData[i].x ); + } + + binormal = binormal; + tangent = tangent; + normal = cross( binormal, tangent ); + + mat3 worldToTangent; + worldToTangent[0] = binormal; + worldToTangent[1] = tangent; + worldToTangent[2] = normal; + + OUT_tangentMat = transpose(worldToTangent); + + gl_Position = OUT_hpos; + correctSSP(gl_Position); } + From f12d8807a849515e0576b68210afd51a5d56d24e Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 13 Apr 2014 19:47:28 +0200 Subject: [PATCH 013/317] Add sampler names to ShaderData declarations necesary for OpenGL. ShaderData changes: * OGLVertexShaderFile/OGLPixelShaderFile not used in DX9. * samplerNames[?] not used in DX9. --- .../Empty/game/core/scripts/client/clouds.cs | 8 ++- .../client/lighting/advanced/lightViz.cs | 14 ++--- .../client/lighting/advanced/shaders.cs | 21 +++++++- .../client/lighting/advanced/shadowViz.cs | 3 ++ .../scripts/client/lighting/basic/init.cs | 2 + .../client/lighting/basic/shadowFilter.cs | 4 +- .../Empty/game/core/scripts/client/postFx.cs | 4 +- .../core/scripts/client/postFx/GammaPostFX.cs | 6 +++ .../game/core/scripts/client/postFx/MLAA.cs | 10 ++++ .../scripts/client/postFx/MotionBlurFx.cs | 6 +++ .../core/scripts/client/postFx/caustics.cs | 8 ++- .../scripts/client/postFx/chromaticLens.cs | 6 +++ .../game/core/scripts/client/postFx/dof.cs | 35 +++++++++++++ .../game/core/scripts/client/postFx/edgeAA.cs | 19 +++---- .../game/core/scripts/client/postFx/flash.cs | 5 ++ .../game/core/scripts/client/postFx/fog.cs | 10 ++-- .../game/core/scripts/client/postFx/fxaa.cs | 3 ++ .../game/core/scripts/client/postFx/glow.cs | 4 +- .../game/core/scripts/client/postFx/hdr.cs | 52 ++++++++++++++++++- .../core/scripts/client/postFx/lightRay.cs | 12 +++++ .../client/postFx/ovrBarrelDistortion.cs | 10 ++++ .../game/core/scripts/client/postFx/ssao.cs | 19 +++++++ .../core/scripts/client/postFx/turbulence.cs | 4 ++ .../game/core/scripts/client/scatterSky.cs | 2 + .../Empty/game/core/scripts/client/shaders.cs | 15 ++++-- .../game/core/scripts/client/terrainBlock.cs | 3 ++ .../Empty/game/core/scripts/client/water.cs | 48 ++++++++++------- .../Full/game/core/scripts/client/clouds.cs | 8 ++- .../client/lighting/advanced/lightViz.cs | 14 ++--- .../client/lighting/advanced/shaders.cs | 21 +++++++- .../client/lighting/advanced/shadowViz.cs | 3 ++ .../scripts/client/lighting/basic/init.cs | 2 + .../client/lighting/basic/shadowFilter.cs | 4 +- .../Full/game/core/scripts/client/postFx.cs | 4 +- .../core/scripts/client/postFx/GammaPostFX.cs | 6 +++ .../game/core/scripts/client/postFx/MLAA.cs | 10 ++++ .../scripts/client/postFx/MotionBlurFx.cs | 6 +++ .../core/scripts/client/postFx/caustics.cs | 8 ++- .../scripts/client/postFx/chromaticLens.cs | 6 +++ .../game/core/scripts/client/postFx/dof.cs | 43 +++++++++++++-- .../game/core/scripts/client/postFx/edgeAA.cs | 19 +++---- .../game/core/scripts/client/postFx/flash.cs | 5 ++ .../game/core/scripts/client/postFx/fog.cs | 10 ++-- .../game/core/scripts/client/postFx/fxaa.cs | 3 ++ .../game/core/scripts/client/postFx/glow.cs | 4 +- .../game/core/scripts/client/postFx/hdr.cs | 52 ++++++++++++++++++- .../core/scripts/client/postFx/lightRay.cs | 12 +++++ .../client/postFx/ovrBarrelDistortion.cs | 10 ++++ .../game/core/scripts/client/postFx/ssao.cs | 19 +++++++ .../core/scripts/client/postFx/turbulence.cs | 4 ++ .../game/core/scripts/client/scatterSky.cs | 4 +- .../Full/game/core/scripts/client/shaders.cs | 15 ++++-- .../game/core/scripts/client/terrainBlock.cs | 3 ++ .../Full/game/core/scripts/client/water.cs | 44 +++++++++------- 54 files changed, 555 insertions(+), 117 deletions(-) diff --git a/Templates/Empty/game/core/scripts/client/clouds.cs b/Templates/Empty/game/core/scripts/client/clouds.cs index 2763ce182..87284890a 100644 --- a/Templates/Empty/game/core/scripts/client/clouds.cs +++ b/Templates/Empty/game/core/scripts/client/clouds.cs @@ -32,6 +32,8 @@ singleton ShaderData( CloudLayerShader ) OGLVertexShaderFile = "shaders/common/gl/cloudLayerV.glsl"; OGLPixelShaderFile = "shaders/common/gl/cloudLayerP.glsl"; + samplerNames[0] = "$normalHeightMap"; + pixVersion = 2.0; }; @@ -44,8 +46,10 @@ singleton ShaderData( BasicCloudsShader ) DXVertexShaderFile = "shaders/common/basicCloudsV.hlsl"; DXPixelShaderFile = "shaders/common/basicCloudsP.hlsl"; - //OGLVertexShaderFile = "shaders/common/gl/basicCloudsV.glsl"; - //OGLPixelShaderFile = "shaders/common/gl/basicCloudsP.glsl"; + OGLVertexShaderFile = "shaders/common/gl/basicCloudsV.glsl"; + OGLPixelShaderFile = "shaders/common/gl/basicCloudsP.glsl"; + + samplerNames[0] = "$diffuseMap"; pixVersion = 2.0; }; diff --git a/Templates/Empty/game/core/scripts/client/lighting/advanced/lightViz.cs b/Templates/Empty/game/core/scripts/client/lighting/advanced/lightViz.cs index 9cb797bb9..8c3ecc03c 100644 --- a/Templates/Empty/game/core/scripts/client/lighting/advanced/lightViz.cs +++ b/Templates/Empty/game/core/scripts/client/lighting/advanced/lightViz.cs @@ -53,7 +53,7 @@ new ShaderData( AL_DepthVisualizeShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/lighting/advanced/dbgDepthVisualizeP.hlsl"; - OGLVertexShaderFile = "shaders/common/postFx/postFxV.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/dbgDepthVisualizeP.glsl"; samplerNames[0] = "prepassBuffer"; @@ -90,10 +90,10 @@ new ShaderData( AL_NormalsVisualizeShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/lighting/advanced/dbgNormalVisualizeP.hlsl"; - OGLVertexShaderFile = "shaders/common/postFx/postFxV.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/dbgNormalVisualizeP.glsl"; - samplerNames[0] = "prepassTex"; + samplerNames[0] = "prepassBuffer"; pixVersion = 2.0; }; @@ -126,8 +126,8 @@ new ShaderData( AL_LightColorVisualizeShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/lighting/advanced/dbgLightColorVisualizeP.hlsl"; - OGLVertexShaderFile = "shaders/common/postFx/postFxV.glsl"; - OGLPixelShaderFile = "shaders/common/lighting/advanced/dl/dbgLightColorVisualizeP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl"; samplerNames[0] = "lightInfoBuffer"; @@ -161,8 +161,8 @@ new ShaderData( AL_LightSpecularVisualizeShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/lighting/advanced/dbgLightSpecularVisualizeP.hlsl"; - OGLVertexShaderFile = "shaders/common/postFx/postFxV.glsl"; - OGLPixelShaderFile = "shaders/common/lighting/advanced/dl/dbgLightSpecularVisualizeP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl"; samplerNames[0] = "lightInfoBuffer"; diff --git a/Templates/Empty/game/core/scripts/client/lighting/advanced/shaders.cs b/Templates/Empty/game/core/scripts/client/lighting/advanced/shaders.cs index fef6c5652..7fda56235 100644 --- a/Templates/Empty/game/core/scripts/client/lighting/advanced/shaders.cs +++ b/Templates/Empty/game/core/scripts/client/lighting/advanced/shaders.cs @@ -61,6 +61,11 @@ new ShaderData( AL_VectorLightShader ) OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/farFrustumQuadV.glsl"; OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/vectorLightP.glsl"; + samplerNames[0] = "$prePassBuffer"; + samplerNames[1] = "$ShadowMap"; + samplerNames[2] = "$ssaoMask"; + samplerNames[3] = "$gTapRotationTex"; + pixVersion = 3.0; }; @@ -121,6 +126,11 @@ new ShaderData( AL_PointLightShader ) OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/convexGeometryV.glsl"; OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/pointLightP.glsl"; + samplerNames[0] = "$prePassBuffer"; + samplerNames[1] = "$shadowMap"; + samplerNames[2] = "$cookieMap"; + samplerNames[3] = "$gTapRotationTex"; + pixVersion = 3.0; }; @@ -131,7 +141,7 @@ new CustomMaterial( AL_PointLightMaterial ) sampler["prePassBuffer"] = "#prepass"; sampler["shadowMap"] = "$dynamiclight"; - sampler["cookieTex"] = "$dynamiclightmask"; + sampler["cookieMap"] = "$dynamiclightmask"; target = "lightinfo"; @@ -147,6 +157,11 @@ new ShaderData( AL_SpotLightShader ) OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/convexGeometryV.glsl"; OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/spotLightP.glsl"; + samplerNames[0] = "$prePassBuffer"; + samplerNames[1] = "$shadowMap"; + samplerNames[2] = "$cookieMap"; + samplerNames[3] = "$gTapRotationTex"; + pixVersion = 3.0; }; @@ -157,7 +172,7 @@ new CustomMaterial( AL_SpotLightMaterial ) sampler["prePassBuffer"] = "#prepass"; sampler["shadowMap"] = "$dynamiclight"; - sampler["cookieTex"] = "$dynamiclightmask"; + sampler["cookieMap"] = "$dynamiclightmask"; target = "lightinfo"; @@ -210,6 +225,8 @@ new ShaderData( AL_ParticlePointLightShader ) OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/convexGeometryV.glsl"; OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/pointLightP.glsl"; + + samplerNames[0] = "$prePassBuffer"; pixVersion = 3.0; }; diff --git a/Templates/Empty/game/core/scripts/client/lighting/advanced/shadowViz.cs b/Templates/Empty/game/core/scripts/client/lighting/advanced/shadowViz.cs index 88b2d7f4a..c8db6456a 100644 --- a/Templates/Empty/game/core/scripts/client/lighting/advanced/shadowViz.cs +++ b/Templates/Empty/game/core/scripts/client/lighting/advanced/shadowViz.cs @@ -28,6 +28,9 @@ new ShaderData( AL_ShadowVisualizeShader ) OGLVertexShaderFile = "shaders/common/gl/guiMaterialV.glsl"; OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/dbgShadowVisualizeP.glsl"; + samplerNames[0] = "$shadowMap"; + samplerNames[1] = "$depthViz"; + pixVersion = 2.0; }; diff --git a/Templates/Empty/game/core/scripts/client/lighting/basic/init.cs b/Templates/Empty/game/core/scripts/client/lighting/basic/init.cs index fd77adb8b..ed2972c11 100644 --- a/Templates/Empty/game/core/scripts/client/lighting/basic/init.cs +++ b/Templates/Empty/game/core/scripts/client/lighting/basic/init.cs @@ -46,6 +46,8 @@ singleton ShaderData( BL_ProjectedShadowShaderData ) OGLVertexShaderFile = "shaders/common/gl/projectedShadowV.glsl"; OGLPixelShaderFile = "shaders/common/gl/projectedShadowP.glsl"; + samplerNames[0] = "inputTex"; + pixVersion = 2.0; }; diff --git a/Templates/Empty/game/core/scripts/client/lighting/basic/shadowFilter.cs b/Templates/Empty/game/core/scripts/client/lighting/basic/shadowFilter.cs index 30a6db6d6..82484f515 100644 --- a/Templates/Empty/game/core/scripts/client/lighting/basic/shadowFilter.cs +++ b/Templates/Empty/game/core/scripts/client/lighting/basic/shadowFilter.cs @@ -26,8 +26,8 @@ singleton ShaderData( BL_ShadowFilterShaderV ) DXVertexShaderFile = "shaders/common/lighting/basic/shadowFilterV.hlsl"; DXPixelShaderFile = "shaders/common/lighting/basic/shadowFilterP.hlsl"; - OGLVertexShaderFile = "shaders/common/lighting/basic/gl/shadowFilterV.glsl"; - OGLPixelShaderFile = "shaders/common/lighting/basic/gl/shadowFilterP.glsl"; + OGLVertexShaderFile = "shaders/common/lighting/basic/gl/shadowFilterV.glsl"; + OGLPixelShaderFile = "shaders/common/lighting/basic/gl/shadowFilterP.glsl"; samplerNames[0] = "$diffuseMap"; diff --git a/Templates/Empty/game/core/scripts/client/postFx.cs b/Templates/Empty/game/core/scripts/client/postFx.cs index bbb0794bb..4e6451544 100644 --- a/Templates/Empty/game/core/scripts/client/postFx.cs +++ b/Templates/Empty/game/core/scripts/client/postFx.cs @@ -36,8 +36,8 @@ singleton ShaderData( PFX_PassthruShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/passthruP.hlsl"; -// OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; -// OGLPixelShaderFile = "shaders/common/postFx/gl/passthruP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/passthruP.glsl"; samplerNames[0] = "$inputTex"; diff --git a/Templates/Empty/game/core/scripts/client/postFx/GammaPostFX.cs b/Templates/Empty/game/core/scripts/client/postFx/GammaPostFX.cs index 61d4603c8..383a0c8cd 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/GammaPostFX.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/GammaPostFX.cs @@ -25,6 +25,12 @@ singleton ShaderData( GammaShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/gammaP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/gammaP.glsl"; + + samplerNames[0] = "$backBuffer"; + samplerNames[1] = "$colorCorrectionTex"; + pixVersion = 2.0; }; diff --git a/Templates/Empty/game/core/scripts/client/postFx/MLAA.cs b/Templates/Empty/game/core/scripts/client/postFx/MLAA.cs index 47156970b..bef075ec4 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/MLAA.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/MLAA.cs @@ -47,7 +47,11 @@ singleton ShaderData( MLAA_EdgeDetectionShader ) DXVertexShaderFile = "shaders/common/postFx/mlaa/offsetV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/mlaa/edgeDetectionP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/mlaa/gl/offsetV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/mlaa/gl/edgeDetectionP.glsl"; + samplerNames[0] = "$colorMapG"; + samplerNames[1] = "$prepassMap"; pixVersion = 3.0; }; @@ -72,6 +76,9 @@ singleton ShaderData( MLAA_BlendWeightCalculationShader ) DXVertexShaderFile = "shaders/common/postFx/mlaa/passthruV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/mlaa/blendWeightCalculationP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/mlaa/gl/passthruV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/mlaa/gl/blendWeightCalculationP.glsl"; + samplerNames[0] = "$edgesMap"; samplerNames[1] = "$edgesMapL"; samplerNames[2] = "$areaMap"; @@ -98,6 +105,9 @@ singleton ShaderData( MLAA_NeighborhoodBlendingShader ) { DXVertexShaderFile = "shaders/common/postFx/mlaa/offsetV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/mlaa/neighborhoodBlendingP.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/mlaa/gl/offsetV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/mlaa/gl/neighborhoodBlendingP.glsl"; samplerNames[0] = "$blendMap"; samplerNames[1] = "$colorMapL"; diff --git a/Templates/Empty/game/core/scripts/client/postFx/MotionBlurFx.cs b/Templates/Empty/game/core/scripts/client/postFx/MotionBlurFx.cs index e94e5ef67..fea0c3bb3 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/MotionBlurFx.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/MotionBlurFx.cs @@ -25,6 +25,12 @@ singleton ShaderData( PFX_MotionBlurShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; //we use the bare-bones postFxV.hlsl DXPixelShaderFile = "shaders/common/postFx/motionBlurP.hlsl"; //new pixel shader + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/motionBlurP.glsl"; + + samplerNames[0] = "$backBuffer"; + samplerNames[1] = "$prepassTex"; + pixVersion = 3.0; }; diff --git a/Templates/Empty/game/core/scripts/client/postFx/caustics.cs b/Templates/Empty/game/core/scripts/client/postFx/caustics.cs index c6a694c51..3e8b14de0 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/caustics.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/caustics.cs @@ -38,9 +38,13 @@ singleton ShaderData( PFX_CausticsShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/caustics/causticsP.hlsl"; - //OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; - //OGLPixelShaderFile = "shaders/common/postFx/gl/passthruP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/caustics/gl/causticsP.glsl"; + samplerNames[0] = "$prepassTex"; + samplerNames[1] = "$causticsTex0"; + samplerNames[2] = "$causticsTex1"; + pixVersion = 3.0; }; diff --git a/Templates/Empty/game/core/scripts/client/postFx/chromaticLens.cs b/Templates/Empty/game/core/scripts/client/postFx/chromaticLens.cs index cf24c5702..705986e7e 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/chromaticLens.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/chromaticLens.cs @@ -47,6 +47,12 @@ singleton ShaderData( PFX_ChromaticLensShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/chromaticLens.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/chromaticLens.glsl"; + + samplerNames[0] = "$backBuffer"; + pixVersion = 3.0; }; diff --git a/Templates/Empty/game/core/scripts/client/postFx/dof.cs b/Templates/Empty/game/core/scripts/client/postFx/dof.cs index 1b20e44c5..1ba1a476b 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/dof.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/dof.cs @@ -320,6 +320,13 @@ singleton ShaderData( PFX_DOFDownSampleShader ) { DXVertexShaderFile = "shaders/common/postFx/dof/DOF_DownSample_V.hlsl"; DXPixelShaderFile = "shaders/common/postFx/dof/DOF_DownSample_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/dof/gl/DOF_DownSample_V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/dof/gl/DOF_DownSample_P.glsl"; + + samplerNames[0] = "$colorSampler"; + samplerNames[1] = "$depthSampler"; + pixVersion = 3.0; }; @@ -327,6 +334,12 @@ singleton ShaderData( PFX_DOFBlurYShader ) { DXVertexShaderFile = "shaders/common/postFx/dof/DOF_Gausian_V.hlsl"; DXPixelShaderFile = "shaders/common/postFx/dof/DOF_Gausian_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/dof/gl/DOF_Gausian_V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/dof/gl/DOF_Gausian_P.glsl"; + + samplerNames[0] = "$diffuseMap"; + pixVersion = 2.0; defines = "BLUR_DIR=float2(0.0,1.0)"; }; @@ -340,6 +353,13 @@ singleton ShaderData( PFX_DOFCalcCoCShader ) { DXVertexShaderFile = "shaders/common/postFx/dof/DOF_CalcCoC_V.hlsl"; DXPixelShaderFile = "shaders/common/postFx/dof/DOF_CalcCoC_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/dof/gl/DOF_CalcCoC_V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/dof/gl/DOF_CalcCoC_P.glsl"; + + samplerNames[0] = "$shrunkSampler"; + samplerNames[1] = "$blurredSampler"; + pixVersion = 3.0; }; @@ -347,6 +367,12 @@ singleton ShaderData( PFX_DOFSmallBlurShader ) { DXVertexShaderFile = "shaders/common/postFx/dof/DOF_SmallBlur_V.hlsl"; DXPixelShaderFile = "shaders/common/postFx/dof/DOF_SmallBlur_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/dof/gl/DOF_SmallBlur_V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/dof/gl/DOF_SmallBlur_P.glsl"; + + samplerNames[0] = "$colorSampler"; + pixVersion = 3.0; }; @@ -354,6 +380,15 @@ singleton ShaderData( PFX_DOFFinalShader ) { DXVertexShaderFile = "shaders/common/postFx/dof/DOF_Final_V.hlsl"; DXPixelShaderFile = "shaders/common/postFx/dof/DOF_Final_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/dof/gl/DOF_Final_V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/dof/gl/DOF_Final_P.glsl"; + + samplerNames[0] = "$colorSampler"; + samplerNames[1] = "$smallBlurSampler"; + samplerNames[2] = "$largeBlurSampler"; + samplerNames[3] = "$depthSampler"; + pixVersion = 3.0; }; diff --git a/Templates/Empty/game/core/scripts/client/postFx/edgeAA.cs b/Templates/Empty/game/core/scripts/client/postFx/edgeAA.cs index 2735df855..dcbd42ba1 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/edgeAA.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/edgeAA.cs @@ -37,10 +37,10 @@ singleton ShaderData( PFX_EdgeAADetectShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/edgeaa/edgeDetectP.hlsl"; - //OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; - //OGLPixelShaderFile = "shaders/common/postFx/gl/passthruP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/edgeaa/gl/edgeDetectP.glsl"; - samplerNames[0] = "$inputTex"; + samplerNames[0] = "$prepassBuffer"; pixVersion = 3.0; }; @@ -50,10 +50,11 @@ singleton ShaderData( PFX_EdgeAAShader ) DXVertexShaderFile = "shaders/common/postFx/edgeaa/edgeAAV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/edgeaa/edgeAAP.hlsl"; - //OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; - //OGLPixelShaderFile = "shaders/common/postFx/gl/passthruP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/edgeaa/gl/edgeAAV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/edgeaa/gl/edgeAAP.glsl"; - samplerNames[0] = "$inputTex"; + samplerNames[0] = "$edgeBuffer"; + samplerNames[1] = "$backBuffer"; pixVersion = 3.0; }; @@ -63,10 +64,10 @@ singleton ShaderData( PFX_EdgeAADebugShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/edgeaa/dbgEdgeDisplayP.hlsl"; - //OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; - //OGLPixelShaderFile = "shaders/common/postFx/gl/passthruP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/edgeaa/gl/dbgEdgeDisplayP.glsl"; - samplerNames[0] = "$inputTex"; + samplerNames[0] = "$edgeBuffer"; pixVersion = 3.0; }; diff --git a/Templates/Empty/game/core/scripts/client/postFx/flash.cs b/Templates/Empty/game/core/scripts/client/postFx/flash.cs index 105a0de34..244d91791 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/flash.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/flash.cs @@ -24,6 +24,11 @@ singleton ShaderData( PFX_FlashShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/flashP.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/flashP.glsl"; + + samplerNames[0] = "$backBuffer"; defines = "WHITE_COLOR=float4(1.0,1.0,1.0,0.0);MUL_COLOR=float4(1.0,0.25,0.25,0.0)"; diff --git a/Templates/Empty/game/core/scripts/client/postFx/fog.cs b/Templates/Empty/game/core/scripts/client/postFx/fog.cs index 5257db595..78b2a8924 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/fog.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/fog.cs @@ -29,8 +29,8 @@ singleton ShaderData( FogPassShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/fogP.hlsl"; -// OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; -// OGLPixelShaderFile = "shaders/common/postFx/gl/fogP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/fogP.glsl"; samplerNames[0] = "$prepassTex"; @@ -75,10 +75,12 @@ singleton ShaderData( UnderwaterFogPassShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/underwaterFogP.hlsl"; -// OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; -// OGLPixelShaderFile = "shaders/common/postFx/gl/fogP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/underwaterFogP.glsl"; samplerNames[0] = "$prepassTex"; + samplerNames[1] = "$backbuffer"; + samplerNames[2] = "$waterDepthGradMap"; pixVersion = 2.0; }; diff --git a/Templates/Empty/game/core/scripts/client/postFx/fxaa.cs b/Templates/Empty/game/core/scripts/client/postFx/fxaa.cs index 7d1ac88c2..d13b9a61e 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/fxaa.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/fxaa.cs @@ -39,6 +39,9 @@ singleton ShaderData( FXAA_ShaderData ) DXVertexShaderFile = "shaders/common/postFx/fxaa/fxaaV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/fxaa/fxaaP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/fxaa/gl/fxaaV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/fxaa/gl/fxaaP.glsl"; + samplerNames[0] = "$colorTex"; pixVersion = 3.0; diff --git a/Templates/Empty/game/core/scripts/client/postFx/glow.cs b/Templates/Empty/game/core/scripts/client/postFx/glow.cs index d8bb086f0..3cc946b04 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/glow.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/glow.cs @@ -26,8 +26,8 @@ singleton ShaderData( PFX_GlowBlurVertShader ) DXVertexShaderFile = "shaders/common/postFx/glowBlurV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/glowBlurP.hlsl"; -// OGLVertexShaderFile = "shaders/common/postFx/glowBlurV.glsl"; -// OGLPixelShaderFile = "shaders/common/postFx/glowBlurP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/glowBlurV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/glowBlurP.glsl"; defines = "BLUR_DIR=float2(0.0,1.0)"; diff --git a/Templates/Empty/game/core/scripts/client/postFx/hdr.cs b/Templates/Empty/game/core/scripts/client/postFx/hdr.cs index 92b3989e8..a5c450799 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/hdr.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/hdr.cs @@ -77,7 +77,13 @@ $HDRPostFX::colorCorrectionRamp = "core/scripts/client/postFx/null_color_ramp.pn singleton ShaderData( HDR_BrightPassShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; - DXPixelShaderFile = "shaders/common/postFx/hdr/brightPassFilterP.hlsl"; + DXPixelShaderFile = "shaders/common/postFx/hdr/brightPassFilterP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/brightPassFilterP.glsl"; + + samplerNames[0] = "$inputTex"; + samplerNames[1] = "$luminanceTex"; + pixVersion = 3.0; }; @@ -85,6 +91,11 @@ singleton ShaderData( HDR_DownScale4x4Shader ) { DXVertexShaderFile = "shaders/common/postFx/hdr/downScale4x4V.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/downScale4x4P.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/hdr/gl/downScale4x4V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/downScale4x4P.glsl"; + + samplerNames[0] = "$inputTex"; + pixVersion = 2.0; }; @@ -92,6 +103,11 @@ singleton ShaderData( HDR_BloomGaussBlurHShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/bloomGaussBlurHP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/bloomGaussBlurHP.glsl"; + + samplerNames[0] = "$inputTex"; + pixVersion = 3.0; }; @@ -99,6 +115,11 @@ singleton ShaderData( HDR_BloomGaussBlurVShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/bloomGaussBlurVP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/bloomGaussBlurVP.glsl"; + + samplerNames[0] = "$inputTex"; + pixVersion = 3.0; }; @@ -106,6 +127,11 @@ singleton ShaderData( HDR_SampleLumShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/sampleLumInitialP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/sampleLumInitialP.glsl"; + + samplerNames[0] = "$inputTex"; + pixVersion = 3.0; }; @@ -113,6 +139,11 @@ singleton ShaderData( HDR_DownSampleLumShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/sampleLumIterativeP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/sampleLumIterativeP.glsl"; + + samplerNames[0] = "$inputTex"; + pixVersion = 3.0; }; @@ -120,6 +151,12 @@ singleton ShaderData( HDR_CalcAdaptedLumShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/calculateAdaptedLumP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/calculateAdaptedLumP.glsl"; + + samplerNames[0] = "$currLum"; + samplerNames[1] = "$lastAdaptedLum"; + pixVersion = 3.0; }; @@ -127,6 +164,14 @@ singleton ShaderData( HDR_CombineShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/finalPassCombineP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/finalPassCombineP.glsl"; + + samplerNames[0] = "$sceneTex"; + samplerNames[1] = "$luminanceTex"; + samplerNames[2] = "$bloomTex"; + samplerNames[3] = "$colorCorrectionTex"; + pixVersion = 3.0; }; @@ -421,6 +466,11 @@ singleton ShaderData( LuminanceVisShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/luminanceVisP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/luminanceVisP.glsl"; + + samplerNames[0] = "$inputTex"; + pixVersion = 3.0; }; diff --git a/Templates/Empty/game/core/scripts/client/postFx/lightRay.cs b/Templates/Empty/game/core/scripts/client/postFx/lightRay.cs index 5a6d7aa28..c13f1ca3b 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/lightRay.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/lightRay.cs @@ -34,6 +34,12 @@ singleton ShaderData( LightRayOccludeShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/lightRay/lightRayOccludeP.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/lightRay/gl/lightRayOccludeP.glsl"; + + samplerNames[0] = "$backBuffer"; + samplerNames[1] = "$prepassTex"; pixVersion = 3.0; }; @@ -42,6 +48,12 @@ singleton ShaderData( LightRayShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/lightRay/lightRayP.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/lightRay/gl/lightRayP.glsl"; + + samplerNames[0] = "$frameSampler"; + samplerNames[1] = "$backBuffer"; pixVersion = 3.0; }; diff --git a/Templates/Empty/game/core/scripts/client/postFx/ovrBarrelDistortion.cs b/Templates/Empty/game/core/scripts/client/postFx/ovrBarrelDistortion.cs index cbd72ec84..1838aa621 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/ovrBarrelDistortion.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/ovrBarrelDistortion.cs @@ -32,6 +32,11 @@ singleton ShaderData( OVRMonoToStereoShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/oculusvr/monoToStereoP.hlsl"; + + //OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.hlsl"; + //OGLPixelShaderFile = "shaders/common/postFx/oculusvr/gl/monoToStereoP.glsl"; + + samplerNames[0] = "$backBuffer"; pixVersion = 2.0; }; @@ -40,6 +45,11 @@ singleton ShaderData( OVRBarrelDistortionShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/oculusvr/barrelDistortionP.hlsl"; + + //OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + //OGLPixelShaderFile = "shaders/common/postFx/oculusvr/gl/barrelDistortionP.glsl"; + + samplerNames[0] = "$backBuffer"; pixVersion = 2.0; }; diff --git a/Templates/Empty/game/core/scripts/client/postFx/ssao.cs b/Templates/Empty/game/core/scripts/client/postFx/ssao.cs index cd5bfc598..063cee087 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/ssao.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/ssao.cs @@ -152,6 +152,14 @@ singleton ShaderData( SSAOShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/ssao/SSAO_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/ssao/gl/SSAO_P.glsl"; + + samplerNames[0] = "$prepassMap"; + samplerNames[1] = "$randNormalTex"; + samplerNames[2] = "$powTable"; + pixVersion = 3.0; }; @@ -159,6 +167,13 @@ singleton ShaderData( SSAOBlurYShader ) { DXVertexShaderFile = "shaders/common/postFx/ssao/SSAO_Blur_V.hlsl"; DXPixelShaderFile = "shaders/common/postFx/ssao/SSAO_Blur_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/ssao/gl/SSAO_Blur_V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/ssao/gl/SSAO_Blur_P.glsl"; + + samplerNames[0] = "$occludeMap"; + samplerNames[1] = "$prepassMap"; + pixVersion = 3.0; defines = "BLUR_DIR=float2(0.0,1.0)"; @@ -266,6 +281,10 @@ singleton ShaderData( SSAOPowTableShader ) { DXVertexShaderFile = "shaders/common/postFx/ssao/SSAO_PowerTable_V.hlsl"; DXPixelShaderFile = "shaders/common/postFx/ssao/SSAO_PowerTable_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/ssao/gl/SSAO_PowerTable_V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/ssao/gl/SSAO_PowerTable_P.glsl"; + pixVersion = 2.0; }; diff --git a/Templates/Empty/game/core/scripts/client/postFx/turbulence.cs b/Templates/Empty/game/core/scripts/client/postFx/turbulence.cs index 767470e67..c2309f808 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/turbulence.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/turbulence.cs @@ -35,6 +35,10 @@ singleton ShaderData( PFX_TurbulenceShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/turbulenceP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/turbulenceP.glsl"; + + samplerNames[0] = "$inputTex"; pixVersion = 3.0; }; diff --git a/Templates/Empty/game/core/scripts/client/scatterSky.cs b/Templates/Empty/game/core/scripts/client/scatterSky.cs index ab1e67cf6..48a8fdbc7 100644 --- a/Templates/Empty/game/core/scripts/client/scatterSky.cs +++ b/Templates/Empty/game/core/scripts/client/scatterSky.cs @@ -44,5 +44,7 @@ singleton ShaderData( ScatterSkyShaderData ) OGLVertexShaderFile = "shaders/common/gl/scatterSkyV.glsl"; OGLPixelShaderFile = "shaders/common/gl/scatterSkyP.glsl"; + samplerNames[0] = "$nightSky"; + pixVersion = 2.0; }; diff --git a/Templates/Empty/game/core/scripts/client/shaders.cs b/Templates/Empty/game/core/scripts/client/shaders.cs index 76b35884c..98d0529eb 100644 --- a/Templates/Empty/game/core/scripts/client/shaders.cs +++ b/Templates/Empty/game/core/scripts/client/shaders.cs @@ -33,6 +33,10 @@ singleton ShaderData( ParticlesShaderData ) OGLVertexShaderFile = "shaders/common/gl/particlesV.glsl"; OGLPixelShaderFile = "shaders/common/gl/particlesP.glsl"; + samplerNames[0] = "$diffuseMap"; + samplerNames[1] = "$prepassTex"; + samplerNames[2] = "$paraboloidLightMap"; + pixVersion = 2.0; }; @@ -44,6 +48,9 @@ singleton ShaderData( OffscreenParticleCompositeShaderData ) OGLVertexShaderFile = "shaders/common/gl/particleCompositeV.glsl"; OGLPixelShaderFile = "shaders/common/gl/particleCompositeP.glsl"; + samplerNames[0] = "$colorSource"; + samplerNames[1] = "$edgeSource"; + pixVersion = 2.0; }; @@ -55,8 +62,8 @@ new ShaderData( ReflectBump ) DXVertexShaderFile = "shaders/common/planarReflectBumpV.hlsl"; DXPixelShaderFile = "shaders/common/planarReflectBumpP.hlsl"; - OGLVertexShaderFile = "shaders/common/gl/planarReflectBumpV.glsl"; - OGLPixelShaderFile = "shaders/common/gl/planarReflectBumpP.glsl"; + OGLVertexShaderFile = "shaders/common/gl/planarReflectBumpV.glsl"; + OGLPixelShaderFile = "shaders/common/gl/planarReflectBumpP.glsl"; samplerNames[0] = "$diffuseMap"; samplerNames[1] = "$refractMap"; @@ -70,8 +77,8 @@ new ShaderData( Reflect ) DXVertexShaderFile = "shaders/common/planarReflectV.hlsl"; DXPixelShaderFile = "shaders/common/planarReflectP.hlsl"; - OGLVertexShaderFile = "shaders/common/gl/planarReflectV.glsl"; - OGLPixelShaderFile = "shaders/common/gl/planarReflectP.glsl"; + OGLVertexShaderFile = "shaders/common/gl/planarReflectV.glsl"; + OGLPixelShaderFile = "shaders/common/gl/planarReflectP.glsl"; samplerNames[0] = "$diffuseMap"; samplerNames[1] = "$refractMap"; diff --git a/Templates/Empty/game/core/scripts/client/terrainBlock.cs b/Templates/Empty/game/core/scripts/client/terrainBlock.cs index cf18094c9..8be68f7d3 100644 --- a/Templates/Empty/game/core/scripts/client/terrainBlock.cs +++ b/Templates/Empty/game/core/scripts/client/terrainBlock.cs @@ -29,5 +29,8 @@ singleton ShaderData( TerrainBlendShader ) OGLVertexShaderFile = "shaders/common/terrain/gl/blendV.glsl"; OGLPixelShaderFile = "shaders/common/terrain/gl/blendP.glsl"; + samplerNames[0] = "layerTex"; + samplerNames[1] = "textureMap"; + pixVersion = 2.0; }; diff --git a/Templates/Empty/game/core/scripts/client/water.cs b/Templates/Empty/game/core/scripts/client/water.cs index 883554df1..d010fe6d0 100644 --- a/Templates/Empty/game/core/scripts/client/water.cs +++ b/Templates/Empty/game/core/scripts/client/water.cs @@ -34,6 +34,14 @@ singleton ShaderData( WaterShader ) OGLVertexShaderFile = "shaders/common/water/gl/waterV.glsl"; OGLPixelShaderFile = "shaders/common/water/gl/waterP.glsl"; + samplerNames[0] = "$bumpMap"; // noise + samplerNames[1] = "$prepassTex"; // #prepass + samplerNames[2] = "$reflectMap"; // $reflectbuff + samplerNames[3] = "$refractBuff"; // $backbuff + samplerNames[4] = "$skyMap"; // $cubemap + samplerNames[5] = "$foamMap"; // foam + samplerNames[6] = "$depthGradMap"; // depthMap ( color gradient ) + pixVersion = 3.0; }; @@ -73,6 +81,14 @@ singleton CustomMaterial( WaterMat ) sampler["prepassTex"] = "#prepass"; sampler["reflectMap"] = "$reflectbuff"; sampler["refractBuff"] = "$backbuff"; + // These samplers are set in code not here. + // This is to allow different WaterObject instances + // to use this same material but override these textures + // per instance. + //sampler["bumpMap"] = ""; + //sampler["skyMap"] = ""; + //sampler["foamMap"] = ""; + //sampler["depthGradMap"] = ""; shader = WaterShader; stateBlock = WaterStateBlock; @@ -85,16 +101,9 @@ singleton CustomMaterial( WaterMat ) // Underwater //----------------------------------------------------------------------------- -singleton ShaderData( UnderWaterShader ) -{ - DXVertexShaderFile = "shaders/common/water/waterV.hlsl"; - DXPixelShaderFile = "shaders/common/water/waterP.hlsl"; - - OGLVertexShaderFile = "shaders/common/water/gl/waterV.glsl"; - OGLPixelShaderFile = "shaders/common/water/gl/waterP.glsl"; - +singleton ShaderData( UnderWaterShader : WaterShader ) +{ defines = "UNDERWATER"; - pixVersion = 3.0; }; singleton CustomMaterial( UnderwaterMat ) @@ -125,8 +134,14 @@ singleton ShaderData( WaterBasicShader ) DXVertexShaderFile = "shaders/common/water/waterBasicV.hlsl"; DXPixelShaderFile = "shaders/common/water/waterBasicP.hlsl"; - OGLVertexShaderFile = "shaders/common/water/gl/waterBasicV.glsl"; - OGLPixelShaderFile = "shaders/common/water/gl/waterBasicP.glsl"; + OGLVertexShaderFile = "shaders/common/water/gl/waterBasicV.glsl"; + OGLPixelShaderFile = "shaders/common/water/gl/waterBasicP.glsl"; + + samplerNames[0] = "$bumpMap"; + samplerNames[2] = "$reflectMap"; + samplerNames[3] = "$refractBuff"; + samplerNames[4] = "$skyMap"; + samplerNames[5] = "$depthGradMap"; pixVersion = 2.0; }; @@ -170,16 +185,9 @@ singleton CustomMaterial( WaterBasicMat ) // Basic UnderWater //----------------------------------------------------------------------------- -singleton ShaderData( UnderWaterBasicShader ) +singleton ShaderData( UnderWaterBasicShader : WaterBasicShader) { - DXVertexShaderFile = "shaders/common/water/waterBasicV.hlsl"; - DXPixelShaderFile = "shaders/common/water/waterBasicP.hlsl"; - - OGLVertexShaderFile = "shaders/common/water/gl/waterBasicV.glsl"; - OGLPixelShaderFile = "shaders/common/water/gl/waterBasicP.glsl"; - - defines = "UNDERWATER"; - pixVersion = 2.0; + defines = "UNDERWATER"; }; singleton CustomMaterial( UnderwaterBasicMat ) diff --git a/Templates/Full/game/core/scripts/client/clouds.cs b/Templates/Full/game/core/scripts/client/clouds.cs index 2763ce182..87284890a 100644 --- a/Templates/Full/game/core/scripts/client/clouds.cs +++ b/Templates/Full/game/core/scripts/client/clouds.cs @@ -32,6 +32,8 @@ singleton ShaderData( CloudLayerShader ) OGLVertexShaderFile = "shaders/common/gl/cloudLayerV.glsl"; OGLPixelShaderFile = "shaders/common/gl/cloudLayerP.glsl"; + samplerNames[0] = "$normalHeightMap"; + pixVersion = 2.0; }; @@ -44,8 +46,10 @@ singleton ShaderData( BasicCloudsShader ) DXVertexShaderFile = "shaders/common/basicCloudsV.hlsl"; DXPixelShaderFile = "shaders/common/basicCloudsP.hlsl"; - //OGLVertexShaderFile = "shaders/common/gl/basicCloudsV.glsl"; - //OGLPixelShaderFile = "shaders/common/gl/basicCloudsP.glsl"; + OGLVertexShaderFile = "shaders/common/gl/basicCloudsV.glsl"; + OGLPixelShaderFile = "shaders/common/gl/basicCloudsP.glsl"; + + samplerNames[0] = "$diffuseMap"; pixVersion = 2.0; }; diff --git a/Templates/Full/game/core/scripts/client/lighting/advanced/lightViz.cs b/Templates/Full/game/core/scripts/client/lighting/advanced/lightViz.cs index 9cb797bb9..8c3ecc03c 100644 --- a/Templates/Full/game/core/scripts/client/lighting/advanced/lightViz.cs +++ b/Templates/Full/game/core/scripts/client/lighting/advanced/lightViz.cs @@ -53,7 +53,7 @@ new ShaderData( AL_DepthVisualizeShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/lighting/advanced/dbgDepthVisualizeP.hlsl"; - OGLVertexShaderFile = "shaders/common/postFx/postFxV.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/dbgDepthVisualizeP.glsl"; samplerNames[0] = "prepassBuffer"; @@ -90,10 +90,10 @@ new ShaderData( AL_NormalsVisualizeShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/lighting/advanced/dbgNormalVisualizeP.hlsl"; - OGLVertexShaderFile = "shaders/common/postFx/postFxV.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/dbgNormalVisualizeP.glsl"; - samplerNames[0] = "prepassTex"; + samplerNames[0] = "prepassBuffer"; pixVersion = 2.0; }; @@ -126,8 +126,8 @@ new ShaderData( AL_LightColorVisualizeShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/lighting/advanced/dbgLightColorVisualizeP.hlsl"; - OGLVertexShaderFile = "shaders/common/postFx/postFxV.glsl"; - OGLPixelShaderFile = "shaders/common/lighting/advanced/dl/dbgLightColorVisualizeP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/dbgLightColorVisualizeP.glsl"; samplerNames[0] = "lightInfoBuffer"; @@ -161,8 +161,8 @@ new ShaderData( AL_LightSpecularVisualizeShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/lighting/advanced/dbgLightSpecularVisualizeP.hlsl"; - OGLVertexShaderFile = "shaders/common/postFx/postFxV.glsl"; - OGLPixelShaderFile = "shaders/common/lighting/advanced/dl/dbgLightSpecularVisualizeP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/dbgLightSpecularVisualizeP.glsl"; samplerNames[0] = "lightInfoBuffer"; diff --git a/Templates/Full/game/core/scripts/client/lighting/advanced/shaders.cs b/Templates/Full/game/core/scripts/client/lighting/advanced/shaders.cs index fef6c5652..7fda56235 100644 --- a/Templates/Full/game/core/scripts/client/lighting/advanced/shaders.cs +++ b/Templates/Full/game/core/scripts/client/lighting/advanced/shaders.cs @@ -61,6 +61,11 @@ new ShaderData( AL_VectorLightShader ) OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/farFrustumQuadV.glsl"; OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/vectorLightP.glsl"; + samplerNames[0] = "$prePassBuffer"; + samplerNames[1] = "$ShadowMap"; + samplerNames[2] = "$ssaoMask"; + samplerNames[3] = "$gTapRotationTex"; + pixVersion = 3.0; }; @@ -121,6 +126,11 @@ new ShaderData( AL_PointLightShader ) OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/convexGeometryV.glsl"; OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/pointLightP.glsl"; + samplerNames[0] = "$prePassBuffer"; + samplerNames[1] = "$shadowMap"; + samplerNames[2] = "$cookieMap"; + samplerNames[3] = "$gTapRotationTex"; + pixVersion = 3.0; }; @@ -131,7 +141,7 @@ new CustomMaterial( AL_PointLightMaterial ) sampler["prePassBuffer"] = "#prepass"; sampler["shadowMap"] = "$dynamiclight"; - sampler["cookieTex"] = "$dynamiclightmask"; + sampler["cookieMap"] = "$dynamiclightmask"; target = "lightinfo"; @@ -147,6 +157,11 @@ new ShaderData( AL_SpotLightShader ) OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/convexGeometryV.glsl"; OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/spotLightP.glsl"; + samplerNames[0] = "$prePassBuffer"; + samplerNames[1] = "$shadowMap"; + samplerNames[2] = "$cookieMap"; + samplerNames[3] = "$gTapRotationTex"; + pixVersion = 3.0; }; @@ -157,7 +172,7 @@ new CustomMaterial( AL_SpotLightMaterial ) sampler["prePassBuffer"] = "#prepass"; sampler["shadowMap"] = "$dynamiclight"; - sampler["cookieTex"] = "$dynamiclightmask"; + sampler["cookieMap"] = "$dynamiclightmask"; target = "lightinfo"; @@ -210,6 +225,8 @@ new ShaderData( AL_ParticlePointLightShader ) OGLVertexShaderFile = "shaders/common/lighting/advanced/gl/convexGeometryV.glsl"; OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/pointLightP.glsl"; + + samplerNames[0] = "$prePassBuffer"; pixVersion = 3.0; }; diff --git a/Templates/Full/game/core/scripts/client/lighting/advanced/shadowViz.cs b/Templates/Full/game/core/scripts/client/lighting/advanced/shadowViz.cs index 88b2d7f4a..c8db6456a 100644 --- a/Templates/Full/game/core/scripts/client/lighting/advanced/shadowViz.cs +++ b/Templates/Full/game/core/scripts/client/lighting/advanced/shadowViz.cs @@ -28,6 +28,9 @@ new ShaderData( AL_ShadowVisualizeShader ) OGLVertexShaderFile = "shaders/common/gl/guiMaterialV.glsl"; OGLPixelShaderFile = "shaders/common/lighting/advanced/gl/dbgShadowVisualizeP.glsl"; + samplerNames[0] = "$shadowMap"; + samplerNames[1] = "$depthViz"; + pixVersion = 2.0; }; diff --git a/Templates/Full/game/core/scripts/client/lighting/basic/init.cs b/Templates/Full/game/core/scripts/client/lighting/basic/init.cs index fd77adb8b..ed2972c11 100644 --- a/Templates/Full/game/core/scripts/client/lighting/basic/init.cs +++ b/Templates/Full/game/core/scripts/client/lighting/basic/init.cs @@ -46,6 +46,8 @@ singleton ShaderData( BL_ProjectedShadowShaderData ) OGLVertexShaderFile = "shaders/common/gl/projectedShadowV.glsl"; OGLPixelShaderFile = "shaders/common/gl/projectedShadowP.glsl"; + samplerNames[0] = "inputTex"; + pixVersion = 2.0; }; diff --git a/Templates/Full/game/core/scripts/client/lighting/basic/shadowFilter.cs b/Templates/Full/game/core/scripts/client/lighting/basic/shadowFilter.cs index 30a6db6d6..82484f515 100644 --- a/Templates/Full/game/core/scripts/client/lighting/basic/shadowFilter.cs +++ b/Templates/Full/game/core/scripts/client/lighting/basic/shadowFilter.cs @@ -26,8 +26,8 @@ singleton ShaderData( BL_ShadowFilterShaderV ) DXVertexShaderFile = "shaders/common/lighting/basic/shadowFilterV.hlsl"; DXPixelShaderFile = "shaders/common/lighting/basic/shadowFilterP.hlsl"; - OGLVertexShaderFile = "shaders/common/lighting/basic/gl/shadowFilterV.glsl"; - OGLPixelShaderFile = "shaders/common/lighting/basic/gl/shadowFilterP.glsl"; + OGLVertexShaderFile = "shaders/common/lighting/basic/gl/shadowFilterV.glsl"; + OGLPixelShaderFile = "shaders/common/lighting/basic/gl/shadowFilterP.glsl"; samplerNames[0] = "$diffuseMap"; diff --git a/Templates/Full/game/core/scripts/client/postFx.cs b/Templates/Full/game/core/scripts/client/postFx.cs index bbb0794bb..4e6451544 100644 --- a/Templates/Full/game/core/scripts/client/postFx.cs +++ b/Templates/Full/game/core/scripts/client/postFx.cs @@ -36,8 +36,8 @@ singleton ShaderData( PFX_PassthruShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/passthruP.hlsl"; -// OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; -// OGLPixelShaderFile = "shaders/common/postFx/gl/passthruP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/passthruP.glsl"; samplerNames[0] = "$inputTex"; diff --git a/Templates/Full/game/core/scripts/client/postFx/GammaPostFX.cs b/Templates/Full/game/core/scripts/client/postFx/GammaPostFX.cs index 61d4603c8..383a0c8cd 100644 --- a/Templates/Full/game/core/scripts/client/postFx/GammaPostFX.cs +++ b/Templates/Full/game/core/scripts/client/postFx/GammaPostFX.cs @@ -25,6 +25,12 @@ singleton ShaderData( GammaShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/gammaP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/gammaP.glsl"; + + samplerNames[0] = "$backBuffer"; + samplerNames[1] = "$colorCorrectionTex"; + pixVersion = 2.0; }; diff --git a/Templates/Full/game/core/scripts/client/postFx/MLAA.cs b/Templates/Full/game/core/scripts/client/postFx/MLAA.cs index 47156970b..f1656fb51 100644 --- a/Templates/Full/game/core/scripts/client/postFx/MLAA.cs +++ b/Templates/Full/game/core/scripts/client/postFx/MLAA.cs @@ -46,8 +46,12 @@ singleton ShaderData( MLAA_EdgeDetectionShader ) { DXVertexShaderFile = "shaders/common/postFx/mlaa/offsetV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/mlaa/edgeDetectionP.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/mlaa/gl/offsetV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/mlaa/gl/edgeDetectionP.glsl"; samplerNames[0] = "$colorMapG"; + samplerNames[1] = "$prepassMap"; pixVersion = 3.0; }; @@ -71,6 +75,9 @@ singleton ShaderData( MLAA_BlendWeightCalculationShader ) { DXVertexShaderFile = "shaders/common/postFx/mlaa/passthruV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/mlaa/blendWeightCalculationP.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/mlaa/gl/passthruV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/mlaa/gl/blendWeightCalculationP.glsl"; samplerNames[0] = "$edgesMap"; samplerNames[1] = "$edgesMapL"; @@ -98,6 +105,9 @@ singleton ShaderData( MLAA_NeighborhoodBlendingShader ) { DXVertexShaderFile = "shaders/common/postFx/mlaa/offsetV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/mlaa/neighborhoodBlendingP.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/mlaa/gl/offsetV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/mlaa/gl/neighborhoodBlendingP.glsl"; samplerNames[0] = "$blendMap"; samplerNames[1] = "$colorMapL"; diff --git a/Templates/Full/game/core/scripts/client/postFx/MotionBlurFx.cs b/Templates/Full/game/core/scripts/client/postFx/MotionBlurFx.cs index e94e5ef67..fea0c3bb3 100644 --- a/Templates/Full/game/core/scripts/client/postFx/MotionBlurFx.cs +++ b/Templates/Full/game/core/scripts/client/postFx/MotionBlurFx.cs @@ -25,6 +25,12 @@ singleton ShaderData( PFX_MotionBlurShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; //we use the bare-bones postFxV.hlsl DXPixelShaderFile = "shaders/common/postFx/motionBlurP.hlsl"; //new pixel shader + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/motionBlurP.glsl"; + + samplerNames[0] = "$backBuffer"; + samplerNames[1] = "$prepassTex"; + pixVersion = 3.0; }; diff --git a/Templates/Full/game/core/scripts/client/postFx/caustics.cs b/Templates/Full/game/core/scripts/client/postFx/caustics.cs index c6a694c51..3e8b14de0 100644 --- a/Templates/Full/game/core/scripts/client/postFx/caustics.cs +++ b/Templates/Full/game/core/scripts/client/postFx/caustics.cs @@ -38,9 +38,13 @@ singleton ShaderData( PFX_CausticsShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/caustics/causticsP.hlsl"; - //OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; - //OGLPixelShaderFile = "shaders/common/postFx/gl/passthruP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/caustics/gl/causticsP.glsl"; + samplerNames[0] = "$prepassTex"; + samplerNames[1] = "$causticsTex0"; + samplerNames[2] = "$causticsTex1"; + pixVersion = 3.0; }; diff --git a/Templates/Full/game/core/scripts/client/postFx/chromaticLens.cs b/Templates/Full/game/core/scripts/client/postFx/chromaticLens.cs index cf24c5702..705986e7e 100644 --- a/Templates/Full/game/core/scripts/client/postFx/chromaticLens.cs +++ b/Templates/Full/game/core/scripts/client/postFx/chromaticLens.cs @@ -47,6 +47,12 @@ singleton ShaderData( PFX_ChromaticLensShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/chromaticLens.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/chromaticLens.glsl"; + + samplerNames[0] = "$backBuffer"; + pixVersion = 3.0; }; diff --git a/Templates/Full/game/core/scripts/client/postFx/dof.cs b/Templates/Full/game/core/scripts/client/postFx/dof.cs index 1b20e44c5..dce41daea 100644 --- a/Templates/Full/game/core/scripts/client/postFx/dof.cs +++ b/Templates/Full/game/core/scripts/client/postFx/dof.cs @@ -318,8 +318,15 @@ singleton GFXStateBlockData( PFX_DOFFinalStateBlock ) singleton ShaderData( PFX_DOFDownSampleShader ) { - DXVertexShaderFile = "shaders/common/postFx/dof/DOF_DownSample_V.hlsl"; - DXPixelShaderFile = "shaders/common/postFx/dof/DOF_DownSample_P.hlsl"; + DXVertexShaderFile = "shaders/common/postFx/dof/DOF_DownSample_V.hlsl"; + DXPixelShaderFile = "shaders/common/postFx/dof/DOF_DownSample_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/dof/gl/DOF_DownSample_V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/dof/gl/DOF_DownSample_P.glsl"; + + samplerNames[0] = "$colorSampler"; + samplerNames[1] = "$depthSampler"; + pixVersion = 3.0; }; @@ -327,6 +334,12 @@ singleton ShaderData( PFX_DOFBlurYShader ) { DXVertexShaderFile = "shaders/common/postFx/dof/DOF_Gausian_V.hlsl"; DXPixelShaderFile = "shaders/common/postFx/dof/DOF_Gausian_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/dof/gl/DOF_Gausian_V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/dof/gl/DOF_Gausian_P.glsl"; + + samplerNames[0] = "$diffuseMap"; + pixVersion = 2.0; defines = "BLUR_DIR=float2(0.0,1.0)"; }; @@ -339,14 +352,27 @@ singleton ShaderData( PFX_DOFBlurXShader : PFX_DOFBlurYShader ) singleton ShaderData( PFX_DOFCalcCoCShader ) { DXVertexShaderFile = "shaders/common/postFx/dof/DOF_CalcCoC_V.hlsl"; - DXPixelShaderFile = "shaders/common/postFx/dof/DOF_CalcCoC_P.hlsl"; + DXPixelShaderFile = "shaders/common/postFx/dof/DOF_CalcCoC_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/dof/gl/DOF_CalcCoC_V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/dof/gl/DOF_CalcCoC_P.glsl"; + + samplerNames[0] = "$shrunkSampler"; + samplerNames[1] = "$blurredSampler"; + pixVersion = 3.0; }; singleton ShaderData( PFX_DOFSmallBlurShader ) { DXVertexShaderFile = "shaders/common/postFx/dof/DOF_SmallBlur_V.hlsl"; - DXPixelShaderFile = "shaders/common/postFx/dof/DOF_SmallBlur_P.hlsl"; + DXPixelShaderFile = "shaders/common/postFx/dof/DOF_SmallBlur_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/dof/gl/DOF_SmallBlur_V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/dof/gl/DOF_SmallBlur_P.glsl"; + + samplerNames[0] = "$colorSampler"; + pixVersion = 3.0; }; @@ -354,6 +380,15 @@ singleton ShaderData( PFX_DOFFinalShader ) { DXVertexShaderFile = "shaders/common/postFx/dof/DOF_Final_V.hlsl"; DXPixelShaderFile = "shaders/common/postFx/dof/DOF_Final_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/dof/gl/DOF_Final_V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/dof/gl/DOF_Final_P.glsl"; + + samplerNames[0] = "$colorSampler"; + samplerNames[1] = "$smallBlurSampler"; + samplerNames[2] = "$largeBlurSampler"; + samplerNames[3] = "$depthSampler"; + pixVersion = 3.0; }; diff --git a/Templates/Full/game/core/scripts/client/postFx/edgeAA.cs b/Templates/Full/game/core/scripts/client/postFx/edgeAA.cs index 2735df855..dcbd42ba1 100644 --- a/Templates/Full/game/core/scripts/client/postFx/edgeAA.cs +++ b/Templates/Full/game/core/scripts/client/postFx/edgeAA.cs @@ -37,10 +37,10 @@ singleton ShaderData( PFX_EdgeAADetectShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/edgeaa/edgeDetectP.hlsl"; - //OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; - //OGLPixelShaderFile = "shaders/common/postFx/gl/passthruP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/edgeaa/gl/edgeDetectP.glsl"; - samplerNames[0] = "$inputTex"; + samplerNames[0] = "$prepassBuffer"; pixVersion = 3.0; }; @@ -50,10 +50,11 @@ singleton ShaderData( PFX_EdgeAAShader ) DXVertexShaderFile = "shaders/common/postFx/edgeaa/edgeAAV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/edgeaa/edgeAAP.hlsl"; - //OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; - //OGLPixelShaderFile = "shaders/common/postFx/gl/passthruP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/edgeaa/gl/edgeAAV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/edgeaa/gl/edgeAAP.glsl"; - samplerNames[0] = "$inputTex"; + samplerNames[0] = "$edgeBuffer"; + samplerNames[1] = "$backBuffer"; pixVersion = 3.0; }; @@ -63,10 +64,10 @@ singleton ShaderData( PFX_EdgeAADebugShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/edgeaa/dbgEdgeDisplayP.hlsl"; - //OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; - //OGLPixelShaderFile = "shaders/common/postFx/gl/passthruP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/edgeaa/gl/dbgEdgeDisplayP.glsl"; - samplerNames[0] = "$inputTex"; + samplerNames[0] = "$edgeBuffer"; pixVersion = 3.0; }; diff --git a/Templates/Full/game/core/scripts/client/postFx/flash.cs b/Templates/Full/game/core/scripts/client/postFx/flash.cs index 105a0de34..244d91791 100644 --- a/Templates/Full/game/core/scripts/client/postFx/flash.cs +++ b/Templates/Full/game/core/scripts/client/postFx/flash.cs @@ -24,6 +24,11 @@ singleton ShaderData( PFX_FlashShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/flashP.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/flashP.glsl"; + + samplerNames[0] = "$backBuffer"; defines = "WHITE_COLOR=float4(1.0,1.0,1.0,0.0);MUL_COLOR=float4(1.0,0.25,0.25,0.0)"; diff --git a/Templates/Full/game/core/scripts/client/postFx/fog.cs b/Templates/Full/game/core/scripts/client/postFx/fog.cs index 5257db595..78b2a8924 100644 --- a/Templates/Full/game/core/scripts/client/postFx/fog.cs +++ b/Templates/Full/game/core/scripts/client/postFx/fog.cs @@ -29,8 +29,8 @@ singleton ShaderData( FogPassShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/fogP.hlsl"; -// OGLVertexShaderFile = "shaders/common/postFx/gl//postFxV.glsl"; -// OGLPixelShaderFile = "shaders/common/postFx/gl/fogP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/fogP.glsl"; samplerNames[0] = "$prepassTex"; @@ -75,10 +75,12 @@ singleton ShaderData( UnderwaterFogPassShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/underwaterFogP.hlsl"; -// OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; -// OGLPixelShaderFile = "shaders/common/postFx/gl/fogP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/underwaterFogP.glsl"; samplerNames[0] = "$prepassTex"; + samplerNames[1] = "$backbuffer"; + samplerNames[2] = "$waterDepthGradMap"; pixVersion = 2.0; }; diff --git a/Templates/Full/game/core/scripts/client/postFx/fxaa.cs b/Templates/Full/game/core/scripts/client/postFx/fxaa.cs index 7d1ac88c2..d13b9a61e 100644 --- a/Templates/Full/game/core/scripts/client/postFx/fxaa.cs +++ b/Templates/Full/game/core/scripts/client/postFx/fxaa.cs @@ -39,6 +39,9 @@ singleton ShaderData( FXAA_ShaderData ) DXVertexShaderFile = "shaders/common/postFx/fxaa/fxaaV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/fxaa/fxaaP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/fxaa/gl/fxaaV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/fxaa/gl/fxaaP.glsl"; + samplerNames[0] = "$colorTex"; pixVersion = 3.0; diff --git a/Templates/Full/game/core/scripts/client/postFx/glow.cs b/Templates/Full/game/core/scripts/client/postFx/glow.cs index d8bb086f0..3cc946b04 100644 --- a/Templates/Full/game/core/scripts/client/postFx/glow.cs +++ b/Templates/Full/game/core/scripts/client/postFx/glow.cs @@ -26,8 +26,8 @@ singleton ShaderData( PFX_GlowBlurVertShader ) DXVertexShaderFile = "shaders/common/postFx/glowBlurV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/glowBlurP.hlsl"; -// OGLVertexShaderFile = "shaders/common/postFx/glowBlurV.glsl"; -// OGLPixelShaderFile = "shaders/common/postFx/glowBlurP.glsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/glowBlurV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/glowBlurP.glsl"; defines = "BLUR_DIR=float2(0.0,1.0)"; diff --git a/Templates/Full/game/core/scripts/client/postFx/hdr.cs b/Templates/Full/game/core/scripts/client/postFx/hdr.cs index 92b3989e8..a5c450799 100644 --- a/Templates/Full/game/core/scripts/client/postFx/hdr.cs +++ b/Templates/Full/game/core/scripts/client/postFx/hdr.cs @@ -77,7 +77,13 @@ $HDRPostFX::colorCorrectionRamp = "core/scripts/client/postFx/null_color_ramp.pn singleton ShaderData( HDR_BrightPassShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; - DXPixelShaderFile = "shaders/common/postFx/hdr/brightPassFilterP.hlsl"; + DXPixelShaderFile = "shaders/common/postFx/hdr/brightPassFilterP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/brightPassFilterP.glsl"; + + samplerNames[0] = "$inputTex"; + samplerNames[1] = "$luminanceTex"; + pixVersion = 3.0; }; @@ -85,6 +91,11 @@ singleton ShaderData( HDR_DownScale4x4Shader ) { DXVertexShaderFile = "shaders/common/postFx/hdr/downScale4x4V.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/downScale4x4P.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/hdr/gl/downScale4x4V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/downScale4x4P.glsl"; + + samplerNames[0] = "$inputTex"; + pixVersion = 2.0; }; @@ -92,6 +103,11 @@ singleton ShaderData( HDR_BloomGaussBlurHShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/bloomGaussBlurHP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/bloomGaussBlurHP.glsl"; + + samplerNames[0] = "$inputTex"; + pixVersion = 3.0; }; @@ -99,6 +115,11 @@ singleton ShaderData( HDR_BloomGaussBlurVShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/bloomGaussBlurVP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/bloomGaussBlurVP.glsl"; + + samplerNames[0] = "$inputTex"; + pixVersion = 3.0; }; @@ -106,6 +127,11 @@ singleton ShaderData( HDR_SampleLumShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/sampleLumInitialP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/sampleLumInitialP.glsl"; + + samplerNames[0] = "$inputTex"; + pixVersion = 3.0; }; @@ -113,6 +139,11 @@ singleton ShaderData( HDR_DownSampleLumShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/sampleLumIterativeP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/sampleLumIterativeP.glsl"; + + samplerNames[0] = "$inputTex"; + pixVersion = 3.0; }; @@ -120,6 +151,12 @@ singleton ShaderData( HDR_CalcAdaptedLumShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/calculateAdaptedLumP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/calculateAdaptedLumP.glsl"; + + samplerNames[0] = "$currLum"; + samplerNames[1] = "$lastAdaptedLum"; + pixVersion = 3.0; }; @@ -127,6 +164,14 @@ singleton ShaderData( HDR_CombineShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/finalPassCombineP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/finalPassCombineP.glsl"; + + samplerNames[0] = "$sceneTex"; + samplerNames[1] = "$luminanceTex"; + samplerNames[2] = "$bloomTex"; + samplerNames[3] = "$colorCorrectionTex"; + pixVersion = 3.0; }; @@ -421,6 +466,11 @@ singleton ShaderData( LuminanceVisShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/hdr/luminanceVisP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/hdr/gl/luminanceVisP.glsl"; + + samplerNames[0] = "$inputTex"; + pixVersion = 3.0; }; diff --git a/Templates/Full/game/core/scripts/client/postFx/lightRay.cs b/Templates/Full/game/core/scripts/client/postFx/lightRay.cs index 5a6d7aa28..c13f1ca3b 100644 --- a/Templates/Full/game/core/scripts/client/postFx/lightRay.cs +++ b/Templates/Full/game/core/scripts/client/postFx/lightRay.cs @@ -34,6 +34,12 @@ singleton ShaderData( LightRayOccludeShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/lightRay/lightRayOccludeP.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/lightRay/gl/lightRayOccludeP.glsl"; + + samplerNames[0] = "$backBuffer"; + samplerNames[1] = "$prepassTex"; pixVersion = 3.0; }; @@ -42,6 +48,12 @@ singleton ShaderData( LightRayShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/lightRay/lightRayP.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/lightRay/gl/lightRayP.glsl"; + + samplerNames[0] = "$frameSampler"; + samplerNames[1] = "$backBuffer"; pixVersion = 3.0; }; diff --git a/Templates/Full/game/core/scripts/client/postFx/ovrBarrelDistortion.cs b/Templates/Full/game/core/scripts/client/postFx/ovrBarrelDistortion.cs index cbd72ec84..1838aa621 100644 --- a/Templates/Full/game/core/scripts/client/postFx/ovrBarrelDistortion.cs +++ b/Templates/Full/game/core/scripts/client/postFx/ovrBarrelDistortion.cs @@ -32,6 +32,11 @@ singleton ShaderData( OVRMonoToStereoShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/oculusvr/monoToStereoP.hlsl"; + + //OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.hlsl"; + //OGLPixelShaderFile = "shaders/common/postFx/oculusvr/gl/monoToStereoP.glsl"; + + samplerNames[0] = "$backBuffer"; pixVersion = 2.0; }; @@ -40,6 +45,11 @@ singleton ShaderData( OVRBarrelDistortionShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/oculusvr/barrelDistortionP.hlsl"; + + //OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + //OGLPixelShaderFile = "shaders/common/postFx/oculusvr/gl/barrelDistortionP.glsl"; + + samplerNames[0] = "$backBuffer"; pixVersion = 2.0; }; diff --git a/Templates/Full/game/core/scripts/client/postFx/ssao.cs b/Templates/Full/game/core/scripts/client/postFx/ssao.cs index cd5bfc598..063cee087 100644 --- a/Templates/Full/game/core/scripts/client/postFx/ssao.cs +++ b/Templates/Full/game/core/scripts/client/postFx/ssao.cs @@ -152,6 +152,14 @@ singleton ShaderData( SSAOShader ) { DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/ssao/SSAO_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/ssao/gl/SSAO_P.glsl"; + + samplerNames[0] = "$prepassMap"; + samplerNames[1] = "$randNormalTex"; + samplerNames[2] = "$powTable"; + pixVersion = 3.0; }; @@ -159,6 +167,13 @@ singleton ShaderData( SSAOBlurYShader ) { DXVertexShaderFile = "shaders/common/postFx/ssao/SSAO_Blur_V.hlsl"; DXPixelShaderFile = "shaders/common/postFx/ssao/SSAO_Blur_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/ssao/gl/SSAO_Blur_V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/ssao/gl/SSAO_Blur_P.glsl"; + + samplerNames[0] = "$occludeMap"; + samplerNames[1] = "$prepassMap"; + pixVersion = 3.0; defines = "BLUR_DIR=float2(0.0,1.0)"; @@ -266,6 +281,10 @@ singleton ShaderData( SSAOPowTableShader ) { DXVertexShaderFile = "shaders/common/postFx/ssao/SSAO_PowerTable_V.hlsl"; DXPixelShaderFile = "shaders/common/postFx/ssao/SSAO_PowerTable_P.hlsl"; + + OGLVertexShaderFile = "shaders/common/postFx/ssao/gl/SSAO_PowerTable_V.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/ssao/gl/SSAO_PowerTable_P.glsl"; + pixVersion = 2.0; }; diff --git a/Templates/Full/game/core/scripts/client/postFx/turbulence.cs b/Templates/Full/game/core/scripts/client/postFx/turbulence.cs index 767470e67..c2309f808 100644 --- a/Templates/Full/game/core/scripts/client/postFx/turbulence.cs +++ b/Templates/Full/game/core/scripts/client/postFx/turbulence.cs @@ -35,6 +35,10 @@ singleton ShaderData( PFX_TurbulenceShader ) DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; DXPixelShaderFile = "shaders/common/postFx/turbulenceP.hlsl"; + OGLVertexShaderFile = "shaders/common/postFx/gl/postFxV.glsl"; + OGLPixelShaderFile = "shaders/common/postFx/gl/turbulenceP.glsl"; + + samplerNames[0] = "$inputTex"; pixVersion = 3.0; }; diff --git a/Templates/Full/game/core/scripts/client/scatterSky.cs b/Templates/Full/game/core/scripts/client/scatterSky.cs index ab1e67cf6..7701b0adf 100644 --- a/Templates/Full/game/core/scripts/client/scatterSky.cs +++ b/Templates/Full/game/core/scripts/client/scatterSky.cs @@ -42,7 +42,9 @@ singleton ShaderData( ScatterSkyShaderData ) DXPixelShaderFile = "shaders/common/scatterSkyP.hlsl"; OGLVertexShaderFile = "shaders/common/gl/scatterSkyV.glsl"; - OGLPixelShaderFile = "shaders/common/gl/scatterSkyP.glsl"; + OGLPixelShaderFile = "shaders/common/gl/scatterSkyP.glsl"; + + samplerNames[0] = "$nightSky"; pixVersion = 2.0; }; diff --git a/Templates/Full/game/core/scripts/client/shaders.cs b/Templates/Full/game/core/scripts/client/shaders.cs index 76b35884c..98d0529eb 100644 --- a/Templates/Full/game/core/scripts/client/shaders.cs +++ b/Templates/Full/game/core/scripts/client/shaders.cs @@ -33,6 +33,10 @@ singleton ShaderData( ParticlesShaderData ) OGLVertexShaderFile = "shaders/common/gl/particlesV.glsl"; OGLPixelShaderFile = "shaders/common/gl/particlesP.glsl"; + samplerNames[0] = "$diffuseMap"; + samplerNames[1] = "$prepassTex"; + samplerNames[2] = "$paraboloidLightMap"; + pixVersion = 2.0; }; @@ -44,6 +48,9 @@ singleton ShaderData( OffscreenParticleCompositeShaderData ) OGLVertexShaderFile = "shaders/common/gl/particleCompositeV.glsl"; OGLPixelShaderFile = "shaders/common/gl/particleCompositeP.glsl"; + samplerNames[0] = "$colorSource"; + samplerNames[1] = "$edgeSource"; + pixVersion = 2.0; }; @@ -55,8 +62,8 @@ new ShaderData( ReflectBump ) DXVertexShaderFile = "shaders/common/planarReflectBumpV.hlsl"; DXPixelShaderFile = "shaders/common/planarReflectBumpP.hlsl"; - OGLVertexShaderFile = "shaders/common/gl/planarReflectBumpV.glsl"; - OGLPixelShaderFile = "shaders/common/gl/planarReflectBumpP.glsl"; + OGLVertexShaderFile = "shaders/common/gl/planarReflectBumpV.glsl"; + OGLPixelShaderFile = "shaders/common/gl/planarReflectBumpP.glsl"; samplerNames[0] = "$diffuseMap"; samplerNames[1] = "$refractMap"; @@ -70,8 +77,8 @@ new ShaderData( Reflect ) DXVertexShaderFile = "shaders/common/planarReflectV.hlsl"; DXPixelShaderFile = "shaders/common/planarReflectP.hlsl"; - OGLVertexShaderFile = "shaders/common/gl/planarReflectV.glsl"; - OGLPixelShaderFile = "shaders/common/gl/planarReflectP.glsl"; + OGLVertexShaderFile = "shaders/common/gl/planarReflectV.glsl"; + OGLPixelShaderFile = "shaders/common/gl/planarReflectP.glsl"; samplerNames[0] = "$diffuseMap"; samplerNames[1] = "$refractMap"; diff --git a/Templates/Full/game/core/scripts/client/terrainBlock.cs b/Templates/Full/game/core/scripts/client/terrainBlock.cs index cf18094c9..8be68f7d3 100644 --- a/Templates/Full/game/core/scripts/client/terrainBlock.cs +++ b/Templates/Full/game/core/scripts/client/terrainBlock.cs @@ -29,5 +29,8 @@ singleton ShaderData( TerrainBlendShader ) OGLVertexShaderFile = "shaders/common/terrain/gl/blendV.glsl"; OGLPixelShaderFile = "shaders/common/terrain/gl/blendP.glsl"; + samplerNames[0] = "layerTex"; + samplerNames[1] = "textureMap"; + pixVersion = 2.0; }; diff --git a/Templates/Full/game/core/scripts/client/water.cs b/Templates/Full/game/core/scripts/client/water.cs index 883554df1..77ce948ae 100644 --- a/Templates/Full/game/core/scripts/client/water.cs +++ b/Templates/Full/game/core/scripts/client/water.cs @@ -34,6 +34,14 @@ singleton ShaderData( WaterShader ) OGLVertexShaderFile = "shaders/common/water/gl/waterV.glsl"; OGLPixelShaderFile = "shaders/common/water/gl/waterP.glsl"; + samplerNames[0] = "$bumpMap"; // noise + samplerNames[1] = "$prepassTex"; // #prepass + samplerNames[2] = "$reflectMap"; // $reflectbuff + samplerNames[3] = "$refractBuff"; // $backbuff + samplerNames[4] = "$skyMap"; // $cubemap + samplerNames[5] = "$foamMap"; // foam + samplerNames[6] = "$depthGradMap"; // depthMap ( color gradient ) + pixVersion = 3.0; }; @@ -73,6 +81,14 @@ singleton CustomMaterial( WaterMat ) sampler["prepassTex"] = "#prepass"; sampler["reflectMap"] = "$reflectbuff"; sampler["refractBuff"] = "$backbuff"; + // These samplers are set in code not here. + // This is to allow different WaterObject instances + // to use this same material but override these textures + // per instance. + //sampler["bumpMap"] = ""; + //sampler["skyMap"] = ""; + //sampler["foamMap"] = ""; + //sampler["depthGradMap"] = ""; shader = WaterShader; stateBlock = WaterStateBlock; @@ -85,16 +101,9 @@ singleton CustomMaterial( WaterMat ) // Underwater //----------------------------------------------------------------------------- -singleton ShaderData( UnderWaterShader ) +singleton ShaderData( UnderWaterShader : WaterShader ) { - DXVertexShaderFile = "shaders/common/water/waterV.hlsl"; - DXPixelShaderFile = "shaders/common/water/waterP.hlsl"; - - OGLVertexShaderFile = "shaders/common/water/gl/waterV.glsl"; - OGLPixelShaderFile = "shaders/common/water/gl/waterP.glsl"; - defines = "UNDERWATER"; - pixVersion = 3.0; }; singleton CustomMaterial( UnderwaterMat ) @@ -125,8 +134,14 @@ singleton ShaderData( WaterBasicShader ) DXVertexShaderFile = "shaders/common/water/waterBasicV.hlsl"; DXPixelShaderFile = "shaders/common/water/waterBasicP.hlsl"; - OGLVertexShaderFile = "shaders/common/water/gl/waterBasicV.glsl"; - OGLPixelShaderFile = "shaders/common/water/gl/waterBasicP.glsl"; + OGLVertexShaderFile = "shaders/common/water/gl/waterBasicV.glsl"; + OGLPixelShaderFile = "shaders/common/water/gl/waterBasicP.glsl"; + + samplerNames[0] = "$bumpMap"; + samplerNames[2] = "$reflectMap"; + samplerNames[3] = "$refractBuff"; + samplerNames[4] = "$skyMap"; + samplerNames[5] = "$depthGradMap"; pixVersion = 2.0; }; @@ -170,16 +185,9 @@ singleton CustomMaterial( WaterBasicMat ) // Basic UnderWater //----------------------------------------------------------------------------- -singleton ShaderData( UnderWaterBasicShader ) +singleton ShaderData( UnderWaterBasicShader : WaterBasicShader) { - DXVertexShaderFile = "shaders/common/water/waterBasicV.hlsl"; - DXPixelShaderFile = "shaders/common/water/waterBasicP.hlsl"; - - OGLVertexShaderFile = "shaders/common/water/gl/waterBasicV.glsl"; - OGLPixelShaderFile = "shaders/common/water/gl/waterBasicP.glsl"; - defines = "UNDERWATER"; - pixVersion = 2.0; }; singleton CustomMaterial( UnderwaterBasicMat ) From 3995b974eddaef0f8600e02b721c6f701ffc8ac3 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Thu, 17 Apr 2014 13:21:52 +0200 Subject: [PATCH 014/317] Fix WaterObject TODO: Retrieve sampler numbers from parameter handles, see r22631. --- Engine/source/environment/waterObject.cpp | 16 ++++++++-------- Engine/source/environment/waterObject.h | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/Engine/source/environment/waterObject.cpp b/Engine/source/environment/waterObject.cpp index 51e737717..ae69060ac 100644 --- a/Engine/source/environment/waterObject.cpp +++ b/Engine/source/environment/waterObject.cpp @@ -91,6 +91,7 @@ void WaterMatParams::clear() mSpecularParamsSC = NULL; mDepthGradMaxSC = NULL; mReflectivitySC = NULL; + mDepthGradSamplerSC = NULL; } void WaterMatParams::init( BaseMatInstance* matInst ) @@ -132,6 +133,7 @@ void WaterMatParams::init( BaseMatInstance* matInst ) mSpecularParamsSC = matInst->getMaterialParameterHandle( "$specularParams" ); mDepthGradMaxSC = matInst->getMaterialParameterHandle( "$depthGradMax" ); mReflectivitySC = matInst->getMaterialParameterHandle( "$reflectivity" ); + mDepthGradSamplerSC = matInst->getMaterialParameterHandle( "$depthGradMap" ); } @@ -747,22 +749,20 @@ void WaterObject::renderObject( ObjectRenderInst *ri, SceneRenderState *state, B void WaterObject::setCustomTextures( S32 matIdx, U32 pass, const WaterMatParams ¶mHandles ) { - // TODO: Retrieve sampler numbers from parameter handles, see r22631. - // Always use the ripple texture. - GFX->setTexture( 0, mRippleTex ); + GFX->setTexture( paramHandles.mRippleSamplerSC->getSamplerRegister(pass), mRippleTex ); // Only above-water in advanced-lighting uses the foam texture. if ( matIdx == WaterMat ) { - GFX->setTexture( 5, mFoamTex ); - GFX->setTexture( 6, mDepthGradientTex ); + GFX->setTexture( paramHandles.mFoamSamplerSC->getSamplerRegister(pass), mFoamTex ); + GFX->setTexture( paramHandles.mDepthGradSamplerSC->getSamplerRegister(pass), mDepthGradientTex ); } if ( ( matIdx == WaterMat || matIdx == BasicWaterMat ) && mCubemap ) - GFX->setCubeTexture( 4, mCubemap->mCubemap ); - else - GFX->setCubeTexture( 4, NULL ); + GFX->setCubeTexture( paramHandles.mCubemapSamplerSC->getSamplerRegister(pass), mCubemap->mCubemap ); + else if(paramHandles.mCubemapSamplerSC->getSamplerRegister(pass) != -1 ) + GFX->setCubeTexture( paramHandles.mCubemapSamplerSC->getSamplerRegister(pass), NULL ); } void WaterObject::drawUnderwaterFilter( SceneRenderState *state ) diff --git a/Engine/source/environment/waterObject.h b/Engine/source/environment/waterObject.h index 06c5e7dd0..7eeb05703 100644 --- a/Engine/source/environment/waterObject.h +++ b/Engine/source/environment/waterObject.h @@ -97,6 +97,7 @@ struct WaterMatParams MaterialParameterHandle* mSpecularParamsSC; MaterialParameterHandle* mDepthGradMaxSC; MaterialParameterHandle* mReflectivitySC; + MaterialParameterHandle* mDepthGradSamplerSC; void clear(); void init(BaseMatInstance* matInst); From 51aca8916576f1b6cf1be564fa8c81351d997c3c Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Thu, 17 Apr 2014 13:36:37 +0200 Subject: [PATCH 015/317] Fix ScatterSkyVertex::color declaration. --- Engine/source/environment/scatterSky.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/environment/scatterSky.h b/Engine/source/environment/scatterSky.h index 254756bc9..2ce581ad3 100644 --- a/Engine/source/environment/scatterSky.h +++ b/Engine/source/environment/scatterSky.h @@ -62,7 +62,7 @@ GFXDeclareVertexFormat( ScatterSkyVertex ) { Point3F point; VectorF normal; - ColorF color; + GFXVertexColor color; }; class ScatterSky : public SceneObject, public ISceneLight From cb8b8b7316dc52099dbead412b7c07c9ad35ffb9 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Thu, 17 Apr 2014 14:18:58 +0200 Subject: [PATCH 016/317] Use shader data for get sampler register in CloudLayer and BasicClouds. --- Engine/source/environment/basicClouds.cpp | 3 ++- Engine/source/environment/basicClouds.h | 1 + Engine/source/environment/cloudLayer.cpp | 3 ++- Engine/source/environment/cloudLayer.h | 1 + 4 files changed, 6 insertions(+), 2 deletions(-) diff --git a/Engine/source/environment/basicClouds.cpp b/Engine/source/environment/basicClouds.cpp index 6f9b28653..737458093 100644 --- a/Engine/source/environment/basicClouds.cpp +++ b/Engine/source/environment/basicClouds.cpp @@ -129,6 +129,7 @@ bool BasicClouds::onAdd() mTexScaleSC = mShader->getShaderConstHandle( "$texScale" ); mTexDirectionSC = mShader->getShaderConstHandle( "$texDirection" ); mTexOffsetSC = mShader->getShaderConstHandle( "$texOffset" ); + mDiffuseMapSC = mShader->getShaderConstHandle( "$diffuseMap" ); // Create StateBlocks GFXStateBlockDesc desc; @@ -312,7 +313,7 @@ void BasicClouds::renderObject( ObjectRenderInst *ri, SceneRenderState *state, B mShaderConsts->setSafe( mTexDirectionSC, mTexDirection[i] * mTexSpeed[i] ); mShaderConsts->setSafe( mTexOffsetSC, mTexOffset[i] ); - GFX->setTexture( 0, mTexture[i] ); + GFX->setTexture( mDiffuseMapSC->getSamplerRegister(), mTexture[i] ); GFX->setVertexBuffer( mVB[i] ); GFX->drawIndexedPrimitive( GFXTriangleList, 0, 0, smVertCount, 0, smTriangleCount ); diff --git a/Engine/source/environment/basicClouds.h b/Engine/source/environment/basicClouds.h index 997ef57b8..adb15139c 100644 --- a/Engine/source/environment/basicClouds.h +++ b/Engine/source/environment/basicClouds.h @@ -103,6 +103,7 @@ protected: GFXShaderConstHandle *mTexScaleSC; GFXShaderConstHandle *mTexDirectionSC; GFXShaderConstHandle *mTexOffsetSC; + GFXShaderConstHandle *mDiffuseMapSC; GFXVertexBufferHandle mVB[TEX_COUNT]; GFXPrimitiveBufferHandle mPB; diff --git a/Engine/source/environment/cloudLayer.cpp b/Engine/source/environment/cloudLayer.cpp index 3901df5ac..ca7ae4ebc 100644 --- a/Engine/source/environment/cloudLayer.cpp +++ b/Engine/source/environment/cloudLayer.cpp @@ -143,6 +143,7 @@ bool CloudLayer::onAdd() mCoverageSC = mShader->getShaderConstHandle( "$cloudCoverage" ); mExposureSC = mShader->getShaderConstHandle( "$cloudExposure" ); mBaseColorSC = mShader->getShaderConstHandle( "$cloudBaseColor" ); + mNormalHeightMapSC = mShader->getShaderConstHandle( "$normalHeightMap" ); // Create StateBlocks GFXStateBlockDesc desc; @@ -365,7 +366,7 @@ void CloudLayer::renderObject( ObjectRenderInst *ri, SceneRenderState *state, Ba mShaderConsts->setSafe( mExposureSC, mExposure ); - GFX->setTexture( 0, mTexture ); + GFX->setTexture( mNormalHeightMapSC->getSamplerRegister(), mTexture ); GFX->setVertexBuffer( mVB ); GFX->setPrimitiveBuffer( mPB ); diff --git a/Engine/source/environment/cloudLayer.h b/Engine/source/environment/cloudLayer.h index 10e9f20af..076c3a631 100644 --- a/Engine/source/environment/cloudLayer.h +++ b/Engine/source/environment/cloudLayer.h @@ -110,6 +110,7 @@ protected: GFXShaderConstHandle *mCoverageSC; GFXShaderConstHandle *mExposureSC; GFXShaderConstHandle *mEyePosWorldSC; + GFXShaderConstHandle *mNormalHeightMapSC; GFXVertexBufferHandle mVB; GFXPrimitiveBufferHandle mPB; From 0f9bc89f32ab60edee42af127fecb7e59383eb29 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Thu, 17 Apr 2014 17:36:29 +0200 Subject: [PATCH 017/317] Add RenderPassData::mSamplerNames for OpenGL code. Not used on DX9. --- Engine/source/materials/processedMaterial.cpp | 3 +++ Engine/source/materials/processedMaterial.h | 1 + 2 files changed, 4 insertions(+) diff --git a/Engine/source/materials/processedMaterial.cpp b/Engine/source/materials/processedMaterial.cpp index 34ce285a0..9e82c65b1 100644 --- a/Engine/source/materials/processedMaterial.cpp +++ b/Engine/source/materials/processedMaterial.cpp @@ -41,7 +41,10 @@ RenderPassData::RenderPassData() void RenderPassData::reset() { for( U32 i = 0; i < Material::MAX_TEX_PER_PASS; ++ i ) + { destructInPlace( &mTexSlot[ i ] ); + mSamplerNames[ i ].clear(); + } dMemset( &mTexSlot, 0, sizeof(mTexSlot) ); dMemset( &mTexType, 0, sizeof(mTexType) ); diff --git a/Engine/source/materials/processedMaterial.h b/Engine/source/materials/processedMaterial.h index 841825aab..b72422cf9 100644 --- a/Engine/source/materials/processedMaterial.h +++ b/Engine/source/materials/processedMaterial.h @@ -68,6 +68,7 @@ public: } mTexSlot[Material::MAX_TEX_PER_PASS]; U32 mTexType[Material::MAX_TEX_PER_PASS]; + String mSamplerNames[Material::MAX_TEX_PER_PASS]; /// The cubemap to use when the texture type is /// set to Material::Cube. From ba36617aecb6f56d31ce535c1f84500880f3ace7 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Thu, 17 Apr 2014 17:36:29 +0200 Subject: [PATCH 018/317] Add RenderPassData::mSamplerNames for OpenGL code. Not used on DX9. --- Engine/source/materials/processedMaterial.cpp | 3 +++ Engine/source/materials/processedMaterial.h | 1 + 2 files changed, 4 insertions(+) diff --git a/Engine/source/materials/processedMaterial.cpp b/Engine/source/materials/processedMaterial.cpp index 34ce285a0..9e82c65b1 100644 --- a/Engine/source/materials/processedMaterial.cpp +++ b/Engine/source/materials/processedMaterial.cpp @@ -41,7 +41,10 @@ RenderPassData::RenderPassData() void RenderPassData::reset() { for( U32 i = 0; i < Material::MAX_TEX_PER_PASS; ++ i ) + { destructInPlace( &mTexSlot[ i ] ); + mSamplerNames[ i ].clear(); + } dMemset( &mTexSlot, 0, sizeof(mTexSlot) ); dMemset( &mTexType, 0, sizeof(mTexType) ); diff --git a/Engine/source/materials/processedMaterial.h b/Engine/source/materials/processedMaterial.h index 841825aab..b72422cf9 100644 --- a/Engine/source/materials/processedMaterial.h +++ b/Engine/source/materials/processedMaterial.h @@ -68,6 +68,7 @@ public: } mTexSlot[Material::MAX_TEX_PER_PASS]; U32 mTexType[Material::MAX_TEX_PER_PASS]; + String mSamplerNames[Material::MAX_TEX_PER_PASS]; /// The cubemap to use when the texture type is /// set to Material::Cube. From 9221b4dd10eedc531e42dabd6891ace9d32a37ab Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Thu, 17 Apr 2014 17:44:49 +0200 Subject: [PATCH 019/317] Update GLSL Shadergen. Not used on DX9. --- .../forest/glsl/windDeformationGLSL.cpp | 65 +- .../glsl/advancedLightingFeaturesGLSL.cpp | 457 ++++----- .../glsl/advancedLightingFeaturesGLSL.h | 20 +- .../advanced/glsl/gBufferConditionerGLSL.cpp | 216 +++-- .../advanced/glsl/gBufferConditionerGLSL.h | 9 +- Engine/source/shaderGen/GLSL/bumpGLSL.cpp | 159 ++-- Engine/source/shaderGen/GLSL/bumpGLSL.h | 13 +- Engine/source/shaderGen/GLSL/depthGLSL.cpp | 54 +- Engine/source/shaderGen/GLSL/depthGLSL.h | 2 +- .../source/shaderGen/GLSL/paraboloidGLSL.cpp | 22 +- .../source/shaderGen/GLSL/pixSpecularGLSL.cpp | 117 +-- .../source/shaderGen/GLSL/pixSpecularGLSL.h | 4 +- .../source/shaderGen/GLSL/shaderCompGLSL.cpp | 275 +++++- Engine/source/shaderGen/GLSL/shaderCompGLSL.h | 11 +- .../shaderGen/GLSL/shaderFeatureGLSL.cpp | 901 +++++++++++------- .../source/shaderGen/GLSL/shaderFeatureGLSL.h | 79 +- .../source/shaderGen/GLSL/shaderGenGLSL.cpp | 13 +- .../shaderGen/GLSL/shaderGenGLSLInit.cpp | 6 + .../source/terrain/glsl/terrFeatureGLSL.cpp | 620 ++++++++---- Engine/source/terrain/glsl/terrFeatureGLSL.h | 29 +- 20 files changed, 1944 insertions(+), 1128 deletions(-) diff --git a/Engine/source/forest/glsl/windDeformationGLSL.cpp b/Engine/source/forest/glsl/windDeformationGLSL.cpp index 00502caa9..ae32b51f7 100644 --- a/Engine/source/forest/glsl/windDeformationGLSL.cpp +++ b/Engine/source/forest/glsl/windDeformationGLSL.cpp @@ -40,6 +40,9 @@ static void _onRegisterFeatures( GFXAdapterType type ) { + if ( type != OpenGL ) + return; + FEATUREMGR->registerFeature( MFT_WindEffect, new WindDeformationGLSL ); } @@ -83,29 +86,14 @@ void WindDeformationGLSL::processVert( Vector &componentList, // save constant space and reduce the memory copied to the // card. // - // This in particular helps when we're instancing. - // // .x = bend scale // .y = branch amplitude // .z = detail amplitude // .w = detail frequency // - Var *windParams; - if ( fd.features[MFT_UseInstancing] ) - { - ShaderConnector *vertStruct = dynamic_cast( componentList[C_VERT_STRUCT] ); - windParams = vertStruct->getElement( RT_TEXCOORD ); - windParams->setName( "inst_windParams" ); - windParams->setType( "vec4" ); - - mInstancingFormat->addElement( "windParams", GFXDeclType_Float4, windParams->constNum ); - } - else - { - windParams = new Var( "windParams", "vec4" ); + Var *windParams = new Var( "windParams", "vec4" ); windParams->uniform = true; windParams->constSortPos = cspPotentialPrimitive; - } // If we're instancing then we need to instance the wind direction // and speed as its unique for each tree instance. @@ -114,6 +102,7 @@ void WindDeformationGLSL::processVert( Vector &componentList, { ShaderConnector *vertStruct = dynamic_cast( componentList[C_VERT_STRUCT] ); windDirAndSpeed = vertStruct->getElement( RT_TEXCOORD ); + windDirAndSpeed->setStructName( "IN" ); windDirAndSpeed->setName( "inst_windDirAndSpeed" ); windDirAndSpeed->setType( "vec3" ); @@ -143,16 +132,31 @@ void WindDeformationGLSL::processVert( Vector &componentList, if ( !inPosition ) inPosition = (Var*)LangElement::find( "position" ); + // Copy the input position to the output first as + // the wind effects are conditional. + Var *outPosition = (Var*)LangElement::find( "inPosition" ); + if ( !outPosition ) + { + outPosition = new Var; + outPosition->setType( "vec3" ); + outPosition->setName( "inPosition" ); + meta->addStatement( new GenOp(" @ = @.xyz;\r\n", new DecOp( outPosition ), inPosition ) ); + } + // Get the incoming color data Var *inColor = (Var*)LangElement::find( "diffuse" ); + // Do a dynamic branch based on wind force. + if ( GFX->getPixelShaderVersion() >= 3.0f ) + meta->addStatement( new GenOp(" if ( any( bvec3(@) ) ) {\r\n", windDirAndSpeed ) ); + // Do the branch and detail bending first so that // it can work in pure object space of the tree. LangElement *effect = new GenOp( "windBranchBending( " - "@.xyz, " // vPos - "normalize( normal ), " // vNormal + "@, " // vPos + "normalize( IN_normal ), " // vNormal "@, " // fTime "@.z, " // fWindSpeed @@ -161,13 +165,13 @@ void WindDeformationGLSL::processVert( Vector &componentList, "@.y, " // fBranchAmp "@.r, " // fBranchAtten - "dot( @[3], vec4( 1.0 ) ), " // fDetailPhase + "dot( @[3], vec4(1) ), " // fDetailPhase "@.z, " // fDetailAmp "@.w, " // fDetailFreq - + "@.b )", // fEdgeAtten - inPosition, // vPos + outPosition, // vPos // vNormal accumTime, // fTime @@ -183,22 +187,15 @@ void WindDeformationGLSL::processVert( Vector &componentList, inColor ); // fEdgeAtten - Var *outPosition = (Var*)LangElement::find( "inPosition" ); - if ( outPosition ) - meta->addStatement( new GenOp( " @.xyz = @;\r\n", outPosition, effect, inPosition ) ); - else - { - outPosition = new Var; - outPosition->setType( "vec3" ); - outPosition->setName( "inPosition" ); - meta->addStatement( new GenOp(" vec3 inPosition = @;\r\n", effect, inPosition ) ); - } + meta->addStatement( new GenOp( " @ = @;\r\n", outPosition, effect ) ); // Now do the trunk bending. - effect = new GenOp( "windTrunkBending( @, @.xy, @.z * @.x )", - outPosition, windDirAndSpeed, outPosition, windParams ); + meta->addStatement( new GenOp(" @ = windTrunkBending( @, @.xy, @.z * @.x );\r\n", + outPosition, outPosition, windDirAndSpeed, outPosition, windParams ) ); - meta->addStatement( new GenOp(" @ = @;\r\n", outPosition, effect ) ); + // End the dynamic branch. + if ( GFX->getPixelShaderVersion() >= 3.0f ) + meta->addStatement( new GenOp(" } // [branch]\r\n" ) ); } ShaderFeatureConstHandles* WindDeformationGLSL::createConstHandles( GFXShader *shader, SimObject *userObject ) diff --git a/Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.cpp b/Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.cpp index 1da348f03..c5f85234a 100644 --- a/Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.cpp +++ b/Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.cpp @@ -35,16 +35,12 @@ void DeferredRTLightingFeatGLSL::processPixMacros( Vector ¯os, const MaterialFeatureData &fd ) { - /// TODO: This needs to be done via some sort of material - /// feature and not just allow all translucent elements to - /// read from the light prepass. - /* - if ( fd.features[MFT_IsTranslucent] ) + // Skip deferred features, and use forward shading instead + if ( fd.features[MFT_ForwardShading] ) { Parent::processPixMacros( macros, fd ); return; } - */ // Pull in the uncondition method for the light info buffer NamedTexTarget *texTarget = NamedTexTarget::find( AdvancedLightBinManager::smBufferName ); @@ -59,48 +55,42 @@ void DeferredRTLightingFeatGLSL::processPixMacros( Vector ¯o void DeferredRTLightingFeatGLSL::processVert( Vector &componentList, const MaterialFeatureData &fd ) { - /// TODO: This needs to be done via some sort of material - /// feature and not just allow all translucent elements to - /// read from the light prepass. - /* - if ( fd.features[MFT_IsTranslucent] ) + // Skip deferred features, and use forward shading instead + if ( fd.features[MFT_ForwardShading] ) { Parent::processVert( componentList, fd ); return; } - */ // Pass screen space position to pixel shader to compute a full screen buffer uv ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *ssPos = connectComp->getElement( RT_TEXCOORD ); ssPos->setName( "screenspacePos" ); + ssPos->setStructName( "OUT" ); ssPos->setType( "vec4" ); -// Var *outPosition = (Var*) LangElement::find( "hpos" ); -// AssertFatal( outPosition, "No hpos, ohnoes." ); + Var *outPosition = (Var*) LangElement::find( "gl_Position" ); + AssertFatal( outPosition, "No gl_Position, ohnoes." ); - output = new GenOp( " @ = gl_Position;\r\n", ssPos ); + output = new GenOp( " @ = @;\r\n", ssPos, outPosition ); } void DeferredRTLightingFeatGLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) { - /// TODO: This needs to be done via some sort of material - /// feature and not just allow all translucent elements to - /// read from the light prepass. - /* - if ( fd.features[MFT_IsTranslucent] ) + // Skip deferred features, and use forward shading instead + if ( fd.features[MFT_ForwardShading] ) { Parent::processPix( componentList, fd ); return; } - */ MultiLine *meta = new MultiLine; ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *ssPos = connectComp->getElement( RT_TEXCOORD ); ssPos->setName( "screenspacePos" ); + ssPos->setStructName( "IN" ); ssPos->setType( "vec4" ); Var *uvScene = new Var; @@ -108,18 +98,20 @@ void DeferredRTLightingFeatGLSL::processPix( Vector &component uvScene->setName( "uvScene" ); LangElement *uvSceneDecl = new DecOp( uvScene ); - Var *rtParams = (Var*) LangElement::find( "renderTargetParams" ); + String rtParamName = String::ToString( "rtParams%s", "lightInfoBuffer" ); + Var *rtParams = (Var*) LangElement::find( rtParamName ); if( !rtParams ) { rtParams = new Var; rtParams->setType( "vec4" ); - rtParams->setName( "renderTargetParams" ); + rtParams->setName( rtParamName ); rtParams->uniform = true; rtParams->constSortPos = cspPass; } meta->addStatement( new GenOp( " @ = @.xy / @.w;\r\n", uvSceneDecl, ssPos, ssPos ) ); // get the screen coord... its -1 to +1 meta->addStatement( new GenOp( " @ = ( @ + 1.0 ) / 2.0;\r\n", uvScene, uvScene ) ); // get the screen coord to 0 to 1 + meta->addStatement( new GenOp( " @.y = 1.0 - @.y;\r\n", uvScene, uvScene ) ); // flip the y axis meta->addStatement( new GenOp( " @ = ( @ * @.zw ) + @.xy;\r\n", uvScene, uvScene, rtParams, rtParams) ); // scale it down and offset it to the rt size Var *lightInfoSamp = new Var; @@ -134,36 +126,60 @@ void DeferredRTLightingFeatGLSL::processPix( Vector &component lightInfoBuffer->sampler = true; lightInfoBuffer->constNum = Var::getTexUnitNum(); // used as texture unit num here - String unconditionLightInfo = String::ToLower( AdvancedLightBinManager::smBufferName ) + "Uncondition"; - - meta->addStatement( new GenOp( " vec3 d_lightcolor;\r\n" ) ); - meta->addStatement( new GenOp( " float d_NL_Att;\r\n" ) ); - meta->addStatement( new GenOp( " float d_specular;\r\n" ) ); - meta->addStatement( new GenOp( avar( " %s(texture2D(@, @), d_lightcolor, d_NL_Att, d_specular);\r\n", unconditionLightInfo.c_str() ), - lightInfoBuffer, uvScene ) ); + // Declare the RTLighting variables in this feature, they will either be assigned + // in this feature, or in the tonemap/lightmap feature + Var *d_lightcolor = new Var( "d_lightcolor", "vec3" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( d_lightcolor ) ) ); - Var *rtShading = new Var; - rtShading->setType( "vec4" ); - rtShading->setName( "rtShading" ); - LangElement *rtShadingDecl = new DecOp( rtShading ); - meta->addStatement( new GenOp( " @ = vec4( d_lightcolor, 1.0 );\r\n", rtShadingDecl ) ); + Var *d_NL_Att = new Var( "d_NL_Att", "float" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( d_NL_Att ) ) ); + + Var *d_specular = new Var( "d_specular", "float" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( d_specular ) ) ); + + + // Perform the uncondition here. + String unconditionLightInfo = String::ToLower( AdvancedLightBinManager::smBufferName ) + "Uncondition"; + meta->addStatement( new GenOp( avar( " %s(tex2D(@, @), @, @, @);\r\n", + unconditionLightInfo.c_str() ), lightInfoBuffer, uvScene, d_lightcolor, d_NL_Att, d_specular ) ); + + // If this has an interlaced pre-pass, do averaging here + if( fd.features[MFT_InterlacedPrePass] ) + { + Var *oneOverTargetSize = (Var*) LangElement::find( "oneOverTargetSize" ); + if( !oneOverTargetSize ) + { + oneOverTargetSize = new Var; + oneOverTargetSize->setType( "vec2" ); + oneOverTargetSize->setName( "oneOverTargetSize" ); + oneOverTargetSize->uniform = true; + oneOverTargetSize->constSortPos = cspPass; + } + + meta->addStatement( new GenOp( " float id_NL_Att, id_specular;\r\n float3 id_lightcolor;\r\n" ) ); + meta->addStatement( new GenOp( avar( " %s(tex2D(@, @ + float2(0.0, @.y)), id_lightcolor, id_NL_Att, id_specular);\r\n", + unconditionLightInfo.c_str() ), lightInfoBuffer, uvScene, oneOverTargetSize ) ); + + meta->addStatement( new GenOp(" @ = lerp(@, id_lightcolor, 0.5);\r\n", d_lightcolor, d_lightcolor ) ); + meta->addStatement( new GenOp(" @ = lerp(@, id_NL_Att, 0.5);\r\n", d_NL_Att, d_NL_Att ) ); + meta->addStatement( new GenOp(" @ = lerp(@, id_specular, 0.5);\r\n", d_specular, d_specular ) ); + } // This is kind of weak sauce - if( !fd.features[MFT_SubSurface] && !fd.features[MFT_ToneMap] && !fd.features[MFT_LightMap] ) - meta->addStatement( new GenOp( " @;\r\n", assignColor( rtShading, Material::Mul ) ) ); + if( !fd.features[MFT_VertLit] && !fd.features[MFT_ToneMap] && !fd.features[MFT_LightMap] && !fd.features[MFT_SubSurface] ) + meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "float4(@, 1.0)", d_lightcolor ), Material::Mul ) ) ); output = meta; } ShaderFeature::Resources DeferredRTLightingFeatGLSL::getResources( const MaterialFeatureData &fd ) { - /// TODO: This needs to be done via some sort of material - /// feature and not just allow all translucent elements to - /// read from the light prepass. - /* - if( fd.features[MFT_IsTranslucent] ) + // Skip deferred features, and use forward shading instead + if ( fd.features[MFT_ForwardShading] ) return Parent::getResources( fd ); - */ + + // HACK: See DeferredRTLightingFeatGLSL::setTexData. + mLastTexIndex = 0; Resources res; res.numTex = 1; @@ -176,21 +192,22 @@ void DeferredRTLightingFeatGLSL::setTexData( Material::StageData &stageDat, RenderPassData &passData, U32 &texIndex ) { - /// TODO: This needs to be done via some sort of material - /// feature and not just allow all translucent elements to - /// read from the light prepass. - /* - if( fd.features[MFT_IsTranslucent] ) + // Skip deferred features, and use forward shading instead + if ( fd.features[MFT_ForwardShading] ) { Parent::setTexData( stageDat, fd, passData, texIndex ); return; } - */ NamedTexTarget *texTarget = NamedTexTarget::find( AdvancedLightBinManager::smBufferName ); if( texTarget ) { - passData.mTexType[ texIndex ] = Material::TexTarget; + // HACK: We store this for use in DeferredRTLightingFeatGLSL::processPix() + // which cannot deduce the texture unit itself. + mLastTexIndex = texIndex; + + passData.mTexType[ texIndex ] = Material::TexTarget; + passData.mSamplerNames[ texIndex ]= "lightInfoBuffer"; passData.mTexSlot[ texIndex++ ].texTarget = texTarget; } } @@ -205,92 +222,31 @@ void DeferredBumpFeatGLSL::processVert( Vector &componentLis // to the pixel shader. MultiLine *meta = new MultiLine; - // setup texture space matrix - Var *texSpaceMat = (Var*) LangElement::find( "objToTangentSpace" ); - if( !texSpaceMat ) - { - LangElement * texSpaceSetup = setupTexSpaceMat( componentList, &texSpaceMat ); - meta->addStatement( texSpaceSetup ); - texSpaceMat = (Var*) LangElement::find( "objToTangentSpace" ); - } - - // turn obj->tangent into world->tangent - Var *worldToTangent = new Var; - worldToTangent->setType( "mat3" ); - worldToTangent->setName( "worldToTangent" ); - LangElement *worldToTangentDecl = new DecOp( worldToTangent ); - - // Get the world->obj transform - Var *worldToObj = new Var; - worldToObj->setType( "mat4" ); - worldToObj->setName( "worldToObj" ); - worldToObj->uniform = true; - worldToObj->constSortPos = cspPrimitive; - - Var *mat3Conversion = new Var; - mat3Conversion->setType( "mat3" ); - mat3Conversion->setName( "worldToObjMat3" ); - LangElement* mat3Lang = new DecOp(mat3Conversion); - meta->addStatement( new GenOp( " @ = mat3(@[0].xyz, @[1].xyz, @[2].xyz);\r\n ", mat3Lang, worldToObj, worldToObj, worldToObj) ); - - // assign world->tangent transform - meta->addStatement( new GenOp( " @ = @ * @;\r\n", worldToTangentDecl, texSpaceMat, mat3Conversion ) ); - - // send transform to pixel shader - ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); - - Var *worldToTangentR1 = connectComp->getElement( RT_TEXCOORD ); - worldToTangentR1->setName( "worldToTangentR1" ); - worldToTangentR1->setType( "vec3" ); - meta->addStatement( new GenOp( " @ = @[0];\r\n", worldToTangentR1, worldToTangent ) ); - - Var *worldToTangentR2 = connectComp->getElement( RT_TEXCOORD ); - worldToTangentR2->setName( "worldToTangentR2" ); - worldToTangentR2->setType( "vec3" ); - meta->addStatement( new GenOp( " @ = @[1];\r\n", worldToTangentR2, worldToTangent ) ); - - Var *worldToTangentR3 = connectComp->getElement( RT_TEXCOORD ); - worldToTangentR3->setName( "worldToTangentR3" ); - worldToTangentR3->setType( "vec3" ); - meta->addStatement( new GenOp( " @ = @[2];\r\n", worldToTangentR3, worldToTangent ) ); + // We need the view to tangent space transform in the pixel shader. + getOutViewToTangent( componentList, meta, fd ); // Make sure there are texcoords - if( !fd.features[MFT_DiffuseMap] ) + if( !fd.features[MFT_Parallax] && !fd.features[MFT_DiffuseMap] ) { - // find incoming texture var - Var *inTex = getVertTexCoord( "texCoord" ); + const bool useTexAnim = fd.features[MFT_TexAnim]; - // grab connector texcoord register - ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); - Var *outTex = connectComp->getElement( RT_TEXCOORD ); - outTex->setName( "outTexCoord" ); - outTex->setType( "vec2" ); - outTex->mapsToSampler = true; + getOutTexCoord( "texCoord", + "vec2", + true, + useTexAnim, + meta, + componentList ); - if( fd.features[MFT_TexAnim] ) - { - inTex->setType( "vec4" ); - - // create texture mat var - Var *texMat = new Var; - texMat->setType( "mat4" ); - texMat->setName( "texMat" ); - texMat->uniform = true; - texMat->constSortPos = cspPotentialPrimitive; - - meta->addStatement( new GenOp( " @ = @ * @;\r\n", outTex, texMat, inTex ) ); - } - else - { - // setup language elements to output incoming tex coords to output - meta->addStatement( new GenOp( " @ = @;\r\n", outTex, inTex ) ); - } + if ( fd.features.hasFeature( MFT_DetailNormalMap ) ) + addOutDetailTexCoord( componentList, + meta, + useTexAnim ); } output = meta; } else if ( fd.materialFeatures[MFT_NormalsOut] || - fd.features[MFT_IsTranslucent] || + fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) { Parent::processVert( componentList, fd ); @@ -312,52 +268,12 @@ void DeferredBumpFeatGLSL::processPix( Vector &componentList, { MultiLine *meta = new MultiLine; - // Pull the world->tangent transform from the vertex shader - ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); - - Var *worldToTangentR1 = connectComp->getElement( RT_TEXCOORD ); - worldToTangentR1->setName( "worldToTangentR1" ); - worldToTangentR1->setType( "vec3" ); - - Var *worldToTangentR2 = connectComp->getElement( RT_TEXCOORD ); - worldToTangentR2->setName( "worldToTangentR2" ); - worldToTangentR2->setType( "vec3" ); - - Var *worldToTangentR3 = connectComp->getElement( RT_TEXCOORD ); - worldToTangentR3->setName( "worldToTangentR3" ); - worldToTangentR3->setType( "vec3" ); - - Var *worldToTangent = new Var; - worldToTangent->setType( "mat3" ); - worldToTangent->setName( "worldToTangent" ); - LangElement *worldToTangentDecl = new DecOp( worldToTangent ); - - // Build world->tangent matrix - meta->addStatement( new GenOp( " @;\r\n", worldToTangentDecl ) ); - meta->addStatement( new GenOp( " @[0] = @;\r\n", worldToTangent, worldToTangentR1 ) ); - meta->addStatement( new GenOp( " @[1] = @;\r\n", worldToTangent, worldToTangentR2 ) ); - meta->addStatement( new GenOp( " @[2] = @;\r\n", worldToTangent, worldToTangentR3 ) ); + Var *viewToTangent = getInViewToTangent( componentList ); // create texture var - Var *bumpMap = new Var; - bumpMap->setType( "sampler2D" ); - bumpMap->setName( "bumpMap" ); - bumpMap->uniform = true; - bumpMap->sampler = true; - bumpMap->constNum = Var::getTexUnitNum(); // used as texture unit num here - - Var *texCoord = (Var*) LangElement::find( "outTexCoord" ); - if( !texCoord ) - { - // grab connector texcoord register - ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); - texCoord = connectComp->getElement( RT_TEXCOORD ); - texCoord->setName( "outTexCoord" ); - texCoord->setType( "vec2" ); - texCoord->mapsToSampler = true; - } - - LangElement * texOp = new GenOp( "texture2D(@, @)", bumpMap, texCoord ); + Var *bumpMap = getNormalMapTex(); + Var *texCoord = getInTexCoord( "texCoord", "vec2", true, componentList ); + LangElement *texOp = new GenOp( "tex2D(@, @)", bumpMap, texCoord ); // create bump normal Var *bumpNorm = new Var; @@ -367,56 +283,75 @@ void DeferredBumpFeatGLSL::processPix( Vector &componentList, LangElement *bumpNormDecl = new DecOp( bumpNorm ); meta->addStatement( expandNormalMap( texOp, bumpNormDecl, bumpNorm, fd ) ); + // If we have a detail normal map we add the xy coords of + // it to the base normal map. This gives us the effect we + // want with few instructions and minial artifacts. + if ( fd.features.hasFeature( MFT_DetailNormalMap ) ) + { + bumpMap = new Var; + bumpMap->setType( "sampler2D" ); + bumpMap->setName( "detailBumpMap" ); + bumpMap->uniform = true; + bumpMap->sampler = true; + bumpMap->constNum = Var::getTexUnitNum(); + + texCoord = getInTexCoord( "detCoord", "vec2", true, componentList ); + texOp = new GenOp( "tex2D(@, @)", bumpMap, texCoord ); + + Var *detailBump = new Var; + detailBump->setName( "detailBump" ); + detailBump->setType( "vec4" ); + meta->addStatement( expandNormalMap( texOp, new DecOp( detailBump ), detailBump, fd ) ); + + Var *detailBumpScale = new Var; + detailBumpScale->setType( "float" ); + detailBumpScale->setName( "detailBumpStrength" ); + detailBumpScale->uniform = true; + detailBumpScale->constSortPos = cspPass; + meta->addStatement( new GenOp( " @.xy += @.xy * @;\r\n", bumpNorm, detailBump, detailBumpScale ) ); + } + // This var is read from GBufferConditionerHLSL and // used in the prepass output. + // + // By using the 'half' type here we get a bunch of partial + // precision optimized code on further operations on the normal + // which helps alot on older Geforce cards. + // Var *gbNormal = new Var; gbNormal->setName( "gbNormal" ); - gbNormal->setType( "vec3" ); + gbNormal->setType( "half3" ); LangElement *gbNormalDecl = new DecOp( gbNormal ); // Normalize is done later... // Note: The reverse mul order is intentional. Affine matrix. - meta->addStatement( new GenOp( " @ = @.xyz * @;\r\n", gbNormalDecl, bumpNorm, worldToTangent ) ); + meta->addStatement( new GenOp( " @ = half3(tMul( @.xyz, @ ));\r\n", gbNormalDecl, bumpNorm, viewToTangent ) ); output = meta; return; } else if ( fd.materialFeatures[MFT_NormalsOut] || - fd.features[MFT_IsTranslucent] || + fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) { Parent::processPix( componentList, fd ); return; } - else if ( fd.features[MFT_PixSpecular] ) + else if ( fd.features[MFT_PixSpecular] && !fd.features[MFT_SpecularMap] ) { Var *bumpSample = (Var *)LangElement::find( "bumpSample" ); if( bumpSample == NULL ) { - Var *texCoord = (Var*) LangElement::find( "outTexCoord" ); - if( !texCoord ) - { - // grab connector texcoord register - ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); - texCoord = connectComp->getElement( RT_TEXCOORD ); - texCoord->setName( "outTexCoord" ); - texCoord->setType( "vec2" ); - texCoord->mapsToSampler = true; - } + Var *texCoord = getInTexCoord( "texCoord", "vec2", true, componentList ); - Var *bumpMap = new Var; - bumpMap->setType( "sampler2D" ); - bumpMap->setName( "bumpMap" ); - bumpMap->uniform = true; - bumpMap->sampler = true; - bumpMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + Var *bumpMap = getNormalMapTex(); bumpSample = new Var; bumpSample->setType( "vec4" ); bumpSample->setName( "bumpSample" ); LangElement *bumpSampleDecl = new DecOp( bumpSample ); - output = new GenOp( " @ = texture2D(@, @);\r\n", bumpSampleDecl, bumpMap, texCoord ); + output = new GenOp( " @ = tex2D(@, @);\r\n", bumpSampleDecl, bumpMap, texCoord ); return; } } @@ -427,7 +362,7 @@ void DeferredBumpFeatGLSL::processPix( Vector &componentList, ShaderFeature::Resources DeferredBumpFeatGLSL::getResources( const MaterialFeatureData &fd ) { if ( fd.materialFeatures[MFT_NormalsOut] || - fd.features[MFT_IsTranslucent] || + fd.features[MFT_ForwardShading] || fd.features[MFT_Parallax] || !fd.features[MFT_RTLighting] ) return Parent::getResources( fd ); @@ -437,7 +372,16 @@ ShaderFeature::Resources DeferredBumpFeatGLSL::getResources( const MaterialFeatu { res.numTex = 1; res.numTexReg = 1; + + if ( fd.features[MFT_PrePassConditioner] && + fd.features.hasFeature( MFT_DetailNormalMap ) ) + { + res.numTex += 1; + if ( !fd.features.hasFeature( MFT_DetailMap ) ) + res.numTexReg += 1; + } } + return res; } @@ -447,21 +391,28 @@ void DeferredBumpFeatGLSL::setTexData( Material::StageData &stageDat, U32 &texIndex ) { if ( fd.materialFeatures[MFT_NormalsOut] || - fd.features[MFT_IsTranslucent] || + fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) { Parent::setTexData( stageDat, fd, passData, texIndex ); return; } - GFXTextureObject *normalMap = stageDat.getTex( MFT_NormalMap ); if ( !fd.features[MFT_Parallax] && !fd.features[MFT_SpecularMap] && ( fd.features[MFT_PrePassConditioner] || - fd.features[MFT_PixSpecular] ) && - normalMap ) + fd.features[MFT_PixSpecular] ) ) { passData.mTexType[ texIndex ] = Material::Bump; - passData.mTexSlot[ texIndex++ ].texObject = normalMap; + passData.mSamplerNames[ texIndex ] = "bumpMap"; + passData.mTexSlot[ texIndex++ ].texObject = stageDat.getTex( MFT_NormalMap ); + + if ( fd.features[MFT_PrePassConditioner] && + fd.features.hasFeature( MFT_DetailNormalMap ) ) + { + passData.mTexType[ texIndex ] = Material::DetailBump; + passData.mSamplerNames[ texIndex ] = "detailBumpMap"; + passData.mTexSlot[ texIndex++ ].texObject = stageDat.getTex( MFT_DetailNormalMap ); + } } } @@ -469,7 +420,7 @@ void DeferredBumpFeatGLSL::setTexData( Material::StageData &stageDat, void DeferredPixelSpecularGLSL::processVert( Vector &componentList, const MaterialFeatureData &fd ) { - if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] ) + if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) { Parent::processVert( componentList, fd ); return; @@ -480,7 +431,7 @@ void DeferredPixelSpecularGLSL::processVert( Vector &component void DeferredPixelSpecularGLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) { - if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] ) + if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) { Parent::processPix( componentList, fd ); return; @@ -523,19 +474,18 @@ void DeferredPixelSpecularGLSL::processPix( Vector &component specStrength->uniform = true; specStrength->constSortPos = cspPotentialPrimitive; - Var *constSpecPow = new Var; - constSpecPow->setType( "float" ); - constSpecPow->setName( "constantSpecularPower" ); - constSpecPow->uniform = true; - constSpecPow->constSortPos = cspPass; - Var *lightInfoSamp = (Var *)LangElement::find( "lightInfoSample" ); - AssertFatal( lightInfoSamp, "Something hosed the deferred features! Can't find lightInfoSample" ); + Var *d_specular = (Var*)LangElement::find( "d_specular" ); + Var *d_NL_Att = (Var*)LangElement::find( "d_NL_Att" ); + + AssertFatal( lightInfoSamp && d_specular && d_NL_Att, + "DeferredPixelSpecularGLSL::processPix - Something hosed the deferred features!" ); // (a^m)^n = a^(m*n) - meta->addStatement( new GenOp( " @ = pow(d_specular, ceil(@ / @)) * @;\r\n", specDecl, specPow, constSpecPow, specStrength ) ); + meta->addStatement( new GenOp( " @ = pow( abs(@), max((@ / AL_ConstantSpecularPower),1.0f)) * @;\r\n", + specDecl, d_specular, specPow, specStrength ) ); - LangElement *specMul = new GenOp( "@ * @", specCol, specular ); + LangElement *specMul = new GenOp( "float4( @.rgb, 0 ) * @", specCol, specular ); LangElement *final = specMul; // We we have a normal map then mask the specular @@ -545,14 +495,15 @@ void DeferredPixelSpecularGLSL::processPix( Vector &component final = new GenOp( "@ * @.a", final, bumpSample ); } - // add to color meta->addStatement( new GenOp( " @;\r\n", assignColor( final, Material::Add ) ) ); + // add to color + meta->addStatement( new GenOp( " @;\r\n", assignColor( final, Material::Add ) ) ); output = meta; } ShaderFeature::Resources DeferredPixelSpecularGLSL::getResources( const MaterialFeatureData &fd ) { - if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] ) + if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) return Parent::getResources( fd ); Resources res; @@ -563,7 +514,7 @@ ShaderFeature::Resources DeferredPixelSpecularGLSL::getResources( const Material ShaderFeature::Resources DeferredMinnaertGLSL::getResources( const MaterialFeatureData &fd ) { Resources res; - if( !fd.features[MFT_IsTranslucent] && fd.features[MFT_RTLighting] ) + if( !fd.features[MFT_ForwardShading] && fd.features[MFT_RTLighting] ) { res.numTex = 1; res.numTexReg = 1; @@ -576,7 +527,7 @@ void DeferredMinnaertGLSL::setTexData( Material::StageData &stageDat, RenderPassData &passData, U32 &texIndex ) { - if( !fd.features[MFT_IsTranslucent] && fd.features[MFT_RTLighting] ) + if( !fd.features[MFT_ForwardShading] && fd.features[MFT_RTLighting] ) { NamedTexTarget *texTarget = NamedTexTarget::find(RenderPrePassMgr::BufferName); if ( texTarget ) @@ -590,7 +541,7 @@ void DeferredMinnaertGLSL::setTexData( Material::StageData &stageDat, void DeferredMinnaertGLSL::processPixMacros( Vector ¯os, const MaterialFeatureData &fd ) { - if( !fd.features[MFT_IsTranslucent] && fd.features[MFT_RTLighting] ) + if( !fd.features[MFT_ForwardShading] && fd.features[MFT_RTLighting] ) { // Pull in the uncondition method for the g buffer NamedTexTarget *texTarget = NamedTexTarget::find( RenderPrePassMgr::BufferName ); @@ -607,55 +558,24 @@ void DeferredMinnaertGLSL::processVert( Vector &componentLis const MaterialFeatureData &fd ) { // If there is no deferred information, bail on this feature - if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] ) + if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) { output = NULL; return; } - // grab incoming vert position - Var *inVertPos = (Var*) LangElement::find( "position" ); - AssertFatal( inVertPos, "Something went bad with ShaderGen. The vertex position should be already defined." ); - - // grab output for gbuffer normal - ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); - Var *outWSEyeVec= connectComp->getElement( RT_TEXCOORD ); - outWSEyeVec->setName( "outWSViewVec" ); - outWSEyeVec->setType( "vec4" ); - - // create objToWorld variable - Var *objToWorld = (Var*) LangElement::find( "objTrans" ); - if( !objToWorld ) - { - objToWorld = new Var; - objToWorld->setType( "mat4x4" ); - objToWorld->setName( "objTrans" ); - objToWorld->uniform = true; - objToWorld->constSortPos = cspPrimitive; - } - - // Eye Pos world - Var *eyePosWorld = (Var*) LangElement::find( "eyePosWorld" ); - if( !eyePosWorld ) - { - eyePosWorld = new Var; - eyePosWorld->setType( "vec3" ); - eyePosWorld->setName( "eyePosWorld" ); - eyePosWorld->uniform = true; - eyePosWorld->constSortPos = cspPass; - } - - // Kick out the world-space normal - LangElement *statement = new GenOp( " @ = vec4(@, @) - vec4(@, 0.0);\r\n", - outWSEyeVec, objToWorld, inVertPos, eyePosWorld ); - output = statement; + // Make sure we pass the world space position to the + // pixel shader so we can calculate a view vector. + MultiLine *meta = new MultiLine; + addOutWsPosition( componentList, fd.features[MFT_UseInstancing], meta ); + output = meta; } void DeferredMinnaertGLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) { // If there is no deferred information, bail on this feature - if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] ) + if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) { output = NULL; return; @@ -679,25 +599,19 @@ void DeferredMinnaertGLSL::processPix( Vector &componentList, Var *uvScene = (Var*) LangElement::find( "uvScene" ); AssertFatal(uvScene != NULL, "Unable to find UVScene, no RTLighting feature?"); - ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); - Var *wsViewVec = (Var*) LangElement::find( "wsPos" ); - if( !wsViewVec ) - { - wsViewVec = connectComp->getElement( RT_TEXCOORD ); - wsViewVec->setName( "outWSViewVec" ); - wsViewVec->setType( "vec4" ); - wsViewVec->mapsToSampler = false; - wsViewVec->uniform = false; - } + MultiLine *meta = new MultiLine; + + // Get the world space view vector. + Var *wsViewVec = getWsView( getInWsPosition( componentList ), meta ); String unconditionPrePassMethod = String::ToLower(RenderPrePassMgr::BufferName) + "Uncondition"; - MultiLine *meta = new MultiLine; - meta->addStatement( new GenOp( avar( " vec4 normalDepth = %s(texture2D(@, @));\r\n", unconditionPrePassMethod.c_str() ), prepassBuffer, uvScene ) ); - meta->addStatement( new GenOp( " vec3 worldViewVec = normalize(@.xyz / @.w);\r\n", wsViewVec, wsViewVec ) ); - meta->addStatement( new GenOp( " float vDotN = dot(normalDepth.xyz, worldViewVec);\r\n" ) ); - meta->addStatement( new GenOp( " float Minnaert = pow(d_NL_Att, @) * pow(vDotN, 1.0 - @);\r\n", minnaertConstant, minnaertConstant ) ); - meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "vec4(Minnaert, Minnaert, Minnaert, 1.0)" ), Material::Mul ) ) ); + Var *d_NL_Att = (Var*)LangElement::find( "d_NL_Att" ); + + meta->addStatement( new GenOp( avar( " float4 normalDepth = %s(@, @);\r\n", unconditionPrePassMethod.c_str() ), prepassBuffer, uvScene ) ); + meta->addStatement( new GenOp( " float vDotN = dot(normalDepth.xyz, @);\r\n", wsViewVec ) ); + meta->addStatement( new GenOp( " float Minnaert = pow( @, @) * pow(vDotN, 1.0 - @);\r\n", d_NL_Att, minnaertConstant, minnaertConstant ) ); + meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "float4(Minnaert, Minnaert, Minnaert, 1.0)" ), Material::Mul ) ) ); output = meta; } @@ -707,7 +621,7 @@ void DeferredSubSurfaceGLSL::processPix( Vector &componentLis const MaterialFeatureData &fd ) { // If there is no deferred information, bail on this feature - if( fd.features[MFT_IsTranslucent] || !fd.features[MFT_RTLighting] ) + if( fd.features[MFT_ForwardShading] || !fd.features[MFT_RTLighting] ) { output = NULL; return; @@ -719,12 +633,13 @@ void DeferredSubSurfaceGLSL::processPix( Vector &componentLis subSurfaceParams->uniform = true; subSurfaceParams->constSortPos = cspPotentialPrimitive; - Var *inColor = (Var*) LangElement::find( "rtShading" ); + Var *d_lightcolor = (Var*)LangElement::find( "d_lightcolor" ); + Var *d_NL_Att = (Var*)LangElement::find( "d_NL_Att" ); MultiLine *meta = new MultiLine; - meta->addStatement( new GenOp( " float subLamb = smoothstep(-@.a, 1.0, d_NL_Att) - smoothstep(0.0, 1.0, d_NL_Att);\r\n", subSurfaceParams ) ); + meta->addStatement( new GenOp( " float subLamb = smoothstep(-@.a, 1.0, @) - smoothstep(0.0, 1.0, @);\r\n", subSurfaceParams, d_NL_Att, d_NL_Att ) ); meta->addStatement( new GenOp( " subLamb = max(0.0, subLamb);\r\n" ) ); - meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "vec4(@.rgb + (subLamb * @.rgb), 1.0)", inColor, subSurfaceParams ), Material::Mul ) ) ); + meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "float4(@ + (subLamb * @.rgb), 1.0)", d_lightcolor, subSurfaceParams ), Material::Mul ) ) ); output = meta; } diff --git a/Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.h b/Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.h index fb37848a8..0e326de6a 100644 --- a/Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.h +++ b/Engine/source/lighting/advanced/glsl/advancedLightingFeaturesGLSL.h @@ -30,13 +30,25 @@ class ConditionerMethodDependency; -/// Lights the pixel by sampling from the light prepass buffer. It will -/// fall back to default vertex lighting functionality if +/// Lights the pixel by sampling from the light prepass +/// buffer. It will fall back to forward lighting +/// functionality for non-deferred rendered surfaces. +/// +/// Also note that this feature is only used in the +/// forward rendering pass. It is not used during the +/// prepass step. +/// class DeferredRTLightingFeatGLSL : public RTLightingFeatGLSL { typedef RTLightingFeatGLSL Parent; +protected: + + /// @see DeferredRTLightingFeatHLSL::processPix() + U32 mLastTexIndex; + public: + virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); @@ -57,12 +69,12 @@ public: virtual String getName() { - return "Deferred RT Lighting Feature"; + return "Deferred RT Lighting"; } }; -/// Used to write the normals during the depth/normal prepass. +/// This is used during the class DeferredBumpFeatGLSL : public BumpFeatGLSL { typedef BumpFeatGLSL Parent; diff --git a/Engine/source/lighting/advanced/glsl/gBufferConditionerGLSL.cpp b/Engine/source/lighting/advanced/glsl/gBufferConditionerGLSL.cpp index fd3b27d85..17a98c969 100644 --- a/Engine/source/lighting/advanced/glsl/gBufferConditionerGLSL.cpp +++ b/Engine/source/lighting/advanced/glsl/gBufferConditionerGLSL.cpp @@ -27,9 +27,10 @@ #include "gfx/gfxStringEnumTranslate.h" #include "materials/materialFeatureTypes.h" #include "materials/materialFeatureData.h" +#include "shaderGen/GLSL/shaderFeatureGLSL.h" -GBufferConditionerGLSL::GBufferConditionerGLSL( const GFXFormat bufferFormat ) : +GBufferConditionerGLSL::GBufferConditionerGLSL( const GFXFormat bufferFormat, const NormalSpace nrmSpace ) : Parent( bufferFormat ) { // Figure out how we should store the normal data. These are the defaults. @@ -39,20 +40,18 @@ GBufferConditionerGLSL::GBufferConditionerGLSL( const GFXFormat bufferFormat ) : // Note: We clear to a depth 1 (the w component) so // that the unrendered parts of the scene end up // farthest to the camera. - + const NormalStorage &twoCmpNrmStorageType = ( nrmSpace == WorldSpace ? Spherical : LambertAzimuthal ); switch(bufferFormat) { case GFXFormatR8G8B8A8: - // TODO: Some kind of logic here. Spherical is better, but is more - // expensive. - mNormalStorageType = Spherical; + mNormalStorageType = twoCmpNrmStorageType; mBitsPerChannel = 8; break; case GFXFormatR16G16B16A16F: // Floating point buffers don't need to encode negative values mCanWriteNegativeValues = true; - mNormalStorageType = Spherical; + mNormalStorageType = twoCmpNrmStorageType; mBitsPerChannel = 16; break; @@ -61,7 +60,7 @@ GBufferConditionerGLSL::GBufferConditionerGLSL( const GFXFormat bufferFormat ) : // precision and high quality normals within a 64bit // buffer format. case GFXFormatR16G16B16A16: - mNormalStorageType = Spherical; + mNormalStorageType = twoCmpNrmStorageType; mBitsPerChannel = 16; break; @@ -83,34 +82,43 @@ GBufferConditionerGLSL::~GBufferConditionerGLSL() void GBufferConditionerGLSL::processVert( Vector &componentList, const MaterialFeatureData &fd ) { - output = NULL; + // If we have a normal map then that feature will + // take care of passing gbNormal to the pixel shader. + if ( fd.features[MFT_NormalMap] ) + return; - if( !fd.features[MFT_NormalMap] ) + MultiLine *meta = new MultiLine; + output = meta; + + // grab incoming vert normal + Var *inNormal = (Var*) LangElement::find( "normal" ); + AssertFatal( inNormal, "Something went bad with ShaderGen. The normal should be already defined." ); + + // grab output for gbuffer normal + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outNormal = connectComp->getElement( RT_TEXCOORD ); + outNormal->setName( "gbNormal" ); + outNormal->setStructName( "OUT" ); + outNormal->setType( "float3" ); + + if( !fd.features[MFT_ParticleNormal] ) { - // grab incoming vert normal - Var *inNormal = (Var*) LangElement::find( "normal" ); - AssertFatal( inNormal, "Something went bad with ShaderGen. The normal should be already defined." ); + // Kick out the view-space normal - // grab output for gbuffer normal - ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); - Var *outNormal = connectComp->getElement( RT_TEXCOORD ); - outNormal->setName( "gbNormal" ); - outNormal->setType( "vec3" ); + // TODO: Total hack because Conditioner is directly derived + // from ShaderFeature and not from ShaderFeatureGLSL. + NamedFeatureGLSL dummy( String::EmptyString ); + dummy.mInstancingFormat = mInstancingFormat; + Var *worldViewOnly = dummy.getWorldView( componentList, fd.features[MFT_UseInstancing], meta ); - // create objToWorld variable - Var *objToWorld = (Var*) LangElement::find( "objTrans" ); - if( !objToWorld ) - { - objToWorld = new Var; - objToWorld->setType( "mat4" ); - objToWorld->setName( "objTrans" ); - objToWorld->uniform = true; - objToWorld->constSortPos = cspPrimitive; - } - - // Kick out the world-space normal - LangElement *statement = new GenOp( " @ = vec3(@ * vec4(normalize(@), 0.0));\r\n", outNormal, objToWorld, inNormal ); - output = statement; + meta->addStatement( new GenOp(" @ = tMul(@, float4( normalize(@), 0.0 ) ).xyz;\r\n", + outNormal, worldViewOnly, inNormal ) ); + } + else + { + // Assume the particle normal generator has already put this in view space + // and normalized it + meta->addStatement( new GenOp( " @ = @;\r\n", outNormal, inNormal ) ); } } @@ -129,7 +137,8 @@ void GBufferConditionerGLSL::processPix( Vector &componentLis { gbNormal = connectComp->getElement( RT_TEXCOORD ); gbNormal->setName( "gbNormal" ); - gbNormal->setType( "vec3" ); + gbNormal->setStructName( "IN" ); + gbNormal->setType( "float3" ); gbNormal->mapsToSampler = false; gbNormal->uniform = false; } @@ -143,16 +152,45 @@ void GBufferConditionerGLSL::processPix( Vector &componentLis Var *unconditionedOut = new Var; - unconditionedOut->setType("vec4"); + unconditionedOut->setType("float4"); unconditionedOut->setName("normal_depth"); LangElement *outputDecl = new DecOp( unconditionedOut ); + // If we're doing prepass blending then we need + // to steal away the alpha channel before the + // conditioner stomps on it. + Var *alphaVal = NULL; + if ( fd.features[ MFT_IsTranslucentZWrite ] ) + { + alphaVal = new Var( "outAlpha", "float" ); + meta->addStatement( new GenOp( " @ = col.a; // MFT_IsTranslucentZWrite\r\n", new DecOp( alphaVal ) ) ); + } + + // If using interlaced normals, invert the normal + if(fd.features[MFT_InterlacedPrePass]) + { + // NOTE: Its safe to not call ShaderFeatureGLSL::addOutVpos() in the vertex + // shader as for SM 3.0 nothing is needed there. + Var *Vpos = (Var*) LangElement::find( "gl_Position" ); //Var *Vpos = ShaderFeatureGLSL::getInVpos( meta, componentList ); + + Var *iGBNormal = new Var( "interlacedGBNormal", "float3" ); + meta->addStatement(new GenOp(" @ = (frac(@.y * 0.5) < 0.1 ? reflect(@, float3(0.0, -1.0, 0.0)) : @);\r\n", new DecOp(iGBNormal), Vpos, gbNormal, gbNormal)); + gbNormal = iGBNormal; + } + // NOTE: We renormalize the normal here as they // will not stay normalized during interpolation. - meta->addStatement( new GenOp(" @ = @;", outputDecl, new GenOp( "vec4(normalize(@), @)", gbNormal, depth ) ) ); + meta->addStatement( new GenOp(" @ = @;", outputDecl, new GenOp( "float4(normalize(@), @)", gbNormal, depth ) ) ); meta->addStatement( assignOutput( unconditionedOut ) ); + // If we have an alpha var then we're doing prepass lerp blending. + if ( alphaVal ) + { + Var *outColor = (Var*)LangElement::find( getOutputTargetVarName( DefaultTarget ) ); + meta->addStatement( new GenOp( " @.ba = float2( 0, @ ); // MFT_IsTranslucentZWrite\r\n", outColor, alphaVal ) ); + } + output = meta; } @@ -180,7 +218,7 @@ Var* GBufferConditionerGLSL::printMethodHeader( MethodType methodType, const Str { Var *methodVar = new Var; methodVar->setName(methodName); - methodVar->setType("vec4"); + methodVar->setType("float4"); DecOp *methodDecl = new DecOp(methodVar); Var *prepassSampler = new Var; @@ -190,12 +228,12 @@ Var* GBufferConditionerGLSL::printMethodHeader( MethodType methodType, const Str Var *screenUV = new Var; screenUV->setName("screenUVVar"); - screenUV->setType("vec2"); + screenUV->setType("float2"); DecOp *screenUVDecl = new DecOp(screenUV); Var *bufferSample = new Var; bufferSample->setName("bufferSample"); - bufferSample->setType("vec4"); + bufferSample->setType("float4"); DecOp *bufferSampleDecl = new DecOp(bufferSample); meta->addStatement( new GenOp( "@(@, @)\r\n", methodDecl, prepassSamplerDecl, screenUVDecl ) ); @@ -204,9 +242,18 @@ Var* GBufferConditionerGLSL::printMethodHeader( MethodType methodType, const Str meta->addStatement( new GenOp( " // Sampler g-buffer\r\n" ) ); +#ifdef TORQUE_OS_XENON + meta->addStatement( new GenOp( " @;\r\n", bufferSampleDecl ) ); + meta->addStatement( new GenOp( " asm { tfetch2D @, @, @, MagFilter = point, MinFilter = point, MipFilter = point };\r\n", bufferSample, screenUV, prepassSampler ) ); +#else // The gbuffer has no mipmaps, so use tex2dlod when - // so that the shader compiler can optimize. - meta->addStatement( new GenOp( " @ = texture2DLod(@, @, 0.0);\r\n", bufferSampleDecl, prepassSampler, screenUV ) ); + // possible so that the shader compiler can optimize. + meta->addStatement( new GenOp( " #if TORQUE_SM >= 30\r\n" ) ); + meta->addStatement( new GenOp( " @ = tex2Dlod(@, float4(@,0,0));\r\n", bufferSampleDecl, prepassSampler, screenUV ) ); + meta->addStatement( new GenOp( " #else\r\n" ) ); + meta->addStatement( new GenOp( " @ = tex2D(@, @);\r\n", bufferSampleDecl, prepassSampler, screenUV ) ); + meta->addStatement( new GenOp( " #endif\r\n\r\n" ) ); +#endif // We don't use this way of passing var's around, so this should cause a crash // if something uses this improperly @@ -218,39 +265,67 @@ Var* GBufferConditionerGLSL::printMethodHeader( MethodType methodType, const Str GenOp* GBufferConditionerGLSL::_posnegEncode( GenOp *val ) { - return mCanWriteNegativeValues ? val : new GenOp("0.5 * (@ + 1.0)", val); + if(mNormalStorageType == LambertAzimuthal) + return mCanWriteNegativeValues ? val : new GenOp(avar("(%f * (@ + %f))", 1.0f/(M_SQRT2_F * 2.0f), M_SQRT2_F), val); + else + return mCanWriteNegativeValues ? val : new GenOp("(0.5 * (@ + 1.0))", val); } GenOp* GBufferConditionerGLSL::_posnegDecode( GenOp *val ) { - return mCanWriteNegativeValues ? val : new GenOp("@ * 2.0 - 1.0", val); + if(mNormalStorageType == LambertAzimuthal) + return mCanWriteNegativeValues ? val : new GenOp(avar("(@ * %f - %f)", M_SQRT2_F * 2.0f, M_SQRT2_F), val); + else + return mCanWriteNegativeValues ? val : new GenOp("(@ * 2.0 - 1.0)", val); } Var* GBufferConditionerGLSL::_conditionOutput( Var *unconditionedOutput, MultiLine *meta ) { Var *retVar = new Var; - retVar->setType("vec4"); + retVar->setType("float4"); retVar->setName("_gbConditionedOutput"); LangElement *outputDecl = new DecOp( retVar ); switch(mNormalStorageType) { case CartesianXYZ: - meta->addStatement( new GenOp( " // g-buffer conditioner: vec4(normal.xyz, depth)\r\n" ) ); - meta->addStatement( new GenOp( " @ = vec4(@, @.a);\r\n", outputDecl, + meta->addStatement( new GenOp( " // g-buffer conditioner: float4(normal.xyz, depth)\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4(@, @.a);\r\n", outputDecl, _posnegEncode(new GenOp("@.xyz", unconditionedOutput)), unconditionedOutput ) ); break; case CartesianXY: - meta->addStatement( new GenOp( " // g-buffer conditioner: vec4(normal.xy, depth Hi + z-sign, depth Lo)\r\n" ) ); - meta->addStatement( new GenOp( " @ = vec4(@, @.a);", outputDecl, - _posnegEncode(new GenOp("vec3(@.xy, sign(@.z))", unconditionedOutput, unconditionedOutput)), unconditionedOutput ) ); + meta->addStatement( new GenOp( " // g-buffer conditioner: float4(normal.xy, depth Hi + z-sign, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4(@, @.a);", outputDecl, + _posnegEncode(new GenOp("float3(@.xy, sign(@.z))", unconditionedOutput, unconditionedOutput)), unconditionedOutput ) ); break; case Spherical: - meta->addStatement( new GenOp( " // g-buffer conditioner: vec4(normal.theta, normal.phi, depth Hi, depth Lo)\r\n" ) ); - meta->addStatement( new GenOp( " @ = vec4(@, 0.0, @.a);\r\n", outputDecl, - _posnegEncode(new GenOp("vec2(atan2(@.y, @.x) / 3.14159265358979323846f, @.z)", unconditionedOutput, unconditionedOutput, unconditionedOutput ) ), + meta->addStatement( new GenOp( " // g-buffer conditioner: float4(normal.theta, normal.phi, depth Hi, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4(@, 0.0, @.a);\r\n", outputDecl, + _posnegEncode(new GenOp("float2(atan2(@.y, @.x) / 3.14159265358979323846f, @.z)", unconditionedOutput, unconditionedOutput, unconditionedOutput ) ), + unconditionedOutput ) ); + + // HACK: This fixes the noise present when using a floating point + // gbuffer on Geforce cards and the "flat areas unlit" issues. + // + // We need work around atan2() above to fix this issue correctly + // without the extra overhead of this test. + // + meta->addStatement( new GenOp( " if ( abs( dot( @.xyz, float3( 0.0, 0.0, 1.0 ) ) ) > 0.999f ) @ = float4( 0, 1 * sign( @.z ), 0, @.a );\r\n", + unconditionedOutput, retVar, unconditionedOutput, unconditionedOutput ) ); + break; + + case LambertAzimuthal: + //http://en.wikipedia.org/wiki/Lambert_azimuthal_equal-area_projection + // + // Note we're casting to half to use partial precision + // sqrt which is much faster on older Geforces while + // still being acceptable for normals. + // + meta->addStatement( new GenOp( " // g-buffer conditioner: float4(normal.X, normal.Y, depth Hi, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4(@, 0.0, @.a);\r\n", outputDecl, + _posnegEncode(new GenOp("sqrt(half(2.0/(1.0 - @.y))) * half2(@.xz)", unconditionedOutput, unconditionedOutput)), unconditionedOutput ) ); break; } @@ -259,11 +334,10 @@ Var* GBufferConditionerGLSL::_conditionOutput( Var *unconditionedOutput, MultiLi if(mNormalStorageType != CartesianXYZ) { const U64 maxValPerChannel = 1 << mBitsPerChannel; - const U64 extraVal = (maxValPerChannel * maxValPerChannel - 1) - (maxValPerChannel - 1) * 2; meta->addStatement( new GenOp( " \r\n // Encode depth into hi/lo\r\n" ) ); - meta->addStatement( new GenOp( avar( " vec3 _tempDepth = fract(@.a * vec3(1.0, %llu.0, %llu.0));\r\n", maxValPerChannel - 1, extraVal ), + meta->addStatement( new GenOp( avar( " float2 _tempDepth = frac(@.a * float2(1.0, %llu.0));\r\n", maxValPerChannel - 1 ), unconditionedOutput ) ); - meta->addStatement( new GenOp( avar( " @.zw = _tempDepth.xy - _tempDepth.yz * vec2(1.0/%llu.0, 1.0/%llu.0);\r\n\r\n", maxValPerChannel - 1, maxValPerChannel - 1 ), + meta->addStatement( new GenOp( avar( " @.zw = _tempDepth.xy - _tempDepth.yy * float2(1.0/%llu.0, 0.0);\r\n\r\n", maxValPerChannel - 1 ), retVar ) ); } @@ -274,33 +348,43 @@ Var* GBufferConditionerGLSL::_conditionOutput( Var *unconditionedOutput, MultiLi Var* GBufferConditionerGLSL::_unconditionInput( Var *conditionedInput, MultiLine *meta ) { Var *retVar = new Var; - retVar->setType("vec4"); + retVar->setType("float4"); retVar->setName("_gbUnconditionedInput"); LangElement *outputDecl = new DecOp( retVar ); switch(mNormalStorageType) { case CartesianXYZ: - meta->addStatement( new GenOp( " // g-buffer unconditioner: vec4(normal.xyz, depth)\r\n" ) ); - meta->addStatement( new GenOp( " @ = vec4(@, @.a);\r\n", outputDecl, + meta->addStatement( new GenOp( " // g-buffer unconditioner: float4(normal.xyz, depth)\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4(@, @.a);\r\n", outputDecl, _posnegDecode(new GenOp("@.xyz", conditionedInput)), conditionedInput ) ); break; case CartesianXY: - meta->addStatement( new GenOp( " // g-buffer unconditioner: vec4(normal.xy, depth Hi + z-sign, depth Lo)\r\n" ) ); - meta->addStatement( new GenOp( " @ = vec4(@, @.a);\r\n", outputDecl, + meta->addStatement( new GenOp( " // g-buffer unconditioner: float4(normal.xy, depth Hi + z-sign, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4(@, @.a);\r\n", outputDecl, _posnegDecode(new GenOp("@.xyz", conditionedInput)), conditionedInput ) ); meta->addStatement( new GenOp( " @.z *= sqrt(1.0 - dot(@.xy, @.xy));\r\n", retVar, retVar, retVar ) ); break; case Spherical: - meta->addStatement( new GenOp( " // g-buffer unconditioner: vec4(normal.theta, normal.phi, depth Hi, depth Lo)\r\n" ) ); - meta->addStatement( new GenOp( " vec2 spGPUAngles = @;\r\n", _posnegDecode(new GenOp("@.xy", conditionedInput)) ) ); - meta->addStatement( new GenOp( " vec2 sincosTheta;\r\n" ) ); - meta->addStatement( new GenOp( " sincosTheta.x = sin(spGPUAngles.x * 3.14159265358979323846);\r\n" ) ); - meta->addStatement( new GenOp( " sincosTheta.y = cos(spGPUAngles.x * 3.14159265358979323846);\r\n" ) ); - meta->addStatement( new GenOp( " vec2 sincosPhi = vec2(sqrt(1.0 - spGPUAngles.y * spGPUAngles.y), spGPUAngles.y);\r\n" ) ); - meta->addStatement( new GenOp( " @ = vec4(sincosTheta.y * sincosPhi.x, sincosTheta.x * sincosPhi.x, sincosPhi.y, @.a);\r\n", outputDecl, conditionedInput ) ); + meta->addStatement( new GenOp( " // g-buffer unconditioner: float4(normal.theta, normal.phi, depth Hi, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " float2 spGPUAngles = @;\r\n", _posnegDecode(new GenOp("@.xy", conditionedInput)) ) ); + meta->addStatement( new GenOp( " float2 sincosTheta;\r\n" ) ); + meta->addStatement( new GenOp( " sincos(spGPUAngles.x * 3.14159265358979323846f, sincosTheta.x, sincosTheta.y);\r\n" ) ); + meta->addStatement( new GenOp( " float2 sincosPhi = float2(sqrt(1.0 - spGPUAngles.y * spGPUAngles.y), spGPUAngles.y);\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4(sincosTheta.y * sincosPhi.x, sincosTheta.x * sincosPhi.x, sincosPhi.y, @.a);\r\n", outputDecl, conditionedInput ) ); + break; + + case LambertAzimuthal: + // Note we're casting to half to use partial precision + // sqrt which is much faster on older Geforces while + // still being acceptable for normals. + // + meta->addStatement( new GenOp( " // g-buffer unconditioner: float4(normal.X, normal.Y, depth Hi, depth Lo)\r\n" ) ); + meta->addStatement( new GenOp( " float2 _inpXY = @;\r\n", _posnegDecode(new GenOp("@.xy", conditionedInput)) ) ); + meta->addStatement( new GenOp( " float _xySQ = dot(_inpXY, _inpXY);\r\n" ) ); + meta->addStatement( new GenOp( " @ = float4( sqrt(half(1.0 - (_xySQ / 4.0))) * _inpXY, -1.0 + (_xySQ / 2.0), @.a).xzyw;\r\n", outputDecl, conditionedInput ) ); break; } @@ -309,7 +393,7 @@ Var* GBufferConditionerGLSL::_unconditionInput( Var *conditionedInput, MultiLine { const U64 maxValPerChannel = 1 << mBitsPerChannel; meta->addStatement( new GenOp( " \r\n // Decode depth\r\n" ) ); - meta->addStatement( new GenOp( avar( " @.w = dot( @.zw, vec2(1.0, 1.0/%llu.0));\r\n", maxValPerChannel - 1 ), + meta->addStatement( new GenOp( avar( " @.w = dot( @.zw, float2(1.0, 1.0/%llu.0));\r\n", maxValPerChannel - 1 ), retVar, conditionedInput ) ); } diff --git a/Engine/source/lighting/advanced/glsl/gBufferConditionerGLSL.h b/Engine/source/lighting/advanced/glsl/gBufferConditionerGLSL.h index 9e9364c88..640891824 100644 --- a/Engine/source/lighting/advanced/glsl/gBufferConditionerGLSL.h +++ b/Engine/source/lighting/advanced/glsl/gBufferConditionerGLSL.h @@ -42,6 +42,13 @@ public: CartesianXYZ, CartesianXY, Spherical, + LambertAzimuthal, + }; + + enum NormalSpace + { + WorldSpace, + ViewSpace, }; protected: @@ -52,7 +59,7 @@ protected: public: - GBufferConditionerGLSL( const GFXFormat bufferFormat ); + GBufferConditionerGLSL( const GFXFormat bufferFormat, const NormalSpace nrmSpace ); virtual ~GBufferConditionerGLSL(); diff --git a/Engine/source/shaderGen/GLSL/bumpGLSL.cpp b/Engine/source/shaderGen/GLSL/bumpGLSL.cpp index 737ddcbaf..1b1cd0437 100644 --- a/Engine/source/shaderGen/GLSL/bumpGLSL.cpp +++ b/Engine/source/shaderGen/GLSL/bumpGLSL.cpp @@ -64,14 +64,14 @@ void BumpFeatGLSL::processPix( Vector &componentList, output = meta; // Get the texture coord. - Var *texCoord = getInTexCoord( "out_texCoord", "vec2", true, componentList ); + Var *texCoord = getInTexCoord( "texCoord", "vec2", true, componentList ); // Sample the bumpmap. Var *bumpMap = getNormalMapTex(); - LangElement *texOp = NULL; //Handle atlased textures + // http://www.infinity-universe.com/Infinity/index.php?option=com_content&task=view&id=65&Itemid=47 if(fd.features[MFT_NormalMapAtlas]) { // This is a big block of code, so put a comment in the shader code @@ -79,52 +79,49 @@ void BumpFeatGLSL::processPix( Vector &componentList, Var *atlasedTex = new Var; atlasedTex->setName("atlasedBumpCoord"); - atlasedTex->setType("vec2"); + atlasedTex->setType( "vec2" ); LangElement *atDecl = new DecOp(atlasedTex); // Parameters of the texture atlas Var *atParams = new Var; - atParams->setType("vec4"); + atParams->setType( "float4" ); atParams->setName("bumpAtlasParams"); atParams->uniform = true; atParams->constSortPos = cspPotentialPrimitive; // Parameters of the texture (tile) this object is using in the atlas Var *tileParams = new Var; - tileParams->setType("vec4"); + tileParams->setType( "float4" ); tileParams->setName("bumpAtlasTileParams"); tileParams->uniform = true; tileParams->constSortPos = cspPotentialPrimitive; const bool is_sm3 = (GFX->getPixelShaderVersion() > 2.0f); - // getPixelShaderVersion() on Mac currently returns 2.0, - // or 3.0 if Advanced Lighting is enabled if(is_sm3) { // Figure out the mip level - meta->addStatement(new GenOp(" vec2 _dx_bump = dFdx(@ * @.z);\r\n", texCoord, atParams)); - meta->addStatement(new GenOp(" vec2 _dy_bump = dFdy(@ * @.z);\r\n", texCoord, atParams)); - meta->addStatement(new GenOp(" float mipLod_bump = 0.5 * log2(max(dot(_dx_bump, _dx_bump), dot(_dy_bump, _dy_bump)));\r\n")); - meta->addStatement(new GenOp(" mipLod_bump = clamp(mipLod_bump, 0.0, @.w);\r\n", atParams)); + meta->addStatement( new GenOp( " float2 _dx_bump = ddx(@ * @.z);\r\n", texCoord, atParams ) ); + meta->addStatement( new GenOp( " float2 _dy_bump = ddy(@ * @.z);\r\n", texCoord, atParams ) ); + meta->addStatement( new GenOp( " float mipLod_bump = 0.5 * log2(max(dot(_dx_bump, _dx_bump), dot(_dy_bump, _dy_bump)));\r\n")); + meta->addStatement( new GenOp( " mipLod_bump = clamp(mipLod_bump, 0.0, @.w);\r\n", atParams)); // And the size of the mip level meta->addStatement(new GenOp(" float mipPixSz_bump = pow(2.0, @.w - mipLod_bump);\r\n", atParams)); - meta->addStatement(new GenOp(" vec2 mipSz_bump = mipPixSz_bump / @.xy;\r\n", atParams)); + meta->addStatement( new GenOp( " float2 mipSz_bump = mipPixSz_bump / @.xy;\r\n", atParams ) ); } else { - meta->addStatement(new GenOp(" vec2 mipSz = float2(1.0, 1.0);\r\n")); + meta->addStatement(new GenOp(" float2 mipSz = float2(1.0, 1.0);\r\n")); } // Tiling mode - // TODO: Select wrap or clamp somehow if( true ) // Wrap - meta->addStatement(new GenOp(" @ = fract(@);\r\n", atDecl, texCoord)); + meta->addStatement( new GenOp( " @ = frac(@);\r\n", atDecl, texCoord ) ); else // Clamp meta->addStatement(new GenOp(" @ = saturate(@);\r\n", atDecl, texCoord)); // Finally scale/offset, and correct for filtering - meta->addStatement(new GenOp(" @ = @ * ((mipSz_bump * @.xy - 1.0) / mipSz_bump) + 0.5 / mipSz_bump + @.xy * @.xy;\r\n", + meta->addStatement( new GenOp( " @ = @ * ((mipSz_bump * @.xy - 1.0) / mipSz_bump) + 0.5 / mipSz_bump + @.xy * @.xy;\r\n", atlasedTex, atlasedTex, atParams, atParams, tileParams)); // Add a newline @@ -132,19 +129,19 @@ void BumpFeatGLSL::processPix( Vector &componentList, if(is_sm3) { - texOp = new GenOp( "texture2DLod(@, vec4(@, 0.0, mipLod_bump)", bumpMap, texCoord ); + texOp = new GenOp( "tex2Dlod(@, float4(@, 0.0, mipLod_bump))", bumpMap, texCoord ); } else { - texOp = new GenOp( "texture2D(@, @)", bumpMap, texCoord ); + texOp = new GenOp( "tex2D(@, @)", bumpMap, texCoord ); } } else { - texOp = new GenOp( "texture2D(@, @)", bumpMap, texCoord ); + texOp = new GenOp( "tex2D(@, @)", bumpMap, texCoord ); } - Var *bumpNorm = new Var( "bumpNormal", "vec4" ); + Var *bumpNorm = new Var( "bumpNormal", "float4" ); meta->addStatement( expandNormalMap( texOp, new DecOp( bumpNorm ), bumpNorm, fd ) ); // If we have a detail normal map we add the xy coords of @@ -160,11 +157,11 @@ void BumpFeatGLSL::processPix( Vector &componentList, bumpMap->constNum = Var::getTexUnitNum(); texCoord = getInTexCoord( "detCoord", "vec2", true, componentList ); - texOp = new GenOp( "texture2D(@, @)", bumpMap, texCoord ); + texOp = new GenOp( "tex2D(@, @)", bumpMap, texCoord ); Var *detailBump = new Var; detailBump->setName( "detailBump" ); - detailBump->setType( "vec4" ); + detailBump->setType( "float4" ); meta->addStatement( expandNormalMap( texOp, new DecOp( detailBump ), detailBump, fd ) ); Var *detailBumpScale = new Var; @@ -175,13 +172,11 @@ void BumpFeatGLSL::processPix( Vector &componentList, meta->addStatement( new GenOp( " @.xy += @.xy * @;\r\n", bumpNorm, detailBump, detailBumpScale ) ); } - // We transform it into world space by reversing the // multiplication by the worldToTanget transform. Var *wsNormal = new Var( "wsNormal", "vec3" ); Var *worldToTanget = getInWorldToTangent( componentList ); - meta->addStatement( new GenOp( " @ = normalize( vec3( @.xyz * @ ) );\r\n", new DecOp( wsNormal ), bumpNorm, worldToTanget ) ); - + meta->addStatement( new GenOp( " @ = normalize( tMul( @.xyz, @ ) );\r\n", new DecOp( wsNormal ), bumpNorm, worldToTanget ) ); } ShaderFeature::Resources BumpFeatGLSL::getResources( const MaterialFeatureData &fd ) @@ -227,20 +222,26 @@ void BumpFeatGLSL::setTexData( Material::StageData &stageDat, if ( fd.features[MFT_NormalMap] ) { passData.mTexType[ texIndex ] = Material::Bump; + passData.mSamplerNames[ texIndex ] = "bumpMap"; passData.mTexSlot[ texIndex++ ].texObject = stageDat.getTex( MFT_NormalMap ); } - if ( fd.features[ MFT_DetailNormalMap ] ) { passData.mTexType[ texIndex ] = Material::DetailBump; + passData.mSamplerNames[ texIndex ] = "detailBumpMap"; passData.mTexSlot[ texIndex++ ].texObject = stageDat.getTex( MFT_DetailNormalMap ); } } -// -Var* ParallaxFeatGLSL::_getUniformVar( const char *name, const char *type ) +ParallaxFeatGLSL::ParallaxFeatGLSL() + : mIncludeDep( "shaders/common/gl/torque.glsl" ) +{ + addDependency( &mIncludeDep ); +} + +Var* ParallaxFeatGLSL::_getUniformVar( const char *name, const char *type, ConstantSortPosition csp ) { Var *theVar = (Var*)LangElement::find( name ); if ( !theVar ) @@ -249,7 +250,7 @@ Var* ParallaxFeatGLSL::_getUniformVar( const char *name, const char *type ) theVar->setType( type ); theVar->setName( name ); theVar->uniform = true; - theVar->constSortPos = cspPass; + theVar->constSortPos = csp; } return theVar; @@ -259,13 +260,13 @@ void ParallaxFeatGLSL::processVert( Vector &componentList, const MaterialFeatureData &fd ) { AssertFatal( GFX->getPixelShaderVersion() >= 2.0, - "ParallaxFeatGLSL::processVert - We don't support SM 1.x!" ); + "ParallaxFeatGLSL::processVert - We don't support SM 1.x!" ); MultiLine *meta = new MultiLine; // Add the texture coords. getOutTexCoord( "texCoord", - "vec2", + "vec2", true, fd.features[MFT_TexAnim], meta, @@ -276,18 +277,36 @@ void ParallaxFeatGLSL::processVert( Vector &componentList, if ( !inPos ) inPos = (Var*)LangElement::find( "position" ); - // Get the object space eye position and the world - // to tangent transform. - Var *eyePos = _getUniformVar( "eyePos", "vec3" ); + // Get the object space eye position and the + // object to tangent space transform. + Var *eyePos = _getUniformVar( "eyePos", "vec3", cspPrimitive ); Var *objToTangentSpace = getOutObjToTangentSpace( componentList, meta, fd ); - // send transform to pixel shader + // Now send the negative view vector in tangent space to the pixel shader. ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); - Var *outViewTS = connectComp->getElement( RT_TEXCOORD, 1 ); - outViewTS->setName( "outViewTS" ); - outViewTS->setType( "vec3" ); - meta->addStatement( new GenOp( " @ = ( @ - @.xyz ) * transpose( @ );\r\n", - outViewTS, inPos, eyePos, objToTangentSpace ) ); + Var *outNegViewTS = connectComp->getElement( RT_TEXCOORD ); + outNegViewTS->setName( "outNegViewTS" ); + outNegViewTS->setStructName( "OUT" ); + outNegViewTS->setType( "vec3" ); + meta->addStatement( new GenOp( " @ = tMul( @, float3( @.xyz - @ ) );\r\n", + outNegViewTS, objToTangentSpace, inPos, eyePos ) ); + + // TODO: I'm at a loss at why i need to flip the binormal/y coord + // to get a good view vector for parallax. Lighting works properly + // with the TS matrix as is... but parallax does not. + // + // Someone figure this out! + // + meta->addStatement( new GenOp( " @.y = -@.y;\r\n", outNegViewTS, outNegViewTS ) ); + + // If we have texture anim matrix the tangent + // space view vector may need to be rotated. + Var *texMat = (Var*)LangElement::find( "texMat" ); + if ( texMat ) + { + meta->addStatement( new GenOp( " @ = tMul(@, float4(@,0)).xyz;\r\n", + outNegViewTS, texMat, outNegViewTS ) ); + } output = meta; } @@ -296,7 +315,7 @@ void ParallaxFeatGLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) { AssertFatal( GFX->getPixelShaderVersion() >= 2.0, - "ParallaxFeatGLSL::processPix - We don't support SM 1.x!" ); + "ParallaxFeatGLSL::processPix - We don't support SM 1.x!" ); MultiLine *meta = new MultiLine; @@ -310,38 +329,28 @@ void ParallaxFeatGLSL::processPix( Vector &componentList, Var *negViewTS = (Var*)LangElement::find( "negViewTS" ); if ( !negViewTS ) { - Var *inViewTS = (Var*)LangElement::find( "outViewTS" ); - if ( !inViewTS ) + Var *inNegViewTS = (Var*)LangElement::find( "outNegViewTS" ); + if ( !inNegViewTS ) { - inViewTS = connectComp->getElement( RT_TEXCOORD, 1 ); - inViewTS->setName( "outViewTS" ); - inViewTS->setType( "vec3" ); + inNegViewTS = connectComp->getElement( RT_TEXCOORD ); + inNegViewTS->setName( "outNegViewTS" ); + inNegViewTS->setStructName( "IN" ); + inNegViewTS->setType( "vec3" ); } negViewTS = new Var( "negViewTS", "vec3" ); - meta->addStatement( new GenOp( " @ = -normalize( @ );\r\n", new DecOp( negViewTS ), inViewTS ) ); + meta->addStatement( new GenOp( " @ = normalize( @ );\r\n", new DecOp( negViewTS ), inNegViewTS ) ); } // Get the rest of our inputs. - Var *parallaxInfo = _getUniformVar( "parallaxInfo", "float" ); + Var *parallaxInfo = _getUniformVar( "parallaxInfo", "float", cspPotentialPrimitive ); Var *normalMap = getNormalMapTex(); - // Do 3 parallax samples to get acceptable - // quality without too much overhead. - Var *pdepth = findOrCreateLocal( "pdepth", "float", meta ); - Var *poffset = findOrCreateLocal( "poffset", "vec2", meta ); - meta->addStatement( new GenOp( " @ = texture2D( @, @.xy ).a;\r\n", pdepth, normalMap, texCoord ) ); - meta->addStatement( new GenOp( " @ = @.xy * ( @ * @ );\r\n", poffset, negViewTS, pdepth, parallaxInfo ) ); - - meta->addStatement( new GenOp( " @ = ( @ + texture2D( @, @.xy + @ ).a ) * 0.5;\r\n", pdepth, pdepth, normalMap, texCoord, poffset ) ); - meta->addStatement( new GenOp( " @ = @.xy * ( @ * @ );\r\n", poffset, negViewTS, pdepth, parallaxInfo ) ); - - meta->addStatement( new GenOp( " @ = ( @ + texture2D( @, @.xy + @ ).a ) * 0.5;\r\n", pdepth, pdepth, normalMap, texCoord, poffset ) ); - meta->addStatement( new GenOp( " @ = @.xy * ( @ * @ );\r\n", poffset, negViewTS, pdepth, parallaxInfo ) ); - - meta->addStatement( new GenOp( " @.xy += @;\r\n", texCoord, poffset ) ); + // Call the library function to do the rest. + meta->addStatement( new GenOp( " @.xy += parallaxOffset( @, @.xy, @, @ );\r\n", + texCoord, normalMap, texCoord, negViewTS, parallaxInfo ) ); - // TODO: Fix second UV. + // TODO: Fix second UV maybe? output = meta; } @@ -349,7 +358,7 @@ void ParallaxFeatGLSL::processPix( Vector &componentList, ShaderFeature::Resources ParallaxFeatGLSL::getResources( const MaterialFeatureData &fd ) { AssertFatal( GFX->getPixelShaderVersion() >= 2.0, - "ParallaxFeatGLSL::getResources - We don't support SM 1.x!" ); + "ParallaxFeatGLSL::getResources - We don't support SM 1.x!" ); Resources res; @@ -370,7 +379,7 @@ void ParallaxFeatGLSL::setTexData( Material::StageData &stageDat, U32 &texIndex ) { AssertFatal( GFX->getPixelShaderVersion() >= 2.0, - "ParallaxFeatGLSL::setTexData - We don't support SM 1.x!" ); + "ParallaxFeatGLSL::setTexData - We don't support SM 1.x!" ); GFXTextureObject *tex = stageDat.getTex( MFT_NormalMap ); if ( tex ) @@ -381,7 +390,6 @@ void ParallaxFeatGLSL::setTexData( Material::StageData &stageDat, } -// void NormalsOutFeatGLSL::processVert( Vector &componentList, const MaterialFeatureData &fd ) { @@ -397,6 +405,7 @@ void NormalsOutFeatGLSL::processVert( Vector &componentList, Var *outNormal = connectComp->getElement( RT_TEXCOORD ); outNormal->setName( "wsNormal" ); + outNormal->setStructName( "OUT" ); outNormal->setType( "vec3" ); outNormal->mapsToSampler = false; @@ -406,13 +415,13 @@ void NormalsOutFeatGLSL::processVert( Vector &componentList, { // Transform the normal to world space. Var *objTrans = getObjTrans( componentList, fd.features[MFT_UseInstancing], meta ); - meta->addStatement( new GenOp( " @ = @ * normalize( @ );\r\n", outNormal, objTrans, inNormal ) ); + meta->addStatement( new GenOp( " @ = tMul( @, normalize( vec4(@, 0.0) ) ).xyz;\r\n", outNormal, objTrans, inNormal ) ); } else { // If we don't have a vertex normal... just pass the // camera facing normal to the pixel shader. - meta->addStatement( new GenOp( " @ = vec3( 0.0, 0.0, 1.0 );\r\n", outNormal ) ); + meta->addStatement( new GenOp( " @ = float3( 0.0, 0.0, 1.0 );\r\n", outNormal ) ); } } @@ -428,20 +437,26 @@ void NormalsOutFeatGLSL::processPix( Vector &componentList, ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); wsNormal = connectComp->getElement( RT_TEXCOORD ); wsNormal->setName( "wsNormal" ); + wsNormal->setStructName( "IN" ); wsNormal->setType( "vec3" ); // If we loaded the normal its our resposibility // to normalize it... the interpolators won't. // - meta->addStatement( new GenOp( " @ = normalize( @ );\r\n", wsNormal, wsNormal ) ); + // Note we cast to half here to get partial precision + // optimized code which is an acceptable loss of + // precision for normals and performs much better + // on older Geforce cards. + // + meta->addStatement( new GenOp( " @ = normalize( half3( @ ) );\r\n", wsNormal, wsNormal ) ); } LangElement *normalOut; Var *outColor = (Var*)LangElement::find( "col" ); - if ( outColor ) - normalOut = new GenOp( "vec4( ( -@ + 1 ) * 0.5, @.a )", wsNormal, outColor ); + if ( outColor && !fd.features[MFT_AlphaTest] ) + normalOut = new GenOp( "float4( ( -@ + 1 ) * 0.5, @.a )", wsNormal, outColor ); else - normalOut = new GenOp( "vec4( ( -@ + 1 ) * 0.5, 1 )", wsNormal ); + normalOut = new GenOp( "float4( ( -@ + 1 ) * 0.5, 1 )", wsNormal ); meta->addStatement( new GenOp( " @;\r\n", assignColor( normalOut, Material::None ) ) ); diff --git a/Engine/source/shaderGen/GLSL/bumpGLSL.h b/Engine/source/shaderGen/GLSL/bumpGLSL.h index 899434f15..b37bb9099 100644 --- a/Engine/source/shaderGen/GLSL/bumpGLSL.h +++ b/Engine/source/shaderGen/GLSL/bumpGLSL.h @@ -26,6 +26,9 @@ #ifndef _SHADERGEN_GLSL_SHADERFEATUREGLSL_H_ #include "shaderGen/GLSL/shaderFeatureGLSL.h" #endif +#ifndef _LANG_ELEMENT_H_ +#include "shaderGen/langElement.h" +#endif struct RenderPassData; class MultiLine; @@ -50,7 +53,6 @@ public: const MaterialFeatureData &fd, RenderPassData &passData, U32 &texIndex ); - virtual String getName() { return "Bumpmap"; } }; @@ -62,10 +64,16 @@ class ParallaxFeatGLSL : public ShaderFeatureGLSL { protected: - static Var* _getUniformVar( const char *name, const char *type ); + static Var* _getUniformVar( const char *name, + const char *type, + ConstantSortPosition csp ); + + ShaderIncludeDependency mIncludeDep; public: + ParallaxFeatGLSL(); + // ShaderFeatureGLSL virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); @@ -80,7 +88,6 @@ public: }; - /// This feature is used to render normals to the /// diffuse target for imposter rendering. class NormalsOutFeatGLSL : public ShaderFeatureGLSL diff --git a/Engine/source/shaderGen/GLSL/depthGLSL.cpp b/Engine/source/shaderGen/GLSL/depthGLSL.cpp index 4c5f1b837..71bdbb96e 100644 --- a/Engine/source/shaderGen/GLSL/depthGLSL.cpp +++ b/Engine/source/shaderGen/GLSL/depthGLSL.cpp @@ -24,36 +24,36 @@ #include "shaderGen/GLSL/depthGLSL.h" #include "materials/materialFeatureTypes.h" +#include "materials/materialFeatureData.h" void EyeSpaceDepthOutGLSL::processVert( Vector &componentList, const MaterialFeatureData &fd ) { - - MultiLine *meta = new MultiLine; + MultiLine *meta = new MultiLine; output = meta; - // grab output + // grab output ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *outWSEyeVec = connectComp->getElement( RT_TEXCOORD ); - outWSEyeVec->setName( "outWSEyeVec" ); - - - // grab incoming vert position - Var *wsPosition = new Var( "depthPos", "vec3" ); + outWSEyeVec->setName( "wsEyeVec" ); + outWSEyeVec->setStructName( "OUT" ); + + // grab incoming vert position + Var *wsPosition = new Var( "depthPos", "float3" ); getWsPosition( componentList, fd.features[MFT_UseInstancing], meta, new DecOp( wsPosition ) ); Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); if( !eyePos ) { eyePos = new Var; - eyePos->setType("vec3"); + eyePos->setType("float3"); eyePos->setName("eyePosWorld"); eyePos->uniform = true; eyePos->constSortPos = cspPass; } -meta->addStatement( new GenOp( " @ = vec4( @.xyz - @, 1 );\r\n", outWSEyeVec, wsPosition, eyePos ) ); + meta->addStatement( new GenOp( " @ = float4( @.xyz - @, 1 );\r\n", outWSEyeVec, wsPosition, eyePos ) ); } void EyeSpaceDepthOutGLSL::processPix( Vector &componentList, @@ -64,14 +64,15 @@ void EyeSpaceDepthOutGLSL::processPix( Vector &componentList, // grab connector position ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *wsEyeVec = connectComp->getElement( RT_TEXCOORD ); - wsEyeVec->setName( "outWSEyeVec" ); - wsEyeVec->setType( "vec4" ); + wsEyeVec->setName( "wsEyeVec" ); + wsEyeVec->setStructName( "IN" ); + wsEyeVec->setType( "float4" ); wsEyeVec->mapsToSampler = false; wsEyeVec->uniform = false; // get shader constants Var *vEye = new Var; - vEye->setType("vec3"); + vEye->setType("float3"); vEye->setName("vEye"); vEye->uniform = true; vEye->constSortPos = cspPass; @@ -83,12 +84,27 @@ void EyeSpaceDepthOutGLSL::processPix( Vector &componentList, LangElement *depthOutDecl = new DecOp( depthOut ); + meta->addStatement( new GenOp( "#ifndef CUBE_SHADOW_MAP\r\n" ) ); meta->addStatement( new GenOp( " @ = dot(@, (@.xyz / @.w));\r\n", depthOutDecl, vEye, wsEyeVec, wsEyeVec ) ); + meta->addStatement( new GenOp( "#else\r\n" ) ); + + Var *farDist = (Var*)Var::find( "oneOverFarplane" ); + if ( !farDist ) + { + farDist = new Var; + farDist->setType("float4"); + farDist->setName("oneOverFarplane"); + farDist->uniform = true; + farDist->constSortPos = cspPass; + } + + meta->addStatement( new GenOp( " @ = length( @.xyz / @.w ) * @.x;\r\n", depthOutDecl, wsEyeVec, wsEyeVec, farDist ) ); + meta->addStatement( new GenOp( "#endif\r\n" ) ); // If there isn't an output conditioner for the pre-pass, than just write // out the depth to rgba and return. if( !fd.features[MFT_PrePassConditioner] ) - meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "vec4(@)", depthOut ), Material::None ) ) ); + meta->addStatement( new GenOp( " @;\r\n", assignColor( new GenOp( "float4(float3(@),1)", depthOut ), Material::None ) ) ); output = meta; } @@ -111,11 +127,12 @@ void DepthOutGLSL::processVert( Vector &componentList, ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); // Grab the output vert. - Var *outPosition = (Var*)LangElement::find( "gl_Position" ); + Var *outPosition = (Var*)LangElement::find( "gl_Position" ); //hpos // Grab our output depth. Var *outDepth = connectComp->getElement( RT_TEXCOORD ); - outDepth->setName( "outDepth" ); + outDepth->setName( "depth" ); + outDepth->setStructName( "OUT" ); outDepth->setType( "float" ); output = new GenOp( " @ = @.z / @.w;\r\n", outDepth, outPosition, outPosition ); @@ -128,7 +145,8 @@ void DepthOutGLSL::processPix( Vector &componentList, // grab connector position Var *depthVar = connectComp->getElement( RT_TEXCOORD ); - depthVar->setName( "outDepth" ); + depthVar->setName( "depth" ); + depthVar->setStructName( "IN" ); depthVar->setType( "float" ); depthVar->mapsToSampler = false; depthVar->uniform = false; @@ -140,7 +158,7 @@ void DepthOutGLSL::processPix( Vector &componentList, depthOut->setName(getOutputVarName()); */ - LangElement *depthOut = new GenOp( "vec4( @, @ * @, 0, 1 )", depthVar, depthVar, depthVar ); + LangElement *depthOut = new GenOp( "float4( @, 0, 0, 1 )", depthVar ); output = new GenOp( " @;\r\n", assignColor( depthOut, Material::None ) ); } diff --git a/Engine/source/shaderGen/GLSL/depthGLSL.h b/Engine/source/shaderGen/GLSL/depthGLSL.h index 97cb62095..c92326415 100644 --- a/Engine/source/shaderGen/GLSL/depthGLSL.h +++ b/Engine/source/shaderGen/GLSL/depthGLSL.h @@ -54,7 +54,7 @@ public: virtual Resources getResources( const MaterialFeatureData &fd ); virtual String getName() { return "Depth (Out)"; } virtual Material::BlendOp getBlendOp() { return Material::None; } - virtual const char* getOutputVarName() const { return "outDepth"; } + virtual const char* getOutputVarName() const { return "IN_depth"; } }; #endif // _DEPTH_GLSL_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/GLSL/paraboloidGLSL.cpp b/Engine/source/shaderGen/GLSL/paraboloidGLSL.cpp index 373712c36..61e56d215 100644 --- a/Engine/source/shaderGen/GLSL/paraboloidGLSL.cpp +++ b/Engine/source/shaderGen/GLSL/paraboloidGLSL.cpp @@ -65,7 +65,7 @@ void ParaboloidVertTransformGLSL::processVert( Vector &compon // http://www.gamedev.net/reference/articles/article2308.asp // Swizzle z and y post-transform - meta->addStatement( new GenOp( " @ = vec4(@ * vec4(@.xyz,1)).xzyw;\r\n", outPosition, worldViewOnly, inPosition ) ); + meta->addStatement( new GenOp( " @ = tMul(@, float4(@.xyz,1)).xzyw;\r\n", outPosition, worldViewOnly, inPosition ) ); meta->addStatement( new GenOp( " float L = length(@.xyz);\r\n", outPosition ) ); if ( isSinglePass ) @@ -73,7 +73,8 @@ void ParaboloidVertTransformGLSL::processVert( Vector &compon // Flip the z in the back case Var *outIsBack = connectComp->getElement( RT_TEXCOORD ); outIsBack->setType( "float" ); - outIsBack->setName( "outIsBack" ); + outIsBack->setName( "isBack" ); + outIsBack->setStructName( "OUT" ); meta->addStatement( new GenOp( " bool isBack = @.z < 0.0;\r\n", outPosition ) ); meta->addStatement( new GenOp( " @ = isBack ? -1.0 : 1.0;\r\n", outIsBack ) ); @@ -94,15 +95,16 @@ void ParaboloidVertTransformGLSL::processVert( Vector &compon // TODO: If we change other shadow shaders to write out // linear depth, than fix this as well! // - // (L - 1.0)/(lightParams.x - 1.0); + // (L - zNear)/(lightParams.x - zNear); // meta->addStatement( new GenOp( " @.z = L / @.x;\r\n", outPosition, lightParams ) ); meta->addStatement( new GenOp( " @.w = 1.0;\r\n", outPosition ) ); // Pass unmodified to pixel shader to allow it to clip properly. Var *outPosXY = connectComp->getElement( RT_TEXCOORD ); - outPosXY->setType( "vec2" ); - outPosXY->setName( "outPosXY" ); + outPosXY->setType( "float2" ); + outPosXY->setName( "posXY" ); + outPosXY->setStructName( "OUT" ); meta->addStatement( new GenOp( " @ = @.xy;\r\n", outPosXY, outPosition ) ); // Scale and offset so it shows up in the atlas properly. @@ -136,16 +138,18 @@ void ParaboloidVertTransformGLSL::processPix( Vector &compon { // Cull things on the back side of the map. Var *isBack = connectComp->getElement( RT_TEXCOORD ); - isBack->setName( "outIsBack" ); + isBack->setName( "isBack" ); + isBack->setStructName( "IN" ); isBack->setType( "float" ); meta->addStatement( new GenOp( " if ( ( abs( @ ) - 0.999 ) < 0 ) discard;\r\n", isBack ) ); } // Cull pixels outside of the valid paraboloid. Var *posXY = connectComp->getElement( RT_TEXCOORD ); - posXY->setName( "outPosXY" ); - posXY->setType( "vec2" ); - meta->addStatement( new GenOp( " if ( ( 1.0 - length( @ ) ) < 0 ) discard;\r\n", posXY ) ); + posXY->setName( "posXY" ); + posXY->setStructName( "IN" ); + posXY->setType( "float2" ); + meta->addStatement( new GenOp( " clip( 1.0 - abs(@.x) );\r\n", posXY ) ); output = meta; } diff --git a/Engine/source/shaderGen/GLSL/pixSpecularGLSL.cpp b/Engine/source/shaderGen/GLSL/pixSpecularGLSL.cpp index ac313a103..4e34e2116 100644 --- a/Engine/source/shaderGen/GLSL/pixSpecularGLSL.cpp +++ b/Engine/source/shaderGen/GLSL/pixSpecularGLSL.cpp @@ -38,98 +38,35 @@ PixelSpecularGLSL::PixelSpecularGLSL() void PixelSpecularGLSL::processVert( Vector &componentList, const MaterialFeatureData &fd ) { - /* AssertFatal( fd.features[MFT_RTLighting], "PixelSpecularHLSL requires RTLighting to be enabled!" ); - MultiLine *meta = new MultiLine; - - // Get the eye world position. - Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); - if( !eyePos ) - { - eyePos = new Var; - eyePos->setType( "float3" ); - eyePos->setName( "eyePosWorld" ); - eyePos->uniform = true; - eyePos->constSortPos = cspPass; - } - - // Grab a register for passing the - // world space view vector. - ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); - Var *wsView = connectComp->getElement( RT_TEXCOORD ); - wsView->setName( "wsView" ); - wsView->setStructName( "OUT" ); - wsView->setType( "float3" ); - - // Get the input position. - Var *position = (Var*)LangElement::find( "inPosition" ); - if ( !position ) - position = (Var*)LangElement::find( "position" ); - - // Get the object to world transform. - Var *objTrans = (Var*) LangElement::find( "objTrans" ); - if ( !objTrans ) - { - objTrans = new Var; - objTrans->setType( "float4x4" ); - objTrans->setName( "objTrans" ); - objTrans->uniform = true; - objTrans->constSortPos = cspPrimitive; - } - - meta->addStatement( new GenOp( " @ = @ - mul( @, float4( @.xyz,1 ) ).xyz;\r\n", - wsView, eyePos, objTrans, position ) ); - - output = meta; - */ + // Nothing to do here... MFT_RTLighting should have + // taken care of passing everything to the pixel shader. } void PixelSpecularGLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) -{ - /* +{ AssertFatal( fd.features[MFT_RTLighting], "PixelSpecularHLSL requires RTLighting to be enabled!" ); - ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + // RTLighting should have spit out the 4 specular + // powers for the 4 potential lights on this pass. + // + // This can sometimes be NULL if RTLighting skips out + // on us for lightmaps or missing normals. + Var *specular = (Var*)LangElement::find( "specular" ); + if ( !specular ) + return; MultiLine *meta = new MultiLine; - // Get the normal and light vectors from which the - // RTLighting feature should have already setup. - Var *wsNormal = (Var*)LangElement::find( "wsNormal" ); - Var *inLightVec = (Var*)LangElement::find( "inLightVec" ); - - // Grab the world space position to eye vector. - Var *wsView = connectComp->getElement( RT_TEXCOORD ); - wsView->setName( "wsView" ); - wsView->setStructName( "IN" ); - wsView->setType( "float3" ); - - // Get the specular power and color. - Var *specPow = new Var( "specularPower", "float" ); - specPow->uniform = true; - specPow->constSortPos = cspPass; - Var *specCol = (Var*)LangElement::find("specularColor"); - if(specCol == NULL) - { - specCol = new Var( "specularColor", "vec4" ); - specCol->uniform = true; - specCol->constSortPos = cspPass; - } - - // Calcuate the specular factor. - Var *specular = new Var( "specular", "float" ); - meta->addStatement( new GenOp( " @ = calcSpecular( -@, normalize( @ ), normalize( @ ), @ );\r\n", - new DecOp( specular ), inLightVec, wsNormal, wsView, specPow ) ); - - LangElement *specMul = new GenOp( "float4(@.rgb,0) * @", specCol, specular ); + LangElement *specMul = new GenOp( "@", specular ); LangElement *final = specMul; // mask out with lightmap if present - if( fd.features[MFT_LightMap] ) + if ( fd.features[MFT_LightMap] ) { LangElement *lmColor = NULL; @@ -141,37 +78,44 @@ void PixelSpecularGLSL::processPix( Vector &componentList, LangElement * lightMap = LangElement::find( "lightMap" ); LangElement * lmCoord = LangElement::find( "texCoord2" ); - lmColor = new GenOp( "tex2D(@, @)", lightMap, lmCoord ); + lmColor = new GenOp( "texture(@, @)", lightMap, lmCoord ); } - final = new GenOp( "@ * float4(@.rgb,0)", specMul, lmColor ); + final = new GenOp( "@ * vec4(@.rgb,0)", specMul, lmColor ); } - // We we have a normal map then mask the specular - if ( !fd.features[MFT_SpecularMap] && fd.features[MFT_NormalMap] ) + // If we have a normal map then mask the specular + if ( fd.features[MFT_SpecularMap] ) + { + Var *specularColor = (Var*)LangElement::find( "specularColor" ); + if (specularColor) + final = new GenOp( "@ * @", final, specularColor ); + } + else if ( fd.features[MFT_NormalMap] && !fd.features[MFT_IsDXTnm] ) { Var *bumpColor = (Var*)LangElement::find( "bumpNormal" ); final = new GenOp( "@ * @.a", final, bumpColor ); } - // Add the specular to the final color. - meta->addStatement( new GenOp( " @;\r\n", assignColor( final, Material::Add ) ) ); + // Add the specular to the final color. + // search for color var + Var *color = (Var*)LangElement::find( "col" ); + meta->addStatement( new GenOp( " @.rgb += ( @ ).rgb;\r\n", color, final ) ); output = meta; - */ } ShaderFeature::Resources PixelSpecularGLSL::getResources( const MaterialFeatureData &fd ) { Resources res; - res.numTexReg = 1; return res; } + void SpecularMapGLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) { // Get the texture coord. - Var *texCoord = getInTexCoord( "out_texCoord", "vec2", true, componentList ); + Var *texCoord = getInTexCoord( "texCoord", "vec2", true, componentList ); // create texture var Var *specularMap = new Var; @@ -180,7 +124,7 @@ void SpecularMapGLSL::processPix( Vector &componentList, const specularMap->uniform = true; specularMap->sampler = true; specularMap->constNum = Var::getTexUnitNum(); - LangElement *texOp = new GenOp( "texture2D(@, @)", specularMap, texCoord ); + LangElement *texOp = new GenOp( "texture(@, @)", specularMap, texCoord ); Var *specularColor = new Var( "specularColor", "vec4" ); @@ -203,6 +147,7 @@ void SpecularMapGLSL::setTexData( Material::StageData &stageDat, if ( tex ) { passData.mTexType[ texIndex ] = Material::Standard; + passData.mSamplerNames[ texIndex ] = "specularMap"; passData.mTexSlot[ texIndex++ ].texObject = tex; } } \ No newline at end of file diff --git a/Engine/source/shaderGen/GLSL/pixSpecularGLSL.h b/Engine/source/shaderGen/GLSL/pixSpecularGLSL.h index eb49b4504..2b7c92b95 100644 --- a/Engine/source/shaderGen/GLSL/pixSpecularGLSL.h +++ b/Engine/source/shaderGen/GLSL/pixSpecularGLSL.h @@ -53,7 +53,6 @@ public: } }; - /// A texture source for the PixSpecular feature class SpecularMapGLSL : public ShaderFeatureGLSL { @@ -75,5 +74,4 @@ public: } }; - -#endif // _PIXSPECULAR_GLSL_H_ \ No newline at end of file +#endif // _PIXSPECULAR_HLSL_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/GLSL/shaderCompGLSL.cpp b/Engine/source/shaderGen/GLSL/shaderCompGLSL.cpp index 7a476ff3b..faf5f92e1 100644 --- a/Engine/source/shaderGen/GLSL/shaderCompGLSL.cpp +++ b/Engine/source/shaderGen/GLSL/shaderCompGLSL.cpp @@ -38,7 +38,7 @@ Var * AppVertConnectorGLSL::getElement( RegisterType type, { Var *newVar = new Var; mElementList.push_back( newVar ); - newVar->setConnectName( "gl_Vertex" ); + newVar->setConnectName( "vPosition" ); return newVar; } @@ -46,28 +46,49 @@ Var * AppVertConnectorGLSL::getElement( RegisterType type, { Var *newVar = new Var; mElementList.push_back( newVar ); - newVar->setConnectName( "gl_Normal" ); + newVar->setConnectName( "vNormal" ); return newVar; } - + + case RT_BINORMAL: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "vBinormal" ); + return newVar; + } case RT_COLOR: { Var *newVar = new Var; mElementList.push_back( newVar ); - newVar->setConnectName( "gl_Color" ); + newVar->setConnectName( "vColor" ); + return newVar; + } + + case RT_TANGENT: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "vTangent" ); + return newVar; + } + + case RT_TANGENTW: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "vTangentW" ); return newVar; } case RT_TEXCOORD: - case RT_BINORMAL: - case RT_TANGENT: { Var *newVar = new Var; mElementList.push_back( newVar ); char out[32]; - dSprintf( (char*)out, sizeof(out), "gl_MultiTexCoord%d", mCurTexElem ); + dSprintf( (char*)out, sizeof(out), "vTexCoord%d", mCurTexElem ); newVar->setConnectName( out ); newVar->constNum = mCurTexElem; newVar->arraySize = numElements; @@ -108,29 +129,55 @@ void AppVertConnectorGLSL::reset() mCurTexElem = 0; } -void AppVertConnectorGLSL::print( Stream &stream ) +void AppVertConnectorGLSL::print( Stream &stream, bool isVertexShader ) { - // print out elements + if(!isVertexShader) + return; + + U8 output[256]; + + // print struct + dSprintf( (char*)output, sizeof(output), "struct VertexData\r\n" ); + stream.write( dStrlen((char*)output), output ); + dSprintf( (char*)output, sizeof(output), "{\r\n" ); + stream.write( dStrlen((char*)output), output ); + for( U32 i=0; itype, "float")) - swizzle = "x"; - else if(!dStrcmp((const char*)var->type, "vec2")) - swizzle = "xy"; - else if(!dStrcmp((const char*)var->type, "vec3")) - swizzle = "xyz"; + + if( var->arraySize == 1) + { + dSprintf( (char*)output, sizeof(output), " %s %s;\r\n", var->type, (char*)var->name ); + stream.write( dStrlen((char*)output), output ); + } else - swizzle = "xyzw"; + { + dSprintf( (char*)output, sizeof(output), " %s %s[%d];\r\n", var->type, (char*)var->name, var->arraySize ); + stream.write( dStrlen((char*)output), output ); + } + } - // This is ugly. We use #defines to match user defined names with - // built in vars. There is no cleaner way to do this. - dSprintf( (char*)output, sizeof(output), "#define %s %s.%s\r\n", var->name, var->connectName, swizzle ); + dSprintf( (char*)output, sizeof(output), "} IN;\r\n\r\n" ); + stream.write( dStrlen((char*)output), output ); + // print in elements + for( U32 i=0; iarraySize; ++j) + { + const char *name = j == 0 ? var->connectName : avar("vTexCoord%d", var->constNum + j) ; + dSprintf( (char*)output, sizeof(output), "in %s %s;\r\n", var->type, name ); + stream.write( dStrlen((char*)output), output ); + } + + dSprintf( (char*)output, sizeof(output), "#define IN_%s IN.%s\r\n", var->name, var->name ); // TODO REMOVE stream.write( dStrlen((char*)output), output ); } + const char* newLine ="\r\n"; + stream.write( dStrlen((char*)newLine), newLine ); } Var * VertPixelConnectorGLSL::getElement( RegisterType type, @@ -140,14 +187,45 @@ Var * VertPixelConnectorGLSL::getElement( RegisterType type, switch( type ) { case RT_POSITION: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "POSITION" ); + return newVar; + } + case RT_NORMAL: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "NORMAL" ); + return newVar; + } + case RT_COLOR: { Var *newVar = new Var; mElementList.push_back( newVar ); + newVar->setConnectName( "COLOR" ); return newVar; } + /*case RT_BINORMAL: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "BINORMAL" ); + return newVar; + } + + case RT_TANGENT: + { + Var *newVar = new Var; + mElementList.push_back( newVar ); + newVar->setConnectName( "TANGENT" ); + return newVar; + } */ + case RT_TEXCOORD: case RT_BINORMAL: case RT_TANGENT: @@ -155,6 +233,10 @@ Var * VertPixelConnectorGLSL::getElement( RegisterType type, Var *newVar = new Var; newVar->arraySize = numElements; + char out[32]; + dSprintf( (char*)out, sizeof(out), "TEXCOORD%d", mCurTexElem ); + newVar->setConnectName( out ); + if ( numRegisters != -1 ) mCurTexElem += numRegisters; else @@ -192,7 +274,7 @@ void VertPixelConnectorGLSL::reset() mCurTexElem = 0; } -void VertPixelConnectorGLSL::print( Stream &stream ) +void VertPixelConnectorGLSL::print( Stream &stream, bool isVerterShader ) { // print out elements for( U32 i=0; iarraySize <= 1) - dSprintf((char*)output, sizeof(output), "varying %s %s;\r\n", var->type, var->name); + dSprintf((char*)output, sizeof(output), "%s %s _%s_;\r\n", (isVerterShader ? "out" : "in"), var->type, var->connectName); else - dSprintf((char*)output, sizeof(output), "varying %s %s[%d];\r\n", var->type, var->name, var->arraySize); + dSprintf((char*)output, sizeof(output), "%s %s _%s_[%d];\r\n", (isVerterShader ? "out" : "in"),var->type, var->connectName, var->arraySize); stream.write( dStrlen((char*)output), output ); } + + printStructDefines(stream, !isVerterShader); } -void VertexParamsDefGLSL::print( Stream &stream ) +void VertPixelConnectorGLSL::printOnMain( Stream &stream, bool isVerterShader ) +{ + if(isVerterShader) + return; + + const char *newLine = "\r\n"; + const char *header = " //-------------------------\r\n"; + stream.write( dStrlen((char*)newLine), newLine ); + stream.write( dStrlen((char*)header), header ); + + // print out elements + for( U32 i=0; iname, "gl_Position")) + continue; + + dSprintf((char*)output, sizeof(output), " %s IN_%s = _%s_;\r\n", var->type, var->name, var->connectName); + + stream.write( dStrlen((char*)output), output ); + } + + stream.write( dStrlen((char*)header), header ); + stream.write( dStrlen((char*)newLine), newLine ); +} + + +void AppVertConnectorGLSL::printOnMain( Stream &stream, bool isVerterShader ) +{ + if(!isVerterShader) + return; + + const char *newLine = "\r\n"; + const char *header = " //-------------------------\r\n"; + stream.write( dStrlen((char*)newLine), newLine ); + stream.write( dStrlen((char*)header), header ); + + // print out elements + for( U32 i=0; iarraySize <= 1) + { + dSprintf((char*)output, sizeof(output), " IN.%s = %s;\r\n", var->name, var->connectName); + stream.write( dStrlen((char*)output), output ); + } + else + { + for(int j = 0; j < var->arraySize; ++j) + { + const char *name = j == 0 ? var->connectName : avar("vTexCoord%d", var->constNum + j) ; + dSprintf((char*)output, sizeof(output), " IN.%s[%d] = %s;\r\n", var->name, j, name ); + stream.write( dStrlen((char*)output), output ); + } + } + } + + stream.write( dStrlen((char*)header), header ); + stream.write( dStrlen((char*)newLine), newLine ); +} + + + + +Vector initDeprecadedDefines() +{ + Vector vec; + vec.push_back( "isBack"); + return vec; +} + +void VertPixelConnectorGLSL::printStructDefines( Stream &stream, bool in ) +{ + const char* connectionDir; + + if(in) + { + connectionDir = "IN"; + } + else + { + + connectionDir = "OUT"; + } + + static Vector deprecatedDefines = initDeprecadedDefines(); + + const char *newLine = "\r\n"; + const char *header = "// Struct defines\r\n"; + stream.write( dStrlen((char*)newLine), newLine ); + stream.write( dStrlen((char*)header), header ); + + // print out elements + for( U32 i=0; iname, "gl_Position")) + continue; + + if(!in) + { + dSprintf((char*)output, sizeof(output), "#define %s_%s _%s_\r\n", connectionDir, var->name, var->connectName); + stream.write( dStrlen((char*)output), output ); + } + + if( deprecatedDefines.contains((char*)var->name)) + continue; + + dSprintf((char*)output, sizeof(output), "#define %s %s_%s\r\n", var->name, connectionDir, var->name); + stream.write( dStrlen((char*)output), output ); + } + + stream.write( dStrlen((char*)newLine), newLine ); +} + +void VertexParamsDefGLSL::print( Stream &stream, bool isVerterShader ) { // find all the uniform variables and print them out for( U32 i=0; i(LangElement::elementList[i]); + if( var ) + { + if( var->uniform && !var->sampler) + { + U8 output[256]; + if(var->arraySize <= 1) + dSprintf((char*)output, sizeof(output), " %s %s = %s;\r\n", var->type, var->name, var->name); + else + dSprintf((char*)output, sizeof(output), " %s %s[%d] = %s;\r\n", var->type, var->name, var->arraySize, var->name); + + stream.write( dStrlen((char*)output), output ); + } + } + } } diff --git a/Engine/source/shaderGen/GLSL/shaderCompGLSL.h b/Engine/source/shaderGen/GLSL/shaderCompGLSL.h index 5b306c0c8..c00c70165 100644 --- a/Engine/source/shaderGen/GLSL/shaderCompGLSL.h +++ b/Engine/source/shaderGen/GLSL/shaderCompGLSL.h @@ -40,7 +40,9 @@ public: virtual void reset(); virtual void sortVars(); - virtual void print( Stream &stream ); + virtual void print( Stream &stream, bool isVerterShader ); + void printStructDefines( Stream &stream, bool in ); + virtual void printOnMain( Stream &stream, bool isVerterShader ); }; class AppVertConnectorGLSL : public ShaderConnector @@ -53,21 +55,22 @@ public: virtual void reset(); virtual void sortVars(); - virtual void print( Stream &stream ); + virtual void print( Stream &stream, bool isVerterShader ); + virtual void printOnMain( Stream &stream, bool isVerterShader ); }; class VertexParamsDefGLSL : public ParamsDef { public: - virtual void print( Stream &stream ); + virtual void print( Stream &stream, bool isVerterShader ); }; class PixelParamsDefGLSL : public ParamsDef { public: - virtual void print( Stream &stream ); + virtual void print( Stream &stream, bool isVerterShader ); }; #endif // _SHADERCOMP_GLSL_H_ \ No newline at end of file diff --git a/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp b/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp index 0e0c1ac46..c8942f454 100644 --- a/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp +++ b/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.cpp @@ -41,26 +41,37 @@ LangElement * ShaderFeatureGLSL::setupTexSpaceMat( Vector &, / Var *B = (Var*) LangElement::find( "B" ); Var *T = (Var*) LangElement::find( "T" ); + Var *tangentW = (Var*) LangElement::find( "tangentW" ); + // setup matrix var *texSpaceMat = new Var; - (*texSpaceMat)->setType( "mat3" ); + (*texSpaceMat)->setType( "float3x3" ); (*texSpaceMat)->setName( "objToTangentSpace" ); MultiLine * meta = new MultiLine; + meta->addStatement( new GenOp( " @;\r\n", new DecOp( *texSpaceMat ) ) ); - // Recreate the binormal if we don't have one. - if ( !B ) + // Protect against missing normal and tangent. + if ( !N || !T ) { - B = new Var; - B->setType( "vec3" ); - B->setName( "B" ); - meta->addStatement( new GenOp( " @ = cross( @, normalize(@) );\r\n", new DecOp( B ), T, N ) ); + meta->addStatement( new GenOp( " tSetMatrixRow(@, 0, float3( 1, 0, 0 )); tSetMatrixRow(@, 1,float3( 0, 1, 0 )); tSetMatrixRow(@,2, float3( 0, 0, 1 ));\r\n", + *texSpaceMat, *texSpaceMat, *texSpaceMat ) ); + return meta; } - meta->addStatement( new GenOp( " @;\r\n", new DecOp( *texSpaceMat ) ) ); - meta->addStatement( new GenOp( " @[0] = vec3(@.x, @.x, normalize(@).x);\r\n", *texSpaceMat, T, B, N ) ); - meta->addStatement( new GenOp( " @[1] = vec3(@.y, @.y, normalize(@).y);\r\n", *texSpaceMat, T, B, N ) ); - meta->addStatement( new GenOp( " @[2] = vec3(@.z, @.z, normalize(@).z);\r\n", *texSpaceMat, T, B, N ) ); + meta->addStatement( new GenOp( " tSetMatrixRow(@, 0, @);\r\n", *texSpaceMat, T ) ); + if ( B ) + meta->addStatement( new GenOp( " tSetMatrixRow(@, 1, @);\r\n", *texSpaceMat, B ) ); + else + { + if(dStricmp((char*)T->type, "vec4") == 0) + meta->addStatement( new GenOp( " tSetMatrixRow(@, 1, cross( @, normalize(@) ) * @.w);\r\n", *texSpaceMat, T, N, T ) ); + else if(tangentW) + meta->addStatement( new GenOp( " tSetMatrixRow(@, 1, cross( @, normalize(@) ) * @);\r\n", *texSpaceMat, T, N, tangentW ) ); + else + meta->addStatement( new GenOp( " tSetMatrixRow(@, 1, cross( @, normalize(@) ));\r\n", *texSpaceMat, T, N ) ); + } + meta->addStatement( new GenOp( " tSetMatrixRow(@, 2, normalize(@));\r\n", *texSpaceMat, N ) ); return meta; } @@ -107,7 +118,7 @@ LangElement* ShaderFeatureGLSL::assignColor( LangElement *elem, case Material::LerpAlpha: if ( !lerpElem ) lerpElem = elem; - assign = new GenOp( "@.rgb = mix( @.rgb, (@).rgb, (@).a )", color, elem, color, lerpElem ); + assign = new GenOp( "@.rgb = lerp( @.rgb, (@).rgb, (@).a )", color, color, elem, lerpElem ); break; case Material::ToneMap: @@ -136,10 +147,19 @@ LangElement *ShaderFeatureGLSL::expandNormalMap( LangElement *sampleNormalOp, if ( fd.features.hasFeature( MFT_IsDXTnm, getProcessIndex() ) ) { + if ( fd.features[MFT_ImposterVert] ) + { + // The imposter system uses object space normals and + // encodes them with the z axis in the alpha component. + meta->addStatement( new GenOp( " @ = float4( normalize( @.xyw * 2.0 - 1.0 ), 0.0 ); // Obj DXTnm\r\n", normalDecl, sampleNormalOp ) ); + } + else + { // DXT Swizzle trick - meta->addStatement( new GenOp( " @ = vec4( @.ag * 2.0 - 1.0, 0.0, 0.0 ); // DXTnm\r\n", normalDecl, sampleNormalOp ) ); + meta->addStatement( new GenOp( " @ = float4( @.ag * 2.0 - 1.0, 0.0, 0.0 ); // DXTnm\r\n", normalDecl, sampleNormalOp ) ); meta->addStatement( new GenOp( " @.z = sqrt( 1.0 - dot( @.xy, @.xy ) ); // DXTnm\r\n", normalVar, normalVar, normalVar ) ); } + } else { meta->addStatement( new GenOp( " @ = @;\r\n", normalDecl, sampleNormalOp ) ); @@ -165,6 +185,18 @@ Var * ShaderFeatureGLSL::getVertTexCoord( const String &name ) inTex = dynamic_cast( LangElement::elementList[i] ); if ( inTex ) { + // NOTE: This used to do this check... + // + // dStrcmp( (char*)inTex->structName, "IN" ) + // + // ... to ensure that the var was from the input + // vertex structure, but this kept some features + // ( ie. imposter vert ) from decoding their own + // coords for other features to use. + // + // If we run into issues with collisions between + // IN vars and local vars we may need to revise. + break; } } @@ -201,7 +233,7 @@ Var* ShaderFeatureGLSL::getOutWorldToTangent( Vector &compon { // turn obj->tangent into world->tangent worldToTangent = new Var; - worldToTangent->setType( "mat3x3" ); + worldToTangent->setType( "float3x3" ); worldToTangent->setName( "worldToTangent" ); LangElement *worldToTangentDecl = new DecOp( worldToTangent ); @@ -211,16 +243,29 @@ Var* ShaderFeatureGLSL::getOutWorldToTangent( Vector &compon { worldToObj = new Var; worldToObj->setName( "worldToObj" ); - worldToObj->setType( "mat4x4" ); + + if ( fd.features[MFT_UseInstancing] ) + { + // We just use transpose to convert the 3x3 portion of + // the object transform to its inverse. + worldToObj->setType( "float3x3" ); + Var *objTrans = getObjTrans( componentList, true, meta ); + meta->addStatement( new GenOp( " @ = transpose( float3x3(@) ); // Instancing!\r\n", new DecOp( worldToObj ), objTrans ) ); + } + else + { + worldToObj->setType( "float4x4" ); worldToObj->uniform = true; worldToObj->constSortPos = cspPrimitive; } + } // assign world->tangent transform - meta->addStatement( new GenOp( " @ = @ * mat3x3( @[0].xyz, @[1].xyz, @[2].xyz );\r\n", worldToTangentDecl, texSpaceMat, worldToObj, worldToObj, worldToObj ) ); + meta->addStatement( new GenOp( " @ = tMul( @, float3x3(@) );\r\n", worldToTangentDecl, texSpaceMat, worldToObj ) ); } else { + // Assume particle normal generation has set this up in the proper space worldToTangent = texSpaceMat; } } @@ -230,13 +275,14 @@ Var* ShaderFeatureGLSL::getOutWorldToTangent( Vector &compon outWorldToTangent = connectComp->getElement( RT_TEXCOORD, 1, 3 ); outWorldToTangent->setName( "outWorldToTangent" ); - outWorldToTangent->setType( "mat3x3" ); + outWorldToTangent->setStructName( "OUT" ); + outWorldToTangent->setType( "float3x3" ); meta->addStatement( new GenOp( " @ = @;\r\n", outWorldToTangent, worldToTangent ) ); return outWorldToTangent; } -Var* ShaderFeatureGLSL::getOutViewToTangent( Vector &componentList, +Var* ShaderFeatureGLSL::getOutViewToTangent( Vector &componentList, MultiLine *meta, const MaterialFeatureData &fd ) { @@ -247,14 +293,14 @@ Var* ShaderFeatureGLSL::getOutViewToTangent( Vector &compone Var *viewToTangent = (Var*)LangElement::find( "viewToTangent" ); if ( !viewToTangent ) { + Var *texSpaceMat = getOutObjToTangentSpace( componentList, meta, fd ); if(!fd.features[MFT_ParticleNormal]) { - // turn obj->tangent into world->tangent viewToTangent = new Var; - viewToTangent->setType( "mat3" ); + viewToTangent->setType( "float3x3" ); viewToTangent->setName( "viewToTangent" ); LangElement *viewToTangentDecl = new DecOp( viewToTangent ); @@ -262,11 +308,7 @@ Var* ShaderFeatureGLSL::getOutViewToTangent( Vector &compone Var *viewToObj = getInvWorldView( componentList, fd.features[MFT_UseInstancing], meta ); // assign world->tangent transform - meta->addStatement( new GenOp( " mat3 mat3ViewToObj;\r\n" ) ); - meta->addStatement( new GenOp( " mat3ViewToObj[0] = @[0].xyz;\r\n", viewToObj ) ); - meta->addStatement( new GenOp( " mat3ViewToObj[1] = @[1].xyz;\r\n", viewToObj ) ); - meta->addStatement( new GenOp( " mat3ViewToObj[2] = @[2].xyz;\r\n", viewToObj ) ); - meta->addStatement( new GenOp( " @ = @ * mat3ViewToObj;\r\n", viewToTangentDecl, texSpaceMat ) ); + meta->addStatement( new GenOp( " @ = tMul( (@), float3x3(@) );\r\n", viewToTangentDecl, texSpaceMat, viewToObj ) ); } else { @@ -280,7 +322,8 @@ Var* ShaderFeatureGLSL::getOutViewToTangent( Vector &compone outViewToTangent = connectComp->getElement( RT_TEXCOORD, 1, 3 ); outViewToTangent->setName( "outViewToTangent" ); - outViewToTangent->setType( "mat3" ); + outViewToTangent->setStructName( "OUT" ); + outViewToTangent->setType( "float3x3" ); meta->addStatement( new GenOp( " @ = @;\r\n", outViewToTangent, viewToTangent ) ); return outViewToTangent; @@ -304,6 +347,7 @@ Var* ShaderFeatureGLSL::getOutTexCoord( const char *name, texCoord = connectComp->getElement( RT_TEXCOORD ); texCoord->setName( outTexName ); + texCoord->setStructName( "OUT" ); texCoord->setType( type ); texCoord->mapsToSampler = mapsToSampler; @@ -313,21 +357,21 @@ Var* ShaderFeatureGLSL::getOutTexCoord( const char *name, // create texture mat var Var *texMat = new Var; - texMat->setType( "mat4" ); + texMat->setType( "float4x4" ); texMat->setName( "texMat" ); texMat->uniform = true; texMat->constSortPos = cspPass; // Statement allows for casting of different types which // eliminates vector truncation problems. - String statement = String::ToString( " @ = %s(@ * @);\r\n", type ); + String statement = String::ToString( " @ = %s(tMul(@, @));\r\n", type ); meta->addStatement( new GenOp( statement , texCoord, texMat, inTex ) ); } else { // Statement allows for casting of different types which // eliminates vector truncation problems. - String statement = String::ToString( " @ = %s(@);\r\n", type ); + String statement = String::ToString( " @ = %s(@);\r\n", type ); meta->addStatement( new GenOp( statement, texCoord, inTex ) ); } } @@ -349,6 +393,7 @@ Var* ShaderFeatureGLSL::getInTexCoord( const char *name, ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); texCoord = connectComp->getElement( RT_TEXCOORD ); texCoord->setName( name ); + texCoord->setStructName( "IN" ); texCoord->setType( type ); texCoord->mapsToSampler = mapsToSampler; } @@ -359,15 +404,97 @@ Var* ShaderFeatureGLSL::getInTexCoord( const char *name, return texCoord; } +Var* ShaderFeatureGLSL::getInColor( const char *name, + const char *type, + Vector &componentList ) +{ + Var *inColor = (Var*)LangElement::find( name ); + if ( !inColor ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + inColor = connectComp->getElement( RT_COLOR ); + inColor->setName( name ); + inColor->setStructName( "IN" ); + inColor->setType( type ); + } + + AssertFatal( dStrcmp( type, (const char*)inColor->type ) == 0, + "ShaderFeatureGLSL::getInColor - Type mismatch!" ); + + return inColor; +} + +Var* ShaderFeatureGLSL::addOutVpos( MultiLine *meta, + Vector &componentList ) +{ + /* + // Nothing to do if we're on SM 3.0... we use the real vpos. + if ( GFX->getPixelShaderVersion() >= 3.0f ) + return NULL; + */ + + // For SM 2.x we need to generate the vpos in the vertex shader + // and pass it as a texture coord to the pixel shader. + + Var *outVpos = (Var*)LangElement::find( "outVpos" ); + if ( !outVpos ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + outVpos = connectComp->getElement( RT_TEXCOORD ); + outVpos->setName( "outVpos" ); + outVpos->setStructName( "OUT" ); + outVpos->setType( "vec4" ); + outVpos->mapsToSampler = false; + + Var *outPosition = (Var*) LangElement::find( "gl_Position" ); + AssertFatal( outPosition, "ShaderFeatureGLSL::addOutVpos - Didn't find the output position." ); + + meta->addStatement( new GenOp( " @ = @;\r\n", outVpos, outPosition ) ); + } + + return outVpos; +} + +Var* ShaderFeatureGLSL::getInVpos( MultiLine *meta, + Vector &componentList ) +{ + Var *inVpos = (Var*)LangElement::find( "vpos" ); + if ( inVpos ) + return inVpos; + + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + /* + if ( GFX->getPixelShaderVersion() >= 3.0f ) + { + inVpos = connectComp->getElement( RT_VPOS ); + inVpos->setName( "vpos" ); + inVpos->setStructName( "IN" ); + inVpos->setType( "vec2" ); + return inVpos; + } + */ + inVpos = connectComp->getElement( RT_TEXCOORD ); + inVpos->setName( "inVpos" ); + inVpos->setStructName( "IN" ); + inVpos->setType( "vec4" ); + + Var *vpos = new Var( "vpos", "vec2" ); + meta->addStatement( new GenOp( " @ = @.xy / @.w;\r\n", new DecOp( vpos ), inVpos, inVpos ) ); + + return vpos; +} + Var* ShaderFeatureGLSL::getInWorldToTangent( Vector &componentList ) { - Var *worldToTangent = (Var*)LangElement::find( "outWorldToTangent" ); + Var *worldToTangent = (Var*)LangElement::find( "worldToTangent" ); if ( !worldToTangent ) { ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); worldToTangent = connectComp->getElement( RT_TEXCOORD, 1, 3 ); - worldToTangent->setName( "outWorldToTangent" ); - worldToTangent->setType( "mat3x3" ); + worldToTangent->setName( "worldToTangent" ); + worldToTangent->setStructName( "IN" ); + worldToTangent->setType( "float3x3" ); } return worldToTangent; @@ -375,13 +502,14 @@ Var* ShaderFeatureGLSL::getInWorldToTangent( Vector &component Var* ShaderFeatureGLSL::getInViewToTangent( Vector &componentList ) { - Var *viewToTangent = (Var*)LangElement::find( "outViewToTangent" ); + Var *viewToTangent = (Var*)LangElement::find( "viewToTangent" ); if ( !viewToTangent ) { ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); viewToTangent = connectComp->getElement( RT_TEXCOORD, 1, 3 ); - viewToTangent->setName( "outViewToTangent" ); - viewToTangent->setType( "mat3" ); + viewToTangent->setName( "viewToTangent" ); + viewToTangent->setStructName( "IN" ); + viewToTangent->setType( "float3x3" ); } return viewToTangent; @@ -403,16 +531,43 @@ Var* ShaderFeatureGLSL::getNormalMapTex() return normalMap; } -Var* ShaderFeatureGLSL::getObjTrans( Vector &componentList, +Var* ShaderFeatureGLSL::getObjTrans( Vector &componentList, bool useInstancing, MultiLine *meta ) { - Var *objTrans = (Var*) LangElement::find( "objTrans" ); if ( objTrans ) + Var *objTrans = (Var*)LangElement::find( "objTrans" ); + if ( objTrans ) return objTrans; - objTrans = new Var; objTrans->setType( "mat4x4" ); + + if ( useInstancing ) + { + ShaderConnector *vertStruct = dynamic_cast( componentList[C_VERT_STRUCT] ); + Var *instObjTrans = vertStruct->getElement( RT_TEXCOORD, 4, 4 ); + instObjTrans->setStructName( "IN" ); + instObjTrans->setName( "inst_objectTrans" ); + + mInstancingFormat->addElement( "objTrans", GFXDeclType_Float4, instObjTrans->constNum+0 ); + mInstancingFormat->addElement( "objTrans", GFXDeclType_Float4, instObjTrans->constNum+1 ); + mInstancingFormat->addElement( "objTrans", GFXDeclType_Float4, instObjTrans->constNum+2 ); + mInstancingFormat->addElement( "objTrans", GFXDeclType_Float4, instObjTrans->constNum+3 ); + + objTrans = new Var; + objTrans->setType( "mat4x4" ); + objTrans->setName( "objTrans" ); + meta->addStatement( new GenOp( " @ = mat4x4( // Instancing!\r\n", new DecOp( objTrans ), instObjTrans ) ); + meta->addStatement( new GenOp( " @[0],\r\n", instObjTrans ) ); + meta->addStatement( new GenOp( " @[1],\r\n", instObjTrans ) ); + meta->addStatement( new GenOp( " @[2],\r\n",instObjTrans ) ); + meta->addStatement( new GenOp( " @[3] );\r\n", instObjTrans ) ); + } + else + { + objTrans = new Var; + objTrans->setType( "float4x4" ); objTrans->setName( "objTrans" ); objTrans->uniform = true; objTrans->constSortPos = cspPrimitive; + } return objTrans; } @@ -425,12 +580,33 @@ Var* ShaderFeatureGLSL::getModelView( Vector &componentList, if ( modelview ) return modelview; - // create modelview variable + if ( useInstancing ) + { + Var *objTrans = getObjTrans( componentList, useInstancing, meta ); + + Var *viewProj = (Var*)LangElement::find( "viewProj" ); + if ( !viewProj ) + { + viewProj = new Var; + viewProj->setType( "float4x4" ); + viewProj->setName( "viewProj" ); + viewProj->uniform = true; + viewProj->constSortPos = cspPass; + } + modelview = new Var; - modelview->setType( "mat4" ); + modelview->setType( "float4x4" ); + modelview->setName( "modelview" ); + meta->addStatement( new GenOp( " @ = tMul( @, @ ); // Instancing!\r\n", new DecOp( modelview ), viewProj, objTrans ) ); + } + else + { + modelview = new Var; + modelview->setType( "float4x4" ); modelview->setName( "modelview" ); modelview->uniform = true; modelview->constSortPos = cspPrimitive; + } return modelview; } @@ -443,15 +619,39 @@ Var* ShaderFeatureGLSL::getWorldView( Vector &componentList, if ( worldView ) return worldView; + if ( useInstancing ) + { + Var *objTrans = getObjTrans( componentList, useInstancing, meta ); + + Var *worldToCamera = (Var*)LangElement::find( "worldToCamera" ); + if ( !worldToCamera ) + { + worldToCamera = new Var; + worldToCamera->setType( "float4x4" ); + worldToCamera->setName( "worldToCamera" ); + worldToCamera->uniform = true; + worldToCamera->constSortPos = cspPass; + } + + worldView = new Var; + worldView->setType( "float4x4" ); + worldView->setName( "worldViewOnly" ); + + meta->addStatement( new GenOp( " @ = tMul( @, @ ); // Instancing!\r\n", new DecOp( worldView ), worldToCamera, objTrans ) ); + } + else + { worldView = new Var; - worldView->setType( "mat4x4" ); + worldView->setType( "float4x4" ); worldView->setName( "worldViewOnly" ); worldView->uniform = true; worldView->constSortPos = cspPrimitive; + } return worldView; } + Var* ShaderFeatureGLSL::getInvWorldView( Vector &componentList, bool useInstancing, MultiLine *meta ) @@ -460,11 +660,27 @@ Var* ShaderFeatureGLSL::getInvWorldView( Vector &componentLis if ( viewToObj ) return viewToObj; + if ( useInstancing ) + { + Var *worldView = getWorldView( componentList, useInstancing, meta ); + + viewToObj = new Var; + viewToObj->setType( "float3x3" ); + viewToObj->setName( "viewToObj" ); + + // We just use transpose to convert the 3x3 portion + // of the world view transform into its inverse. + + meta->addStatement( new GenOp( " @ = transpose( float3x3(@) ); // Instancing!\r\n", new DecOp( viewToObj ), worldView ) ); + } + else + { viewToObj = new Var; - viewToObj->setType( "mat4" ); + viewToObj->setType( "float4x4" ); viewToObj->setName( "viewToObj" ); viewToObj->uniform = true; viewToObj->constSortPos = cspPrimitive; + } return viewToObj; } @@ -491,7 +707,7 @@ void ShaderFeatureGLSL::getWsPosition( Vector &componentList, Var *objTrans = getObjTrans( componentList, useInstancing, meta ); - meta->addStatement( new GenOp( " @ = ( @ * vec4( @.xyz, 1 ) ).xyz;\r\n", + meta->addStatement( new GenOp( " @ = tMul( @, float4( @.xyz, 1 ) ).xyz;\r\n", wsPosition, objTrans, inPosition ) ); } @@ -505,6 +721,7 @@ Var* ShaderFeatureGLSL::addOutWsPosition( Vector &componentLis ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); outWsPosition = connectComp->getElement( RT_TEXCOORD ); outWsPosition->setName( "outWsPosition" ); + outWsPosition->setStructName( "OUT" ); outWsPosition->setType( "vec3" ); outWsPosition->mapsToSampler = false; @@ -516,12 +733,13 @@ Var* ShaderFeatureGLSL::addOutWsPosition( Vector &componentLis Var* ShaderFeatureGLSL::getInWsPosition( Vector &componentList ) { - Var *wsPosition = (Var*)LangElement::find( "outWsPosition" ); + Var *wsPosition = (Var*)LangElement::find( "wsPosition" ); if ( !wsPosition ) { ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); wsPosition = connectComp->getElement( RT_TEXCOORD ); - wsPosition->setName( "outWsPosition" ); + wsPosition->setName( "wsPosition" ); + wsPosition->setStructName( "IN" ); wsPosition->setType( "vec3" ); } @@ -566,7 +784,7 @@ Var* ShaderFeatureGLSL::addOutDetailTexCoord( Vector &compon // create detail variable Var *detScale = new Var; - detScale->setType( "vec2" ); + detScale->setType( "vec2" ); detScale->setName( "detailScale" ); detScale->uniform = true; detScale->constSortPos = cspPotentialPrimitive; @@ -575,25 +793,26 @@ Var* ShaderFeatureGLSL::addOutDetailTexCoord( Vector &compon ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); outTex = connectComp->getElement( RT_TEXCOORD ); outTex->setName( "detCoord" ); - outTex->setType( "vec2" ); + outTex->setStructName( "OUT" ); + outTex->setType( "vec2" ); outTex->mapsToSampler = true; if ( useTexAnim ) { - inTex->setType( "vec4" ); + inTex->setType( "vec4" ); // Find or create the texture matrix. Var *texMat = (Var*)LangElement::find( "texMat" ); if ( !texMat ) { texMat = new Var; - texMat->setType( "mat4x4" ); + texMat->setType( "float4x4" ); texMat->setName( "texMat" ); texMat->uniform = true; texMat->constSortPos = cspPass; } - meta->addStatement( new GenOp( " @ = (@ * @) * @;\r\n", outTex, texMat, inTex, detScale ) ); + meta->addStatement( new GenOp( " @ = tMul(@, @) * @;\r\n", outTex, texMat, inTex, detScale ) ); } else { @@ -608,7 +827,7 @@ Var* ShaderFeatureGLSL::addOutDetailTexCoord( Vector &compon // Base Texture //**************************************************************************** -void DiffuseMapFeatGLSL::processVert( Vector &componentList, +void DiffuseMapFeatGLSL::processVert( Vector &componentList, const MaterialFeatureData &fd ) { MultiLine *meta = new MultiLine; @@ -625,7 +844,7 @@ void DiffuseMapFeatGLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) { // grab connector texcoord register - Var *inTex = getInTexCoord( "out_texCoord", "vec2", true, componentList ); + Var *inTex = getInTexCoord( "texCoord", "vec2", true, componentList ); // create texture var Var *diffuseMap = new Var; @@ -645,7 +864,7 @@ void DiffuseMapFeatGLSL::processPix( Vector &componentList, diffColor->setName( "diffuseColor" ); LangElement *colorDecl = new DecOp( diffColor ); - meta->addStatement( new GenOp( " @ = texture2D(@, @);\r\n", + meta->addStatement( new GenOp( " @ = tex2D(@, @);\r\n", colorDecl, diffuseMap, inTex ) ); @@ -662,19 +881,19 @@ void DiffuseMapFeatGLSL::processPix( Vector &componentList, Var *atlasedTex = new Var; atlasedTex->setName("atlasedTexCoord"); - atlasedTex->setType("float2"); + atlasedTex->setType("vec2"); LangElement *atDecl = new DecOp(atlasedTex); // Parameters of the texture atlas Var *atParams = new Var; - atParams->setType("float4"); + atParams->setType("vec4"); atParams->setName("diffuseAtlasParams"); atParams->uniform = true; atParams->constSortPos = cspPotentialPrimitive; // Parameters of the texture (tile) this object is using in the atlas Var *tileParams = new Var; - tileParams->setType("float4"); + tileParams->setType("vec4"); tileParams->setName("diffuseAtlasTileParams"); tileParams->uniform = true; tileParams->constSortPos = cspPotentialPrimitive; @@ -683,24 +902,24 @@ void DiffuseMapFeatGLSL::processPix( Vector &componentList, if(is_sm3) { // Figure out the mip level - meta->addStatement(new GenOp(" vec2 _dx = ddx(@ * @.z);\r\n", inTex, atParams)); - meta->addStatement(new GenOp(" vec2 _dy = ddy(@ * @.z);\r\n", inTex, atParams)); + meta->addStatement(new GenOp(" float2 _dx = ddx(@ * @.z);\r\n", inTex, atParams)); + meta->addStatement(new GenOp(" float2 _dy = ddy(@ * @.z);\r\n", inTex, atParams)); meta->addStatement(new GenOp(" float mipLod = 0.5 * log2(max(dot(_dx, _dx), dot(_dy, _dy)));\r\n")); meta->addStatement(new GenOp(" mipLod = clamp(mipLod, 0.0, @.w);\r\n", atParams)); // And the size of the mip level meta->addStatement(new GenOp(" float mipPixSz = pow(2.0, @.w - mipLod);\r\n", atParams)); - meta->addStatement(new GenOp(" vec2 mipSz = mipPixSz / @.xy;\r\n", atParams)); + meta->addStatement(new GenOp(" float2 mipSz = mipPixSz / @.xy;\r\n", atParams)); } else { - meta->addStatement(new GenOp(" vec2 mipSz = float2(1.0, 1.0);\r\n")); + meta->addStatement(new GenOp(" float2 mipSz = float2(1.0, 1.0);\r\n")); } // Tiling mode // TODO: Select wrap or clamp somehow if( true ) // Wrap - meta->addStatement(new GenOp(" @ = fract(@);\r\n", atDecl, inTex)); + meta->addStatement(new GenOp(" @ = frac(@);\r\n", atDecl, inTex)); else // Clamp meta->addStatement(new GenOp(" @ = saturate(@);\r\n", atDecl, inTex)); @@ -724,7 +943,7 @@ void DiffuseMapFeatGLSL::processPix( Vector &componentList, #ifdef DEBUG_ATLASED_UV_COORDS if(!fd.features[MFT_PrePassConditioner]) { - meta->addStatement(new GenOp(" @ = vec4(@.xy, mipLod / @.w, 1.0);\r\n", new DecOp(diffColor), inTex, atParams)); + meta->addStatement(new GenOp(" @ = float4(@.xy, mipLod / @.w, 1.0);\r\n", new DecOp(diffColor), inTex, atParams)); meta->addStatement(new GenOp(" @; return OUT;\r\n", assignColor(diffColor, Material::Mul))); return; } @@ -732,12 +951,12 @@ void DiffuseMapFeatGLSL::processPix( Vector &componentList, if(is_sm3) { - meta->addStatement(new GenOp( " @ = texture2Dlod(@, float4(@, 0.0, mipLod));\r\n", + meta->addStatement(new GenOp( " @ = tex2Dlod(@, float4(@, 0.0, mipLod));\r\n", new DecOp(diffColor), diffuseMap, inTex)); } else { - meta->addStatement(new GenOp( " @ = texture2D(@, @);\r\n", + meta->addStatement(new GenOp( " @ = tex2D(@, @);\r\n", new DecOp(diffColor), diffuseMap, inTex)); } @@ -745,7 +964,7 @@ void DiffuseMapFeatGLSL::processPix( Vector &componentList, } else { - LangElement *statement = new GenOp( "texture2D(@, @)", diffuseMap, inTex ); + LangElement *statement = new GenOp( "tex2D(@, @)", diffuseMap, inTex ); output = new GenOp( " @;\r\n", assignColor( statement, Material::Mul ) ); } @@ -767,7 +986,10 @@ void DiffuseMapFeatGLSL::setTexData( Material::StageData &stageDat, { GFXTextureObject *tex = stageDat.getTex( MFT_DiffuseMap ); if ( tex ) + { + passData.mSamplerNames[ texIndex ] = "diffuseMap"; passData.mTexSlot[ texIndex++ ].texObject = tex; + } } @@ -785,6 +1007,7 @@ void OverlayTexFeatGLSL::processVert( Vector &componentList, ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *outTex = connectComp->getElement( RT_TEXCOORD ); outTex->setName( "outTexCoord2" ); + outTex->setStructName( "OUT" ); outTex->setType( "vec2" ); outTex->mapsToSampler = true; @@ -797,13 +1020,13 @@ void OverlayTexFeatGLSL::processVert( Vector &componentList, if ( !texMat ) { texMat = new Var; - texMat->setType( "mat4x4" ); + texMat->setType( "float4x4" ); texMat->setName( "texMat" ); texMat->uniform = true; texMat->constSortPos = cspPass; } - output = new GenOp( " @ = @ * @;\r\n", outTex, texMat, inTex ); + output = new GenOp( " @ = tMul(@, @);\r\n", outTex, texMat, inTex ); return; } @@ -818,7 +1041,8 @@ void OverlayTexFeatGLSL::processPix( Vector &componentList, // grab connector texcoord register ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *inTex = connectComp->getElement( RT_TEXCOORD ); - inTex->setName( "outTexCoord2" ); + inTex->setName( "texCoord2" ); + inTex->setStructName( "IN" ); inTex->setType( "vec2" ); inTex->mapsToSampler = true; @@ -830,7 +1054,7 @@ void OverlayTexFeatGLSL::processPix( Vector &componentList, diffuseMap->sampler = true; diffuseMap->constNum = Var::getTexUnitNum(); // used as texture unit num here - LangElement *statement = new GenOp( "texture2D(@, @)", diffuseMap, inTex ); + LangElement *statement = new GenOp( "tex2D(@, @)", diffuseMap, inTex ); output = new GenOp( " @;\r\n", assignColor( statement, Material::LerpAlpha ) ); } @@ -899,6 +1123,7 @@ void DiffuseVertColorFeatureGLSL::processVert( Vector< ShaderComponent* >& comp AssertFatal( connectComp, "DiffuseVertColorFeatureGLSL::processVert - C_CONNECTOR is not a ShaderConnector" ); Var* outColor = connectComp->getElement( RT_COLOR ); outColor->setName( "vertColor" ); + outColor->setStructName( "OUT" ); outColor->setType( "vec4" ); output = new GenOp( " @ = @;\r\n", outColor, inColor ); @@ -917,6 +1142,7 @@ void DiffuseVertColorFeatureGLSL::processPix( Vector &compon AssertFatal( connectComp, "DiffuseVertColorFeatureGLSL::processVert - C_CONNECTOR is not a ShaderConnector" ); vertColor = connectComp->getElement( RT_COLOR ); vertColor->setName( "vertColor" ); + vertColor->setStructName( "IN" ); vertColor->setType( "vec4" ); } @@ -934,12 +1160,13 @@ void LightmapFeatGLSL::processVert( Vector &componentList, const MaterialFeatureData &fd ) { // grab tex register from incoming vert - Var *inTex = (Var*) LangElement::find( "texCoord2" ); + Var *inTex = getVertTexCoord( "texCoord2" ); // grab connector texcoord register ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *outTex = connectComp->getElement( RT_TEXCOORD ); - outTex->setName( "outTexCoord2" ); + outTex->setName( "texCoord2" ); + outTex->setStructName( "OUT" ); outTex->setType( "vec2" ); outTex->mapsToSampler = true; @@ -953,7 +1180,8 @@ void LightmapFeatGLSL::processPix( Vector &componentList, // grab connector texcoord register ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *inTex = connectComp->getElement( RT_TEXCOORD ); - inTex->setName( "outTexCoord2" ); + inTex->setName( "texCoord2" ); + inTex->setStructName( "IN" ); inTex->setType( "vec2" ); inTex->mapsToSampler = true; @@ -974,7 +1202,7 @@ void LightmapFeatGLSL::processPix( Vector &componentList, lmColor->setType( "vec4" ); LangElement *lmColorDecl = new DecOp( lmColor ); - output = new GenOp( " @ = texture2D(@, @);\r\n", lmColorDecl, lightMap, inTex ); + output = new GenOp( " @ = tex2D(@, @);\r\n", lmColorDecl, lightMap, inTex ); return; } @@ -995,21 +1223,27 @@ void LightmapFeatGLSL::processPix( Vector &componentList, // Lightmap has already been included in the advanced light bin, so // no need to do any sampling or anything if(bPreProcessedLighting) - statement = new GenOp( "vec4(@, 1.0)", inColor ); + statement = new GenOp( "float4(@, 1.0)", inColor ); else - statement = new GenOp( "texture2D(@, @) + vec4(@.rgb, 0.0)", lightMap, inTex, inColor ); + statement = new GenOp( "tex2D(@, @) + float4(@.rgb, 0.0)", lightMap, inTex, inColor ); } } - else - { - statement = new GenOp( "texture2D(@, @)", lightMap, inTex ); - } + + // If we still don't have it... then just sample the lightmap. + if ( !statement ) + statement = new GenOp( "tex2D(@, @)", lightMap, inTex ); // Assign to proper render target + MultiLine *meta = new MultiLine; if( fd.features[MFT_LightbufferMRT] ) - output = new GenOp( " @;\r\n", assignColor( statement, Material::None, NULL, ShaderFeature::RenderTarget1 ) ); + { + meta->addStatement( new GenOp( " @;\r\n", assignColor( statement, Material::None, NULL, ShaderFeature::RenderTarget1 ) ) ); + meta->addStatement( new GenOp( " @.a = 0.0001;\r\n", LangElement::find( getOutputTargetVarName(ShaderFeature::RenderTarget1) ) ) ); + } else - output = new GenOp( " @;\r\n", assignColor( statement, Material::Mul ) ); + meta->addStatement( new GenOp( " @;\r\n", assignColor( statement, Material::Mul ) ) ); + + output = meta; } ShaderFeature::Resources LightmapFeatGLSL::getResources( const MaterialFeatureData &fd ) @@ -1027,6 +1261,7 @@ void LightmapFeatGLSL::setTexData( Material::StageData &stageDat, U32 &texIndex ) { GFXTextureObject *tex = stageDat.getTex( MFT_LightMap ); + passData.mSamplerNames[ texIndex ] = "lightMap"; if ( tex ) passData.mTexSlot[ texIndex++ ].texObject = tex; else @@ -1054,7 +1289,8 @@ void TonemapFeatGLSL::processVert( Vector &componentList, if ( inTex2 ) { Var *outTex2 = connectComp->getElement( RT_TEXCOORD ); - outTex2->setName( "toneMapCoord" ); + outTex2->setName( "texCoord2" ); + outTex2->setStructName( "OUT" ); outTex2->setType( "vec2" ); outTex2->mapsToSampler = true; @@ -1069,7 +1305,8 @@ void TonemapFeatGLSL::processPix( Vector &componentList, ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *inTex2 = connectComp->getElement( RT_TEXCOORD ); - inTex2->setName( "toneMapCoord" ); + inTex2->setName( "texCoord2" ); + inTex2->setStructName( "IN" ); inTex2->setType( "vec2" ); inTex2->mapsToSampler = true; @@ -1089,14 +1326,14 @@ void TonemapFeatGLSL::processPix( Vector &componentList, toneMapColor->setName( "toneMapColor" ); LangElement *toneMapColorDecl = new DecOp( toneMapColor ); - meta->addStatement( new GenOp( " @ = texture2D(@, @);\r\n", toneMapColorDecl, toneMap, inTex2 ) ); + meta->addStatement( new GenOp( " @ = tex2D(@, @);\r\n", toneMapColorDecl, toneMap, inTex2 ) ); // We do a different calculation if there is a diffuse map or not Material::BlendOp blendOp = Material::Mul; if ( fd.features[MFT_DiffuseMap] ) { // Reverse the tonemap - meta->addStatement( new GenOp( " @ = -1.0 * log(1.0 - @);\r\n", toneMapColor, toneMapColor ) ); + meta->addStatement( new GenOp( " @ = -1.0f * log(1.0f - @);\r\n", toneMapColor, toneMapColor ) ); // Re-tonemap with the current color factored in blendOp = Material::ToneMap; @@ -1126,7 +1363,10 @@ void TonemapFeatGLSL::processPix( Vector &componentList, // Assign to proper render target if( fd.features[MFT_LightbufferMRT] ) + { meta->addStatement( new GenOp( " @;\r\n", assignColor( toneMapColor, Material::None, NULL, ShaderFeature::RenderTarget1 ) ) ); + meta->addStatement( new GenOp( " @.a = 0.0001;\r\n", LangElement::find( getOutputTargetVarName(ShaderFeature::RenderTarget1) ) ) ); + } else meta->addStatement( new GenOp( " @;\r\n", assignColor( toneMapColor, blendOp ) ) ); @@ -1151,6 +1391,7 @@ void TonemapFeatGLSL::setTexData( Material::StageData &stageDat, if ( tex ) { passData.mTexType[ texIndex ] = Material::ToneMapTex; + passData.mSamplerNames[ texIndex ] = "toneMap"; passData.mTexSlot[ texIndex++ ].texObject = tex; } } @@ -1194,6 +1435,7 @@ void VertLitGLSL::processVert( Vector &componentList, ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *outColor = connectComp->getElement( RT_COLOR ); outColor->setName( "vertColor" ); + outColor->setStructName( "OUT" ); outColor->setType( "vec4" ); output = new GenOp( " @ = @;\r\n", outColor, inColor ); @@ -1221,6 +1463,7 @@ void VertLitGLSL::processPix( Vector &componentList, AssertFatal( connectComp, "VertLitGLSL::processVert - C_CONNECTOR is not a ShaderConnector" ); vertColor = connectComp->getElement( RT_COLOR ); vertColor->setName( "vertColor" ); + vertColor->setStructName( "IN" ); vertColor->setType( "vec4" ); } @@ -1239,12 +1482,11 @@ void VertLitGLSL::processPix( Vector &componentList, LangElement *finalVertColorDecl = new DecOp( finalVertColor ); // Reverse the tonemap - meta->addStatement( new GenOp( " @ = -1.0 * log(1.0 - @);\r\n", finalVertColorDecl, vertColor ) ); + meta->addStatement( new GenOp( " @ = -1.0f * log(1.0f - @);\r\n", finalVertColorDecl, vertColor ) ); // Set the blend op to tonemap blendOp = Material::ToneMap; outColor = finalVertColor; - } // Add in the realtime lighting contribution, if applicable @@ -1253,7 +1495,6 @@ void VertLitGLSL::processPix( Vector &componentList, Var *rtLightingColor = (Var*) LangElement::find( "d_lightcolor" ); if(rtLightingColor != NULL) { - // Find out if RTLighting should be added or substituted bool bPreProcessedLighting = false; AdvancedLightBinManager *lightBin; if ( Sim::findObject( "AL_LightBinMgr", lightBin ) ) @@ -1263,15 +1504,18 @@ void VertLitGLSL::processPix( Vector &componentList, // the dynamic light buffer, and it already has the baked-vertex-color // included in it if(bPreProcessedLighting) - outColor = new GenOp( "vec4(@.rgb, 1.0)", rtLightingColor ); + outColor = new GenOp( "float4(@.rgb, 1.0)", rtLightingColor ); else - outColor = new GenOp( "vec4(@.rgb, 0.0) + @", rtLightingColor, outColor ); + outColor = new GenOp( "float4(@.rgb + @.rgb, 1.0)", rtLightingColor, outColor ); } } // Output the color if ( fd.features[MFT_LightbufferMRT] ) + { meta->addStatement( new GenOp( " @;\r\n", assignColor( outColor, Material::None, NULL, ShaderFeature::RenderTarget1 ) ) ); + meta->addStatement( new GenOp( " @.a = 0.0001;\r\n", LangElement::find( getOutputTargetVarName(ShaderFeature::RenderTarget1) ) ) ); + } else meta->addStatement( new GenOp( " @;\r\n", assignColor( outColor, blendOp ) ) ); @@ -1283,7 +1527,6 @@ U32 VertLitGLSL::getOutputTargets( const MaterialFeatureData &fd ) const return fd.features[MFT_LightbufferMRT] ? ShaderFeature::RenderTarget1 : ShaderFeature::DefaultTarget; } - //**************************************************************************** // Detail map //**************************************************************************** @@ -1319,7 +1562,7 @@ void DetailFeatGLSL::processPix( Vector &componentList, // TODO: We could add a feature to toggle between this // and a simple multiplication with the detail map. - LangElement *statement = new GenOp( "( texture2D(@, @) * 2.0 ) - 1.0", detailMap, inTex ); + LangElement *statement = new GenOp( "( tex2D(@, @) * 2.0 ) - 1.0", detailMap, inTex ); output = new GenOp( " @;\r\n", assignColor( statement, Material::Add ) ); } @@ -1339,7 +1582,10 @@ void DetailFeatGLSL::setTexData( Material::StageData &stageDat, { GFXTextureObject *tex = stageDat.getTex( MFT_DetailMap ); if ( tex ) + { + passData.mSamplerNames[texIndex] = "detailMap"; passData.mTexSlot[ texIndex++ ].texObject = tex; + } } @@ -1347,6 +1593,17 @@ void DetailFeatGLSL::setTexData( Material::StageData &stageDat, // Vertex position //**************************************************************************** +void VertPositionGLSL::determineFeature( Material *material, + const GFXVertexFormat *vertexFormat, + U32 stageNum, + const FeatureType &type, + const FeatureSet &features, + MaterialFeatureData *outFeatureData ) +{ + // This feature is always on! + outFeatureData->features.addFeature( type ); +} + void VertPositionGLSL::processVert( Vector &componentList, const MaterialFeatureData &fd ) { @@ -1365,7 +1622,8 @@ void VertPositionGLSL::processVert( Vector &componentList, Var *modelview = getModelView( componentList, fd.features[MFT_UseInstancing], meta ); - meta->addStatement( new GenOp( " @ = @ * vec4(@.xyz,1);\r\n", outPosition, modelview, inPosition ) ); + meta->addStatement( new GenOp( " @ = tMul(@, float4(@.xyz,1));\r\n", + outPosition, modelview, inPosition ) ); output = meta; } @@ -1378,6 +1636,11 @@ void VertPositionGLSL::processVert( Vector &componentList, void ReflectCubeFeatGLSL::processVert( Vector &componentList, const MaterialFeatureData &fd ) { + // search for vert normal + Var *inNormal = (Var*) LangElement::find( "normal" ); + if ( !inNormal ) + return; + MultiLine * meta = new MultiLine; // If a base or bump tex is present in the material, but not in the @@ -1395,7 +1658,8 @@ void ReflectCubeFeatGLSL::processVert( Vector &componentList, // grab connector texcoord register ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *outTex = connectComp->getElement( RT_TEXCOORD ); - outTex->setName( "outTexCoord" ); + outTex->setName( "texCoord" ); + outTex->setStructName( "OUT" ); outTex->setType( "vec2" ); outTex->mapsToSampler = true; @@ -1405,54 +1669,60 @@ void ReflectCubeFeatGLSL::processVert( Vector &componentList, } // create cubeTrans - Var *cubeTrans = new Var; - cubeTrans->setType( "mat3" ); - cubeTrans->setName( "cubeTrans" ); - cubeTrans->uniform = true; - cubeTrans->constSortPos = cspPrimitive; - - // create cubeEye position - Var *cubeEyePos = new Var; - cubeEyePos->setType( "vec3" ); - cubeEyePos->setName( "cubeEyePos" ); - cubeEyePos->uniform = true; - cubeEyePos->constSortPos = cspPrimitive; - - // search for vert normal - Var *inNormal = (Var*) LangElement::find( "normal" ); + bool useInstancing = fd.features[MFT_UseInstancing]; + Var *cubeTrans = getObjTrans( componentList, useInstancing, meta ); // cube vert position Var * cubeVertPos = new Var; cubeVertPos->setName( "cubeVertPos" ); - cubeVertPos->setType( "vec3" ); + cubeVertPos->setType( "vec3" ); LangElement *cubeVertPosDecl = new DecOp( cubeVertPos ); - meta->addStatement( new GenOp( " @ = @ * @.xyz;\r\n", + meta->addStatement( new GenOp( " @ = tMul(mat3( @ ), @).xyz;\r\n", cubeVertPosDecl, cubeTrans, LangElement::find( "position" ) ) ); // cube normal Var * cubeNormal = new Var; cubeNormal->setName( "cubeNormal" ); - cubeNormal->setType( "vec3" ); + cubeNormal->setType( "vec3" ); LangElement *cubeNormDecl = new DecOp( cubeNormal ); - meta->addStatement( new GenOp( " @ = normalize( @ * normalize(@).xyz );\r\n", + meta->addStatement( new GenOp( " @ = ( tMul( (@), vec4(@, 0) ) ).xyz;\r\n", cubeNormDecl, cubeTrans, inNormal ) ); + // grab the eye position + Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); + if ( !eyePos ) + { + eyePos = new Var( "eyePosWorld", "vec3" ); + eyePos->uniform = true; + eyePos->constSortPos = cspPass; + } + + // cube position + Var * cubePos = new Var; + cubePos->setName( "cubePos" ); + cubePos->setType( "vec3" ); + LangElement *cubePosDecl = new DecOp( cubePos ); + + meta->addStatement( new GenOp( " @ = vec3( @[3][0], @[3][1], @[3][2] );\r\n", + cubePosDecl, cubeTrans, cubeTrans, cubeTrans ) ); + // eye to vert Var * eyeToVert = new Var; eyeToVert->setName( "eyeToVert" ); - eyeToVert->setType( "vec3" ); + eyeToVert->setType( "vec3" ); LangElement *e2vDecl = new DecOp( eyeToVert ); - meta->addStatement( new GenOp( " @ = @ - @;\r\n", - e2vDecl, cubeVertPos, cubeEyePos ) ); + meta->addStatement( new GenOp( " @ = @ - ( @ - @ );\r\n", + e2vDecl, cubeVertPos, eyePos, cubePos ) ); // grab connector texcoord register ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *reflectVec = connectComp->getElement( RT_TEXCOORD ); reflectVec->setName( "reflectVec" ); - reflectVec->setType( "vec3" ); + reflectVec->setStructName( "OUT" ); + reflectVec->setType( "vec3" ); reflectVec->mapsToSampler = true; meta->addStatement( new GenOp( " @ = reflect(@, @);\r\n", reflectVec, eyeToVert, cubeNormal ) ); @@ -1476,7 +1746,7 @@ void ReflectCubeFeatGLSL::processPix( Vector &componentList, fd.materialFeatures[MFT_NormalMap] ) { // grab connector texcoord register - Var *inTex = getInTexCoord( "outTexCoord", "vec2", true, componentList ); + Var *inTex = getInTexCoord( "texCoord", "vec2", true, componentList ); // create texture var Var *newMap = new Var; @@ -1494,7 +1764,7 @@ void ReflectCubeFeatGLSL::processPix( Vector &componentList, glossColor = color; - meta->addStatement( new GenOp( " @ = texture2D( @, @ );\r\n", colorDecl, newMap, inTex ) ); + meta->addStatement( new GenOp( " @ = tex2D( @, @ );\r\n", colorDecl, newMap, inTex ) ); } } else @@ -1508,12 +1778,13 @@ void ReflectCubeFeatGLSL::processPix( Vector &componentList, ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *reflectVec = connectComp->getElement( RT_TEXCOORD ); reflectVec->setName( "reflectVec" ); + reflectVec->setStructName( "IN" ); reflectVec->setType( "vec3" ); reflectVec->mapsToSampler = true; // create cubemap var Var *cubeMap = new Var; - cubeMap->setType( "samplerCube" ); + cubeMap->setType( "samplerCUBE" ); cubeMap->setName( "cubeMap" ); cubeMap->uniform = true; cubeMap->sampler = true; @@ -1527,7 +1798,7 @@ void ReflectCubeFeatGLSL::processPix( Vector &componentList, if ( fd.materialFeatures[MFT_RTLighting] ) attn =(Var*)LangElement::find("d_NL_Att"); - LangElement *texCube = new GenOp( "textureCube( @, @ )", cubeMap, reflectVec ); + LangElement *texCube = new GenOp( "texCUBE( @, @ )", cubeMap, reflectVec ); LangElement *lerpVal = NULL; Material::BlendOp blendOp = Material::LerpAlpha; @@ -1583,22 +1854,27 @@ void ReflectCubeFeatGLSL::setTexData( Material::StageData &stageDat, !passData.mFeatureData.features[MFT_NormalMap] ) { GFXTextureObject *tex = stageDat.getTex( MFT_DetailMap ); - if ( tex && - stageFeatures.features[MFT_DiffuseMap] ) + if ( tex && stageFeatures.features[MFT_DiffuseMap] ) + { + passData.mSamplerNames[ texIndex ] = "diffuseMap"; passData.mTexSlot[ texIndex++ ].texObject = tex; + } else { tex = stageDat.getTex( MFT_NormalMap ); - if ( tex && - stageFeatures.features[ MFT_NormalMap ] ) + if ( tex && stageFeatures.features[ MFT_NormalMap ] ) + { + passData.mSamplerNames[ texIndex ] = "bumpMap"; passData.mTexSlot[ texIndex++ ].texObject = tex; + } } } if( stageDat.getCubemap() ) { passData.mCubeMap = stageDat.getCubemap(); + passData.mSamplerNames[texIndex] = "cubeMap"; passData.mTexType[texIndex++] = Material::Cube; } else @@ -1606,6 +1882,7 @@ void ReflectCubeFeatGLSL::setTexData( Material::StageData &stageDat, if( stageFeatures.features[MFT_CubeMap] ) { // assuming here that it is a scenegraph cubemap + passData.mSamplerNames[texIndex] = "cubeMap"; passData.mTexType[texIndex++] = Material::SGCube; } } @@ -1639,18 +1916,17 @@ void RTLightingFeatGLSL::processVert( Vector &componentList, Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); if ( !eyePos ) { - eyePos = new Var( "eyePosWorld", "float3" ); + eyePos = new Var( "eyePosWorld", "vec3" ); eyePos->uniform = true; eyePos->constSortPos = cspPass; } - //TODO: should this be the same as "Find the incoming vertex normal" below? Var *inPosition = (Var*)LangElement::find( "position" ); Var *outNormal = connectComp->getElement( RT_TEXCOORD ); outNormal->setName( "wsNormal" ); outNormal->setStructName( "OUT" ); - outNormal->setType( "float3" ); + outNormal->setType( "vec3" ); outNormal->mapsToSampler = false; // Transform the normal to world space. @@ -1665,9 +1941,7 @@ void RTLightingFeatGLSL::processVert( Vector &componentList, } // Find the incoming vertex normal. - Var *inNormal = (Var*)LangElement::find( "inNormal" ); - if ( !inNormal ) - inNormal = (Var*)LangElement::find( "normal" ); + Var *inNormal = (Var*)LangElement::find( "normal" ); // Skip out on realtime lighting if we don't have a normal // or we're doing some sort of baked lighting. @@ -1675,29 +1949,25 @@ void RTLightingFeatGLSL::processVert( Vector &componentList, fd.features[MFT_LightMap] || fd.features[MFT_ToneMap] || fd.features[MFT_VertLit] ) - return; - - // Get the transform to world space. - Var *objTrans = getObjTrans( componentList, fd.features[MFT_UseInstancing], meta ); + return; // If there isn't a normal map then we need to pass // the world space normal to the pixel shader ourselves. if ( !fd.features[MFT_NormalMap] ) { Var *outNormal = connectComp->getElement( RT_TEXCOORD ); - outNormal->setName( "outWsNormal" ); + outNormal->setName( "wsNormal" ); + outNormal->setStructName( "OUT" ); outNormal->setType( "vec3" ); outNormal->mapsToSampler = false; + + // Get the transform to world space. + Var *objTrans = getObjTrans( componentList, fd.features[MFT_UseInstancing], meta ); // Transform the normal to world space. - meta->addStatement( new GenOp( " @ = ( @ * vec4( normalize( @ ), 0.0 ) ).xyz;\r\n", outNormal, objTrans, inNormal ) ); + meta->addStatement( new GenOp( " @ = tMul( @, float4( normalize( @ ), 0.0 ) ).xyz;\r\n", outNormal, objTrans, inNormal ) ); } - // Get the input position. - Var *inPosition = (Var*)LangElement::find( "inPosition" ); - if ( !inPosition ) - inPosition = (Var*)LangElement::find( "position" ); - addOutWsPosition( componentList, fd.features[MFT_UseInstancing], meta ); output = meta; @@ -1723,41 +1993,25 @@ void RTLightingFeatGLSL::processPix( Vector &componentList, Var *wsNormal = (Var*)LangElement::find( "wsNormal" ); if ( !wsNormal ) { - Var *outWsNormal = connectComp->getElement( RT_TEXCOORD ); - outWsNormal->setName( "outWsNormal" ); - outWsNormal->setType( "vec3" ); + wsNormal = connectComp->getElement( RT_TEXCOORD ); + wsNormal->setName( "wsNormal" ); + wsNormal->setStructName( "IN" ); + wsNormal->setType( "vec3" ); - wsNormal = new Var( "wsNormal", "vec3" ); - - // If we loaded the normal its our resposibility + // If we loaded the normal its our responsibility // to normalize it... the interpolators won't. - meta->addStatement( new GenOp( " @ = normalize( @ );\r\n", - new DecOp( wsNormal ), outWsNormal ) ); + // + // Note we cast to half here to get partial precision + // optimized code which is an acceptable loss of + // precision for normals and performs much better + // on older Geforce cards. + // + meta->addStatement( new GenOp( " @ = normalize( half3( @ ) );\r\n", wsNormal, wsNormal ) ); } - Var *wsPosition = getInWsPosition( componentList ); - - // If we have a specular feature then we need to - // get the world space view vector to pass to the - // lighting calculation. - Var *wsView = new Var( "wsView", "vec3" ); - if ( fd.features[MFT_PixSpecular] ) - { - Var *eyePos = (Var*)LangElement::find( "eyePosWorld" ); - if ( !eyePos ) - { - eyePos = new Var; - eyePos->setType( "vec3" ); - eyePos->setName( "eyePosWorld" ); - eyePos->uniform = true; - eyePos->constSortPos = cspPass; - } - - meta->addStatement( new GenOp( " @ = normalize( @ - @ );\r\n", - new DecOp( wsView ), eyePos, wsPosition ) ); - } - else - meta->addStatement( new GenOp( " @ = vec3( 0 );\r\n", new DecOp( wsView ) ) ); + // Now the wsPosition and wsView. + Var *wsPosition = getInWsPosition( componentList ); + Var *wsView = getWsView( wsPosition, meta ); // Create temporaries to hold results of lighting. Var *rtShading = new Var( "rtShading", "vec4" ); @@ -1765,18 +2019,66 @@ void RTLightingFeatGLSL::processPix( Vector &componentList, meta->addStatement( new GenOp( " @; @;\r\n", new DecOp( rtShading ), new DecOp( specular ) ) ); - // Calculate the diffuse shading and specular powers. - meta->addStatement( new GenOp( " compute4Lights( @, @, @, @, @ );\r\n", - wsView, wsPosition, wsNormal, rtShading, specular ) ); - // Look for a light mask generated from a previous // feature (this is done for BL terrain lightmaps). - Var *lightMask = (Var*)LangElement::find( "lightMask" ); - if ( lightMask ) - meta->addStatement( new GenOp( " @.rgb *= @;\r\n", rtShading, lightMask ) ); + LangElement *lightMask = LangElement::find( "lightMask" ); + if ( !lightMask ) + lightMask = new GenOp( "float4( 1, 1, 1, 1 )" ); + + // Get all the light constants. + Var *inLightPos = new Var( "inLightPos", "vec4" ); + inLightPos->uniform = true; + inLightPos->arraySize = 3; + inLightPos->constSortPos = cspPotentialPrimitive; + + Var *inLightInvRadiusSq = new Var( "inLightInvRadiusSq", "vec4" ); + inLightInvRadiusSq->uniform = true; + inLightInvRadiusSq->constSortPos = cspPotentialPrimitive; + + Var *inLightColor = new Var( "inLightColor", "vec4" ); + inLightColor->uniform = true; + inLightColor->arraySize = 4; + inLightColor->constSortPos = cspPotentialPrimitive; + + Var *inLightSpotDir = new Var( "inLightSpotDir", "vec4" ); + inLightSpotDir->uniform = true; + inLightSpotDir->arraySize = 3; + inLightSpotDir->constSortPos = cspPotentialPrimitive; + + Var *inLightSpotAngle = new Var( "inLightSpotAngle", "vec4" ); + inLightSpotAngle->uniform = true; + inLightSpotAngle->constSortPos = cspPotentialPrimitive; + + Var *lightSpotFalloff = new Var( "inLightSpotFalloff", "vec4" ); + lightSpotFalloff->uniform = true; + lightSpotFalloff->constSortPos = cspPotentialPrimitive; + + Var *specularPower = new Var( "specularPower", "float" ); + specularPower->uniform = true; + specularPower->constSortPos = cspPotentialPrimitive; + + Var *specularColor = (Var*)LangElement::find( "specularColor" ); + if ( !specularColor ) + { + specularColor = new Var( "specularColor", "vec4" ); + specularColor->uniform = true; + specularColor->constSortPos = cspPotentialPrimitive; + } + + Var *ambient = new Var( "ambient", "vec4" ); + ambient->uniform = true; + ambient->constSortPos = cspPass; + + // Calculate the diffuse shading and specular powers. + meta->addStatement( new GenOp( " compute4Lights( @, @, @, @,\r\n" + " @, @, @, @, @, @, @, @,\r\n" + " @, @ );\r\n", + wsView, wsPosition, wsNormal, lightMask, + inLightPos, inLightInvRadiusSq, inLightColor, inLightSpotDir, inLightSpotAngle, lightSpotFalloff, specularPower, specularColor, + rtShading, specular ) ); // Apply the lighting to the diffuse color. - LangElement *lighting = new GenOp( "vec4( @.rgb + ambient.rgb, 1 )", rtShading ); + LangElement *lighting = new GenOp( "float4( @.rgb + @.rgb, 1 )", rtShading, ambient ); meta->addStatement( new GenOp( " @;\r\n", assignColor( lighting, Material::Mul ) ) ); output = meta; } @@ -1844,6 +2146,7 @@ void FogFeatGLSL::processVert( Vector &componentList, ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *fogAmount = connectComp->getElement( RT_TEXCOORD ); fogAmount->setName( "fogAmount" ); + fogAmount->setStructName( "OUT" ); fogAmount->setType( "float" ); fogAmount->mapsToSampler = false; @@ -1891,6 +2194,7 @@ void FogFeatGLSL::processPix( Vector &componentList, ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); fogAmount = connectComp->getElement( RT_TEXCOORD ); fogAmount->setName( "fogAmount" ); + fogAmount->setStructName( "IN" ); fogAmount->setType( "float" ); } else @@ -1917,7 +2221,7 @@ void FogFeatGLSL::processPix( Vector &componentList, } // Lerp between the fog color and diffuse color. - LangElement *fogLerp = new GenOp( "mix( @.rgb, @.rgb, @ )", fogColor, color, fogAmount ); + LangElement *fogLerp = new GenOp( "lerp( @.rgb, @.rgb, @ )", fogColor, color, fogAmount ); meta->addStatement( new GenOp( " @.rgb = @;\r\n", color, fogLerp ) ); output = meta; @@ -1935,42 +2239,59 @@ ShaderFeature::Resources FogFeatGLSL::getResources( const MaterialFeatureData &f // Visibility //**************************************************************************** +VisibilityFeatGLSL::VisibilityFeatGLSL() + : mTorqueDep( "shaders/common/gl/torque.glsl" ) +{ + addDependency( &mTorqueDep ); +} + void VisibilityFeatGLSL::processVert( Vector &componentList, const MaterialFeatureData &fd ) { - // Pass screen space position to pixel shader to compute a full screen buffer uv - Var* ssPos = ( Var* ) LangElement::find( "screenspacePos" ); - if( !ssPos ) + MultiLine *meta = new MultiLine; + output = meta; + + if ( fd.features[ MFT_UseInstancing ] ) { - ShaderConnector* connectComp = dynamic_cast< ShaderConnector* >( componentList[C_CONNECTOR] ); - AssertFatal( connectComp, "VisibilityFeatGLSL::processVert - invalid ShaderConnector" ); + // We pass the visibility to the pixel shader via + // another output register. + // + // TODO: We should see if we can share this register + // with some other common instanced data. + // + ShaderConnector *conn = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outVisibility = conn->getElement( RT_TEXCOORD ); + outVisibility->setStructName( "OUT" ); + outVisibility->setName( "visibility" ); + outVisibility->setType( "float" ); + + ShaderConnector *vertStruct = dynamic_cast( componentList[C_VERT_STRUCT] ); + Var *instVisibility = vertStruct->getElement( RT_TEXCOORD, 1 ); + instVisibility->setStructName( "IN" ); + instVisibility->setName( "inst_visibility" ); + instVisibility->setType( "float" ); + mInstancingFormat->addElement( "visibility", GFXDeclType_Float, instVisibility->constNum ); - Var* ssPos = connectComp->getElement( RT_TEXCOORD ); - ssPos->setName( "screenspacePos" ); - ssPos->setType( "vec4" ); - - Var* outPosition = ( Var* ) LangElement::find( "gl_Position" ); - AssertFatal( outPosition, "VisibilityFeatGLSL::processVert - No gl_Position" ); - - output = new GenOp( " @ = @;\r\n", ssPos, outPosition ); + meta->addStatement( new GenOp( " @ = @; // Instancing!\r\n", outVisibility, instVisibility ) ); } + + if ( fd.features[ MFT_IsTranslucent ] ) + return; + + addOutVpos( meta, componentList ); } void VisibilityFeatGLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) -{ - // Look up output color. - - Var* color = ( Var* ) LangElement::find( "col" ); - if( !color ) +{ + // Get the visibility constant. + Var *visibility = NULL; + if ( fd.features[ MFT_UseInstancing ] ) + visibility = getInTexCoord( "visibility", "float", false, componentList ); + else { - output = NULL; - return; - } + visibility = (Var*)LangElement::find( "visibility" ); - Var* visibility = (Var*)LangElement::find( "visibility" ); - - // Create visibility var. if ( !visibility ) { visibility = new Var(); @@ -1979,94 +2300,36 @@ void VisibilityFeatGLSL::processPix( Vector &componentList, visibility->uniform = true; visibility->constSortPos = cspPotentialPrimitive; } - - Var* ssPos = ( Var* ) LangElement::find( "screenspacePos" ); - if( !ssPos ) - { - ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); - AssertFatal( connectComp, "VisibilityFeatGLSL::processPix - invalid ShaderConnector" ); - - ssPos = connectComp->getElement( RT_TEXCOORD ); - ssPos->setName( "screenspacePos" ); - ssPos->setType( "vec4" ); } MultiLine* meta = new MultiLine; output = meta; - //Compute final visibility from incoming visibility and strength of fade effects - Var *finalVisibility = new Var( "finalVisibility", "float" ); - meta->addStatement(new GenOp( " @ = @;\r\n", new DecOp( finalVisibility ), visibility ) ); - Var *imposterFade = (Var*) LangElement::find( "imposterFade" ); - if ( imposterFade ) - meta->addStatement( new GenOp( " @ *= @;\r\n", finalVisibility, imposterFade ) ); - Var *foliageFade = (Var*) LangElement::find( "foliageFade" ); - if ( foliageFade ) - meta->addStatement( new GenOp( " @ *= @;\r\n", finalVisibility, foliageFade ) ); - - if( !fd.features[ MFT_IsTranslucent ] ) + // Translucent objects do a simple alpha fade. + if ( fd.features[ MFT_IsTranslucent ] ) { - // Create fizzMap sampler. - - Var* fizzMap = ( Var* ) LangElement::find( "fizzMap" ); - if( !fizzMap ) - { - fizzMap = new Var; - fizzMap->setType( "sampler2D" ); - fizzMap->setName( "fizzMap" ); - fizzMap->uniform = true; - fizzMap->sampler = true; - fizzMap->constNum = Var::getTexUnitNum(); + Var *color = (Var*) LangElement::find( "col" ); + meta->addStatement( new GenOp( " @.a *= @;\r\n", color, visibility ) ); + return; } - // Create fizzScale uniform. - - Var* fizzScale = new Var; - fizzScale->setType( "float2" ); - fizzScale->setName( "fizzScale" ); - fizzScale->uniform = true; - fizzScale->constSortPos = cspPass; - - meta->addStatement( new GenOp( " float fizz = tex2D( @, (@.xy / @.w) * fizzScale ).r + 0.15;\r\n", fizzMap, ssPos, ssPos ) ); - meta->addStatement( new GenOp( " if( ( fizz * @ - 0.329 ) < 0.0 )\r\n" - " discard\r\n;", finalVisibility ) ); - } - else if( color ) - { - meta->addStatement( new GenOp( " @.w *= @;\r\n", color, finalVisibility ) ); - } + // Everything else does a fizzle. + Var *vPos = getInVpos( meta, componentList ); + meta->addStatement( new GenOp( " fizzle( @, @ );\r\n", vPos, visibility ) ); } ShaderFeature::Resources VisibilityFeatGLSL::getResources( const MaterialFeatureData &fd ) { Resources res; + // TODO: Fix for instancing. + if ( !fd.features[ MFT_IsTranslucent ] ) - { - res.numTex = 1; res.numTexReg = 1; - } return res; } -void VisibilityFeatGLSL::setTexData( Material::StageData &stageDat, - const MaterialFeatureData &fd, - RenderPassData &passData, - U32 &texIndex ) -{ - if ( !fd.features[ MFT_IsTranslucent ] ) - { - GFXTexHandle texHandle( "core/art/fizz_noise.dds", &GFXDefaultStaticDiffuseProfile, "VisibilityFeatHLSL_fizz_noise" ); - stageDat.setTex( MFT_Visibility, texHandle ); - - GFXTextureObject *tex = stageDat.getTex( MFT_Visibility ); - if ( tex ) - passData.mTexSlot[ texIndex++ ].texObject = tex; - } -} - - //**************************************************************************** // AlphaTest //**************************************************************************** @@ -2100,7 +2363,7 @@ void AlphaTestGLSL::processPix( Vector &componentList, alphaTestVal->constSortPos = cspPotentialPrimitive; // Do the clip. - output = new GenOp( " if ( ( @.a - @ ) < 0 ) discard;\r\n", color, alphaTestVal ); + output = new GenOp( " clip( @.a - @ );\r\n", color, alphaTestVal ); } @@ -2123,6 +2386,7 @@ void GlowMaskGLSL::processPix( Vector &componentList, output = new GenOp( " @.rgb = 0;\r\n", color ); } + //**************************************************************************** // RenderTargetZero //**************************************************************************** @@ -2140,10 +2404,11 @@ void RenderTargetZeroGLSL::processPix( Vector &componentList, //**************************************************************************** HDROutGLSL::HDROutGLSL() -: mTorqueDep( "shaders/common/gl/torque.glsl" ) + : mTorqueDep( "shaders/common/gl/torque.glsl" ) { addDependency( &mTorqueDep ); } + void HDROutGLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) { @@ -2153,7 +2418,6 @@ void HDROutGLSL::processPix( Vector &componentList, output = new GenOp( " @ = hdrEncode( @ );\r\n", color, color ); } - //**************************************************************************** // FoliageFeatureGLSL //**************************************************************************** @@ -2169,42 +2433,23 @@ FoliageFeatureGLSL::FoliageFeatureGLSL() void FoliageFeatureGLSL::processVert( Vector &componentList, const MaterialFeatureData &fd ) { - - MultiLine *meta = new MultiLine; - // Get the input variables we need. Var *inPosition = (Var*)LangElement::find( "inPosition" ); - if ( !inPosition ) { - // inPosition = (Var*)LangElement::find( "position" ); - inPosition = new Var( "inPosition", "vec3" ); - meta->addStatement( new GenOp( " @ = @;\n", new DecOp( inPosition ), (Var*)LangElement::find( "position" ) ) ); - } + if ( !inPosition ) + inPosition = (Var*)LangElement::find( "position" ); Var *inColor = (Var*)LangElement::find( "diffuse" ); - Var *outColor = new Var( "inDiffuse", "vec4" ); - meta->addStatement( new GenOp( " @ = @;\n", new DecOp( outColor ), inColor ) ); Var *inParams = (Var*)LangElement::find( "texCoord" ); - Var *outParams = getOutTexCoord( "texCoord", - "vec2", - true, - fd.features[MFT_TexAnim], - meta, - componentList ); - + MultiLine *meta = new MultiLine; // Declare the normal and tangent variables since they do not exist // in this vert type, but we do need to set them up for others. - Var *inNormal = (Var*)LangElement::find( "inNormal" ); - if ( !inNormal ) { - inNormal = new Var( "inNormal", "vec3" ); - meta->addStatement( new GenOp( " @ = @;\n", new DecOp( inNormal ), (Var*)LangElement::find( "normal" ) ) ); - } - //Var *normal = (Var*)LangElement::find( "normal" ); - AssertFatal( inNormal, "FoliageFeatureHLSL requires vert normal!" ); + Var *normal = (Var*)LangElement::find( "normal" ); + AssertFatal( normal, "FoliageFeatureGLSL requires vert normal!" ); Var *tangent = new Var; tangent->setType( "vec3" ); @@ -2216,6 +2461,7 @@ void FoliageFeatureGLSL::processVert( Vector &componentList, ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *fade = connectComp->getElement( RT_TEXCOORD ); fade->setName( "foliageFade" ); + fade->setStructName( "OUT" ); fade->setType( "float" ); // grab the eye position @@ -2227,12 +2473,11 @@ void FoliageFeatureGLSL::processVert( Vector &componentList, eyePos->constSortPos = cspPass; } - // All actual work is offloaded to this method. - meta->addStatement( new GenOp( " foliageProcessVert( @, @, @, @, @, @, @ );\r\n", inPosition, outColor, inParams, outParams, inNormal, tangent, eyePos ) ); + meta->addStatement( new GenOp( " foliageProcessVert( @, @, @, @, @, @ );\r\n", inPosition, inColor, inParams, normal, tangent, eyePos ) ); // Assign to foliageFade. InColor.a was set to the correct value inside foliageProcessVert. - meta->addStatement( new GenOp( " @ = @.a;\r\n", fade, outColor ) ); + meta->addStatement( new GenOp( " @ = @.a;\r\n", fade, inColor ) ); output = meta; } @@ -2244,7 +2489,26 @@ void FoliageFeatureGLSL::processPix( Vector &componentList, ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *fade = connectComp->getElement( RT_TEXCOORD ); fade->setName( "foliageFade" ); + fade->setStructName( "IN" ); fade->setType( "float" ); + + // Find / create visibility + Var *visibility = (Var*) LangElement::find( "visibility" ); + if ( !visibility ) + { + visibility = new Var(); + visibility->setType( "float" ); + visibility->setName( "visibility" ); + visibility->uniform = true; + visibility->constSortPos = cspPotentialPrimitive; + } + + MultiLine *meta = new MultiLine; + + // Multiply foliageFade into visibility. + meta->addStatement( new GenOp( " @ *= @;\r\n", visibility, fade ) ); + + output = meta; } void FoliageFeatureGLSL::determineFeature( Material *material, const GFXVertexFormat *vertexFormat, U32 stageNum, const FeatureType &type, const FeatureSet &features, MaterialFeatureData *outFeatureData ) @@ -2255,6 +2519,7 @@ void FoliageFeatureGLSL::determineFeature( Material *material, const GFXVertexFo outFeatureData->features.addFeature( type ); } + ShaderFeatureConstHandles* FoliageFeatureGLSL::createConstHandles( GFXShader *shader, SimObject *userObject ) { GroundCover *gcover = dynamic_cast< GroundCover* >( userObject ); @@ -2269,10 +2534,6 @@ ShaderFeatureConstHandles* FoliageFeatureGLSL::createConstHandles( GFXShader *sh } -//**************************************************************************** -// ParticleNormal -//**************************************************************************** - void ParticleNormalFeatureGLSL::processVert(Vector &componentList, const MaterialFeatureData &fd) { MultiLine *meta = new MultiLine; @@ -2292,7 +2553,7 @@ void ParticleNormalFeatureGLSL::processVert(Vector &componentL // screen because there is a discontinuity at (0, 1, 0) for gbuffer encoding. Do not // cause this value to be (0, -1, 0) or interlaced normals will be discontinuous. // [11/23/2009 Pat] - meta->addStatement(new GenOp(" @ = vec3(0.0, -0.97, 0.14);\r\n", new DecOp(normal))); + meta->addStatement(new GenOp(" @ = float3(0.0, -0.97, 0.14);\r\n", new DecOp(normal))); } Var *T = (Var*) LangElement::find( "T" ); @@ -2301,17 +2562,16 @@ void ParticleNormalFeatureGLSL::processVert(Vector &componentL T = new Var; T->setType( "vec3" ); T->setName( "T" ); - meta->addStatement(new GenOp(" @ = vec3(0.0, 0.0, -1.0);\r\n", new DecOp(T))); + meta->addStatement(new GenOp(" @ = float3(0.0, 0.0, -1.0);\r\n", new DecOp(T))); } } - //**************************************************************************** // ImposterVertFeatureGLSL //**************************************************************************** ImposterVertFeatureGLSL::ImposterVertFeatureGLSL() -: mDep( "shaders/common/gl/imposter.glsl" ) + : mDep( "shaders/common/gl/imposter.glsl" ) { addDependency( &mDep ); } @@ -2338,7 +2598,7 @@ void ImposterVertFeatureGLSL::processVert( Vector &component Var *imposterUVs = new Var; imposterUVs->setType( "vec4" ); imposterUVs->setName( "imposterUVs" ); - imposterUVs->arraySize = 64; // See imposter.hlsl + imposterUVs->arraySize = 64; // See imposter.glsl imposterUVs->uniform = true; imposterUVs->constSortPos = cspPotentialPrimitive; @@ -2362,15 +2622,15 @@ void ImposterVertFeatureGLSL::processVert( Vector &component meta->addStatement( new GenOp( " @;\r\n", new DecOp( outTexCoord ) ) ); Var *outWorldToTangent = new Var; - outWorldToTangent->setType( "mat3" ); + outWorldToTangent->setType( "float3x3" ); outWorldToTangent->setName( "worldToTangent" ); meta->addStatement( new GenOp( " @;\r\n", new DecOp( outWorldToTangent ) ) ); - //Var *outWorldToTangent = getOutWorldToTangent( componentList, meta, fd ); // Add imposterFade to the OUT structure. ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *outFade = connectComp->getElement( RT_TEXCOORD ); outFade->setName( "imposterFade" ); + outFade->setStructName( "OUT" ); outFade->setType( "float" ); // Assign OUT.imposterFade @@ -2410,7 +2670,7 @@ void ImposterVertFeatureGLSL::processVert( Vector &component // If we new viewToTangent... its the same as the // world to tangent for an imposter. Var *viewToTangent = new Var; - viewToTangent->setType( "mat3" ); + viewToTangent->setType( "float3x3" ); viewToTangent->setName( "viewToTangent" ); meta->addStatement( new GenOp( " @ = @;\r\n", new DecOp( viewToTangent ), outWorldToTangent ) ); } @@ -2418,14 +2678,13 @@ void ImposterVertFeatureGLSL::processVert( Vector &component void ImposterVertFeatureGLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) { - // Find / create IN.imposterFade ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *fade = connectComp->getElement( RT_TEXCOORD ); fade->setName( "imposterFade" ); + fade->setStructName( "IN" ); fade->setType( "float" ); - /* // Find / create visibility Var *visibility = (Var*) LangElement::find( "visibility" ); if ( !visibility ) @@ -2440,10 +2699,9 @@ void ImposterVertFeatureGLSL::processPix( Vector &componentLis MultiLine *meta = new MultiLine; // Multiply foliageFade into visibility. - //meta->addStatement( new GenOp( " @ *= @;\r\n", visibility, fade ) ); + meta->addStatement( new GenOp( " @ *= @;\r\n", visibility, fade ) ); output = meta; - */ } void ImposterVertFeatureGLSL::determineFeature( Material *material, @@ -2455,4 +2713,5 @@ void ImposterVertFeatureGLSL::determineFeature( Material *material, { if ( features.hasFeature( MFT_ImposterVert ) ) outFeatureData->features.addFeature( MFT_ImposterVert ); -} \ No newline at end of file +} + diff --git a/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.h b/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.h index 14af0300f..a0e831e93 100644 --- a/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.h +++ b/Engine/source/shaderGen/GLSL/shaderFeatureGLSL.h @@ -25,9 +25,6 @@ #ifndef _SHADERFEATURE_H_ #include "shaderGen/shaderFeature.h" #endif -#ifndef _MATERIALFEATUREDATA_H_ - #include "materials/materialFeatureData.h" -#endif struct LangElement; struct MaterialFeatureData; @@ -54,13 +51,25 @@ public: bool mapsToSampler, Vector &componentList ); + static Var* getInColor( const char *name, + const char *type, + Vector &componentList ); + + /// + static Var* addOutVpos( MultiLine *meta, + Vector &componentList ); + + /// Returns the VPOS input register for the pixel shader. + static Var* getInVpos( MultiLine *meta, + Vector &componentList ); + /// Returns the "objToTangentSpace" transform or creates one if this /// is the first feature to need it. Var* getOutObjToTangentSpace( Vector &componentList, MultiLine *meta, const MaterialFeatureData &fd ); - /// Returns the existing output "worldToTangent" transform or + /// Returns the existing output "outWorldToTangent" transform or /// creates one if this is the first feature to need it. Var* getOutWorldToTangent( Vector &componentList, MultiLine *meta, @@ -70,7 +79,7 @@ public: /// adding it to the input connector if it doesn't exist. static Var* getInWorldToTangent( Vector &componentList ); - /// Returns the existing output "viewToTangent" transform or + /// Returns the existing output "outViewToTangent" transform or /// creates one if this is the first feature to need it. Var* getOutViewToTangent( Vector &componentList, MultiLine *meta, @@ -81,17 +90,16 @@ public: static Var* getInViewToTangent( Vector &componentList ); /// Calculates the world space position in the vertex shader and - /// assigns it to the passed language element. It does not pass /// it across the connector to the pixel shader. + /// assigns it to the passed language element. It does not pass + /// it across the connector to the pixel shader. /// @see addOutWsPosition void getWsPosition( Vector &componentList, - bool useInstancing, MultiLine *meta, LangElement *wsPosition ); /// Adds the "wsPosition" to the input connector if it doesn't exist. Var* addOutWsPosition( Vector &componentList, - bool useInstancing, MultiLine *meta ); @@ -129,7 +137,6 @@ public: bool useInstancing, MultiLine *meta ); - // ShaderFeature Var* getVertTexCoord( const String &name ); LangElement* setupTexSpaceMat( Vector &componentList, Var **texSpaceMat ); @@ -151,25 +158,27 @@ public: virtual String getName() { return mName; } }; - class RenderTargetZeroGLSL : public ShaderFeatureGLSL { -protected: ShaderFeature::OutputTarget mOutputTargetMask; +protected: + ShaderFeature::OutputTarget mOutputTargetMask; String mFeatureName; public: RenderTargetZeroGLSL( const ShaderFeature::OutputTarget target ) : mOutputTargetMask( target ) { - char buffer[256]; dSprintf(buffer, sizeof(buffer), "Render Target Output = 0.0, output mask %04b", mOutputTargetMask); - mFeatureName = buffer; } + char buffer[256]; + dSprintf(buffer, sizeof(buffer), "Render Target Output = 0.0, output mask %04b", mOutputTargetMask); + mFeatureName = buffer; + } virtual String getName() { return mFeatureName; } virtual void processPix( Vector &componentList, const MaterialFeatureData &fd ); - virtual U32 getOutputTargets( const MaterialFeatureData &fd ) const { return - mOutputTargetMask; } + + virtual U32 getOutputTargets( const MaterialFeatureData &fd ) const { return mOutputTargetMask; } }; @@ -190,11 +199,7 @@ public: U32 stageNum, const FeatureType &type, const FeatureSet &features, - MaterialFeatureData *outFeatureData ) - { - // This feature is always on! - outFeatureData->features.addFeature( type ); - } + MaterialFeatureData *outFeatureData ); }; @@ -297,7 +302,6 @@ public: } }; - /// Diffuse vertex color class DiffuseVertColorFeatureGLSL : public ShaderFeatureGLSL { @@ -316,7 +320,6 @@ public: } }; - /// Lightmap class LightmapFeatGLSL : public ShaderFeatureGLSL { @@ -491,7 +494,14 @@ public: /// Visibility class VisibilityFeatGLSL : public ShaderFeatureGLSL { +protected: + + ShaderIncludeDependency mTorqueDep; + public: + + VisibilityFeatGLSL(); + virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); @@ -500,11 +510,6 @@ public: virtual Resources getResources( const MaterialFeatureData &fd ); - virtual void setTexData( Material::StageData &stageDat, - const MaterialFeatureData &fd, - RenderPassData &passData, - U32 &texIndex ); - virtual Material::BlendOp getBlendOp() { return Material::None; } virtual String getName() @@ -547,10 +552,10 @@ public: } }; - /// This should be the final feature on most pixel shaders which /// encodes the color for the current HDR target format. -/// @see HDRPostFx/// @see LightManager +/// @see HDRPostFx +/// @see LightManager /// @see torque.glsl class HDROutGLSL : public ShaderFeatureGLSL { @@ -570,9 +575,9 @@ public: virtual String getName() { return "HDR Output"; } }; - /// -class FoliageFeatureGLSL : public ShaderFeatureGLSL{ +class FoliageFeatureGLSL : public ShaderFeatureGLSL +{ protected: ShaderIncludeDependency mDep; @@ -583,6 +588,7 @@ public: virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, const MaterialFeatureData &fd ); @@ -597,11 +603,10 @@ public: const FeatureType &type, const FeatureSet &features, MaterialFeatureData *outFeatureData ); + virtual ShaderFeatureConstHandles* createConstHandles( GFXShader *shader, SimObject *userObject ); }; - -/// class ParticleNormalFeatureGLSL : public ShaderFeatureGLSL { public: @@ -616,6 +621,9 @@ public: }; + +/// Special feature for unpacking imposter verts. +/// @see RenderImposterMgr class ImposterVertFeatureGLSL : public ShaderFeatureGLSL { protected: @@ -623,9 +631,12 @@ protected: ShaderIncludeDependency mDep; public: - ImposterVertFeatureGLSL(); + + ImposterVertFeatureGLSL(); + virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); + virtual void processPix( Vector &componentList, const MaterialFeatureData &fd ); diff --git a/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp b/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp index ebbf2b3d8..f694b11e3 100644 --- a/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp +++ b/Engine/source/shaderGen/GLSL/shaderGenGLSL.cpp @@ -56,7 +56,9 @@ void ShaderGenPrinterGLSL::printMainComment( Stream& stream ) void ShaderGenPrinterGLSL::printVertexShaderCloser( Stream& stream ) { - const char *closer = "}\r\n"; + // We are render OpenGL upside down for use DX9 texture coords. + // Must be the last vertex feature. + const char *closer = " gl_Position.y *= -1;\r\n}\r\n"; stream.write( dStrlen(closer), closer ); } @@ -67,7 +69,7 @@ void ShaderGenPrinterGLSL::printPixelShaderOutputStruct( Stream& stream, const M void ShaderGenPrinterGLSL::printPixelShaderCloser( Stream& stream ) { - const char *closer = " gl_FragColor = col;\r\n}\r\n"; + const char *closer = " OUT_FragColor0 = col;\r\n}\r\n"; stream.write( dStrlen(closer), closer ); } @@ -124,6 +126,11 @@ ShaderComponent* ShaderGenComponentFactoryGLSL::createVertexInputConnector( cons var = vertComp->getElement( RT_TANGENT ); var->setName( "T" ); } + else if ( element.isSemantic( GFXSemantic::TANGENTW ) ) + { + var = vertComp->getElement( RT_TANGENTW ); + var->setName( "tangentW" ); + } else if ( element.isSemantic( GFXSemantic::BINORMAL ) ) { var = vertComp->getElement( RT_BINORMAL ); @@ -152,7 +159,7 @@ ShaderComponent* ShaderGenComponentFactoryGLSL::createVertexInputConnector( cons if ( !var ) continue; - var->setStructName( "" ); + var->setStructName( "IN" ); var->setType( typeToString( element.getType() ) ); } diff --git a/Engine/source/shaderGen/GLSL/shaderGenGLSLInit.cpp b/Engine/source/shaderGen/GLSL/shaderGenGLSLInit.cpp index 1318137bb..03634fa50 100644 --- a/Engine/source/shaderGen/GLSL/shaderGenGLSLInit.cpp +++ b/Engine/source/shaderGen/GLSL/shaderGenGLSLInit.cpp @@ -76,6 +76,8 @@ void _initShaderGenGLSL( ShaderGen *shaderGen ) FEATUREMGR->registerFeature( MFT_ParaboloidVertTransform, new ParaboloidVertTransformGLSL ); FEATUREMGR->registerFeature( MFT_IsSinglePassParaboloid, new NamedFeatureGLSL( "Single Pass Paraboloid" ) ); + FEATUREMGR->registerFeature( MFT_UseInstancing, new NamedFeatureGLSL( "Hardware Instancing" ) ); + FEATUREMGR->registerFeature( MFT_RenderTarget1_Zero, new RenderTargetZeroGLSL ( ShaderFeature::RenderTarget1 ) ); @@ -89,6 +91,10 @@ void _initShaderGenGLSL( ShaderGen *shaderGen ) FEATUREMGR->registerFeature( MFT_ImposterVert, new ImposterVertFeatureGLSL ); + //FEATUREMGR->registerFeature( MFT_LightbufferMRT, new NamedFeatureGLSL( "Lightbuffer MRT" ) ); + //FEATUREMGR->registerFeature( MFT_IsTranslucentZWrite, new NamedFeatureGLSL( "Translucent ZWrite" ) ); + //FEATUREMGR->registerFeature( MFT_InterlacedPrePass, new NamedFeatureGLSL( "Interlaced Pre Pass" ) ); + } MODULE_BEGIN( ShaderGenGLSL ) diff --git a/Engine/source/terrain/glsl/terrFeatureGLSL.cpp b/Engine/source/terrain/glsl/terrFeatureGLSL.cpp index c23d4d85d..63bc8cb60 100644 --- a/Engine/source/terrain/glsl/terrFeatureGLSL.cpp +++ b/Engine/source/terrain/glsl/terrFeatureGLSL.cpp @@ -30,22 +30,35 @@ #include "shaderGen/langElement.h" #include "shaderGen/shaderOp.h" #include "shaderGen/featureMgr.h" +#include "shaderGen/shaderGen.h" #include "core/module.h" +namespace +{ + void register_glsl_shader_features_for_terrain(GFXAdapterType type) + { + if(type != OpenGL) + return; + + FEATUREMGR->registerFeature( MFT_TerrainBaseMap, new TerrainBaseMapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_TerrainParallaxMap, new NamedFeatureGLSL( "Terrain Parallax Texture" ) ); + FEATUREMGR->registerFeature( MFT_TerrainDetailMap, new TerrainDetailMapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_TerrainNormalMap, new TerrainNormalMapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_TerrainMacroMap, new TerrainMacroMapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatGLSL ); + FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureGLSL( "Terrain Side Projection" ) ); + FEATUREMGR->registerFeature( MFT_TerrainAdditive, new TerrainAdditiveFeatGLSL ); + } + +}; MODULE_BEGIN( TerrainFeatGLSL ) - MODULE_INIT_AFTER( ShaderGenFeatureMgr ) + MODULE_INIT_AFTER( ShaderGen ) MODULE_INIT - { - FEATUREMGR->registerFeature( MFT_TerrainBaseMap, new TerrainBaseMapFeatGLSL ); - FEATUREMGR->registerFeature( MFT_TerrainParallaxMap, new TerrainParallaxMapFeatGLSL ); - FEATUREMGR->registerFeature( MFT_TerrainDetailMap, new TerrainDetailMapFeatGLSL ); - FEATUREMGR->registerFeature( MFT_TerrainNormalMap, new TerrainNormalMapFeatGLSL ); - FEATUREMGR->registerFeature( MFT_TerrainLightMap, new TerrainLightMapFeatGLSL ); - FEATUREMGR->registerFeature( MFT_TerrainSideProject, new NamedFeatureGLSL( "Terrain Side Projection" ) ); - FEATUREMGR->registerFeature( MFT_TerrainAdditive, new TerrainAdditiveFeatGLSL ); + { + SHADERGEN->getFeatureInitSignal().notify(®ister_glsl_shader_features_for_terrain); } MODULE_END; @@ -68,7 +81,7 @@ Var* TerrainFeatGLSL::_getUniformVar( const char *name, const char *type, Consta Var* TerrainFeatGLSL::_getInDetailCoord( Vector &componentList ) { - String name( String::ToString( "outDetCoord%d", getProcessIndex() ) ); + String name( String::ToString( "detCoord%d", getProcessIndex() ) ); Var *inDet = (Var*)LangElement::find( name ); if ( !inDet ) @@ -77,6 +90,7 @@ Var* TerrainFeatGLSL::_getInDetailCoord( Vector &componentList inDet = connectComp->getElement( RT_TEXCOORD ); inDet->setName( name ); + inDet->setStructName( "IN" ); inDet->setType( "vec4" ); inDet->mapsToSampler = true; } @@ -84,6 +98,25 @@ Var* TerrainFeatGLSL::_getInDetailCoord( Vector &componentList return inDet; } +Var* TerrainFeatGLSL::_getInMacroCoord( Vector &componentList ) +{ + String name( String::ToString( "macroCoord%d", getProcessIndex() ) ); + Var *inDet = (Var*)LangElement::find( name ); + + if ( !inDet ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + + inDet = connectComp->getElement( RT_TEXCOORD ); + inDet->setName( name ); + inDet->setStructName( "IN" ); + inDet->setType( "vec4" ); + inDet->mapsToSampler = true; + } + + return inDet; +} + Var* TerrainFeatGLSL::_getNormalMapTex() { String name( String::ToString( "normalMap%d", getProcessIndex() ) ); @@ -119,6 +152,24 @@ Var* TerrainFeatGLSL::_getDetailIdStrengthParallax() return detailInfo; } +Var* TerrainFeatGLSL::_getMacroIdStrengthParallax() +{ + String name( String::ToString( "macroIdStrengthParallax%d", getProcessIndex() ) ); + + Var *detailInfo = (Var*)LangElement::find( name ); + if ( !detailInfo ) + { + detailInfo = new Var; + detailInfo->setType( "vec3" ); + detailInfo->setName( name ); + detailInfo->uniform = true; + detailInfo->constSortPos = cspPotentialPrimitive; + } + + return detailInfo; +} + + void TerrainBaseMapFeatGLSL::processVert( Vector &componentList, const MaterialFeatureData &fd ) { @@ -146,7 +197,7 @@ void TerrainBaseMapFeatGLSL::processVert( Vector &componentLis // So instead i fixed this by flipping the base and detail // coord y scale to compensate when rendering. // - meta->addStatement( new GenOp( " @ = @.xyz * vec3( @, @, -@ );\r\n", + meta->addStatement( new GenOp( " @ = @.xyz * float3( @, @, -@ );\r\n", new DecOp( inTex ), inPos, oneOverTerrainSize, oneOverTerrainSize, oneOverTerrainSize ) ); } @@ -155,6 +206,7 @@ void TerrainBaseMapFeatGLSL::processVert( Vector &componentLis // Pass the texture coord to the pixel shader. Var *outTex = connectComp->getElement( RT_TEXCOORD ); outTex->setName( "outTexCoord" ); + outTex->setStructName( "OUT" ); outTex->setType( "vec3" ); outTex->mapsToSampler = true; meta->addStatement( new GenOp( " @.xy = @.xy;\r\n", outTex, inTex ) ); @@ -166,7 +218,7 @@ void TerrainBaseMapFeatGLSL::processVert( Vector &componentLis { Var *inNormal = (Var*)LangElement::find( "normal" ); meta->addStatement( - new GenOp( " @.z = pow( abs( dot( normalize( vec3( @.x, @.y, 0.0 ) ), vec3( 0, 1, 0 ) ) ), 10.0 );\r\n", + new GenOp( " @.z = pow( abs( dot( normalize( float3( @.x, @.y, 0 ) ), float3( 0, 1, 0 ) ) ), 10.0 );\r\n", outTex, inNormal, inNormal ) ); } else @@ -182,7 +234,7 @@ void TerrainBaseMapFeatGLSL::processVert( Vector &componentLis Var *inTangentZ = getVertTexCoord( "tcTangentZ" ); Var *inTanget = new Var( "T", "vec3" ); Var *squareSize = _getUniformVar( "squareSize", "float", cspPass ); - meta->addStatement( new GenOp( " @ = normalize( vec3( @, 0.0, @ ) );\r\n", + meta->addStatement( new GenOp( " @ = normalize( float3( @, 0, @ ) );\r\n", new DecOp( inTanget ), squareSize, inTangentZ ) ); } @@ -190,7 +242,7 @@ void TerrainBaseMapFeatGLSL::processPix( Vector &componentLis const MaterialFeatureData &fd ) { // grab connector texcoord register - Var *texCoord = getInTexCoord( "outTexCoord", "vec3", true, componentList ); + Var *texCoord = getInTexCoord( "texCoord", "vec3", true, componentList ); // We do nothing more if this is a prepass. if ( fd.features.hasFeature( MFT_PrePassConditioner ) ) @@ -209,7 +261,7 @@ void TerrainBaseMapFeatGLSL::processPix( Vector &componentLis Var *baseColor = new Var; baseColor->setType( "vec4" ); baseColor->setName( "baseColor" ); - meta->addStatement( new GenOp( " @ = texture2D( @, @.xy );\r\n", new DecOp( baseColor ), diffuseMap, texCoord ) ); + meta->addStatement( new GenOp( " @ = tex2D( @, @.xy );\r\n", new DecOp( baseColor ), diffuseMap, texCoord ) ); meta->addStatement( new GenOp( " @;\r\n", assignColor( baseColor, Material::Mul ) ) ); output = meta; @@ -228,8 +280,11 @@ ShaderFeature::Resources TerrainBaseMapFeatGLSL::getResources( const MaterialFea } TerrainDetailMapFeatGLSL::TerrainDetailMapFeatGLSL() - : mTerrainDep( "shaders/common/terrain/terrain.glsl" ) + : mTorqueDep( "shaders/common/gl/torque.glsl" ), + mTerrainDep( "shaders/common/terrain/terrain.glsl" ) + { + addDependency( &mTorqueDep ); addDependency( &mTerrainDep ); } @@ -238,13 +293,6 @@ void TerrainDetailMapFeatGLSL::processVert( Vector &component { const U32 detailIndex = getProcessIndex(); - - // If this is a prepass and we don't have a - // matching normal map... we have nothing to do. - if ( fd.features.hasFeature( MFT_PrePassConditioner ) && - !fd.features.hasFeature( MFT_TerrainNormalMap, detailIndex ) ) - return; - // Grab incoming texture coords... the base map feature // made sure this was created. Var *inTex = (Var*)LangElement::find( "texCoord" ); @@ -260,6 +308,26 @@ void TerrainDetailMapFeatGLSL::processVert( Vector &component MultiLine *meta = new MultiLine; + // If we have parallax mapping then make sure we've sent + // the negative view vector to the pixel shader. + if ( fd.features.hasFeature( MFT_TerrainParallaxMap ) && + !LangElement::find( "outNegViewTS" ) ) + { + // Get the object to tangent transform which + // will consume 3 output registers. + Var *objToTangentSpace = getOutObjToTangentSpace( componentList, meta, fd ); + + // Now use a single output register to send the negative + // view vector in tangent space to the pixel shader. + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outNegViewTS = connectComp->getElement( RT_TEXCOORD ); + outNegViewTS->setName( "outNegViewTS" ); + outNegViewTS->setStructName( "OUT" ); + outNegViewTS->setType( "vec3" ); + meta->addStatement( new GenOp( " @ = tMul( @, float3( @ - @.xyz ) );\r\n", + outNegViewTS, objToTangentSpace, eyePos, inPos ) ); + } + // Get the distance from the eye to this vertex. Var *dist = (Var*)LangElement::find( "dist" ); if ( !dist ) @@ -275,7 +343,8 @@ void TerrainDetailMapFeatGLSL::processVert( Vector &component // grab connector texcoord register ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); Var *outTex = connectComp->getElement( RT_TEXCOORD ); - outTex->setName( String::ToString( "outDetCoord%d", detailIndex ) ); + outTex->setName( String::ToString( "detCoord%d", detailIndex ) ); + outTex->setStructName( "OUT" ); outTex->setType( "vec4" ); outTex->mapsToSampler = true; @@ -293,7 +362,7 @@ void TerrainDetailMapFeatGLSL::processVert( Vector &component // its scale is flipped to correct for the non negative y // in texCoord. // - // See TerrainBaseMapFeatHLSL::processVert(). + // See TerrainBaseMapFeatGLSL::processVert(). // meta->addStatement( new GenOp( " @.xyz = @ * @.xyx;\r\n", outTex, inTex, detScaleAndFade ) ); @@ -308,17 +377,30 @@ void TerrainDetailMapFeatGLSL::processPix( Vector &component const MaterialFeatureData &fd ) { const U32 detailIndex = getProcessIndex(); + Var *inTex = getVertTexCoord( "texCoord" ); - // If this is a prepass and we don't have a - // matching normal map... we have nothing to do. - if ( fd.features.hasFeature( MFT_PrePassConditioner ) && - !fd.features.hasFeature( MFT_TerrainNormalMap, detailIndex ) ) - return; - - Var *inTex = getVertTexCoord( "outTexCoord" ); - MultiLine *meta = new MultiLine; + // We need the negative tangent space view vector + // as in parallax mapping we step towards the camera. + Var *negViewTS = (Var*)LangElement::find( "negViewTS" ); + if ( !negViewTS && + fd.features.hasFeature( MFT_TerrainParallaxMap ) ) + { + Var *inNegViewTS = (Var*)LangElement::find( "outNegViewTS" ); + if ( !inNegViewTS ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + inNegViewTS = connectComp->getElement( RT_TEXCOORD ); + inNegViewTS->setName( "outNegViewTS" ); + inNegViewTS->setStructName( "IN" ); + inNegViewTS->setType( "vec3" ); + } + + negViewTS = new Var( "negViewTS", "vec3" ); + meta->addStatement( new GenOp( " @ = normalize( @ );\r\n", new DecOp( negViewTS ), inNegViewTS ) ); + } + // Get the layer samples. Var *layerSample = (Var*)LangElement::find( "layerSample" ); if ( !layerSample ) @@ -336,7 +418,7 @@ void TerrainDetailMapFeatGLSL::processPix( Vector &component layerTex->constNum = Var::getTexUnitNum(); // Read the layer texture to get the samples. - meta->addStatement( new GenOp( " @ = round( texture2D( @, @.xy ) * 255.0f );\r\n", + meta->addStatement( new GenOp( " @ = round( tex2D( @, @.xy ) * 255.0f );\r\n", new DecOp( layerSample ), layerTex, inTex ) ); } @@ -372,17 +454,43 @@ void TerrainDetailMapFeatGLSL::processPix( Vector &component blendTotal = new Var; blendTotal->setName( "blendTotal" ); blendTotal->setType( "float" ); - meta->addStatement( new GenOp( " @ = 0.0;\r\n", new DecOp( blendTotal ) ) ); + meta->addStatement( new GenOp( " @ = 0;\r\n", new DecOp( blendTotal ) ) ); } // Add to the blend total. - meta->addStatement( new GenOp( " @ += @;\r\n", blendTotal, detailBlend ) ); - //meta->addStatement( new GenOp( " @ += @ * @.y * @.w;\r\n", - //blendTotal, detailBlend, detailInfo, inDet ) ); + meta->addStatement( new GenOp( " @ = max( @, @ );\r\n", blendTotal, blendTotal, detailBlend ) ); - // Nothing more to do for a detail texture in prepass. + // If we had a parallax feature... then factor in the parallax + // amount so that it fades out with the layer blending. + if ( fd.features.hasFeature( MFT_TerrainParallaxMap, detailIndex ) ) + { + // Get the rest of our inputs. + Var *normalMap = _getNormalMapTex(); + + // Call the library function to do the rest. + meta->addStatement( new GenOp( " @.xy += parallaxOffset( @, @.xy, @, @.z * @ );\r\n", + inDet, normalMap, inDet, negViewTS, detailInfo, detailBlend ) ); + } + + // If this is a prepass then we skip color. if ( fd.features.hasFeature( MFT_PrePassConditioner ) ) { + // Check to see if we have a gbuffer normal. + Var *gbNormal = (Var*)LangElement::find( "gbNormal" ); + + // If we have a gbuffer normal and we don't have a + // normal map feature then we need to lerp in a + // default normal else the normals below this layer + // will show thru. + if ( gbNormal && + !fd.features.hasFeature( MFT_TerrainNormalMap, detailIndex ) ) + { + Var *viewToTangent = getInViewToTangent( componentList ); + + meta->addStatement( new GenOp( " @ = lerp( @, tGetMatrix3Row(@, 2), min( @, @.w ) );\r\n", + gbNormal, gbNormal, viewToTangent, detailBlend, inDet ) ); + } + output = meta; return; } @@ -407,6 +515,7 @@ void TerrainDetailMapFeatGLSL::processPix( Vector &component // If we're using SM 3.0 then take advantage of // dynamic branching to skip layers per-pixel. + if ( GFX->getPixelShaderVersion() >= 3.0f ) meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) ); @@ -421,12 +530,12 @@ void TerrainDetailMapFeatGLSL::processPix( Vector &component // if ( fd.features.hasFeature( MFT_TerrainSideProject, detailIndex ) ) { - meta->addStatement( new GenOp( " @ = ( mix( texture2D( @, @.yz ), texture2D( @, @.xz ), @.z ) * 2.0 ) - 1.0;\r\n", + meta->addStatement( new GenOp( " @ = ( lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z ) * 2.0 ) - 1.0;\r\n", detailColor, detailMap, inDet, detailMap, inDet, inTex ) ); } else { - meta->addStatement( new GenOp( " @ = ( texture2D( @, @.xy ) * 2.0 ) - 1.0;\r\n", + meta->addStatement( new GenOp( " @ = ( tex2D( @, @.xy ) * 2.0 ) - 1.0;\r\n", detailColor, detailMap, inDet ) ); } @@ -436,7 +545,7 @@ void TerrainDetailMapFeatGLSL::processPix( Vector &component Var *baseColor = (Var*)LangElement::find( "baseColor" ); Var *outColor = (Var*)LangElement::find( "col" ); - meta->addStatement( new GenOp( " @ = mix( @, @ + @, @ );\r\n", + meta->addStatement( new GenOp( " @ = lerp( @, @ + @, @ );\r\n", outColor, outColor, baseColor, detailColor, detailBlend ) ); meta->addStatement( new GenOp( " }\r\n" ) ); @@ -448,28 +557,293 @@ ShaderFeature::Resources TerrainDetailMapFeatGLSL::getResources( const MaterialF { Resources res; + if ( getProcessIndex() == 0 ) + { + // If this is the first detail pass then we + // samples from the layer tex. + res.numTex += 1; + + // If this material also does parallax then it + // will generate the negative view vector and the + // worldToTanget transform. + if ( fd.features.hasFeature( MFT_TerrainParallaxMap ) ) + res.numTexReg += 4; + } + + // If this isn't the prepass then we sample + // from the detail texture for diffuse coloring. + if ( !fd.features.hasFeature( MFT_PrePassConditioner ) ) + res.numTex += 1; + + // If we have parallax for this layer then we'll also + // be sampling the normal map for the parallax heightmap. + if ( fd.features.hasFeature( MFT_TerrainParallaxMap, getProcessIndex() ) ) + res.numTex += 1; + + // Finally we always send the detail texture + // coord to the pixel shader. + res.numTexReg += 1; + + return res; +} + + +TerrainMacroMapFeatGLSL::TerrainMacroMapFeatGLSL() + : mTorqueDep( "shaders/common/gl/torque.glsl" ), + mTerrainDep( "shaders/common/terrain/terrain.glsl" ) + +{ + addDependency( &mTorqueDep ); + addDependency( &mTerrainDep ); +} + + +void TerrainMacroMapFeatGLSL::processVert( Vector &componentList, + const MaterialFeatureData &fd ) +{ + const U32 detailIndex = getProcessIndex(); + + // Grab incoming texture coords... the base map feature + // made sure this was created. + Var *inTex = (Var*)LangElement::find( "texCoord" ); + AssertFatal( inTex, "The texture coord is missing!" ); + + // Grab the input position. + Var *inPos = (Var*)LangElement::find( "inPosition" ); + if ( !inPos ) + inPos = (Var*)LangElement::find( "position" ); + + // Get the object space eye position. + Var *eyePos = _getUniformVar( "eyePos", "vec3", cspPotentialPrimitive ); + + MultiLine *meta = new MultiLine; + + // Get the distance from the eye to this vertex. + Var *dist = (Var*)LangElement::find( "macroDist" ); + if ( !dist ) + { + dist = new Var; + dist->setType( "float" ); + dist->setName( "macroDist" ); + + meta->addStatement( new GenOp( " @ = distance( @.xyz, @ );\r\n", + new DecOp( dist ), inPos, eyePos ) ); + } + + // grab connector texcoord register + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + Var *outTex = connectComp->getElement( RT_TEXCOORD ); + outTex->setName( String::ToString( "macroCoord%d", detailIndex ) ); + outTex->setStructName( "OUT" ); + outTex->setType( "vec4" ); + outTex->mapsToSampler = true; + + // Get the detail scale and fade info. + Var *detScaleAndFade = new Var; + detScaleAndFade->setType( "vec4" ); + detScaleAndFade->setName( String::ToString( "macroScaleAndFade%d", detailIndex ) ); + detScaleAndFade->uniform = true; + detScaleAndFade->constSortPos = cspPotentialPrimitive; + + // Setup the detail coord. + meta->addStatement( new GenOp( " @.xyz = @ * @.xyx;\r\n", outTex, inTex, detScaleAndFade ) ); + + // And sneak the detail fade thru the w detailCoord. + meta->addStatement( new GenOp( " @.w = clamp( ( @.z - @ ) * @.w, 0.0, 1.0 );\r\n", + outTex, detScaleAndFade, dist, detScaleAndFade ) ); + + output = meta; +} + + +void TerrainMacroMapFeatGLSL::processPix( Vector &componentList, + const MaterialFeatureData &fd ) +{ + const U32 detailIndex = getProcessIndex(); + Var *inTex = getVertTexCoord( "texCoord" ); + + MultiLine *meta = new MultiLine; + + // We need the negative tangent space view vector + // as in parallax mapping we step towards the camera. + Var *negViewTS = (Var*)LangElement::find( "negViewTS" ); + if ( !negViewTS && + fd.features.hasFeature( MFT_TerrainParallaxMap ) ) + { + Var *inNegViewTS = (Var*)LangElement::find( "outNegViewTS" ); + if ( !inNegViewTS ) + { + ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); + inNegViewTS = connectComp->getElement( RT_TEXCOORD ); + inNegViewTS->setName( "outNegViewTS" ); + inNegViewTS->setStructName( "IN" ); + inNegViewTS->setType( "vec3" ); + } + + negViewTS = new Var( "negViewTS", "vec3" ); + meta->addStatement( new GenOp( " @ = normalize( @ );\r\n", new DecOp( negViewTS ), inNegViewTS ) ); + } + + // Get the layer samples. + Var *layerSample = (Var*)LangElement::find( "layerSample" ); + if ( !layerSample ) + { + layerSample = new Var; + layerSample->setType( "vec4" ); + layerSample->setName( "layerSample" ); + + // Get the layer texture var + Var *layerTex = new Var; + layerTex->setType( "sampler2D" ); + layerTex->setName( "macrolayerTex" ); + layerTex->uniform = true; + layerTex->sampler = true; + layerTex->constNum = Var::getTexUnitNum(); + + // Read the layer texture to get the samples. + meta->addStatement( new GenOp( " @ = round( tex2D( @, @.xy ) * 255.0f );\r\n", + new DecOp( layerSample ), layerTex, inTex ) ); + } + + Var *layerSize = (Var*)LangElement::find( "layerSize" ); + if ( !layerSize ) + { + layerSize = new Var; + layerSize->setType( "float" ); + layerSize->setName( "layerSize" ); + layerSize->uniform = true; + layerSize->constSortPos = cspPass; + } + + // Grab the incoming detail coord. + Var *inDet = _getInMacroCoord( componentList ); + + // Get the detail id. + Var *detailInfo = _getMacroIdStrengthParallax(); + + // Create the detail blend var. + Var *detailBlend = new Var; + detailBlend->setType( "float" ); + detailBlend->setName( String::ToString( "macroBlend%d", detailIndex ) ); + + // Calculate the blend for this detail texture. + meta->addStatement( new GenOp( " @ = calcBlend( @.x, @.xy, @, @ );\r\n", + new DecOp( detailBlend ), detailInfo, inTex, layerSize, layerSample ) ); + + // Get a var and accumulate the blend amount. + Var *blendTotal = (Var*)LangElement::find( "blendTotal" ); + if ( !blendTotal ) + { + blendTotal = new Var; + //blendTotal->setName( "blendTotal" ); + blendTotal->setName( "blendTotal" ); + blendTotal->setType( "float" ); + meta->addStatement( new GenOp( " @ = 0;\r\n", new DecOp( blendTotal ) ) ); + } + + // Add to the blend total. + meta->addStatement( new GenOp( " @ = max( @, @ );\r\n", blendTotal, blendTotal, detailBlend ) ); + + // If this is a prepass then we skip color. if ( fd.features.hasFeature( MFT_PrePassConditioner ) ) { - // If this is a prepass and we don't have a - // matching normal map... we use no resources. - if ( !fd.features.hasFeature( MFT_TerrainNormalMap, getProcessIndex() ) ) - return res; + // Check to see if we have a gbuffer normal. + Var *gbNormal = (Var*)LangElement::find( "gbNormal" ); - // If this is the first matching normal map then - // it also samples from the layer tex. - if ( !fd.features.hasFeature( MFT_TerrainNormalMap, getProcessIndex() - 1 ) ) - res.numTex += 1; + // If we have a gbuffer normal and we don't have a + // normal map feature then we need to lerp in a + // default normal else the normals below this layer + // will show thru. + if ( gbNormal && + !fd.features.hasFeature( MFT_TerrainNormalMap, detailIndex ) ) + { + Var *viewToTangent = getInViewToTangent( componentList ); + + meta->addStatement( new GenOp( " @ = lerp( @, tGetMatrix3Row(@, 2), min( @, @.w ) );\r\n", + gbNormal, gbNormal, viewToTangent, detailBlend, inDet ) ); + } + + output = meta; + return; + } + + Var *detailColor = (Var*)LangElement::find( "macroColor" ); + if ( !detailColor ) + { + detailColor = new Var; + detailColor->setType( "vec4" ); + detailColor->setName( "macroColor" ); + meta->addStatement( new GenOp( " @;\r\n", new DecOp( detailColor ) ) ); + } + + // Get the detail texture. + Var *detailMap = new Var; + detailMap->setType( "sampler2D" ); + detailMap->setName( String::ToString( "macroMap%d", detailIndex ) ); + detailMap->uniform = true; + detailMap->sampler = true; + detailMap->constNum = Var::getTexUnitNum(); // used as texture unit num here + + // If we're using SM 3.0 then take advantage of + // dynamic branching to skip layers per-pixel. + if ( GFX->getPixelShaderVersion() >= 3.0f ) + meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) ); + + meta->addStatement( new GenOp( " {\r\n" ) ); + + // Note that we're doing the standard greyscale detail + // map technique here which can darken and lighten the + // diffuse texture. + // + // We take two color samples and lerp between them for + // side projection layers... else a single sample. + // + if ( fd.features.hasFeature( MFT_TerrainSideProject, detailIndex ) ) + { + meta->addStatement( new GenOp( " @ = ( lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z ) * 2.0 ) - 1.0;\r\n", + detailColor, detailMap, inDet, detailMap, inDet, inTex ) ); } else { - // If this is the first detail pass then it - // also samples from the layer tex. - if ( !fd.features.hasFeature( MFT_TerrainDetailMap, getProcessIndex() - 1 ) ) - res.numTex += 1; - - res.numTex += 1; + meta->addStatement( new GenOp( " @ = ( tex2D( @, @.xy ) * 2.0 ) - 1.0;\r\n", + detailColor, detailMap, inDet ) ); } + meta->addStatement( new GenOp( " @ *= @.y * @.w;\r\n", + detailColor, detailInfo, inDet ) ); + + Var *baseColor = (Var*)LangElement::find( "baseColor" ); + Var *outColor = (Var*)LangElement::find( "col" ); + + meta->addStatement( new GenOp( " @ = lerp( @, @ + @, @ );\r\n", + outColor, outColor, outColor, detailColor, detailBlend ) ); + //outColor, outColor, baseColor, detailColor, detailBlend ) ); + + meta->addStatement( new GenOp( " }\r\n" ) ); + + output = meta; +} + + + +ShaderFeature::Resources TerrainMacroMapFeatGLSL::getResources( const MaterialFeatureData &fd ) +{ + Resources res; + + if ( getProcessIndex() == 0 ) + { + // If this is the first detail pass then we + // samples from the layer tex. + res.numTex += 1; + } + + // If this isn't the prepass then we sample + // from the detail texture for diffuse coloring. + if ( !fd.features.hasFeature( MFT_PrePassConditioner ) ) + res.numTex += 1; + + // Finally we always send the detail texture + // coord to the pixel shader. res.numTexReg += 1; return res; @@ -510,7 +884,7 @@ void TerrainNormalMapFeatGLSL::processPix( Vector &component gbNormal = new Var; gbNormal->setName( "gbNormal" ); gbNormal->setType( "vec3" ); - meta->addStatement( new GenOp( " @ = @[2];\r\n", new DecOp( gbNormal ), viewToTangent ) ); + meta->addStatement( new GenOp( " @ = tGetMatrix3Row(@, 2);\r\n", new DecOp( gbNormal ), viewToTangent ) ); } const U32 normalIndex = getProcessIndex(); @@ -520,7 +894,6 @@ void TerrainNormalMapFeatGLSL::processPix( Vector &component // If we're using SM 3.0 then take advantage of // dynamic branching to skip layers per-pixel. - if ( GFX->getPixelShaderVersion() >= 3.0f ) meta->addStatement( new GenOp( " if ( @ > 0.0f )\r\n", detailBlend ) ); @@ -531,7 +904,7 @@ void TerrainNormalMapFeatGLSL::processPix( Vector &component /// Get the texture coord. Var *inDet = _getInDetailCoord( componentList ); - Var *inTex = getVertTexCoord( "outTexCoord" ); + Var *inTex = getVertTexCoord( "texCoord" ); // Sample the normal map. // @@ -540,11 +913,11 @@ void TerrainNormalMapFeatGLSL::processPix( Vector &component LangElement *texOp; if ( fd.features.hasFeature( MFT_TerrainSideProject, normalIndex ) ) { - texOp = new GenOp( "mix( texture2D( @, @.yz ), texture2D( @, @.xz ), @.z )", + texOp = new GenOp( "lerp( tex2D( @, @.yz ), tex2D( @, @.xz ), @.z )", normalMap, inDet, normalMap, inDet, inTex ); } else - texOp = new GenOp( "texture2D(@, @.xy)", normalMap, inDet ); + texOp = new GenOp( "tex2D(@, @.xy)", normalMap, inDet ); // create bump normal Var *bumpNorm = new Var; @@ -556,7 +929,7 @@ void TerrainNormalMapFeatGLSL::processPix( Vector &component // Normalize is done later... // Note: The reverse mul order is intentional. Affine matrix. - meta->addStatement( new GenOp( " @ = mix( @, @.xyz * @, min( @, @.w ) );\r\n", + meta->addStatement( new GenOp( " @ = lerp( @, tMul( @.xyz, @ ), min( @, @.w ) );\r\n", gbNormal, gbNormal, bumpNorm, viewToTangent, detailBlend, inDet ) ); // End the conditional block. @@ -578,9 +951,11 @@ ShaderFeature::Resources TerrainNormalMapFeatGLSL::getResources( const MaterialF // We only need to process normals during the prepass. if ( fd.features.hasFeature( MFT_PrePassConditioner ) ) { - // If this is the first normal map then it - // will generate the worldToTanget transform. - if ( !fd.features.hasFeature( MFT_TerrainNormalMap, getProcessIndex() - 1 ) ) + // If this is the first normal map and there + // are no parallax features then we will + // generate the worldToTanget transform. + if ( !fd.features.hasFeature( MFT_TerrainParallaxMap ) && + ( getProcessIndex() == 0 || !fd.features.hasFeature( MFT_TerrainNormalMap, getProcessIndex() - 1 ) ) ) res.numTexReg = 3; res.numTex = 1; @@ -589,100 +964,11 @@ ShaderFeature::Resources TerrainNormalMapFeatGLSL::getResources( const MaterialF return res; } -TerrainParallaxMapFeatGLSL::TerrainParallaxMapFeatGLSL() - : mIncludeDep( "shaders/common/gl/torque.glsl" ) -{ - addDependency( &mIncludeDep ); -} - -void TerrainParallaxMapFeatGLSL::processVert( Vector &componentList, - const MaterialFeatureData &fd ) -{ - if ( LangElement::find( "outNegViewTS" ) ) - return; - - MultiLine *meta = new MultiLine; - - // Grab the input position. - Var *inPos = (Var*)LangElement::find( "inPosition" ); - if ( !inPos ) - inPos = (Var*)LangElement::find( "position" ); - - // Get the object space eye position and the - // object to tangent transform. - Var *eyePos = _getUniformVar( "eyePos", "vec3" , cspPotentialPrimitive ); - Var *objToTangentSpace = getOutObjToTangentSpace( componentList, meta,fd ); - - // Now send the negative view vector in tangent space to the pixel shader. - ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); - Var *outNegViewTS = connectComp->getElement( RT_TEXCOORD ); - outNegViewTS->setName( "outNegViewTS" ); - outNegViewTS->setType( "vec3" ); - meta->addStatement( new GenOp( " @ = @ * vec3( @ - @.xyz );\r\n", - outNegViewTS, objToTangentSpace, eyePos, inPos ) ); - - output = meta; -} - -void TerrainParallaxMapFeatGLSL::processPix( Vector &componentList, - const MaterialFeatureData &fd ) -{ - MultiLine *meta = new MultiLine; - - ShaderConnector *connectComp = dynamic_cast( componentList[C_CONNECTOR] ); - - // We need the negative tangent space view vector - // as in parallax mapping we step towards the camera. - Var *negViewTS = (Var*)LangElement::find( "negViewTS" ); - if ( !negViewTS ) - { - Var *inNegViewTS = (Var*)LangElement::find( "outNegViewTS" ); - if ( !inNegViewTS ) - { - inNegViewTS = connectComp->getElement( RT_TEXCOORD ); - inNegViewTS->setName( "outNegViewTS" ); - inNegViewTS->setType( "vec3" ); - } - - negViewTS = new Var( "negViewTS", "vec3" ); - meta->addStatement( new GenOp( " @ = normalize( @ );\r\n", new DecOp( negViewTS ), inNegViewTS ) ); - } - - // Get the rest of our inputs. - Var *detailInfo = _getDetailIdStrengthParallax(); - Var *normalMap = _getNormalMapTex(); - Var *texCoord = _getInDetailCoord( componentList ); - - // Call the library function to do the rest. - meta->addStatement( new GenOp( " @.xy += parallaxOffset( @, @.xy, @, @.z );\r\n", - texCoord, normalMap, texCoord, negViewTS, detailInfo ) ); - - output = meta; -} - -ShaderFeature::Resources TerrainParallaxMapFeatGLSL::getResources( const MaterialFeatureData &fd ) -{ - Resources res; - - // If this is the first parallax feature then - // it will generate the tangetEye vector and - // the worldToTanget transform. - if ( getProcessIndex() == 0 || !fd.features.hasFeature( MFT_TerrainParallaxMap, getProcessIndex() - 1 ) ) - res.numTexReg = 4; - - // If this isn't the prepass then we will - // be adding a normal map. - if ( !fd.features.hasFeature( MFT_PrePassConditioner ) ) - res.numTex = 1; - - return res; -} - void TerrainLightMapFeatGLSL::processPix( Vector &componentList, const MaterialFeatureData &fd ) { // grab connector texcoord register - Var *inTex = (Var*)LangElement::find( "outTexCoord" ); + Var *inTex = (Var*)LangElement::find( "texCoord" ); if ( !inTex ) return; @@ -694,13 +980,23 @@ void TerrainLightMapFeatGLSL::processPix( Vector &componentLis lightMap->sampler = true; lightMap->constNum = Var::getTexUnitNum(); - // Create a 'lightMask' value which is read by - // RTLighting to mask out the directional lighting. - Var *lightMask = new Var; - lightMask->setType( "vec3" ); - lightMask->setName( "lightMask" ); + MultiLine *meta = new MultiLine; - output = new GenOp( " @ = texture2D( @, @.xy ).rgb;\r\n", new DecOp( lightMask ), lightMap, inTex ); + // Find or create the lightMask value which is read by + // RTLighting to mask out the lights. + // + // The first light is always the sunlight so we apply + // the shadow mask to only the first channel. + // + Var *lightMask = (Var*)LangElement::find( "lightMask" ); + if ( !lightMask ) + { + lightMask = new Var( "lightMask", "vec4" ); + meta->addStatement( new GenOp( " @ = vec4(1);\r\n", new DecOp( lightMask ) ) ); + } + + meta->addStatement( new GenOp( " @[0] = tex2D( @, @.xy ).r;\r\n", lightMask, lightMap, inTex ) ); + output = meta; } ShaderFeature::Resources TerrainLightMapFeatGLSL::getResources( const MaterialFeatureData &fd ) @@ -721,7 +1017,7 @@ void TerrainAdditiveFeatGLSL::processPix( Vector &componentLis MultiLine *meta = new MultiLine; - meta->addStatement( new GenOp( " if ( @ - 0.0001 < 0.0 ) discard;\r\n", blendTotal ) ); + meta->addStatement( new GenOp( " clip( @ - 0.0001 );\r\n", blendTotal ) ); meta->addStatement( new GenOp( " @.a = @;\r\n", color, blendTotal ) ); output = meta; diff --git a/Engine/source/terrain/glsl/terrFeatureGLSL.h b/Engine/source/terrain/glsl/terrFeatureGLSL.h index c181ef493..790a8a342 100644 --- a/Engine/source/terrain/glsl/terrFeatureGLSL.h +++ b/Engine/source/terrain/glsl/terrFeatureGLSL.h @@ -30,6 +30,7 @@ #include "shaderGen/langElement.h" #endif + /// A shared base class for terrain features which /// includes some helper functions. class TerrainFeatGLSL : public ShaderFeatureGLSL @@ -38,14 +39,18 @@ protected: Var* _getInDetailCoord(Vector &componentList ); + Var* _getInMacroCoord(Vector &componentList ); + Var* _getNormalMapTex(); static Var* _getUniformVar( const char *name, const char *type, ConstantSortPosition csp ); Var* _getDetailIdStrengthParallax(); + Var* _getMacroIdStrengthParallax(); }; + class TerrainBaseMapFeatGLSL : public TerrainFeatGLSL { public: @@ -61,10 +66,12 @@ public: virtual String getName() { return "Terrain Base Texture"; } }; + class TerrainDetailMapFeatGLSL : public TerrainFeatGLSL { protected: + ShaderIncludeDependency mTorqueDep; ShaderIncludeDependency mTerrainDep; public: @@ -83,10 +90,17 @@ public: }; -class TerrainNormalMapFeatGLSL : public TerrainFeatGLSL +class TerrainMacroMapFeatGLSL : public TerrainFeatGLSL { +protected: + + ShaderIncludeDependency mTorqueDep; + ShaderIncludeDependency mTerrainDep; + public: + TerrainMacroMapFeatGLSL(); + virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); @@ -95,19 +109,14 @@ public: virtual Resources getResources( const MaterialFeatureData &fd ); - virtual String getName() { return "Terrain Normal Texture"; } + virtual String getName() { return "Terrain Macro Texture"; } }; -class TerrainParallaxMapFeatGLSL : public TerrainFeatGLSL + +class TerrainNormalMapFeatGLSL : public TerrainFeatGLSL { -protected: - - ShaderIncludeDependency mIncludeDep; - public: - TerrainParallaxMapFeatGLSL(); - virtual void processVert( Vector &componentList, const MaterialFeatureData &fd ); @@ -116,7 +125,7 @@ public: virtual Resources getResources( const MaterialFeatureData &fd ); - virtual String getName() { return "Terrain Parallax Texture"; } + virtual String getName() { return "Terrain Normal Texture"; } }; class TerrainLightMapFeatGLSL : public TerrainFeatGLSL From 086c6b54165788b7d6e5179bbf44f7028dead4bc Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Thu, 17 Apr 2014 20:29:44 +0200 Subject: [PATCH 020/317] Add GFXShader::init with support for ordered vector of sampler names for shader. --- Engine/source/gfx/gfxShader.cpp | 13 +++++++++++++ Engine/source/gfx/gfxShader.h | 17 +++++++++++++++++ 2 files changed, 30 insertions(+) diff --git a/Engine/source/gfx/gfxShader.cpp b/Engine/source/gfx/gfxShader.cpp index 0bbcb4942..5e81d8a10 100644 --- a/Engine/source/gfx/gfxShader.cpp +++ b/Engine/source/gfx/gfxShader.cpp @@ -45,16 +45,29 @@ GFXShader::~GFXShader() Torque::FS::RemoveChangeNotification( mPixelFile, this, &GFXShader::_onFileChanged ); } +#ifndef TORQUE_OPENGL bool GFXShader::init( const Torque::Path &vertFile, const Torque::Path &pixFile, F32 pixVersion, const Vector ¯os ) +{ + Vector samplerNames; + return init( vertFile, pixFile, pixVersion, macros, samplerNames ); +} +#endif + +bool GFXShader::init( const Torque::Path &vertFile, + const Torque::Path &pixFile, + F32 pixVersion, + const Vector ¯os, + const Vector &samplerNames) { // Store the inputs for use in reloading. mVertexFile = vertFile; mPixelFile = pixFile; mPixVersion = pixVersion; mMacros = macros; + mSamplerNamesOrdered = samplerNames; // Before we compile the shader make sure the // conditioner features have been updated. diff --git a/Engine/source/gfx/gfxShader.h b/Engine/source/gfx/gfxShader.h index 8c77a5448..4b2c10f46 100644 --- a/Engine/source/gfx/gfxShader.h +++ b/Engine/source/gfx/gfxShader.h @@ -237,6 +237,13 @@ protected: /// The macros to be passed to the shader. Vector mMacros; + /// Ordered SamplerNames + /// We need to store a list of sampler for allow OpenGL to + /// assign correct location for each sampler. + /// GLSL 150 not allow explicit uniform location. + /// Only used on OpenGL + Vector mSamplerNamesOrdered; + /// The pixel version this is compiled for. F32 mPixVersion; @@ -292,10 +299,20 @@ public: virtual ~GFXShader(); /// + /// Deprecated. Remove on T3D 4.0 +#ifndef TORQUE_OPENGL bool init( const Torque::Path &vertFile, const Torque::Path &pixFile, F32 pixVersion, const Vector ¯os ); +#endif + + /// + bool init( const Torque::Path &vertFile, + const Torque::Path &pixFile, + F32 pixVersion, + const Vector ¯os, + const Vector &samplerNames); /// Reloads the shader from disk. bool reload(); From bc80fbf9662158ee5871ad60e83e473ce089d50f Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Thu, 17 Apr 2014 20:33:51 +0200 Subject: [PATCH 021/317] Handle texel-pixel offset with diferents graphics APIs. --- Engine/source/gfx/util/screenspace.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/Engine/source/gfx/util/screenspace.cpp b/Engine/source/gfx/util/screenspace.cpp index 3ef64be48..727eccb63 100644 --- a/Engine/source/gfx/util/screenspace.cpp +++ b/Engine/source/gfx/util/screenspace.cpp @@ -21,6 +21,7 @@ //----------------------------------------------------------------------------- #include "gfx/util/screenspace.h" +#include "gfx/gfxDevice.h" // The conversion from screen space to the render target // is made more complex because screen space is relative @@ -41,9 +42,11 @@ void ScreenSpace::RenderTargetParameters(const Point3I &targetSize, const RectI Point2F targetScale( (F32)targetViewport.extent.x / (F32)targetSize.x, (F32)targetViewport.extent.y / (F32)targetSize.y ); + bool hasTexelPixelOffset = GFX->getAdapterType() == Direct3D9; + // Get the target half pixel size. - const Point2F halfPixel( 0.5f / targetSize.x, - 0.5f / targetSize.y ); + const Point2F halfPixel( hasTexelPixelOffset ? 0.5f / targetSize.x : 0.0, + hasTexelPixelOffset ? 0.5f / targetSize.y : 0.0); rtParams.set( targetOffset.x + halfPixel.x, targetOffset.y + halfPixel.y, From 0137c86765d56ec361b256fd81332dd2f9e24e18 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Wed, 23 Apr 2014 21:09:50 +0200 Subject: [PATCH 022/317] Add SlotSignal for handle automatic disconnect on Signal or SignalSlot destruction. Example: ``` Win32WindowManager::Win32WindowManager() { // Register in the process list. mOnProcessSignalSlot.setDelegate( this, &Win32WindowManager::_process ); Process::notify( mOnProcessSignalSlot, PROCESS_INPUT_ORDER ); // When Signal it's destroyed, all slots are disconected. // When a SignalSlot it's destroyed, it's disconected from Signal. } ``` --- Engine/source/core/util/tSignal.h | 103 ++++++++++++++++++++++++++++++ 1 file changed, 103 insertions(+) diff --git a/Engine/source/core/util/tSignal.h b/Engine/source/core/util/tSignal.h index ea2e67954..a275d8da4 100644 --- a/Engine/source/core/util/tSignal.h +++ b/Engine/source/core/util/tSignal.h @@ -77,6 +77,8 @@ protected: void insert(DelegateLink* node, F32 order); void unlink(); + + virtual ~DelegateLink() {} }; DelegateLink mList; @@ -92,6 +94,78 @@ protected: Vector mTriggerNext; }; +template class SignalBaseT; + +/// Class for handle automatic diconnect form Signal when destroyed +template< typename Signature > +class SignalSlot +{ +public: + typedef Delegate< Signature > DelegateSig; + typedef SignalBaseT< Signature > SignalSig; + + SignalSlot() : mSignal(NULL) + { + + } + + ~SignalSlot() + { + disconnect(); + } + + const DelegateSig& getDelegate() { return mDlg; } + + /// setDelegate disconect form Signal old delegate and connect new delegate + template + void setDelegate( const X &fn ) { setDelegate( DelegateSig( fn ) ); } + + template + void setDelegate( const X &ptr, const Y &fn ) { setDelegate( DelegateSig( ptr, fn ) ); } + + void setDelegate( const DelegateSig &dlg) + { + SignalSig* signal = mSignal; + if( isConnected() ) + disconnect(); + + mDlg = dlg; + if( signal && mDlg ) + signal->notify( mDlg ); + } + + /// is connected to Signal + bool isConnected() const { return mSignal; } + + /// disconnect from Signal + void disconnect() + { + if( mSignal ) + { + SignalSig *oldSignal = mSignal; + mSignal = NULL; + oldSignal->remove( mDlg ); + } + } + +protected: + friend class SignalSig; + + void _setSignal(SignalSig *sig) + { + mSignal = sig; + } + + SignalSig* _getSignal() const { return mSignal; } + + DelegateSig mDlg; + SignalSig *mSignal; + +private: + SignalSlot( const SignalSlot&) {} + SignalSlot& operator=( const SignalSlot&) {} +}; + template class SignalBaseT : public SignalBase { public: @@ -163,6 +237,18 @@ public: notify(dlg, order); } + void notify( SignalSlot &slot, F32 order = 0.5f) + { + if( !slot.getDelegate() ) + return; + + if( slot.isConnected() ) + slot.disconnect(); + + slot._setSignal( this ); + mList.insert( new SlotLinkImpl(slot), order ); + } + template void remove(T obj,U func) { @@ -198,6 +284,23 @@ protected: DelegateLinkImpl(DelegateSig dlg) : mDelegate(dlg) {} }; + struct SlotLinkImpl : public DelegateLinkImpl + { + SlotLinkImpl(SignalSlot& slot) : mSlot( &slot ), DelegateLinkImpl( slot.getDelegate() ) + { + + } + + ~SlotLinkImpl() + { + if( mSlot ) + mSlot->_setSignal( NULL ); + } + + protected: + SignalSlot *mSlot; + }; + DelegateSig & getDelegate(SignalBase::DelegateLink * link) { return ((DelegateLinkImpl*)link)->mDelegate; From 881d578aae6be135a84250ed3778c72e1b8a5852 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Wed, 23 Apr 2014 21:16:34 +0200 Subject: [PATCH 023/317] Fix crash on exit T3D when build with CMake. Process::_signalProcess is destroyed first, and then Win32WindowManager::~Win32WindowManager() try to remove from deleted Signal causing a crash. SlotSignal handle this automatically. --- Engine/source/core/util/journal/process.h | 5 +++++ Engine/source/windowManager/win32/win32WindowMgr.cpp | 6 ++---- Engine/source/windowManager/win32/win32WindowMgr.h | 2 ++ 3 files changed, 9 insertions(+), 4 deletions(-) diff --git a/Engine/source/core/util/journal/process.h b/Engine/source/core/util/journal/process.h index 19040e747..4d21898d9 100644 --- a/Engine/source/core/util/journal/process.h +++ b/Engine/source/core/util/journal/process.h @@ -96,6 +96,11 @@ public: get()._signalProcess.notify(del,order); } + static void notify(SignalSlot &slot, F32 order = PROCESS_DEFAULT_ORDER) + { + get()._signalProcess.notify(slot,order); + } + template static void notify(T func, F32 order = PROCESS_DEFAULT_ORDER) { diff --git a/Engine/source/windowManager/win32/win32WindowMgr.cpp b/Engine/source/windowManager/win32/win32WindowMgr.cpp index 05e889c9a..b094e1bd2 100644 --- a/Engine/source/windowManager/win32/win32WindowMgr.cpp +++ b/Engine/source/windowManager/win32/win32WindowMgr.cpp @@ -41,7 +41,8 @@ PlatformWindowManager * CreatePlatformWindowManager() Win32WindowManager::Win32WindowManager() { // Register in the process list. - Process::notify(this, &Win32WindowManager::_process, PROCESS_INPUT_ORDER); + mOnProcessSignalSlot.setDelegate( this, &Win32WindowManager::_process ); + Process::notify( mOnProcessSignalSlot, PROCESS_INPUT_ORDER ); // Init our list of allocated windows. mWindowListHead = NULL; @@ -58,9 +59,6 @@ Win32WindowManager::Win32WindowManager() Win32WindowManager::~Win32WindowManager() { - // Get ourselves off the process list. - Process::remove(this, &Win32WindowManager::_process); - // Kill all our windows first. while(mWindowListHead) // The destructors update the list, so this works just fine. diff --git a/Engine/source/windowManager/win32/win32WindowMgr.h b/Engine/source/windowManager/win32/win32WindowMgr.h index a5c747501..be9cdaa42 100644 --- a/Engine/source/windowManager/win32/win32WindowMgr.h +++ b/Engine/source/windowManager/win32/win32WindowMgr.h @@ -85,6 +85,8 @@ class Win32WindowManager : public PlatformWindowManager /// If a curtain window is present, then its HWND will be stored here. HWND mCurtainWindow; + SignalSlot mOnProcessSignalSlot; + public: Win32WindowManager(); ~Win32WindowManager(); From 060b2a4e299eb2ac6327f75b04ce2b5f907542a1 Mon Sep 17 00:00:00 2001 From: JackDavidson Date: Sun, 4 May 2014 22:17:24 -0700 Subject: [PATCH 024/317] Commit to add "Coverage" option to procedural terrain generator This commit adds a "Coverage" option to the procedural terrain generator. Allows mixing of textures. See http://www.garagegames.com/community/blogs/view/22371 for more info. This version has many improvements - coverage is now entered as a percentage, accepts decimals, and doesn't have any bugs as far as I know. --- .../source/gui/worldEditor/terrainEditor.cpp | 18 +++- Engine/source/gui/worldEditor/terrainEditor.h | 2 +- .../gui/ProceduralTerrainPainterGui.gui | 84 +++++++++++++++++- .../gui/ProceduralTerrainPainterGui.gui | 85 ++++++++++++++++++- 4 files changed, 178 insertions(+), 11 deletions(-) diff --git a/Engine/source/gui/worldEditor/terrainEditor.cpp b/Engine/source/gui/worldEditor/terrainEditor.cpp index 3aa17e1be..617bb130a 100644 --- a/Engine/source/gui/worldEditor/terrainEditor.cpp +++ b/Engine/source/gui/worldEditor/terrainEditor.cpp @@ -34,6 +34,7 @@ #include "gui/core/guiCanvas.h" #include "gui/worldEditor/terrainActions.h" #include "terrain/terrMaterial.h" +#include @@ -2868,7 +2869,7 @@ ConsoleMethod( TerrainEditor, setSlopeLimitMaxAngle, F32, 3, 3, 0) } //------------------------------------------------------------------------------ -void TerrainEditor::autoMaterialLayer( F32 mMinHeight, F32 mMaxHeight, F32 mMinSlope, F32 mMaxSlope ) +void TerrainEditor::autoMaterialLayer( F32 mMinHeight, F32 mMaxHeight, F32 mMinSlope, F32 mMaxSlope, F32 mCoverage ) { if (!mActiveTerrain) return; @@ -2876,7 +2877,12 @@ void TerrainEditor::autoMaterialLayer( F32 mMinHeight, F32 mMaxHeight, F32 mMinS S32 mat = getPaintMaterialIndex(); if (mat == -1) return; - + + //setup for randomized coverage + mCoverage*=100; + srand((unsigned)time(0)); + int randomNumber; + mUndoSel = new Selection; U32 terrBlocks = mActiveTerrain->getBlockSize(); @@ -2894,6 +2900,10 @@ void TerrainEditor::autoMaterialLayer( F32 mMinHeight, F32 mMaxHeight, F32 mMinS if (gi.mMaterial == mat) continue; + + randomNumber = (rand() % 10000); + if (randomNumber > mCoverage) + continue; Point3F wp; gridToWorld(gp, wp); @@ -2933,7 +2943,7 @@ void TerrainEditor::autoMaterialLayer( F32 mMinHeight, F32 mMaxHeight, F32 mMinS scheduleMaterialUpdate(); } -ConsoleMethod( TerrainEditor, autoMaterialLayer, void, 6, 6, "(float minHeight, float maxHeight, float minSlope, float maxSlope)") +ConsoleMethod( TerrainEditor, autoMaterialLayer, void, 7, 7, "(float minHeight, float maxHeight, float minSlope, float maxSlope, float coverage)") { - object->autoMaterialLayer( dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]), dAtof(argv[5]) ); + object->autoMaterialLayer( dAtof(argv[2]), dAtof(argv[3]), dAtof(argv[4]), dAtof(argv[5]), dAtof(argv[6])); } diff --git a/Engine/source/gui/worldEditor/terrainEditor.h b/Engine/source/gui/worldEditor/terrainEditor.h index f0d03daf3..1b5d7cc15 100644 --- a/Engine/source/gui/worldEditor/terrainEditor.h +++ b/Engine/source/gui/worldEditor/terrainEditor.h @@ -230,7 +230,7 @@ class TerrainEditor : public EditTSCtrl void submitMaterialUndo( String actionName ); void onMaterialUndo( TerrainBlock *terr ); - void autoMaterialLayer( F32 mMinHeight, F32 mMaxHeight, F32 mMinSlope, F32 mMaxSlope ); + void autoMaterialLayer( F32 mMinHeight, F32 mMaxHeight, F32 mMinSlope, F32 mMaxSlope, F32 mCoverage ); private: diff --git a/Templates/Empty/game/tools/worldEditor/gui/ProceduralTerrainPainterGui.gui b/Templates/Empty/game/tools/worldEditor/gui/ProceduralTerrainPainterGui.gui index d2eccdbb7..d25f848f0 100644 --- a/Templates/Empty/game/tools/worldEditor/gui/ProceduralTerrainPainterGui.gui +++ b/Templates/Empty/game/tools/worldEditor/gui/ProceduralTerrainPainterGui.gui @@ -20,7 +20,7 @@ HorizSizing = "right"; VertSizing = "bottom"; Position = "285 83"; - Extent = "175 209"; + Extent = "175 233"; MinExtent = "8 2"; canSave = "1"; Visible = "1"; @@ -52,7 +52,7 @@ Profile = "ToolsGuiButtonProfile"; HorizSizing = "right"; VertSizing = "bottom"; - Position = "19 164"; + Position = "19 193"; Extent = "140 30"; MinExtent = "8 2"; canSave = "1"; @@ -305,6 +305,83 @@ sinkAllKeyEvents = "0"; passwordMask = "*"; }; + + + new GuiTextCtrl() { + text = "COVERAGE"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "10 165"; + extent = "55 13"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "1"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "97 162"; + extent = "66 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + visible = "1"; + active = "1"; + variable = "$TPPCoverage"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "%"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "77 164"; + extent = "11 14"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; }; }; //--- OBJECT WRITE END --- @@ -313,6 +390,7 @@ $TPPHeightMin = -10000; $TPPHeightMax = 10000; $TPPSlopeMin = 0; $TPPSlopeMax = 90; +$TPPCoverage = 100; function autoLayers() { @@ -322,5 +400,5 @@ function autoLayers() function generateProceduralTerrainMask() { Canvas.popDialog(ProceduralTerrainPainterGui); - ETerrainEditor.autoMaterialLayer($TPPHeightMin, $TPPHeightMax, $TPPSlopeMin, $TPPSlopeMax); + ETerrainEditor.autoMaterialLayer($TPPHeightMin, $TPPHeightMax, $TPPSlopeMin, $TPPSlopeMax, $TPPCoverage); } diff --git a/Templates/Full/game/tools/worldEditor/gui/ProceduralTerrainPainterGui.gui b/Templates/Full/game/tools/worldEditor/gui/ProceduralTerrainPainterGui.gui index d2eccdbb7..c00e31e4f 100644 --- a/Templates/Full/game/tools/worldEditor/gui/ProceduralTerrainPainterGui.gui +++ b/Templates/Full/game/tools/worldEditor/gui/ProceduralTerrainPainterGui.gui @@ -20,7 +20,7 @@ HorizSizing = "right"; VertSizing = "bottom"; Position = "285 83"; - Extent = "175 209"; + Extent = "175 233"; MinExtent = "8 2"; canSave = "1"; Visible = "1"; @@ -52,7 +52,7 @@ Profile = "ToolsGuiButtonProfile"; HorizSizing = "right"; VertSizing = "bottom"; - Position = "19 164"; + Position = "19 193"; Extent = "140 30"; MinExtent = "8 2"; canSave = "1"; @@ -305,6 +305,83 @@ sinkAllKeyEvents = "0"; passwordMask = "*"; }; + + + new GuiTextCtrl() { + text = "COVERAGE"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "10 165"; + extent = "55 13"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextEditCtrl() { + historySize = "0"; + tabComplete = "0"; + sinkAllKeyEvents = "0"; + password = "0"; + passwordMask = "*"; + text = "1"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "97 162"; + extent = "66 18"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTextEditProfile"; + visible = "1"; + active = "1"; + variable = "$TPPCoverage"; + tooltipProfile = "GuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; + new GuiTextCtrl() { + text = "%"; + maxLength = "1024"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "0"; + anchorLeft = "1"; + anchorRight = "0"; + position = "77 164"; + extent = "11 14"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "ToolsGuiTextProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "ToolsGuiToolTipProfile"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + }; }; }; //--- OBJECT WRITE END --- @@ -313,6 +390,7 @@ $TPPHeightMin = -10000; $TPPHeightMax = 10000; $TPPSlopeMin = 0; $TPPSlopeMax = 90; +$TPPCoverage = 100; function autoLayers() { @@ -322,5 +400,6 @@ function autoLayers() function generateProceduralTerrainMask() { Canvas.popDialog(ProceduralTerrainPainterGui); - ETerrainEditor.autoMaterialLayer($TPPHeightMin, $TPPHeightMax, $TPPSlopeMin, $TPPSlopeMax); + ETerrainEditor.autoMaterialLayer($TPPHeightMin, $TPPHeightMax, $TPPSlopeMin, $TPPSlopeMax, $TPPCoverage); } + From 9ebfd0f6b3a4f3ae7ee3c8d5a4c7f7303f201d23 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lukas=20J=C3=B8rgensen?= Date: Fri, 3 Jan 2014 00:30:16 +0100 Subject: [PATCH 025/317] T2D style 'Stock colors' This commit implements stock colors as they are implemented in T2D. It allows for using predefined stock colors e.g.: ``` %object.color = "OrangeRed"; ``` Instead of ``` %object.color = "255 69 0"; ``` --- Engine/source/console/consoleTypes.cpp | 61 +++- Engine/source/core/color.cpp | 486 +++++++++++++++++++++++++ Engine/source/core/color.h | 61 ++++ 3 files changed, 604 insertions(+), 4 deletions(-) diff --git a/Engine/source/console/consoleTypes.cpp b/Engine/source/console/consoleTypes.cpp index a948686a4..059a9170c 100644 --- a/Engine/source/console/consoleTypes.cpp +++ b/Engine/source/console/consoleTypes.cpp @@ -29,6 +29,7 @@ #include "core/color.h" #include "console/simBase.h" #include "math/mRect.h" +#include "core/strings/stringUnit.h" //----------------------------------------------------------------------------- // TypeString @@ -567,7 +568,17 @@ ImplementConsoleTypeCasters( TypeColorF, ColorF ) ConsoleGetType( TypeColorF ) { - ColorF * color = (ColorF*)dptr; + // Fetch color. + const ColorF* color = (ColorF*)dptr; + + // Fetch stock color name. + StringTableEntry colorName = StockColor::name( *color ); + + // Write as color name if was found. + if ( colorName != StringTable->EmptyString() ) + return colorName; + + // Format as color components. char* returnBuffer = Con::getReturnBuffer(256); dSprintf(returnBuffer, 256, "%g %g %g %g", color->red, color->green, color->blue, color->alpha); return(returnBuffer); @@ -578,6 +589,22 @@ ConsoleSetType( TypeColorF ) ColorF *tmpColor = (ColorF *) dptr; if(argc == 1) { + // Is only a single argument passed? + if ( StringUnit::getUnitCount( argv[0], " " ) == 1 ) + { + // Is this a stock color name? + if ( !StockColor::isColor(argv[0]) ) + { + // No, so warn. + Con::warnf( "TypeColorF() - Invalid single argument of '%s' could not be interpreted as a stock color name. Using default.", argv[0] ); + } + + // Set stock color (if it's invalid we'll get the default. + tmpColor->set( argv[0] ); + + return; + } + tmpColor->set(0, 0, 0, 1); F32 r,g,b,a; S32 args = dSscanf(argv[0], "%g %g %g %g", &r, &g, &b, &a); @@ -602,7 +629,7 @@ ConsoleSetType( TypeColorF ) tmpColor->alpha = dAtof(argv[3]); } else - Con::printf("Color must be set as { r, g, b [,a] }"); + Con::printf("Color must be set as { r, g, b [,a] }, { r g b [b] } or { stockColorName }"); } //----------------------------------------------------------------------------- @@ -613,7 +640,17 @@ ImplementConsoleTypeCasters( TypeColorI, ColorI ) ConsoleGetType( TypeColorI ) { - ColorI *color = (ColorI *) dptr; + // Fetch color. + ColorI* color = (ColorI*)dptr; + + // Fetch stock color name. + StringTableEntry colorName = StockColor::name( *color ); + + // Write as color name if was found. + if ( colorName != StringTable->EmptyString() ) + return colorName; + + // Format as color components. char* returnBuffer = Con::getReturnBuffer(256); dSprintf(returnBuffer, 256, "%d %d %d %d", color->red, color->green, color->blue, color->alpha); return returnBuffer; @@ -624,6 +661,22 @@ ConsoleSetType( TypeColorI ) ColorI *tmpColor = (ColorI *) dptr; if(argc == 1) { + // Is only a single argument passed? + if ( StringUnit::getUnitCount( argv[0], " " ) == 1 ) + { + // Is this a stock color name? + if ( !StockColor::isColor(argv[0]) ) + { + // No, so warn. + Con::warnf( "TypeColorF() - Invalid single argument of '%s' could not be interpreted as a stock color name. Using default.", argv[0] ); + } + + // Set stock color (if it's invalid we'll get the default. + tmpColor->set( argv[0] ); + + return; + } + tmpColor->set(0, 0, 0, 255); S32 r,g,b,a; S32 args = dSscanf(argv[0], "%d %d %d %d", &r, &g, &b, &a); @@ -648,7 +701,7 @@ ConsoleSetType( TypeColorI ) tmpColor->alpha = dAtoi(argv[3]); } else - Con::printf("Color must be set as { r, g, b [,a] }"); + Con::printf("Color must be set as { r, g, b [,a] }, { r g b [b] } or { stockColorName }"); } //----------------------------------------------------------------------------- diff --git a/Engine/source/core/color.cpp b/Engine/source/core/color.cpp index 17df1cb44..2c6d360e3 100644 --- a/Engine/source/core/color.cpp +++ b/Engine/source/core/color.cpp @@ -38,3 +38,489 @@ const ColorI ColorI::BLACK( 0, 0, 0 ); const ColorI ColorI::RED( 255, 0, 0 ); const ColorI ColorI::GREEN( 0, 255, 0 ); const ColorI ColorI::BLUE( 0, 0, 255 ); + +#include "console/console.h" +#include "console/consoleTypes.h" + +#ifndef _STRINGUNIT_H_ +#include "core/strings/stringUnit.h" +#endif + +#ifndef _TDICTIONARY_H_ +#include "core/util/tDictionary.h" +#endif + +#include "console/consoleInternal.h" + +//----------------------------------------------------------------------------- + +typedef HashTable typeNameToColorFHash; +typedef HashTable typeNameToColorIHash; +typedef HashTable typeColorFToNameHash; +typedef HashTable typeColorIToNameHash; + +static typeNameToColorFHash mNameToColorF; +static typeNameToColorIHash mNameToColorI; +static typeColorFToNameHash mColorFToName; +static typeColorIToNameHash mColorIToName; + +#define DEFAULT_UNKNOWN_STOCK_COLOR_NAME "White" + +//----------------------------------------------------------------------------- + +StockColorItem StockColorTable[] = +{ + StockColorItem( "InvisibleBlack", 0, 0, 0, 0 ), + StockColorItem( "TransparentWhite", 255, 255, 255, 0 ), + StockColorItem( "AliceBlue", 240, 248, 255 ), + StockColorItem( "AntiqueWhite", 250, 235, 215 ), + StockColorItem( "Aqua", 0, 255, 255 ), + StockColorItem( "Aquamarine", 127, 255, 212 ), + StockColorItem( "Azure", 240, 255, 255 ), + StockColorItem( "Beige", 245, 245, 220 ), + StockColorItem( "Bisque", 255, 228, 196 ), + StockColorItem( "Black", 0, 0, 0, 255 ), + StockColorItem( "BlanchedAlmond", 255, 235, 205, 255 ), + StockColorItem( "Blue", 0, 0, 255 ), + StockColorItem( "BlueViolet", 138, 43, 226 ), + StockColorItem( "Brown", 165, 42, 42, 255 ), + StockColorItem( "BurlyWood", 222, 184, 135 ), + StockColorItem( "CadetBlue", 95, 158, 160 ), + StockColorItem( "Chartreuse", 127, 255, 0 ), + StockColorItem( "Chocolate", 210, 105, 30 ), + StockColorItem( "Coral", 255, 127, 80 ), + StockColorItem( "CornflowerBlue", 100, 149, 237 ), + StockColorItem( "Cornsilk", 255, 248, 220 ), + StockColorItem( "Crimson", 220, 20, 60 ), + StockColorItem( "Cyan", 0, 255, 255 ), + StockColorItem( "DarkBlue", 0, 0, 139 ), + StockColorItem( "DarkCyan", 0, 139, 139 ), + StockColorItem( "DarkGoldenrod", 184, 134, 11 ), + StockColorItem( "DarkGray", 169, 169, 169), + StockColorItem( "DarkGreen", 0, 100, 0 ), + StockColorItem( "DarkKhaki", 189, 183, 107 ), + StockColorItem( "DarkMagenta", 139, 0, 139 ), + StockColorItem( "DarkOliveGreen", 85, 107, 47 ), + StockColorItem( "DarkOrange", 255, 140, 0 ), + StockColorItem( "DarkOrchid", 153, 50, 204 ), + StockColorItem( "DarkRed", 139, 0, 0 ), + StockColorItem( "DarkSalmon", 233, 150, 122 ), + StockColorItem( "DarkSeaGreen", 143, 188, 139 ), + StockColorItem( "DarkSlateBlue", 72, 61, 139 ), + StockColorItem( "DarkSlateGray", 47, 79, 79 ), + StockColorItem( "DarkTurquoise", 0, 206, 209 ), + StockColorItem( "DarkViolet", 148, 0, 211 ), + StockColorItem( "DeepPink", 255, 20, 147 ), + StockColorItem( "DeepSkyBlue", 0, 191, 255 ), + StockColorItem( "DimGray", 105, 105, 105 ), + StockColorItem( "DodgerBlue", 30, 144, 255 ), + StockColorItem( "Firebrick", 178, 34, 34 ), + StockColorItem( "FloralWhite", 255, 250, 240 ), + StockColorItem( "ForestGreen", 34, 139, 34 ), + StockColorItem( "Fuchsia", 255, 0, 255 ), + StockColorItem( "Gainsboro", 220, 220, 220 ), + StockColorItem( "GhostWhite", 248, 248, 255 ), + StockColorItem( "Gold", 255, 215, 0 ), + StockColorItem( "Goldenrod", 218, 165, 32 ), + StockColorItem( "Gray", 128, 128, 128 ), + StockColorItem( "Green", 0, 128, 0 ), + StockColorItem( "GreenYellow", 173, 255, 47 ), + StockColorItem( "Honeydew", 240, 255, 24 ), + StockColorItem( "HotPink", 255, 105, 180 ), + StockColorItem( "IndianRed", 205, 92, 92 ), + StockColorItem( "Indigo", 75, 0, 130 ), + StockColorItem( "Ivory", 255, 255, 240 ), + StockColorItem( "Khaki", 240, 230, 140 ), + StockColorItem( "Lavender", 230, 230, 250 ), + StockColorItem( "LavenderBlush", 255, 240, 245 ), + StockColorItem( "LawnGreen", 124, 252, 0 ), + StockColorItem( "LemonChiffon", 255, 250, 205 ), + StockColorItem( "LightBlue", 173, 216, 230 ), + StockColorItem( "LightCoral", 240, 128, 128 ), + StockColorItem( "LightCyan", 224, 255, 255), + StockColorItem( "LightGoldenrodYellow", 250, 250, 210 ), + StockColorItem( "LightGray", 211, 211, 211), + StockColorItem( "LightGreen", 144, 238, 144 ), + StockColorItem( "LightPink", 255, 182, 193 ), + StockColorItem( "LightSalmon", 255, 160, 122 ), + StockColorItem( "LightSeaGreen", 32, 178, 170 ), + StockColorItem( "LightSkyBlue",135, 206, 250 ), + StockColorItem( "LightSlateGray", 119, 136, 153 ), + StockColorItem( "LightSteelBlue", 176, 196, 222 ), + StockColorItem( "LightYellow", 255, 255, 224 ), + StockColorItem( "Lime", 0, 255, 0 ), + StockColorItem( "LimeGreen", 50, 205, 50 ), + StockColorItem( "Linen", 250, 240, 230 ), + StockColorItem( "Magenta", 255, 0, 255 ), + StockColorItem( "Maroon", 128, 0, 0 ), + StockColorItem( "MediumAquamarine", 102, 205, 170 ), + StockColorItem( "MediumBlue", 0, 0, 205 ), + StockColorItem( "MediumOrchid", 186, 85, 211 ), + StockColorItem( "MediumPurple", 147, 112, 219 ), + StockColorItem( "MediumSeaGreen", 60, 179, 113 ), + StockColorItem( "MediumSlateBlue", 123, 104, 238 ), + StockColorItem( "MediumSpringGreen", 0, 250, 154 ), + StockColorItem( "MediumTurquoise", 72, 209, 204 ), + StockColorItem( "MediumVioletRed", 199, 21, 133 ), + StockColorItem( "MidnightBlue", 25, 25, 112 ), + StockColorItem( "MintCream", 245, 255, 250 ), + StockColorItem( "MistyRose", 255, 228, 225 ), + StockColorItem( "Moccasin", 255, 228, 181 ), + StockColorItem( "NavajoWhite", 255, 222, 173 ), + StockColorItem( "Navy", 0, 0, 128 ), + StockColorItem( "OldLace", 253, 245, 230 ), + StockColorItem( "Olive", 128, 128, 0 ), + StockColorItem( "OliveDrab", 107, 142, 35 ), + StockColorItem( "Orange", 255, 165, 0 ), + StockColorItem( "OrangeRed", 255, 69, 0 ), + StockColorItem( "Orchid", 218, 112, 214 ), + StockColorItem( "PaleGoldenrod", 238, 232, 170 ), + StockColorItem( "PaleGreen", 152, 251, 152 ), + StockColorItem( "PaleTurquoise", 175, 238, 238 ), + StockColorItem( "PaleVioletRed", 219, 112, 147 ), + StockColorItem( "PapayaWhip", 255, 239, 213 ), + StockColorItem( "PeachPuff", 255, 218, 185 ), + StockColorItem( "Peru", 205, 133, 63 ), + StockColorItem( "Pink", 55, 192, 203 ), + StockColorItem( "Plum", 221, 160, 221 ), + StockColorItem( "PowderBlue", 176, 224, 230 ), + StockColorItem( "Purple", 128, 0, 128 ), + StockColorItem( "Red", 255, 0, 0 ), + StockColorItem( "RosyBrown", 188, 143, 143 ), + StockColorItem( "RoyalBlue", 65, 105, 225 ), + StockColorItem( "SaddleBrown", 139, 69, 19 ), + StockColorItem( "Salmon", 250, 128, 114 ), + StockColorItem( "SandyBrown", 244, 164, 96 ), + StockColorItem( "SeaGreen", 46, 139, 87 ), + StockColorItem( "SeaShell", 255, 245, 238 ), + StockColorItem( "Sienna", 160, 82, 45 ), + StockColorItem( "Silver", 192, 192, 192 ), + StockColorItem( "SkyBlue", 135, 206, 235 ), + StockColorItem( "SlateBlue", 106, 90, 205 ), + StockColorItem( "SlateGray", 112, 128, 144 ), + StockColorItem( "Snow", 255, 250, 250 ), + StockColorItem( "SpringGreen", 0, 255, 127 ), + StockColorItem( "SteelBlue", 70, 130, 180 ), + StockColorItem( "Tan", 210, 180, 140 ), + StockColorItem( "Teal", 0, 128, 128 ), + StockColorItem( "Thistle", 216, 191, 216 ), + StockColorItem( "Tomato", 255, 99, 71 ), + StockColorItem( "Turquoise", 64, 224, 208 ), + StockColorItem( "Violet", 238, 130, 238 ), + StockColorItem( "Wheat", 245, 222, 179 ), + StockColorItem( "White", 255, 255, 255 ), + StockColorItem( "WhiteSmoke", 245, 245, 245 ), + StockColorItem( "Yellow", 255, 255, 0 ), + StockColorItem( "YellowGreen", 154, 205, 50 ) +}; + +//----------------------------------------------------------------------------- + +static bool stockColorsCreated = false; + +void StockColor::create( void ) +{ + // Finish if already created. + if ( stockColorsCreated ) + return; + + // Fetch stock color count. + const S32 stockColorCount = sizeof(StockColorTable) / sizeof(StockColorItem); + + // Insert all stock colors. + for( S32 index = 0; index < stockColorCount; ++index ) + { + // Fetch stock color item. + StockColorItem& stockColor = StockColorTable[index]; + + // Fetch stock color item. + StringTableEntry colorName = StringTable->insert( stockColor.mColorName ); + + // Insert stock color mappings. + mNameToColorF.insertUnique(colorName, stockColor.mColorF); + mNameToColorI.insertUnique(colorName, stockColor.mColorI); + mColorFToName.insertUnique(stockColor.mColorF, colorName); + mColorIToName.insertUnique(stockColor.mColorI, colorName); + } + + // Flag as created. + stockColorsCreated = true; +} + +//----------------------------------------------------------------------------- + +void StockColor::destroy( void ) +{ + // Finish if not created. + if ( !stockColorsCreated ) + return; + + // Clear stock color mappings. + mNameToColorF.clear(); + mNameToColorI.clear(); + mColorFToName.clear(); + mColorIToName.clear(); + + // Flag as not created. + stockColorsCreated = false; +} + +//----------------------------------------------------------------------------- + +bool StockColor::isColor( const char* pStockColorName ) +{ + // Sanity! + AssertFatal( pStockColorName != NULL, "Cannot fetch a NULL stock color name." ); + + // Fetch color name. + StringTableEntry colorName = StringTable->insert( pStockColorName ); + + // Find if color name exists or not. + return mNameToColorF.find( colorName ) != mNameToColorF.end(); +} + +//----------------------------------------------------------------------------- + +const ColorF& StockColor::colorF( const char* pStockColorName ) +{ + // Sanity! + AssertFatal( pStockColorName != NULL, "Cannot fetch a NULL stock color name." ); + + // Fetch color name. + StringTableEntry colorName = StringTable->insert( pStockColorName ); + + // Find stock color. + typeNameToColorFHash::Iterator colorItr = mNameToColorF.find( colorName ); + + // Return color if found. + if ( colorItr != mNameToColorF.end() ) + return colorItr->value; + + // Warn. + Con::warnf( "Could not find stock color name '%s'.", pStockColorName ); + + // Return default stock color. + return mNameToColorF.find( DEFAULT_UNKNOWN_STOCK_COLOR_NAME )->value; +} + +//----------------------------------------------------------------------------- + +const ColorI& StockColor::colorI( const char* pStockColorName ) +{ + // Sanity! + AssertFatal( pStockColorName != NULL, "Cannot fetch a NULL stock color name." ); + + // Fetch color name. + StringTableEntry colorName = StringTable->insert( pStockColorName ); + + // Find stock color. + typeNameToColorIHash::Iterator colorItr = mNameToColorI.find( colorName ); + + // Return color if found. + if ( colorItr != mNameToColorI.end() ) + return colorItr->value; + + // Warn. + Con::warnf( "Could not find stock color name '%s'.", colorName ); + + // Return default stock color. + return mNameToColorI.find( DEFAULT_UNKNOWN_STOCK_COLOR_NAME )->value; +} + +//----------------------------------------------------------------------------- + +StringTableEntry StockColor::name( const ColorF& color ) +{ + // Find stock color name. + typeColorFToNameHash::Iterator colorNameItr = mColorFToName.find( color ); + + // Return name if found. + if ( colorNameItr != mColorFToName.end() ) + return colorNameItr->value; + + // Return empty string. + return StringTable->EmptyString(); +} + +//----------------------------------------------------------------------------- + +StringTableEntry StockColor::name( const ColorI& color ) +{ + // Find stock color name. + typeColorIToNameHash::Iterator colorNameItr = mColorIToName.find( color ); + + // Return name if found. + if ( colorNameItr != mColorIToName.end() ) + return colorNameItr->value; + + // Return empty string. + return StringTable->EmptyString(); +} + +//----------------------------------------------------------------------------- + +S32 StockColor::getCount( void ) +{ + return sizeof(StockColorTable) / sizeof(StockColorItem); +} + +//----------------------------------------------------------------------------- + +const StockColorItem* StockColor::getColorItem( const S32 index ) +{ + // Fetch stock color count. + const S32 stockColorCount = StockColor::getCount(); + + // Is the stock color index in range? + if ( index < 0 || index >= stockColorCount ) + { + // No, so warn. + Con::warnf("StockColor::getName() - Specified color index '%d' is out of range. Range is 0 to %d.", index, stockColorCount-1 ); + return NULL; + } + + // Return color name. + return &(StockColorTable[index]); +} + +//----------------------------------------------------------------------------- + +ColorF::ColorF( const char* pStockColorName ) +{ + // Set stock color. + *this = StockColor::colorF( pStockColorName ); +} + +//----------------------------------------------------------------------------- + +void ColorF::set( const char* pStockColorName ) +{ + // Set stock color. + *this = StockColor::colorF( pStockColorName ); +} + +//----------------------------------------------------------------------------- + +const ColorF& ColorF::StockColor( const char* pStockColorName ) +{ + return StockColor::colorF( pStockColorName ); +} + +//----------------------------------------------------------------------------- + +StringTableEntry ColorF::StockColor( void ) +{ + // Return stock color name. + return StockColor::name( *this ); +} + +//----------------------------------------------------------------------------- + +ColorI::ColorI( const char* pStockColorName ) +{ + // Set stock color. + *this = StockColor::colorI( pStockColorName ); +} + +//----------------------------------------------------------------------------- + +void ColorI::set( const char* pStockColorName ) +{ + // Set stock color. + *this = StockColor::colorI( pStockColorName ); +} + +//----------------------------------------------------------------------------- + +const ColorI& ColorI::StockColor( const char* pStockColorName ) +{ + return StockColor::colorI( pStockColorName ); +} + +//----------------------------------------------------------------------------- + +StringTableEntry ColorI::StockColor( void ) +{ + // Return stock color name. + return StockColor::name( *this ); +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( getStockColorCount, S32, 1, 1, "() - Gets a count of available stock colors.\n" + "@return A count of available stock colors." ) +{ + return StockColor::getCount(); +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( getStockColorName, const char*, 2, 2, "(stockColorIndex) - Gets the stock color name at the specified index.\n" + "@param stockColorIndex The zero-based index of the stock color name to retrieve.\n" + "@return The stock color name at the specified index or nothing if the string is invalid." ) +{ + // Fetch stock color index. + const S32 stockColorIndex = dAtoi(argv[1]); + + // Fetch the color item. + const StockColorItem* pColorItem = StockColor::getColorItem( stockColorIndex ); + + return pColorItem == NULL ? NULL : pColorItem->getColorName(); +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( isStockColor, bool, 2, 2, "(stockColorName) - Gets whether the specified name is a stock color or not.\n" + "@param stockColorName - The stock color name to test for.\n" + "@return Whether the specified name is a stock color or not.\n" ) +{ + // Fetch stock color name. + const char* pStockColorName = argv[1]; + + // Return whether this is a stock color name or not. + return StockColor::isColor( pStockColorName ); +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( getStockColorF, const char*, 2, 2, "(stockColorName) - Gets a floating-point-based stock color by name.\n" + "@param stockColorName - The stock color name to retrieve.\n" + "@return The stock color that matches the specified color name. Returns nothing if the color name is not found.\n" ) +{ + // Fetch stock color name. + const char* pStockColorName = argv[1]; + + // Return nothing if stock color name is invalid. + if ( !StockColor::isColor( pStockColorName ) ) + return StringTable->EmptyString(); + + // Fetch stock color. + const ColorF& color = StockColor::colorF( pStockColorName ); + + // Format stock color. + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%g %g %g %g", color.red, color.green, color.blue, color.alpha); + return(returnBuffer); +} + +//----------------------------------------------------------------------------- + +ConsoleFunction( getStockColorI, const char*, 2, 2, "(stockColorName) - Gets a byte-based stock color by name.\n" + "@param stockColorName - The stock color name to retrieve.\n" + "@return The stock color that matches the specified color name. Returns nothing if the color name is not found.\n" ) +{ + // Fetch stock color name. + const char* pStockColorName = argv[1]; + + // Return nothing if stock color name is invalid. + if ( !StockColor::isColor( pStockColorName ) ) + return StringTable->EmptyString(); + + // Fetch stock color. + const ColorI& color = StockColor::colorI( pStockColorName ); + + // Format stock color. + char* returnBuffer = Con::getReturnBuffer(256); + dSprintf(returnBuffer, 256, "%d %d %d %d", color.red, color.green, color.blue, color.alpha); + return(returnBuffer); +} \ No newline at end of file diff --git a/Engine/source/core/color.h b/Engine/source/core/color.h index 7a9206cd3..b0620fc4f 100644 --- a/Engine/source/core/color.h +++ b/Engine/source/core/color.h @@ -49,11 +49,18 @@ class ColorF const F32 in_b, const F32 in_a = 1.0f); + ColorF( const char* pStockColorName ); + void set(const F32 in_r, const F32 in_g, const F32 in_b, const F32 in_a = 1.0f); + void set( const char* pStockColorName ); + + static const ColorF& StockColor( const char* pStockColorName ); + StringTableEntry StockColor( void ); + ColorF& operator*=(const ColorF& in_mul); // Can be useful for lighting ColorF operator*(const ColorF& in_mul) const; ColorF& operator+=(const ColorF& in_rAdd); @@ -123,6 +130,8 @@ class ColorI const U8 in_a = U8(255)); ColorI(const ColorI& in_rCopy, const U8 in_a); + ColorI( const char* pStockColorName ); + void set(const U8 in_r, const U8 in_g, const U8 in_b, @@ -131,6 +140,11 @@ class ColorI void set(const ColorI& in_rCopy, const U8 in_a); + void set( const char* pStockColorName ); + + static const ColorI& StockColor( const char* pStockColorName ); + StringTableEntry StockColor( void ); + ColorI& operator*=(const F32 in_mul); ColorI operator*(const F32 in_mul) const; @@ -175,6 +189,53 @@ class ColorI static const ColorI BLUE; }; +//----------------------------------------------------------------------------- + +class StockColorItem +{ +private: + StockColorItem() {} + +public: + StockColorItem( const char* pName, const U8 red, const U8 green, const U8 blue, const U8 alpha = 255 ) + { + // Sanity! + AssertFatal( pName != NULL, "Stock color name cannot be NULL." ); + + // Set stock color. + // NOTE:- We'll use the char pointer here. We can yet use the string-table unfortunately. + mColorName = pName; + mColorI.set( red, green, blue, alpha ); + mColorF = mColorI; + } + + inline const char* getColorName( void ) const { return mColorName; } + inline const ColorF& getColorF( void ) const { return mColorF; } + inline const ColorI& getColorI( void ) const { return mColorI; } + + const char* mColorName; + ColorF mColorF; + ColorI mColorI; +}; + +//----------------------------------------------------------------------------- + +class StockColor +{ +public: + static bool isColor( const char* pStockColorName ); + static const ColorF& colorF( const char* pStockColorName ); + static const ColorI& colorI( const char* pStockColorName ); + static StringTableEntry name( const ColorF& color ); + static StringTableEntry name( const ColorI& color ); + + static S32 getCount( void ); + static const StockColorItem* getColorItem( const S32 index ); + + static void create( void ); + static void destroy( void ); +}; + //------------------------------------------------------------------------------ //-------------------------------------- INLINES (ColorF) // From b2e6895554df59da027b845d56ef3203bd994466 Mon Sep 17 00:00:00 2001 From: bank Date: Thu, 27 Mar 2014 12:23:36 +0400 Subject: [PATCH 026/317] Fix: preload() method on datablocks should always set errorStr when returning false. Otherwise it will dump the previous error message (content of the errorStr). --- Engine/source/T3D/player.cpp | 3 +++ Engine/source/T3D/rigidShape.cpp | 1 + Engine/source/T3D/shapeBase.cpp | 3 +++ Engine/source/T3D/shapeImage.cpp | 3 +++ Engine/source/T3D/vehicles/vehicle.cpp | 1 + 5 files changed, 11 insertions(+) diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index da29b63e0..75c42e9b6 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -586,7 +586,10 @@ bool PlayerData::preload(bool server, String &errorStr) Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode(mShapeFP[i].getPath()); if (!fileRef) + { + errorStr = String::ToString("PlayerData: Mounted image %d loading failed, shape \"%s\" is not found.",i,mShapeFP[i].getPath()); return false; + } if(server) mCRCFP[i] = fileRef->getChecksum(); diff --git a/Engine/source/T3D/rigidShape.cpp b/Engine/source/T3D/rigidShape.cpp index 5d21d97c8..0a7ee742a 100644 --- a/Engine/source/T3D/rigidShape.cpp +++ b/Engine/source/T3D/rigidShape.cpp @@ -302,6 +302,7 @@ bool RigidShapeData::preload(bool server, String &errorStr) if (!collisionDetails.size() || collisionDetails[0] == -1) { Con::errorf("RigidShapeData::preload failed: Rigid shapes must define a collision-1 detail"); + errorStr = String::ToString("RigidShapeData: Couldn't load shape \"%s\"",shapeName); return false; } diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index 571b3c163..c2e102c79 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -306,7 +306,10 @@ bool ShapeBaseData::preload(bool server, String &errorStr) Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode(mShape.getPath()); if (!fileRef) + { + errorStr = String::ToString("ShapeBaseData: Couldn't load shape \"%s\"",shapeName); return false; + } if(server) mCRC = fileRef->getChecksum(); diff --git a/Engine/source/T3D/shapeImage.cpp b/Engine/source/T3D/shapeImage.cpp index 554eeb55b..3bdff8642 100644 --- a/Engine/source/T3D/shapeImage.cpp +++ b/Engine/source/T3D/shapeImage.cpp @@ -462,7 +462,10 @@ bool ShapeBaseImageData::preload(bool server, String &errorStr) Torque::FS::FileNodeRef fileRef = Torque::FS::GetFileNode(shape[i].getPath()); if (!fileRef) + { + errorStr = String::ToString("ShapeBaseImageData: Couldn't load shape \"%s\"",name); return false; + } if(server) { diff --git a/Engine/source/T3D/vehicles/vehicle.cpp b/Engine/source/T3D/vehicles/vehicle.cpp index f4b58ac61..18a36b75f 100644 --- a/Engine/source/T3D/vehicles/vehicle.cpp +++ b/Engine/source/T3D/vehicles/vehicle.cpp @@ -218,6 +218,7 @@ bool VehicleData::preload(bool server, String &errorStr) if (!collisionDetails.size() || collisionDetails[0] == -1) { Con::errorf("VehicleData::preload failed: Vehicle models must define a collision-1 detail"); + errorStr = String::ToString("VehicleData: Couldn't load shape \"%s\"",shapeName); return false; } From ff3d72747e47515f9dd10e7c8636959e3077fff9 Mon Sep 17 00:00:00 2001 From: bank Date: Fri, 28 Mar 2014 11:54:11 +0400 Subject: [PATCH 027/317] Fix FixedSizeVector() initialization. mArray[6] was never assigned (ownerless experssion). --- Engine/source/core/util/tFixedSizeVector.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Engine/source/core/util/tFixedSizeVector.h b/Engine/source/core/util/tFixedSizeVector.h index cab8a2186..ec7e74251 100644 --- a/Engine/source/core/util/tFixedSizeVector.h +++ b/Engine/source/core/util/tFixedSizeVector.h @@ -46,19 +46,19 @@ class FixedSizeVector FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f ) { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; } FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g ) - { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; } + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ] = g; } FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h ) - { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; } + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ] = g; mArray[ 7 ] = h; } FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i ) - { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; } + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ] = g; mArray[ 7 ] = h; mArray[ 8 ] = i; } FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i, const T& j ) - { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; } + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ] = g; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; } FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i, const T& j, const T& k ) - { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; mArray[ 10 ] = k; } + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ] = g; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; mArray[ 10 ] = k; } FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i, const T& j, const T& k, const T& l ) - { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; mArray[ 10 ] = k; mArray[ 11 ] = l; } + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ] = g; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; mArray[ 10 ] = k; mArray[ 11 ] = l; } FixedSizeVector( const T& a, const T& b, const T& c, const T& d, const T& e, const T& f, const T& g, const T& h, const T& i, const T& j, const T& k, const T& l, const T& m ) - { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ]; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; mArray[ 10 ] = k; mArray[ 11 ] = l; mArray[ 12 ] =m; } + { mArray[ 0 ] = a; mArray[ 1 ] = b; mArray[ 2 ] = c; mArray[ 3 ] = d; mArray[ 4 ] = e; mArray[ 5 ] = f; mArray[ 6 ] = g; mArray[ 7 ] = h; mArray[ 8 ] = i; mArray[ 9 ] = j; mArray[ 10 ] = k; mArray[ 11 ] = l; mArray[ 12 ] =m; } U32 size() const { return SIZE; } bool empty() const { return ( SIZE == 0 ); } From e7eab10ad51c18e59083a949ce8320281ce1f6eb Mon Sep 17 00:00:00 2001 From: bank Date: Thu, 1 May 2014 12:34:43 +0400 Subject: [PATCH 028/317] Corrected the use of #pragma warning() We should use #pragma warning(push/pop) instead of (default: X) --- Engine/source/console/engineAPI.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/source/console/engineAPI.h b/Engine/source/console/engineAPI.h index 4358844b4..4f5e430f4 100644 --- a/Engine/source/console/engineAPI.h +++ b/Engine/source/console/engineAPI.h @@ -76,6 +76,7 @@ // Disable some VC warnings that are irrelevant to us. +#pragma warning( push ) #pragma warning( disable : 4510 ) // default constructor could not be generated; all the Args structures are never constructed by us #pragma warning( disable : 4610 ) // can never be instantiated; again Args is never constructed by us @@ -2788,7 +2789,6 @@ struct _EngineConsoleCallbackHelper // Re-enable some VC warnings we disabled for this file. -#pragma warning( default : 4510 ) -#pragma warning( default : 4610 ) +#pragma warning( pop ) // 4510 and 4610 #endif // !_ENGINEAPI_H_ From e6673eaf30113b55085ecafb2017eef852af8bdd Mon Sep 17 00:00:00 2001 From: bank Date: Thu, 1 May 2014 12:37:57 +0400 Subject: [PATCH 029/317] Fix: the string literal is implicitly casted to the bool type. --- Engine/source/cinterface/c_scripting.cpp | 4 ++-- Engine/source/cinterface/cinterface.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine/source/cinterface/c_scripting.cpp b/Engine/source/cinterface/c_scripting.cpp index 7a66feaec..32a2d5b9d 100644 --- a/Engine/source/cinterface/c_scripting.cpp +++ b/Engine/source/cinterface/c_scripting.cpp @@ -222,7 +222,7 @@ extern "C" { { // maxArgs improper on a number of console function/methods if (argc < entry->mMinArgs)// || argc > entry->mMaxArgs) - return ""; + return false; SimObject* o = NULL; @@ -230,7 +230,7 @@ extern "C" { { o = Sim::findObject(dAtoi(argv[1])); if (!o) - return ""; + return false; } return entry->cb.mBoolCallbackFunc(o, argc, argv); diff --git a/Engine/source/cinterface/cinterface.cpp b/Engine/source/cinterface/cinterface.cpp index c98b294cd..df980334e 100644 --- a/Engine/source/cinterface/cinterface.cpp +++ b/Engine/source/cinterface/cinterface.cpp @@ -303,7 +303,7 @@ extern "C" { Namespace::Entry* entry = GetEntry(nameSpace, name); if (!entry) - return ""; + return false; return entry->cb.mBoolCallbackFunc(NULL, argc, argv); } From 9f2d44966e43bb54247963cde1309b5a7a0f6785 Mon Sep 17 00:00:00 2001 From: bank Date: Thu, 1 May 2014 12:51:08 +0400 Subject: [PATCH 030/317] Fix: Some compilers do not auto-cast String and Path classes to (char *). --- Engine/source/T3D/player.cpp | 2 +- Engine/source/gfx/D3D9/gfxD3D9Shader.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 75c42e9b6..6edbd2e21 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -587,7 +587,7 @@ bool PlayerData::preload(bool server, String &errorStr) if (!fileRef) { - errorStr = String::ToString("PlayerData: Mounted image %d loading failed, shape \"%s\" is not found.",i,mShapeFP[i].getPath()); + errorStr = String::ToString("PlayerData: Mounted image %d loading failed, shape \"%s\" is not found.",i,mShapeFP[i].getPath().getFullPath().c_str()); return false; } diff --git a/Engine/source/gfx/D3D9/gfxD3D9Shader.cpp b/Engine/source/gfx/D3D9/gfxD3D9Shader.cpp index 34b9c47d2..a7ff68a78 100644 --- a/Engine/source/gfx/D3D9/gfxD3D9Shader.cpp +++ b/Engine/source/gfx/D3D9/gfxD3D9Shader.cpp @@ -444,7 +444,7 @@ const String GFXD3D9ShaderConstBuffer::describeSelf() const GenericConstBufferLayout::ParamDesc pd; mVertexConstBufferLayoutF->getDesc(i, pd); - ret += String::ToString(" Constant name: %s", pd.name); + ret += String::ToString(" Constant name: %s", pd.name.c_str()); } return ret; From 15bad30b2cb314879212d1506f7e26d2fa406132 Mon Sep 17 00:00:00 2001 From: bank Date: Thu, 1 May 2014 12:55:01 +0400 Subject: [PATCH 031/317] Fix: Underflow of the buffer 'ctx'. memset function should not receive sizeof of the pointer. --- Engine/source/core/util/md5.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/core/util/md5.cpp b/Engine/source/core/util/md5.cpp index 8358d7655..41503cb86 100644 --- a/Engine/source/core/util/md5.cpp +++ b/Engine/source/core/util/md5.cpp @@ -154,7 +154,7 @@ void MD5Final( unsigned char digest[16], MD5Context* ctx) MD5Transform(ctx->buf, (int *) ctx->in); byteReverse((unsigned char *) ctx->buf, 4); memcpy(digest, ctx->buf, 16); - memset(ctx, 0, sizeof(ctx)); /* In case it's sensitive */ + memset(ctx, 0, sizeof(MD5Context)); /* In case it's sensitive */ } From c7a5a120805b64c0b390d38a4819dd25409426f0 Mon Sep 17 00:00:00 2001 From: bank Date: Thu, 1 May 2014 12:57:33 +0400 Subject: [PATCH 032/317] Fix wrong check for bits (using '&&' instead of '&'). --- Engine/source/gfx/D3D9/gfxD3D9TextureManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/gfx/D3D9/gfxD3D9TextureManager.cpp b/Engine/source/gfx/D3D9/gfxD3D9TextureManager.cpp index 417d7f803..7edb59c44 100644 --- a/Engine/source/gfx/D3D9/gfxD3D9TextureManager.cpp +++ b/Engine/source/gfx/D3D9/gfxD3D9TextureManager.cpp @@ -236,7 +236,7 @@ void GFXD3D9TextureManager::_innerCreateTexture( GFXD3D9TextureObject *retTex, // If this is a render target, and it wants AA or wants to match the backbuffer (for example, to share the z) // Check the caps though, if we can't stretchrect between textures, use the old RT method. (Which hopefully means // that they can't force AA on us as well.) - if (retTex->mProfile->isRenderTarget() && mslevel != 0 && (mDeviceCaps.Caps2 && D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES)) + if (retTex->mProfile->isRenderTarget() && mslevel != 0 && (mDeviceCaps.Caps2 & D3DDEVCAPS2_CAN_STRETCHRECT_FROM_TEXTURES)) { D3D9Assert(mD3DDevice->CreateRenderTarget(width, height, d3dTextureFormat, mstype, mslevel, false, retTex->getSurfacePtr(), NULL), From 68b12981ae19486b23b98df351e4258d77d702d9 Mon Sep 17 00:00:00 2001 From: bank Date: Thu, 1 May 2014 13:11:21 +0400 Subject: [PATCH 033/317] Fix: dSprintf should receive size of the buffer, not the size of the pointer. It was safe, as we are using static console return buffer, which is large enough to hold enough data (default is 2048). --- Engine/source/T3D/missionArea.cpp | 2 +- Engine/source/gui/editor/guiDebugger.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/source/T3D/missionArea.cpp b/Engine/source/T3D/missionArea.cpp index d11aa91ce..1e592b7b2 100644 --- a/Engine/source/T3D/missionArea.cpp +++ b/Engine/source/T3D/missionArea.cpp @@ -179,7 +179,7 @@ DefineEngineMethod( MissionArea, getArea, const char *, (),, char* returnBuffer = Con::getReturnBuffer(48); RectI area = object->getArea(); - dSprintf(returnBuffer, sizeof(returnBuffer), "%d %d %d %d", area.point.x, area.point.y, area.extent.x, area.extent.y); + dSprintf(returnBuffer, 48, "%d %d %d %d", area.point.x, area.point.y, area.extent.x, area.extent.y); return(returnBuffer); } diff --git a/Engine/source/gui/editor/guiDebugger.cpp b/Engine/source/gui/editor/guiDebugger.cpp index 625704bee..dbebc4e83 100644 --- a/Engine/source/gui/editor/guiDebugger.cpp +++ b/Engine/source/gui/editor/guiDebugger.cpp @@ -79,7 +79,7 @@ ConsoleMethod(DbgFileView, getCurrentLine, const char *, 2, 2, "()" S32 lineNum; const char *file = object->getCurrentLine(lineNum); char* ret = Con::getReturnBuffer(256); - dSprintf(ret, sizeof(ret), "%s\t%d", file, lineNum); + dSprintf(ret, 256, "%s\t%d", file, lineNum); return ret; } From e7fb6a54de0af8d14d294c6fac494965ee1c780a Mon Sep 17 00:00:00 2001 From: bank Date: Thu, 1 May 2014 13:22:13 +0400 Subject: [PATCH 034/317] Fix: Added missing virtual destructors for classes with virtual functions. --- Engine/source/T3D/fx/cameraFXMgr.h | 1 + Engine/source/core/threadStatic.h | 1 + Engine/source/gfx/screenshot.h | 1 + Engine/source/gfx/video/videoCapture.h | 2 ++ Engine/source/shaderGen/shaderFeature.h | 2 ++ Engine/source/ts/collada/colladaUtils.h | 2 ++ 6 files changed, 9 insertions(+) diff --git a/Engine/source/T3D/fx/cameraFXMgr.h b/Engine/source/T3D/fx/cameraFXMgr.h index 5a560bfc6..b40411f3a 100644 --- a/Engine/source/T3D/fx/cameraFXMgr.h +++ b/Engine/source/T3D/fx/cameraFXMgr.h @@ -45,6 +45,7 @@ protected: public: CameraFX(); + virtual ~CameraFX() { } MatrixF & getTrans(){ return mCamFXTrans; } virtual bool isExpired(){ return mElapsedTime >= mDuration; } diff --git a/Engine/source/core/threadStatic.h b/Engine/source/core/threadStatic.h index 716d590b9..2f5a16cbf 100644 --- a/Engine/source/core/threadStatic.h +++ b/Engine/source/core/threadStatic.h @@ -47,6 +47,7 @@ public: : mHitCount( 0 ) #endif { }; + virtual ~_TorqueThreadStatic() { } static const U32 getListIndex(){ return mListIndex; } diff --git a/Engine/source/gfx/screenshot.h b/Engine/source/gfx/screenshot.h index 63de45280..9de224f06 100644 --- a/Engine/source/gfx/screenshot.h +++ b/Engine/source/gfx/screenshot.h @@ -74,6 +74,7 @@ public: /// Constructor. ScreenShot(); + virtual ~ScreenShot() { } /// Used to start the screenshot capture. void setPending( const char *filename, bool writeJPG, S32 tiles, F32 overlap ); diff --git a/Engine/source/gfx/video/videoCapture.h b/Engine/source/gfx/video/videoCapture.h index 5ef80c836..7f46b133f 100644 --- a/Engine/source/gfx/video/videoCapture.h +++ b/Engine/source/gfx/video/videoCapture.h @@ -221,6 +221,8 @@ protected: // List with bitmaps which are done encoding ThreadSafeDeque< GBitmap* > mProcessedBitmaps; public: + virtual ~VideoEncoder() { } + // Stores an encoded bitmap to be dealt with later void pushProcessedBitmap( GBitmap* bitmap ); diff --git a/Engine/source/shaderGen/shaderFeature.h b/Engine/source/shaderGen/shaderFeature.h index a5f0a7d89..f18fae7f9 100644 --- a/Engine/source/shaderGen/shaderFeature.h +++ b/Engine/source/shaderGen/shaderFeature.h @@ -48,6 +48,8 @@ class ShaderFeatureConstHandles { public: + virtual ~ShaderFeatureConstHandles() { } + virtual void init( GFXShader *shader ) = 0; virtual void setConsts( SceneRenderState *state, diff --git a/Engine/source/ts/collada/colladaUtils.h b/Engine/source/ts/collada/colladaUtils.h index e2ee4b377..1619f63fe 100644 --- a/Engine/source/ts/collada/colladaUtils.h +++ b/Engine/source/ts/collada/colladaUtils.h @@ -430,6 +430,8 @@ public: class BasePrimitive { public: + virtual ~BasePrimitive() { } + /// Return true if the element is a geometric primitive type static bool isPrimitive(const daeElement* element) { From b0fcc91058c3889a45da477a684d1cf5bb9a633d Mon Sep 17 00:00:00 2001 From: bank Date: Thu, 1 May 2014 13:24:26 +0400 Subject: [PATCH 035/317] Fix: removed 'virtual' for classes which doesn't have child. --- Engine/source/console/telnetDebugger.h | 2 +- Engine/source/ts/tsShapeInstance.h | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine/source/console/telnetDebugger.h b/Engine/source/console/telnetDebugger.h index 6916ae935..5a30c9cf7 100644 --- a/Engine/source/console/telnetDebugger.h +++ b/Engine/source/console/telnetDebugger.h @@ -127,7 +127,7 @@ public: void breakProcess(); - virtual void executionStopped(CodeBlock *code, U32 lineNumber); + void executionStopped(CodeBlock *code, U32 lineNumber); void send(const char *s); void setDebugParameters(S32 port, const char *password, bool waitForClient); void processConsoleLine(const char *consoleLine); diff --git a/Engine/source/ts/tsShapeInstance.h b/Engine/source/ts/tsShapeInstance.h index f507f7153..7f83851c4 100644 --- a/Engine/source/ts/tsShapeInstance.h +++ b/Engine/source/ts/tsShapeInstance.h @@ -485,8 +485,8 @@ protected: /// @} - virtual void render( const TSRenderState &rdata ); - virtual void render( const TSRenderState &rdata, S32 dl, F32 intraDL = 0.0f ); + void render( const TSRenderState &rdata ); + void render( const TSRenderState &rdata, S32 dl, F32 intraDL = 0.0f ); void animate() { animate( mCurrentDetailLevel ); } void animate(S32 dl); From a7756b5591c492e1f39ae57797c37e705aed6c34 Mon Sep 17 00:00:00 2001 From: bank Date: Tue, 6 May 2014 15:35:46 +0400 Subject: [PATCH 036/317] Renamed local variable so it doesn't interfere with the provided argument. String errorStr should be filled in only when ::preload returns false. --- Engine/source/T3D/fx/explosion.cpp | 6 +++--- Engine/source/T3D/fx/lightning.cpp | 10 +++++----- Engine/source/T3D/player.cpp | 6 +++--- Engine/source/T3D/projectile.cpp | 6 +++--- Engine/source/T3D/proximityMine.cpp | 10 +++++----- 5 files changed, 19 insertions(+), 19 deletions(-) diff --git a/Engine/source/T3D/fx/explosion.cpp b/Engine/source/T3D/fx/explosion.cpp index b84efad3f..ad063f5af 100644 --- a/Engine/source/T3D/fx/explosion.cpp +++ b/Engine/source/T3D/fx/explosion.cpp @@ -736,9 +736,9 @@ bool ExplosionData::preload(bool server, String &errorStr) if( !server ) { - String errorStr; - if( !sfxResolve( &soundProfile, errorStr ) ) - Con::errorf(ConsoleLogEntry::General, "Error, unable to load sound profile for explosion datablock: %s", errorStr.c_str()); + String sfxErrorStr; + if( !sfxResolve( &soundProfile, sfxErrorStr ) ) + Con::errorf(ConsoleLogEntry::General, "Error, unable to load sound profile for explosion datablock: %s", sfxErrorStr.c_str()); if (!particleEmitter && particleEmitterId != 0) if (Sim::findObject(particleEmitterId, particleEmitter) == false) Con::errorf(ConsoleLogEntry::General, "Error, unable to load particle emitter for explosion datablock"); diff --git a/Engine/source/T3D/fx/lightning.cpp b/Engine/source/T3D/fx/lightning.cpp index 338bd0a95..ed6322f96 100644 --- a/Engine/source/T3D/fx/lightning.cpp +++ b/Engine/source/T3D/fx/lightning.cpp @@ -287,14 +287,14 @@ bool LightningData::preload(bool server, String &errorStr) if (server == false) { - String errorStr; + String sfxErrorStr; for (U32 i = 0; i < MaxThunders; i++) { - if( !sfxResolve( &thunderSounds[ i ], errorStr ) ) - Con::errorf(ConsoleLogEntry::General, "LightningData::preload: Invalid packet: %s", errorStr.c_str()); + if( !sfxResolve( &thunderSounds[ i ], sfxErrorStr ) ) + Con::errorf(ConsoleLogEntry::General, "LightningData::preload: Invalid packet: %s", sfxErrorStr.c_str()); } - if( !sfxResolve( &strikeSound, errorStr ) ) - Con::errorf(ConsoleLogEntry::General, "LightningData::preload: Invalid packet: %s", errorStr.c_str()); + if( !sfxResolve( &strikeSound, sfxErrorStr ) ) + Con::errorf(ConsoleLogEntry::General, "LightningData::preload: Invalid packet: %s", sfxErrorStr.c_str()); for (U32 i = 0; i < MaxTextures; i++) { diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 6edbd2e21..f7c20e066 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -427,9 +427,9 @@ bool PlayerData::preload(bool server, String &errorStr) { for( U32 i = 0; i < MaxSounds; ++ i ) { - String errorStr; - if( !sfxResolve( &sound[ i ], errorStr ) ) - Con::errorf( "PlayerData::preload: %s", errorStr.c_str() ); + String sfxErrorStr; + if( !sfxResolve( &sound[ i ], sfxErrorStr ) ) + Con::errorf( "PlayerData::preload: %s", sfxErrorStr.c_str() ); } } diff --git a/Engine/source/T3D/projectile.cpp b/Engine/source/T3D/projectile.cpp index 86886c2b5..5bd211260 100644 --- a/Engine/source/T3D/projectile.cpp +++ b/Engine/source/T3D/projectile.cpp @@ -320,9 +320,9 @@ bool ProjectileData::preload(bool server, String &errorStr) if (Sim::findObject(decalId, decal) == false) Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet, bad datablockId(decal): %d", decalId); - String errorStr; - if( !sfxResolve( &sound, errorStr ) ) - Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet: %s", errorStr.c_str()); + String sfxErrorStr; + if( !sfxResolve( &sound, sfxErrorStr ) ) + Con::errorf(ConsoleLogEntry::General, "ProjectileData::preload: Invalid packet: %s", sfxErrorStr.c_str()); if (!lightDesc && lightDescId != 0) if (Sim::findObject(lightDescId, lightDesc) == false) diff --git a/Engine/source/T3D/proximityMine.cpp b/Engine/source/T3D/proximityMine.cpp index c37947a16..8ce533343 100644 --- a/Engine/source/T3D/proximityMine.cpp +++ b/Engine/source/T3D/proximityMine.cpp @@ -136,11 +136,11 @@ bool ProximityMineData::preload( bool server, String& errorStr ) if ( !server ) { // Resolve sounds - String errorStr; - if( !sfxResolve( &armingSound, errorStr ) ) - Con::errorf( ConsoleLogEntry::General, "ProximityMineData::preload: Invalid packet: %s", errorStr.c_str() ); - if( !sfxResolve( &triggerSound, errorStr ) ) - Con::errorf( ConsoleLogEntry::General, "ProximityMineData::preload: Invalid packet: %s", errorStr.c_str() ); + String sfxErrorStr; + if( !sfxResolve( &armingSound, sfxErrorStr ) ) + Con::errorf( ConsoleLogEntry::General, "ProximityMineData::preload: Invalid packet: %s", sfxErrorStr.c_str() ); + if( !sfxResolve( &triggerSound, sfxErrorStr ) ) + Con::errorf( ConsoleLogEntry::General, "ProximityMineData::preload: Invalid packet: %s", sfxErrorStr.c_str() ); } if ( mShape ) From 6d176cf8db45ae8b934f57f1f18c03ea6a785b7d Mon Sep 17 00:00:00 2001 From: bank Date: Tue, 6 May 2014 15:39:17 +0400 Subject: [PATCH 037/317] Rename local variable to prevent confusion with member variable. --- Engine/source/console/codeBlock.cpp | 12 ++++++------ Engine/source/gui/controls/guiPopUpCtrl.cpp | 16 ++++++++-------- Engine/source/gui/controls/guiPopUpCtrlEx.cpp | 16 ++++++++-------- Engine/source/ts/collada/colladaAppMesh.cpp | 16 ++++++++-------- Engine/source/ts/collada/colladaAppMesh.h | 8 ++++---- Engine/source/ts/tsShape.cpp | 6 +++--- Engine/source/ts/tsShape.h | 2 +- Engine/source/ts/tsShapeOldRead.cpp | 8 ++++---- 8 files changed, 42 insertions(+), 42 deletions(-) diff --git a/Engine/source/console/codeBlock.cpp b/Engine/source/console/codeBlock.cpp index 896d2a4cf..c3e96a03b 100644 --- a/Engine/source/console/codeBlock.cpp +++ b/Engine/source/console/codeBlock.cpp @@ -403,14 +403,14 @@ bool CodeBlock::read(StringTableEntry fileName, Stream &st) for(U32 i = 0; i < size; i++) st.read(&functionFloats[i]); } - U32 codeSize; - st.read(&codeSize); + U32 codeLength; + st.read(&codeLength); st.read(&lineBreakPairCount); - U32 totSize = codeSize + lineBreakPairCount * 2; + U32 totSize = codeLength + lineBreakPairCount * 2; code = new U32[totSize]; - for(i = 0; i < codeSize; i++) + for(i = 0; i < codeLength; i++) { U8 b; st.read(&b); @@ -420,10 +420,10 @@ bool CodeBlock::read(StringTableEntry fileName, Stream &st) code[i] = b; } - for(i = codeSize; i < totSize; i++) + for(i = codeLength; i < totSize; i++) st.read(&code[i]); - lineBreakPairs = code + codeSize; + lineBreakPairs = code + codeLength; // StringTable-ize our identifiers. U32 identCount; diff --git a/Engine/source/gui/controls/guiPopUpCtrl.cpp b/Engine/source/gui/controls/guiPopUpCtrl.cpp index e4b2903f7..b67776461 100644 --- a/Engine/source/gui/controls/guiPopUpCtrl.cpp +++ b/Engine/source/gui/controls/guiPopUpCtrl.cpp @@ -1011,8 +1011,8 @@ void GuiPopUpMenuCtrl::onRender( Point2I offset, const RectI &updateRect ) { // We're making use of a bitmap border, so take into account the // right cap of the border. - RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); - localStart.x = getWidth() - mBitmapBounds[2].extent.x - txt_w; + RectI* bitmapBounds = mProfile->mBitmapArrayRects.address(); + localStart.x = getWidth() - bitmapBounds[2].extent.x - txt_w; } else { @@ -1024,8 +1024,8 @@ void GuiPopUpMenuCtrl::onRender( Point2I offset, const RectI &updateRect ) { // We're making use of a bitmap border, so take into account the // right cap of the border. - RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); - localStart.x = (getWidth() - mBitmapBounds[2].extent.x - txt_w) / 2; + RectI* bitmapBounds = mProfile->mBitmapArrayRects.address(); + localStart.x = (getWidth() - bitmapBounds[2].extent.x - txt_w) / 2; } else { @@ -1043,8 +1043,8 @@ void GuiPopUpMenuCtrl::onRender( Point2I offset, const RectI &updateRect ) { // We're making use of a bitmap border, so take into account the // right cap of the border. - RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); - localStart.x = getWidth() - mBitmapBounds[2].extent.x - txt_w; + RectI* bitmapBounds = mProfile->mBitmapArrayRects.address(); + localStart.x = getWidth() - bitmapBounds[2].extent.x - txt_w; } else { @@ -1095,8 +1095,8 @@ void GuiPopUpMenuCtrl::onRender( Point2I offset, const RectI &updateRect ) { // We're making use of a bitmap border, so take into account the // right cap of the border. - RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); - Point2I textpos = localToGlobalCoord( Point2I( getWidth() - txt_w - mBitmapBounds[2].extent.x, localStart.y ) ); + RectI* bitmapBounds = mProfile->mBitmapArrayRects.address(); + Point2I textpos = localToGlobalCoord( Point2I( getWidth() - txt_w - bitmapBounds[2].extent.x, localStart.y ) ); GFX->getDrawUtil()->drawText( mProfile->mFont, textpos, buff, mProfile->mFontColors ); } else diff --git a/Engine/source/gui/controls/guiPopUpCtrlEx.cpp b/Engine/source/gui/controls/guiPopUpCtrlEx.cpp index 275faf75c..4db07ed33 100644 --- a/Engine/source/gui/controls/guiPopUpCtrlEx.cpp +++ b/Engine/source/gui/controls/guiPopUpCtrlEx.cpp @@ -1167,8 +1167,8 @@ void GuiPopUpMenuCtrlEx::onRender(Point2I offset, const RectI &updateRect) { // We're making use of a bitmap border, so take into account the // right cap of the border. - RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); - localStart.x = getWidth() - mBitmapBounds[2].extent.x - txt_w; + RectI* bitmapBounds = mProfile->mBitmapArrayRects.address(); + localStart.x = getWidth() - bitmapBounds[2].extent.x - txt_w; } else { @@ -1180,8 +1180,8 @@ void GuiPopUpMenuCtrlEx::onRender(Point2I offset, const RectI &updateRect) { // We're making use of a bitmap border, so take into account the // right cap of the border. - RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); - localStart.x = (getWidth() - mBitmapBounds[2].extent.x - txt_w) / 2; + RectI* bitmapBounds = mProfile->mBitmapArrayRects.address(); + localStart.x = (getWidth() - bitmapBounds[2].extent.x - txt_w) / 2; } else { @@ -1199,8 +1199,8 @@ void GuiPopUpMenuCtrlEx::onRender(Point2I offset, const RectI &updateRect) { // We're making use of a bitmap border, so take into account the // right cap of the border. - RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); - localStart.x = getWidth() - mBitmapBounds[2].extent.x - txt_w; + RectI* bitmapBounds = mProfile->mBitmapArrayRects.address(); + localStart.x = getWidth() - bitmapBounds[2].extent.x - txt_w; } else { @@ -1251,8 +1251,8 @@ void GuiPopUpMenuCtrlEx::onRender(Point2I offset, const RectI &updateRect) { // We're making use of a bitmap border, so take into account the // right cap of the border. - RectI* mBitmapBounds = mProfile->mBitmapArrayRects.address(); - Point2I textpos = localToGlobalCoord( Point2I( getWidth() - txt_w - mBitmapBounds[2].extent.x, localStart.y ) ); + RectI* bitmapBounds = mProfile->mBitmapArrayRects.address(); + Point2I textpos = localToGlobalCoord( Point2I( getWidth() - txt_w - bitmapBounds[2].extent.x, localStart.y ) ); GFX->getDrawUtil()->drawText( mProfile->mFont, textpos, buff, mProfile->mFontColors ); } else diff --git a/Engine/source/ts/collada/colladaAppMesh.cpp b/Engine/source/ts/collada/colladaAppMesh.cpp index 1b58140e5..6563e244a 100644 --- a/Engine/source/ts/collada/colladaAppMesh.cpp +++ b/Engine/source/ts/collada/colladaAppMesh.cpp @@ -623,7 +623,7 @@ void ColladaAppMesh::getPrimitives(const domGeometry* geometry) delete meshPrims[iPrim]; } -void ColladaAppMesh::getVertexData(const domGeometry* geometry, F32 time, const MatrixF& objectOffset, +void ColladaAppMesh::getVertexData(const domGeometry* geometry, F32 time, const MatrixF& objOffset, Vector& v_points, Vector& v_norms, Vector& v_colors, @@ -693,7 +693,7 @@ void ColladaAppMesh::getVertexData(const domGeometry* geometry, F32 time, const if (appNode->invertMeshes) points_array[iVert].z = -points_array[iVert].z; - objectOffset.mulP(points_array[iVert]); + objOffset.mulP(points_array[iVert]); } if (appendValues || ((tuple.uv >= 0) && (tuple.uv < streams.uvs.size()))) { @@ -755,7 +755,7 @@ void ColladaAppMesh::getVertexData(const domGeometry* geometry, F32 time, const delete meshPrims[iPrim]; } -void ColladaAppMesh::getMorphVertexData(const domMorph* morph, F32 time, const MatrixF& objectOffset, +void ColladaAppMesh::getMorphVertexData(const domMorph* morph, F32 time, const MatrixF& objOffset, Vector& v_points, Vector& v_norms, Vector& v_colors, @@ -807,7 +807,7 @@ void ColladaAppMesh::getMorphVertexData(const domMorph* morph, F32 time, const M return; getPrimitives(baseGeometry); - getVertexData(baseGeometry, time, objectOffset, v_points, v_norms, v_colors, v_uvs, v_uv2s, true); + getVertexData(baseGeometry, time, objOffset, v_points, v_norms, v_colors, v_uvs, v_uv2s, true); // Get pointers to the arrays of base geometry data Point3F* points_array = &v_points[v_points.size() - vertTuples.size()]; @@ -863,7 +863,7 @@ void ColladaAppMesh::getMorphVertexData(const domMorph* morph, F32 time, const M if (uv2s_array) targetUv2s.set(uv2s_array, vertTuples.size()); - getVertexData(targetGeoms[iTarget], time, objectOffset, targetPoints, targetNorms, targetColors, targetUvs, targetUv2s, false); + getVertexData(targetGeoms[iTarget], time, objOffset, targetPoints, targetNorms, targetColors, targetUvs, targetUv2s, false); // Combine with base geometry for (int iVert = 0; iVert < vertTuples.size(); iVert++) { @@ -883,7 +883,7 @@ void ColladaAppMesh::getMorphVertexData(const domMorph* morph, F32 time, const M } } -void ColladaAppMesh::lockMesh(F32 t, const MatrixF& objectOffset) +void ColladaAppMesh::lockMesh(F32 t, const MatrixF& objOffset) { // Find the geometry element for this mesh. Could be one of 3 things: // 1) a simple static mesh (Collada element) @@ -923,10 +923,10 @@ void ColladaAppMesh::lockMesh(F32 t, const MatrixF& objectOffset) // Now get the vertex data at the specified time if (geometry->getElementType() == COLLADA_TYPE::GEOMETRY) { getPrimitives(daeSafeCast(geometry)); - getVertexData(daeSafeCast(geometry), t, objectOffset, points, normals, colors, uvs, uv2s, true); + getVertexData(daeSafeCast(geometry), t, objOffset, points, normals, colors, uvs, uv2s, true); } else if (geometry->getElementType() == COLLADA_TYPE::MORPH) { - getMorphVertexData(daeSafeCast(geometry), t, objectOffset, points, normals, colors, uvs, uv2s); + getMorphVertexData(daeSafeCast(geometry), t, objOffset, points, normals, colors, uvs, uv2s); } else { daeErrorHandler::get()->handleWarning(avar("Unsupported geometry type " diff --git a/Engine/source/ts/collada/colladaAppMesh.h b/Engine/source/ts/collada/colladaAppMesh.h index e7d07c376..17ac1cad1 100644 --- a/Engine/source/ts/collada/colladaAppMesh.h +++ b/Engine/source/ts/collada/colladaAppMesh.h @@ -109,11 +109,11 @@ protected: bool checkGeometryType(const daeElement* element); void getPrimitives(const domGeometry* geometry); - void getVertexData( const domGeometry* geometry, F32 time, const MatrixF& objectOffset, + void getVertexData( const domGeometry* geometry, F32 time, const MatrixF& objOffset, Vector& points, Vector& norms, Vector& colors, Vector& uvs, Vector& uv2s, bool appendValues); - void getMorphVertexData( const domMorph* morph, F32 time, const MatrixF& objectOffset, + void getMorphVertexData( const domMorph* morph, F32 time, const MatrixF& objOffset, Vector& points, Vector& norms, Vector& colors, Vector& uvs, Vector& uv2s ); @@ -211,8 +211,8 @@ public: /// Generate the vertex, normal and triangle data for the mesh. /// /// @param time Time at which to generate the mesh data - /// @param objectOffset Transform to apply to the generated data (bounds transform) - void lockMesh(F32 time, const MatrixF& objectOffset); + /// @param objOffset Transform to apply to the generated data (bounds transform) + void lockMesh(F32 time, const MatrixF& objOffset); /// Get the transform of this mesh at a certain time /// diff --git a/Engine/source/ts/tsShape.cpp b/Engine/source/ts/tsShape.cpp index 726efabff..d01a871c5 100644 --- a/Engine/source/ts/tsShape.cpp +++ b/Engine/source/ts/tsShape.cpp @@ -1225,7 +1225,7 @@ void TSShape::assembleShape() if (smReadVersion<23) { // get detail information about skins... - S32 * detailFirstSkin = tsalloc.getPointer32(numDetails); + S32 * detFirstSkin = tsalloc.getPointer32(numDetails); S32 * detailNumSkins = tsalloc.getPointer32(numDetails); tsalloc.checkGuard(); @@ -1257,7 +1257,7 @@ void TSShape::assembleShape() ptr32 = tsalloc.allocShape32(numSkins); for (i=0; i Date: Thu, 1 May 2014 14:58:17 +0400 Subject: [PATCH 038/317] Fix GuiTreeViewCtrl: we should use the member var, removed local decl. --- Engine/source/gui/controls/guiTreeViewCtrl.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/source/gui/controls/guiTreeViewCtrl.cpp b/Engine/source/gui/controls/guiTreeViewCtrl.cpp index b6d2c99de..dae1cb918 100644 --- a/Engine/source/gui/controls/guiTreeViewCtrl.cpp +++ b/Engine/source/gui/controls/guiTreeViewCtrl.cpp @@ -3583,7 +3583,7 @@ void GuiTreeViewCtrl::onMiddleMouseDown(const GuiEvent & event) for (S32 j = 0; j < mSelected.size(); j++) { Con::printf("%d", mSelected[j]); } - S32 mCurrentDragCell = mMouseOverCell.y; + mCurrentDragCell = mMouseOverCell.y; S32 midpCell = (mCurrentDragCell) * mItemHeight + (mItemHeight/2); S32 currentY = pt.y; S32 yDiff = currentY-midpCell; @@ -3648,7 +3648,7 @@ void GuiTreeViewCtrl::onMouseDown(const GuiEvent & event) break; } } - S32 mCurrentDragCell = mMouseOverCell.y; + mCurrentDragCell = mMouseOverCell.y; if (mVisibleItems[firstSelectedIndex] != firstItem ) { /* From 5c986f755a79239891e48b60950f5feb321d59df Mon Sep 17 00:00:00 2001 From: bank Date: Tue, 6 May 2014 12:37:53 +0400 Subject: [PATCH 039/317] Fix: Bit shifting of the 32-bit value with a subsequent expansion to the 64-bit type. We now shift the 64-bit value, which is now treated correctly. --- .../source/lighting/advanced/hlsl/gBufferConditionerHLSL.cpp | 4 ++-- Engine/source/terrain/terrCell.cpp | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine/source/lighting/advanced/hlsl/gBufferConditionerHLSL.cpp b/Engine/source/lighting/advanced/hlsl/gBufferConditionerHLSL.cpp index d4d30f9ad..ed946e944 100644 --- a/Engine/source/lighting/advanced/hlsl/gBufferConditionerHLSL.cpp +++ b/Engine/source/lighting/advanced/hlsl/gBufferConditionerHLSL.cpp @@ -333,7 +333,7 @@ Var* GBufferConditionerHLSL::_conditionOutput( Var *unconditionedOutput, MultiLi // Encode depth into two channels if(mNormalStorageType != CartesianXYZ) { - const U64 maxValPerChannel = 1 << mBitsPerChannel; + const U64 maxValPerChannel = (U64)1 << mBitsPerChannel; meta->addStatement( new GenOp( " \r\n // Encode depth into hi/lo\r\n" ) ); meta->addStatement( new GenOp( avar( " float2 _tempDepth = frac(@.a * float2(1.0, %llu.0));\r\n", maxValPerChannel - 1 ), unconditionedOutput ) ); @@ -391,7 +391,7 @@ Var* GBufferConditionerHLSL::_unconditionInput( Var *conditionedInput, MultiLine // Recover depth from encoding if(mNormalStorageType != CartesianXYZ) { - const U64 maxValPerChannel = 1 << mBitsPerChannel; + const U64 maxValPerChannel = (U64)1 << mBitsPerChannel; meta->addStatement( new GenOp( " \r\n // Decode depth\r\n" ) ); meta->addStatement( new GenOp( avar( " @.w = dot( @.zw, float2(1.0, 1.0/%llu.0));\r\n", maxValPerChannel - 1 ), retVar, conditionedInput ) ); diff --git a/Engine/source/terrain/terrCell.cpp b/Engine/source/terrain/terrCell.cpp index dac6c910a..a0478ed77 100644 --- a/Engine/source/terrain/terrCell.cpp +++ b/Engine/source/terrain/terrCell.cpp @@ -849,7 +849,7 @@ void TerrCell::_updateMaterials() if ( index == U8_MAX || index > 63 ) continue; - mMaterials |= (U64)(1< Date: Sun, 11 May 2014 19:39:50 +0200 Subject: [PATCH 040/317] Fix for avoid a zero division on _StringTable::resize. --- Engine/source/core/stringTable.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Engine/source/core/stringTable.cpp b/Engine/source/core/stringTable.cpp index 3c2ff86f8..e94ca50e7 100644 --- a/Engine/source/core/stringTable.cpp +++ b/Engine/source/core/stringTable.cpp @@ -195,8 +195,11 @@ StringTableEntry _StringTable::lookupn(const char* val, S32 len, const bool cas } //-------------------------------------- -void _StringTable::resize(const U32 newSize) +void _StringTable::resize(const U32 _newSize) { + /// avoid a possible 0 division + const U32 newSize = _newSize ? _newSize : 1; + Node *head = NULL, *walk, *temp; U32 i; // reverse individual bucket lists From 32a73f9eb2dc1326e8dfeb984cede773553798f6 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 11 May 2014 19:46:43 +0200 Subject: [PATCH 041/317] Fix Dereference of null pointer on String::operator+=. --- Engine/source/core/util/str.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/core/util/str.cpp b/Engine/source/core/util/str.cpp index 9a89ef2a1..7d50f33ee 100644 --- a/Engine/source/core/util/str.cpp +++ b/Engine/source/core/util/str.cpp @@ -760,7 +760,7 @@ String& String::operator=(const String &src) String& String::operator+=(const StringChar *src) { - if( src == NULL && !*src ) + if( src == NULL || !*src ) return *this; // Append the given string into a new string From 8e2fca11c3ea9607e60590a7b10fef9982e95a52 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 11 May 2014 19:54:11 +0200 Subject: [PATCH 042/317] Fix ALDeviceList::GetDeviceVersion incorrect check of valid pointer. --- Engine/source/sfx/openal/aldlist.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/source/sfx/openal/aldlist.cpp b/Engine/source/sfx/openal/aldlist.cpp index e3991a7ee..2b68c91ce 100644 --- a/Engine/source/sfx/openal/aldlist.cpp +++ b/Engine/source/sfx/openal/aldlist.cpp @@ -161,9 +161,9 @@ const char *ALDeviceList::GetDeviceName(int index) void ALDeviceList::GetDeviceVersion(int index, int *major, int *minor) { if (index < GetNumDevices()) { - if (*major) + if (major) *major = vDeviceInfo[index].iMajorVersion; - if (*minor) + if (minor) *minor = vDeviceInfo[index].iMinorVersion; } return; From 4de9bd4e8596ac854b19b1ddc4b2b7246f95bf03 Mon Sep 17 00:00:00 2001 From: Lukas Joergensen Date: Mon, 12 May 2014 11:01:04 +0200 Subject: [PATCH 043/317] Initialize StockColors This commit makes sure the StockColors are initialized and destroyed, using the module system. --- Engine/source/core/color.cpp | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/Engine/source/core/color.cpp b/Engine/source/core/color.cpp index 2c6d360e3..a28ef430f 100644 --- a/Engine/source/core/color.cpp +++ b/Engine/source/core/color.cpp @@ -66,6 +66,24 @@ static typeColorIToNameHash mColorIToName; #define DEFAULT_UNKNOWN_STOCK_COLOR_NAME "White" +MODULE_BEGIN( StockColors ) + + MODULE_INIT_AFTER( GFX ) + + MODULE_INIT + { + // Create the stock colors. + StockColor::create(); + } + + MODULE_SHUTDOWN + { + // Destroy the stock colors. + StockColor::destroy(); + } + +MODULE_END; + //----------------------------------------------------------------------------- StockColorItem StockColorTable[] = From a33e500b8d3f48f898ab6c0fa65bb2c2ad80597f Mon Sep 17 00:00:00 2001 From: Azaezel Date: Mon, 12 May 2014 16:37:25 -0500 Subject: [PATCH 044/317] alters projectile decal application so that it picks a random rotAroundNormal when adding it to a scene. (keeps em from always pointing north or up) --- Engine/source/T3D/projectile.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/T3D/projectile.cpp b/Engine/source/T3D/projectile.cpp index 86886c2b5..f52102d2e 100644 --- a/Engine/source/T3D/projectile.cpp +++ b/Engine/source/T3D/projectile.cpp @@ -1011,7 +1011,7 @@ void Projectile::explode( const Point3F &p, const Point3F &n, const U32 collideT // Client (impact) decal. if ( mDataBlock->decal ) - gDecalManager->addDecal( p, n, 0.0f, mDataBlock->decal ); + gDecalManager->addDecal(p, n, mRandF(0.0f, M_2PI_F), mDataBlock->decal); // Client object updateSound(); From a73a4a84f1921b99acdcfc80d49df2f125b93b21 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Tue, 13 May 2014 17:24:28 -0500 Subject: [PATCH 045/317] followup to https://github.com/GarageGames/Torque3D/issues/393 --- Engine/source/T3D/rigidShape.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/T3D/rigidShape.cpp b/Engine/source/T3D/rigidShape.cpp index 189119070..7e46b6a68 100644 --- a/Engine/source/T3D/rigidShape.cpp +++ b/Engine/source/T3D/rigidShape.cpp @@ -1140,11 +1140,11 @@ void RigidShape::updatePos(F32 dt) void RigidShape::updateForces(F32 /*dt*/) { + if (mDisableMove) return; Point3F gravForce(0, 0, sRigidShapeGravity * mRigid.mass * mGravityMod); MatrixF currTransform; mRigid.getTransform(&currTransform); - mRigid.atRest = false; Point3F torque(0, 0, 0); Point3F force(0, 0, 0); From 4e203f21b3cc28b02f7bdec318e90e5576318e91 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Tue, 13 May 2014 17:40:25 -0500 Subject: [PATCH 046/317] debris collision mask expanded so that it also hits buildings and the like. --- Engine/source/T3D/debris.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/T3D/debris.cpp b/Engine/source/T3D/debris.cpp index 8b07154ec..ab65304d0 100644 --- a/Engine/source/T3D/debris.cpp +++ b/Engine/source/T3D/debris.cpp @@ -41,7 +41,7 @@ #include "lighting/lightQuery.h" -const U32 csmStaticCollisionMask = TerrainObjectType; +const U32 csmStaticCollisionMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType; const U32 csmDynamicCollisionMask = StaticShapeObjectType; From 9342115b1ebc89a49ea0a084f4d5af9082a92698 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Tue, 13 May 2014 19:53:00 -0500 Subject: [PATCH 047/317] helper functions for AI: testing if an object is in line of sight (and optionally if it's enabled), if a point is clear, and if an object is within a given angular field of view (optionally specified beyond a stock 45 degrees, as well as optionally checking if the target is enabled) --- Engine/source/T3D/aiPlayer.cpp | 135 ++++++++++++++++++++++++++++----- Engine/source/T3D/aiPlayer.h | 3 + 2 files changed, 118 insertions(+), 20 deletions(-) diff --git a/Engine/source/T3D/aiPlayer.cpp b/Engine/source/T3D/aiPlayer.cpp index 6d54358be..0b869c2a0 100644 --- a/Engine/source/T3D/aiPlayer.cpp +++ b/Engine/source/T3D/aiPlayer.cpp @@ -28,6 +28,10 @@ #include "T3D/gameBase/moveManager.h" #include "console/engineAPI.h" +static U32 AIPLAYER_LOSMASK = TerrainObjectType | WaterObjectType | + ShapeBaseObjectType | StaticShapeObjectType | + PlayerObjectType | ItemObjectType; + IMPLEMENT_CO_NETOBJECT_V1(AIPlayer); ConsoleDocClass( AIPlayer, @@ -417,28 +421,19 @@ bool AIPlayer::getAIMove(Move *movePtr) // Test for target location in sight if it's an object. The LOS is // run from the eye position to the center of the object's bounding, // which is not very accurate. - if (mAimObject) { - MatrixF eyeMat; - getEyeTransform(&eyeMat); - eyeMat.getColumn(3,&location); - Point3F targetLoc = mAimObject->getBoxCenter(); - - // This ray ignores non-static shapes. Cast Ray returns true - // if it hit something. - RayInfo dummy; - if (getContainer()->castRay( location, targetLoc, - StaticShapeObjectType | StaticObjectType | - TerrainObjectType, &dummy)) { - if (mTargetInLOS) { - throwCallback( "onTargetExitLOS" ); - mTargetInLOS = false; - } + if (mAimObject) + { + mTargetInLOS = checkInLos(mAimObject.getPointer(), false); + if (mTargetInLOS) + { + throwCallback("onTargetEnterLOS"); + mTargetInLOS = true; } else - if (!mTargetInLOS) { - throwCallback( "onTargetEnterLOS" ); - mTargetInLOS = true; - } + { + throwCallback("onTargetExitLOS"); + mTargetInLOS = false; + } } // Replicate the trigger state into the move so that @@ -610,3 +605,103 @@ DefineEngineMethod( AIPlayer, getAimObject, S32, (),, GameBase* obj = object->getAimObject(); return obj? obj->getId(): -1; } + +bool AIPlayer::checkInLos(GameBase* target, bool _checkEnabled = false) +{ + if (!isServerObject()) return false; + if (!(bool(target))) return false; + if (_checkEnabled) + { + ShapeBase *shapeBaseCheck = dynamic_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != Enabled) return false; + } + + RayInfo ri; + + disableCollision(); + + S32 mountCount = target->getMountedObjectCount(); + for (S32 i = 0; i < mountCount; i++) + { + target->getMountedObject(i)->disableCollision(); + } + Point3F muzzlePoint; + getMuzzlePointAI(0, &muzzlePoint); + bool hit = gServerContainer.castRay(muzzlePoint, target->getBoxCenter(), AIPLAYER_LOSMASK, &ri); + enableCollision(); + + for (S32 i = 0; i < mountCount; i++) + { + target->getMountedObject(i)->enableCollision(); + } + + if (hit) + { + if (target != dynamic_cast(ri.object)) hit = false; + } + + return hit; +} + +bool AIPlayer::checkLosClear(Point3F _pos) +{ + if (!isServerObject()) return false; + + RayInfo ri; + + disableCollision(); + + Point3F muzzlePoint; + getMuzzlePointAI(0, &muzzlePoint); + gServerContainer.castRay(muzzlePoint, _pos, AIPLAYER_LOSMASK, &ri); + bool emptySpace = bool(ri.object == NULL); + enableCollision(); + return emptySpace; +} + +DefineEngineMethod(AIPlayer, checkInLos, bool, (ShapeBase* obj, bool checkEnabled), (0, false), + "@brief Check for an object in line of sight.\n") +{ + return object->checkInLos(obj, checkEnabled); +} + +bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled = false) +{ + if (!isServerObject()) return false; + if (!(bool(target))) return false; + if (_checkEnabled) + { + ShapeBase *shapeBaseCheck = dynamic_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != Enabled) return false; + } + + MatrixF cam = getTransform(); + Point3F camPos; + VectorF camDir; + + cam.getColumn(3, &camPos); + cam.getColumn(1, &camDir); + + camFov = mDegToRad(camFov) / 2; + + Point3F shapePos; + // Use the render transform instead of the box center + // otherwise it'll jitter. + MatrixF srtMat = target->getTransform(); + srtMat.getColumn(3, &shapePos); + VectorF shapeDir = shapePos - camPos; + // Test to see if it's within our viewcone, this test doesn't + // actually match the viewport very well, should consider + // projection and box test. + shapeDir.normalize(); + F32 dot = mDot(shapeDir, camDir); + return (dot > camFov); +} + +DefineEngineMethod(AIPlayer, checkInFoV, bool, (ShapeBase* obj, F32 fov, bool checkEnabled), (0, 45, false), + "@brief Check for an object within a specified veiw cone.\n") +{ + return object->checkInFoV(obj, fov, checkEnabled); +} \ No newline at end of file diff --git a/Engine/source/T3D/aiPlayer.h b/Engine/source/T3D/aiPlayer.h index 4e0bb5a3a..283db14db 100644 --- a/Engine/source/T3D/aiPlayer.h +++ b/Engine/source/T3D/aiPlayer.h @@ -80,6 +80,9 @@ public: void setAimLocation( const Point3F &location ); Point3F getAimLocation() const { return mAimLocation; } void clearAim(); + bool checkInLos(GameBase* target, bool _checkEnabled); + bool checkLosClear(Point3F _pos); + bool checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled); // Movement sets/gets void setMoveSpeed( const F32 speed ); From 894d4f6d36ac94f2c2fd25999448be94a6b29f86 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Tue, 13 May 2014 21:38:49 -0500 Subject: [PATCH 048/317] editor default and retrieval values for particle emitter ejectionOffsetVariance (sets the sliderbars and text from memory when reloading an emitter DB) --- .../game/tools/particleEditor/particleEmitterEditor.ed.cs | 3 +++ .../Full/game/tools/particleEditor/particleEmitterEditor.ed.cs | 3 +++ 2 files changed, 6 insertions(+) diff --git a/Templates/Empty/game/tools/particleEditor/particleEmitterEditor.ed.cs b/Templates/Empty/game/tools/particleEditor/particleEmitterEditor.ed.cs index 11678a053..8d290f0a3 100644 --- a/Templates/Empty/game/tools/particleEditor/particleEmitterEditor.ed.cs +++ b/Templates/Empty/game/tools/particleEditor/particleEmitterEditor.ed.cs @@ -84,6 +84,9 @@ function PE_EmitterEditor::guiSync( %this ) PE_EmitterEditor-->PEE_ejectionOffset_slider.setValue( %data.ejectionOffset ); PE_EmitterEditor-->PEE_ejectionOffset_textEdit.setText( %data.ejectionOffset ); + PE_EmitterEditor-->PEE_ejectionOffsetVariance_slider.setValue( %data.ejectionOffsetVariance ); + PE_EmitterEditor-->PEE_ejectionOffsetVariance_textEdit.setText( %data.ejectionOffsetVariance ); + %blendTypeId = PE_EmitterEditor-->PEE_blendType.findText( %data.blendStyle ); PE_EmitterEditor-->PEE_blendType.setSelected( %blendTypeId, false ); diff --git a/Templates/Full/game/tools/particleEditor/particleEmitterEditor.ed.cs b/Templates/Full/game/tools/particleEditor/particleEmitterEditor.ed.cs index 11678a053..8d290f0a3 100644 --- a/Templates/Full/game/tools/particleEditor/particleEmitterEditor.ed.cs +++ b/Templates/Full/game/tools/particleEditor/particleEmitterEditor.ed.cs @@ -84,6 +84,9 @@ function PE_EmitterEditor::guiSync( %this ) PE_EmitterEditor-->PEE_ejectionOffset_slider.setValue( %data.ejectionOffset ); PE_EmitterEditor-->PEE_ejectionOffset_textEdit.setText( %data.ejectionOffset ); + PE_EmitterEditor-->PEE_ejectionOffsetVariance_slider.setValue( %data.ejectionOffsetVariance ); + PE_EmitterEditor-->PEE_ejectionOffsetVariance_textEdit.setText( %data.ejectionOffsetVariance ); + %blendTypeId = PE_EmitterEditor-->PEE_blendType.findText( %data.blendStyle ); PE_EmitterEditor-->PEE_blendType.setSelected( %blendTypeId, false ); From 8aebb67aa1c77c66ac7bf62b738830c9a34479c1 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Tue, 13 May 2014 22:45:25 -0500 Subject: [PATCH 049/317] conforms losmask to standard nomenclature --- Engine/source/T3D/aiPlayer.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/aiPlayer.cpp b/Engine/source/T3D/aiPlayer.cpp index 0b869c2a0..d3aed1fd4 100644 --- a/Engine/source/T3D/aiPlayer.cpp +++ b/Engine/source/T3D/aiPlayer.cpp @@ -28,7 +28,7 @@ #include "T3D/gameBase/moveManager.h" #include "console/engineAPI.h" -static U32 AIPLAYER_LOSMASK = TerrainObjectType | WaterObjectType | +static U32 sAIPlayerLoSMask = TerrainObjectType | WaterObjectType | ShapeBaseObjectType | StaticShapeObjectType | PlayerObjectType | ItemObjectType; @@ -628,7 +628,7 @@ bool AIPlayer::checkInLos(GameBase* target, bool _checkEnabled = false) } Point3F muzzlePoint; getMuzzlePointAI(0, &muzzlePoint); - bool hit = gServerContainer.castRay(muzzlePoint, target->getBoxCenter(), AIPLAYER_LOSMASK, &ri); + bool hit = gServerContainer.castRay(muzzlePoint, target->getBoxCenter(), sAIPlayerLoSMask, &ri); enableCollision(); for (S32 i = 0; i < mountCount; i++) @@ -654,7 +654,7 @@ bool AIPlayer::checkLosClear(Point3F _pos) Point3F muzzlePoint; getMuzzlePointAI(0, &muzzlePoint); - gServerContainer.castRay(muzzlePoint, _pos, AIPLAYER_LOSMASK, &ri); + gServerContainer.castRay(muzzlePoint, _pos, sAIPlayerLoSMask, &ri); bool emptySpace = bool(ri.object == NULL); enableCollision(); return emptySpace; From 10abee5e67129a95dc70eebaac72d1e776a278de Mon Sep 17 00:00:00 2001 From: Azaezel Date: Mon, 19 May 2014 10:18:46 -0500 Subject: [PATCH 050/317] Fix for issue #660 with stated preferred decal. (The more complex example of the two possible.) --- Templates/Full/game/art/datablocks/weapons/grenadefx.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Templates/Full/game/art/datablocks/weapons/grenadefx.cs b/Templates/Full/game/art/datablocks/weapons/grenadefx.cs index 473cd81b8..2db50a5bd 100644 --- a/Templates/Full/game/art/datablocks/weapons/grenadefx.cs +++ b/Templates/Full/game/art/datablocks/weapons/grenadefx.cs @@ -893,7 +893,7 @@ datablock ProjectileData(GrenadeLauncherProjectile) explosion = GrenadeLauncherExplosion; waterExplosion = GrenadeLauncherWaterExplosion; - decal = ExpBlastDecal; + decal = ScorchRXDecal; splash = GrenadeSplash; particleEmitter = GrenadeProjSmokeTrailEmitter; From 82a36d8a323e51c2e665f8fa13e17cfbbee32330 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Mon, 19 May 2014 20:23:31 +0200 Subject: [PATCH 051/317] Add support for modules and libraries to CMake. --- Tools/CMake/basics.cmake | 53 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/Tools/CMake/basics.cmake b/Tools/CMake/basics.cmake index 16e7ed27c..7c1df5d8c 100644 --- a/Tools/CMake/basics.cmake +++ b/Tools/CMake/basics.cmake @@ -73,6 +73,53 @@ macro(addLib lib) target_link_libraries(${PROJECT_NAME} "${lib}") endmacro() +# adds a library dependency. Are processed on addExecutable or addStaticLib +macro(addRequiredLibrary lib) + #message(STATUS "${PROJECT_NAME} : add lib : ${lib}") + LIST( APPEND ${PROJECT_NAME}_required_library ${lib} ) +endmacro() + +# adds a link dependency. Are processed on addExecutable or addStaticLib +macro(addRequiredLink lib) + #message(STATUS "${PROJECT_NAME} : add lib : ${lib}") + LIST( APPEND ${PROJECT_NAME}_required_link ${lib} ) +endmacro() + +macro( _processProjectLibrary ) + # Append currect project to PROJECT_STACK + LIST( APPEND PROJECT_STACK "${PROJECT_NAME}" ) + + foreach( lib ${${PROJECT_NAME}_required_library} ) + #message( "adding library dependency: ${lib}" ) + include( ${lib} ) + endforeach() + + #clear required libraries + set( ${PROJECT_NAME}_required_library ) + + # pop currect project form PROJECT_STACK + LIST(REMOVE_AT PROJECT_STACK -1) + + # get currect project form stack + if( PROJECT_STACK ) + LIST(GET PROJECT_STACK -1 TEMP_PROJECT) + project( ${TEMP_PROJECT} ) + endif() + + +endmacro() + +macro( _processProjectLinks ) + #message( "_processProjectLinks: ${PROJECT_NAME}" ) + foreach( lib ${${PROJECT_NAME}_required_link} ) + addLib( ${lib} ) + endforeach() + + #clear required libraries + set( ${PROJECT_NAME}_required_link ) +endmacro() + + # adds a path to search for libs macro(addLibPath dir) link_directories(${dir}) @@ -146,6 +193,9 @@ macro(addStaticLib) #foreach(dir ${${PROJECT_NAME}_paths}) addInclude("${firstDir}") #endforeach() + + _processProjectLinks() + _processProjectLibrary() endmacro() # macro to add an executable @@ -161,6 +211,9 @@ macro(addExecutable) add_executable("${PROJECT_NAME}" WIN32 ${${PROJECT_NAME}_files}) # omg - only use the first folder ... otehrwise we get lots of header name collisions addInclude("${firstDir}") + + _processProjectLinks() + _processProjectLibrary() endmacro() macro(setupVersionNumbers) From dca11afded13d593ff90b068cff799a14c74afe2 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Mon, 19 May 2014 22:15:22 +0200 Subject: [PATCH 052/317] Add support for CMake "required definitions" to modules and libraries. --- Tools/CMake/basics.cmake | 28 ++++++++++++++++++++++++++++ 1 file changed, 28 insertions(+) diff --git a/Tools/CMake/basics.cmake b/Tools/CMake/basics.cmake index 7c1df5d8c..7dc4a5d81 100644 --- a/Tools/CMake/basics.cmake +++ b/Tools/CMake/basics.cmake @@ -61,6 +61,32 @@ macro(addDebugDef def) set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY COMPILE_DEFINITIONS_DEBUG "${def}") endmacro() +# adds a required definition. Are processed on addExecutable or addStaticLib +macro(addRequiredDefinition def) + #message(STATUS "${PROJECT_NAME} : add def : ${def}") + LIST( APPEND ${PROJECT_NAME}_required_definition ${def} ) +endmacro() +# adds a required debug definition. Are processed on addExecutable or addStaticLib +macro(addRequiredDebugDefinition def) + #message(STATUS "${PROJECT_NAME} : add def : ${def}") + LIST( APPEND ${PROJECT_NAME}_required_debug_definition ${def} ) +endmacro() + +# add definitions to project +macro( _processProjectDefinition ) + foreach( def ${${PROJECT_NAME}_required_definition} ) + addDef( ${def} ) + endforeach() + + foreach( def ${${PROJECT_NAME}_required_debug_definition} ) + addDef( ${def} ) + endforeach() + + #clear required defs + set( ${PROJECT_NAME}_required_definition ) + set( ${PROJECT_NAME}_required_debug_definition ) +endmacro() + # adds an include path macro(addInclude incPath) #message(STATUS "${PROJECT_NAME} : add include path : ${incPath}") @@ -196,6 +222,7 @@ macro(addStaticLib) _processProjectLinks() _processProjectLibrary() + _processProjectDefinition() endmacro() # macro to add an executable @@ -214,6 +241,7 @@ macro(addExecutable) _processProjectLinks() _processProjectLibrary() + _processProjectDefinition() endmacro() macro(setupVersionNumbers) From 9ba72e13d479a392613899d89b8cf5d54e919805 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Mon, 19 May 2014 22:15:44 +0200 Subject: [PATCH 053/317] CMake Navigation module. --- Tools/CMake/libraries/library_recast.cmake | 18 ++++++++++++++++++ Tools/CMake/modules/module_navigation.cmake | 15 +++++++++++++++ Tools/CMake/torque3d.cmake | 6 ++++++ 3 files changed, 39 insertions(+) create mode 100644 Tools/CMake/libraries/library_recast.cmake create mode 100644 Tools/CMake/modules/module_navigation.cmake diff --git a/Tools/CMake/libraries/library_recast.cmake b/Tools/CMake/libraries/library_recast.cmake new file mode 100644 index 000000000..677459606 --- /dev/null +++ b/Tools/CMake/libraries/library_recast.cmake @@ -0,0 +1,18 @@ +# Recast library +project(recast) + +# Source +addPathRec( "${libDir}/recast/DebugUtils/Source" ) +addPathRec( "${libDir}/recast/Recast/Source" ) +addPathRec( "${libDir}/recast/Detour/Source" ) +addPathRec( "${libDir}/recast/DetourCrowd/Source" ) +addPathRec( "${libDir}/recast/DetourTileCache/Source" ) + +# Additional includes +include_directories( "${libDir}/recast/DebugUtils/Include" ) +include_directories( "${libDir}/recast/Recast/Include" ) +include_directories( "${libDir}/recast/Detour/Include" ) +include_directories( "${libDir}/recast/DetourTileCache/Include" ) +include_directories( "${libDir}/recast/DetourCrowd/Include" ) + +addStaticLib() \ No newline at end of file diff --git a/Tools/CMake/modules/module_navigation.cmake b/Tools/CMake/modules/module_navigation.cmake new file mode 100644 index 000000000..57c92ae77 --- /dev/null +++ b/Tools/CMake/modules/module_navigation.cmake @@ -0,0 +1,15 @@ +# Navigation module + +addRequiredDefinition( "TORQUE_NAVIGATION_ENABLED" ) +addRequiredLibrary( "libraries/library_recast.cmake" ) + +# files +addPathRec( "${srcDir}/navigation" ) + +# include paths +include_directories( "${libDir}/recast/DebugUtils/Include" ) +include_directories( "${libDir}/recast/Recast/Include" ) +include_directories( "${libDir}/recast/Detour/Include" ) +include_directories( "${libDir}/recast/DetourTileCache/Include" ) +include_directories( "${libDir}/recast/DetourCrowd/Include" ) + diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 06845ee22..72a876f06 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -21,6 +21,8 @@ option(TORQUE_HIFI "HIFI? support" OFF) mark_as_advanced(TORQUE_HIFI) option(TORQUE_EXTENDED_MOVE "Extended move support" OFF) mark_as_advanced(TORQUE_EXTENDED_MOVE) +option(TORQUE_NAVIGATION "Enable Navigation module" OFF) +#mark_as_advanced(TORQUE_NAVIGATION) ############################################################################### # options @@ -224,6 +226,10 @@ else() addPath("${srcDir}/T3D/gameBase/std") endif() +if(TORQUE_NAVIGATION) + include( "modules/module_navigation.cmake" ) +endif() + ############################################################################### # platform specific things ############################################################################### From 80f31c0068ba56336d86806d9000619ae40a6f6f Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Mon, 19 May 2014 22:43:48 +0200 Subject: [PATCH 054/317] CMake Oculus VR module. --- Tools/CMake/modules/module_oculusVR.cmake | 15 +++++++++++++++ Tools/CMake/torque3d.cmake | 13 +++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 Tools/CMake/modules/module_oculusVR.cmake diff --git a/Tools/CMake/modules/module_oculusVR.cmake b/Tools/CMake/modules/module_oculusVR.cmake new file mode 100644 index 000000000..044154f7c --- /dev/null +++ b/Tools/CMake/modules/module_oculusVR.cmake @@ -0,0 +1,15 @@ +# module OculusVR + +# Source +addPathRec( "${srcDir}/platform/input/oculusVR" ) + +# Includes +include_directories( "${TORQUE_OCULUSVR_SDK_PATH}/LibOVR/Include" ) +include_directories( "${TORQUE_OCULUSVR_SDK_PATH}/LibOVR/Src" ) + +# Libs +if( WIN32 ) + link_directories( "${TORQUE_OCULUSVR_SDK_PATH}/LibOVR/Lib/Win32" ) + addRequiredLink( "libovr.lib" ) + addRequiredLink( "libovrd.lib" ) +endif() \ No newline at end of file diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 72a876f06..4b8cb3ffa 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -24,6 +24,15 @@ mark_as_advanced(TORQUE_EXTENDED_MOVE) option(TORQUE_NAVIGATION "Enable Navigation module" OFF) #mark_as_advanced(TORQUE_NAVIGATION) +#Oculus VR +option(TORQUE_OCULUSVR "Enable OCULUSVR module" OFF) +mark_as_advanced(TORQUE_OCULUSVR) +if(TORQUE_OCULUSVR) + set(TORQUE_OCULUSVR_SDK_PATH "" CACHE PATH "OCULUSVR library path" FORCE) +else() # hide variable + set(TORQUE_OCULUSVR_SDK_PATH "" CACHE INTERNAL "" FORCE) +endif() + ############################################################################### # options ############################################################################### @@ -230,6 +239,10 @@ if(TORQUE_NAVIGATION) include( "modules/module_navigation.cmake" ) endif() +if(TORQUE_OCULUSVR) + include( "modules/module_oculusVR.cmake" ) +endif() + ############################################################################### # platform specific things ############################################################################### From 2f7e5bfb7356b14d4152b366ea1a06021e95aae3 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Mon, 19 May 2014 22:55:38 +0200 Subject: [PATCH 055/317] CMake Razor Hydra module. --- Tools/CMake/modules/module_hydra.cmake | 19 +++++++++++++++++++ Tools/CMake/torque3d.cmake | 13 +++++++++++++ 2 files changed, 32 insertions(+) create mode 100644 Tools/CMake/modules/module_hydra.cmake diff --git a/Tools/CMake/modules/module_hydra.cmake b/Tools/CMake/modules/module_hydra.cmake new file mode 100644 index 000000000..a3139e6c2 --- /dev/null +++ b/Tools/CMake/modules/module_hydra.cmake @@ -0,0 +1,19 @@ +# module OculusVR + +# Source +addPathRec( "${srcDir}/platform/input/razerHydra" ) + +# Includes +include_directories( "${TORQUE_RAZERHYDRA_SDK_PATH}/include" ) + +# Install +if( WIN32 ) + # File Copy for Release + INSTALL(FILES "${TORQUE_RAZERHYDRA_SDK_PATH}/bin/win32/release_dll/sixense.dll" DESTINATION "${projectOutDir}") + + # File Copy for Debug + INSTALL(FILES "${TORQUE_RAZERHYDRA_SDK_PATH}/bin/win32/debug_dll/sixensed.dll" DESTINATION "${projectOutDir}" CONFIGURATIONS "Debug" ) + # Only needed by the debug sixense library + INSTALL(FILES "${TORQUE_RAZERHYDRA_SDK_PATH}/samples/win32/sixense_simple3d/DeviceDLL.dll" DESTINATION "${projectOutDir}" CONFIGURATIONS "Debug" ) +endif() + diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 4b8cb3ffa..65ff16a4a 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -33,6 +33,15 @@ else() # hide variable set(TORQUE_OCULUSVR_SDK_PATH "" CACHE INTERNAL "" FORCE) endif() +#Hydra +option(TORQUE_HYDRA "Enable HYDRA module" OFF) +mark_as_advanced(TORQUE_HYDRA) +if(TORQUE_HYDRA) + set(TORQUE_HYDRA_SDK_PATH "" CACHE PATH "HYDRA library path" FORCE) +else() # hide variable + set(TORQUE_HYDRA_SDK_PATH "" CACHE INTERNAL "" FORCE) +endif() + ############################################################################### # options ############################################################################### @@ -243,6 +252,10 @@ if(TORQUE_OCULUSVR) include( "modules/module_oculusVR.cmake" ) endif() +if(TORQUE_HYDRA) + include( "modules/module_hydra.cmake" ) +endif() + ############################################################################### # platform specific things ############################################################################### From 7240c121fc7edce45696aa79e2290758d9b40c21 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Tue, 20 May 2014 00:12:31 +0200 Subject: [PATCH 056/317] Fix CMake Navigation module. --- Tools/CMake/modules/module_navigation.cmake | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/CMake/modules/module_navigation.cmake b/Tools/CMake/modules/module_navigation.cmake index 57c92ae77..512ed42f3 100644 --- a/Tools/CMake/modules/module_navigation.cmake +++ b/Tools/CMake/modules/module_navigation.cmake @@ -2,6 +2,7 @@ addRequiredDefinition( "TORQUE_NAVIGATION_ENABLED" ) addRequiredLibrary( "libraries/library_recast.cmake" ) +addRequiredLink( "recast" ) # files addPathRec( "${srcDir}/navigation" ) From 62fc2a7e022ccfebd43f39bf936e7efa4afece3a Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Tue, 20 May 2014 23:24:08 +0200 Subject: [PATCH 057/317] Small fixes for CMake modules/libraries. --- Tools/CMake/basics.cmake | 2 +- Tools/CMake/torque3d.cmake | 16 ++++++++++------ 2 files changed, 11 insertions(+), 7 deletions(-) diff --git a/Tools/CMake/basics.cmake b/Tools/CMake/basics.cmake index 7dc4a5d81..aa829fafa 100644 --- a/Tools/CMake/basics.cmake +++ b/Tools/CMake/basics.cmake @@ -79,7 +79,7 @@ macro( _processProjectDefinition ) endforeach() foreach( def ${${PROJECT_NAME}_required_debug_definition} ) - addDef( ${def} ) + addDebugDef( ${def} ) endforeach() #clear required defs diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 65ff16a4a..0f8b99435 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -480,10 +480,14 @@ endif() ############################################################################### # Installation ############################################################################### -INSTALL(DIRECTORY "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/game" DESTINATION "${projectDir}") -if(WIN32) - INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/cleanShaders.bat" DESTINATION "${projectDir}") - INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/DeleteCachedDTSs.bat" DESTINATION "${projectDir}") - INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/DeleteDSOs.bat" DESTINATION "${projectDir}") - INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/DeletePrefs.bat" DESTINATION "${projectDir}") + +if(TORQUE_TEMPLATE) + message("Prepare Template(${TORQUE_TEMPLATE}) install...") + INSTALL(DIRECTORY "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/game" DESTINATION "${projectDir}") + if(WIN32) + INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/cleanShaders.bat" DESTINATION "${projectDir}") + INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/DeleteCachedDTSs.bat" DESTINATION "${projectDir}") + INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/DeleteDSOs.bat" DESTINATION "${projectDir}") + INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/DeletePrefs.bat" DESTINATION "${projectDir}") + endif() endif() From b991adb03392c27d82d82008a102b65715bdfdb1 Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Wed, 21 May 2014 12:27:40 +0100 Subject: [PATCH 058/317] Insert TS function var names in the precompile step, allowing unreferenced parameters to be used --- Engine/source/console/astNodes.cpp | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Engine/source/console/astNodes.cpp b/Engine/source/console/astNodes.cpp index 11cc44f90..0a8a9694a 100644 --- a/Engine/source/console/astNodes.cpp +++ b/Engine/source/console/astNodes.cpp @@ -1831,7 +1831,10 @@ U32 FunctionDeclStmtNode::precompileStmt(U32) argc = 0; for(VarNode *walk = args; walk; walk = (VarNode *)((StmtNode*)walk)->getNext()) + { + precompileIdent(walk->varName); argc++; + } CodeBlock::smInFunction = true; From eb74525a38ede6909add08bd98340bc8c0b5826c Mon Sep 17 00:00:00 2001 From: Andrew Mac Date: Wed, 21 May 2014 14:50:44 -0300 Subject: [PATCH 059/317] Added support for large lists of shape formats. Needed for assimp. --- .../source/gui/editor/guiInspectorTypes.cpp | 3 +- .../source/ts/collada/colladaShapeLoader.cpp | 9 +++ Engine/source/ts/loader/tsShapeLoader.cpp | 61 +++++++++++++++++++ Engine/source/ts/loader/tsShapeLoader.h | 13 ++++ .../Empty/game/tools/gui/openFileDialog.ed.cs | 26 ++++++++ .../shapeEditor/scripts/shapeEditor.ed.cs | 10 +-- .../worldEditor/scripts/editors/creator.ed.cs | 8 +-- .../Full/game/tools/gui/openFileDialog.ed.cs | 26 ++++++++ .../shapeEditor/scripts/shapeEditor.ed.cs | 10 +-- .../worldEditor/scripts/editors/creator.ed.cs | 8 +-- 10 files changed, 154 insertions(+), 20 deletions(-) diff --git a/Engine/source/gui/editor/guiInspectorTypes.cpp b/Engine/source/gui/editor/guiInspectorTypes.cpp index 979028c7c..dfa9c5a15 100644 --- a/Engine/source/gui/editor/guiInspectorTypes.cpp +++ b/Engine/source/gui/editor/guiInspectorTypes.cpp @@ -764,8 +764,7 @@ GuiControl* GuiInspectorTypeShapeFilename::constructEditControl() // Change filespec char szBuffer[512]; - dSprintf( szBuffer, sizeof(szBuffer), "getLoadFilename(\"%s\", \"%d.apply\", %d.getData());", - "DTS Files (*.dts)|*.dts|COLLADA Files (*.dae)|*.dae|(All Files (*.*)|*.*|", getId(), getId() ); + dSprintf( szBuffer, sizeof(szBuffer), "getLoadFormatFilename(\"%d.apply\", %d.getData());", getId(), getId() ); mBrowseButton->setField( "Command", szBuffer ); // Create "Open in ShapeEditor" button diff --git a/Engine/source/ts/collada/colladaShapeLoader.cpp b/Engine/source/ts/collada/colladaShapeLoader.cpp index 3822e9e01..2db3a09ef 100644 --- a/Engine/source/ts/collada/colladaShapeLoader.cpp +++ b/Engine/source/ts/collada/colladaShapeLoader.cpp @@ -50,6 +50,15 @@ #include "core/util/zip/zipVolume.h" #include "gfx/bitmap/gBitmap.h" +MODULE_BEGIN( ColladaShapeLoader ) + MODULE_INIT_AFTER( ShapeLoader ) + MODULE_INIT + { + TSShapeLoader::addFormat("Collada", "dae"); + TSShapeLoader::addFormat("Google Earth", "kmz"); + } +MODULE_END; + // static DAE sDAE; // Collada model database (holds the last loaded file) static Torque::Path sLastPath; // Path of the last loaded Collada file diff --git a/Engine/source/ts/loader/tsShapeLoader.cpp b/Engine/source/ts/loader/tsShapeLoader.cpp index 5170867df..bf1540e69 100644 --- a/Engine/source/ts/loader/tsShapeLoader.cpp +++ b/Engine/source/ts/loader/tsShapeLoader.cpp @@ -21,6 +21,7 @@ //----------------------------------------------------------------------------- #include "platform/platform.h" +#include "console/engineAPI.h" #include "ts/loader/tsShapeLoader.h" #include "core/volume.h" @@ -30,6 +31,14 @@ #include "ts/tsShapeInstance.h" #include "ts/tsMaterialList.h" +MODULE_BEGIN( ShapeLoader ) + MODULE_INIT_AFTER( GFX ) + MODULE_INIT + { + TSShapeLoader::addFormat("Torque DTS", "dts"); + TSShapeLoader::addFormat("Torque DSQ", "dsq"); + } +MODULE_END; const F32 TSShapeLoader::DefaultTime = -1.0f; const double TSShapeLoader::MinFrameRate = 15.0f; @@ -37,6 +46,8 @@ const double TSShapeLoader::MaxFrameRate = 60.0f; const double TSShapeLoader::AppGroundFrameRate = 10.0f; Torque::Path TSShapeLoader::shapePath; +Vector TSShapeLoader::smFormats; + //------------------------------------------------------------------------------ // Utility functions @@ -1270,3 +1281,53 @@ TSShapeLoader::~TSShapeLoader() delete appSequences[iSeq]; appSequences.clear(); } + +// Static functions to handle supported formats for shape loader. +void TSShapeLoader::addFormat(String name, String extension) +{ + ShapeFormat newFormat; + newFormat.mName = name; + newFormat.mExtension = extension; + smFormats.push_back(newFormat); +} + +String TSShapeLoader::getFormatExtensions() +{ + // "*.dsq TAB *.dae TAB + StringBuilder output; + for(U32 n = 0; n < smFormats.size(); ++n) + { + output.append("*."); + output.append(smFormats[n].mExtension); + output.append("\t"); + } + return output.end(); +} + +String TSShapeLoader::getFormatFilters() +{ + // "DSQ Files|*.dsq|COLLADA Files|*.dae|" + StringBuilder output; + for(U32 n = 0; n < smFormats.size(); ++n) + { + output.append(smFormats[n].mName); + output.append("|*."); + output.append(smFormats[n].mExtension); + output.append("|"); + } + return output.end(); +} + +DefineConsoleFunction( getFormatExtensions, const char*, ( ),, + "Returns a list of supported shape format extensions separated by tabs." + "Example output: *.dsq TAB *.dae TAB") +{ + return Con::getReturnBuffer(TSShapeLoader::getFormatExtensions()); +} + +DefineConsoleFunction( getFormatFilters, const char*, ( ),, + "Returns a list of supported shape formats in filter form.\n" + "Example output: DSQ Files|*.dsq|COLLADA Files|*.dae|") +{ + return Con::getReturnBuffer(TSShapeLoader::getFormatFilters()); +} \ No newline at end of file diff --git a/Engine/source/ts/loader/tsShapeLoader.h b/Engine/source/ts/loader/tsShapeLoader.h index 83e9db0ca..42dafc51c 100644 --- a/Engine/source/ts/loader/tsShapeLoader.h +++ b/Engine/source/ts/loader/tsShapeLoader.h @@ -45,6 +45,19 @@ class TSShapeLoader { +// Supported Format List +protected: + struct ShapeFormat + { + String mName; + String mExtension; + }; + static Vector smFormats; +public: + static void addFormat(String name, String extension); + static String getFormatExtensions(); + static String getFormatFilters(); + public: enum eLoadPhases { diff --git a/Templates/Empty/game/tools/gui/openFileDialog.ed.cs b/Templates/Empty/game/tools/gui/openFileDialog.ed.cs index 00a388789..b988d3361 100644 --- a/Templates/Empty/game/tools/gui/openFileDialog.ed.cs +++ b/Templates/Empty/game/tools/gui/openFileDialog.ed.cs @@ -42,3 +42,29 @@ function getLoadFilename(%filespec, %callback, %currentFile) %dlg.delete(); } + +// Opens a choose file dialog with format filters already loaded +// in. This avoids the issue of passing a massive list of format +// filters into a function as an arguement. +function getLoadFormatFilename(%callback, %currentFile) +{ + %dlg = new OpenFileDialog() + { + Filters = getFormatFilters() @ "(All Files (*.*)|*.*|"; + DefaultFile = %currentFile; + ChangePath = false; + MustExist = true; + MultipleFiles = false; + }; + + if ( filePath( %currentFile ) !$= "" ) + %dlg.DefaultPath = filePath(%currentFile); + + if ( %dlg.Execute() ) + { + eval(%callback @ "(\"" @ %dlg.FileName @ "\");"); + $Tools::FileDialogs::LastFilePath = filePath( %dlg.FileName ); + } + + %dlg.delete(); +} diff --git a/Templates/Empty/game/tools/shapeEditor/scripts/shapeEditor.ed.cs b/Templates/Empty/game/tools/shapeEditor/scripts/shapeEditor.ed.cs index 5eb26f3c0..6282f32ce 100644 --- a/Templates/Empty/game/tools/shapeEditor/scripts/shapeEditor.ed.cs +++ b/Templates/Empty/game/tools/shapeEditor/scripts/shapeEditor.ed.cs @@ -366,7 +366,7 @@ function ShapeEdSelectWindow::navigate( %this, %address ) %this-->shapeLibrary.clear(); ShapeEdSelectMenu.clear(); - %filePatterns = "*.dts" TAB "*.dae" TAB "*.kmz"; + %filePatterns = getFormatExtensions(); %fullPath = findFirstFileMultiExpr( %filePatterns ); while ( %fullPath !$= "" ) @@ -1632,7 +1632,7 @@ function ShapeEdSequences::onAddSequence( %this, %name ) if ( %from $= "" ) { // No sequence selected => open dialog to browse for one - getLoadFilename( "DSQ Files|*.dsq|COLLADA Files|*.dae|Google Earth Files|*.kmz", %this @ ".onAddSequenceFromBrowse", ShapeEdFromMenu.lastPath ); + getLoadFormatFilename( %this @ ".onAddSequenceFromBrowse", ShapeEdFromMenu.lastPath ); return; } else @@ -1740,7 +1740,7 @@ function ShapeEdSeqFromMenu::onSelect( %this, %id, %text ) %this.setText( %seqFrom ); // Allow the user to browse for an external source of animation data - getLoadFilename( "DSQ Files|*.dsq|COLLADA Files|*.dae|Google Earth Files|*.kmz", %this @ ".onBrowseSelect", %this.lastPath ); + getLoadFormatFilename( %this @ ".onBrowseSelect", %this.lastPath ); } else { @@ -2862,7 +2862,7 @@ function ShapeEdDetails::onAddMeshFromFile( %this, %path ) { if ( %path $= "" ) { - getLoadFilename( "DTS Files|*.dts|COLLADA Files|*.dae|Google Earth Files|*.kmz", %this @ ".onAddMeshFromFile", %this.lastPath ); + getLoadFormatFilename( %this @ ".onAddMeshFromFile", %this.lastPath ); return; } @@ -3291,7 +3291,7 @@ function ShapeEdMountShapeMenu::onSelect( %this, %id, %text ) if ( %text $= "Browse..." ) { // Allow the user to browse for an external model file - getLoadFilename( "DTS Files|*.dts|COLLADA Files|*.dae|Google Earth Files|*.kmz", %this @ ".onBrowseSelect", %this.lastPath ); + getLoadFormatFilename( %this @ ".onBrowseSelect", %this.lastPath ); } else { diff --git a/Templates/Empty/game/tools/worldEditor/scripts/editors/creator.ed.cs b/Templates/Empty/game/tools/worldEditor/scripts/editors/creator.ed.cs index 2e0a3165c..d63542d67 100644 --- a/Templates/Empty/game/tools/worldEditor/scripts/editors/creator.ed.cs +++ b/Templates/Empty/game/tools/worldEditor/scripts/editors/creator.ed.cs @@ -324,13 +324,13 @@ function EWCreatorWindow::navigate( %this, %address ) if ( %this.tab $= "Meshes" ) { - %fullPath = findFirstFileMultiExpr( "*.dts" TAB "*.dae" TAB "*.kmz" TAB "*.dif" ); + %fullPath = findFirstFileMultiExpr( getFormatExtensions() ); while ( %fullPath !$= "" ) { if (strstr(%fullPath, "cached.dts") != -1) { - %fullPath = findNextFileMultiExpr( "*.dts" TAB "*.dae" TAB "*.kmz" TAB "*.dif" ); + %fullPath = findNextFileMultiExpr( getFormatExtensions() ); continue; } @@ -338,7 +338,7 @@ function EWCreatorWindow::navigate( %this, %address ) %splitPath = strreplace( %fullPath, "/", " " ); if( getWord(%splitPath, 0) $= "tools" ) { - %fullPath = findNextFileMultiExpr( "*.dts" TAB "*.dae" TAB "*.kmz" TAB "*.dif" ); + %fullPath = findNextFileMultiExpr( getFormatExtensions() ); continue; } @@ -396,7 +396,7 @@ function EWCreatorWindow::navigate( %this, %address ) } } - %fullPath = findNextFileMultiExpr( "*.dts" TAB "*.dae" TAB "*.kmz" TAB "*.dif" ); + %fullPath = findNextFileMultiExpr( getFormatExtensions() ); } } diff --git a/Templates/Full/game/tools/gui/openFileDialog.ed.cs b/Templates/Full/game/tools/gui/openFileDialog.ed.cs index 00a388789..b988d3361 100644 --- a/Templates/Full/game/tools/gui/openFileDialog.ed.cs +++ b/Templates/Full/game/tools/gui/openFileDialog.ed.cs @@ -42,3 +42,29 @@ function getLoadFilename(%filespec, %callback, %currentFile) %dlg.delete(); } + +// Opens a choose file dialog with format filters already loaded +// in. This avoids the issue of passing a massive list of format +// filters into a function as an arguement. +function getLoadFormatFilename(%callback, %currentFile) +{ + %dlg = new OpenFileDialog() + { + Filters = getFormatFilters() @ "(All Files (*.*)|*.*|"; + DefaultFile = %currentFile; + ChangePath = false; + MustExist = true; + MultipleFiles = false; + }; + + if ( filePath( %currentFile ) !$= "" ) + %dlg.DefaultPath = filePath(%currentFile); + + if ( %dlg.Execute() ) + { + eval(%callback @ "(\"" @ %dlg.FileName @ "\");"); + $Tools::FileDialogs::LastFilePath = filePath( %dlg.FileName ); + } + + %dlg.delete(); +} diff --git a/Templates/Full/game/tools/shapeEditor/scripts/shapeEditor.ed.cs b/Templates/Full/game/tools/shapeEditor/scripts/shapeEditor.ed.cs index 5eb26f3c0..6282f32ce 100644 --- a/Templates/Full/game/tools/shapeEditor/scripts/shapeEditor.ed.cs +++ b/Templates/Full/game/tools/shapeEditor/scripts/shapeEditor.ed.cs @@ -366,7 +366,7 @@ function ShapeEdSelectWindow::navigate( %this, %address ) %this-->shapeLibrary.clear(); ShapeEdSelectMenu.clear(); - %filePatterns = "*.dts" TAB "*.dae" TAB "*.kmz"; + %filePatterns = getFormatExtensions(); %fullPath = findFirstFileMultiExpr( %filePatterns ); while ( %fullPath !$= "" ) @@ -1632,7 +1632,7 @@ function ShapeEdSequences::onAddSequence( %this, %name ) if ( %from $= "" ) { // No sequence selected => open dialog to browse for one - getLoadFilename( "DSQ Files|*.dsq|COLLADA Files|*.dae|Google Earth Files|*.kmz", %this @ ".onAddSequenceFromBrowse", ShapeEdFromMenu.lastPath ); + getLoadFormatFilename( %this @ ".onAddSequenceFromBrowse", ShapeEdFromMenu.lastPath ); return; } else @@ -1740,7 +1740,7 @@ function ShapeEdSeqFromMenu::onSelect( %this, %id, %text ) %this.setText( %seqFrom ); // Allow the user to browse for an external source of animation data - getLoadFilename( "DSQ Files|*.dsq|COLLADA Files|*.dae|Google Earth Files|*.kmz", %this @ ".onBrowseSelect", %this.lastPath ); + getLoadFormatFilename( %this @ ".onBrowseSelect", %this.lastPath ); } else { @@ -2862,7 +2862,7 @@ function ShapeEdDetails::onAddMeshFromFile( %this, %path ) { if ( %path $= "" ) { - getLoadFilename( "DTS Files|*.dts|COLLADA Files|*.dae|Google Earth Files|*.kmz", %this @ ".onAddMeshFromFile", %this.lastPath ); + getLoadFormatFilename( %this @ ".onAddMeshFromFile", %this.lastPath ); return; } @@ -3291,7 +3291,7 @@ function ShapeEdMountShapeMenu::onSelect( %this, %id, %text ) if ( %text $= "Browse..." ) { // Allow the user to browse for an external model file - getLoadFilename( "DTS Files|*.dts|COLLADA Files|*.dae|Google Earth Files|*.kmz", %this @ ".onBrowseSelect", %this.lastPath ); + getLoadFormatFilename( %this @ ".onBrowseSelect", %this.lastPath ); } else { diff --git a/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs b/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs index 2e0a3165c..d63542d67 100644 --- a/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs +++ b/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs @@ -324,13 +324,13 @@ function EWCreatorWindow::navigate( %this, %address ) if ( %this.tab $= "Meshes" ) { - %fullPath = findFirstFileMultiExpr( "*.dts" TAB "*.dae" TAB "*.kmz" TAB "*.dif" ); + %fullPath = findFirstFileMultiExpr( getFormatExtensions() ); while ( %fullPath !$= "" ) { if (strstr(%fullPath, "cached.dts") != -1) { - %fullPath = findNextFileMultiExpr( "*.dts" TAB "*.dae" TAB "*.kmz" TAB "*.dif" ); + %fullPath = findNextFileMultiExpr( getFormatExtensions() ); continue; } @@ -338,7 +338,7 @@ function EWCreatorWindow::navigate( %this, %address ) %splitPath = strreplace( %fullPath, "/", " " ); if( getWord(%splitPath, 0) $= "tools" ) { - %fullPath = findNextFileMultiExpr( "*.dts" TAB "*.dae" TAB "*.kmz" TAB "*.dif" ); + %fullPath = findNextFileMultiExpr( getFormatExtensions() ); continue; } @@ -396,7 +396,7 @@ function EWCreatorWindow::navigate( %this, %address ) } } - %fullPath = findNextFileMultiExpr( "*.dts" TAB "*.dae" TAB "*.kmz" TAB "*.dif" ); + %fullPath = findNextFileMultiExpr( getFormatExtensions() ); } } From b07b971f0fe1bf10b70d60dc66e541495f78b502 Mon Sep 17 00:00:00 2001 From: Thomas Fischer Date: Sat, 24 May 2014 15:01:46 +0200 Subject: [PATCH 060/317] fixed cmake compilation on linux --- Tools/CMake/torque3d.cmake | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 0f8b99435..1e9fb9bf6 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -84,14 +84,14 @@ mark_as_advanced(TORQUE_DEBUG_GFX_MODE) #option(DEBUG_SPEW "more debug" OFF) set(TORQUE_NO_DSO_GENERATION ON) -# warning C4800: 'XXX' : forcing value to bool 'true' or 'false' (performance warning) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4800") -# warning C4018: '<' : signed/unsigned mismatch -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4018") -# warning C4244: 'initializing' : conversion from 'XXX' to 'XXX', possible loss of data -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4244") - if(WIN32) + # warning C4800: 'XXX' : forcing value to bool 'true' or 'false' (performance warning) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4800") + # warning C4018: '<' : signed/unsigned mismatch + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4018") + # warning C4244: 'initializing' : conversion from 'XXX' to 'XXX', possible loss of data + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4244") + link_directories($ENV{DXSDK_DIR}/Lib/x86) endif() From 18ba0646c0fcf707ed897729902b4af4ea3bccf3 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 25 May 2014 16:50:19 +0200 Subject: [PATCH 061/317] Increased stability Torqu3D: unit-tests running without a crash. See the console.log after ran unitTest_runTests( "", true ). @signmotion --- Engine/source/T3D/debris.cpp | 13 ++++++++-- Engine/source/T3D/fx/explosion.cpp | 7 ++++++ Engine/source/T3D/fx/splash.cpp | 14 +++++++++-- Engine/source/T3D/physics/physicsDebris.cpp | 9 ++++++- Engine/source/T3D/projectile.cpp | 7 ++++++ Engine/source/T3D/vehicles/vehicleBlocker.cpp | 6 +++++ .../core/util/journal/test/testJournal.cpp | 12 +++++++++ .../core/util/journal/test/testProcess.cpp | 2 ++ .../gui/editor/inspector/dynamicGroup.cpp | 3 +++ Engine/source/gui/editor/inspector/field.cpp | 2 +- Engine/source/gui/editor/inspector/group.cpp | 2 +- .../gui/editor/inspector/variableField.cpp | 6 +++++ Engine/source/platform/test/testNet.cpp | 10 ++++++-- Engine/source/scene/sceneManager.cpp | 4 ++- Engine/source/sfx/sfxSource.h | 2 +- Engine/source/sfx/sfxSystem.cpp | 3 ++- Engine/source/unit/test.cpp | 5 ++++ Engine/source/unit/tests/testComponents.cpp | 25 +++++++++++++------ 18 files changed, 113 insertions(+), 19 deletions(-) diff --git a/Engine/source/T3D/debris.cpp b/Engine/source/T3D/debris.cpp index 78526b7ad..c7b5fc42b 100644 --- a/Engine/source/T3D/debris.cpp +++ b/Engine/source/T3D/debris.cpp @@ -511,6 +511,12 @@ bool Debris::onAdd() return false; } + if( !mDataBlock ) + { + Con::errorf("Debris::onAdd - Fail - No datablock"); + return false; + } + // create emitters for( int i=0; iremoveObjectFromScene(this); - getContainer()->removeObject(this); + if( getSceneManager() ) + getSceneManager()->removeObjectFromScene(this); + + if( getContainer() ) + getContainer()->removeObject(this); Parent::onRemove(); } diff --git a/Engine/source/T3D/fx/explosion.cpp b/Engine/source/T3D/fx/explosion.cpp index b84efad3f..5e06d738d 100644 --- a/Engine/source/T3D/fx/explosion.cpp +++ b/Engine/source/T3D/fx/explosion.cpp @@ -771,6 +771,7 @@ bool ExplosionData::preload(bool server, String &errorStr) //-------------------------------------- // Explosion::Explosion() + : mDataBlock( NULL ) { mTypeMask |= ExplosionObjectType | LightObjectType; @@ -831,6 +832,12 @@ bool Explosion::onAdd() if ( !conn || !Parent::onAdd() ) return false; + if( !mDataBlock ) + { + Con::errorf("Explosion::onAdd - Fail - No datablok"); + return false; + } + mDelayMS = mDataBlock->delayMS + sgRandom.randI( -mDataBlock->delayVariance, mDataBlock->delayVariance ); mEndingMS = mDataBlock->lifetimeMS + sgRandom.randI( -mDataBlock->lifetimeVariance, mDataBlock->lifetimeVariance ); diff --git a/Engine/source/T3D/fx/splash.cpp b/Engine/source/T3D/fx/splash.cpp index 7a3eda0f9..1558f2dbc 100644 --- a/Engine/source/T3D/fx/splash.cpp +++ b/Engine/source/T3D/fx/splash.cpp @@ -303,6 +303,7 @@ bool SplashData::preload(bool server, String &errorStr) // Splash //-------------------------------------------------------------------------- Splash::Splash() + : mDataBlock( NULL ) { dMemset( mEmitterList, 0, sizeof( mEmitterList ) ); @@ -353,6 +354,12 @@ bool Splash::onAdd() if(!conn || !Parent::onAdd()) return false; + if( !mDataBlock ) + { + Con::errorf("Splash::onAdd - Fail - No datablock"); + return false; + } + mDelayMS = mDataBlock->delayMS + sgRandom.randI( -mDataBlock->delayVariance, mDataBlock->delayVariance ); mEndingMS = mDataBlock->lifetimeMS + sgRandom.randI( -mDataBlock->lifetimeVariance, mDataBlock->lifetimeVariance ); @@ -408,8 +415,11 @@ void Splash::onRemove() ringList.clear(); - getSceneManager()->removeObjectFromScene(this); - getContainer()->removeObject(this); + if( getSceneManager() ) + getSceneManager()->removeObjectFromScene(this); + + if( getContainer() ) + getContainer()->removeObject(this); Parent::onRemove(); } diff --git a/Engine/source/T3D/physics/physicsDebris.cpp b/Engine/source/T3D/physics/physicsDebris.cpp index 38549980a..ba9c7e4b6 100644 --- a/Engine/source/T3D/physics/physicsDebris.cpp +++ b/Engine/source/T3D/physics/physicsDebris.cpp @@ -311,7 +311,8 @@ PhysicsDebris* PhysicsDebris::create( PhysicsDebrisData *datablock, } PhysicsDebris::PhysicsDebris() - : mLifetime( 0.0f ), + : mDataBlock( NULL ), + mLifetime( 0.0f ), mShapeInstance( NULL ), mWorld( NULL ), mInitialLinVel( Point3F::Zero ) @@ -342,6 +343,12 @@ bool PhysicsDebris::onAdd() if ( !Parent::onAdd() ) return false; + if( !mDataBlock ) + { + Con::errorf("PhysicsDebris::onAdd - Fail - No datablock"); + return false; + } + // If it has a fixed lifetime then calculate it. if ( mDataBlock->lifetime > 0.0f ) { diff --git a/Engine/source/T3D/projectile.cpp b/Engine/source/T3D/projectile.cpp index 86886c2b5..ccf9a7505 100644 --- a/Engine/source/T3D/projectile.cpp +++ b/Engine/source/T3D/projectile.cpp @@ -550,6 +550,7 @@ S32 ProjectileData::scaleValue( S32 value, bool down ) // Projectile::Projectile() : mPhysicsWorld( NULL ), + mDataBlock( NULL ), mCurrPosition( 0, 0, 0 ), mCurrVelocity( 0, 0, 1 ), mSourceObjectId( -1 ), @@ -697,6 +698,12 @@ bool Projectile::onAdd() if(!Parent::onAdd()) return false; + if( !mDataBlock ) + { + Con::errorf("Projectile::onAdd - Fail - Not datablock"); + return false; + } + if (isServerObject()) { ShapeBase* ptr; diff --git a/Engine/source/T3D/vehicles/vehicleBlocker.cpp b/Engine/source/T3D/vehicles/vehicleBlocker.cpp index e85d44ff9..a31969d1b 100644 --- a/Engine/source/T3D/vehicles/vehicleBlocker.cpp +++ b/Engine/source/T3D/vehicles/vehicleBlocker.cpp @@ -71,6 +71,12 @@ bool VehicleBlocker::onAdd() mObjBox.minExtents.set(-mDimensions.x, -mDimensions.y, 0); mObjBox.maxExtents.set( mDimensions.x, mDimensions.y, mDimensions.z); + if( !mObjBox.isValidBox() ) + { + Con::errorf("VehicleBlocker::onAdd - Fail - No valid object box"); + return false; + } + resetWorldBox(); setRenderTransform(mObjToWorld); diff --git a/Engine/source/core/util/journal/test/testJournal.cpp b/Engine/source/core/util/journal/test/testJournal.cpp index 404f7637e..9bf13fbf2 100644 --- a/Engine/source/core/util/journal/test/testJournal.cpp +++ b/Engine/source/core/util/journal/test/testJournal.cpp @@ -48,6 +48,12 @@ CreateUnitTest(TestsJournalRecordAndPlayback, "Journal/Basic") // Initialize journal recording and fire off some events... Journal::Record("test.jrn"); + if( !Journal::IsRecording() ) + { + test(false, "Fail"); + return; + } + testEvent.trigger(16); testEvent.trigger(17); testEvent.trigger(18); @@ -132,6 +138,12 @@ CreateUnitTest(TestsJournalDynamicSignals, "Journal/DynamicSignals") // Initialize journal recording and fire off some events... Journal::Record("test.jrn"); + if( !Journal::IsRecording() ) + { + test(false, "Fail"); + return; + } + testEvent.trigger(1); dynamicA->trigger(8, 100); testEvent.trigger(2); diff --git a/Engine/source/core/util/journal/test/testProcess.cpp b/Engine/source/core/util/journal/test/testProcess.cpp index 1dc847b34..0f8da95a5 100644 --- a/Engine/source/core/util/journal/test/testProcess.cpp +++ b/Engine/source/core/util/journal/test/testProcess.cpp @@ -52,5 +52,7 @@ CreateUnitTest(TestingProcess, "Journal/Process") for(S32 i=0; i<30; i++) test(Process::processEvents(), "Should quit after 30 ProcessEvents() calls - not before!"); test(!Process::processEvents(), "Should quit after the 30th ProcessEvent() call!"); + + Process::remove(this, &TestingProcess::process); } }; \ No newline at end of file diff --git a/Engine/source/gui/editor/inspector/dynamicGroup.cpp b/Engine/source/gui/editor/inspector/dynamicGroup.cpp index 9f2285cfb..1e9d19caf 100644 --- a/Engine/source/gui/editor/inspector/dynamicGroup.cpp +++ b/Engine/source/gui/editor/inspector/dynamicGroup.cpp @@ -98,6 +98,9 @@ static S32 QSORT_CALLBACK compareEntries(const void* a,const void* b) //----------------------------------------------------------------------------- bool GuiInspectorDynamicGroup::inspectGroup() { + if( !mParent ) + return false; + // clear the first responder if it's set mStack->clearFirstResponder(); diff --git a/Engine/source/gui/editor/inspector/field.cpp b/Engine/source/gui/editor/inspector/field.cpp index ce5b0a560..5dd9e6c25 100644 --- a/Engine/source/gui/editor/inspector/field.cpp +++ b/Engine/source/gui/editor/inspector/field.cpp @@ -455,7 +455,7 @@ void GuiInspectorField::setInspectorProfile() { GuiControlProfile *profile = NULL; - if( mInspector->getNumInspectObjects() > 1 ) + if( mInspector && (mInspector->getNumInspectObjects() > 1) ) { if( !hasSameValueInAllObjects() ) Sim::findObject( "GuiInspectorMultiFieldDifferentProfile", profile ); diff --git a/Engine/source/gui/editor/inspector/group.cpp b/Engine/source/gui/editor/inspector/group.cpp index 7a52e7ca3..b272032ff 100644 --- a/Engine/source/gui/editor/inspector/group.cpp +++ b/Engine/source/gui/editor/inspector/group.cpp @@ -230,7 +230,7 @@ void GuiInspectorGroup::clearFields() bool GuiInspectorGroup::inspectGroup() { // We can't inspect a group without a target! - if( !mParent->getNumInspectObjects() ) + if( !mParent || !mParent->getNumInspectObjects() ) return false; // to prevent crazy resizing, we'll just freeze our stack for a sec.. diff --git a/Engine/source/gui/editor/inspector/variableField.cpp b/Engine/source/gui/editor/inspector/variableField.cpp index 810ba45a7..f773722e0 100644 --- a/Engine/source/gui/editor/inspector/variableField.cpp +++ b/Engine/source/gui/editor/inspector/variableField.cpp @@ -50,6 +50,12 @@ GuiInspectorVariableField::~GuiInspectorVariableField() bool GuiInspectorVariableField::onAdd() { + if( !mInspector ) + { + Con::errorf("GuiInspectorVariableField::onAdd - Fail - No inspector"); + return false; + } + setInspectorProfile(); // Hack: skip our immediate parent diff --git a/Engine/source/platform/test/testNet.cpp b/Engine/source/platform/test/testNet.cpp index 7be6b8a03..ae2a0c226 100644 --- a/Engine/source/platform/test/testNet.cpp +++ b/Engine/source/platform/test/testNet.cpp @@ -87,8 +87,8 @@ CreateUnitTest( TestTCPRequest, "Platform/Net/TCPRequest") // Open a TCP connection to garagegames.com mSocket = Net::openConnectTo("ip:72.246.107.193:80"); - - while(Process::processEvents()) + U32 limit = Platform::getRealMilliseconds() + (5*1000); + while(Process::processEvents() && (Platform::getRealMilliseconds() < limit) ) ; // Unhook from the signals. @@ -176,6 +176,12 @@ CreateUnitTest( TestTCPRequestJournal, "Platform/Net/JournalTCPRequest") { Journal::Record("journalTCP.jrn"); + if( !Journal::IsRecording() ) + { + test(0, "Failed."); + return; + } + makeRequest(); S32 bytesRead = mDataRecved; diff --git a/Engine/source/scene/sceneManager.cpp b/Engine/source/scene/sceneManager.cpp index fa6721ac7..d07e9fb9a 100644 --- a/Engine/source/scene/sceneManager.cpp +++ b/Engine/source/scene/sceneManager.cpp @@ -605,6 +605,7 @@ bool SceneManager::addObjectToScene( SceneObject* object ) void SceneManager::removeObjectFromScene( SceneObject* obj ) { + AssertFatal( obj, "SceneManager::removeObjectFromScene - Object is not declared" ); AssertFatal( obj->getSceneManager() == this, "SceneManager::removeObjectFromScene - Object not part of SceneManager" ); // Notify the object. @@ -613,7 +614,8 @@ void SceneManager::removeObjectFromScene( SceneObject* obj ) // Remove the object from the container. - getContainer()->removeObject( obj ); + if( getContainer() ) + getContainer()->removeObject( obj ); // Remove the object from the zoning system. diff --git a/Engine/source/sfx/sfxSource.h b/Engine/source/sfx/sfxSource.h index 83e31d0d2..e47b179d0 100644 --- a/Engine/source/sfx/sfxSource.h +++ b/Engine/source/sfx/sfxSource.h @@ -434,7 +434,7 @@ class SFXSource : public SimGroup virtual bool isVirtualized() const { return false; } /// Returns true if this is a looping source. - bool isLooping() const { return mDescription->mIsLooping; } + bool isLooping() const { return mDescription.isValid() && mDescription->mIsLooping; } /// @} diff --git a/Engine/source/sfx/sfxSystem.cpp b/Engine/source/sfx/sfxSystem.cpp index c43a44b2f..4c3d1fdca 100644 --- a/Engine/source/sfx/sfxSystem.cpp +++ b/Engine/source/sfx/sfxSystem.cpp @@ -686,7 +686,8 @@ void SFXSystem::_onRemoveSource( SFXSource* source ) if( dynamic_cast< SFXSound* >( source ) ) { SFXSoundVector::iterator iter = find( mSounds.begin(), mSounds.end(), static_cast< SFXSound* >( source ) ); - mSounds.erase_fast( iter ); + if( iter != mSounds.end() ) + mSounds.erase_fast( iter ); mStatNumSounds = mSounds.size(); } diff --git a/Engine/source/unit/test.cpp b/Engine/source/unit/test.cpp index 5e6438639..050425687 100644 --- a/Engine/source/unit/test.cpp +++ b/Engine/source/unit/test.cpp @@ -29,6 +29,8 @@ #include "unit/test.h" +#include "core/util/journal/process.h" + namespace UnitTesting { @@ -275,6 +277,9 @@ bool TestRun::test(const char* module, bool skipInteractive) Platform::setCurrentDirectory(cwdSave); + // sanity check for avoid Process::requestShutdown() called on some tests + Process::processEvents(); + // And indicate our failure situation in the return value. return !_failureCount; } diff --git a/Engine/source/unit/tests/testComponents.cpp b/Engine/source/unit/tests/testComponents.cpp index 12297bb08..b4a78ca39 100644 --- a/Engine/source/unit/tests/testComponents.cpp +++ b/Engine/source/unit/tests/testComponents.cpp @@ -91,9 +91,17 @@ public: // for this functionality later void unit_test( UnitTest *test ) { + AssertFatal(test, "CachedInterfaceExampleComponent::unit_test - NULL UnitTest"); + + if( !test ) + return; + test->test( mpU32 != NULL, "Pointer to dependent interface is NULL" ); - test->test( *(*mpU32) & ( 1 << 24 ), "Pointer to interface data is bogus." ); - test->test( *(*mpU32) != *mMyId, "Two of me have the same ID, bad!" ); + if( mpU32 ) + { + test->test( *(*mpU32) & ( 1 << 24 ), "Pointer to interface data is bogus." ); + test->test( *(*mpU32) != *mMyId, "Two of me have the same ID, bad!" ); + } } }; @@ -130,13 +138,16 @@ CreateUnitTest(TestComponentInterfacing, "Components/Composition") test( componentB->getOwner() == testComponent, "testComponent did not properly set the mOwner field of componentB to NULL." ); // Register the object with the simulation, kicking off the interface registration - test( testComponent->registerObject(), "Failed to register testComponent" ); + const bool registered = testComponent->registerObject(); + test( registered, "Failed to register testComponent" ); // Interface tests - componentA->unit_test( this ); - componentB->unit_test( this ); - - testComponent->deleteObject(); + if( registered ) + { + componentA->unit_test( this ); + componentB->unit_test( this ); + testComponent->deleteObject(); + } test( m.check(), "Component composition test leaked memory." ); } From 1e06fd2e60d2a1b0aa28edc06dd361af6ad31cb2 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Wed, 28 May 2014 12:23:18 +0200 Subject: [PATCH 062/317] Improvements on CMake generator. * Suppress selected warnings on VC. * Configurable directories (for CI server). --- Tools/CMake/basics.cmake | 18 ++++++++++++------ Tools/CMake/torque3d.cmake | 14 +++++++------- 2 files changed, 19 insertions(+), 13 deletions(-) diff --git a/Tools/CMake/basics.cmake b/Tools/CMake/basics.cmake index aa829fafa..8a87fe1d2 100644 --- a/Tools/CMake/basics.cmake +++ b/Tools/CMake/basics.cmake @@ -2,9 +2,15 @@ project("Torque3DEngine") set(TORQUE_TEMPLATE "Full" CACHE STRING "the template to use") -set(projectDir "${CMAKE_SOURCE_DIR}/My Projects/${TORQUE_APP_NAME}") -set(projectOutDir "${projectDir}/game") -set(projectSrcDir "${projectDir}/source") +if(NOT projectDir) + set(projectDir "${CMAKE_SOURCE_DIR}/My Projects/${TORQUE_APP_NAME}") +endif() +if(NOT projectOutDir) + set(projectOutDir "${projectDir}/game") +endif() +if(NOT projectSrcDir) + set(projectSrcDir "${projectDir}/source") +endif() set(libDir "${CMAKE_SOURCE_DIR}/Engine/lib") set(srcDir "${CMAKE_SOURCE_DIR}/Engine/source") set(cmakeDir "${CMAKE_SOURCE_DIR}/Tools/CMake") @@ -281,12 +287,12 @@ set(TORQUE_STATIC ON) #option(TORQUE_STATIC "enables or disable static" OFF) if(WIN32) - # default disabled warnings: 4018;4100;4121;4127;4130;4244;4245;4389;4511;4512;4800; - set(TORQUE_CXX_FLAGS "/MP /O2 /Ob2 /Oi /Ot /Oy /GT /Zi /W4 /nologo /GF /EHsc /GS- /Gy- /Qpar- /arch:SSE2 /fp:fast /fp:except- /GR /Zc:wchar_t-" CACHE TYPE STRING) + set(TORQUE_DISABLED_WARNINGS " ") + set(TORQUE_CXX_FLAGS "/MP /O2 /Ob2 /Oi /Ot /Oy /GT /Zi /W4 /nologo /GF /EHsc /GS- /Gy- /Qpar- /arch:SSE2 /fp:fast /fp:except- /GR /Zc:wchar_t- /wd4018 /wd4100 /wd4121 /wd4127 /wd4130 /wd4244 /wd4245 /wd4389 /wd4511 /wd4512 /wd4800 /wd4995 /D_CRT_SECURE_NO_WARNINGS " CACHE TYPE STRING) mark_as_advanced(TORQUE_CXX_FLAGS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORQUE_CXX_FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS}") - #set(CMAKE_EXE_LINKER_FLAGS "/OPT:NOREF") + set(CMAKE_EXE_LINKER_FLAGS "/LARGEADDRESSAWARE") #set(STATIC_LIBRARY_FLAGS "/OPT:NOREF") # Force static runtime libraries diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 1e9fb9bf6..0f8b99435 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -84,14 +84,14 @@ mark_as_advanced(TORQUE_DEBUG_GFX_MODE) #option(DEBUG_SPEW "more debug" OFF) set(TORQUE_NO_DSO_GENERATION ON) -if(WIN32) - # warning C4800: 'XXX' : forcing value to bool 'true' or 'false' (performance warning) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4800") - # warning C4018: '<' : signed/unsigned mismatch - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4018") - # warning C4244: 'initializing' : conversion from 'XXX' to 'XXX', possible loss of data - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4244") +# warning C4800: 'XXX' : forcing value to bool 'true' or 'false' (performance warning) +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4800") +# warning C4018: '<' : signed/unsigned mismatch +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4018") +# warning C4244: 'initializing' : conversion from 'XXX' to 'XXX', possible loss of data +set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4244") +if(WIN32) link_directories($ENV{DXSDK_DIR}/Lib/x86) endif() From 808dcb653cb4c04a4d4c9e008ad962226c29dda3 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Wed, 28 May 2014 17:55:05 +0200 Subject: [PATCH 063/317] Remove TABs on CMake files. --- Tools/CMake/basics.cmake | 128 +++++++++++++++++++-------------------- 1 file changed, 64 insertions(+), 64 deletions(-) diff --git a/Tools/CMake/basics.cmake b/Tools/CMake/basics.cmake index 8a87fe1d2..43d7c4e5d 100644 --- a/Tools/CMake/basics.cmake +++ b/Tools/CMake/basics.cmake @@ -35,14 +35,14 @@ macro(addPath dir) ${dir}/*.h) LIST(APPEND ${PROJECT_NAME}_files "${tmpa}") LIST(APPEND ${PROJECT_NAME}_paths "${dir}") - #message(STATUS "addPath ${PROJECT_NAME} : ${tmpa}") + #message(STATUS "addPath ${PROJECT_NAME} : ${tmpa}") #set(t "${${t}};${tmpa}") endmacro() # adds a file to the sources macro(addFile filename) LIST(APPEND ${PROJECT_NAME}_files "${filename}") - #message(STATUS "addFile ${PROJECT_NAME} : ${filename}") + #message(STATUS "addFile ${PROJECT_NAME} : ${filename}") endmacro() # finds and adds sources files in a folder recursively @@ -55,7 +55,7 @@ macro(addPathRec dir) ${dir}/*.h) LIST(APPEND ${PROJECT_NAME}_files "${tmpa}") LIST(APPEND ${PROJECT_NAME}_paths "${dir}") - #message(STATUS "addPathRec ${PROJECT_NAME} : ${tmpa}") + #message(STATUS "addPathRec ${PROJECT_NAME} : ${tmpa}") endmacro() # adds a definition @@ -187,17 +187,17 @@ macro(generateFiltersSpecial relDir) string(REGEX REPLACE "(.*)(/[^/]*)$" "\\1" SRCGR ${SRCGR}) # do not have any ../ dirs string(REPLACE "../" "" SRCGR ${SRCGR}) - IF("${SRCGR}" MATCHES "^torque3d/My Projects/.*$") - string(REPLACE "torque3d/My Projects/${PROJECT_NAME}/" "" SRCGR ${SRCGR}) - string(REPLACE "/source" "" SRCGR ${SRCGR}) - endif() + IF("${SRCGR}" MATCHES "^torque3d/My Projects/.*$") + string(REPLACE "torque3d/My Projects/${PROJECT_NAME}/" "" SRCGR ${SRCGR}) + string(REPLACE "/source" "" SRCGR ${SRCGR}) + endif() # Source_group expects \\ (double antislash), not / (slash) string(REPLACE / \\ SRCGR ${SRCGR}) #STRING(REPLACE "//" "/" SRCGR ${SRCGR}) - IF(EXISTS "${f}" AND NOT IS_DIRECTORY "${f}") - #message(STATUS "FILE: ${f} -> ${SRCGR}") - source_group("${SRCGR}" FILES ${f}) - endif() + IF(EXISTS "${f}" AND NOT IS_DIRECTORY "${f}") + #message(STATUS "FILE: ${f} -> ${SRCGR}") + source_group("${SRCGR}" FILES ${f}) + endif() endforeach() endmacro() # macro to add a static library @@ -216,11 +216,11 @@ macro(addStaticLib) endif() endforeach() generateFilters("${firstDir}") - if(TORQUE_STATIC) - add_library("${PROJECT_NAME}" STATIC ${${PROJECT_NAME}_files}) - else() - add_library("${PROJECT_NAME}" SHARED ${${PROJECT_NAME}_files}) - endif() + if(TORQUE_STATIC) + add_library("${PROJECT_NAME}" STATIC ${${PROJECT_NAME}_files}) + else() + add_library("${PROJECT_NAME}" SHARED ${${PROJECT_NAME}_files}) + endif() # omg - only use the first folder ... otehrwise we get lots of header name collisions #foreach(dir ${${PROJECT_NAME}_paths}) addInclude("${firstDir}") @@ -251,36 +251,36 @@ macro(addExecutable) endmacro() macro(setupVersionNumbers) - set(TORQUE_APP_VERSION_MAJOR 1 CACHE INTEGER "") - set(TORQUE_APP_VERSION_MINOR 0 CACHE INTEGER "") - set(TORQUE_APP_VERSION_PATCH 0 CACHE INTEGER "") - set(TORQUE_APP_VERSION_TWEAK 0 CACHE INTEGER "") - mark_as_advanced(TORQUE_APP_VERSION_TWEAK) - MATH(EXPR TORQUE_APP_VERSION "${TORQUE_APP_VERSION_MAJOR} * 1000 + ${TORQUE_APP_VERSION_MINOR} * 100 + ${TORQUE_APP_VERSION_PATCH} * 10 + ${TORQUE_APP_VERSION_TWEAK}") - set(TORQUE_APP_VERSION_STRING "${TORQUE_APP_VERSION_MAJOR}.${TORQUE_APP_VERSION_MINOR}.${TORQUE_APP_VERSION_PATCH}.${TORQUE_APP_VERSION_TWEAK}") - #message(STATUS "version numbers: ${TORQUE_APP_VERSION} / ${TORQUE_APP_VERSION_STRING}") + set(TORQUE_APP_VERSION_MAJOR 1 CACHE INTEGER "") + set(TORQUE_APP_VERSION_MINOR 0 CACHE INTEGER "") + set(TORQUE_APP_VERSION_PATCH 0 CACHE INTEGER "") + set(TORQUE_APP_VERSION_TWEAK 0 CACHE INTEGER "") + mark_as_advanced(TORQUE_APP_VERSION_TWEAK) + MATH(EXPR TORQUE_APP_VERSION "${TORQUE_APP_VERSION_MAJOR} * 1000 + ${TORQUE_APP_VERSION_MINOR} * 100 + ${TORQUE_APP_VERSION_PATCH} * 10 + ${TORQUE_APP_VERSION_TWEAK}") + set(TORQUE_APP_VERSION_STRING "${TORQUE_APP_VERSION_MAJOR}.${TORQUE_APP_VERSION_MINOR}.${TORQUE_APP_VERSION_PATCH}.${TORQUE_APP_VERSION_TWEAK}") + #message(STATUS "version numbers: ${TORQUE_APP_VERSION} / ${TORQUE_APP_VERSION_STRING}") endmacro() macro(setupPackaging) - INCLUDE(CPack) - # only enable zips for now - set(CPACK_BINARY_NSIS OFF CACHE INTERNAL "" FORCE) - set(CPACK_BINARY_ZIP ON CACHE INTERNAL "" FORCE) - set(CPACK_SOURCE_ZIP OFF CACHE INTERNAL "" FORCE) - SET(CPACK_GENERATOR "ZIP") - SET(CPACK_PACKAGE_VENDOR "${PROJECT_NAME}") - SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_NAME}") - SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 1) - SET(CPACK_OUTPUT_FILE_PREFIX "${projectDir}/packages/${PROJECT_NAME}") - SET(CPACK_PACKAGE_INSTALL_DIRECTORY "") - #SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ReadMe.txt") - #SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/Copyright.txt") - SET(CPACK_PACKAGE_VERSION_MAJOR "${TORQUE_APP_VERSION_MAJOR}") - SET(CPACK_PACKAGE_VERSION_MINOR "${TORQUE_APP_VERSION_MINOR}") - SET(CPACK_PACKAGE_VERSION_PATCH "${TORQUE_APP_VERSION_PATCH}") - #SET(CPACK_PACKAGE_EXECUTABLES "${PROJECT_NAME}" "${PROJECT_NAME}") - SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${TORQUE_APP_VERSION_STRING}") - #SET(CPACK_SOURCE_STRIP_FILES "") + INCLUDE(CPack) + # only enable zips for now + set(CPACK_BINARY_NSIS OFF CACHE INTERNAL "" FORCE) + set(CPACK_BINARY_ZIP ON CACHE INTERNAL "" FORCE) + set(CPACK_SOURCE_ZIP OFF CACHE INTERNAL "" FORCE) + SET(CPACK_GENERATOR "ZIP") + SET(CPACK_PACKAGE_VENDOR "${PROJECT_NAME}") + SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_NAME}") + SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 1) + SET(CPACK_OUTPUT_FILE_PREFIX "${projectDir}/packages/${PROJECT_NAME}") + SET(CPACK_PACKAGE_INSTALL_DIRECTORY "") + #SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ReadMe.txt") + #SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/Copyright.txt") + SET(CPACK_PACKAGE_VERSION_MAJOR "${TORQUE_APP_VERSION_MAJOR}") + SET(CPACK_PACKAGE_VERSION_MINOR "${TORQUE_APP_VERSION_MINOR}") + SET(CPACK_PACKAGE_VERSION_PATCH "${TORQUE_APP_VERSION_PATCH}") + #SET(CPACK_PACKAGE_EXECUTABLES "${PROJECT_NAME}" "${PROJECT_NAME}") + SET(CPACK_SOURCE_PACKAGE_FILE_NAME "${PROJECT_NAME}-${TORQUE_APP_VERSION_STRING}") + #SET(CPACK_SOURCE_STRIP_FILES "") endmacro() # always static for now set(TORQUE_STATIC ON) @@ -289,36 +289,36 @@ set(TORQUE_STATIC ON) if(WIN32) set(TORQUE_DISABLED_WARNINGS " ") set(TORQUE_CXX_FLAGS "/MP /O2 /Ob2 /Oi /Ot /Oy /GT /Zi /W4 /nologo /GF /EHsc /GS- /Gy- /Qpar- /arch:SSE2 /fp:fast /fp:except- /GR /Zc:wchar_t- /wd4018 /wd4100 /wd4121 /wd4127 /wd4130 /wd4244 /wd4245 /wd4389 /wd4511 /wd4512 /wd4800 /wd4995 /D_CRT_SECURE_NO_WARNINGS " CACHE TYPE STRING) - mark_as_advanced(TORQUE_CXX_FLAGS) + mark_as_advanced(TORQUE_CXX_FLAGS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORQUE_CXX_FLAGS}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "/LARGEADDRESSAWARE") #set(STATIC_LIBRARY_FLAGS "/OPT:NOREF") # Force static runtime libraries - if(TORQUE_STATIC) - FOREACH(flag - CMAKE_C_FLAGS_RELEASE - CMAKE_C_FLAGS_RELWITHDEBINFO - CMAKE_C_FLAGS_DEBUG - CMAKE_C_FLAGS_DEBUG_INIT - CMAKE_CXX_FLAGS_RELEASE - CMAKE_CXX_FLAGS_RELWITHDEBINFO - CMAKE_CXX_FLAGS_DEBUG - CMAKE_CXX_FLAGS_DEBUG_INIT) - STRING(REPLACE "/MD" "/MT" "${flag}" "${${flag}}") - SET("${flag}" "${${flag}} /EHsc") - ENDFOREACH() - endif() + if(TORQUE_STATIC) + FOREACH(flag + CMAKE_C_FLAGS_RELEASE + CMAKE_C_FLAGS_RELWITHDEBINFO + CMAKE_C_FLAGS_DEBUG + CMAKE_C_FLAGS_DEBUG_INIT + CMAKE_CXX_FLAGS_RELEASE + CMAKE_CXX_FLAGS_RELWITHDEBINFO + CMAKE_CXX_FLAGS_DEBUG + CMAKE_CXX_FLAGS_DEBUG_INIT) + STRING(REPLACE "/MD" "/MT" "${flag}" "${${flag}}") + SET("${flag}" "${${flag}} /EHsc") + ENDFOREACH() + endif() endif() # fix the debug/release subfolders on windows if(MSVC) - FOREACH(CONF ${CMAKE_CONFIGURATION_TYPES}) - # Go uppercase (DEBUG, RELEASE...) - STRING(TOUPPER "${CONF}" CONF) - #SET("CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CONF}" "${projectOutDir}") - SET("CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONF}" "${projectOutDir}") - ENDFOREACH() + FOREACH(CONF ${CMAKE_CONFIGURATION_TYPES}) + # Go uppercase (DEBUG, RELEASE...) + STRING(TOUPPER "${CONF}" CONF) + #SET("CMAKE_ARCHIVE_OUTPUT_DIRECTORY_${CONF}" "${projectOutDir}") + SET("CMAKE_RUNTIME_OUTPUT_DIRECTORY_${CONF}" "${projectOutDir}") + ENDFOREACH() endif() From efa3c02291ec450939cb361315995299001011f4 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Wed, 28 May 2014 19:21:12 +0200 Subject: [PATCH 064/317] CMake: Remove unsused variable, and revert a small change. --- Tools/CMake/basics.cmake | 1 - Tools/CMake/torque3d.cmake | 14 +++++++------- 2 files changed, 7 insertions(+), 8 deletions(-) diff --git a/Tools/CMake/basics.cmake b/Tools/CMake/basics.cmake index 43d7c4e5d..1903efc2f 100644 --- a/Tools/CMake/basics.cmake +++ b/Tools/CMake/basics.cmake @@ -287,7 +287,6 @@ set(TORQUE_STATIC ON) #option(TORQUE_STATIC "enables or disable static" OFF) if(WIN32) - set(TORQUE_DISABLED_WARNINGS " ") set(TORQUE_CXX_FLAGS "/MP /O2 /Ob2 /Oi /Ot /Oy /GT /Zi /W4 /nologo /GF /EHsc /GS- /Gy- /Qpar- /arch:SSE2 /fp:fast /fp:except- /GR /Zc:wchar_t- /wd4018 /wd4100 /wd4121 /wd4127 /wd4130 /wd4244 /wd4245 /wd4389 /wd4511 /wd4512 /wd4800 /wd4995 /D_CRT_SECURE_NO_WARNINGS " CACHE TYPE STRING) mark_as_advanced(TORQUE_CXX_FLAGS) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORQUE_CXX_FLAGS}") diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 0f8b99435..1e9fb9bf6 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -84,14 +84,14 @@ mark_as_advanced(TORQUE_DEBUG_GFX_MODE) #option(DEBUG_SPEW "more debug" OFF) set(TORQUE_NO_DSO_GENERATION ON) -# warning C4800: 'XXX' : forcing value to bool 'true' or 'false' (performance warning) -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4800") -# warning C4018: '<' : signed/unsigned mismatch -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4018") -# warning C4244: 'initializing' : conversion from 'XXX' to 'XXX', possible loss of data -set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4244") - if(WIN32) + # warning C4800: 'XXX' : forcing value to bool 'true' or 'false' (performance warning) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4800") + # warning C4018: '<' : signed/unsigned mismatch + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4018") + # warning C4244: 'initializing' : conversion from 'XXX' to 'XXX', possible loss of data + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4244") + link_directories($ENV{DXSDK_DIR}/Lib/x86) endif() From 2a1a412d743b38ae4648e7be9be80ea464045a55 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Thu, 29 May 2014 17:40:57 +1000 Subject: [PATCH 065/317] Don't assign NULL to a Vector. --- Engine/source/core/util/tSignal.h | 1 - 1 file changed, 1 deletion(-) diff --git a/Engine/source/core/util/tSignal.h b/Engine/source/core/util/tSignal.h index d13fb534e..d5b21decf 100644 --- a/Engine/source/core/util/tSignal.h +++ b/Engine/source/core/util/tSignal.h @@ -54,7 +54,6 @@ public: { mList.next = mList.prev = &mList; mList.order = 0.5f; - mTriggerNext = NULL; } ~SignalBase(); From ac96ac75681ddb8f557c1608f346c2cb4ccea044 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Fri, 30 May 2014 00:16:43 +0200 Subject: [PATCH 066/317] Fix AssertFatal/TORQUE_UNUSED are not optimized on release build causing performance problems. MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit http://cnicholson.net/2009/02/stupid-c-tricks-adventures-in-assert/ > "Both gcc and MSVC are smart enough to optimize out the evaluation of x, but only if they can determine if there are no side effects associated with the evaluation. Unfortunately, this can only be done if the body of x is known entirely to the compiler. If x is a function call to another module MSVC can still make it go away with Link-Time Code Generation (via cross-module inlining), but poor gcc is dead in the water and emits the call. Either way, we’re relying on compiler and linker optimizations to make this code go away." --- Engine/source/platform/types.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/platform/types.h b/Engine/source/platform/types.h index 8ce702a3c..0a7bd63ba 100644 --- a/Engine/source/platform/types.h +++ b/Engine/source/platform/types.h @@ -40,7 +40,7 @@ typedef double F64; ///< Compiler independent 64-bit float struct EmptyType {}; ///< "Null" type used by templates -#define TORQUE_UNUSED(var) (void)(var) +#define TORQUE_UNUSED(var) (void)sizeof(var) //------------------------------------------------------------------------------ //------------------------------------- String Types From 6450294855b24f617d59deb2b07c9fa83d63ba1c Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Fri, 30 May 2014 12:35:39 +0200 Subject: [PATCH 067/317] Fixes for dedicated build on linux. Thx @Bloodknigh for Signal fix. --- Engine/source/core/util/tSignal.h | 2 +- .../advanced/advancedLightingFeatures.cpp | 2 +- Engine/source/materials/miscShdrDat.h | 1 + Engine/source/platform/types.gcc.h | 2 +- Engine/source/platformPOSIX/posixVolume.cpp | 4 ++-- Engine/source/platformPOSIX/posixVolume.h | 8 ++++---- .../source/platformX86UNIX/x86UNIXFileio.cpp | 18 +++++++++--------- Engine/source/shaderGen/GLSL/shaderCompGLSL.h | 4 +++- 8 files changed, 22 insertions(+), 19 deletions(-) diff --git a/Engine/source/core/util/tSignal.h b/Engine/source/core/util/tSignal.h index d13fb534e..07a412a38 100644 --- a/Engine/source/core/util/tSignal.h +++ b/Engine/source/core/util/tSignal.h @@ -149,7 +149,7 @@ public: } protected: - friend class SignalSig; + friend class SignalBaseT< Signature >; void _setSignal(SignalSig *sig) { diff --git a/Engine/source/lighting/advanced/advancedLightingFeatures.cpp b/Engine/source/lighting/advanced/advancedLightingFeatures.cpp index fb61494b5..32deccc55 100644 --- a/Engine/source/lighting/advanced/advancedLightingFeatures.cpp +++ b/Engine/source/lighting/advanced/advancedLightingFeatures.cpp @@ -55,7 +55,7 @@ void AdvancedLightingFeatures::registerFeatures( const GFXFormat &prepassTargetF if(GFX->getAdapterType() == OpenGL) { #if defined( TORQUE_OS_MAC ) || defined( TORQUE_OS_LINUX ) - cond = new GBufferConditionerGLSL( prepassTargetFormat ); + cond = new GBufferConditionerGLSL( prepassTargetFormat, GBufferConditionerGLSL::ViewSpace ); FEATUREMGR->registerFeature(MFT_PrePassConditioner, cond); FEATUREMGR->registerFeature(MFT_RTLighting, new DeferredRTLightingFeatGLSL()); FEATUREMGR->registerFeature(MFT_NormalMap, new DeferredBumpFeatGLSL()); diff --git a/Engine/source/materials/miscShdrDat.h b/Engine/source/materials/miscShdrDat.h index 6441f1b07..25422008d 100644 --- a/Engine/source/materials/miscShdrDat.h +++ b/Engine/source/materials/miscShdrDat.h @@ -41,6 +41,7 @@ enum RegisterType RT_NORMAL, RT_BINORMAL, RT_TANGENT, + RT_TANGENTW, RT_COLOR, RT_TEXCOORD, RT_VPOS, diff --git a/Engine/source/platform/types.gcc.h b/Engine/source/platform/types.gcc.h index 76a9a7eab..1d49feacc 100644 --- a/Engine/source/platform/types.gcc.h +++ b/Engine/source/platform/types.gcc.h @@ -60,7 +60,7 @@ typedef unsigned long long U64; # define TORQUE_OS_WIN # define TORQUE_OS_WIN64 # include "platform/types.win.h" -#if defined(__WIN32__) || defined(_WIN32) +#elif defined(__WIN32__) || defined(_WIN32) # define TORQUE_OS_STRING "Win32" # define TORQUE_OS_WIN # define TORQUE_OS_WIN32 diff --git a/Engine/source/platformPOSIX/posixVolume.cpp b/Engine/source/platformPOSIX/posixVolume.cpp index 604611543..a5d0871d0 100644 --- a/Engine/source/platformPOSIX/posixVolume.cpp +++ b/Engine/source/platformPOSIX/posixVolume.cpp @@ -251,7 +251,7 @@ Path PosixFile::getName() const return _path; } -FileNode::Status PosixFile::getStatus() const +FileNode::NodeStatus PosixFile::getStatus() const { return _status; } @@ -536,7 +536,7 @@ bool PosixDirectory::getAttributes(Attributes* attr) return true; } -FileNode::Status PosixDirectory::getStatus() const +FileNode::NodeStatus PosixDirectory::getStatus() const { return _status; } diff --git a/Engine/source/platformPOSIX/posixVolume.h b/Engine/source/platformPOSIX/posixVolume.h index 3f6b48cf0..4445ed38e 100644 --- a/Engine/source/platformPOSIX/posixVolume.h +++ b/Engine/source/platformPOSIX/posixVolume.h @@ -68,7 +68,7 @@ class PosixFile: public File Path _path; String _name; FILE* _handle; - Status _status; + NodeStatus _status; PosixFile(const Path& path,String name); bool _updateInfo(); @@ -78,7 +78,7 @@ public: ~PosixFile(); Path getName() const; - Status getStatus() const; + NodeStatus getStatus() const; bool getAttributes(Attributes*); U32 getPosition(); @@ -103,7 +103,7 @@ class PosixDirectory: public Directory Path _path; String _name; DIR* _handle; - Status _status; + NodeStatus _status; PosixDirectory(const Path& path,String name); void _updateStatus(); @@ -112,7 +112,7 @@ public: ~PosixDirectory(); Path getName() const; - Status getStatus() const; + NodeStatus getStatus() const; bool getAttributes(Attributes*); bool open(); diff --git a/Engine/source/platformX86UNIX/x86UNIXFileio.cpp b/Engine/source/platformX86UNIX/x86UNIXFileio.cpp index 680faf822..22c40a187 100644 --- a/Engine/source/platformX86UNIX/x86UNIXFileio.cpp +++ b/Engine/source/platformX86UNIX/x86UNIXFileio.cpp @@ -469,7 +469,7 @@ bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite) // Sets capability appropriate to the openMode. // Returns the currentStatus of the file. //----------------------------------------------------------------------------- - File::Status File::open(const char *filename, const AccessMode openMode) + File::FileStatus File::open(const char *filename, const AccessMode openMode) { AssertFatal(NULL != filename, "File::open: NULL filename"); AssertWarn(NULL == handle, "File::open: handle already valid"); @@ -584,7 +584,7 @@ bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite) // // Returns the currentStatus of the file. //----------------------------------------------------------------------------- - File::Status File::setPosition(S32 position, bool absolutePos) + File::FileStatus File::setPosition(S32 position, bool absolutePos) { AssertFatal(Closed != currentStatus, "File::setPosition: file closed"); AssertFatal(NULL != handle, "File::setPosition: invalid file handle"); @@ -645,7 +645,7 @@ bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite) // It is an error to flush a read-only file. // Returns the currentStatus of the file. //----------------------------------------------------------------------------- - File::Status File::flush() + File::FileStatus File::flush() { AssertFatal(Closed != currentStatus, "File::flush: file closed"); AssertFatal(NULL != handle, "File::flush: invalid file handle"); @@ -662,7 +662,7 @@ bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite) // // Returns the currentStatus //----------------------------------------------------------------------------- - File::Status File::close() + File::FileStatus File::close() { // if the handle is non-NULL, close it if necessary and free it if (NULL != handle) @@ -684,7 +684,7 @@ bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite) //----------------------------------------------------------------------------- // Self-explanatory. //----------------------------------------------------------------------------- - File::Status File::getStatus() const + File::FileStatus File::getStatus() const { return currentStatus; } @@ -692,7 +692,7 @@ bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite) //----------------------------------------------------------------------------- // Sets and returns the currentStatus when an error has been encountered. //----------------------------------------------------------------------------- - File::Status File::setStatus() + File::FileStatus File::setStatus() { Con::printf("File IO error: %s", strerror(errno)); return currentStatus = IOError; @@ -701,7 +701,7 @@ bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite) //----------------------------------------------------------------------------- // Sets and returns the currentStatus to status. //----------------------------------------------------------------------------- - File::Status File::setStatus(File::Status status) + File::FileStatus File::setStatus(File::FileStatus status) { return currentStatus = status; } @@ -712,7 +712,7 @@ bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite) // The number of bytes read is available in bytesRead if a non-Null pointer is // provided. //----------------------------------------------------------------------------- - File::Status File::read(U32 size, char *dst, U32 *bytesRead) + File::FileStatus File::read(U32 size, char *dst, U32 *bytesRead) { #ifdef DEBUG // fprintf(stdout,"reading %d bytes\n",size);fflush(stdout); @@ -770,7 +770,7 @@ bool dPathCopy(const char *fromName, const char *toName, bool nooverwrite) // The number of bytes written is available in bytesWritten if a non-Null // pointer is provided. //----------------------------------------------------------------------------- - File::Status File::write(U32 size, const char *src, U32 *bytesWritten) + File::FileStatus File::write(U32 size, const char *src, U32 *bytesWritten) { // JMQ: despite the U32 parameters, the maximum filesize supported by this // function is probably the max value of S32, due to the unix syscall diff --git a/Engine/source/shaderGen/GLSL/shaderCompGLSL.h b/Engine/source/shaderGen/GLSL/shaderCompGLSL.h index c00c70165..12cd4ae93 100644 --- a/Engine/source/shaderGen/GLSL/shaderCompGLSL.h +++ b/Engine/source/shaderGen/GLSL/shaderCompGLSL.h @@ -40,6 +40,7 @@ public: virtual void reset(); virtual void sortVars(); + virtual void print( Stream &stream) {} // TODO OPENGL temporal fix for dedicated build on Linux virtual void print( Stream &stream, bool isVerterShader ); void printStructDefines( Stream &stream, bool in ); virtual void printOnMain( Stream &stream, bool isVerterShader ); @@ -55,6 +56,7 @@ public: virtual void reset(); virtual void sortVars(); + virtual void print( Stream &stream) {} // TODO OPENGL temporal fix for dedicated build on Linux virtual void print( Stream &stream, bool isVerterShader ); virtual void printOnMain( Stream &stream, bool isVerterShader ); }; @@ -73,4 +75,4 @@ public: virtual void print( Stream &stream, bool isVerterShader ); }; -#endif // _SHADERCOMP_GLSL_H_ \ No newline at end of file +#endif // _SHADERCOMP_GLSL_H_ From 11ac92acccb4ca4ab7d26aed90487c3f21c5a02e Mon Sep 17 00:00:00 2001 From: Azaezel Date: Fri, 30 May 2014 22:49:17 -0500 Subject: [PATCH 068/317] adds the name of the sequence just completed for simple FSMs and the like. example usage: function shapeBaseData::onEndSequence(%this, %obj, %slot, %name) { %script = "on" @ %name; if(%this.isMethod(%script)) %this.call(%script, %obj); } function fooData::onDeploy(%this,%obj) { error("fooData::onDeploy" SPC %this SPC %obj); } --- Engine/source/T3D/shapeBase.cpp | 7 ++++--- Engine/source/T3D/shapeBase.h | 2 +- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index eff6bd855..4a4156965 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -114,11 +114,12 @@ IMPLEMENT_CALLBACK( ShapeBaseData, onTrigger, void, ( ShapeBase* obj, S32 index, "@param index Index of the trigger that changed\n" "@param state New state of the trigger\n" ); -IMPLEMENT_CALLBACK( ShapeBaseData, onEndSequence, void, ( ShapeBase* obj, S32 slot ), ( obj, slot ), +IMPLEMENT_CALLBACK(ShapeBaseData, onEndSequence, void, (ShapeBase* obj, S32 slot, const String &name), (obj, slot, name), "@brief Called when a thread playing a non-cyclic sequence reaches the end of the " "sequence.\n\n" "@param obj The ShapeBase object\n" - "@param slot Thread slot that finished playing\n" ); + "@param slot Thread slot that finished playing\n" + "@param name Thread name that finished playing\n"); IMPLEMENT_CALLBACK( ShapeBaseData, onForceUncloak, void, ( ShapeBase* obj, const char* reason ), ( obj, reason ), "@brief Called when the object is forced to uncloak.\n\n" @@ -2352,7 +2353,7 @@ void ShapeBase::advanceThreads(F32 dt) st.atEnd = true; updateThread(st); if (!isGhost()) { - mDataBlock->onEndSequence_callback( this, i ); + mDataBlock->onEndSequence_callback(this, i, this->getThreadSequenceName(i)); } } diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index 0bb54faf4..51a450437 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -648,7 +648,7 @@ public: DECLARE_CALLBACK( void, onCollision, ( ShapeBase* obj, SceneObject* collObj, VectorF vec, F32 len ) ); DECLARE_CALLBACK( void, onDamage, ( ShapeBase* obj, F32 delta ) ); DECLARE_CALLBACK( void, onTrigger, ( ShapeBase* obj, S32 index, bool state ) ); - DECLARE_CALLBACK( void, onEndSequence, ( ShapeBase* obj, S32 slot ) ); + DECLARE_CALLBACK( void, onEndSequence, (ShapeBase* obj, S32 slot, const String &name)); DECLARE_CALLBACK( void, onForceUncloak, ( ShapeBase* obj, const char* reason ) ); /// @} }; From 492f217330c586bf596dce527b2505a504e69719 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Mon, 2 Jun 2014 01:26:39 +0200 Subject: [PATCH 069/317] Move CMake library's files to libraries dir. --- Tools/CMake/CMakeLists.txt | 30 +++++++++---------- Tools/CMake/{ => libraries}/collada.cmake | 0 .../CMake/{ => libraries}/convexDecomp.cmake | 0 Tools/CMake/{ => libraries}/libogg.cmake | 0 Tools/CMake/{ => libraries}/libtheora.cmake | 0 Tools/CMake/{ => libraries}/libvorbis.cmake | 0 Tools/CMake/{ => libraries}/ljpeg.cmake | 0 Tools/CMake/{ => libraries}/lmng.cmake | 0 Tools/CMake/{ => libraries}/lpng.cmake | 0 Tools/CMake/{ => libraries}/lungif.cmake | 0 Tools/CMake/{ => libraries}/opcode.cmake | 0 Tools/CMake/{ => libraries}/pcre.cmake | 0 Tools/CMake/{ => libraries}/squish.cmake | 0 Tools/CMake/{ => libraries}/tinyxml.cmake | 0 Tools/CMake/{ => libraries}/zlib.cmake | 0 15 files changed, 15 insertions(+), 15 deletions(-) rename Tools/CMake/{ => libraries}/collada.cmake (100%) rename Tools/CMake/{ => libraries}/convexDecomp.cmake (100%) rename Tools/CMake/{ => libraries}/libogg.cmake (100%) rename Tools/CMake/{ => libraries}/libtheora.cmake (100%) rename Tools/CMake/{ => libraries}/libvorbis.cmake (100%) rename Tools/CMake/{ => libraries}/ljpeg.cmake (100%) rename Tools/CMake/{ => libraries}/lmng.cmake (100%) rename Tools/CMake/{ => libraries}/lpng.cmake (100%) rename Tools/CMake/{ => libraries}/lungif.cmake (100%) rename Tools/CMake/{ => libraries}/opcode.cmake (100%) rename Tools/CMake/{ => libraries}/pcre.cmake (100%) rename Tools/CMake/{ => libraries}/squish.cmake (100%) rename Tools/CMake/{ => libraries}/tinyxml.cmake (100%) rename Tools/CMake/{ => libraries}/zlib.cmake (100%) diff --git a/Tools/CMake/CMakeLists.txt b/Tools/CMake/CMakeLists.txt index 950d8cf99..6953e9385 100644 --- a/Tools/CMake/CMakeLists.txt +++ b/Tools/CMake/CMakeLists.txt @@ -3,26 +3,26 @@ include(basics.cmake) setupVersionNumbers() #the libs -include(lmng.cmake) -include(lpng.cmake) -include(lungif.cmake) -include(zlib.cmake) -include(ljpeg.cmake) -include(tinyxml.cmake) -include(opcode.cmake) -include(squish.cmake) -include(collada.cmake) -include(pcre.cmake) -include(convexDecomp.cmake) +include(libraries/lmng.cmake) +include(libraries/lpng.cmake) +include(libraries/lungif.cmake) +include(libraries/zlib.cmake) +include(libraries/ljpeg.cmake) +include(libraries/tinyxml.cmake) +include(libraries/opcode.cmake) +include(libraries/squish.cmake) +include(libraries/collada.cmake) +include(libraries/pcre.cmake) +include(libraries/convexDecomp.cmake) if(TORQUE_SFX_VORBIS) - include(libvorbis.cmake) - include(libogg.cmake) + include(libraries/libvorbis.cmake) + include(libraries/libogg.cmake) endif() if(TORQUE_THEORA) - include(libtheora.cmake) + include(libraries/libtheora.cmake) endif() # the main engine, should come last include(torque3d.cmake) -#setupPackaging() \ No newline at end of file +#setupPackaging() diff --git a/Tools/CMake/collada.cmake b/Tools/CMake/libraries/collada.cmake similarity index 100% rename from Tools/CMake/collada.cmake rename to Tools/CMake/libraries/collada.cmake diff --git a/Tools/CMake/convexDecomp.cmake b/Tools/CMake/libraries/convexDecomp.cmake similarity index 100% rename from Tools/CMake/convexDecomp.cmake rename to Tools/CMake/libraries/convexDecomp.cmake diff --git a/Tools/CMake/libogg.cmake b/Tools/CMake/libraries/libogg.cmake similarity index 100% rename from Tools/CMake/libogg.cmake rename to Tools/CMake/libraries/libogg.cmake diff --git a/Tools/CMake/libtheora.cmake b/Tools/CMake/libraries/libtheora.cmake similarity index 100% rename from Tools/CMake/libtheora.cmake rename to Tools/CMake/libraries/libtheora.cmake diff --git a/Tools/CMake/libvorbis.cmake b/Tools/CMake/libraries/libvorbis.cmake similarity index 100% rename from Tools/CMake/libvorbis.cmake rename to Tools/CMake/libraries/libvorbis.cmake diff --git a/Tools/CMake/ljpeg.cmake b/Tools/CMake/libraries/ljpeg.cmake similarity index 100% rename from Tools/CMake/ljpeg.cmake rename to Tools/CMake/libraries/ljpeg.cmake diff --git a/Tools/CMake/lmng.cmake b/Tools/CMake/libraries/lmng.cmake similarity index 100% rename from Tools/CMake/lmng.cmake rename to Tools/CMake/libraries/lmng.cmake diff --git a/Tools/CMake/lpng.cmake b/Tools/CMake/libraries/lpng.cmake similarity index 100% rename from Tools/CMake/lpng.cmake rename to Tools/CMake/libraries/lpng.cmake diff --git a/Tools/CMake/lungif.cmake b/Tools/CMake/libraries/lungif.cmake similarity index 100% rename from Tools/CMake/lungif.cmake rename to Tools/CMake/libraries/lungif.cmake diff --git a/Tools/CMake/opcode.cmake b/Tools/CMake/libraries/opcode.cmake similarity index 100% rename from Tools/CMake/opcode.cmake rename to Tools/CMake/libraries/opcode.cmake diff --git a/Tools/CMake/pcre.cmake b/Tools/CMake/libraries/pcre.cmake similarity index 100% rename from Tools/CMake/pcre.cmake rename to Tools/CMake/libraries/pcre.cmake diff --git a/Tools/CMake/squish.cmake b/Tools/CMake/libraries/squish.cmake similarity index 100% rename from Tools/CMake/squish.cmake rename to Tools/CMake/libraries/squish.cmake diff --git a/Tools/CMake/tinyxml.cmake b/Tools/CMake/libraries/tinyxml.cmake similarity index 100% rename from Tools/CMake/tinyxml.cmake rename to Tools/CMake/libraries/tinyxml.cmake diff --git a/Tools/CMake/zlib.cmake b/Tools/CMake/libraries/zlib.cmake similarity index 100% rename from Tools/CMake/zlib.cmake rename to Tools/CMake/libraries/zlib.cmake From 8a508950624830c39cfba3610d13b3a7e0cbc01a Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Mon, 2 Jun 2014 01:35:06 +0200 Subject: [PATCH 070/317] Clean CMake files. --- Tools/CMake/CMakeLists.txt | 22 +- Tools/CMake/basics.cmake | 262 ++++++++++-------- Tools/CMake/cleanup-win.bat.in | 2 +- Tools/CMake/libraries/collada.cmake | 4 +- Tools/CMake/libraries/convexDecomp.cmake | 2 +- Tools/CMake/libraries/libogg.cmake | 6 +- Tools/CMake/libraries/libtheora.cmake | 4 +- Tools/CMake/libraries/libvorbis.cmake | 4 +- Tools/CMake/libraries/ljpeg.cmake | 2 +- Tools/CMake/libraries/lmng.cmake | 3 +- Tools/CMake/libraries/lpng.cmake | 4 +- Tools/CMake/libraries/lungif.cmake | 5 +- Tools/CMake/libraries/opcode.cmake | 4 +- Tools/CMake/libraries/pcre.cmake | 8 +- .../{library_recast.cmake => recast.cmake} | 12 +- Tools/CMake/libraries/squish.cmake | 2 +- Tools/CMake/libraries/tinyxml.cmake | 2 +- Tools/CMake/libraries/zlib.cmake | 2 +- Tools/CMake/modules/module_hydra.cmake | 2 +- Tools/CMake/modules/module_navigation.cmake | 15 +- Tools/CMake/modules/module_oculusVR.cmake | 8 +- Tools/CMake/template.cmake | 8 +- Tools/CMake/torque3d.cmake | 94 +++---- 23 files changed, 239 insertions(+), 238 deletions(-) rename Tools/CMake/libraries/{library_recast.cmake => recast.cmake} (50%) diff --git a/Tools/CMake/CMakeLists.txt b/Tools/CMake/CMakeLists.txt index 6953e9385..766c1c771 100644 --- a/Tools/CMake/CMakeLists.txt +++ b/Tools/CMake/CMakeLists.txt @@ -2,27 +2,7 @@ include(basics.cmake) setupVersionNumbers() -#the libs -include(libraries/lmng.cmake) -include(libraries/lpng.cmake) -include(libraries/lungif.cmake) -include(libraries/zlib.cmake) -include(libraries/ljpeg.cmake) -include(libraries/tinyxml.cmake) -include(libraries/opcode.cmake) -include(libraries/squish.cmake) -include(libraries/collada.cmake) -include(libraries/pcre.cmake) -include(libraries/convexDecomp.cmake) -if(TORQUE_SFX_VORBIS) - include(libraries/libvorbis.cmake) - include(libraries/libogg.cmake) -endif() -if(TORQUE_THEORA) - include(libraries/libtheora.cmake) -endif() - # the main engine, should come last include(torque3d.cmake) -#setupPackaging() +#setupPackaging() \ No newline at end of file diff --git a/Tools/CMake/basics.cmake b/Tools/CMake/basics.cmake index 1903efc2f..2b4ff0278 100644 --- a/Tools/CMake/basics.cmake +++ b/Tools/CMake/basics.cmake @@ -15,7 +15,6 @@ set(libDir "${CMAKE_SOURCE_DIR}/Engine/lib") set(srcDir "${CMAKE_SOURCE_DIR}/Engine/source") set(cmakeDir "${CMAKE_SOURCE_DIR}/Tools/CMake") - # hide some things mark_as_advanced(CMAKE_INSTALL_PREFIX) mark_as_advanced(CMAKE_CONFIGURATION_TYPES) @@ -25,18 +24,25 @@ mark_as_advanced(CMAKE_CONFIGURATION_TYPES) #set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${projectOutDir}/game) #set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${projectOutDir}/game) +############################################################################### +### Source File Handling +############################################################################### + # finds and adds sources files in a folder macro(addPath dir) - set(tmpa "") - file(GLOB tmpa + set(tmp_files "") + set(glob_config GLOB) + if(${ARGC} GREATER 1 AND "${ARGV1}" STREQUAL "REC") + set(glob_config GLOB_RECURSE) + endif() + file(${glob_config} tmp_files ${dir}/*.cpp ${dir}/*.c ${dir}/*.cc ${dir}/*.h) - LIST(APPEND ${PROJECT_NAME}_files "${tmpa}") + LIST(APPEND ${PROJECT_NAME}_files "${tmp_files}") LIST(APPEND ${PROJECT_NAME}_paths "${dir}") - #message(STATUS "addPath ${PROJECT_NAME} : ${tmpa}") - #set(t "${${t}};${tmpa}") + #message(STATUS "addPath ${PROJECT_NAME} : ${tmp_files}") endmacro() # adds a file to the sources @@ -47,111 +53,115 @@ endmacro() # finds and adds sources files in a folder recursively macro(addPathRec dir) - set(tmpa "") - file(GLOB_RECURSE tmpa - ${dir}/*.cpp - ${dir}/*.c - ${dir}/*.cc - ${dir}/*.h) - LIST(APPEND ${PROJECT_NAME}_files "${tmpa}") - LIST(APPEND ${PROJECT_NAME}_paths "${dir}") - #message(STATUS "addPathRec ${PROJECT_NAME} : ${tmpa}") + addPath("${dir}" "REC") endmacro() -# adds a definition +############################################################################### +### Definition Handling +############################################################################### +macro(__addDef def config) + # two possibilities: a) target already known, so add it directly, or b) target not yet known, so add it to its cache + if(TARGET ${PROJECT_NAME}) + #message(STATUS "directly applying defs: ${PROJECT_NAME} with config ${config}: ${def}") + if("${config}" STREQUAL "") + set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY COMPILE_DEFINITIONS "${def}") + else() + set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY COMPILE_DEFINITIONS_${config} "${def}") + endif() + else() + list(APPEND ${PROJECT_NAME}_defs_${config} ${def}) + #message(STATUS "added definition to cache: ${PROJECT_NAME}_defs_${config}: ${${PROJECT_NAME}_defs_${config}}") + endif() +endmacro() + +# adds a definition: argument 1: Nothing(for all), _DEBUG, _RELEASE, macro(addDef def) - set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY COMPILE_DEFINITIONS "${def}") -endmacro() -# adds a definition -macro(addDebugDef def) - set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY COMPILE_DEFINITIONS_DEBUG "${def}") + set(def_configs "") + if(${ARGC} GREATER 1) + foreach(config "${ARGV1}") + __addDef(${def} ${config}) + endforeach() + else() + __addDef(${def} "") + endif() endmacro() -# adds a required definition. Are processed on addExecutable or addStaticLib -macro(addRequiredDefinition def) - #message(STATUS "${PROJECT_NAME} : add def : ${def}") - LIST( APPEND ${PROJECT_NAME}_required_definition ${def} ) -endmacro() -# adds a required debug definition. Are processed on addExecutable or addStaticLib -macro(addRequiredDebugDefinition def) - #message(STATUS "${PROJECT_NAME} : add def : ${def}") - LIST( APPEND ${PROJECT_NAME}_required_debug_definition ${def} ) +# this applies cached definitions onto the target +macro(_process_defs) + if(DEFINED ${PROJECT_NAME}_defs_) + set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY COMPILE_DEFINITIONS "${${PROJECT_NAME}_defs_}") + #message(STATUS "applying defs to project ${PROJECT_NAME} on all configs: ${${PROJECT_NAME}_defs_}") + endif() + foreach(def_config ${CMAKE_CONFIGURATION_TYPES}) + string(TOUPPER "${def_config}" def_config) + if(DEFINED ${PROJECT_NAME}_defs_${def_config}) + set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY COMPILE_DEFINITIONS_${def_config} "${${PROJECT_NAME}_defs_${def_config}}") + #message(STATUS "applying defs to project ${PROJECT_NAME} on config ${def_config}: ${${PROJECT_NAME}_defs_${def_config}}") + endif() + endforeach() endmacro() -# add definitions to project -macro( _processProjectDefinition ) - foreach( def ${${PROJECT_NAME}_required_definition} ) - addDef( ${def} ) - endforeach() - - foreach( def ${${PROJECT_NAME}_required_debug_definition} ) - addDebugDef( ${def} ) +############################################################################### +### Source Library Handling +############################################################################### +macro(addLibSrc libPath) + set(cached_project_name ${PROJECT_NAME}) + include(${libPath}) + project(${cached_project_name}) +endmacro() + +############################################################################### +### Linked Library Handling +############################################################################### +macro(addLib libs) + foreach(lib ${libs}) + # check if we can build it ourselfs + if(EXISTS "${CMAKE_CURRENT_SOURCE_DIR}/libraries/${lib}.cmake") + addLibSrc("${CMAKE_CURRENT_SOURCE_DIR}/libraries/${lib}.cmake") + endif() + # then link against it + # two possibilities: a) target already known, so add it directly, or b) target not yet known, so add it to its cache + if(TARGET ${PROJECT_NAME}) + target_link_libraries(${PROJECT_NAME} "${lib}") + else() + list(APPEND ${PROJECT_NAME}_libs ${lib}) + endif() endforeach() - - #clear required defs - set( ${PROJECT_NAME}_required_definition ) - set( ${PROJECT_NAME}_required_debug_definition ) endmacro() -# adds an include path +# this applies cached definitions onto the target +macro(_process_libs) + if(DEFINED ${PROJECT_NAME}_libs) + target_link_libraries(${PROJECT_NAME} "${${PROJECT_NAME}_libs}") + endif() +endmacro() + +############################################################################### +### Include Handling +############################################################################### macro(addInclude incPath) - #message(STATUS "${PROJECT_NAME} : add include path : ${incPath}") - set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY INCLUDE_DIRECTORIES "${incPath}") + if(TARGET ${PROJECT_NAME}) + set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY INCLUDE_DIRECTORIES "${incPath}") + else() + list(APPEND ${PROJECT_NAME}_includes ${incPath}) + endif() endmacro() -# adds a library to link against -macro(addLib lib) - #message(STATUS "${PROJECT_NAME} : add lib : ${lib}") - target_link_libraries(${PROJECT_NAME} "${lib}") +# this applies cached definitions onto the target +macro(_process_includes) + if(DEFINED ${PROJECT_NAME}_includes) + set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY INCLUDE_DIRECTORIES "${${PROJECT_NAME}_includes}") + endif() endmacro() -# adds a library dependency. Are processed on addExecutable or addStaticLib -macro(addRequiredLibrary lib) - #message(STATUS "${PROJECT_NAME} : add lib : ${lib}") - LIST( APPEND ${PROJECT_NAME}_required_library ${lib} ) -endmacro() +############################################################################### -# adds a link dependency. Are processed on addExecutable or addStaticLib -macro(addRequiredLink lib) - #message(STATUS "${PROJECT_NAME} : add lib : ${lib}") - LIST( APPEND ${PROJECT_NAME}_required_link ${lib} ) +macro(_postTargetProcess) + _process_includes() + _process_defs() + _process_libs() endmacro() -macro( _processProjectLibrary ) - # Append currect project to PROJECT_STACK - LIST( APPEND PROJECT_STACK "${PROJECT_NAME}" ) - - foreach( lib ${${PROJECT_NAME}_required_library} ) - #message( "adding library dependency: ${lib}" ) - include( ${lib} ) - endforeach() - - #clear required libraries - set( ${PROJECT_NAME}_required_library ) - - # pop currect project form PROJECT_STACK - LIST(REMOVE_AT PROJECT_STACK -1) - - # get currect project form stack - if( PROJECT_STACK ) - LIST(GET PROJECT_STACK -1 TEMP_PROJECT) - project( ${TEMP_PROJECT} ) - endif() - - -endmacro() - -macro( _processProjectLinks ) - #message( "_processProjectLinks: ${PROJECT_NAME}" ) - foreach( lib ${${PROJECT_NAME}_required_link} ) - addLib( ${lib} ) - endforeach() - - #clear required libraries - set( ${PROJECT_NAME}_required_link ) -endmacro() - - # adds a path to search for libs macro(addLibPath dir) link_directories(${dir}) @@ -163,7 +173,7 @@ macro(generateFilters relDir) # Get the path of the file relative to ${DIRECTORY}, # then alter it (not compulsory) file(RELATIVE_PATH SRCGR ${relDir} ${f}) - set(SRCGR "${PROJECT_NAME}/${SRCGR}") + set(SRCGR "${PROJECT_NAME}/${SRCGR}") # Extract the folder, ie remove the filename part string(REGEX REPLACE "(.*)(/[^/]*)$" "\\1" SRCGR ${SRCGR}) # do not have any ../ dirs @@ -182,7 +192,7 @@ macro(generateFiltersSpecial relDir) # Get the path of the file relative to ${DIRECTORY}, # then alter it (not compulsory) file(RELATIVE_PATH SRCGR ${relDir} ${f}) - set(SRCGR "torque3d/${SRCGR}") + set(SRCGR "torque3d/${SRCGR}") # Extract the folder, ie remove the filename part string(REGEX REPLACE "(.*)(/[^/]*)$" "\\1" SRCGR ${SRCGR}) # do not have any ../ dirs @@ -200,8 +210,9 @@ macro(generateFiltersSpecial relDir) endif() endforeach() endmacro() + # macro to add a static library -macro(addStaticLib) +macro(finishLibrary) # more paths? if(${ARGC} GREATER 0) foreach(dir ${ARGV0}) @@ -216,23 +227,30 @@ macro(addStaticLib) endif() endforeach() generateFilters("${firstDir}") + + # set per target compile flags + if(TORQUE_CXX_FLAGS_${PROJECT_NAME}) + set_source_files_properties(${${PROJECT_NAME}_files} PROPERTIES COMPILE_FLAGS "${TORQUE_CXX_FLAGS_${PROJECT_NAME}}") + else() + set_source_files_properties(${${PROJECT_NAME}_files} PROPERTIES COMPILE_FLAGS "${TORQUE_CXX_FLAGS_LIBS}") + endif() + if(TORQUE_STATIC) add_library("${PROJECT_NAME}" STATIC ${${PROJECT_NAME}_files}) else() add_library("${PROJECT_NAME}" SHARED ${${PROJECT_NAME}_files}) endif() - # omg - only use the first folder ... otehrwise we get lots of header name collisions + + # omg - only use the first folder ... otherwise we get lots of header name collisions #foreach(dir ${${PROJECT_NAME}_paths}) addInclude("${firstDir}") #endforeach() - - _processProjectLinks() - _processProjectLibrary() - _processProjectDefinition() + + _postTargetProcess() endmacro() # macro to add an executable -macro(addExecutable) +macro(finishExecutable) # now inspect the paths we got set(firstDir "") foreach(dir ${${PROJECT_NAME}_paths}) @@ -241,13 +259,18 @@ macro(addExecutable) endif() endforeach() generateFiltersSpecial("${firstDir}") + + # set per target compile flags + if(TORQUE_CXX_FLAGS_${PROJECT_NAME}) + set_source_files_properties(${${PROJECT_NAME}_files} PROPERTIES COMPILE_FLAGS "${TORQUE_CXX_FLAGS_${PROJECT_NAME}}") + else() + set_source_files_properties(${${PROJECT_NAME}_files} PROPERTIES COMPILE_FLAGS "${TORQUE_CXX_FLAGS_EXECUTABLES}") + endif() + add_executable("${PROJECT_NAME}" WIN32 ${${PROJECT_NAME}_files}) - # omg - only use the first folder ... otehrwise we get lots of header name collisions addInclude("${firstDir}") - - _processProjectLinks() - _processProjectLibrary() - _processProjectDefinition() + + _postTargetProcess() endmacro() macro(setupVersionNumbers) @@ -287,13 +310,20 @@ set(TORQUE_STATIC ON) #option(TORQUE_STATIC "enables or disable static" OFF) if(WIN32) - set(TORQUE_CXX_FLAGS "/MP /O2 /Ob2 /Oi /Ot /Oy /GT /Zi /W4 /nologo /GF /EHsc /GS- /Gy- /Qpar- /arch:SSE2 /fp:fast /fp:except- /GR /Zc:wchar_t- /wd4018 /wd4100 /wd4121 /wd4127 /wd4130 /wd4244 /wd4245 /wd4389 /wd4511 /wd4512 /wd4800 /wd4995 /D_CRT_SECURE_NO_WARNINGS " CACHE TYPE STRING) - mark_as_advanced(TORQUE_CXX_FLAGS) - set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORQUE_CXX_FLAGS}") + set(TORQUE_CXX_FLAGS_EXECUTABLES "/wd4018 /wd4100 /wd4121 /wd4127 /wd4130 /wd4244 /wd4245 /wd4389 /wd4511 /wd4512 /wd4800 /wd4995 " CACHE TYPE STRING) + mark_as_advanced(TORQUE_CXX_FLAGS_EXECUTABLES) + + set(TORQUE_CXX_FLAGS_LIBS "/W0" CACHE TYPE STRING) + mark_as_advanced(TORQUE_CXX_FLAGS_LIBS) + + set(TORQUE_CXX_FLAGS_COMMON "-DUNICODE -D_UNICODE /MP /O2 /Ob2 /Oi /Ot /Oy /GT /Zi /W4 /nologo /GF /EHsc /GS- /Gy- /Qpar- /arch:SSE2 /fp:fast /fp:except- /GR /Zc:wchar_t- /D_CRT_SECURE_NO_WARNINGS" CACHE TYPE STRING) + mark_as_advanced(TORQUE_CXX_FLAGS_COMMON) + + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORQUE_CXX_FLAGS_COMMON}") set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS}") set(CMAKE_EXE_LINKER_FLAGS "/LARGEADDRESSAWARE") #set(STATIC_LIBRARY_FLAGS "/OPT:NOREF") - + # Force static runtime libraries if(TORQUE_STATIC) FOREACH(flag @@ -309,6 +339,16 @@ if(WIN32) SET("${flag}" "${${flag}} /EHsc") ENDFOREACH() endif() +else() + # TODO: improve default settings on other platforms + set(TORQUE_CXX_FLAGS_EXECUTABLES "" CACHE TYPE STRING) + mark_as_advanced(TORQUE_CXX_FLAGS_EXECUTABLES) + set(TORQUE_CXX_FLAGS_LIBS "" CACHE TYPE STRING) + mark_as_advanced(TORQUE_CXX_FLAGS_LIBS) + set(TORQUE_CXX_FLAGS_COMMON "" CACHE TYPE STRING) + mark_as_advanced(TORQUE_CXX_FLAGS) + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORQUE_CXX_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS}") endif() diff --git a/Tools/CMake/cleanup-win.bat.in b/Tools/CMake/cleanup-win.bat.in index 0e9906dd8..c8c3a4fad 100644 --- a/Tools/CMake/cleanup-win.bat.in +++ b/Tools/CMake/cleanup-win.bat.in @@ -44,4 +44,4 @@ IF EXIST "banlist.cs" del /s banlist.cs :: logs echo logs IF EXIST "torque3d.log" del /s torque3d.log -echo DONE! \ No newline at end of file +echo DONE! diff --git a/Tools/CMake/libraries/collada.cmake b/Tools/CMake/libraries/collada.cmake index a875643d8..00fa94197 100644 --- a/Tools/CMake/libraries/collada.cmake +++ b/Tools/CMake/libraries/collada.cmake @@ -6,8 +6,6 @@ addPath("${libDir}/collada/src/modules/LIBXMLPlugin") addPath("${libDir}/collada/src/modules/stdErrPlugin") addPath("${libDir}/collada/src/modules/STLDatabase") -addStaticLib() - addDef(DOM_INCLUDE_TINYXML) addDef(PCRE_STATIC) @@ -15,3 +13,5 @@ addInclude(${libDir}/collada/include) addInclude(${libDir}/collada/include/1.4) addInclude(${libDir}/pcre) addInclude(${libDir}/tinyxml) + +finishLibrary() diff --git a/Tools/CMake/libraries/convexDecomp.cmake b/Tools/CMake/libraries/convexDecomp.cmake index eb98a6144..3b02db6df 100644 --- a/Tools/CMake/libraries/convexDecomp.cmake +++ b/Tools/CMake/libraries/convexDecomp.cmake @@ -1,3 +1,3 @@ project(convexDecomp) -addStaticLib("${libDir}/convexDecomp") +finishLibrary("${libDir}/convexDecomp") diff --git a/Tools/CMake/libraries/libogg.cmake b/Tools/CMake/libraries/libogg.cmake index fc83fd957..1b3fdf37f 100644 --- a/Tools/CMake/libraries/libogg.cmake +++ b/Tools/CMake/libraries/libogg.cmake @@ -1,7 +1,7 @@ project(libogg) -addPathRec("${libDir}/libogg") - -addStaticLib() +addPath("${libDir}/libogg" REC) addInclude(${libDir}/libogg/include) + +finishLibrary() diff --git a/Tools/CMake/libraries/libtheora.cmake b/Tools/CMake/libraries/libtheora.cmake index 0e6dda65d..023176aa4 100644 --- a/Tools/CMake/libraries/libtheora.cmake +++ b/Tools/CMake/libraries/libtheora.cmake @@ -2,9 +2,9 @@ project(libtheora) addPathRec("${libDir}/libtheora") -addStaticLib() - addDef(TORQUE_OGGTHEORA) addDef(TORQUE_OGGVORIBS) addInclude(${libDir}/libogg/include) addInclude(${libDir}/libtheora/include) + +finishLibrary() diff --git a/Tools/CMake/libraries/libvorbis.cmake b/Tools/CMake/libraries/libvorbis.cmake index 9f1e2eb4a..5fe0248af 100644 --- a/Tools/CMake/libraries/libvorbis.cmake +++ b/Tools/CMake/libraries/libvorbis.cmake @@ -2,8 +2,8 @@ project(libvorbis) addPathRec("${libDir}/libvorbis") -addStaticLib() - addDef(TORQUE_OGGVORBIS) addInclude(${libDir}/libvorbis/include) addInclude(${libDir}/libogg/include) + +finishLibrary() diff --git a/Tools/CMake/libraries/ljpeg.cmake b/Tools/CMake/libraries/ljpeg.cmake index d518cb504..74ea3d8fb 100644 --- a/Tools/CMake/libraries/ljpeg.cmake +++ b/Tools/CMake/libraries/ljpeg.cmake @@ -1,3 +1,3 @@ project(ljpeg) -addStaticLib("${libDir}/ljpeg") +finishLibrary("${libDir}/ljpeg") diff --git a/Tools/CMake/libraries/lmng.cmake b/Tools/CMake/libraries/lmng.cmake index 27da5444f..c526d50c9 100644 --- a/Tools/CMake/libraries/lmng.cmake +++ b/Tools/CMake/libraries/lmng.cmake @@ -1,9 +1,10 @@ project(lmng) -addStaticLib("${libDir}/${PROJECT_NAME}") addDef(MNG_OPTIMIZE_OBJCLEANUP) addInclude(${libDir}/lpng) addInclude(${libDir}/zlib) addInclude(${libDir}/ljpeg) + +finishLibrary("${libDir}/${PROJECT_NAME}") diff --git a/Tools/CMake/libraries/lpng.cmake b/Tools/CMake/libraries/lpng.cmake index 0dbb15501..a62c98bee 100644 --- a/Tools/CMake/libraries/lpng.cmake +++ b/Tools/CMake/libraries/lpng.cmake @@ -1,7 +1,7 @@ project(lpng) -addStaticLib("${libDir}/${PROJECT_NAME}") - # addDef(PNG_NO_ASSEMBLER_CODE) addInclude(${libDir}/zlib) + +finishLibrary("${libDir}/${PROJECT_NAME}") diff --git a/Tools/CMake/libraries/lungif.cmake b/Tools/CMake/libraries/lungif.cmake index 2ede4dd3a..a3db81c93 100644 --- a/Tools/CMake/libraries/lungif.cmake +++ b/Tools/CMake/libraries/lungif.cmake @@ -1,5 +1,6 @@ project(lungif) -addStaticLib("${libDir}/${PROJECT_NAME}") - addDef(_GBA_NO_FILEIO) + + +finishLibrary("${libDir}/${PROJECT_NAME}") diff --git a/Tools/CMake/libraries/opcode.cmake b/Tools/CMake/libraries/opcode.cmake index df94b0c7f..1b2a54e03 100644 --- a/Tools/CMake/libraries/opcode.cmake +++ b/Tools/CMake/libraries/opcode.cmake @@ -3,7 +3,7 @@ project(opcode) addPath("${libDir}/${PROJECT_NAME}") addPath("${libDir}/${PROJECT_NAME}/Ice") -addStaticLib() - addDef(TORQUE_OPCODE) addDef(ICE_NO_DLL) + +finishLibrary() diff --git a/Tools/CMake/libraries/pcre.cmake b/Tools/CMake/libraries/pcre.cmake index 0f86b50b2..c060fbdec 100644 --- a/Tools/CMake/libraries/pcre.cmake +++ b/Tools/CMake/libraries/pcre.cmake @@ -1,8 +1,10 @@ project(pcre) -addStaticLib("${libDir}/pcre") - addDef(PCRE_STATIC) addDef(HAVE_CONFIG_H) -set_property(TARGET pcre PROPERTY COMPILE_FLAGS /TP) #/TP = compile as C++ +finishLibrary("${libDir}/pcre") + +if(WIN32) + set_property(TARGET pcre PROPERTY COMPILE_FLAGS /TP) #/TP = compile as C++ +endif() \ No newline at end of file diff --git a/Tools/CMake/libraries/library_recast.cmake b/Tools/CMake/libraries/recast.cmake similarity index 50% rename from Tools/CMake/libraries/library_recast.cmake rename to Tools/CMake/libraries/recast.cmake index 677459606..cf0f4ff96 100644 --- a/Tools/CMake/libraries/library_recast.cmake +++ b/Tools/CMake/libraries/recast.cmake @@ -9,10 +9,10 @@ addPathRec( "${libDir}/recast/DetourCrowd/Source" ) addPathRec( "${libDir}/recast/DetourTileCache/Source" ) # Additional includes -include_directories( "${libDir}/recast/DebugUtils/Include" ) -include_directories( "${libDir}/recast/Recast/Include" ) -include_directories( "${libDir}/recast/Detour/Include" ) -include_directories( "${libDir}/recast/DetourTileCache/Include" ) -include_directories( "${libDir}/recast/DetourCrowd/Include" ) +addInclude( "${libDir}/recast/DebugUtils/Include" ) +addInclude( "${libDir}/recast/Recast/Include" ) +addInclude( "${libDir}/recast/Detour/Include" ) +addInclude( "${libDir}/recast/DetourTileCache/Include" ) +addInclude( "${libDir}/recast/DetourCrowd/Include" ) -addStaticLib() \ No newline at end of file +finishLibrary() \ No newline at end of file diff --git a/Tools/CMake/libraries/squish.cmake b/Tools/CMake/libraries/squish.cmake index acc3332bc..5b38bd8e7 100644 --- a/Tools/CMake/libraries/squish.cmake +++ b/Tools/CMake/libraries/squish.cmake @@ -1,3 +1,3 @@ project(squish) -addStaticLib("${libDir}/${PROJECT_NAME}") +finishLibrary("${libDir}/${PROJECT_NAME}") diff --git a/Tools/CMake/libraries/tinyxml.cmake b/Tools/CMake/libraries/tinyxml.cmake index c17dfbba1..1b59d54a8 100644 --- a/Tools/CMake/libraries/tinyxml.cmake +++ b/Tools/CMake/libraries/tinyxml.cmake @@ -1,3 +1,3 @@ project(tinyxml) -addStaticLib("${libDir}/${PROJECT_NAME}") +finishLibrary("${libDir}/${PROJECT_NAME}") diff --git a/Tools/CMake/libraries/zlib.cmake b/Tools/CMake/libraries/zlib.cmake index 6df47de04..7c5397b09 100644 --- a/Tools/CMake/libraries/zlib.cmake +++ b/Tools/CMake/libraries/zlib.cmake @@ -1,3 +1,3 @@ project(zlib) -addStaticLib("${libDir}/${PROJECT_NAME}") +finishLibrary("${libDir}/${PROJECT_NAME}") diff --git a/Tools/CMake/modules/module_hydra.cmake b/Tools/CMake/modules/module_hydra.cmake index a3139e6c2..2ef5c97bd 100644 --- a/Tools/CMake/modules/module_hydra.cmake +++ b/Tools/CMake/modules/module_hydra.cmake @@ -4,7 +4,7 @@ addPathRec( "${srcDir}/platform/input/razerHydra" ) # Includes -include_directories( "${TORQUE_RAZERHYDRA_SDK_PATH}/include" ) +addInclude( "${TORQUE_RAZERHYDRA_SDK_PATH}/include" ) # Install if( WIN32 ) diff --git a/Tools/CMake/modules/module_navigation.cmake b/Tools/CMake/modules/module_navigation.cmake index 512ed42f3..d25d7adad 100644 --- a/Tools/CMake/modules/module_navigation.cmake +++ b/Tools/CMake/modules/module_navigation.cmake @@ -1,16 +1,15 @@ # Navigation module -addRequiredDefinition( "TORQUE_NAVIGATION_ENABLED" ) -addRequiredLibrary( "libraries/library_recast.cmake" ) -addRequiredLink( "recast" ) +addDef( "TORQUE_NAVIGATION_ENABLED" ) +addLib( "recast" ) # files addPathRec( "${srcDir}/navigation" ) # include paths -include_directories( "${libDir}/recast/DebugUtils/Include" ) -include_directories( "${libDir}/recast/Recast/Include" ) -include_directories( "${libDir}/recast/Detour/Include" ) -include_directories( "${libDir}/recast/DetourTileCache/Include" ) -include_directories( "${libDir}/recast/DetourCrowd/Include" ) +addInclude( "${libDir}/recast/DebugUtils/Include" ) +addInclude( "${libDir}/recast/Recast/Include" ) +addInclude( "${libDir}/recast/Detour/Include" ) +addInclude( "${libDir}/recast/DetourTileCache/Include" ) +addInclude( "${libDir}/recast/DetourCrowd/Include" ) diff --git a/Tools/CMake/modules/module_oculusVR.cmake b/Tools/CMake/modules/module_oculusVR.cmake index 044154f7c..45f9b780f 100644 --- a/Tools/CMake/modules/module_oculusVR.cmake +++ b/Tools/CMake/modules/module_oculusVR.cmake @@ -4,12 +4,12 @@ addPathRec( "${srcDir}/platform/input/oculusVR" ) # Includes -include_directories( "${TORQUE_OCULUSVR_SDK_PATH}/LibOVR/Include" ) -include_directories( "${TORQUE_OCULUSVR_SDK_PATH}/LibOVR/Src" ) +addInclude( "${TORQUE_OCULUSVR_SDK_PATH}/LibOVR/Include" ) +addInclude( "${TORQUE_OCULUSVR_SDK_PATH}/LibOVR/Src" ) # Libs if( WIN32 ) link_directories( "${TORQUE_OCULUSVR_SDK_PATH}/LibOVR/Lib/Win32" ) - addRequiredLink( "libovr.lib" ) - addRequiredLink( "libovrd.lib" ) + addLib( "libovr" ) + addLib( "libovrd" ) endif() \ No newline at end of file diff --git a/Tools/CMake/template.cmake b/Tools/CMake/template.cmake index 8040cddb0..52faefe0e 100644 --- a/Tools/CMake/template.cmake +++ b/Tools/CMake/template.cmake @@ -4,14 +4,11 @@ # 1st thing: the project name project(pcre) -# 2nd: add the paths where the source code is +# add the paths where the source code is addPath("${libDir}/pcre") addPathRec("${libDir}/pcre") -# 3rd: add addStaticLib() -addStaticLib() - # then add definitions addDef(PCRE_STATIC) addDef(HAVE_CONFIG_H) @@ -19,3 +16,6 @@ addDef(HAVE_CONFIG_H) # and maybe more include paths addInclude(${libDir}/libvorbis/include) addInclude(${libDir}/libogg/include) + +# finally: add finishLibrary() +finishLibrary() diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 1e9fb9bf6..fcc24bad5 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -7,12 +7,12 @@ project(${TORQUE_APP_NAME}) ############################################################################### option(TORQUE_SFX_VORBIS "Vorbis Sound" ON) mark_as_advanced(TORQUE_SFX_VORBIS) +option(TORQUE_THEORA "Theora Video Support" ON) +mark_as_advanced(TORQUE_THEORA) option(TORQUE_ADVANCED_LIGHTING "Advanced Lighting" ON) mark_as_advanced(TORQUE_ADVANCED_LIGHTING) option(TORQUE_BASIC_LIGHTING "Basic Lighting" ON) mark_as_advanced(TORQUE_BASIC_LIGHTING) -option(TORQUE_THEORA "Theora Video Support" ON) -mark_as_advanced(TORQUE_THEORA) option(TORQUE_SFX_DirectX "DirectX Sound" ON) mark_as_advanced(TORQUE_SFX_DirectX) option(TORQUE_SFX_OPENAL "OpenAL Sound" ON) @@ -199,14 +199,17 @@ if(TORQUE_ADVANCED_LIGHTING) addPathRec("${srcDir}/lighting/shadowMap") addPathRec("${srcDir}/lighting/advanced/hlsl") #addPathRec("${srcDir}/lighting/advanced/glsl") + addDef(TORQUE_ADVANCED_LIGHTING) endif() if(TORQUE_BASIC_LIGHTING) addPathRec("${srcDir}/lighting/basic") addPathRec("${srcDir}/lighting/shadowMap") + addDef(TORQUE_BASIC_LIGHTING) endif() # DirectX Sound if(TORQUE_SFX_DirectX) + addLib(x3daudio.lib) addPathRec("${srcDir}/sfx/dsound") addPathRec("${srcDir}/sfx/xaudio") endif() @@ -216,6 +219,15 @@ if(TORQUE_SFX_OPENAL) addPath("${srcDir}/sfx/openal") #addPath("${srcDir}/sfx/openal/mac") addPath("${srcDir}/sfx/openal/win32") + addInclude("${libDir}/openal/win32") +endif() + +# Vorbis +if(TORQUE_SFX_VORBIS) + addInclude(${libDir}/libvorbis/include) + addDef(TORQUE_OGGVORBIS) + addLib(libvorbis) + addLib(libogg) endif() # Theora @@ -223,6 +235,11 @@ if(TORQUE_THEORA) addPath("${srcDir}/core/ogg") addPath("${srcDir}/gfx/video") addPath("${srcDir}/gui/theora") + + addDef(TORQUE_OGGTHEORA) + addDef(TORQUE_OGGVORIBS) + addInclude(${libDir}/libtheora/include) + addLib(libtheora) endif() # Include tools for non-tool builds (or define player if a tool build) @@ -236,10 +253,12 @@ endif() if(TORQUE_HIFI) addPath("${srcDir}/T3D/gameBase/hifi") + addDef(TORQUE_HIFI_NET) endif() if(TORQUE_EXTENDED_MOVE) addPath("${srcDir}/T3D/gameBase/extended") + addDef(TORQUE_EXTENDED_MOVE) else() addPath("${srcDir}/T3D/gameBase/std") endif() @@ -337,7 +356,7 @@ endif() ############################################################################### ############################################################################### -addExecutable() +finishExecutable() ############################################################################### ############################################################################### @@ -356,15 +375,15 @@ if(EXISTS "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/game/main.cs.in" AND CONFIGURE_FILE("${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/game/main.cs.in" "${projectOutDir}/main.cs") endif() if(WIN32) - if(NOT EXISTS "${projectSrcDir}/torque.rc") - CONFIGURE_FILE("${cmakeDir}/torque-win.rc.in" "${projectSrcDir}/torque.rc") - endif() - if(NOT EXISTS "${projectOutDir}/${PROJECT_NAME}-debug.bat") - CONFIGURE_FILE("${cmakeDir}/app-debug-win.bat.in" "${projectOutDir}/${PROJECT_NAME}-debug.bat") - endif() - if(NOT EXISTS "${projectOutDir}/cleanup.bat") - CONFIGURE_FILE("${cmakeDir}/cleanup-win.bat.in" "${projectOutDir}/cleanup.bat") - endif() + if(NOT EXISTS "${projectSrcDir}/torque.rc") + CONFIGURE_FILE("${cmakeDir}/torque-win.rc.in" "${projectSrcDir}/torque.rc") + endif() + if(NOT EXISTS "${projectOutDir}/${PROJECT_NAME}-debug.bat") + CONFIGURE_FILE("${cmakeDir}/app-debug-win.bat.in" "${projectOutDir}/${PROJECT_NAME}-debug.bat") + endif() + if(NOT EXISTS "${projectOutDir}/cleanup.bat") + CONFIGURE_FILE("${cmakeDir}/cleanup-win.bat.in" "${projectOutDir}/cleanup.bat") + endif() endif() ############################################################################### @@ -384,18 +403,17 @@ addLib(convexDecomp) if(WIN32) # copy pasted from T3D build system, some might not be needed - set(TORQUE_EXTERNAL_LIBS "COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;d3d9.lib;d3dx9.lib;DxErr.lib;ole32.lib;shell32.lib;oleaut32.lib;version.lib" CACHE STRING "external libs to link against") - mark_as_advanced(TORQUE_EXTERNAL_LIBS) + set(TORQUE_EXTERNAL_LIBS "COMCTL32.LIB;COMDLG32.LIB;USER32.LIB;ADVAPI32.LIB;GDI32.LIB;WINMM.LIB;WSOCK32.LIB;vfw32.lib;Imm32.lib;d3d9.lib;d3dx9.lib;DxErr.lib;ole32.lib;shell32.lib;oleaut32.lib;version.lib" CACHE STRING "external libs to link against") + mark_as_advanced(TORQUE_EXTERNAL_LIBS) addLib("${TORQUE_EXTERNAL_LIBS}") endif() ############################################################################### # Always enabled Definitions ############################################################################### -addDebugDef(TORQUE_DEBUG) -addDebugDef(TORQUE_ENABLE_ASSERTS) -addDebugDef(TORQUE_DEBUG_GFX_MODE) - +addDef(TORQUE_DEBUG DEBUG) +addDef(TORQUE_ENABLE_ASSERTS "DEBUG;RelWithDebInfo") +addDef(TORQUE_DEBUG_GFX_MODE "RelWithDebInfo") addDef(TORQUE_SHADERGEN) addDef(INITGUID) addDef(NTORQUE_SHARED) @@ -413,46 +431,6 @@ addDef(PCRE_STATIC) addDef(_CRT_SECURE_NO_WARNINGS) addDef(_CRT_SECURE_NO_DEPRECATE) - -############################################################################### -# Modules -############################################################################### -if(TORQUE_SFX_DirectX) - addLib(x3daudio.lib) -endif() - -if(TORQUE_ADVANCED_LIGHTING) - addDef(TORQUE_ADVANCED_LIGHTING) -endif() -if(TORQUE_BASIC_LIGHTING) - addDef(TORQUE_BASIC_LIGHTING) -endif() - -if(TORQUE_SFX_OPENAL) - addInclude("${libDir}/openal/win32") -endif() - -if(TORQUE_SFX_VORBIS) - addInclude(${libDir}/libvorbis/include) - addDef(TORQUE_OGGVORBIS) - addLib(libvorbis) - addLib(libogg) -endif() - -if(TORQUE_THEORA) - addDef(TORQUE_OGGTHEORA) - addDef(TORQUE_OGGVORIBS) - addInclude(${libDir}/libtheora/include) - addLib(libtheora) -endif() - -if(TORQUE_HIFI) - addDef(TORQUE_HIFI_NET) -endif() -if(TORQUE_EXTENDED_MOVE) - addDef(TORQUE_EXTENDED_MOVE) -endif() - ############################################################################### # Include Paths ############################################################################### From 17bd69e659e393546aea9d0b76671060dba3bff5 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Mon, 2 Jun 2014 02:15:55 +0200 Subject: [PATCH 071/317] CMake changes for dedicated builds. --- Tools/CMake/basics.cmake | 9 +- Tools/CMake/libraries/convexDecomp.cmake | 4 + Tools/CMake/libraries/libtheora.cmake | 14 ++- Tools/CMake/libraries/libvorbis.cmake | 4 + Tools/CMake/torque3d.cmake | 128 +++++++++++++++++++---- 5 files changed, 138 insertions(+), 21 deletions(-) diff --git a/Tools/CMake/basics.cmake b/Tools/CMake/basics.cmake index 2b4ff0278..f1da3e2bf 100644 --- a/Tools/CMake/basics.cmake +++ b/Tools/CMake/basics.cmake @@ -39,7 +39,8 @@ macro(addPath dir) ${dir}/*.cpp ${dir}/*.c ${dir}/*.cc - ${dir}/*.h) + ${dir}/*.h + ${dir}/*.asm) LIST(APPEND ${PROJECT_NAME}_files "${tmp_files}") LIST(APPEND ${PROJECT_NAME}_paths "${dir}") #message(STATUS "addPath ${PROJECT_NAME} : ${tmp_files}") @@ -351,6 +352,12 @@ else() set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${CMAKE_CXX_FLAGS}") endif() +if(UNIX) + SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${projectOutDir}") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY "${projectOutDir}") + SET(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${projectOutDir}") + set(CMAKE_LIBRARY_OUTPUT_DIRECTORY_DEBUG "${projectOutDir}") +endif() # fix the debug/release subfolders on windows if(MSVC) diff --git a/Tools/CMake/libraries/convexDecomp.cmake b/Tools/CMake/libraries/convexDecomp.cmake index 3b02db6df..3cb4c2950 100644 --- a/Tools/CMake/libraries/convexDecomp.cmake +++ b/Tools/CMake/libraries/convexDecomp.cmake @@ -1,3 +1,7 @@ project(convexDecomp) +if(UNIX) + addDef(LINUX) +endif() + finishLibrary("${libDir}/convexDecomp") diff --git a/Tools/CMake/libraries/libtheora.cmake b/Tools/CMake/libraries/libtheora.cmake index 023176aa4..3a95275dd 100644 --- a/Tools/CMake/libraries/libtheora.cmake +++ b/Tools/CMake/libraries/libtheora.cmake @@ -1,6 +1,18 @@ project(libtheora) -addPathRec("${libDir}/libtheora") +addPath( "${libDir}/libtheora" ) +addPathRec( "${libDir}/libtheora/include" ) +addPath( "${libDir}/libtheora/lib" ) +addPath( "${libDir}/libtheora/lib/dec" ) +addPath( "${libDir}/libtheora/lib/enc" ) + +if(WIN32) + addPath( "${libDir}/libtheora/lib/dec/x86_vc" ) + addPath( "${libDir}/libtheora/lib/enc/x86_32_vs" ) +else() + addPath( "${libDir}/libtheora/lib/dec/x86" ) + addPath( "${libDir}/libtheora/lib/enc/x86_32" ) +endif() addDef(TORQUE_OGGTHEORA) addDef(TORQUE_OGGVORIBS) diff --git a/Tools/CMake/libraries/libvorbis.cmake b/Tools/CMake/libraries/libvorbis.cmake index 5fe0248af..01d71a675 100644 --- a/Tools/CMake/libraries/libvorbis.cmake +++ b/Tools/CMake/libraries/libvorbis.cmake @@ -6,4 +6,8 @@ addDef(TORQUE_OGGVORBIS) addInclude(${libDir}/libvorbis/include) addInclude(${libDir}/libogg/include) +if(UNIX) + addInclude(${libDir}/libvorbis/lib) +endif() + finishLibrary() diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index fcc24bad5..c17009c1d 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -1,5 +1,16 @@ project(${TORQUE_APP_NAME}) +if(UNIX) + # default compiler flags + # force compile 32 bit + set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -m32 -Wall -Wundef -msse -pipe -Wfatal-errors ${TORQUE_ADDITIONAL_LINKER_FLAGS}") + set(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} -m32 -Wall -Wundef -msse -pipe -Wfatal-errors ${TORQUE_ADDITIONAL_LINKER_FLAGS}") + + # for asm files + SET (CMAKE_ASM_NASM_OBJECT_FORMAT "elf") + ENABLE_LANGUAGE (ASM_NASM) +endif() + # TODO: fmod support ############################################################################### @@ -13,8 +24,12 @@ option(TORQUE_ADVANCED_LIGHTING "Advanced Lighting" ON) mark_as_advanced(TORQUE_ADVANCED_LIGHTING) option(TORQUE_BASIC_LIGHTING "Basic Lighting" ON) mark_as_advanced(TORQUE_BASIC_LIGHTING) -option(TORQUE_SFX_DirectX "DirectX Sound" ON) -mark_as_advanced(TORQUE_SFX_DirectX) +if(WIN32) + option(TORQUE_SFX_DirectX "DirectX Sound" ON) + mark_as_advanced(TORQUE_SFX_DirectX) +else() + set(TORQUE_SFX_DirectX OFF) +endif() option(TORQUE_SFX_OPENAL "OpenAL Sound" ON) mark_as_advanced(TORQUE_SFX_OPENAL) option(TORQUE_HIFI "HIFI? support" OFF) @@ -23,6 +38,13 @@ option(TORQUE_EXTENDED_MOVE "Extended move support" OFF) mark_as_advanced(TORQUE_EXTENDED_MOVE) option(TORQUE_NAVIGATION "Enable Navigation module" OFF) #mark_as_advanced(TORQUE_NAVIGATION) +if(WIN32) + option(TORQUE_OPENGL "Allow OpenGL render" OFF) + #mark_as_advanced(TORQUE_OPENGL) +else() + set(TORQUE_OPENGL ON) # we need OpenGL to render on Linux/Mac + option(TORQUE_DEDICATED "Torque dedicated" OFF) +endif() #Oculus VR option(TORQUE_OCULUSVR "Enable OCULUSVR module" OFF) @@ -45,10 +67,18 @@ endif() ############################################################################### # options ############################################################################### +if(NOT MSVC) # handle single-configuration generator + set(TORQUE_BUILD_TYPE "Debug" CACHE STRING "Select one of Debug, Release and RelWithDebInfo") + set_property(CACHE TORQUE_BUILD_TYPE PROPERTY STRINGS "Debug" "Release" "RelWithDebInfo") + + set(TORQUE_ADDITIONAL_LINKER_FLAGS "" CACHE STRING "Additional linker flags") + mark_as_advanced(TORQUE_ADDITIONAL_LINKER_FLAGS) +endif() + option(TORQUE_MULTITHREAD "Multi Threading" ON) mark_as_advanced(TORQUE_MULTITHREAD) -option(TORQUE_DISABLE_MEMORY_MANAGER "Disable memory manager" OFF) +option(TORQUE_DISABLE_MEMORY_MANAGER "Disable memory manager" ON) mark_as_advanced(TORQUE_DISABLE_MEMORY_MANAGER) option(TORQUE_DISABLE_VIRTUAL_MOUNT_SYSTEM "Disable virtual mount system" OFF) @@ -130,7 +160,9 @@ addPath("${srcDir}/math/test") addPath("${srcDir}/platform") addPath("${srcDir}/cinterface") addPath("${srcDir}/platform/nativeDialogs") -addPath("${srcDir}/platform/menus") +if( NOT TORQUE_DEDICATED ) + addPath("${srcDir}/platform/menus") +endif() addPath("${srcDir}/platform/test") addPath("${srcDir}/platform/threads") addPath("${srcDir}/platform/async") @@ -197,8 +229,12 @@ addPathRec("${projectSrcDir}") if(TORQUE_ADVANCED_LIGHTING) addPath("${srcDir}/lighting/advanced") addPathRec("${srcDir}/lighting/shadowMap") - addPathRec("${srcDir}/lighting/advanced/hlsl") - #addPathRec("${srcDir}/lighting/advanced/glsl") + if(WIN32) + addPathRec("${srcDir}/lighting/advanced/hlsl") + endif() + if(TORQUE_OPENGL) + addPathRec("${srcDir}/lighting/advanced/glsl") + endif() addDef(TORQUE_ADVANCED_LIGHTING) endif() if(TORQUE_BASIC_LIGHTING) @@ -215,11 +251,17 @@ if(TORQUE_SFX_DirectX) endif() # OpenAL -if(TORQUE_SFX_OPENAL) +if(TORQUE_SFX_OPENAL AND NOT TORQUE_DEDICATED) addPath("${srcDir}/sfx/openal") #addPath("${srcDir}/sfx/openal/mac") - addPath("${srcDir}/sfx/openal/win32") - addInclude("${libDir}/openal/win32") + if(WIN32) + addPath("${srcDir}/sfx/openal/win32") + addInclude("${libDir}/openal/win32") + endif() + if(UNIX) + addPath("${srcDir}/sfx/openal/linux") + endif() + endif() # Vorbis @@ -275,6 +317,15 @@ if(TORQUE_HYDRA) include( "modules/module_hydra.cmake" ) endif() +if(TORQUE_DISABLE_MEMORY_MANAGER) + addDef(TORQUE_DISABLE_MEMORY_MANAGER) +endif() + +if(TORQUE_DEDICATED) + addDef(TORQUE_DEDICATED) +endif() + + ############################################################################### # platform specific things ############################################################################### @@ -339,19 +390,40 @@ if(PS3) endif() if(UNIX) - # linux_dedicated - addPath("${srcDir}/windowManager/dedicated") - # linux - addPath("${srcDir}/platformX86UNIX") + if(TORQUE_DEDICATED) + addPath("${srcDir}/windowManager/dedicated") + # ${srcDir}/platformX86UNIX/*.client.* files are not needed + # @todo: move to separate file + file( GLOB tmp_files + ${srcDir}/platformX86UNIX/*.cpp + ${srcDir}/platformX86UNIX/*.c + ${srcDir}/platformX86UNIX/*.cc + ${srcDir}/platformX86UNIX/*.h ) + file( GLOB tmp_remove_files ${srcDir}/platformX86UNIX/*client.* ) + LIST( REMOVE_ITEM tmp_files ${tmp_remove_files} ) + foreach( f ${tmp_files} ) + addFile( ${f} ) + endforeach() + else() + addPath("${srcDir}/platformX86UNIX") + endif() + addPath("${srcDir}/platformX86UNIX/threads") addPath("${srcDir}/platformPOSIX") - addPath("${srcDir}/gfx/gl") - addPath("${srcDir}/gfx/gl/ggl") - addPath("${srcDir}/gfx/gl/ggl/x11") # This one is not yet implemented! - addPath("${srcDir}/gfx/gl/ggl/generated") +endif() + +if( TORQUE_OPENGL ) addPath("${srcDir}/shaderGen/GLSL") - addPath("${srcDir}/terrain/glsl") - addPath("${srcDir}/forest/glsl") + if( TORQUE_OPENGL AND NOT TORQUE_DEDICATED ) + addPath("${srcDir}/gfx/gl") + addPath("${srcDir}/gfx/gl/tGL") + addPath("${srcDir}/terrain/glsl") + addPath("${srcDir}/forest/glsl") + endif() + + if(WIN32 AND NOT TORQUE_SDL) + addPath("${srcDir}/gfx/gl/win32") + endif() endif() ############################################################################### @@ -408,6 +480,15 @@ if(WIN32) addLib("${TORQUE_EXTERNAL_LIBS}") endif() +if(UNIX) + # copy pasted from T3D build system, some might not be needed + set(TORQUE_EXTERNAL_LIBS "dl Xxf86vm Xext X11 Xft stdc++ pthread GL" CACHE STRING "external libs to link against") + mark_as_advanced(TORQUE_EXTERNAL_LIBS) + + string(REPLACE " " ";" TORQUE_EXTERNAL_LIBS_LIST ${TORQUE_EXTERNAL_LIBS}) + addLib( "${TORQUE_EXTERNAL_LIBS_LIST}" ) +endif() + ############################################################################### # Always enabled Definitions ############################################################################### @@ -431,6 +512,10 @@ addDef(PCRE_STATIC) addDef(_CRT_SECURE_NO_WARNINGS) addDef(_CRT_SECURE_NO_DEPRECATE) +if(UNIX) + addDef(LINUX) +endif() + ############################################################################### # Include Paths ############################################################################### @@ -455,6 +540,11 @@ if(WIN32) set_property(TARGET ${PROJECT_NAME} APPEND PROPERTY INCLUDE_DIRECTORIES $ENV{DXSDK_DIR}/Include) endif() +if(UNIX) + addInclude("/usr/include/freetype2/freetype") + addInclude("/usr/include/freetype2") +endif() + ############################################################################### # Installation ############################################################################### From 82bb19e1b51456953cd0f815bca959676f0ebbbf Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Mon, 5 May 2014 19:57:04 +0200 Subject: [PATCH 072/317] Fix for error on Clang compiler: reinterpret_cast from 'const void *' to 'FunctionType *' (aka 'void (*)(EngineObject *, const char *, const char *)') casts away qualifiers return R( reinterpret_cast< FunctionType const* >( /*const_cast*/(mFn) )( mThis, a, b ) ); --- Engine/source/console/engineAPI.h | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/Engine/source/console/engineAPI.h b/Engine/source/console/engineAPI.h index d8b27ea82..2923c8bb4 100644 --- a/Engine/source/console/engineAPI.h +++ b/Engine/source/console/engineAPI.h @@ -2533,79 +2533,79 @@ struct _EngineCallbackHelper R call() const { typedef R( FunctionType )( EngineObject* ); - return R( reinterpret_cast< FunctionType* >( mFn )( mThis ) ); + return R( reinterpret_cast< FunctionType* >( const_cast(mFn) )( mThis ) ); } template< typename R, typename A > R call( A a ) const { typedef R( FunctionType )( EngineObject*, A ); - return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a ) ); + return R( reinterpret_cast< FunctionType* >( const_cast(mFn) )( mThis, a ) ); } template< typename R, typename A, typename B > R call( A a, B b ) const { typedef R( FunctionType )( EngineObject*, A, B ); - return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b ) ); + return R( reinterpret_cast< FunctionType* >( const_cast(mFn) )( mThis, a, b ) ); } template< typename R, typename A, typename B, typename C > R call( A a, B b, C c ) const { typedef R( FunctionType )( EngineObject*, A, B, C ); - return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c ) ); + return R( reinterpret_cast< FunctionType* >( const_cast(mFn) )( mThis, a, b, c ) ); } template< typename R, typename A, typename B, typename C, typename D > R call( A a, B b, C c, D d ) const { typedef R( FunctionType )( EngineObject*, A, B, C, D ); - return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d ) ); + return R( reinterpret_cast< FunctionType* >( const_cast(mFn) )( mThis, a, b, c, d ) ); } template< typename R, typename A, typename B, typename C, typename D, typename E > R call( A a, B b, C c, D d, E e ) const { typedef R( FunctionType )( EngineObject*, A, B, C, D, E ); - return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e ) ); + return R( reinterpret_cast< FunctionType* >( const_cast(mFn) )( mThis, a, b, c, d, e ) ); } template< typename R, typename A, typename B, typename C, typename D, typename E, typename F > R call( A a, B b, C c, D d, E e, F f ) const { typedef R( FunctionType )( EngineObject*, A, B, C, D, E, F ); - return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e, f ) ); + return R( reinterpret_cast< FunctionType* >( const_cast(mFn) )( mThis, a, b, c, d, e, f ) ); } template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G > R call( A a, B b, C c, D d, E e, F f, G g ) const { typedef R( FunctionType )( EngineObject*, A, B, C, D, E, F, G ); - return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e, f, g ) ); + return R( reinterpret_cast< FunctionType* >( const_cast(mFn) )( mThis, a, b, c, d, e, f, g ) ); } template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H > R call( A a, B b, C c, D d, E e, F f, G g, H h ) const { typedef R( FunctionType )( EngineObject*, A, B, C, D, E, F, G, H ); - return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e, f, g, h ) ); + return R( reinterpret_cast< FunctionType* >( const_cast(mFn) )( mThis, a, b, c, d, e, f, g, h ) ); } template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I > R call( A a, B b, C c, D d, E e, F f, G g, H h, I i ) const { typedef R( FunctionType )( EngineObject*, A, B, C, D, E, F, G, H, I ); - return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e, f, g, h, i ) ); + return R( reinterpret_cast< FunctionType* >( const_cast(mFn) )( mThis, a, b, c, d, e, f, g, h, i ) ); } template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J > R call( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j ) const { typedef R( FunctionType )( EngineObject*, A, B, C, D, E, F, G, H, I, J ); - return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e, f, g, h, i, j ) ); + return R( reinterpret_cast< FunctionType* >( const_cast(mFn) )( mThis, a, b, c, d, e, f, g, h, i, j ) ); } template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K > R call( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k ) const { typedef R( FunctionType )( EngineObject*, A, B, C, D, E, F, G, H, I, J, K ); - return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e, f, g, h, i, j, k ) ); + return R( reinterpret_cast< FunctionType* >( const_cast(mFn) )( mThis, a, b, c, d, e, f, g, h, i, j, k ) ); } template< typename R, typename A, typename B, typename C, typename D, typename E, typename F, typename G, typename H, typename I, typename J, typename K, typename L > R call( A a, B b, C c, D d, E e, F f, G g, H h, I i, J j, K k, L l ) const { typedef R( FunctionType )( EngineObject*, A, B, C, D, E, F, G, H, I, J, K, L l ); - return R( reinterpret_cast< FunctionType* >( mFn )( mThis, a, b, c, d, e, f, g, h, i, j, k, l ) ); + return R( reinterpret_cast< FunctionType* >( const_cast(mFn) )( mThis, a, b, c, d, e, f, g, h, i, j, k, l ) ); } }; From 1f085a8cd210dd5337922f68739a07f21878e226 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Mon, 5 May 2014 19:58:55 +0200 Subject: [PATCH 073/317] Fix error on Clang compiler: 'ival' is a protected member of 'Dictionary::Entry' --- Engine/source/console/consoleInternal.h | 2 ++ 1 file changed, 2 insertions(+) diff --git a/Engine/source/console/consoleInternal.h b/Engine/source/console/consoleInternal.h index 0a9c9060e..07c336d98 100644 --- a/Engine/source/console/consoleInternal.h +++ b/Engine/source/console/consoleInternal.h @@ -311,6 +311,8 @@ public: // NOTE: This is protected to ensure no one outside // of this structure is messing with it. + friend class Dictionary; + #pragma warning( push ) #pragma warning( disable : 4201 ) // warning C4201: nonstandard extension used : nameless struct/union From d0a64026b0f73ca305eb0e1327c8b1ff6e9c9898 Mon Sep 17 00:00:00 2001 From: Thomas Fischer Date: Mon, 2 Jun 2014 13:41:00 +0200 Subject: [PATCH 074/317] removed build status icon this is a very bad idea for forks. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index cafae3be7..a461b5db0 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Torque 3D v3.5.1 ![Build status](http://builds.garagegames.com/app/rest/builds/buildType:(id:bt79)/statusIcon) +Torque 3D v3.5.1 ================ MIT Licensed Open Source version of [Torque 3D](http://www.garagegames.com/products/torque-3d) from [GarageGames](http://www.garagegames.com) From 87ba9a70843a1b9a46389274aded6e4e74dd2442 Mon Sep 17 00:00:00 2001 From: rextimmy Date: Sat, 7 Jun 2014 12:11:44 +1000 Subject: [PATCH 075/317] code cleanup --- Engine/source/T3D/physics/physx3/px3Body.cpp | 24 +------------------ Engine/source/T3D/physics/physx3/px3Body.h | 2 +- Engine/source/T3D/physics/physx3/px3World.cpp | 21 +--------------- README.md | 14 +++++++++++ 4 files changed, 17 insertions(+), 44 deletions(-) diff --git a/Engine/source/T3D/physics/physx3/px3Body.cpp b/Engine/source/T3D/physics/physx3/px3Body.cpp index 4adc39a3c..026309f08 100644 --- a/Engine/source/T3D/physics/physx3/px3Body.cpp +++ b/Engine/source/T3D/physics/physx3/px3Body.cpp @@ -147,10 +147,6 @@ bool Px3Body::init( PhysicsCollision *shape, { physx::PxRigidDynamic *actor = mActor->is(); physx::PxRigidBodyExt::setMassAndUpdateInertia(*actor,mass); - if(mBodyFlags & BF_CCD) - actor->setRigidBodyFlag(physx::PxRigidBodyFlag::eENABLE_CCD, true); - else - actor->setRigidBodyFlag(physx::PxRigidBodyFlag::eENABLE_CCD, false); } // This sucks, but it has to happen if we want @@ -223,7 +219,7 @@ void Px3Body::getState( PhysicsState *outState ) outState->linVelocity = px3Cast( actor->getLinearVelocity() ); outState->angVelocity = px3Cast( actor->getAngularVelocity() ); outState->sleeping = actor->isSleeping(); - outState->momentum = px3Cast( (1.0f/actor->getMass()) * actor->getLinearVelocity() );//?? + outState->momentum = px3Cast( (1.0f/actor->getMass()) * actor->getLinearVelocity() ); } @@ -368,24 +364,6 @@ void Px3Body::setSimulationEnabled( bool enabled ) delete [] shapes; } - -void Px3Body::moveKinematicTo( const MatrixF &transform ) -{ - AssertFatal( mActor, "Px3Body::moveKinematicTo - The actor is null!" ); - - const bool isKinematic = mBodyFlags & BF_KINEMATIC; - if (!isKinematic ) - { - Con::errorf("Px3Body::moveKinematicTo is only for kinematic bodies."); - return; - } - - mWorld->releaseWriteLock(); - - physx::PxRigidDynamic *actor = mActor->is(); - actor->setKinematicTarget(px3Cast(transform)); -} - void Px3Body::setTransform( const MatrixF &transform ) { AssertFatal( mActor, "Px3Body::setTransform - The actor is null!" ); diff --git a/Engine/source/T3D/physics/physx3/px3Body.h b/Engine/source/T3D/physics/physx3/px3Body.h index 4fcb223d1..79096f57b 100644 --- a/Engine/source/T3D/physics/physx3/px3Body.h +++ b/Engine/source/T3D/physics/physx3/px3Body.h @@ -88,7 +88,6 @@ public: // PhysicsObject virtual PhysicsWorld* getWorld(); virtual void setTransform( const MatrixF &xfm ); - virtual void moveKinematicTo( const MatrixF &xfm ); virtual MatrixF& getTransform( MatrixF *outMatrix ); virtual Box3F getWorldBounds(); virtual void setSimulationEnabled( bool enabled ); @@ -100,6 +99,7 @@ public: U32 bodyFlags, SceneObject *obj, PhysicsWorld *world ); + virtual bool isDynamic() const; virtual PhysicsCollision* getColShape(); virtual void setSleepThreshold( F32 linear, F32 angular ); diff --git a/Engine/source/T3D/physics/physx3/px3World.cpp b/Engine/source/T3D/physics/physx3/px3World.cpp index a7617a723..4f333b733 100644 --- a/Engine/source/T3D/physics/physx3/px3World.cpp +++ b/Engine/source/T3D/physics/physx3/px3World.cpp @@ -54,23 +54,6 @@ physx::PxDefaultAllocator Px3World::smMemoryAlloc; F32 Px3World::smPhysicsStepTime = 1.0f/(F32)TickMs; U32 Px3World::smPhysicsMaxIterations = 4; -//filter shader with support for CCD pairs -static physx::PxFilterFlags sCcdFilterShader( - physx::PxFilterObjectAttributes attributes0, - physx::PxFilterData filterData0, - physx::PxFilterObjectAttributes attributes1, - physx::PxFilterData filterData1, - physx::PxPairFlags& pairFlags, - const void* constantBlock, - physx::PxU32 constantBlockSize) -{ - pairFlags = physx::PxPairFlag::eRESOLVE_CONTACTS; - pairFlags |= physx::PxPairFlag::eCCD_LINEAR; - return physx::PxFilterFlags(); -} - - - Px3World::Px3World(): mScene( NULL ), mProcessList( NULL ), mIsSimulating( false ), @@ -180,7 +163,6 @@ bool Px3World::restartSDK( bool destroyOnly, Px3World *clientWorld, Px3World *se return false; } - //just for testing-must remove, should really be enabled via console like physx 2 plugin #ifdef TORQUE_DEBUG physx::PxVisualDebuggerConnectionFlags connectionFlags(physx::PxVisualDebuggerExt::getAllConnectionFlags()); smPvdConnection = physx::PxVisualDebuggerExt::createConnection(gPhysics3SDK->getPvdConnectionManager(), @@ -241,8 +223,7 @@ bool Px3World::initWorld( bool isServer, ProcessList *processList ) sceneDesc.flags |= physx::PxSceneFlag::eENABLE_CCD; sceneDesc.flags |= physx::PxSceneFlag::eENABLE_ACTIVETRANSFORMS; - - sceneDesc.filterShader = sCcdFilterShader; + sceneDesc.filterShader = physx::PxDefaultSimulationFilterShader; mScene = gPhysics3SDK->createScene(sceneDesc); diff --git a/README.md b/README.md index 93e4b09b4..c06f58f4e 100644 --- a/README.md +++ b/README.md @@ -1,5 +1,10 @@ +<<<<<<< HEAD Torque 3D v3.5 - PhysX 3.3 Basic Plugin ========================== +======= +Torque 3D v3.5.1 +================ +>>>>>>> GarageGames/development This is a basic PhysX 3.3 plugin that does not contain any added features like cloth,particles and CCD. This plugin provides no more features than the bullet plugin and can therefore be used as a drop in alternative. It does not modify any files outside of the physx3 folder. A far more advanced physx3 plugin with CCD, cloth and particles can be found on this repository under the physx3 branch. @@ -25,6 +30,7 @@ Setting up PhysX 3.3 manually The following libraries will also be needed: +<<<<<<< HEAD Release , Debug - PhysX3_x86.lib,PhysX3CHECKED_x86.lib @@ -39,6 +45,14 @@ Release , Debug With debug build feel free to change CHECKED to DEBUG if you prefer but it will still require the CHECKED dll's though. Running a project +======= +* [Complete Torque 3D 3.5.1 zip package](http://mit.garagegames.com/Torque3D-3-5-1.zip) with updated TorqueScript documentation, the *Project Manager*, and compiled versions of the templates. +* [Torque 3D Project Manager v2.1](http://mit.garagegames.com/T3DProjectManager-2-1.zip) on its own for use in your T3D forks. + +If you're looking for an older release see the [Torque 3D Archive](https://github.com/GarageGames/Torque3D/wiki/Torque-3D-Archive) + +Creating a New Project Based on a Template +>>>>>>> GarageGames/development ------------------------------------------ - To run a release project you will need the following from the SDK bin folder: From 2ddce1a9b3617c45b69334d191153c46bd04ba46 Mon Sep 17 00:00:00 2001 From: rextimmy Date: Sat, 7 Jun 2014 12:14:00 +1000 Subject: [PATCH 076/317] README update --- README.md | 8 +------- 1 file changed, 1 insertion(+), 7 deletions(-) diff --git a/README.md b/README.md index c06f58f4e..b2199e922 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,5 @@ -<<<<<<< HEAD -Torque 3D v3.5 - PhysX 3.3 Basic Plugin +Torque 3D v3.5.1 - PhysX 3.3 Basic Plugin ========================== -======= -Torque 3D v3.5.1 -================ ->>>>>>> GarageGames/development This is a basic PhysX 3.3 plugin that does not contain any added features like cloth,particles and CCD. This plugin provides no more features than the bullet plugin and can therefore be used as a drop in alternative. It does not modify any files outside of the physx3 folder. A far more advanced physx3 plugin with CCD, cloth and particles can be found on this repository under the physx3 branch. @@ -30,7 +25,6 @@ Setting up PhysX 3.3 manually The following libraries will also be needed: -<<<<<<< HEAD Release , Debug - PhysX3_x86.lib,PhysX3CHECKED_x86.lib From 1ca3190994c01c0b42bd257f12acb7555d2e750e Mon Sep 17 00:00:00 2001 From: rextimmy Date: Sat, 7 Jun 2014 12:23:12 +1000 Subject: [PATCH 077/317] Removed old px3Cast --- Engine/source/T3D/physics/physx3/px3Cast.h | 137 --------------------- 1 file changed, 137 deletions(-) delete mode 100644 Engine/source/T3D/physics/physx3/px3Cast.h diff --git a/Engine/source/T3D/physics/physx3/px3Cast.h b/Engine/source/T3D/physics/physx3/px3Cast.h deleted file mode 100644 index 971e830c8..000000000 --- a/Engine/source/T3D/physics/physx3/px3Cast.h +++ /dev/null @@ -1,137 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#ifndef _PHYSX3_CASTS_H_ -#define _PHYSX3_CASTS_H_ - -#ifndef _MPOINT3_H_ -#include "math/mPoint3.h" -#endif -#ifndef _MMATRIX_H_ -#include "math/mMatrix.h" -#endif -#ifndef _MBOX_H_ -#include "math/mBox.h" -#endif -#ifndef _MQUAT_H_ -#include "math/mQuat.h" -#endif -#ifndef _MTRANSFORM_H_ -#include "math/mTransform.h" -#endif - - -template inline T px3Cast( const F &from ); - -//------------------------------------------------------------------------- - -template<> -inline Point3F px3Cast( const physx::PxVec3 &vec ) -{ - return Point3F( vec.x, vec.y, vec.z ); -} - -template<> -inline physx::PxVec3 px3Cast( const Point3F &point ) -{ - return physx::PxVec3( point.x, point.y, point.z ); -} -//------------------------------------------------------------------------- -template<> -inline QuatF px3Cast( const physx::PxQuat &quat ) -{ - /// The Torque quat has the opposite winding order. - return QuatF( -quat.x, -quat.y, -quat.z, quat.w ); -} - -template<> -inline physx::PxQuat px3Cast( const QuatF &quat ) -{ - /// The Torque quat has the opposite winding order. - physx::PxQuat result( -quat.x, -quat.y, -quat.z, quat.w ); - return result; -} -//------------------------------------------------------------------------- - -template<> -inline physx::PxExtendedVec3 px3Cast( const Point3F &point ) -{ - return physx::PxExtendedVec3( point.x, point.y, point.z ); -} - -template<> -inline Point3F px3Cast( const physx::PxExtendedVec3 &xvec ) -{ - return Point3F( xvec.x, xvec.y, xvec.z ); -} - -//------------------------------------------------------------------------- - -template<> -inline physx::PxBounds3 px3Cast( const Box3F &box ) -{ - physx::PxBounds3 bounds(px3Cast(box.minExtents), - px3Cast(box.maxExtents)); - return bounds; -} - -template<> -inline Box3F px3Cast( const physx::PxBounds3 &bounds ) -{ - return Box3F( bounds.minimum.x, - bounds.minimum.y, - bounds.minimum.z, - bounds.maximum.x, - bounds.maximum.y, - bounds.maximum.z ); -} - -//------------------------------------------------------------------------- - -template<> -inline physx::PxTransform px3Cast( const MatrixF &xfm ) -{ - physx::PxTransform out; - QuatF q; - q.set(xfm); - out.q = px3Cast(q); - out.p = px3Cast(xfm.getPosition()); - return out; -} - -template<> -inline TransformF px3Cast(const physx::PxTransform &xfm) -{ - TransformF out(px3Cast(xfm.p),AngAxisF(px3Cast(xfm.q))); - return out; -} - -template<> -inline MatrixF px3Cast( const physx::PxTransform &xfm ) -{ - MatrixF out; - TransformF t = px3Cast(xfm); - out = t.getMatrix(); - return out; -} - -#endif From b0c83601d1bdc1a1eb5a7f6b64ddd8d4360d9a32 Mon Sep 17 00:00:00 2001 From: rextimmy Date: Sat, 7 Jun 2014 12:32:42 +1000 Subject: [PATCH 078/317] Readme fixup --- README.md | 175 ++++++++++++++++++++++++++++++++++++++---------------- 1 file changed, 124 insertions(+), 51 deletions(-) diff --git a/README.md b/README.md index b2199e922..d2085998f 100644 --- a/README.md +++ b/README.md @@ -1,65 +1,138 @@ -Torque 3D v3.5.1 - PhysX 3.3 Basic Plugin -========================== +Torque 3D v3.5.1 +================ -This is a basic PhysX 3.3 plugin that does not contain any added features like cloth,particles and CCD. This plugin provides no more features than the bullet plugin and can therefore be used as a drop in alternative. It does not modify any files outside of the physx3 folder. A far more advanced physx3 plugin with CCD, cloth and particles can be found on this repository under the physx3 branch. +MIT Licensed Open Source version of [Torque 3D](http://www.garagegames.com/products/torque-3d) from [GarageGames](http://www.garagegames.com) -Setting up PhysX 3.3 using the Torque 3D Project Manager ------------------------------------------- - - You can find a pre compiled binary of the Torque3D Project Manager that supports PhysX 3.3 here: http://www.narivtech.com/downloads/T3DProjectManager-2-1-devel.zip and source code here: https://github.com/rextimmy/Torque3D-ProjectManager/tree/development - - For the Project Manager to find PhysX 3.3 SDK you have two options 1)Create an environment variable called TORQUE_PHYSX3_PATH and have that pointing to the location you installed the SDK 2)Place the SDK into a folder called "Program Files"/NVIDIA Corporation/NVIDIA PhysX SDK/v3.3.0_win - - Simply choose PhysX 3.3 physics from the modules list in the project manager and everything should be automatically taken care of. +More Information +---------------- -Setting up PhysX 3.3 manually ------------------------------------------- +* Torque 3D [main repository](https://github.com/GarageGames/Torque3D) +* Torque 3D [GitHub Wiki](https://github.com/GarageGames/Torque3D/wiki) +* Documentation is in the [Torque3D-Documentation](https://github.com/GarageGames/Torque3D-Documentation) GitHub repo. +* Project Manager is in the [Torque3D-ProjectManager](https://github.com/GarageGames/Torque3D-ProjectManager) GitHub repo. +* T3D [Beginner's Forum](http://www.garagegames.com/community/forums/73) +* T3D [Professional Forum](http://www.garagegames.com/community/forums/63) +* Torque 3D [FPS Tutorial](http://www.garagegames.com/products/torque-3d/fps#/1-setup/1) +* GarageGames [Store](http://www.garagegames.com/products) +* GarageGames [Professional Services](http://services.garagegames.com/) - - You will need the latest SDK from NVIDIA. This requires signing up for their developer program. If you don't already have access to their developer site then sign up now as access is not immediate. - - Set up a standard Torque3D project, don't include any PhysX or Bullet, just regular Torque Physics in project manager options (if you're using it) - - Generate Projects and open the source code in Visual Studio ( or the IDE of your choice ) - - In the solution explorer in the DLL for your project you should find Source Files -> Engine -> T3D -> physics - - Add a new filter "physx3" and then right click on it and add existing item - - Add all the files found under Engine\Source\T3D\physics\physx3\ - - Now you need to add the PhysX SDK. - - Under the properties for the DLL project, under Linker -> Additional Library Directories add the lib\win32 directory for the PhysX 3.3 SDK. For example, mine is in: C:\Program Files (x86)\NVIDIA Corporation\NVIDIA PhysX SDK\v3.3.0_win\Lib\win32 - - In the same window under C/C++ you should see Additional Include Directories, you need to add the Include directory for the PhysX 3.3 SDK. For example, mine is in: C:\Program Files %28x86%29\NVIDIA Corporation\NVIDIA PhysX SDK\v3.3.0_win\Include - - You should now be able to compile now without any issues. +Pre-compiled Version +-------------------- -The following libraries will also be needed: +In addition to GitHub we also have a couple of pre-packaged files for you to download if you would prefer to not compile the code yourself: -Release , Debug - - - PhysX3_x86.lib,PhysX3CHECKED_x86.lib - - PhysX3Common_x86.lib,PhysX3CommonCHECKED_x86.lib - - PhysX3Extensions.lib,PhysX3ExtensionsCHECKED.lib - - PhysX3Cooking_x86.lib,PhysX3CookingCHECKED_x86.lib - - PxTask.lib,PxTaskCHECKED.lib - - PhysX3CharacterKinematic_x86.lib,PhysX3CharacterKinematicCHECKED_x86.lib - - PhysXVisualDebuggerSDK.lib, PhysXVisualDebuggerSDKCHECKED.lib - - PhysXProfileSDK.lib, PhysXProfileSDKCHECKED.lib - -With debug build feel free to change CHECKED to DEBUG if you prefer but it will still require the CHECKED dll's though. - -Running a project -======= * [Complete Torque 3D 3.5.1 zip package](http://mit.garagegames.com/Torque3D-3-5-1.zip) with updated TorqueScript documentation, the *Project Manager*, and compiled versions of the templates. * [Torque 3D Project Manager v2.1](http://mit.garagegames.com/T3DProjectManager-2-1.zip) on its own for use in your T3D forks. If you're looking for an older release see the [Torque 3D Archive](https://github.com/GarageGames/Torque3D/wiki/Torque-3D-Archive) Creating a New Project Based on a Template ->>>>>>> GarageGames/development ------------------------------------------ - - To run a release project you will need the following from the SDK bin folder: - 1. PhysX3_x86.dll - 2. PhysX3CharacterKinematic_x86.dll - 3. PhysX3Common_x86.dll - 4. PhysX3Cooking_x86.dll - - - To run a debug project you will need the following from the SDK bin folder: - 1. PhysX3CHECKED_x86.dll - 2. nvToolsExt32_1.dll - 3. PhysX3CookingCHECKED_x86.dll - 4. PhysX3CommonCHECKED_x86.dll - 5. PhysX3CharacterKinematicCHECKED_x86.dll - -Place these files along side the exe and this should get you up and running. +The templates included with Torque 3D provide a starting point for your project. Once we have created our own project based on a template we may then compile an executable and begin work on our game. The following templates are included in this version of Torque 3D: + +* Empty +* Full + +### Using PhysX ### + +If you plan on creating a project that uses PhysX you will first need to have the PhysX SDK intalled on your computer. Without the PhysX SDK in place the project generation step will fail when using either the *Project Manager* or manual project generation methods. + +PhysX SDK version 2.8.4.6 is required for Torque 3D's PhysX templates. The following steps are used to install this SDK: + +1. In a web browser, go to the [NVidia Support Center](http://supportcenteronline.com/ics/support/default.asp?deptID=1949) +2. If you do not have an account, you will need to register with them to have the support staff create an account for you. +3. If you have an account, login. +4. On the middle of the page, on the right, click on Downloads. +5. On the far right column, under Old downloads, click More. +6. Download the Windows 2.8.4.6 version. +7. Run the installer and follow the steps to install it in the default location. +8. Depending on your operating system version, you may need to reboot after the installation. + +### Using the Project Manager to Create a Project ### + +The *Project Manager* may be used to create a new game project based on one of the templates that are included with Torque 3D. If you are using Torque 3D directly from the [GitHub](https://github.com/GarageGames/Torque3D) repository then you will need to get the *Project Manager* from the [Torque3D-ProjectManager](https://github.com/GarageGames/Torque3D-ProjectManager) repo, or download a pre-compiled version from the [Project Manager Wiki Page](https://github.com/GarageGames/Torque3D/wiki/Project-Manager). + +The following steps use the *Project Manager* to create a new project. This is a quick summary and more detailed instructions may be found on the [Project Manager Wiki Page](https://github.com/GarageGames/Torque3D/wiki/Project-Manager) + +1. Run the *Project Manager*. +2. Click the *New Project* button. +3. Choose a template from the drop down on the right. +4. Pick which modules you want to compile into your project by using the *Choose Modules* button. +5. Give the project a name. +6. Click the *Create* button to create the project. This will open a new dialog window that shows the progress. +7. When it finishes, click the *Finished* button. +8. You may click on the *Open Folder* button to go to the project's directory. + +### Manually Creating a Project ### + +We may also manually create a project based on a template. The following steps outline how to do this: + +1. Open the *Templates* directory. +2. Right-click on the template you would like to use and choose *Copy*. +3. Go to the *My Projects* directory and paste the template there. +4. Rename the pasted template to the name of your project/game. +5. Go into your project's *game* directory and rename all executables, DLL files and the .torsion file (and maybe .torsion.opt) from the template name to that of your project (these files may not be present at this time). +6. Open the .torsion file in a text editor and replace all references to the template's name with that of your project (you only need to do this if you plan on using Torsion). You will need to also do this with the .torsion.opt if it exists. +7. Open you project's *source/torqueConfig.h* file in a text editor and change the `TORQUE_APP_NAME` define to the name of your project. +8. In your project's *buildFiles/config* directory open each .conf file and find each reference to the template's name and replace it with the name of your project. +9. Open your project's *game/main.cs* file in a text editor and change the `$appName` assignment to the name of your project. +10. Go to your project's directory and double click on the *generateProjects.bat* to create your project's solution files. + +Compiling Torque 3D (Windows) +----------------------------- +If this is the first time you will compile Torque 3D, or if you have added or removed any files to either the standard Torque 3D *Engine/source* directory or your project's *source* directory, you will need to run your project's *generateProjects.bat* file. This will rebuild your project's solution and project files. Now follow these steps to compile Torque 3D: + +1. Navigate to your project's *buildFiles/VisualStudio 2010* directory (or the *2008* directory if that is the version of Visual Studio you are using). +2. Double click on your project's .sln file. This will open Visual Studio. +3. When Visual Studio has fully loaded, press `F7` to start compiling your project. + +Compiling Torque 3D (Linux) +----------------------------- +This version of Torque 3D supports being run as a dedicated server under Linux. As with a Windows build you will need to run your project's *generateProjects.command* file to properly generate the required make file. + +Prior to compiling Torque 3D under Linux, you will need to make sure you have the appropriate libraries and tools installed. The exact packages will depend on which Linux distribution you are using. For example, under Ubuntu you will need: + +* build-essential +* nasm +* git +* php5-cli +* libsdl-dev +* libogg-dev + +With everything in place you may now follow these steps to compile Torque 3D: + +1. Change to you project's *buildFiles/Make_Ded* directory. +2. Enter the `make clean` command. +3. Enter the either the `make debug` or `make release` command depending on the type of build you wish to make. +4. Go to your project's *game* directory. +5. To start your game enter the following command (we'll use the name *MyGame* as the example project name): + `./MyGame -dedicated -mission "levels/Empty Terrain.mis"` + where the argument after the `-mission` switch is the path to the mission to load. + +Accessing the Editors +--------------------- +From the Main Menu or while in-game, pressing F10 opens the GUI Editor and pressing F11 opens the World Editor. + +License +------- + +Copyright (c) 2012 GarageGames, LLC + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to +deal in the Software without restriction, including without limitation the +rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +sell copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +IN THE SOFTWARE. \ No newline at end of file From de6e5e20b74bb352232727f25a5a4019bb5cffd6 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Mon, 9 Jun 2014 22:59:16 +1000 Subject: [PATCH 079/317] Fix random number generation. --- Engine/source/gui/worldEditor/terrainEditor.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Engine/source/gui/worldEditor/terrainEditor.cpp b/Engine/source/gui/worldEditor/terrainEditor.cpp index 617bb130a..223edfb80 100644 --- a/Engine/source/gui/worldEditor/terrainEditor.cpp +++ b/Engine/source/gui/worldEditor/terrainEditor.cpp @@ -34,7 +34,6 @@ #include "gui/core/guiCanvas.h" #include "gui/worldEditor/terrainActions.h" #include "terrain/terrMaterial.h" -#include @@ -2877,12 +2876,7 @@ void TerrainEditor::autoMaterialLayer( F32 mMinHeight, F32 mMaxHeight, F32 mMinS S32 mat = getPaintMaterialIndex(); if (mat == -1) return; - - //setup for randomized coverage - mCoverage*=100; - srand((unsigned)time(0)); - int randomNumber; - + mUndoSel = new Selection; U32 terrBlocks = mActiveTerrain->getBlockSize(); @@ -2901,8 +2895,7 @@ void TerrainEditor::autoMaterialLayer( F32 mMinHeight, F32 mMaxHeight, F32 mMinS if (gi.mMaterial == mat) continue; - randomNumber = (rand() % 10000); - if (randomNumber > mCoverage) + if (mRandI(0, 100) > mCoverage) continue; Point3F wp; From 709bd99ed498ef942fb53613f0814d03ebdcb215 Mon Sep 17 00:00:00 2001 From: rextimmy Date: Tue, 10 Jun 2014 09:06:21 +1000 Subject: [PATCH 080/317] Vehicle gamepad fix for full template. --- Templates/Full/game/scripts/client/client.cs | 3 +++ Templates/Full/game/scripts/client/default.bind.cs | 8 ++++++++ 2 files changed, 11 insertions(+) diff --git a/Templates/Full/game/scripts/client/client.cs b/Templates/Full/game/scripts/client/client.cs index eff6c923a..689b79dff 100644 --- a/Templates/Full/game/scripts/client/client.cs +++ b/Templates/Full/game/scripts/client/client.cs @@ -145,6 +145,9 @@ function clientCmdtoggleVehicleMap(%toggle) if(%toggle) { moveMap.pop(); + // clear movement + $mvForwardAction = 0; + $mvBackwardAction = 0; vehicleMap.push(); } else diff --git a/Templates/Full/game/scripts/client/default.bind.cs b/Templates/Full/game/scripts/client/default.bind.cs index 803124bc0..17092d5b2 100644 --- a/Templates/Full/game/scripts/client/default.bind.cs +++ b/Templates/Full/game/scripts/client/default.bind.cs @@ -734,3 +734,11 @@ vehicleMap.bindCmd(keyboard, "escape", "", "handleEscape();"); vehicleMap.bind( keyboard, v, toggleFreeLook ); // v for vanity //vehicleMap.bind(keyboard, tab, toggleFirstPerson ); vehicleMap.bind(keyboard, "alt c", toggleCamera); +// bind the left thumbstick for steering +vehicleMap.bind( gamepad, thumblx, "D", "-0.23 0.23", gamepadYaw ); +// bind the gas, break, and reverse buttons +vehicleMap.bind( gamepad, btn_a, moveforward ); +vehicleMap.bind( gamepad, btn_b, brake ); +vehicleMap.bind( gamepad, btn_x, movebackward ); +// bind exiting the vehicle to a button +vehicleMap.bindCmd(gamepad, btn_y,"getout();",""); From 8f550d5f8ce5c12dae432a6d40e554e599316b0d Mon Sep 17 00:00:00 2001 From: rextimmy Date: Tue, 10 Jun 2014 21:27:17 +1000 Subject: [PATCH 081/317] Changed method of releasing a physics body with physx 2.8 --- Engine/source/T3D/physics/physx/pxWorld.cpp | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/Engine/source/T3D/physics/physx/pxWorld.cpp b/Engine/source/T3D/physics/physx/pxWorld.cpp index c4f33a21d..0ec3d7fea 100644 --- a/Engine/source/T3D/physics/physx/pxWorld.cpp +++ b/Engine/source/T3D/physics/physx/pxWorld.cpp @@ -448,9 +448,8 @@ void PxWorld::releaseActor( NxActor &actor ) // Clear the userdata. actor.userData = NULL; - // If the scene is not simulating then we have the - // write lock and can safely delete it now. - if ( !mIsSimulating ) + // actors are one of the few objects that are stable removing this way in physx 2.8 + if (mScene->isWritable() ) { mScene->releaseActor( actor ); } From 416c50690e5307fa4a2dfd2c5d9a86389fe9f747 Mon Sep 17 00:00:00 2001 From: rextimmy Date: Tue, 10 Jun 2014 22:40:30 +1000 Subject: [PATCH 082/317] Bullet 2.82 update --- Engine/lib/bullet/CMakeLists.txt | 27 +- Engine/lib/bullet/COPYING | 17 + Engine/lib/bullet/ChangeLog | 4 + Engine/lib/bullet/RELEASING.TXT | 8 +- Engine/lib/bullet/VERSION | 2 +- Engine/lib/bullet/configure.ac | 6 +- .../BroadphaseCollision/btDispatcher.h | 5 +- .../btOverlappingPairCache.cpp | 2 +- .../btOverlappingPairCache.h | 11 +- .../BroadphaseCollision/btQuantizedBvh.cpp | 18 + .../bullet/src/BulletCollision/CMakeLists.txt | 9 +- .../btCollisionConfiguration.h | 2 - .../btCollisionDispatcher.cpp | 4 +- .../CollisionDispatch/btCollisionObject.cpp | 3 +- .../CollisionDispatch/btCollisionObject.h | 37 +- .../btCollisionObjectWrapper.h | 7 +- .../CollisionDispatch/btCollisionWorld.cpp | 89 +- .../CollisionDispatch/btCollisionWorld.h | 20 +- .../btCompoundCollisionAlgorithm.cpp | 13 +- .../btCompoundCollisionAlgorithm.h | 13 + .../btCompoundCompoundCollisionAlgorithm.cpp | 421 ++++ .../btCompoundCompoundCollisionAlgorithm.h | 90 + .../btConvex2dConvex2dAlgorithm.cpp | 1 - .../btConvexConcaveCollisionAlgorithm.cpp | 20 +- .../btConvexConvexAlgorithm.cpp | 1 - .../btDefaultCollisionConfiguration.cpp | 32 +- .../btDefaultCollisionConfiguration.h | 14 +- .../btHashedSimplePairCache.cpp | 278 +++ .../btHashedSimplePairCache.h | 174 ++ .../CollisionShapes/btBvhTriangleMeshShape.h | 8 +- .../CollisionShapes/btCompoundShape.cpp | 4 +- .../CollisionShapes/btConeShape.cpp | 4 + .../CollisionShapes/btConeShape.h | 53 +- .../CollisionShapes/btConvexHullShape.cpp | 9 +- .../CollisionShapes/btConvexHullShape.h | 2 +- .../CollisionShapes/btConvexShape.cpp | 10 + .../btHeightfieldTerrainShape.cpp | 4 +- .../CollisionShapes/btMultiSphereShape.cpp | 14 +- .../btPolyhedralConvexShape.cpp | 3 + .../CollisionShapes/btTriangleMesh.cpp | 8 +- .../CollisionShapes/btTriangleMesh.h | 2 +- .../Gimpact/btCompoundFromGimpact.h | 93 + .../Gimpact/btGImpactCollisionAlgorithm.cpp | 80 +- .../Gimpact/btGImpactCollisionAlgorithm.h | 4 + .../Gimpact/btGImpactShape.cpp | 35 + .../BulletCollision/Gimpact/btGImpactShape.h | 25 +- .../btConvexPenetrationDepthSolver.h | 4 +- .../btDiscreteCollisionDetectorInterface.h | 5 +- .../btGjkEpaPenetrationDepthSolver.cpp | 4 +- .../btGjkEpaPenetrationDepthSolver.h | 2 +- .../btGjkPairDetector.cpp | 29 +- .../NarrowPhaseCollision/btGjkPairDetector.h | 2 +- .../btMinkowskiPenetrationDepthSolver.cpp | 3 +- .../btMinkowskiPenetrationDepthSolver.h | 2 +- .../NarrowPhaseCollision/btRaycastCallback.h | 2 +- .../bullet/src/BulletDynamics/CMakeLists.txt | 43 +- .../btCharacterControllerInterface.h | 3 +- .../btKinematicCharacterController.cpp | 169 +- .../btKinematicCharacterController.h | 6 +- .../btConeTwistConstraint.cpp | 10 +- .../ConstraintSolver/btConeTwistConstraint.h | 62 +- .../ConstraintSolver/btConstraintSolver.h | 16 +- .../ConstraintSolver/btFixedConstraint.cpp | 129 + .../ConstraintSolver/btFixedConstraint.h | 49 + .../ConstraintSolver/btGearConstraint.h | 98 +- .../btGeneric6DofConstraint.cpp | 15 +- .../btGeneric6DofConstraint.h | 44 +- .../btGeneric6DofSpringConstraint.h | 33 +- .../ConstraintSolver/btHingeConstraint.cpp | 14 +- .../ConstraintSolver/btHingeConstraint.h | 37 +- .../btPoint2PointConstraint.cpp | 7 +- .../btPoint2PointConstraint.h | 22 +- .../btSequentialImpulseConstraintSolver.cpp | 395 ++-- .../btSequentialImpulseConstraintSolver.h | 31 +- .../ConstraintSolver/btSliderConstraint.cpp | 7 + .../ConstraintSolver/btSliderConstraint.h | 46 +- .../ConstraintSolver/btSolverBody.h | 15 +- .../ConstraintSolver/btSolverConstraint.h | 5 +- .../ConstraintSolver/btTypedConstraint.cpp | 10 +- .../ConstraintSolver/btTypedConstraint.h | 64 +- .../Dynamics/btDiscreteDynamicsWorld.cpp | 39 +- .../Dynamics/btDiscreteDynamicsWorld.h | 15 +- .../BulletDynamics/Dynamics/btDynamicsWorld.h | 3 +- .../src/BulletDynamics/Dynamics/btRigidBody.h | 6 +- .../Dynamics/btSimpleDynamicsWorld.cpp | 4 +- .../Featherstone/btMultiBody.cpp | 1009 ++++++++ .../BulletDynamics/Featherstone/btMultiBody.h | 466 ++++ .../Featherstone/btMultiBodyConstraint.cpp | 527 +++++ .../Featherstone/btMultiBodyConstraint.h | 166 ++ .../btMultiBodyConstraintSolver.cpp | 795 +++++++ .../btMultiBodyConstraintSolver.h | 85 + .../Featherstone/btMultiBodyDynamicsWorld.cpp | 578 +++++ .../Featherstone/btMultiBodyDynamicsWorld.h | 56 + .../btMultiBodyJointLimitConstraint.cpp | 133 ++ .../btMultiBodyJointLimitConstraint.h | 44 + .../Featherstone/btMultiBodyJointMotor.cpp | 89 + .../Featherstone/btMultiBodyJointMotor.h | 47 + .../Featherstone/btMultiBodyLink.h | 110 + .../Featherstone/btMultiBodyLinkCollider.h | 92 + .../Featherstone/btMultiBodyPoint2Point.cpp | 143 ++ .../Featherstone/btMultiBodyPoint2Point.h | 60 + .../btMultiBodySolverConstraint.h | 82 + .../MLCPSolvers/btDantzigLCP.cpp | 2079 +++++++++++++++++ .../BulletDynamics/MLCPSolvers/btDantzigLCP.h | 77 + .../MLCPSolvers/btDantzigSolver.h | 112 + .../MLCPSolvers/btMLCPSolver.cpp | 626 +++++ .../BulletDynamics/MLCPSolvers/btMLCPSolver.h | 81 + .../MLCPSolvers/btMLCPSolverInterface.h | 33 + .../BulletDynamics/MLCPSolvers/btPATHSolver.h | 151 ++ .../MLCPSolvers/btSolveProjectedGaussSeidel.h | 80 + .../src/BulletMultiThreaded/CMakeLists.txt | 5 +- .../GpuSoftBodySolvers/DX11/CMakeLists.txt | 5 +- .../OpenCL/AMD/CMakeLists.txt | 5 +- .../OpenCL/Apple/CMakeLists.txt | 5 +- .../OpenCL/Intel/CMakeLists.txt | 5 +- .../OpenCL/MiniCL/CMakeLists.txt | 5 +- .../OpenCL/NVidia/CMakeLists.txt | 5 +- .../BulletMultiThreaded/PlatformDefinitions.h | 4 + .../SpuGatheringCollisionDispatcher.cpp | 8 +- .../SpuConvexPenetrationDepthSolver.h | 3 +- .../SpuGatheringCollisionTask.cpp | 10 +- .../SpuMinkowskiPenetrationDepthSolver.cpp | 3 +- .../SpuMinkowskiPenetrationDepthSolver.h | 3 +- .../btParallelConstraintSolver.cpp | 34 +- .../btParallelConstraintSolver.h | 2 +- .../bullet/src/BulletSoftBody/CMakeLists.txt | 4 +- .../bullet/src/BulletSoftBody/btSoftBody.cpp | 18 +- .../bullet/src/BulletSoftBody/btSoftBody.h | 2 + .../btSoftBodyConcaveCollisionAlgorithm.cpp | 8 +- .../src/BulletSoftBody/btSoftBodyHelpers.cpp | 6 +- .../bullet/src/BulletSoftBody/btSparseSDF.h | 17 +- .../lib/bullet/src/LinearMath/CMakeLists.txt | 5 +- .../src/LinearMath/btConvexHullComputer.cpp | 14 +- .../lib/bullet/src/LinearMath/btIDebugDraw.h | 138 +- .../lib/bullet/src/LinearMath/btMatrix3x3.h | 31 +- Engine/lib/bullet/src/LinearMath/btMatrixX.h | 504 ++++ .../src/LinearMath/btPolarDecomposition.cpp | 4 +- .../lib/bullet/src/LinearMath/btQuaternion.h | 44 +- Engine/lib/bullet/src/LinearMath/btScalar.h | 94 +- .../bullet/src/LinearMath/btSerializer.cpp | 1583 +++++++------ .../lib/bullet/src/LinearMath/btSerializer.h | 3 +- .../lib/bullet/src/LinearMath/btVector3.cpp | 59 +- Engine/lib/bullet/src/LinearMath/btVector3.h | 71 +- Engine/lib/bullet/src/Makefile.am | 53 +- Engine/lib/bullet/src/MiniCL/CMakeLists.txt | 5 +- .../lib/bullet/src/btBulletDynamicsCommon.h | 2 + 146 files changed, 12202 insertions(+), 1422 deletions(-) create mode 100644 Engine/lib/bullet/COPYING create mode 100644 Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.cpp create mode 100644 Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.h create mode 100644 Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btHashedSimplePairCache.cpp create mode 100644 Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btHashedSimplePairCache.h create mode 100644 Engine/lib/bullet/src/BulletCollision/Gimpact/btCompoundFromGimpact.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btFixedConstraint.cpp create mode 100644 Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btFixedConstraint.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.cpp create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointMotor.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyLink.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyLinkCollider.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodySolverConstraint.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigLCP.cpp create mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigLCP.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigSolver.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btMLCPSolver.cpp create mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btMLCPSolver.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btMLCPSolverInterface.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btPATHSolver.h create mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btSolveProjectedGaussSeidel.h create mode 100644 Engine/lib/bullet/src/LinearMath/btMatrixX.h diff --git a/Engine/lib/bullet/CMakeLists.txt b/Engine/lib/bullet/CMakeLists.txt index 594c908c6..18a089a9e 100644 --- a/Engine/lib/bullet/CMakeLists.txt +++ b/Engine/lib/bullet/CMakeLists.txt @@ -5,7 +5,7 @@ set(CMAKE_ALLOW_LOOSE_LOOP_CONSTRUCTS true) SET(MSVC_INCREMENTAL_DEFAULT ON) PROJECT(BULLET_PHYSICS) -SET(BULLET_VERSION 2.81) +SET(BULLET_VERSION 2.82) IF(COMMAND cmake_policy) cmake_policy(SET CMP0003 NEW) @@ -113,7 +113,7 @@ IF (BUILD_MULTITHREADING) ELSE(CMAKE_CL_64) SET(CMAKE_NVSDKCOMPUTE_LIBPATH ${NVIDIA_OPENCL_BASE_DIR}/lib/Win32 ) ENDIF(CMAKE_CL_64) - SET(NVIDIA_OPENCL_LIBRARIES ${CMAKE_NVSDKCOMPUTE_LIBPATH}/OpenCL.lib) + SET(NVIDIA_OPENCL_LIBRARIES ${CMAKE_NVSDKCOMPUTE_LIBPATH}/OpenCL.lib) OPTION(BUILD_NVIDIA_OPENCL_DEMOS "Build OpenCL demos for NVidia (GPU)" ON) ELSE() @@ -214,9 +214,13 @@ IF(MSVC) ENDFOREACH(flag_var) ENDIF (NOT USE_MSVC_RUNTIME_LIBRARY_DLL) - OPTION(USE_MSVC_SSE "Use MSVC /arch:sse option" ON) - IF (USE_MSVC_SSE) + IF (CMAKE_CL_64) + ADD_DEFINITIONS(-D_WIN64) + ELSE() + OPTION(USE_MSVC_SSE "Use MSVC /arch:sse option" ON) + IF (USE_MSVC_SSE) ADD_DEFINITIONS(/arch:SSE) + ENDIF() ENDIF() OPTION(USE_MSVC_FAST_FLOATINGPOINT "Use MSVC /fp:fast option" ON) IF (USE_MSVC_FAST_FLOATINGPOINT) @@ -418,3 +422,18 @@ OPTION(BUILD_UNIT_TESTS "Build Unit Tests" OFF) IF (BUILD_UNIT_TESTS) SUBDIRS(UnitTests) ENDIF() + +set (BULLET_CONFIG_CMAKE_PATH lib${LIB_SUFFIX}/cmake/bullet ) +list (APPEND BULLET_LIBRARIES LinearMath) +list (APPEND BULLET_LIBRARIES BulletCollisions) +list (APPEND BULLET_LIBRARIES BulletDynamics) +list (APPEND BULLET_LIBRARIES BulletSoftBody) +set (BULLET_USE_FILE ${CMAKE_INSTALL_PREFIX}/${BULLET_CONFIG_CMAKE_PATH}/UseBullet.cmake) +configure_file ( ${CMAKE_SOURCE_DIR}/BulletConfig.cmake.in + ${CMAKE_CURRENT_BINARY_DIR}/BulletConfig.cmake + @ONLY ESCAPE_QUOTES + ) +install ( FILES ${CMAKE_SOURCE_DIR}/UseBullet.cmake + ${CMAKE_CURRENT_BINARY_DIR}/BulletConfig.cmake + DESTINATION ${BULLET_CONFIG_CMAKE_PATH} + ) diff --git a/Engine/lib/bullet/COPYING b/Engine/lib/bullet/COPYING new file mode 100644 index 000000000..794842d9b --- /dev/null +++ b/Engine/lib/bullet/COPYING @@ -0,0 +1,17 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2011 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +All files in the Bullet/src folder are under this Zlib license. +Files in the Extras and Demos folder may have a different license, see the respective files. diff --git a/Engine/lib/bullet/ChangeLog b/Engine/lib/bullet/ChangeLog index 4a0ccf492..f5c85c801 100644 --- a/Engine/lib/bullet/ChangeLog +++ b/Engine/lib/bullet/ChangeLog @@ -4,6 +4,10 @@ Primary author and maintainer: Erwin Coumans This ChangeLog is incomplete, for an up-to-date list of all fixed issues see http://bullet.googlecode.com using http://tinyurl.com/yabmjjj +2013 October 23 + - Bullet 2.82 release + - See docs/BulletQuickstart.pdf or issue tracked for details. + 2012 September 10 - Bullet 2.81 release preparation diff --git a/Engine/lib/bullet/RELEASING.TXT b/Engine/lib/bullet/RELEASING.TXT index 8fba4a5a8..49d6ba40e 100644 --- a/Engine/lib/bullet/RELEASING.TXT +++ b/Engine/lib/bullet/RELEASING.TXT @@ -2,9 +2,11 @@ This document details the steps necessary to package a release of Bullet. 1) Preparing for release: -update VERSION in several places -update ChangeLog -regenerate MSVC project files +update VERSION in several places (/VERSION file, /CMakeLists.txt, /configure.ac, /src/LinearMath/btScalar.h, /src/LinearMath/btSerializer.h around line 441) +re-generate serialization structures, if they changed (/src/LinearMath/btSerializer.cpp using makesdna) +update ChangeLog with larger/important changes +regenerate MSVC project files using build/vs_all.bat +create a Subversion tag revision in bullet.googlecode.com/svn/tags/bullet- 2) Generating the release .zip: Do an SVN export on a Windows machine into the directory: bullet-X.YY diff --git a/Engine/lib/bullet/VERSION b/Engine/lib/bullet/VERSION index 00ddb02a1..90c00fa39 100644 --- a/Engine/lib/bullet/VERSION +++ b/Engine/lib/bullet/VERSION @@ -1 +1 @@ -2.81 +2.82 diff --git a/Engine/lib/bullet/configure.ac b/Engine/lib/bullet/configure.ac index 1d793612a..3e9780b80 100644 --- a/Engine/lib/bullet/configure.ac +++ b/Engine/lib/bullet/configure.ac @@ -9,8 +9,8 @@ AC_PREREQ([2.54]) #---------------------------------------------------------------------------- AC_INIT( [bullet], - [2.81], - [bullet@erwincoumans.com]) + [2.82], + [erwin.coumans@gmail.com]) AC_CANONICAL_HOST AC_CONFIG_SRCDIR([configure.ac]) AM_INIT_AUTOMAKE @@ -163,7 +163,7 @@ CXXFLAGS="$ARCH_SPECIFIC_CFLAGS $CXXFLAGS $CFLAGS" #---------------------------------------------------------------------------- # Emit generated files. #---------------------------------------------------------------------------- -AC_CONFIG_FILES([bullet.pc Makefile Demos/Makefile Demos/SoftDemo/Makefile Demos/AllBulletDemos/Makefile Demos/MultiThreadedDemo/Makefile Demos/OpenGL/Makefile Demos/BasicDemo/Makefile Demos/CcdPhysicsDemo/Makefile Demos/VehicleDemo/Makefile Demos/TerrainDemo/Makefile src/Makefile Extras/Makefile]) +AC_CONFIG_FILES([bullet.pc Makefile Demos/Makefile Demos/SoftDemo/Makefile Demos/AllBulletDemos/Makefile Demos/MultiThreadedDemo/Makefile Demos/OpenGL/Makefile Demos/ForkLiftDemo/Makefile Demos/FeatherstoneMultiBodyDemo/Makefile Demos/BasicDemo/Makefile Demos/CcdPhysicsDemo/Makefile Demos/VehicleDemo/Makefile Demos/TerrainDemo/Makefile src/Makefile Extras/Makefile]) AC_OUTPUT AC_MSG_NOTICE([ diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.h b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.h index 1ebb37797..89c307d14 100644 --- a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.h +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btDispatcher.h @@ -25,7 +25,6 @@ class btOverlappingPairCache; struct btCollisionObjectWrapper; class btPersistentManifold; -class btStackAlloc; class btPoolAllocator; struct btDispatcherInfo @@ -47,8 +46,7 @@ struct btDispatcherInfo m_useEpa(true), m_allowedCcdPenetration(btScalar(0.04)), m_useConvexConservativeDistanceUtil(false), - m_convexConservativeDistanceThreshold(0.0f), - m_stackAllocator(0) + m_convexConservativeDistanceThreshold(0.0f) { } @@ -64,7 +62,6 @@ struct btDispatcherInfo btScalar m_allowedCcdPenetration; bool m_useConvexConservativeDistanceUtil; btScalar m_convexConservativeDistanceThreshold; - btStackAlloc* m_stackAllocator; }; ///The btDispatcher interface class can be used in combination with broadphase to dispatch calculations for overlapping pairs. diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp index 041bbe05a..ae22dadc7 100644 --- a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.cpp @@ -53,7 +53,7 @@ btHashedOverlappingPairCache::~btHashedOverlappingPairCache() void btHashedOverlappingPairCache::cleanOverlappingPair(btBroadphasePair& pair,btDispatcher* dispatcher) { - if (pair.m_algorithm) + if (pair.m_algorithm && dispatcher) { { pair.m_algorithm->~btCollisionAlgorithm(); diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h index 7a3806c1d..eee90e473 100644 --- a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btOverlappingPairCache.h @@ -96,6 +96,12 @@ class btHashedOverlappingPairCache : public btOverlappingPairCache btOverlapFilterCallback* m_overlapFilterCallback; bool m_blockedForChanges; +protected: + + btAlignedObjectArray m_hashTable; + btAlignedObjectArray m_next; + btOverlappingPairCallback* m_ghostPairCallback; + public: btHashedOverlappingPairCache(); @@ -265,11 +271,6 @@ private: virtual void sortOverlappingPairs(btDispatcher* dispatcher); -protected: - - btAlignedObjectArray m_hashTable; - btAlignedObjectArray m_next; - btOverlappingPairCallback* m_ghostPairCallback; }; diff --git a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btQuantizedBvh.cpp b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btQuantizedBvh.cpp index c911435a9..889216df5 100644 --- a/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btQuantizedBvh.cpp +++ b/Engine/lib/bullet/src/BulletCollision/BroadphaseCollision/btQuantizedBvh.cpp @@ -96,7 +96,25 @@ void btQuantizedBvh::setQuantizationValues(const btVector3& bvhAabbMin,const btV m_bvhAabbMax = bvhAabbMax + clampValue; btVector3 aabbSize = m_bvhAabbMax - m_bvhAabbMin; m_bvhQuantization = btVector3(btScalar(65533.0),btScalar(65533.0),btScalar(65533.0)) / aabbSize; + m_useQuantization = true; + + { + unsigned short vecIn[3]; + btVector3 v; + { + quantize(vecIn,m_bvhAabbMin,false); + v = unQuantize(vecIn); + m_bvhAabbMin.setMin(v-clampValue); + } + { + quantize(vecIn,m_bvhAabbMax,true); + v = unQuantize(vecIn); + m_bvhAabbMax.setMax(v+clampValue); + } + aabbSize = m_bvhAabbMax - m_bvhAabbMin; + m_bvhQuantization = btVector3(btScalar(65533.0),btScalar(65533.0),btScalar(65533.0)) / aabbSize; + } } diff --git a/Engine/lib/bullet/src/BulletCollision/CMakeLists.txt b/Engine/lib/bullet/src/BulletCollision/CMakeLists.txt index e740e98ae..c4723ae25 100644 --- a/Engine/lib/bullet/src/BulletCollision/CMakeLists.txt +++ b/Engine/lib/bullet/src/BulletCollision/CMakeLists.txt @@ -19,6 +19,7 @@ SET(BulletCollision_SRCS CollisionDispatch/btCollisionObject.cpp CollisionDispatch/btCollisionWorld.cpp CollisionDispatch/btCompoundCollisionAlgorithm.cpp + CollisionDispatch/btCompoundCompoundCollisionAlgorithm.cpp CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp CollisionDispatch/btConvexConvexAlgorithm.cpp CollisionDispatch/btConvexPlaneCollisionAlgorithm.cpp @@ -26,6 +27,7 @@ SET(BulletCollision_SRCS CollisionDispatch/btDefaultCollisionConfiguration.cpp CollisionDispatch/btEmptyCollisionAlgorithm.cpp CollisionDispatch/btGhostObject.cpp + CollisionDispatch/btHashedSimplePairCache.cpp CollisionDispatch/btInternalEdgeUtility.cpp CollisionDispatch/btInternalEdgeUtility.h CollisionDispatch/btManifoldResult.cpp @@ -122,8 +124,10 @@ SET(CollisionDispatch_HDRS CollisionDispatch/btCollisionCreateFunc.h CollisionDispatch/btCollisionDispatcher.h CollisionDispatch/btCollisionObject.h + CollisionDispatch/btCollisionObjectWrapper.h CollisionDispatch/btCollisionWorld.h CollisionDispatch/btCompoundCollisionAlgorithm.h + CollisionDispatch/btCompoundCompoundCollisionAlgorithm.h CollisionDispatch/btConvexConcaveCollisionAlgorithm.h CollisionDispatch/btConvexConvexAlgorithm.h CollisionDispatch/btConvex2dConvex2dAlgorithm.h @@ -131,6 +135,7 @@ SET(CollisionDispatch_HDRS CollisionDispatch/btDefaultCollisionConfiguration.h CollisionDispatch/btEmptyCollisionAlgorithm.h CollisionDispatch/btGhostObject.h + CollisionDispatch/btHashedSimplePairCache.h CollisionDispatch/btManifoldResult.h CollisionDispatch/btSimulationIslandManager.h CollisionDispatch/btSphereBoxCollisionAlgorithm.h @@ -255,7 +260,9 @@ IF (INSTALL_LIBS) IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) INSTALL(TARGETS BulletCollision DESTINATION .) ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) - INSTALL(TARGETS BulletCollision DESTINATION lib${LIB_SUFFIX}) + INSTALL(TARGETS BulletCollision RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ARCHIVE DESTINATION lib${LIB_SUFFIX}) INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION ${INCLUDE_INSTALL_DIR} FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE PATTERN "CMakeFiles" EXCLUDE) INSTALL(FILES ../btBulletCollisionCommon.h diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionConfiguration.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionConfiguration.h index f63e0923b..669498494 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionConfiguration.h +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionConfiguration.h @@ -18,7 +18,6 @@ subject to the following restrictions: struct btCollisionAlgorithmCreateFunc; -class btStackAlloc; class btPoolAllocator; ///btCollisionConfiguration allows to configure Bullet collision detection @@ -38,7 +37,6 @@ public: virtual btPoolAllocator* getCollisionAlgorithmPool() = 0; - virtual btStackAlloc* getStackAllocator() = 0; virtual btCollisionAlgorithmCreateFunc* getCollisionAlgorithmCreateFunc(int proxyType0,int proxyType1) =0; diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp index d1ef1ffc9..669d0b6b5 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionDispatcher.cpp @@ -258,8 +258,8 @@ void btCollisionDispatcher::defaultNearCallback(btBroadphasePair& collisionPair, if (dispatcher.needsCollision(colObj0,colObj1)) { - btCollisionObjectWrapper obj0Wrap(0,colObj0->getCollisionShape(),colObj0,colObj0->getWorldTransform()); - btCollisionObjectWrapper obj1Wrap(0,colObj1->getCollisionShape(),colObj1,colObj1->getWorldTransform()); + btCollisionObjectWrapper obj0Wrap(0,colObj0->getCollisionShape(),colObj0,colObj0->getWorldTransform(),-1,-1); + btCollisionObjectWrapper obj1Wrap(0,colObj1->getCollisionShape(),colObj1,colObj1->getWorldTransform(),-1,-1); //dispatcher will keep algorithms persistent in the collision pair diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.cpp index cf8ed59a5..d09241000 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.cpp @@ -38,7 +38,8 @@ btCollisionObject::btCollisionObject() m_hitFraction(btScalar(1.)), m_ccdSweptSphereRadius(btScalar(0.)), m_ccdMotionThreshold(btScalar(0.)), - m_checkCollideWith(false) + m_checkCollideWith(false), + m_updateRevision(0) { m_worldTransform.setIdentity(); } diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h index 2f17967fe..89cad1682 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObject.h @@ -92,7 +92,11 @@ protected: int m_internalType; ///users can point to their objects, m_userPointer is not used by Bullet, see setUserPointer/getUserPointer - void* m_userObjectPointer; + union + { + void* m_userObjectPointer; + int m_userIndex; + }; ///time of impact calculation btScalar m_hitFraction; @@ -106,6 +110,9 @@ protected: /// If some object should have elaborate collision filtering by sub-classes int m_checkCollideWith; + ///internal update revision number. It will be increased when the object changes. This allows some subsystems to perform lazy evaluation. + int m_updateRevision; + virtual bool checkCollideWithOverride(const btCollisionObject* /* co */) const { return true; @@ -135,7 +142,8 @@ public: CO_GHOST_OBJECT=4, CO_SOFT_BODY=8, CO_HF_FLUID=16, - CO_USER_TYPE=32 + CO_USER_TYPE=32, + CO_FEATHERSTONE_LINK=64 }; enum AnisotropicFrictionFlags @@ -202,6 +210,7 @@ public: virtual void setCollisionShape(btCollisionShape* collisionShape) { + m_updateRevision++; m_collisionShape = collisionShape; m_rootCollisionShape = collisionShape; } @@ -257,6 +266,7 @@ public: void setRestitution(btScalar rest) { + m_updateRevision++; m_restitution = rest; } btScalar getRestitution() const @@ -265,6 +275,7 @@ public: } void setFriction(btScalar frict) { + m_updateRevision++; m_friction = frict; } btScalar getFriction() const @@ -274,6 +285,7 @@ public: void setRollingFriction(btScalar frict) { + m_updateRevision++; m_rollingFriction = frict; } btScalar getRollingFriction() const @@ -300,6 +312,7 @@ public: void setWorldTransform(const btTransform& worldTrans) { + m_updateRevision++; m_worldTransform = worldTrans; } @@ -332,16 +345,19 @@ public: void setInterpolationWorldTransform(const btTransform& trans) { + m_updateRevision++; m_interpolationWorldTransform = trans; } void setInterpolationLinearVelocity(const btVector3& linvel) { + m_updateRevision++; m_interpolationLinearVelocity = linvel; } void setInterpolationAngularVelocity(const btVector3& angvel) { + m_updateRevision++; m_interpolationAngularVelocity = angvel; } @@ -431,13 +447,28 @@ public: { return m_userObjectPointer; } - + + int getUserIndex() const + { + return m_userIndex; + } ///users can point to their objects, userPointer is not used by Bullet void setUserPointer(void* userPointer) { m_userObjectPointer = userPointer; } + ///users can point to their objects, userPointer is not used by Bullet + void setUserIndex(int index) + { + m_userIndex = index; + } + + int getUpdateRevisionInternal() const + { + return m_updateRevision; + } + inline bool checkCollideWith(const btCollisionObject* co) const { diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObjectWrapper.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObjectWrapper.h index 2b8ffeaa2..952440b7d 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObjectWrapper.h +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionObjectWrapper.h @@ -27,9 +27,12 @@ public: const btCollisionShape* m_shape; const btCollisionObject* m_collisionObject; const btTransform& m_worldTransform; + int m_partId; + int m_index; - btCollisionObjectWrapper(const btCollisionObjectWrapper* parent, const btCollisionShape* shape, const btCollisionObject* collisionObject, const btTransform& worldTransform) - : m_parent(parent), m_shape(shape), m_collisionObject(collisionObject), m_worldTransform(worldTransform) + btCollisionObjectWrapper(const btCollisionObjectWrapper* parent, const btCollisionShape* shape, const btCollisionObject* collisionObject, const btTransform& worldTransform, int partId, int index) + : m_parent(parent), m_shape(shape), m_collisionObject(collisionObject), m_worldTransform(worldTransform), + m_partId(partId), m_index(index) {} SIMD_FORCE_INLINE const btTransform& getWorldTransform() const { return m_worldTransform; } diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp index 91f4c6200..093c6f9b2 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.cpp @@ -31,11 +31,10 @@ subject to the following restrictions: #include "BulletCollision/BroadphaseCollision/btDbvt.h" #include "LinearMath/btAabbUtil2.h" #include "LinearMath/btQuickprof.h" -#include "LinearMath/btStackAlloc.h" #include "LinearMath/btSerializer.h" #include "BulletCollision/CollisionShapes/btConvexPolyhedron.h" #include "BulletCollision/CollisionDispatch/btCollisionObjectWrapper.h" - +#include "BulletCollision/Gimpact/btGImpactShape.h" //#define DISABLE_DBVT_COMPOUNDSHAPE_RAYCAST_ACCELERATION @@ -73,8 +72,6 @@ m_broadphasePairCache(pairCache), m_debugDrawer(0), m_forceUpdateAllAabbs(true) { - m_stackAlloc = collisionConfiguration->getStackAllocator(); - m_dispatchInfo.m_stackAllocator = m_stackAlloc; } @@ -268,7 +265,7 @@ void btCollisionWorld::rayTestSingle(const btTransform& rayFromTrans,const btTra const btTransform& colObjWorldTransform, RayResultCallback& resultCallback) { - btCollisionObjectWrapper colObWrap(0,collisionShape,collisionObject,colObjWorldTransform); + btCollisionObjectWrapper colObWrap(0,collisionShape,collisionObject,colObjWorldTransform,-1,-1); btCollisionWorld::rayTestSingleInternal(rayFromTrans,rayToTrans,&colObWrap,resultCallback); } @@ -290,13 +287,19 @@ void btCollisionWorld::rayTestSingleInternal(const btTransform& rayFromTrans,con btConvexShape* convexShape = (btConvexShape*) collisionShape; btVoronoiSimplexSolver simplexSolver; -#define USE_SUBSIMPLEX_CONVEX_CAST 1 -#ifdef USE_SUBSIMPLEX_CONVEX_CAST - btSubsimplexConvexCast convexCaster(castShape,convexShape,&simplexSolver); -#else - //btGjkConvexCast convexCaster(castShape,convexShape,&simplexSolver); + btSubsimplexConvexCast subSimplexConvexCaster(castShape,convexShape,&simplexSolver); + + btGjkConvexCast gjkConvexCaster(castShape,convexShape,&simplexSolver); + //btContinuousConvexCollision convexCaster(castShape,convexShape,&simplexSolver,0); -#endif //#USE_SUBSIMPLEX_CONVEX_CAST + bool condition = true; + btConvexCast* convexCasterPtr = 0; + if (resultCallback.m_flags & btTriangleRaycastCallback::kF_UseSubSimplexConvexCastRaytest) + convexCasterPtr = &subSimplexConvexCaster; + else + convexCasterPtr = &gjkConvexCaster; + + btConvexCast& convexCaster = *convexCasterPtr; if (convexCaster.calcTimeOfImpact(rayFromTrans,rayToTrans,colObjWorldTransform,colObjWorldTransform,castResult)) { @@ -328,34 +331,26 @@ void btCollisionWorld::rayTestSingleInternal(const btTransform& rayFromTrans,con } else { if (collisionShape->isConcave()) { - // BT_PROFILE("rayTestConcave"); - if (collisionShape->getShapeType()==TRIANGLE_MESH_SHAPE_PROXYTYPE) - { - ///optimized version for btBvhTriangleMeshShape - btBvhTriangleMeshShape* triangleMesh = (btBvhTriangleMeshShape*)collisionShape; - btTransform worldTocollisionObject = colObjWorldTransform.inverse(); - btVector3 rayFromLocal = worldTocollisionObject * rayFromTrans.getOrigin(); - btVector3 rayToLocal = worldTocollisionObject * rayToTrans.getOrigin(); - //ConvexCast::CastResult + //ConvexCast::CastResult struct BridgeTriangleRaycastCallback : public btTriangleRaycastCallback { btCollisionWorld::RayResultCallback* m_resultCallback; const btCollisionObject* m_collisionObject; - btTriangleMeshShape* m_triangleMesh; + const btConcaveShape* m_triangleMesh; btTransform m_colObjWorldTransform; BridgeTriangleRaycastCallback( const btVector3& from,const btVector3& to, - btCollisionWorld::RayResultCallback* resultCallback, const btCollisionObject* collisionObject,btTriangleMeshShape* triangleMesh,const btTransform& colObjWorldTransform): - //@BP Mod - btTriangleRaycastCallback(from,to, resultCallback->m_flags), - m_resultCallback(resultCallback), - m_collisionObject(collisionObject), - m_triangleMesh(triangleMesh), - m_colObjWorldTransform(colObjWorldTransform) - { - } + btCollisionWorld::RayResultCallback* resultCallback, const btCollisionObject* collisionObject,const btConcaveShape* triangleMesh,const btTransform& colObjWorldTransform): + //@BP Mod + btTriangleRaycastCallback(from,to, resultCallback->m_flags), + m_resultCallback(resultCallback), + m_collisionObject(collisionObject), + m_triangleMesh(triangleMesh), + m_colObjWorldTransform(colObjWorldTransform) + { + } virtual btScalar reportHit(const btVector3& hitNormalLocal, btScalar hitFraction, int partId, int triangleIndex ) @@ -378,10 +373,28 @@ void btCollisionWorld::rayTestSingleInternal(const btTransform& rayFromTrans,con }; + btTransform worldTocollisionObject = colObjWorldTransform.inverse(); + btVector3 rayFromLocal = worldTocollisionObject * rayFromTrans.getOrigin(); + btVector3 rayToLocal = worldTocollisionObject * rayToTrans.getOrigin(); + + // BT_PROFILE("rayTestConcave"); + if (collisionShape->getShapeType()==TRIANGLE_MESH_SHAPE_PROXYTYPE) + { + ///optimized version for btBvhTriangleMeshShape + btBvhTriangleMeshShape* triangleMesh = (btBvhTriangleMeshShape*)collisionShape; + BridgeTriangleRaycastCallback rcb(rayFromLocal,rayToLocal,&resultCallback,collisionObjectWrap->getCollisionObject(),triangleMesh,colObjWorldTransform); rcb.m_hitFraction = resultCallback.m_closestHitFraction; triangleMesh->performRaycast(&rcb,rayFromLocal,rayToLocal); - } else + } + else if(collisionShape->getShapeType()==GIMPACT_SHAPE_PROXYTYPE) + { + btGImpactMeshShape* concaveShape = (btGImpactMeshShape*)collisionShape; + + BridgeTriangleRaycastCallback rcb(rayFromLocal,rayToLocal,&resultCallback,collisionObjectWrap->getCollisionObject(),concaveShape, colObjWorldTransform); + rcb.m_hitFraction = resultCallback.m_closestHitFraction; + concaveShape->processAllTrianglesRay(&rcb,rayFromLocal,rayToLocal); + }else { //generic (slower) case btConcaveShape* concaveShape = (btConcaveShape*)collisionShape; @@ -509,7 +522,7 @@ void btCollisionWorld::rayTestSingleInternal(const btTransform& rayFromTrans,con const btTransform& childTrans = m_compoundShape->getChildTransform(i); btTransform childWorldTrans = m_colObjWorldTransform * childTrans; - btCollisionObjectWrapper tmpOb(0,childCollisionShape,m_collisionObject,childWorldTrans); + btCollisionObjectWrapper tmpOb(0,childCollisionShape,m_collisionObject,childWorldTrans,-1,i); // replace collision shape so that callback can determine the triangle @@ -567,7 +580,7 @@ void btCollisionWorld::objectQuerySingle(const btConvexShape* castShape,const bt const btTransform& colObjWorldTransform, ConvexResultCallback& resultCallback, btScalar allowedPenetration) { - btCollisionObjectWrapper tmpOb(0,collisionShape,collisionObject,colObjWorldTransform); + btCollisionObjectWrapper tmpOb(0,collisionShape,collisionObject,colObjWorldTransform,-1,-1); btCollisionWorld::objectQuerySingleInternal(castShape,convexFromTrans,convexToTrans,&tmpOb,resultCallback,allowedPenetration); } @@ -823,7 +836,7 @@ void btCollisionWorld::objectQuerySingleInternal(const btConvexShape* castShape, LocalInfoAdder my_cb(i, &resultCallback); - btCollisionObjectWrapper tmpObj(colObjWrap,childCollisionShape,colObjWrap->getCollisionObject(),childWorldTrans); + btCollisionObjectWrapper tmpObj(colObjWrap,childCollisionShape,colObjWrap->getCollisionObject(),childWorldTrans,-1,i); objectQuerySingleInternal(castShape, convexFromTrans,convexToTrans, &tmpObj,my_cb, allowedPenetration); @@ -1135,8 +1148,8 @@ struct btSingleContactCallback : public btBroadphaseAabbCallback //only perform raycast if filterMask matches if(m_resultCallback.needsCollision(collisionObject->getBroadphaseHandle())) { - btCollisionObjectWrapper ob0(0,m_collisionObject->getCollisionShape(),m_collisionObject,m_collisionObject->getWorldTransform()); - btCollisionObjectWrapper ob1(0,collisionObject->getCollisionShape(),collisionObject,collisionObject->getWorldTransform()); + btCollisionObjectWrapper ob0(0,m_collisionObject->getCollisionShape(),m_collisionObject,m_collisionObject->getWorldTransform(),-1,-1); + btCollisionObjectWrapper ob1(0,collisionObject->getCollisionShape(),collisionObject,collisionObject->getWorldTransform(),-1,-1); btCollisionAlgorithm* algorithm = m_world->getDispatcher()->findAlgorithm(&ob0,&ob1); if (algorithm) @@ -1171,8 +1184,8 @@ void btCollisionWorld::contactTest( btCollisionObject* colObj, ContactResultCall ///it reports one or more contact points (including the one with deepest penetration) void btCollisionWorld::contactPairTest(btCollisionObject* colObjA, btCollisionObject* colObjB, ContactResultCallback& resultCallback) { - btCollisionObjectWrapper obA(0,colObjA->getCollisionShape(),colObjA,colObjA->getWorldTransform()); - btCollisionObjectWrapper obB(0,colObjB->getCollisionShape(),colObjB,colObjB->getWorldTransform()); + btCollisionObjectWrapper obA(0,colObjA->getCollisionShape(),colObjA,colObjA->getWorldTransform(),-1,-1); + btCollisionObjectWrapper obB(0,colObjB->getCollisionShape(),colObjB,colObjB->getWorldTransform(),-1,-1); btCollisionAlgorithm* algorithm = getDispatcher()->findAlgorithm(&obA,&obB); if (algorithm) diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.h index 9412242e8..b3fffdecd 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.h +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCollisionWorld.h @@ -1,6 +1,6 @@ /* Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2006 Erwin Coumans http://bulletphysics.com/Bullet/ +Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org This software is provided 'as-is', without any express or implied warranty. In no event will the authors be held liable for any damages arising from the use of this software. @@ -18,13 +18,11 @@ subject to the following restrictions: * @mainpage Bullet Documentation * * @section intro_sec Introduction - * Bullet Collision Detection & Physics SDK - * * Bullet is a Collision Detection and Rigid Body Dynamics Library. The Library is Open Source and free for commercial use, under the ZLib license ( http://opensource.org/licenses/zlib-license.php ). * * The main documentation is Bullet_User_Manual.pdf, included in the source code distribution. * There is the Physics Forum for feedback and general Collision Detection and Physics discussions. - * Please visit http://www.bulletphysics.com + * Please visit http://www.bulletphysics.org * * @section install_sec Installation * @@ -32,7 +30,16 @@ subject to the following restrictions: * You can download the Bullet Physics Library from the Google Code repository: http://code.google.com/p/bullet/downloads/list * * @subsection step2 Step 2: Building - * Bullet main build system for all platforms is cmake, you can download http://www.cmake.org + * Bullet has multiple build systems, including premake, cmake and autotools. Premake and cmake support all platforms. + * Premake is included in the Bullet/build folder for Windows, Mac OSX and Linux. + * Under Windows you can click on Bullet/build/vs2010.bat to create Microsoft Visual Studio projects. + * On Mac OSX and Linux you can open a terminal and generate Makefile, codeblocks or Xcode4 projects: + * cd Bullet/build + * ./premake4_osx gmake or ./premake4_linux gmake or ./premake4_linux64 gmake or (for Mac) ./premake4_osx xcode4 + * cd Bullet/build/gmake + * make + * + * An alternative to premake is cmake. You can download cmake from http://www.cmake.org * cmake can autogenerate projectfiles for Microsoft Visual Studio, Apple Xcode, KDevelop and Unix Makefiles. * The easiest is to run the CMake cmake-gui graphical user interface and choose the options and generate projectfiles. * You can also use cmake in the command-line. Here are some examples for various platforms: @@ -65,7 +72,6 @@ subject to the following restrictions: #ifndef BT_COLLISION_WORLD_H #define BT_COLLISION_WORLD_H -class btStackAlloc; class btCollisionShape; class btConvexShape; class btBroadphaseInterface; @@ -91,8 +97,6 @@ protected: btDispatcherInfo m_dispatchInfo; - btStackAlloc* m_stackAlloc; - btBroadphaseInterface* m_broadphasePairCache; btIDebugDraw* m_debugDrawer; diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp index 290d67797..991841ee2 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp @@ -11,6 +11,7 @@ subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. + */ #include "BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h" @@ -22,6 +23,8 @@ subject to the following restrictions: #include "btManifoldResult.h" #include "BulletCollision/CollisionDispatch/btCollisionObjectWrapper.h" +btShapePairCallback gCompoundChildShapePairCallback = 0; + btCompoundCollisionAlgorithm::btCompoundCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci,const btCollisionObjectWrapper* body0Wrap,const btCollisionObjectWrapper* body1Wrap,bool isSwapped) :btActivatingCollisionAlgorithm(ci,body0Wrap,body1Wrap), m_isSwapped(isSwapped), @@ -61,7 +64,7 @@ void btCompoundCollisionAlgorithm::preallocateChildAlgorithms(const btCollisionO const btCollisionShape* childShape = compoundShape->getChildShape(i); - btCollisionObjectWrapper childWrap(colObjWrap,childShape,colObjWrap->getCollisionObject(),colObjWrap->getWorldTransform());//wrong child trans, but unused (hopefully) + btCollisionObjectWrapper childWrap(colObjWrap,childShape,colObjWrap->getCollisionObject(),colObjWrap->getWorldTransform(),-1,i);//wrong child trans, but unused (hopefully) m_childCollisionAlgorithms[i] = m_dispatcher->findAlgorithm(&childWrap,otherObjWrap,m_sharedManifold); } } @@ -129,10 +132,16 @@ public: childShape->getAabb(newChildWorldTrans,aabbMin0,aabbMax0); m_otherObjWrap->getCollisionShape()->getAabb(m_otherObjWrap->getWorldTransform(),aabbMin1,aabbMax1); + if (gCompoundChildShapePairCallback) + { + if (!gCompoundChildShapePairCallback(m_otherObjWrap->getCollisionShape(), childShape)) + return; + } + if (TestAabbAgainstAabb2(aabbMin0,aabbMax0,aabbMin1,aabbMax1)) { - btCollisionObjectWrapper compoundWrap(this->m_compoundColObjWrap,childShape,m_compoundColObjWrap->getCollisionObject(),newChildWorldTrans); + btCollisionObjectWrapper compoundWrap(this->m_compoundColObjWrap,childShape,m_compoundColObjWrap->getCollisionObject(),newChildWorldTrans,-1,index); //the contactpoint is still projected back using the original inverted worldtrans diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h index b16fc5246..536751456 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h @@ -11,6 +11,7 @@ subject to the following restrictions: 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. + */ #ifndef BT_COMPOUND_COLLISION_ALGORITHM_H @@ -28,6 +29,10 @@ class btDispatcher; class btDispatcher; class btCollisionObject; +class btCollisionShape; +typedef bool (*btShapePairCallback)(const btCollisionShape* pShape0, const btCollisionShape* pShape1); +extern btShapePairCallback gCompoundChildShapePairCallback; + /// btCompoundCollisionAlgorithm supports collision between CompoundCollisionShapes and other collision shapes class btCompoundCollisionAlgorithm : public btActivatingCollisionAlgorithm { @@ -37,6 +42,7 @@ class btCompoundCollisionAlgorithm : public btActivatingCollisionAlgorithm class btPersistentManifold* m_sharedManifold; bool m_ownsManifold; + int m_compoundShapeRevision;//to keep track of changes, so that childAlgorithm array can be updated void removeChildAlgorithms(); @@ -49,6 +55,12 @@ public: virtual ~btCompoundCollisionAlgorithm(); + btCollisionAlgorithm* getChildAlgorithm (int n) const + { + return m_childCollisionAlgorithms[n]; + } + + virtual void processCollision (const btCollisionObjectWrapper* body0Wrap,const btCollisionObjectWrapper* body1Wrap,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); @@ -63,6 +75,7 @@ public: } } + struct CreateFunc :public btCollisionAlgorithmCreateFunc { virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, const btCollisionObjectWrapper* body0Wrap,const btCollisionObjectWrapper* body1Wrap) diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.cpp new file mode 100644 index 000000000..a52dd34fe --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.cpp @@ -0,0 +1,421 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +*/ + +#include "btCompoundCompoundCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" +#include "BulletCollision/CollisionShapes/btCompoundShape.h" +#include "BulletCollision/BroadphaseCollision/btDbvt.h" +#include "LinearMath/btIDebugDraw.h" +#include "LinearMath/btAabbUtil2.h" +#include "BulletCollision/CollisionDispatch/btManifoldResult.h" +#include "BulletCollision/CollisionDispatch/btCollisionObjectWrapper.h" + + +btShapePairCallback gCompoundCompoundChildShapePairCallback = 0; + +btCompoundCompoundCollisionAlgorithm::btCompoundCompoundCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci,const btCollisionObjectWrapper* body0Wrap,const btCollisionObjectWrapper* body1Wrap,bool isSwapped) +:btActivatingCollisionAlgorithm(ci,body0Wrap,body1Wrap), +m_sharedManifold(ci.m_manifold) +{ + m_ownsManifold = false; + + void* ptr = btAlignedAlloc(sizeof(btHashedSimplePairCache),16); + m_childCollisionAlgorithmCache= new(ptr) btHashedSimplePairCache(); + + const btCollisionObjectWrapper* col0ObjWrap = body0Wrap; + btAssert (col0ObjWrap->getCollisionShape()->isCompound()); + + const btCollisionObjectWrapper* col1ObjWrap = body1Wrap; + btAssert (col1ObjWrap->getCollisionShape()->isCompound()); + + const btCompoundShape* compoundShape0 = static_cast(col0ObjWrap->getCollisionShape()); + m_compoundShapeRevision0 = compoundShape0->getUpdateRevision(); + + const btCompoundShape* compoundShape1 = static_cast(col1ObjWrap->getCollisionShape()); + m_compoundShapeRevision1 = compoundShape1->getUpdateRevision(); + + +} + + +btCompoundCompoundCollisionAlgorithm::~btCompoundCompoundCollisionAlgorithm() +{ + removeChildAlgorithms(); + m_childCollisionAlgorithmCache->~btHashedSimplePairCache(); + btAlignedFree(m_childCollisionAlgorithmCache); +} + +void btCompoundCompoundCollisionAlgorithm::getAllContactManifolds(btManifoldArray& manifoldArray) +{ + int i; + btSimplePairArray& pairs = m_childCollisionAlgorithmCache->getOverlappingPairArray(); + for (i=0;igetAllContactManifolds(manifoldArray); + } + } +} + + +void btCompoundCompoundCollisionAlgorithm::removeChildAlgorithms() +{ + btSimplePairArray& pairs = m_childCollisionAlgorithmCache->getOverlappingPairArray(); + + int numChildren = pairs.size(); + int i; + for (i=0;i~btCollisionAlgorithm(); + m_dispatcher->freeCollisionAlgorithm(algo); + } + } + m_childCollisionAlgorithmCache->removeAllPairs(); +} + +struct btCompoundCompoundLeafCallback : btDbvt::ICollide +{ + int m_numOverlapPairs; + + + const btCollisionObjectWrapper* m_compound0ColObjWrap; + const btCollisionObjectWrapper* m_compound1ColObjWrap; + btDispatcher* m_dispatcher; + const btDispatcherInfo& m_dispatchInfo; + btManifoldResult* m_resultOut; + + + class btHashedSimplePairCache* m_childCollisionAlgorithmCache; + + btPersistentManifold* m_sharedManifold; + + btCompoundCompoundLeafCallback (const btCollisionObjectWrapper* compound1ObjWrap, + const btCollisionObjectWrapper* compound0ObjWrap, + btDispatcher* dispatcher, + const btDispatcherInfo& dispatchInfo, + btManifoldResult* resultOut, + btHashedSimplePairCache* childAlgorithmsCache, + btPersistentManifold* sharedManifold) + :m_compound0ColObjWrap(compound1ObjWrap),m_compound1ColObjWrap(compound0ObjWrap),m_dispatcher(dispatcher),m_dispatchInfo(dispatchInfo),m_resultOut(resultOut), + m_childCollisionAlgorithmCache(childAlgorithmsCache), + m_sharedManifold(sharedManifold), + m_numOverlapPairs(0) + { + + } + + + + + void Process(const btDbvtNode* leaf0,const btDbvtNode* leaf1) + { + m_numOverlapPairs++; + + + int childIndex0 = leaf0->dataAsInt; + int childIndex1 = leaf1->dataAsInt; + + + btAssert(childIndex0>=0); + btAssert(childIndex1>=0); + + + const btCompoundShape* compoundShape0 = static_cast(m_compound0ColObjWrap->getCollisionShape()); + btAssert(childIndex0getNumChildShapes()); + + const btCompoundShape* compoundShape1 = static_cast(m_compound1ColObjWrap->getCollisionShape()); + btAssert(childIndex1getNumChildShapes()); + + const btCollisionShape* childShape0 = compoundShape0->getChildShape(childIndex0); + const btCollisionShape* childShape1 = compoundShape1->getChildShape(childIndex1); + + //backup + btTransform orgTrans0 = m_compound0ColObjWrap->getWorldTransform(); + const btTransform& childTrans0 = compoundShape0->getChildTransform(childIndex0); + btTransform newChildWorldTrans0 = orgTrans0*childTrans0 ; + + btTransform orgTrans1 = m_compound1ColObjWrap->getWorldTransform(); + const btTransform& childTrans1 = compoundShape1->getChildTransform(childIndex1); + btTransform newChildWorldTrans1 = orgTrans1*childTrans1 ; + + + //perform an AABB check first + btVector3 aabbMin0,aabbMax0,aabbMin1,aabbMax1; + childShape0->getAabb(newChildWorldTrans0,aabbMin0,aabbMax0); + childShape1->getAabb(newChildWorldTrans1,aabbMin1,aabbMax1); + + if (gCompoundCompoundChildShapePairCallback) + { + if (!gCompoundCompoundChildShapePairCallback(childShape0,childShape1)) + return; + } + + if (TestAabbAgainstAabb2(aabbMin0,aabbMax0,aabbMin1,aabbMax1)) + { + btCollisionObjectWrapper compoundWrap0(this->m_compound0ColObjWrap,childShape0, m_compound0ColObjWrap->getCollisionObject(),newChildWorldTrans0,-1,childIndex0); + btCollisionObjectWrapper compoundWrap1(this->m_compound1ColObjWrap,childShape1,m_compound1ColObjWrap->getCollisionObject(),newChildWorldTrans1,-1,childIndex1); + + + btSimplePair* pair = m_childCollisionAlgorithmCache->findPair(childIndex0,childIndex1); + + btCollisionAlgorithm* colAlgo = 0; + + if (pair) + { + colAlgo = (btCollisionAlgorithm*)pair->m_userPointer; + + } else + { + colAlgo = m_dispatcher->findAlgorithm(&compoundWrap0,&compoundWrap1,m_sharedManifold); + pair = m_childCollisionAlgorithmCache->addOverlappingPair(childIndex0,childIndex1); + btAssert(pair); + pair->m_userPointer = colAlgo; + } + + btAssert(colAlgo); + + const btCollisionObjectWrapper* tmpWrap0 = 0; + const btCollisionObjectWrapper* tmpWrap1 = 0; + + tmpWrap0 = m_resultOut->getBody0Wrap(); + tmpWrap1 = m_resultOut->getBody1Wrap(); + + m_resultOut->setBody0Wrap(&compoundWrap0); + m_resultOut->setBody1Wrap(&compoundWrap1); + + m_resultOut->setShapeIdentifiersA(-1,childIndex0); + m_resultOut->setShapeIdentifiersB(-1,childIndex1); + + + colAlgo->processCollision(&compoundWrap0,&compoundWrap1,m_dispatchInfo,m_resultOut); + + m_resultOut->setBody0Wrap(tmpWrap0); + m_resultOut->setBody1Wrap(tmpWrap1); + + + + } + } +}; + + +static DBVT_INLINE bool MyIntersect( const btDbvtAabbMm& a, + const btDbvtAabbMm& b, const btTransform& xform) +{ + btVector3 newmin,newmax; + btTransformAabb(b.Mins(),b.Maxs(),0.f,xform,newmin,newmax); + btDbvtAabbMm newb = btDbvtAabbMm::FromMM(newmin,newmax); + return Intersect(a,newb); +} + + +static inline void MycollideTT( const btDbvtNode* root0, + const btDbvtNode* root1, + const btTransform& xform, + btCompoundCompoundLeafCallback* callback) +{ + + if(root0&&root1) + { + int depth=1; + int treshold=btDbvt::DOUBLE_STACKSIZE-4; + btAlignedObjectArray stkStack; + stkStack.resize(btDbvt::DOUBLE_STACKSIZE); + stkStack[0]=btDbvt::sStkNN(root0,root1); + do { + btDbvt::sStkNN p=stkStack[--depth]; + if(MyIntersect(p.a->volume,p.b->volume,xform)) + { + if(depth>treshold) + { + stkStack.resize(stkStack.size()*2); + treshold=stkStack.size()-4; + } + if(p.a->isinternal()) + { + if(p.b->isinternal()) + { + stkStack[depth++]=btDbvt::sStkNN(p.a->childs[0],p.b->childs[0]); + stkStack[depth++]=btDbvt::sStkNN(p.a->childs[1],p.b->childs[0]); + stkStack[depth++]=btDbvt::sStkNN(p.a->childs[0],p.b->childs[1]); + stkStack[depth++]=btDbvt::sStkNN(p.a->childs[1],p.b->childs[1]); + } + else + { + stkStack[depth++]=btDbvt::sStkNN(p.a->childs[0],p.b); + stkStack[depth++]=btDbvt::sStkNN(p.a->childs[1],p.b); + } + } + else + { + if(p.b->isinternal()) + { + stkStack[depth++]=btDbvt::sStkNN(p.a,p.b->childs[0]); + stkStack[depth++]=btDbvt::sStkNN(p.a,p.b->childs[1]); + } + else + { + callback->Process(p.a,p.b); + } + } + } + } while(depth); + } +} + +void btCompoundCompoundCollisionAlgorithm::processCollision (const btCollisionObjectWrapper* body0Wrap,const btCollisionObjectWrapper* body1Wrap,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + + const btCollisionObjectWrapper* col0ObjWrap = body0Wrap; + const btCollisionObjectWrapper* col1ObjWrap= body1Wrap; + + btAssert (col0ObjWrap->getCollisionShape()->isCompound()); + btAssert (col1ObjWrap->getCollisionShape()->isCompound()); + const btCompoundShape* compoundShape0 = static_cast(col0ObjWrap->getCollisionShape()); + const btCompoundShape* compoundShape1 = static_cast(col1ObjWrap->getCollisionShape()); + + ///btCompoundShape might have changed: + ////make sure the internal child collision algorithm caches are still valid + if ((compoundShape0->getUpdateRevision() != m_compoundShapeRevision0) || (compoundShape1->getUpdateRevision() != m_compoundShapeRevision1)) + { + ///clear all + removeChildAlgorithms(); + } + + + ///we need to refresh all contact manifolds + ///note that we should actually recursively traverse all children, btCompoundShape can nested more then 1 level deep + ///so we should add a 'refreshManifolds' in the btCollisionAlgorithm + { + int i; + btManifoldArray manifoldArray; + btSimplePairArray& pairs = m_childCollisionAlgorithmCache->getOverlappingPairArray(); + for (i=0;igetAllContactManifolds(manifoldArray); + for (int m=0;mgetNumContacts()) + { + resultOut->setPersistentManifold(manifoldArray[m]); + resultOut->refreshContactPoints(); + resultOut->setPersistentManifold(0); + } + } + manifoldArray.resize(0); + } + } + } + + + const btDbvt* tree0 = compoundShape0->getDynamicAabbTree(); + const btDbvt* tree1 = compoundShape1->getDynamicAabbTree(); + + btCompoundCompoundLeafCallback callback(col0ObjWrap,col1ObjWrap,this->m_dispatcher,dispatchInfo,resultOut,this->m_childCollisionAlgorithmCache,m_sharedManifold); + + + const btTransform xform=col0ObjWrap->getWorldTransform().inverse()*col1ObjWrap->getWorldTransform(); + MycollideTT(tree0->m_root,tree1->m_root,xform,&callback); + + //printf("#compound-compound child/leaf overlap =%d \r",callback.m_numOverlapPairs); + + //remove non-overlapping child pairs + + { + btAssert(m_removePairs.size()==0); + + //iterate over all children, perform an AABB check inside ProcessChildShape + btSimplePairArray& pairs = m_childCollisionAlgorithmCache->getOverlappingPairArray(); + + int i; + btManifoldArray manifoldArray; + + + + + + btVector3 aabbMin0,aabbMax0,aabbMin1,aabbMax1; + + for (i=0;igetChildShape(pairs[i].m_indexA); + orgTrans0 = col0ObjWrap->getWorldTransform(); + orgInterpolationTrans0 = col0ObjWrap->getWorldTransform(); + const btTransform& childTrans0 = compoundShape0->getChildTransform(pairs[i].m_indexA); + newChildWorldTrans0 = orgTrans0*childTrans0 ; + childShape0->getAabb(newChildWorldTrans0,aabbMin0,aabbMax0); + } + + { + btTransform orgInterpolationTrans1; + const btCollisionShape* childShape1 = 0; + btTransform orgTrans1; + btTransform newChildWorldTrans1; + + childShape1 = compoundShape1->getChildShape(pairs[i].m_indexB); + orgTrans1 = col1ObjWrap->getWorldTransform(); + orgInterpolationTrans1 = col1ObjWrap->getWorldTransform(); + const btTransform& childTrans1 = compoundShape1->getChildTransform(pairs[i].m_indexB); + newChildWorldTrans1 = orgTrans1*childTrans1 ; + childShape1->getAabb(newChildWorldTrans1,aabbMin1,aabbMax1); + } + + + + if (!TestAabbAgainstAabb2(aabbMin0,aabbMax0,aabbMin1,aabbMax1)) + { + algo->~btCollisionAlgorithm(); + m_dispatcher->freeCollisionAlgorithm(algo); + m_removePairs.push_back(btSimplePair(pairs[i].m_indexA,pairs[i].m_indexB)); + } + } + } + for (int i=0;iremoveOverlappingPair(m_removePairs[i].m_indexA,m_removePairs[i].m_indexB); + } + m_removePairs.clear(); + } + +} + +btScalar btCompoundCompoundCollisionAlgorithm::calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut) +{ + btAssert(0); + return 0.f; + +} + + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.h new file mode 100644 index 000000000..7e2d7ad70 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.h @@ -0,0 +1,90 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. + +*/ + +#ifndef BT_COMPOUND_COMPOUND_COLLISION_ALGORITHM_H +#define BT_COMPOUND_COMPOUND_COLLISION_ALGORITHM_H + +#include "BulletCollision/CollisionDispatch/btActivatingCollisionAlgorithm.h" +#include "BulletCollision/BroadphaseCollision/btDispatcher.h" +#include "BulletCollision/BroadphaseCollision/btBroadphaseInterface.h" + +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +class btDispatcher; +#include "BulletCollision/BroadphaseCollision/btBroadphaseProxy.h" +#include "BulletCollision/CollisionDispatch/btCollisionCreateFunc.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "BulletCollision/CollisionDispatch/btHashedSimplePairCache.h" +class btDispatcher; +class btCollisionObject; + +class btCollisionShape; +typedef bool (*btShapePairCallback)(const btCollisionShape* pShape0, const btCollisionShape* pShape1); +extern btShapePairCallback gCompoundCompoundChildShapePairCallback; + +/// btCompoundCompoundCollisionAlgorithm supports collision between two btCompoundCollisionShape shapes +class btCompoundCompoundCollisionAlgorithm : public btActivatingCollisionAlgorithm +{ + + class btHashedSimplePairCache* m_childCollisionAlgorithmCache; + btSimplePairArray m_removePairs; + + class btPersistentManifold* m_sharedManifold; + bool m_ownsManifold; + + + int m_compoundShapeRevision0;//to keep track of changes, so that childAlgorithm array can be updated + int m_compoundShapeRevision1; + + void removeChildAlgorithms(); + +// void preallocateChildAlgorithms(const btCollisionObjectWrapper* body0Wrap,const btCollisionObjectWrapper* body1Wrap); + +public: + + btCompoundCompoundCollisionAlgorithm( const btCollisionAlgorithmConstructionInfo& ci,const btCollisionObjectWrapper* body0Wrap,const btCollisionObjectWrapper* body1Wrap,bool isSwapped); + + virtual ~btCompoundCompoundCollisionAlgorithm(); + + + + virtual void processCollision (const btCollisionObjectWrapper* body0Wrap,const btCollisionObjectWrapper* body1Wrap,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + btScalar calculateTimeOfImpact(btCollisionObject* body0,btCollisionObject* body1,const btDispatcherInfo& dispatchInfo,btManifoldResult* resultOut); + + virtual void getAllContactManifolds(btManifoldArray& manifoldArray); + + + struct CreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, const btCollisionObjectWrapper* body0Wrap,const btCollisionObjectWrapper* body1Wrap) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btCompoundCompoundCollisionAlgorithm)); + return new(mem) btCompoundCompoundCollisionAlgorithm(ci,body0Wrap,body1Wrap,false); + } + }; + + struct SwappedCreateFunc :public btCollisionAlgorithmCreateFunc + { + virtual btCollisionAlgorithm* CreateCollisionAlgorithm(btCollisionAlgorithmConstructionInfo& ci, const btCollisionObjectWrapper* body0Wrap,const btCollisionObjectWrapper* body1Wrap) + { + void* mem = ci.m_dispatcher1->allocateCollisionAlgorithm(sizeof(btCompoundCompoundCollisionAlgorithm)); + return new(mem) btCompoundCompoundCollisionAlgorithm(ci,body0Wrap,body1Wrap,true); + } + }; + +}; + +#endif //BT_COMPOUND_COMPOUND_COLLISION_ALGORITHM_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp index 3e1afede1..4ec9ae713 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp @@ -132,7 +132,6 @@ void btConvex2dConvex2dAlgorithm ::processCollision (const btCollisionObjectWrap input.m_maximumDistanceSquared*= input.m_maximumDistanceSquared; } - input.m_stackAlloc = dispatchInfo.m_stackAllocator; input.m_transformA = body0Wrap->getWorldTransform(); input.m_transformB = body1Wrap->getWorldTransform(); diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp index 6905e9737..e23f5f7a8 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.cpp @@ -76,21 +76,27 @@ void btConvexTriangleCallback::clearCache() } - -void btConvexTriangleCallback::processTriangle(btVector3* triangle,int partId, int triangleIndex) +void btConvexTriangleCallback::processTriangle(btVector3* triangle,int +partId, int triangleIndex) { - - //just for debugging purposes - //printf("triangle %d",m_triangleCount++); + if (!TestTriangleAgainstAabb2(triangle, m_aabbMin, m_aabbMax)) + { + return; + } - //aabb filter is already applied! + //just for debugging purposes + //printf("triangle %d",m_triangleCount++); + + const btCollisionObject* ob = const_cast(m_triBodyWrap->getCollisionObject()); btCollisionAlgorithmConstructionInfo ci; ci.m_dispatcher1 = m_dispatcher; //const btCollisionObject* ob = static_cast(m_triBodyWrap->getCollisionObject()); + + #if 0 ///debug drawing of the overlapping triangles @@ -110,7 +116,7 @@ void btConvexTriangleCallback::processTriangle(btVector3* triangle,int partId, i tm.setMargin(m_collisionMarginTriangle); - btCollisionObjectWrapper triObWrap(m_triBodyWrap,&tm,m_triBodyWrap->getCollisionObject(),m_triBodyWrap->getWorldTransform());//correct transform? + btCollisionObjectWrapper triObWrap(m_triBodyWrap,&tm,m_triBodyWrap->getCollisionObject(),m_triBodyWrap->getWorldTransform(),partId,triangleIndex);//correct transform? btCollisionAlgorithm* colAlgo = ci.m_dispatcher1->findAlgorithm(m_convexBodyWrap,&triObWrap,m_manifoldPtr); const btCollisionObjectWrapper* tmpWrap = 0; diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp index 62f98a846..7f2722aa4 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btConvexConvexAlgorithm.cpp @@ -373,7 +373,6 @@ void btConvexConvexAlgorithm ::processCollision (const btCollisionObjectWrapper* input.m_maximumDistanceSquared*= input.m_maximumDistanceSquared; } - input.m_stackAlloc = dispatchInfo.m_stackAllocator; input.m_transformA = body0Wrap->getWorldTransform(); input.m_transformB = body1Wrap->getWorldTransform(); diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp index 7faee6faf..c3cacec4a 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.cpp @@ -19,6 +19,8 @@ subject to the following restrictions: #include "BulletCollision/CollisionDispatch/btEmptyCollisionAlgorithm.h" #include "BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h" #include "BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h" +#include "BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.h" + #include "BulletCollision/CollisionDispatch/btConvexPlaneCollisionAlgorithm.h" #include "BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.h" #include "BulletCollision/CollisionDispatch/btSphereSphereCollisionAlgorithm.h" @@ -32,7 +34,6 @@ subject to the following restrictions: -#include "LinearMath/btStackAlloc.h" #include "LinearMath/btPoolAllocator.h" @@ -65,6 +66,10 @@ btDefaultCollisionConfiguration::btDefaultCollisionConfiguration(const btDefault m_swappedConvexConcaveCreateFunc = new (mem)btConvexConcaveCollisionAlgorithm::SwappedCreateFunc; mem = btAlignedAlloc(sizeof(btCompoundCollisionAlgorithm::CreateFunc),16); m_compoundCreateFunc = new (mem)btCompoundCollisionAlgorithm::CreateFunc; + + mem = btAlignedAlloc(sizeof(btCompoundCompoundCollisionAlgorithm::CreateFunc),16); + m_compoundCompoundCreateFunc = new (mem)btCompoundCompoundCollisionAlgorithm::CreateFunc; + mem = btAlignedAlloc(sizeof(btCompoundCollisionAlgorithm::SwappedCreateFunc),16); m_swappedCompoundCreateFunc = new (mem)btCompoundCollisionAlgorithm::SwappedCreateFunc; mem = btAlignedAlloc(sizeof(btEmptyAlgorithm::CreateFunc),16); @@ -106,16 +111,6 @@ btDefaultCollisionConfiguration::btDefaultCollisionConfiguration(const btDefault collisionAlgorithmMaxElementSize = btMax(collisionAlgorithmMaxElementSize,maxSize2); collisionAlgorithmMaxElementSize = btMax(collisionAlgorithmMaxElementSize,maxSize3); - if (constructionInfo.m_stackAlloc) - { - m_ownsStackAllocator = false; - this->m_stackAlloc = constructionInfo.m_stackAlloc; - } else - { - m_ownsStackAllocator = true; - void* mem = btAlignedAlloc(sizeof(btStackAlloc),16); - m_stackAlloc = new(mem)btStackAlloc(constructionInfo.m_defaultStackAllocatorSize); - } if (constructionInfo.m_persistentManifoldPool) { @@ -144,12 +139,6 @@ btDefaultCollisionConfiguration::btDefaultCollisionConfiguration(const btDefault btDefaultCollisionConfiguration::~btDefaultCollisionConfiguration() { - if (m_ownsStackAllocator) - { - m_stackAlloc->destroy(); - m_stackAlloc->~btStackAlloc(); - btAlignedFree(m_stackAlloc); - } if (m_ownsCollisionAlgorithmPool) { m_collisionAlgorithmPool->~btPoolAllocator(); @@ -172,6 +161,9 @@ btDefaultCollisionConfiguration::~btDefaultCollisionConfiguration() m_compoundCreateFunc->~btCollisionAlgorithmCreateFunc(); btAlignedFree( m_compoundCreateFunc); + m_compoundCompoundCreateFunc->~btCollisionAlgorithmCreateFunc(); + btAlignedFree(m_compoundCompoundCreateFunc); + m_swappedCompoundCreateFunc->~btCollisionAlgorithmCreateFunc(); btAlignedFree( m_swappedCompoundCreateFunc); @@ -275,6 +267,12 @@ btCollisionAlgorithmCreateFunc* btDefaultCollisionConfiguration::getCollisionAlg return m_swappedConvexConcaveCreateFunc; } + + if (btBroadphaseProxy::isCompound(proxyType0) && btBroadphaseProxy::isCompound(proxyType1)) + { + return m_compoundCompoundCreateFunc; + } + if (btBroadphaseProxy::isCompound(proxyType0)) { return m_compoundCreateFunc; diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h index 474785bfc..2078420e1 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h @@ -22,23 +22,19 @@ class btConvexPenetrationDepthSolver; struct btDefaultCollisionConstructionInfo { - btStackAlloc* m_stackAlloc; btPoolAllocator* m_persistentManifoldPool; btPoolAllocator* m_collisionAlgorithmPool; int m_defaultMaxPersistentManifoldPoolSize; int m_defaultMaxCollisionAlgorithmPoolSize; int m_customCollisionAlgorithmMaxElementSize; - int m_defaultStackAllocatorSize; int m_useEpaPenetrationAlgorithm; btDefaultCollisionConstructionInfo() - :m_stackAlloc(0), - m_persistentManifoldPool(0), + :m_persistentManifoldPool(0), m_collisionAlgorithmPool(0), m_defaultMaxPersistentManifoldPoolSize(4096), m_defaultMaxCollisionAlgorithmPoolSize(4096), m_customCollisionAlgorithmMaxElementSize(0), - m_defaultStackAllocatorSize(0), m_useEpaPenetrationAlgorithm(true) { } @@ -56,8 +52,6 @@ protected: int m_persistentManifoldPoolSize; - btStackAlloc* m_stackAlloc; - bool m_ownsStackAllocator; btPoolAllocator* m_persistentManifoldPool; bool m_ownsPersistentManifoldPool; @@ -75,6 +69,8 @@ protected: btCollisionAlgorithmCreateFunc* m_convexConcaveCreateFunc; btCollisionAlgorithmCreateFunc* m_swappedConvexConcaveCreateFunc; btCollisionAlgorithmCreateFunc* m_compoundCreateFunc; + btCollisionAlgorithmCreateFunc* m_compoundCompoundCreateFunc; + btCollisionAlgorithmCreateFunc* m_swappedCompoundCreateFunc; btCollisionAlgorithmCreateFunc* m_emptyCreateFunc; btCollisionAlgorithmCreateFunc* m_sphereSphereCF; @@ -105,10 +101,6 @@ public: return m_collisionAlgorithmPool; } - virtual btStackAlloc* getStackAllocator() - { - return m_stackAlloc; - } virtual btVoronoiSimplexSolver* getSimplexSolver() { diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btHashedSimplePairCache.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btHashedSimplePairCache.cpp new file mode 100644 index 000000000..cfcca5654 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btHashedSimplePairCache.cpp @@ -0,0 +1,278 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + + + +#include "btHashedSimplePairCache.h" + + +#include + +int gOverlappingSimplePairs = 0; +int gRemoveSimplePairs =0; +int gAddedSimplePairs =0; +int gFindSimplePairs =0; + + + + +btHashedSimplePairCache::btHashedSimplePairCache(): + m_blockedForChanges(false) +{ + int initialAllocatedSize= 2; + m_overlappingPairArray.reserve(initialAllocatedSize); + growTables(); +} + + + + +btHashedSimplePairCache::~btHashedSimplePairCache() +{ +} + + + + + + +void btHashedSimplePairCache::removeAllPairs() +{ + m_overlappingPairArray.clear(); + m_hashTable.clear(); + m_next.clear(); + + int initialAllocatedSize= 2; + m_overlappingPairArray.reserve(initialAllocatedSize); + growTables(); +} + + + +btSimplePair* btHashedSimplePairCache::findPair(int indexA, int indexB) +{ + gFindSimplePairs++; + + + /*if (indexA > indexB) + btSwap(indexA, indexB);*/ + + int hash = static_cast(getHash(static_cast(indexA), static_cast(indexB)) & (m_overlappingPairArray.capacity()-1)); + + if (hash >= m_hashTable.size()) + { + return NULL; + } + + int index = m_hashTable[hash]; + while (index != BT_SIMPLE_NULL_PAIR && equalsPair(m_overlappingPairArray[index], indexA, indexB) == false) + { + index = m_next[index]; + } + + if (index == BT_SIMPLE_NULL_PAIR) + { + return NULL; + } + + btAssert(index < m_overlappingPairArray.size()); + + return &m_overlappingPairArray[index]; +} + +//#include + +void btHashedSimplePairCache::growTables() +{ + + int newCapacity = m_overlappingPairArray.capacity(); + + if (m_hashTable.size() < newCapacity) + { + //grow hashtable and next table + int curHashtableSize = m_hashTable.size(); + + m_hashTable.resize(newCapacity); + m_next.resize(newCapacity); + + + int i; + + for (i= 0; i < newCapacity; ++i) + { + m_hashTable[i] = BT_SIMPLE_NULL_PAIR; + } + for (i = 0; i < newCapacity; ++i) + { + m_next[i] = BT_SIMPLE_NULL_PAIR; + } + + for(i=0;i(getHash(static_cast(indexA),static_cast(indexB)) & (m_overlappingPairArray.capacity()-1)); // New hash value with new mask + m_next[i] = m_hashTable[hashValue]; + m_hashTable[hashValue] = i; + } + + + } +} + +btSimplePair* btHashedSimplePairCache::internalAddPair(int indexA, int indexB) +{ + + int hash = static_cast(getHash(static_cast(indexA),static_cast(indexB)) & (m_overlappingPairArray.capacity()-1)); // New hash value with new mask + + + btSimplePair* pair = internalFindPair(indexA, indexB, hash); + if (pair != NULL) + { + return pair; + } + + int count = m_overlappingPairArray.size(); + int oldCapacity = m_overlappingPairArray.capacity(); + void* mem = &m_overlappingPairArray.expandNonInitializing(); + + int newCapacity = m_overlappingPairArray.capacity(); + + if (oldCapacity < newCapacity) + { + growTables(); + //hash with new capacity + hash = static_cast(getHash(static_cast(indexA),static_cast(indexB)) & (m_overlappingPairArray.capacity()-1)); + } + + pair = new (mem) btSimplePair(indexA,indexB); + + pair->m_userPointer = 0; + + m_next[count] = m_hashTable[hash]; + m_hashTable[hash] = count; + + return pair; +} + + + +void* btHashedSimplePairCache::removeOverlappingPair(int indexA, int indexB) +{ + gRemoveSimplePairs++; + + + /*if (indexA > indexB) + btSwap(indexA, indexB);*/ + + int hash = static_cast(getHash(static_cast(indexA),static_cast(indexB)) & (m_overlappingPairArray.capacity()-1)); + + btSimplePair* pair = internalFindPair(indexA, indexB, hash); + if (pair == NULL) + { + return 0; + } + + + void* userData = pair->m_userPointer; + + + int pairIndex = int(pair - &m_overlappingPairArray[0]); + btAssert(pairIndex < m_overlappingPairArray.size()); + + // Remove the pair from the hash table. + int index = m_hashTable[hash]; + btAssert(index != BT_SIMPLE_NULL_PAIR); + + int previous = BT_SIMPLE_NULL_PAIR; + while (index != pairIndex) + { + previous = index; + index = m_next[index]; + } + + if (previous != BT_SIMPLE_NULL_PAIR) + { + btAssert(m_next[previous] == pairIndex); + m_next[previous] = m_next[pairIndex]; + } + else + { + m_hashTable[hash] = m_next[pairIndex]; + } + + // We now move the last pair into spot of the + // pair being removed. We need to fix the hash + // table indices to support the move. + + int lastPairIndex = m_overlappingPairArray.size() - 1; + + // If the removed pair is the last pair, we are done. + if (lastPairIndex == pairIndex) + { + m_overlappingPairArray.pop_back(); + return userData; + } + + // Remove the last pair from the hash table. + const btSimplePair* last = &m_overlappingPairArray[lastPairIndex]; + /* missing swap here too, Nat. */ + int lastHash = static_cast(getHash(static_cast(last->m_indexA), static_cast(last->m_indexB)) & (m_overlappingPairArray.capacity()-1)); + + index = m_hashTable[lastHash]; + btAssert(index != BT_SIMPLE_NULL_PAIR); + + previous = BT_SIMPLE_NULL_PAIR; + while (index != lastPairIndex) + { + previous = index; + index = m_next[index]; + } + + if (previous != BT_SIMPLE_NULL_PAIR) + { + btAssert(m_next[previous] == lastPairIndex); + m_next[previous] = m_next[lastPairIndex]; + } + else + { + m_hashTable[lastHash] = m_next[lastPairIndex]; + } + + // Copy the last pair into the remove pair's spot. + m_overlappingPairArray[pairIndex] = m_overlappingPairArray[lastPairIndex]; + + // Insert the last pair into the hash table + m_next[pairIndex] = m_hashTable[lastHash]; + m_hashTable[lastHash] = pairIndex; + + m_overlappingPairArray.pop_back(); + + return userData; +} +//#include + + + + + + + + + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btHashedSimplePairCache.h b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btHashedSimplePairCache.h new file mode 100644 index 000000000..e88ef97e9 --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/CollisionDispatch/btHashedSimplePairCache.h @@ -0,0 +1,174 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2006 Erwin Coumans http://continuousphysics.com/Bullet/ + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_HASHED_SIMPLE_PAIR_CACHE_H +#define BT_HASHED_SIMPLE_PAIR_CACHE_H + + + +#include "LinearMath/btAlignedObjectArray.h" + +const int BT_SIMPLE_NULL_PAIR=0xffffffff; + +struct btSimplePair +{ + btSimplePair(int indexA,int indexB) + :m_indexA(indexA), + m_indexB(indexB), + m_userPointer(0) + { + } + + int m_indexA; + int m_indexB; + union + { + void* m_userPointer; + int m_userValue; + }; +}; + +typedef btAlignedObjectArray btSimplePairArray; + + + +extern int gOverlappingSimplePairs; +extern int gRemoveSimplePairs; +extern int gAddedSimplePairs; +extern int gFindSimplePairs; + + + + +class btHashedSimplePairCache +{ + btSimplePairArray m_overlappingPairArray; + + bool m_blockedForChanges; + + +protected: + + btAlignedObjectArray m_hashTable; + btAlignedObjectArray m_next; + + +public: + btHashedSimplePairCache(); + virtual ~btHashedSimplePairCache(); + + void removeAllPairs(); + + virtual void* removeOverlappingPair(int indexA,int indexB); + + // Add a pair and return the new pair. If the pair already exists, + // no new pair is created and the old one is returned. + virtual btSimplePair* addOverlappingPair(int indexA,int indexB) + { + gAddedSimplePairs++; + + return internalAddPair(indexA,indexB); + } + + + virtual btSimplePair* getOverlappingPairArrayPtr() + { + return &m_overlappingPairArray[0]; + } + + const btSimplePair* getOverlappingPairArrayPtr() const + { + return &m_overlappingPairArray[0]; + } + + btSimplePairArray& getOverlappingPairArray() + { + return m_overlappingPairArray; + } + + const btSimplePairArray& getOverlappingPairArray() const + { + return m_overlappingPairArray; + } + + + btSimplePair* findPair(int indexA,int indexB); + + int GetCount() const { return m_overlappingPairArray.size(); } + + int getNumOverlappingPairs() const + { + return m_overlappingPairArray.size(); + } +private: + + btSimplePair* internalAddPair(int indexA, int indexB); + + void growTables(); + + SIMD_FORCE_INLINE bool equalsPair(const btSimplePair& pair, int indexA, int indexB) + { + return pair.m_indexA == indexA && pair.m_indexB == indexB; + } + + + + SIMD_FORCE_INLINE unsigned int getHash(unsigned int indexA, unsigned int indexB) + { + int key = static_cast(((unsigned int)indexA) | (((unsigned int)indexB) <<16)); + // Thomas Wang's hash + + key += ~(key << 15); + key ^= (key >> 10); + key += (key << 3); + key ^= (key >> 6); + key += ~(key << 11); + key ^= (key >> 16); + return static_cast(key); + } + + + + + + SIMD_FORCE_INLINE btSimplePair* internalFindPair(int proxyIdA , int proxyIdB, int hash) + { + + int index = m_hashTable[hash]; + + while( index != BT_SIMPLE_NULL_PAIR && equalsPair(m_overlappingPairArray[index], proxyIdA, proxyIdB) == false) + { + index = m_next[index]; + } + + if ( index == BT_SIMPLE_NULL_PAIR ) + { + return NULL; + } + + btAssert(index < m_overlappingPairArray.size()); + + return &m_overlappingPairArray[index]; + } + + +}; + + + + +#endif //BT_HASHED_SIMPLE_PAIR_CACHE_H + + diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h index d1c216298..493d63553 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btBvhTriangleMeshShape.h @@ -21,7 +21,13 @@ subject to the following restrictions: #include "LinearMath/btAlignedAllocator.h" #include "btTriangleInfoMap.h" -///The btBvhTriangleMeshShape is a static-triangle mesh shape with several optimizations, such as bounding volume hierarchy and cache friendly traversal for PlayStation 3 Cell SPU. It is recommended to enable useQuantizedAabbCompression for better memory usage. +///The btBvhTriangleMeshShape is a static-triangle mesh shape, it can only be used for fixed/non-moving objects. +///If you required moving concave triangle meshes, it is recommended to perform convex decomposition +///using HACD, see Bullet/Demos/ConvexDecompositionDemo. +///Alternatively, you can use btGimpactMeshShape for moving concave triangle meshes. +///btBvhTriangleMeshShape has several optimizations, such as bounding volume hierarchy and +///cache friendly traversal for PlayStation 3 Cell SPU. +///It is recommended to enable useQuantizedAabbCompression for better memory usage. ///It takes a triangle mesh as input, for example a btTriangleMesh or btTriangleIndexVertexArray. The btBvhTriangleMeshShape class allows for triangle mesh deformations by a refit or partialRefit method. ///Instead of building the bounding volume hierarchy acceleration structure, it is also possible to serialize (save) and deserialize (load) the structure from disk. ///See Demos\ConcaveDemo\ConcavePhysicsDemo.cpp for an example. diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.cpp index 12f422f19..0aa75f2bf 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btCompoundShape.cpp @@ -273,6 +273,8 @@ void btCompoundShape::calculatePrincipalAxisTransform(btScalar* masses, btTransf + + void btCompoundShape::setLocalScaling(const btVector3& scaling) { @@ -283,7 +285,7 @@ void btCompoundShape::setLocalScaling(const btVector3& scaling) // childScale = childScale * (childTrans.getBasis() * scaling); childScale = childScale * scaling / m_localScaling; m_children[i].m_childShape->setLocalScaling(childScale); - childTrans.setOrigin((childTrans.getOrigin())*scaling); + childTrans.setOrigin((childTrans.getOrigin()) * scaling / m_localScaling); updateChildTransform(i, childTrans,false); } diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConeShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConeShape.cpp index 5e83087b3..2d83c8bfb 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConeShape.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConeShape.cpp @@ -62,6 +62,10 @@ void btConeShape::setConeUpIndex(int upIndex) default: btAssert(0); }; + + m_implicitShapeDimensions[m_coneIndices[0]] = m_radius; + m_implicitShapeDimensions[m_coneIndices[1]] = m_height; + m_implicitShapeDimensions[m_coneIndices[2]] = m_radius; } btVector3 btConeShape::coneLocalSupport(const btVector3& v) const diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConeShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConeShape.h index 5966ae48f..4a0df0d55 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConeShape.h +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConeShape.h @@ -90,6 +90,13 @@ public: } virtual void setLocalScaling(const btVector3& scaling); + + + virtual int calculateSerializeBufferSize() const; + + ///fills the dataBuffer and returns the struct name (and 0 on failure) + virtual const char* serialize(void* dataBuffer, btSerializer* serializer) const; + }; @@ -104,19 +111,61 @@ class btConeShapeX : public btConeShape return btVector3 (1,0,0); } + //debugging + virtual const char* getName()const + { + return "ConeX"; + } + + }; ///btConeShapeZ implements a Cone shape, around the Z axis class btConeShapeZ : public btConeShape { - public: - btConeShapeZ(btScalar radius,btScalar height); +public: + btConeShapeZ(btScalar radius,btScalar height); virtual btVector3 getAnisotropicRollingFrictionDirection() const { return btVector3 (0,0,1); } + //debugging + virtual const char* getName()const + { + return "ConeZ"; + } + + }; + +///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 +struct btConeShapeData +{ + btConvexInternalShapeData m_convexInternalShapeData; + + int m_upIndex; + + char m_padding[4]; +}; + +SIMD_FORCE_INLINE int btConeShape::calculateSerializeBufferSize() const +{ + return sizeof(btConeShapeData); +} + +///fills the dataBuffer and returns the struct name (and 0 on failure) +SIMD_FORCE_INLINE const char* btConeShape::serialize(void* dataBuffer, btSerializer* serializer) const +{ + btConeShapeData* shapeData = (btConeShapeData*) dataBuffer; + + btConvexInternalShape::serialize(&shapeData->m_convexInternalShapeData,serializer); + + shapeData->m_upIndex = m_coneIndices[1]; + + return "btConeShapeData"; +} + #endif //BT_CONE_MINKOWSKI_H diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.cpp index 4d0ca1451..0623e351a 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.cpp @@ -13,6 +13,10 @@ subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ +#if defined (_WIN32) || defined (__i386__) +#define BT_USE_SSE_IN_API +#endif + #include "btConvexHullShape.h" #include "BulletCollision/CollisionShapes/btCollisionMargin.h" @@ -45,10 +49,11 @@ void btConvexHullShape::setLocalScaling(const btVector3& scaling) recalcLocalAabb(); } -void btConvexHullShape::addPoint(const btVector3& point) +void btConvexHullShape::addPoint(const btVector3& point, bool recalculateLocalAabb) { m_unscaledPoints.push_back(point); - recalcLocalAabb(); + if (recalculateLocalAabb) + recalcLocalAabb(); } diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.h index f4e8f644b..3bd598ec4 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.h +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexHullShape.h @@ -36,7 +36,7 @@ public: ///btConvexHullShape make an internal copy of the points. btConvexHullShape(const btScalar* points=0,int numPoints=0, int stride=sizeof(btVector3)); - void addPoint(const btVector3& point); + void addPoint(const btVector3& point, bool recalculateLocalAabb = true); btVector3* getUnscaledPoints() diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexShape.cpp index 3ffa42228..f03d0b21e 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexShape.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btConvexShape.cpp @@ -13,10 +13,15 @@ subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ +#if defined (_WIN32) || defined (__i386__) +#define BT_USE_SSE_IN_API +#endif + #include "btConvexShape.h" #include "btTriangleShape.h" #include "btSphereShape.h" #include "btCylinderShape.h" +#include "btConeShape.h" #include "btCapsuleShape.h" #include "btConvexHullShape.h" #include "btConvexPointCloudShape.h" @@ -332,6 +337,11 @@ btScalar btConvexShape::getMarginNonVirtual () const btCylinderShape* cylShape = (btCylinderShape*)this; return cylShape->getMarginNV(); } + case CONE_SHAPE_PROXYTYPE: + { + btConeShape* conShape = (btConeShape*)this; + return conShape->getMarginNV(); + } case CAPSULE_SHAPE_PROXYTYPE: { btCapsuleShape* capsuleShape = (btCapsuleShape*)this; diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp index 8d4080a63..26322791d 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btHeightfieldTerrainShape.cpp @@ -369,7 +369,7 @@ void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback getVertex(x+1,j+1,vertices[2]); callback->processTriangle(vertices,x,j); //second triangle - getVertex(x,j,vertices[0]); + // getVertex(x,j,vertices[0]);//already got this vertex before, thanks to Danny Chapman getVertex(x+1,j+1,vertices[1]); getVertex(x,j+1,vertices[2]); callback->processTriangle(vertices,x,j); @@ -382,7 +382,7 @@ void btHeightfieldTerrainShape::processAllTriangles(btTriangleCallback* callback callback->processTriangle(vertices,x,j); //second triangle getVertex(x+1,j,vertices[0]); - getVertex(x,j+1,vertices[1]); + //getVertex(x,j+1,vertices[1]); getVertex(x+1,j+1,vertices[2]); callback->processTriangle(vertices,x,j); } diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.cpp index 5bae24250..a7362ea01 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btMultiSphereShape.cpp @@ -13,7 +13,9 @@ subject to the following restrictions: 3. This notice may not be removed or altered from any source distribution. */ - +#if defined (_WIN32) || defined (__i386__) +#define BT_USE_SSE_IN_API +#endif #include "btMultiSphereShape.h" #include "BulletCollision/CollisionShapes/btCollisionMargin.h" @@ -40,7 +42,7 @@ btMultiSphereShape::btMultiSphereShape (const btVector3* positions,const btScala } #ifndef MIN - #define MIN( _a, _b) ((_a) < (_b) ? (_a) : (_b)) + #define MIN( _a, _b) ((_a) < (_b) ? (_a) : (_b)) #endif btVector3 btMultiSphereShape::localGetSupportingVertexWithoutMargin(const btVector3& vec0)const { @@ -67,10 +69,10 @@ btMultiSphereShape::btMultiSphereShape (const btVector3* positions,const btScala const btScalar* rad = &m_radiArray[0]; int numSpheres = m_localPositionArray.size(); - for( int k = 0; k < numSpheres; k+= 128 ) - { - btVector3 temp[128]; - int inner_count = MIN( numSpheres - k, 128 ); + for( int k = 0; k < numSpheres; k+= 128 ) + { + btVector3 temp[128]; + int inner_count = MIN( numSpheres - k, 128 ); for( long i = 0; i < inner_count; i++ ) { temp[i] = (*pos) +vec*m_localScaling*(*rad) - vec * getMargin(); diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp index 7dec689bd..4854f370f 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btPolyhedralConvexShape.cpp @@ -12,6 +12,9 @@ subject to the following restrictions: 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. 3. This notice may not be removed or altered from any source distribution. */ +#if defined (_WIN32) || defined (__i386__) +#define BT_USE_SSE_IN_API +#endif #include "BulletCollision/CollisionShapes/btPolyhedralConvexShape.h" #include "btConvexPolyhedron.h" diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.cpp b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.cpp index 51a2f8a07..5fbed334e 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.cpp +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.cpp @@ -111,10 +111,10 @@ int btTriangleMesh::findOrAddVertex(const btVector3& vertex, bool removeDuplicat return i/3; } } - } - m_3componentVertices.push_back((float)vertex.getX()); - m_3componentVertices.push_back((float)vertex.getY()); - m_3componentVertices.push_back((float)vertex.getZ()); + } + m_3componentVertices.push_back(vertex.getX()); + m_3componentVertices.push_back(vertex.getY()); + m_3componentVertices.push_back(vertex.getZ()); m_indexedMeshes[0].m_numVertices++; m_indexedMeshes[0].m_vertexBase = (unsigned char*)&m_3componentVertices[0]; return (m_3componentVertices.size()/3)-1; diff --git a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.h b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.h index 29d1b5cda..0afc2321f 100644 --- a/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.h +++ b/Engine/lib/bullet/src/BulletCollision/CollisionShapes/btTriangleMesh.h @@ -27,7 +27,7 @@ subject to the following restrictions: class btTriangleMesh : public btTriangleIndexVertexArray { btAlignedObjectArray m_4componentVertices; - btAlignedObjectArray m_3componentVertices; + btAlignedObjectArray m_3componentVertices; btAlignedObjectArray m_32bitIndices; btAlignedObjectArray m_16bitIndices; diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btCompoundFromGimpact.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/btCompoundFromGimpact.h new file mode 100644 index 000000000..02f8b678a --- /dev/null +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btCompoundFromGimpact.h @@ -0,0 +1,93 @@ +#ifndef BT_COMPOUND_FROM_GIMPACT +#define BT_COMPOUND_FROM_GIMPACT + +#include "BulletCollision/CollisionShapes/btCompoundShape.h" +#include "btGImpactShape.h" +#include "BulletCollision/NarrowPhaseCollision/btRaycastCallback.h" + +struct MyCallback : public btTriangleRaycastCallback + { + int m_ignorePart; + int m_ignoreTriangleIndex; + + + MyCallback(const btVector3& from, const btVector3& to, int ignorePart, int ignoreTriangleIndex) + :btTriangleRaycastCallback(from,to), + m_ignorePart(ignorePart), + m_ignoreTriangleIndex(ignoreTriangleIndex) + { + + } + virtual btScalar reportHit(const btVector3& hitNormalLocal, btScalar hitFraction, int partId, int triangleIndex) + { + if (partId!=m_ignorePart || triangleIndex!=m_ignoreTriangleIndex) + { + if (hitFraction < m_hitFraction) + return hitFraction; + } + + return m_hitFraction; + } + }; + struct MyInternalTriangleIndexCallback :public btInternalTriangleIndexCallback + { + const btGImpactMeshShape* m_gimpactShape; + btCompoundShape* m_colShape; + btScalar m_depth; + + MyInternalTriangleIndexCallback (btCompoundShape* colShape, const btGImpactMeshShape* meshShape, btScalar depth) + :m_colShape(colShape), + m_gimpactShape(meshShape), + m_depth(depth) + { + } + + virtual void internalProcessTriangleIndex(btVector3* triangle,int partId,int triangleIndex) + { + btVector3 scale = m_gimpactShape->getLocalScaling(); + btVector3 v0=triangle[0]*scale; + btVector3 v1=triangle[1]*scale; + btVector3 v2=triangle[2]*scale; + + btVector3 centroid = (v0+v1+v2)/3; + btVector3 normal = (v1-v0).cross(v2-v0); + normal.normalize(); + btVector3 rayFrom = centroid; + btVector3 rayTo = centroid-normal*m_depth; + + MyCallback cb(rayFrom,rayTo,partId,triangleIndex); + + m_gimpactShape->processAllTrianglesRay(&cb,rayFrom, rayTo); + if (cb.m_hitFraction<1) + { + rayTo.setInterpolate3(cb.m_from,cb.m_to,cb.m_hitFraction); + //rayTo = cb.m_from; + //rayTo = rayTo.lerp(cb.m_to,cb.m_hitFraction); + //gDebugDraw.drawLine(tr(centroid),tr(centroid+normal),btVector3(1,0,0)); + } + + + + btBU_Simplex1to4* tet = new btBU_Simplex1to4(v0,v1,v2,rayTo); + btTransform ident; + ident.setIdentity(); + m_colShape->addChildShape(ident,tet); + } + }; + +btCompoundShape* btCreateCompoundFromGimpactShape(const btGImpactMeshShape* gimpactMesh, btScalar depth) +{ + btCompoundShape* colShape = new btCompoundShape(); + + btTransform tr; + tr.setIdentity(); + + MyInternalTriangleIndexCallback cb(colShape,gimpactMesh, depth); + btVector3 aabbMin,aabbMax; + gimpactMesh->getAabb(tr,aabbMin,aabbMax); + gimpactMesh->getMeshInterface()->InternalProcessAllTriangles(&cb,aabbMin,aabbMax); + + return colShape; +} + +#endif //BT_COMPOUND_FROM_GIMPACT \ No newline at end of file diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp index 5997e443e..2e87475e3 100644 --- a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.cpp @@ -231,17 +231,15 @@ void btGImpactCollisionAlgorithm::shape_vs_shape_collision( { - btCollisionObjectWrapper ob0(body0Wrap,shape0,body0Wrap->getCollisionObject(), body0Wrap->getWorldTransform()); - btCollisionObjectWrapper ob1(body1Wrap,shape1,body1Wrap->getCollisionObject(),body1Wrap->getWorldTransform()); - - btCollisionAlgorithm* algor = newAlgorithm(&ob0,&ob1); + + btCollisionAlgorithm* algor = newAlgorithm(body0Wrap,body1Wrap); // post : checkManifold is called m_resultOut->setShapeIdentifiersA(m_part0,m_triface0); m_resultOut->setShapeIdentifiersB(m_part1,m_triface1); - - algor->processCollision(&ob0,&ob1,*m_dispatchInfo,m_resultOut); - + + algor->processCollision(body0Wrap,body1Wrap,*m_dispatchInfo,m_resultOut); + algor->~btCollisionAlgorithm(); m_dispatcher->freeCollisionAlgorithm(algor); } @@ -258,8 +256,8 @@ void btGImpactCollisionAlgorithm::convex_vs_convex_collision( m_resultOut->setShapeIdentifiersA(m_part0,m_triface0); m_resultOut->setShapeIdentifiersB(m_part1,m_triface1); - btCollisionObjectWrapper ob0(body0Wrap,shape0,body0Wrap->getCollisionObject(),body0Wrap->getWorldTransform()); - btCollisionObjectWrapper ob1(body1Wrap,shape1,body1Wrap->getCollisionObject(),body1Wrap->getWorldTransform()); + btCollisionObjectWrapper ob0(body0Wrap,shape0,body0Wrap->getCollisionObject(),body0Wrap->getWorldTransform(),m_part0,m_triface0); + btCollisionObjectWrapper ob1(body1Wrap,shape1,body1Wrap->getCollisionObject(),body1Wrap->getWorldTransform(),m_part1,m_triface1); checkConvexAlgorithm(&ob0,&ob1); m_convex_algorithm->processCollision(&ob0,&ob1,*m_dispatchInfo,m_resultOut); @@ -553,8 +551,8 @@ void btGImpactCollisionAlgorithm::gimpact_vs_gimpact( tr1 = orgtrans1*shape1->getChildTransform(m_triface1); } - btCollisionObjectWrapper ob0(body0Wrap,colshape0,body0Wrap->getCollisionObject(),tr0); - btCollisionObjectWrapper ob1(body1Wrap,colshape1,body1Wrap->getCollisionObject(),tr1); + btCollisionObjectWrapper ob0(body0Wrap,colshape0,body0Wrap->getCollisionObject(),tr0,m_part0,m_triface0); + btCollisionObjectWrapper ob1(body1Wrap,colshape1,body1Wrap->getCollisionObject(),tr1,m_part1,m_triface1); //collide two convex shapes convex_vs_convex_collision(&ob0,&ob1,colshape0,colshape1); @@ -654,17 +652,29 @@ void btGImpactCollisionAlgorithm::gimpact_vs_shape(const btCollisionObjectWrappe tr0 = orgtrans0*shape0->getChildTransform(child_index); } - btCollisionObjectWrapper ob0(body0Wrap,colshape0,body0Wrap->getCollisionObject(),body0Wrap->getWorldTransform()); + btCollisionObjectWrapper ob0(body0Wrap,colshape0,body0Wrap->getCollisionObject(),body0Wrap->getWorldTransform(),m_part0,m_triface0); + const btCollisionObjectWrapper* prevObj0 = m_resultOut->getBody0Wrap(); + + if (m_resultOut->getBody0Wrap()->getCollisionObject()==ob0.getCollisionObject()) + { + m_resultOut->setBody0Wrap(&ob0); + } else + { + m_resultOut->setBody1Wrap(&ob0); + } //collide two shapes if(swapped) { + shape_vs_shape_collision(body1Wrap,&ob0,shape1,colshape0); } else { + shape_vs_shape_collision(&ob0,body1Wrap,colshape0,shape1); } + m_resultOut->setBody0Wrap(prevObj0); } @@ -686,10 +696,29 @@ void btGImpactCollisionAlgorithm::gimpact_vs_compoundshape(const btCollisionObje const btCollisionShape * colshape1 = shape1->getChildShape(i); btTransform childtrans1 = orgtrans1*shape1->getChildTransform(i); - btCollisionObjectWrapper ob1(body1Wrap,colshape1,body1Wrap->getCollisionObject(),childtrans1); + btCollisionObjectWrapper ob1(body1Wrap,colshape1,body1Wrap->getCollisionObject(),childtrans1,-1,i); + + const btCollisionObjectWrapper* tmp = 0; + if (m_resultOut->getBody0Wrap()->getCollisionObject()==ob1.getCollisionObject()) + { + tmp = m_resultOut->getBody0Wrap(); + m_resultOut->setBody0Wrap(&ob1); + } else + { + tmp = m_resultOut->getBody1Wrap(); + m_resultOut->setBody1Wrap(&ob1); + } //collide child shape gimpact_vs_shape(body0Wrap, &ob1, shape0,colshape1,swapped); + + if (m_resultOut->getBody0Wrap()->getCollisionObject()==ob1.getCollisionObject()) + { + m_resultOut->setBody0Wrap(tmp); + } else + { + m_resultOut->setBody1Wrap(tmp); + } } } @@ -778,8 +807,31 @@ public: algorithm->setPart1(partId); algorithm->setFace1(triangleIndex); } + + btCollisionObjectWrapper ob1Wrap(body1Wrap,&tri1,body1Wrap->getCollisionObject(),body1Wrap->getWorldTransform(),partId,triangleIndex); + const btCollisionObjectWrapper * tmp = 0; + + if (algorithm->internalGetResultOut()->getBody0Wrap()->getCollisionObject()==ob1Wrap.getCollisionObject()) + { + tmp = algorithm->internalGetResultOut()->getBody0Wrap(); + algorithm->internalGetResultOut()->setBody0Wrap(&ob1Wrap); + } else + { + tmp = algorithm->internalGetResultOut()->getBody1Wrap(); + algorithm->internalGetResultOut()->setBody1Wrap(&ob1Wrap); + } + algorithm->gimpact_vs_shape( - body0Wrap,body1Wrap,gimpactshape0,&tri1,swapped); + body0Wrap,&ob1Wrap,gimpactshape0,&tri1,swapped); + + if (algorithm->internalGetResultOut()->getBody0Wrap()->getCollisionObject()==ob1Wrap.getCollisionObject()) + { + algorithm->internalGetResultOut()->setBody0Wrap(tmp); + } else + { + algorithm->internalGetResultOut()->setBody1Wrap(tmp); + } + } }; diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.h index c01b1eee8..f85a94cb4 100644 --- a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.h +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactCollisionAlgorithm.h @@ -210,6 +210,10 @@ public: manifoldArray.push_back(m_manifoldPtr); } + btManifoldResult* internalGetResultOut() + { + return m_resultOut; + } struct CreateFunc :public btCollisionAlgorithmCreateFunc { diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.cpp b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.cpp index cceace55e..ac8efdf38 100644 --- a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.cpp +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.cpp @@ -25,6 +25,7 @@ subject to the following restrictions: #define CALC_EXACT_INERTIA 1 + void btGImpactCompoundShape::calculateLocalInertia(btScalar mass,btVector3& inertia) const { lockChildShapes(); @@ -144,6 +145,31 @@ void btGImpactMeshShape::rayTest(const btVector3& rayFrom, const btVector3& rayT { } +void btGImpactMeshShapePart::processAllTrianglesRay(btTriangleCallback* callback,const btVector3& rayFrom, const btVector3& rayTo) const +{ + lockChildShapes(); + + btAlignedObjectArray collided; + btVector3 rayDir(rayTo - rayFrom); + rayDir.normalize(); + m_box_set.rayQuery(rayDir, rayFrom, collided); + + if(collided.size()==0) + { + unlockChildShapes(); + return; + } + + int part = (int)getPart(); + btPrimitiveTriangle triangle; + int i = collided.size(); + while(i--) + { + getPrimitiveTriangle(collided[i],triangle); + callback->processTriangle(triangle.m_vertices,part,collided[i]); + } + unlockChildShapes(); +} void btGImpactMeshShapePart::processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const { @@ -182,6 +208,15 @@ void btGImpactMeshShape::processAllTriangles(btTriangleCallback* callback,const } } +void btGImpactMeshShape::processAllTrianglesRay(btTriangleCallback* callback,const btVector3& rayFrom, const btVector3& rayTo) const +{ + int i = m_mesh_parts.size(); + while(i--) + { + m_mesh_parts[i]->processAllTrianglesRay(callback, rayFrom, rayTo); + } +} + ///fills the dataBuffer and returns the struct name (and 0 on failure) const char* btGImpactMeshShape::serialize(void* dataBuffer, btSerializer* serializer) const diff --git a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.h b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.h index dbcd4d701..3d1f48d47 100644 --- a/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.h +++ b/Engine/lib/bullet/src/BulletCollision/Gimpact/btGImpactShape.h @@ -51,6 +51,7 @@ enum eGIMPACT_SHAPE_TYPE }; + //! Helper class for tetrahedrons class btTetrahedronShapeEx:public btBU_Simplex1to4 { @@ -288,6 +289,15 @@ public: (void) callback; (void) aabbMin; (void) aabbMax; } + //! Function for retrieve triangles. + /*! + It gives the triangles in local space + */ + virtual void processAllTrianglesRay(btTriangleCallback* /*callback*/,const btVector3& /*rayFrom*/, const btVector3& /*rayTo*/) const + { + + } + //!@} }; @@ -635,25 +645,25 @@ public: return (int )numverts; } - SIMD_FORCE_INLINE void get_indices(int face_index,int &i0,int &i1,int &i2) const + SIMD_FORCE_INLINE void get_indices(int face_index,unsigned int &i0,unsigned int &i1,unsigned int &i2) const { if(indicestype == PHY_SHORT) { - unsigned short * s_indices = (unsigned short *)(indexbase + face_index*indexstride); + unsigned short* s_indices = (unsigned short *)(indexbase + face_index * indexstride); i0 = s_indices[0]; i1 = s_indices[1]; i2 = s_indices[2]; } else { - int * i_indices = (int *)(indexbase + face_index*indexstride); + unsigned int * i_indices = (unsigned int *)(indexbase + face_index*indexstride); i0 = i_indices[0]; i1 = i_indices[1]; i2 = i_indices[2]; } } - SIMD_FORCE_INLINE void get_vertex(int vertex_index, btVector3 & vertex) const + SIMD_FORCE_INLINE void get_vertex(unsigned int vertex_index, btVector3 & vertex) const { if(type == PHY_DOUBLE) { @@ -682,7 +692,7 @@ public: virtual void get_primitive_triangle(int prim_index,btPrimitiveTriangle & triangle) const { - int indices[3]; + unsigned int indices[3]; get_indices(prim_index,indices[0],indices[1],indices[2]); get_vertex(indices[0],triangle.m_vertices[0]); get_vertex(indices[1],triangle.m_vertices[1]); @@ -692,7 +702,7 @@ public: SIMD_FORCE_INLINE void get_bullet_triangle(int prim_index,btTriangleShapeEx & triangle) const { - int indices[3]; + unsigned int indices[3]; get_indices(prim_index,indices[0],indices[1],indices[2]); get_vertex(indices[0],triangle.m_vertices1[0]); get_vertex(indices[1],triangle.m_vertices1[1]); @@ -885,6 +895,7 @@ public: } virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; + virtual void processAllTrianglesRay(btTriangleCallback* callback,const btVector3& rayFrom,const btVector3& rayTo) const; }; @@ -1141,6 +1152,8 @@ public: */ virtual void processAllTriangles(btTriangleCallback* callback,const btVector3& aabbMin,const btVector3& aabbMax) const; + virtual void processAllTrianglesRay (btTriangleCallback* callback,const btVector3& rayFrom,const btVector3& rayTo) const; + virtual int calculateSerializeBufferSize() const; ///fills the dataBuffer and returns the struct name (and 0 on failure) diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h index 72eb5aec4..29620abff 100644 --- a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btConvexPenetrationDepthSolver.h @@ -17,7 +17,6 @@ subject to the following restrictions: #ifndef BT_CONVEX_PENETRATION_DEPTH_H #define BT_CONVEX_PENETRATION_DEPTH_H -class btStackAlloc; class btVector3; #include "btSimplexSolverInterface.h" class btConvexShape; @@ -33,8 +32,7 @@ public: const btConvexShape* convexA,const btConvexShape* convexB, const btTransform& transA,const btTransform& transB, btVector3& v, btVector3& pa, btVector3& pb, - class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc - ) = 0; + class btIDebugDraw* debugDraw) = 0; }; diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h index f958cc523..46ce1ab75 100644 --- a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h @@ -19,7 +19,6 @@ subject to the following restrictions: #include "LinearMath/btTransform.h" #include "LinearMath/btVector3.h" -class btStackAlloc; /// This interface is made to be used by an iterative approach to do TimeOfImpact calculations /// This interface allows to query for closest points and penetration depth between two (convex) objects @@ -43,15 +42,13 @@ struct btDiscreteCollisionDetectorInterface struct ClosestPointInput { ClosestPointInput() - :m_maximumDistanceSquared(btScalar(BT_LARGE_FLOAT)), - m_stackAlloc(0) + :m_maximumDistanceSquared(btScalar(BT_LARGE_FLOAT)) { } btTransform m_transformA; btTransform m_transformB; btScalar m_maximumDistanceSquared; - btStackAlloc* m_stackAlloc; }; virtual ~btDiscreteCollisionDetectorInterface() {}; diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp index c6dc3f3a6..572ec36f5 100644 --- a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.cpp @@ -25,7 +25,7 @@ bool btGjkEpaPenetrationDepthSolver::calcPenDepth( btSimplexSolverInterface& sim const btConvexShape* pConvexA, const btConvexShape* pConvexB, const btTransform& transformA, const btTransform& transformB, btVector3& v, btVector3& wWitnessOnA, btVector3& wWitnessOnB, - class btIDebugDraw* debugDraw, btStackAlloc* stackAlloc ) + class btIDebugDraw* debugDraw) { (void)debugDraw; @@ -34,7 +34,7 @@ bool btGjkEpaPenetrationDepthSolver::calcPenDepth( btSimplexSolverInterface& sim // const btScalar radialmargin(btScalar(0.)); - btVector3 guessVector(transformA.getOrigin()-transformB.getOrigin()); + btVector3 guessVector(transformB.getOrigin()-transformA.getOrigin()); btGjkEpaSolver2::sResults results; diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h index a49689a15..1ed6340af 100644 --- a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkEpaPenetrationDepthSolver.h @@ -33,7 +33,7 @@ class btGjkEpaPenetrationDepthSolver : public btConvexPenetrationDepthSolver const btConvexShape* pConvexA, const btConvexShape* pConvexB, const btTransform& transformA, const btTransform& transformB, btVector3& v, btVector3& wWitnessOnA, btVector3& wWitnessOnB, - class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc ); + class btIDebugDraw* debugDraw); private : diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp index 8af16b9cf..887757949 100644 --- a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.cpp @@ -50,7 +50,8 @@ m_marginA(objectA->getMargin()), m_marginB(objectB->getMargin()), m_ignoreMargin(false), m_lastUsedMethod(-1), -m_catchDegeneracies(1) +m_catchDegeneracies(1), +m_fixContactNormalDirection(1) { } btGjkPairDetector::btGjkPairDetector(const btConvexShape* objectA,const btConvexShape* objectB,int shapeTypeA,int shapeTypeB,btScalar marginA, btScalar marginB, btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver* penetrationDepthSolver) @@ -65,7 +66,8 @@ m_marginA(marginA), m_marginB(marginB), m_ignoreMargin(false), m_lastUsedMethod(-1), -m_catchDegeneracies(1) +m_catchDegeneracies(1), +m_fixContactNormalDirection(1) { } @@ -353,7 +355,7 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu m_minkowskiA,m_minkowskiB, localTransA,localTransB, m_cachedSeparatingAxis, tmpPointOnA, tmpPointOnB, - debugDraw,input.m_stackAlloc + debugDraw ); @@ -438,6 +440,27 @@ void btGjkPairDetector::getClosestPointsNonVirtual(const ClosestPointInput& inpu } #endif + if (m_fixContactNormalDirection) + { + ///@workaround for sticky convex collisions + //in some degenerate cases (usually when the use uses very small margins) + //the contact normal is pointing the wrong direction + //so fix it now (until we can deal with all degenerate cases in GJK and EPA) + //contact normals need to point from B to A in all cases, so we can simply check if the contact normal really points from B to A + //We like to use a dot product of the normal against the difference of the centroids, + //once the centroid is available in the API + //until then we use the center of the aabb to approximate the centroid + btVector3 aabbMin,aabbMax; + m_minkowskiA->getAabb(localTransA,aabbMin,aabbMax); + btVector3 posA = (aabbMax+aabbMin)*btScalar(0.5); + + m_minkowskiB->getAabb(localTransB,aabbMin,aabbMax); + btVector3 posB = (aabbMin+aabbMax)*btScalar(0.5); + + btVector3 diff = posA-posB; + if (diff.dot(normalInB) < 0.f) + normalInB *= -1.f; + } m_cachedSeparatingAxis = normalInB; m_cachedSeparatingDistance = distance; diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h index f0043b8b9..feeae6862 100644 --- a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btGjkPairDetector.h @@ -52,7 +52,7 @@ public: int m_curIter; int m_degenerateSimplex; int m_catchDegeneracies; - + int m_fixContactNormalDirection; btGjkPairDetector(const btConvexShape* objectA,const btConvexShape* objectB,btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver* penetrationDepthSolver); btGjkPairDetector(const btConvexShape* objectA,const btConvexShape* objectB,int shapeTypeA,int shapeTypeB,btScalar marginA, btScalar marginB, btSimplexSolverInterface* simplexSolver,btConvexPenetrationDepthSolver* penetrationDepthSolver); diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp index fe31f08d6..fa45f4903 100644 --- a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.cpp @@ -26,11 +26,10 @@ bool btMinkowskiPenetrationDepthSolver::calcPenDepth(btSimplexSolverInterface& s const btConvexShape* convexA,const btConvexShape* convexB, const btTransform& transA,const btTransform& transB, btVector3& v, btVector3& pa, btVector3& pb, - class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc + class btIDebugDraw* debugDraw ) { - (void)stackAlloc; (void)v; bool check2d= convexA->isConvex2d() && convexB->isConvex2d(); diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h index 6a8fe52f3..fd533b4fc 100644 --- a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btMinkowskiPenetrationDepthSolver.h @@ -32,7 +32,7 @@ public: const btConvexShape* convexA,const btConvexShape* convexB, const btTransform& transA,const btTransform& transB, btVector3& v, btVector3& pa, btVector3& pb, - class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc + class btIDebugDraw* debugDraw ); }; diff --git a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h index f012889a7..3999d4005 100644 --- a/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h +++ b/Engine/lib/bullet/src/BulletCollision/NarrowPhaseCollision/btRaycastCallback.h @@ -35,7 +35,7 @@ public: kF_None = 0, kF_FilterBackfaces = 1 << 0, kF_KeepUnflippedNormal = 1 << 1, // Prevents returned face normal getting flipped when a ray hits a back-facing triangle - + kF_UseSubSimplexConvexCastRaytest = 1 << 2, // Uses an approximate but faster ray versus convex intersection algorithm kF_Terminator = 0xFFFFFFFF }; unsigned int m_flags; diff --git a/Engine/lib/bullet/src/BulletDynamics/CMakeLists.txt b/Engine/lib/bullet/src/BulletDynamics/CMakeLists.txt index 100cd7a88..cc4727639 100644 --- a/Engine/lib/bullet/src/BulletDynamics/CMakeLists.txt +++ b/Engine/lib/bullet/src/BulletDynamics/CMakeLists.txt @@ -6,6 +6,7 @@ SET(BulletDynamics_SRCS Character/btKinematicCharacterController.cpp ConstraintSolver/btConeTwistConstraint.cpp ConstraintSolver/btContactConstraint.cpp + ConstraintSolver/btFixedConstraint.cpp ConstraintSolver/btGearConstraint.cpp ConstraintSolver/btGeneric6DofConstraint.cpp ConstraintSolver/btGeneric6DofSpringConstraint.cpp @@ -23,6 +24,15 @@ SET(BulletDynamics_SRCS Dynamics/Bullet-C-API.cpp Vehicle/btRaycastVehicle.cpp Vehicle/btWheelInfo.cpp + Featherstone/btMultiBody.cpp + Featherstone/btMultiBodyConstraintSolver.cpp + Featherstone/btMultiBodyDynamicsWorld.cpp + Featherstone/btMultiBodyJointLimitConstraint.cpp + Featherstone/btMultiBodyConstraint.cpp + Featherstone/btMultiBodyPoint2Point.cpp + Featherstone/btMultiBodyJointMotor.cpp + MLCPSolvers/btDantzigLCP.cpp + MLCPSolvers/btMLCPSolver.cpp ) SET(Root_HDRS @@ -34,6 +44,7 @@ SET(ConstraintSolver_HDRS ConstraintSolver/btConstraintSolver.h ConstraintSolver/btContactConstraint.h ConstraintSolver/btContactSolverInfo.h + ConstraintSolver/btFixedConstraint.h ConstraintSolver/btGearConstraint.h ConstraintSolver/btGeneric6DofConstraint.h ConstraintSolver/btGeneric6DofSpringConstraint.h @@ -62,6 +73,29 @@ SET(Vehicle_HDRS Vehicle/btWheelInfo.h ) +SET(Featherstone_HDRS + Featherstone/btMultiBody.h + Featherstone/btMultiBodyConstraintSolver.h + Featherstone/btMultiBodyDynamicsWorld.h + Featherstone/btMultiBodyLink.h + Featherstone/btMultiBodyLinkCollider.h + Featherstone/btMultiBodySolverConstraint.h + Featherstone/btMultiBodyConstraint.h + Featherstone/btMultiBodyJointLimitConstraint.h + Featherstone/btMultiBodyConstraint.h + Featherstone/btMultiBodyPoint2Point.h + Featherstone/btMultiBodyJointMotor.h +) + +SET(MLCPSolvers_HDRS + MLCPSolvers/btDantzigLCP.h + MLCPSolvers/btDantzigSolver.h + MLCPSolvers/btMLCPSolver.h + MLCPSolvers/btMLCPSolverInterface.h + MLCPSolvers/btPATHSolver.h + MLCPSolvers/btSolveProjectedGaussSeidel.h +) + SET(Character_HDRS Character/btCharacterControllerInterface.h Character/btKinematicCharacterController.h @@ -75,6 +109,8 @@ SET(BulletDynamics_HDRS ${Dynamics_HDRS} ${Vehicle_HDRS} ${Character_HDRS} + ${Featherstone_HDRS} + ${MLCPSolvers_HDRS} ) @@ -91,7 +127,9 @@ IF (INSTALL_LIBS) IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) INSTALL(TARGETS BulletDynamics DESTINATION .) ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) - INSTALL(TARGETS BulletDynamics DESTINATION lib${LIB_SUFFIX}) + INSTALL(TARGETS BulletDynamics RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ARCHIVE DESTINATION lib${LIB_SUFFIX}) INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION ${INCLUDE_INSTALL_DIR} FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE PATTERN "CMakeFiles" EXCLUDE) @@ -108,7 +146,8 @@ DESTINATION ${INCLUDE_INSTALL_DIR}/BulletDynamics) SET_PROPERTY(SOURCE ${Dynamics_HDRS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/Dynamics) SET_PROPERTY(SOURCE ${Vehicle_HDRS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/Vehicle) SET_PROPERTY(SOURCE ${Character_HDRS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/Character) - + SET_PROPERTY(SOURCE ${Featherstone_HDRS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/Featherstone) + SET_PROPERTY(SOURCE ${MLCPSolvers_HDRS} PROPERTY MACOSX_PACKAGE_LOCATION Headers/MLCPSolvers) ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) ENDIF (NOT INTERNAL_CREATE_DISTRIBUTABLE_MSVC_PROJECTFILES) ENDIF (INSTALL_LIBS) diff --git a/Engine/lib/bullet/src/BulletDynamics/Character/btCharacterControllerInterface.h b/Engine/lib/bullet/src/BulletDynamics/Character/btCharacterControllerInterface.h index c81813c92..dffb06dfe 100644 --- a/Engine/lib/bullet/src/BulletDynamics/Character/btCharacterControllerInterface.h +++ b/Engine/lib/bullet/src/BulletDynamics/Character/btCharacterControllerInterface.h @@ -31,7 +31,7 @@ public: virtual void setWalkDirection(const btVector3& walkDirection) = 0; virtual void setVelocityForTimeInterval(const btVector3& velocity, btScalar timeInterval) = 0; - virtual void reset () = 0; + virtual void reset ( btCollisionWorld* collisionWorld ) = 0; virtual void warp (const btVector3& origin) = 0; virtual void preStep ( btCollisionWorld* collisionWorld) = 0; @@ -40,6 +40,7 @@ public: virtual void jump () = 0; virtual bool onGround () const = 0; + virtual void setUpInterpolate (bool value) = 0; }; #endif //BT_CHARACTER_CONTROLLER_INTERFACE_H diff --git a/Engine/lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.cpp b/Engine/lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.cpp index 3b9a7f14c..8f1cd20bf 100644 --- a/Engine/lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.cpp +++ b/Engine/lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.cpp @@ -14,6 +14,7 @@ subject to the following restrictions: */ +#include #include "LinearMath/btIDebugDraw.h" #include "BulletCollision/CollisionDispatch/btGhostObject.h" #include "BulletCollision/CollisionShapes/btMultiSphereShape.h" @@ -77,6 +78,9 @@ public: if (convexResult.m_hitCollisionObject == m_me) return btScalar(1.0); + if (!convexResult.m_hitCollisionObject->hasContactResponse()) + return btScalar(1.0); + btVector3 hitNormalWorld; if (normalInWorldSpace) { @@ -146,7 +150,11 @@ btKinematicCharacterController::btKinematicCharacterController (btPairCachingGho m_jumpSpeed = 10.0; // ? m_wasOnGround = false; m_wasJumping = false; + m_interpolateUp = true; setMaxSlope(btRadians(45.0)); + m_currentStepOffset = 0; + full_drop = false; + bounce_fix = false; } btKinematicCharacterController::~btKinematicCharacterController () @@ -187,6 +195,12 @@ bool btKinematicCharacterController::recoverFromPenetration ( btCollisionWorld* m_manifoldArray.resize(0); btBroadphasePair* collisionPair = &m_ghostObject->getOverlappingPairCache()->getOverlappingPairArray()[i]; + + btCollisionObject* obj0 = static_cast(collisionPair->m_pProxy0->m_clientObject); + btCollisionObject* obj1 = static_cast(collisionPair->m_pProxy1->m_clientObject); + + if ((obj0 && !obj0->hasContactResponse()) || (obj1 && !obj1->hasContactResponse())) + continue; if (collisionPair->m_algorithm) collisionPair->m_algorithm->getAllContactManifolds(m_manifoldArray); @@ -260,7 +274,10 @@ void btKinematicCharacterController::stepUp ( btCollisionWorld* world) { // we moved up only a fraction of the step height m_currentStepOffset = m_stepHeight * callback.m_closestHitFraction; - m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); + if (m_interpolateUp == true) + m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); + else + m_currentPosition = m_targetPosition; } m_verticalVelocity = 0.0; m_verticalOffset = 0.0; @@ -325,7 +342,8 @@ void btKinematicCharacterController::stepForwardAndStrafe ( btCollisionWorld* co { if (m_normalizedDirection.dot(m_touchingNormal) > btScalar(0.0)) { - updateTargetPositionBasedOnCollision (m_touchingNormal); + //interferes with step movement + //updateTargetPositionBasedOnCollision (m_touchingNormal); } } @@ -397,7 +415,8 @@ void btKinematicCharacterController::stepForwardAndStrafe ( btCollisionWorld* co void btKinematicCharacterController::stepDown ( btCollisionWorld* collisionWorld, btScalar dt) { - btTransform start, end; + btTransform start, end, end_double; + bool runonce = false; // phase 3: down /*btScalar additionalDownStep = (m_wasOnGround && !onGround()) ? m_stepHeight : 0.0; @@ -406,44 +425,124 @@ void btKinematicCharacterController::stepDown ( btCollisionWorld* collisionWorld btVector3 gravity_drop = getUpAxisDirections()[m_upAxis] * downVelocity; m_targetPosition -= (step_drop + gravity_drop);*/ + btVector3 orig_position = m_targetPosition; + btScalar downVelocity = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt; - if(downVelocity > 0.0 && downVelocity < m_stepHeight + + if(downVelocity > 0.0 && downVelocity > m_fallSpeed && (m_wasOnGround || !m_wasJumping)) - { - downVelocity = m_stepHeight; - } + downVelocity = m_fallSpeed; btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + downVelocity); m_targetPosition -= step_drop; - start.setIdentity (); - end.setIdentity (); - - start.setOrigin (m_currentPosition); - end.setOrigin (m_targetPosition); - btKinematicClosestNotMeConvexResultCallback callback (m_ghostObject, getUpAxisDirections()[m_upAxis], m_maxSlopeCosine); - callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; - callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; + callback.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; + callback.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; + + btKinematicClosestNotMeConvexResultCallback callback2 (m_ghostObject, getUpAxisDirections()[m_upAxis], m_maxSlopeCosine); + callback2.m_collisionFilterGroup = getGhostObject()->getBroadphaseHandle()->m_collisionFilterGroup; + callback2.m_collisionFilterMask = getGhostObject()->getBroadphaseHandle()->m_collisionFilterMask; + + while (1) + { + start.setIdentity (); + end.setIdentity (); + + end_double.setIdentity (); + + start.setOrigin (m_currentPosition); + end.setOrigin (m_targetPosition); + + //set double test for 2x the step drop, to check for a large drop vs small drop + end_double.setOrigin (m_targetPosition - step_drop); + + if (m_useGhostObjectSweepTest) + { + m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + + if (!callback.hasHit()) + { + //test a double fall height, to see if the character should interpolate it's fall (full) or not (partial) + m_ghostObject->convexSweepTest (m_convexShape, start, end_double, callback2, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + } + } else + { + collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + + if (!callback.hasHit()) + { + //test a double fall height, to see if the character should interpolate it's fall (large) or not (small) + collisionWorld->convexSweepTest (m_convexShape, start, end_double, callback2, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + } + } - if (m_useGhostObjectSweepTest) - { - m_ghostObject->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); - } else - { - collisionWorld->convexSweepTest (m_convexShape, start, end, callback, collisionWorld->getDispatchInfo().m_allowedCcdPenetration); + btScalar downVelocity2 = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt; + bool has_hit = false; + if (bounce_fix == true) + has_hit = callback.hasHit() || callback2.hasHit(); + else + has_hit = callback2.hasHit(); + + if(downVelocity2 > 0.0 && downVelocity2 < m_stepHeight && has_hit == true && runonce == false + && (m_wasOnGround || !m_wasJumping)) + { + //redo the velocity calculation when falling a small amount, for fast stairs motion + //for larger falls, use the smoother/slower interpolated movement by not touching the target position + + m_targetPosition = orig_position; + downVelocity = m_stepHeight; + + btVector3 step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + downVelocity); + m_targetPosition -= step_drop; + runonce = true; + continue; //re-run previous tests + } + break; } - if (callback.hasHit()) + if (callback.hasHit() || runonce == true) { // we dropped a fraction of the height -> hit floor - m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); + + btScalar fraction = (m_currentPosition.getY() - callback.m_hitPointWorld.getY()) / 2; + + //printf("hitpoint: %g - pos %g\n", callback.m_hitPointWorld.getY(), m_currentPosition.getY()); + + if (bounce_fix == true) + { + if (full_drop == true) + m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); + else + //due to errors in the closestHitFraction variable when used with large polygons, calculate the hit fraction manually + m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, fraction); + } + else + m_currentPosition.setInterpolate3 (m_currentPosition, m_targetPosition, callback.m_closestHitFraction); + + full_drop = false; + m_verticalVelocity = 0.0; m_verticalOffset = 0.0; m_wasJumping = false; } else { // we dropped the full height + full_drop = true; + + if (bounce_fix == true) + { + downVelocity = (m_verticalVelocity<0.f?-m_verticalVelocity:0.f) * dt; + if (downVelocity > m_fallSpeed && (m_wasOnGround || !m_wasJumping)) + { + m_targetPosition += step_drop; //undo previous target change + downVelocity = m_fallSpeed; + step_drop = getUpAxisDirections()[m_upAxis] * (m_currentStepOffset + downVelocity); + m_targetPosition -= step_drop; + } + } + //printf("full drop - %g, %g\n", m_currentPosition.getY(), m_targetPosition.getY()); + m_currentPosition = m_targetPosition; } } @@ -476,13 +575,24 @@ btScalar timeInterval m_useWalkDirection = false; m_walkDirection = velocity; m_normalizedDirection = getNormalizedVector(m_walkDirection); - m_velocityTimeInterval = timeInterval; + m_velocityTimeInterval += timeInterval; } - - -void btKinematicCharacterController::reset () +void btKinematicCharacterController::reset ( btCollisionWorld* collisionWorld ) { + m_verticalVelocity = 0.0; + m_verticalOffset = 0.0; + m_wasOnGround = false; + m_wasJumping = false; + m_walkDirection.setValue(0,0,0); + m_velocityTimeInterval = 0.0; + + //clear pair cache + btHashedOverlappingPairCache *cache = m_ghostObject->getOverlappingPairCache(); + while (cache->getOverlappingPairArray().size() > 0) + { + cache->removeOverlappingPair(cache->getOverlappingPairArray()[0].m_pProxy0, cache->getOverlappingPairArray()[0].m_pProxy1, collisionWorld->getDispatcher()); + } } void btKinematicCharacterController::warp (const btVector3& origin) @@ -653,3 +763,8 @@ btVector3* btKinematicCharacterController::getUpAxisDirections() void btKinematicCharacterController::debugDraw(btIDebugDraw* debugDrawer) { } + +void btKinematicCharacterController::setUpInterpolate(bool value) +{ + m_interpolateUp = value; +} diff --git a/Engine/lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.h b/Engine/lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.h index 8ec63735c..add6f30a6 100644 --- a/Engine/lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.h +++ b/Engine/lib/bullet/src/BulletDynamics/Character/btKinematicCharacterController.h @@ -81,6 +81,9 @@ protected: int m_upAxis; static btVector3* getUpAxisDirections(); + bool m_interpolateUp; + bool full_drop; + bool bounce_fix; btVector3 computeReflectionDirection (const btVector3& direction, const btVector3& normal); btVector3 parallelComponent (const btVector3& direction, const btVector3& normal); @@ -133,7 +136,7 @@ public: virtual void setVelocityForTimeInterval(const btVector3& velocity, btScalar timeInterval); - void reset (); + void reset ( btCollisionWorld* collisionWorld ); void warp (const btVector3& origin); void preStep ( btCollisionWorld* collisionWorld); @@ -161,6 +164,7 @@ public: } bool onGround () const; + void setUpInterpolate (bool value); }; #endif // BT_KINEMATIC_CHARACTER_CONTROLLER_H diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp index 45589275e..15a4c92de 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.cpp @@ -53,6 +53,7 @@ btConeTwistConstraint::btConeTwistConstraint(btRigidBody& rbA,const btTransform& m_useSolveConstraintObsolete(CONETWIST_USE_OBSOLETE_SOLVER) { m_rbBFrame = m_rbAFrame; + m_rbBFrame.setOrigin(btVector3(0., 0., 0.)); init(); } @@ -136,6 +137,9 @@ void btConeTwistConstraint::getInfo2NonVirtual (btConstraintInfo2* info,const bt btVector3 a1neg = -a1; a1neg.getSkewSymmetricMatrix(angular0,angular1,angular2); } + info->m_J2linearAxis[0] = -1; + info->m_J2linearAxis[info->rowskip+1] = -1; + info->m_J2linearAxis[2*info->rowskip+2] = -1; btVector3 a2 = transB.getBasis() * m_rbBFrame.getOrigin(); { btVector3* angular0 = (btVector3*)(info->m_J2angularAxis); @@ -725,7 +729,8 @@ void btConeTwistConstraint::calcAngleInfo2(const btTransform& transA, const btTr { if(m_swingSpan1 < m_fixThresh) { // hinge around Y axis - if(!(btFuzzyZero(y))) +// if(!(btFuzzyZero(y))) + if((!(btFuzzyZero(x))) || (!(btFuzzyZero(z)))) { m_solveSwingLimit = true; if(m_swingSpan2 >= m_fixThresh) @@ -747,7 +752,8 @@ void btConeTwistConstraint::calcAngleInfo2(const btTransform& transA, const btTr } else { // hinge around Z axis - if(!btFuzzyZero(z)) +// if(!btFuzzyZero(z)) + if((!(btFuzzyZero(x))) || (!(btFuzzyZero(y)))) { m_solveSwingLimit = true; if(m_swingSpan1 >= m_fixThresh) diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.h index 09c048bed..1735b524d 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.h +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConeTwistConstraint.h @@ -40,6 +40,15 @@ and swing 1 and 2 are along the z and y axes respectively. #include "btJacobianEntry.h" #include "btTypedConstraint.h" +#ifdef BT_USE_DOUBLE_PRECISION +#define btConeTwistConstraintData2 btConeTwistConstraintDoubleData +#define btConeTwistConstraintDataName "btConeTwistConstraintDoubleData" +#else +#define btConeTwistConstraintData2 btConeTwistConstraintData +#define btConeTwistConstraintDataName "btConeTwistConstraintData" +#endif //BT_USE_DOUBLE_PRECISION + + class btRigidBody; enum btConeTwistFlags @@ -295,7 +304,30 @@ public: }; -///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 + + +struct btConeTwistConstraintDoubleData +{ + btTypedConstraintDoubleData m_typeConstraintData; + btTransformDoubleData m_rbAFrame; + btTransformDoubleData m_rbBFrame; + + //limits + double m_swingSpan1; + double m_swingSpan2; + double m_twistSpan; + double m_limitSoftness; + double m_biasFactor; + double m_relaxationFactor; + + double m_damping; + + + +}; + +#ifdef BT_BACKWARDS_COMPATIBLE_SERIALIZATION +///this structure is not used, except for loading pre-2.82 .bullet files struct btConeTwistConstraintData { btTypedConstraintData m_typeConstraintData; @@ -315,12 +347,12 @@ struct btConeTwistConstraintData char m_pad[4]; }; - - +#endif //BT_BACKWARDS_COMPATIBLE_SERIALIZATION +// SIMD_FORCE_INLINE int btConeTwistConstraint::calculateSerializeBufferSize() const { - return sizeof(btConeTwistConstraintData); + return sizeof(btConeTwistConstraintData2); } @@ -328,21 +360,21 @@ SIMD_FORCE_INLINE int btConeTwistConstraint::calculateSerializeBufferSize() cons ///fills the dataBuffer and returns the struct name (and 0 on failure) SIMD_FORCE_INLINE const char* btConeTwistConstraint::serialize(void* dataBuffer, btSerializer* serializer) const { - btConeTwistConstraintData* cone = (btConeTwistConstraintData*) dataBuffer; + btConeTwistConstraintData2* cone = (btConeTwistConstraintData2*) dataBuffer; btTypedConstraint::serialize(&cone->m_typeConstraintData,serializer); - m_rbAFrame.serializeFloat(cone->m_rbAFrame); - m_rbBFrame.serializeFloat(cone->m_rbBFrame); + m_rbAFrame.serialize(cone->m_rbAFrame); + m_rbBFrame.serialize(cone->m_rbBFrame); - cone->m_swingSpan1 = float(m_swingSpan1); - cone->m_swingSpan2 = float(m_swingSpan2); - cone->m_twistSpan = float(m_twistSpan); - cone->m_limitSoftness = float(m_limitSoftness); - cone->m_biasFactor = float(m_biasFactor); - cone->m_relaxationFactor = float(m_relaxationFactor); - cone->m_damping = float(m_damping); + cone->m_swingSpan1 = m_swingSpan1; + cone->m_swingSpan2 = m_swingSpan2; + cone->m_twistSpan = m_twistSpan; + cone->m_limitSoftness = m_limitSoftness; + cone->m_biasFactor = m_biasFactor; + cone->m_relaxationFactor = m_relaxationFactor; + cone->m_damping = m_damping; - return "btConeTwistConstraintData"; + return btConeTwistConstraintDataName; } diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConstraintSolver.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConstraintSolver.h index 6f673102b..1ba1cd1e8 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConstraintSolver.h +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btConstraintSolver.h @@ -28,6 +28,14 @@ class btIDebugDraw; class btStackAlloc; class btDispatcher; /// btConstraintSolver provides solver interface + + +enum btConstraintSolverType +{ + BT_SEQUENTIAL_IMPULSE_SOLVER=1, + BT_MLCP_SOLVER=2 +}; + class btConstraintSolver { @@ -38,12 +46,16 @@ public: virtual void prepareSolve (int /* numBodies */, int /* numManifolds */) {;} ///solve a group of constraints - virtual btScalar solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints, const btContactSolverInfo& info,class btIDebugDraw* debugDrawer, btStackAlloc* stackAlloc,btDispatcher* dispatcher) = 0; + virtual btScalar solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints, const btContactSolverInfo& info,class btIDebugDraw* debugDrawer,btDispatcher* dispatcher) = 0; - virtual void allSolved (const btContactSolverInfo& /* info */,class btIDebugDraw* /* debugDrawer */, btStackAlloc* /* stackAlloc */) {;} + virtual void allSolved (const btContactSolverInfo& /* info */,class btIDebugDraw* /* debugDrawer */) {;} ///clear internal cached data and reset random seed virtual void reset() = 0; + + virtual btConstraintSolverType getSolverType() const=0; + + }; diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btFixedConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btFixedConstraint.cpp new file mode 100644 index 000000000..f93a3280f --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btFixedConstraint.cpp @@ -0,0 +1,129 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + + +#include "btFixedConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" +#include "LinearMath/btTransformUtil.h" +#include + + +btFixedConstraint::btFixedConstraint(btRigidBody& rbA,btRigidBody& rbB, const btTransform& frameInA,const btTransform& frameInB) +:btTypedConstraint(FIXED_CONSTRAINT_TYPE,rbA,rbB) +{ + m_pivotInA = frameInA.getOrigin(); + m_pivotInB = frameInB.getOrigin(); + m_relTargetAB = frameInA.getRotation()*frameInB.getRotation().inverse(); + +} + +btFixedConstraint::~btFixedConstraint () +{ +} + + +void btFixedConstraint::getInfo1 (btConstraintInfo1* info) +{ + info->m_numConstraintRows = 6; + info->nub = 6; +} + +void btFixedConstraint::getInfo2 (btConstraintInfo2* info) +{ + //fix the 3 linear degrees of freedom + + + const btVector3& worldPosA = m_rbA.getCenterOfMassTransform().getOrigin(); + const btMatrix3x3& worldOrnA = m_rbA.getCenterOfMassTransform().getBasis(); + const btVector3& worldPosB= m_rbB.getCenterOfMassTransform().getOrigin(); + const btMatrix3x3& worldOrnB = m_rbB.getCenterOfMassTransform().getBasis(); + + + info->m_J1linearAxis[0] = 1; + info->m_J1linearAxis[info->rowskip+1] = 1; + info->m_J1linearAxis[2*info->rowskip+2] = 1; + + btVector3 a1 = worldOrnA*m_pivotInA; + { + btVector3* angular0 = (btVector3*)(info->m_J1angularAxis); + btVector3* angular1 = (btVector3*)(info->m_J1angularAxis+info->rowskip); + btVector3* angular2 = (btVector3*)(info->m_J1angularAxis+2*info->rowskip); + btVector3 a1neg = -a1; + a1neg.getSkewSymmetricMatrix(angular0,angular1,angular2); + } + + if (info->m_J2linearAxis) + { + info->m_J2linearAxis[0] = -1; + info->m_J2linearAxis[info->rowskip+1] = -1; + info->m_J2linearAxis[2*info->rowskip+2] = -1; + } + + btVector3 a2 = worldOrnB*m_pivotInB; + + { + // btVector3 a2n = -a2; + btVector3* angular0 = (btVector3*)(info->m_J2angularAxis); + btVector3* angular1 = (btVector3*)(info->m_J2angularAxis+info->rowskip); + btVector3* angular2 = (btVector3*)(info->m_J2angularAxis+2*info->rowskip); + a2.getSkewSymmetricMatrix(angular0,angular1,angular2); + } + + // set right hand side for the linear dofs + btScalar k = info->fps * info->erp; + + btVector3 linearError = k*(a2+worldPosB-a1-worldPosA); + int j; + for (j=0; j<3; j++) + { + + + + info->m_constraintError[j*info->rowskip] = linearError[j]; + //printf("info->m_constraintError[%d]=%f\n",j,info->m_constraintError[j]); + } + + //fix the 3 angular degrees of freedom + + int start_row = 3; + int s = info->rowskip; + int start_index = start_row * s; + + // 3 rows to make body rotations equal + info->m_J1angularAxis[start_index] = 1; + info->m_J1angularAxis[start_index + s + 1] = 1; + info->m_J1angularAxis[start_index + s*2+2] = 1; + if ( info->m_J2angularAxis) + { + info->m_J2angularAxis[start_index] = -1; + info->m_J2angularAxis[start_index + s+1] = -1; + info->m_J2angularAxis[start_index + s*2+2] = -1; + } + + // set right hand side for the angular dofs + + btVector3 diff; + btScalar angle; + btMatrix3x3 mrelCur = worldOrnA *worldOrnB.inverse(); + btQuaternion qrelCur; + mrelCur.getRotation(qrelCur); + btTransformUtil::calculateDiffAxisAngleQuaternion(m_relTargetAB,qrelCur,diff,angle); + diff*=-angle; + for (j=0; j<3; j++) + { + info->m_constraintError[(3+j)*info->rowskip] = k * diff[j]; + } + +} \ No newline at end of file diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btFixedConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btFixedConstraint.h new file mode 100644 index 000000000..697e319e2 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btFixedConstraint.h @@ -0,0 +1,49 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_FIXED_CONSTRAINT_H +#define BT_FIXED_CONSTRAINT_H + +#include "btTypedConstraint.h" + +ATTRIBUTE_ALIGNED16(class) btFixedConstraint : public btTypedConstraint +{ + btVector3 m_pivotInA; + btVector3 m_pivotInB; + btQuaternion m_relTargetAB; + +public: + btFixedConstraint(btRigidBody& rbA,btRigidBody& rbB, const btTransform& frameInA,const btTransform& frameInB); + + virtual ~btFixedConstraint(); + + + virtual void getInfo1 (btConstraintInfo1* info); + + virtual void getInfo2 (btConstraintInfo2* info); + + virtual void setParam(int num, btScalar value, int axis = -1) + { + btAssert(0); + } + virtual btScalar getParam(int num, int axis = -1) const + { + btAssert(0); + return 0.f; + } + +}; + +#endif //BT_FIXED_CONSTRAINT_H diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGearConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGearConstraint.h index 60f600948..f9afcb912 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGearConstraint.h +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGearConstraint.h @@ -19,6 +19,18 @@ subject to the following restrictions: #define BT_GEAR_CONSTRAINT_H #include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" + + +#ifdef BT_USE_DOUBLE_PRECISION +#define btGearConstraintData btGearConstraintDoubleData +#define btGearConstraintDataName "btGearConstraintDoubleData" +#else +#define btGearConstraintData btGearConstraintFloatData +#define btGearConstraintDataName "btGearConstraintFloatData" +#endif //BT_USE_DOUBLE_PRECISION + + + ///The btGeatConstraint will couple the angular velocity for two bodies around given local axis and ratio. ///See Bullet/Demos/ConstraintDemo for an example use. class btGearConstraint : public btTypedConstraint @@ -39,18 +51,102 @@ public: ///internal method used by the constraint solver, don't use them directly virtual void getInfo2 (btConstraintInfo2* info); + void setAxisA(btVector3& axisA) + { + m_axisInA = axisA; + } + void setAxisB(btVector3& axisB) + { + m_axisInB = axisB; + } + void setRatio(btScalar ratio) + { + m_ratio = ratio; + } + const btVector3& getAxisA() const + { + return m_axisInA; + } + const btVector3& getAxisB() const + { + return m_axisInB; + } + btScalar getRatio() const + { + return m_ratio; + } + + virtual void setParam(int num, btScalar value, int axis = -1) { + (void) num; + (void) value; + (void) axis; btAssert(0); - }; + } ///return the local value of parameter virtual btScalar getParam(int num, int axis = -1) const { + (void) num; + (void) axis; btAssert(0); return 0.f; } + virtual int calculateSerializeBufferSize() const; + + ///fills the dataBuffer and returns the struct name (and 0 on failure) + virtual const char* serialize(void* dataBuffer, btSerializer* serializer) const; }; + + + +///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 +struct btGearConstraintFloatData +{ + btTypedConstraintFloatData m_typeConstraintData; + + btVector3FloatData m_axisInA; + btVector3FloatData m_axisInB; + + float m_ratio; + char m_padding[4]; +}; + +struct btGearConstraintDoubleData +{ + btTypedConstraintDoubleData m_typeConstraintData; + + btVector3DoubleData m_axisInA; + btVector3DoubleData m_axisInB; + + double m_ratio; +}; + +SIMD_FORCE_INLINE int btGearConstraint::calculateSerializeBufferSize() const +{ + return sizeof(btGearConstraintData); +} + + ///fills the dataBuffer and returns the struct name (and 0 on failure) +SIMD_FORCE_INLINE const char* btGearConstraint::serialize(void* dataBuffer, btSerializer* serializer) const +{ + btGearConstraintData* gear = (btGearConstraintData*)dataBuffer; + btTypedConstraint::serialize(&gear->m_typeConstraintData,serializer); + + m_axisInA.serialize( gear->m_axisInA ); + m_axisInB.serialize( gear->m_axisInB ); + + gear->m_ratio = m_ratio; + + return btGearConstraintDataName; +} + + + + + + #endif //BT_GEAR_CONSTRAINT_H diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp index d4b4a9ad4..bc2b5a85d 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp @@ -781,17 +781,16 @@ int btGeneric6DofConstraint::get_limit_motor_info2( if (powered || limit) { // if the joint is powered, or has joint limits, add in the extra row btScalar *J1 = rotational ? info->m_J1angularAxis : info->m_J1linearAxis; - btScalar *J2 = rotational ? info->m_J2angularAxis : 0; + btScalar *J2 = rotational ? info->m_J2angularAxis : info->m_J2linearAxis; J1[srow+0] = ax1[0]; J1[srow+1] = ax1[1]; J1[srow+2] = ax1[2]; - if(rotational) - { - J2[srow+0] = -ax1[0]; - J2[srow+1] = -ax1[1]; - J2[srow+2] = -ax1[2]; - } - if((!rotational)) + + J2[srow+0] = -ax1[0]; + J2[srow+1] = -ax1[1]; + J2[srow+2] = -ax1[2]; + + if((!rotational)) { if (m_useOffsetForConstraintFrame) { diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h index 0409f9537..431a52416 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.h @@ -35,6 +35,14 @@ class btRigidBody; +#ifdef BT_USE_DOUBLE_PRECISION +#define btGeneric6DofConstraintData2 btGeneric6DofConstraintDoubleData2 +#define btGeneric6DofConstraintDataName "btGeneric6DofConstraintDoubleData2" +#else +#define btGeneric6DofConstraintData2 btGeneric6DofConstraintData +#define btGeneric6DofConstraintDataName "btGeneric6DofConstraintData" +#endif //BT_USE_DOUBLE_PRECISION + //! Rotation Limit structure for generic joints class btRotationalLimitMotor @@ -561,7 +569,7 @@ public: }; -///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 + struct btGeneric6DofConstraintData { btTypedConstraintData m_typeConstraintData; @@ -578,35 +586,51 @@ struct btGeneric6DofConstraintData int m_useOffsetForConstraintFrame; }; +struct btGeneric6DofConstraintDoubleData2 +{ + btTypedConstraintDoubleData m_typeConstraintData; + btTransformDoubleData m_rbAFrame; // constraint axii. Assumes z is hinge axis. + btTransformDoubleData m_rbBFrame; + + btVector3DoubleData m_linearUpperLimit; + btVector3DoubleData m_linearLowerLimit; + + btVector3DoubleData m_angularUpperLimit; + btVector3DoubleData m_angularLowerLimit; + + int m_useLinearReferenceFrameA; + int m_useOffsetForConstraintFrame; +}; + SIMD_FORCE_INLINE int btGeneric6DofConstraint::calculateSerializeBufferSize() const { - return sizeof(btGeneric6DofConstraintData); + return sizeof(btGeneric6DofConstraintData2); } ///fills the dataBuffer and returns the struct name (and 0 on failure) SIMD_FORCE_INLINE const char* btGeneric6DofConstraint::serialize(void* dataBuffer, btSerializer* serializer) const { - btGeneric6DofConstraintData* dof = (btGeneric6DofConstraintData*)dataBuffer; + btGeneric6DofConstraintData2* dof = (btGeneric6DofConstraintData2*)dataBuffer; btTypedConstraint::serialize(&dof->m_typeConstraintData,serializer); - m_frameInA.serializeFloat(dof->m_rbAFrame); - m_frameInB.serializeFloat(dof->m_rbBFrame); + m_frameInA.serialize(dof->m_rbAFrame); + m_frameInB.serialize(dof->m_rbBFrame); int i; for (i=0;i<3;i++) { - dof->m_angularLowerLimit.m_floats[i] = float(m_angularLimits[i].m_loLimit); - dof->m_angularUpperLimit.m_floats[i] = float(m_angularLimits[i].m_hiLimit); - dof->m_linearLowerLimit.m_floats[i] = float(m_linearLimits.m_lowerLimit[i]); - dof->m_linearUpperLimit.m_floats[i] = float(m_linearLimits.m_upperLimit[i]); + dof->m_angularLowerLimit.m_floats[i] = m_angularLimits[i].m_loLimit; + dof->m_angularUpperLimit.m_floats[i] = m_angularLimits[i].m_hiLimit; + dof->m_linearLowerLimit.m_floats[i] = m_linearLimits.m_lowerLimit[i]; + dof->m_linearUpperLimit.m_floats[i] = m_linearLimits.m_upperLimit[i]; } dof->m_useLinearReferenceFrameA = m_useLinearReferenceFrameA? 1 : 0; dof->m_useOffsetForConstraintFrame = m_useOffsetForConstraintFrame ? 1 : 0; - return "btGeneric6DofConstraintData"; + return btGeneric6DofConstraintDataName; } diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.h index 6fabb3036..1b2e0f62c 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.h +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.h @@ -21,6 +21,15 @@ subject to the following restrictions: #include "btTypedConstraint.h" #include "btGeneric6DofConstraint.h" +#ifdef BT_USE_DOUBLE_PRECISION +#define btGeneric6DofSpringConstraintData2 btGeneric6DofSpringConstraintDoubleData2 +#define btGeneric6DofSpringConstraintDataName "btGeneric6DofSpringConstraintDoubleData2" +#else +#define btGeneric6DofSpringConstraintData2 btGeneric6DofSpringConstraintData +#define btGeneric6DofSpringConstraintDataName "btGeneric6DofSpringConstraintData" +#endif //BT_USE_DOUBLE_PRECISION + + /// Generic 6 DOF constraint that allows to set spring motors to any translational and rotational DOF @@ -65,7 +74,6 @@ public: }; -///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 struct btGeneric6DofSpringConstraintData { btGeneric6DofConstraintData m_6dofData; @@ -76,26 +84,37 @@ struct btGeneric6DofSpringConstraintData float m_springDamping[6]; }; +struct btGeneric6DofSpringConstraintDoubleData2 +{ + btGeneric6DofConstraintDoubleData2 m_6dofData; + + int m_springEnabled[6]; + double m_equilibriumPoint[6]; + double m_springStiffness[6]; + double m_springDamping[6]; +}; + + SIMD_FORCE_INLINE int btGeneric6DofSpringConstraint::calculateSerializeBufferSize() const { - return sizeof(btGeneric6DofSpringConstraintData); + return sizeof(btGeneric6DofSpringConstraintData2); } ///fills the dataBuffer and returns the struct name (and 0 on failure) SIMD_FORCE_INLINE const char* btGeneric6DofSpringConstraint::serialize(void* dataBuffer, btSerializer* serializer) const { - btGeneric6DofSpringConstraintData* dof = (btGeneric6DofSpringConstraintData*)dataBuffer; + btGeneric6DofSpringConstraintData2* dof = (btGeneric6DofSpringConstraintData2*)dataBuffer; btGeneric6DofConstraint::serialize(&dof->m_6dofData,serializer); int i; for (i=0;i<6;i++) { - dof->m_equilibriumPoint[i] = (float)m_equilibriumPoint[i]; - dof->m_springDamping[i] = (float)m_springDamping[i]; + dof->m_equilibriumPoint[i] = m_equilibriumPoint[i]; + dof->m_springDamping[i] = m_springDamping[i]; dof->m_springEnabled[i] = m_springEnabled[i]? 1 : 0; - dof->m_springStiffness[i] = (float)m_springStiffness[i]; + dof->m_springStiffness[i] = m_springStiffness[i]; } - return "btGeneric6DofSpringConstraintData"; + return btGeneric6DofSpringConstraintDataName; } #endif // BT_GENERIC_6DOF_SPRING_CONSTRAINT_H diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp index 20b35fb9c..c18974130 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.cpp @@ -369,6 +369,10 @@ void btHingeConstraint::getInfo2Internal(btConstraintInfo2* info, const btTransf info->m_J1angularAxis[i*skip+1]=0; info->m_J1angularAxis[i*skip+2]=0; + info->m_J2linearAxis[i*skip]=0; + info->m_J2linearAxis[i*skip+1]=0; + info->m_J2linearAxis[i*skip+2]=0; + info->m_J2angularAxis[i*skip]=0; info->m_J2angularAxis[i*skip+1]=0; info->m_J2angularAxis[i*skip+2]=0; @@ -384,6 +388,10 @@ void btHingeConstraint::getInfo2Internal(btConstraintInfo2* info, const btTransf info->m_J1linearAxis[0] = 1; info->m_J1linearAxis[skip + 1] = 1; info->m_J1linearAxis[2 * skip + 2] = 1; + + info->m_J2linearAxis[0] = -1; + info->m_J2linearAxis[skip + 1] = -1; + info->m_J2linearAxis[2 * skip + 2] = -1; } @@ -797,7 +805,11 @@ void btHingeConstraint::getInfo2InternalUsingFrameOffset(btConstraintInfo2* info for (i=0; i<3; i++) info->m_J1linearAxis[s0+i] = p[i]; for (i=0; i<3; i++) info->m_J1linearAxis[s1+i] = q[i]; for (i=0; i<3; i++) info->m_J1linearAxis[s2+i] = ax1[i]; - + + for (i=0; i<3; i++) info->m_J2linearAxis[s0+i] = -p[i]; + for (i=0; i<3; i++) info->m_J2linearAxis[s1+i] = -q[i]; + for (i=0; i<3; i++) info->m_J2linearAxis[s2+i] = -ax1[i]; + // compute three elements of right hand side btScalar rhs = k * p.dot(ofs); diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.h index a7f2cca55..7c33ac24e 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.h +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btHingeConstraint.h @@ -28,8 +28,8 @@ subject to the following restrictions: class btRigidBody; #ifdef BT_USE_DOUBLE_PRECISION -#define btHingeConstraintData btHingeConstraintDoubleData -#define btHingeConstraintDataName "btHingeConstraintDoubleData" +#define btHingeConstraintData btHingeConstraintDoubleData2 //rename to 2 for backwards compatibility, so we can still load the 'btHingeConstraintDoubleData' version +#define btHingeConstraintDataName "btHingeConstraintDoubleData2" #else #define btHingeConstraintData btHingeConstraintFloatData #define btHingeConstraintDataName "btHingeConstraintFloatData" @@ -302,7 +302,10 @@ public: }; -///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 + +//only for backward compatibility +#ifdef BT_BACKWARDS_COMPATIBLE_SERIALIZATION +///this structure is not used, except for loading pre-2.82 .bullet files struct btHingeConstraintDoubleData { btTypedConstraintData m_typeConstraintData; @@ -321,7 +324,9 @@ struct btHingeConstraintDoubleData float m_relaxationFactor; }; -///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 +#endif //BT_BACKWARDS_COMPATIBLE_SERIALIZATION + + struct btHingeConstraintFloatData { btTypedConstraintData m_typeConstraintData; @@ -344,6 +349,30 @@ struct btHingeConstraintFloatData +///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 +struct btHingeConstraintDoubleData2 +{ + btTypedConstraintDoubleData m_typeConstraintData; + btTransformDoubleData m_rbAFrame; // constraint axii. Assumes z is hinge axis. + btTransformDoubleData m_rbBFrame; + int m_useReferenceFrameA; + int m_angularOnly; + int m_enableAngularMotor; + double m_motorTargetVelocity; + double m_maxMotorImpulse; + + double m_lowerLimit; + double m_upperLimit; + double m_limitSoftness; + double m_biasFactor; + double m_relaxationFactor; + char m_padding1[4]; + +}; + + + + SIMD_FORCE_INLINE int btHingeConstraint::calculateSerializeBufferSize() const { return sizeof(btHingeConstraintData); diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp index 11b2ec801..3c0430b90 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.cpp @@ -116,10 +116,9 @@ void btPoint2PointConstraint::getInfo2NonVirtual (btConstraintInfo2* info, const a1neg.getSkewSymmetricMatrix(angular0,angular1,angular2); } - /*info->m_J2linearAxis[0] = -1; - info->m_J2linearAxis[s+1] = -1; - info->m_J2linearAxis[2*s+2] = -1; - */ + info->m_J2linearAxis[0] = -1; + info->m_J2linearAxis[info->rowskip+1] = -1; + info->m_J2linearAxis[2*info->rowskip+2] = -1; btVector3 a2 = body1_trans.getBasis()*getPivotInB(); diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h index 1e13416df..912189494 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btPoint2PointConstraint.h @@ -24,10 +24,10 @@ class btRigidBody; #ifdef BT_USE_DOUBLE_PRECISION -#define btPoint2PointConstraintData btPoint2PointConstraintDoubleData -#define btPoint2PointConstraintDataName "btPoint2PointConstraintDoubleData" +#define btPoint2PointConstraintData2 btPoint2PointConstraintDoubleData2 +#define btPoint2PointConstraintDataName "btPoint2PointConstraintDoubleData2" #else -#define btPoint2PointConstraintData btPoint2PointConstraintFloatData +#define btPoint2PointConstraintData2 btPoint2PointConstraintFloatData #define btPoint2PointConstraintDataName "btPoint2PointConstraintFloatData" #endif //BT_USE_DOUBLE_PRECISION @@ -133,6 +133,17 @@ struct btPoint2PointConstraintFloatData btVector3FloatData m_pivotInB; }; +///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 +struct btPoint2PointConstraintDoubleData2 +{ + btTypedConstraintDoubleData m_typeConstraintData; + btVector3DoubleData m_pivotInA; + btVector3DoubleData m_pivotInB; +}; + +#ifdef BT_BACKWARDS_COMPATIBLE_SERIALIZATION +///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 +///this structure is not used, except for loading pre-2.82 .bullet files ///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 struct btPoint2PointConstraintDoubleData { @@ -140,18 +151,19 @@ struct btPoint2PointConstraintDoubleData btVector3DoubleData m_pivotInA; btVector3DoubleData m_pivotInB; }; +#endif //BT_BACKWARDS_COMPATIBLE_SERIALIZATION SIMD_FORCE_INLINE int btPoint2PointConstraint::calculateSerializeBufferSize() const { - return sizeof(btPoint2PointConstraintData); + return sizeof(btPoint2PointConstraintData2); } ///fills the dataBuffer and returns the struct name (and 0 on failure) SIMD_FORCE_INLINE const char* btPoint2PointConstraint::serialize(void* dataBuffer, btSerializer* serializer) const { - btPoint2PointConstraintData* p2pData = (btPoint2PointConstraintData*)dataBuffer; + btPoint2PointConstraintData2* p2pData = (btPoint2PointConstraintData2*)dataBuffer; btTypedConstraint::serialize(&p2pData->m_typeConstraintData,serializer); m_pivotInA.serialize(p2pData->m_pivotInA); diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp index 89cf46cfe..be93e3543 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.cpp @@ -14,6 +14,8 @@ subject to the following restrictions: */ //#define COMPUTE_IMPULSE_DENOM 1 +//#define BT_ADDITIONAL_DEBUG + //It is not necessary (redundant) to refresh contact manifolds, this refresh has been moved to the collision algorithms. #include "btSequentialImpulseConstraintSolver.h" @@ -63,8 +65,8 @@ void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowGenericSIMD( __m128 lowerLimit1 = _mm_set1_ps(c.m_lowerLimit); __m128 upperLimit1 = _mm_set1_ps(c.m_upperLimit); __m128 deltaImpulse = _mm_sub_ps(_mm_set1_ps(c.m_rhs), _mm_mul_ps(_mm_set1_ps(c.m_appliedImpulse),_mm_set1_ps(c.m_cfm))); - __m128 deltaVel1Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal.mVec128,body1.internalGetDeltaLinearVelocity().mVec128), btSimdDot3(c.m_relpos1CrossNormal.mVec128,body1.internalGetDeltaAngularVelocity().mVec128)); - __m128 deltaVel2Dotn = _mm_sub_ps(btSimdDot3(c.m_relpos2CrossNormal.mVec128,body2.internalGetDeltaAngularVelocity().mVec128),btSimdDot3((c.m_contactNormal).mVec128,body2.internalGetDeltaLinearVelocity().mVec128)); + __m128 deltaVel1Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal1.mVec128,body1.internalGetDeltaLinearVelocity().mVec128), btSimdDot3(c.m_relpos1CrossNormal.mVec128,body1.internalGetDeltaAngularVelocity().mVec128)); + __m128 deltaVel2Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal2.mVec128,body2.internalGetDeltaLinearVelocity().mVec128), btSimdDot3(c.m_relpos2CrossNormal.mVec128,body2.internalGetDeltaAngularVelocity().mVec128)); deltaImpulse = _mm_sub_ps(deltaImpulse,_mm_mul_ps(deltaVel1Dotn,_mm_set1_ps(c.m_jacDiagABInv))); deltaImpulse = _mm_sub_ps(deltaImpulse,_mm_mul_ps(deltaVel2Dotn,_mm_set1_ps(c.m_jacDiagABInv))); btSimdScalar sum = _mm_add_ps(cpAppliedImp,deltaImpulse); @@ -77,12 +79,12 @@ void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowGenericSIMD( __m128 upperMinApplied = _mm_sub_ps(upperLimit1,cpAppliedImp); deltaImpulse = _mm_or_ps( _mm_and_ps(resultUpperLess, deltaImpulse), _mm_andnot_ps(resultUpperLess, upperMinApplied) ); c.m_appliedImpulse = _mm_or_ps( _mm_and_ps(resultUpperLess, c.m_appliedImpulse), _mm_andnot_ps(resultUpperLess, upperLimit1) ); - __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal.mVec128,body1.internalGetInvMass().mVec128); - __m128 linearComponentB = _mm_mul_ps((c.m_contactNormal).mVec128,body2.internalGetInvMass().mVec128); + __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal1.mVec128,body1.internalGetInvMass().mVec128); + __m128 linearComponentB = _mm_mul_ps((c.m_contactNormal2).mVec128,body2.internalGetInvMass().mVec128); __m128 impulseMagnitude = deltaImpulse; body1.internalGetDeltaLinearVelocity().mVec128 = _mm_add_ps(body1.internalGetDeltaLinearVelocity().mVec128,_mm_mul_ps(linearComponentA,impulseMagnitude)); body1.internalGetDeltaAngularVelocity().mVec128 = _mm_add_ps(body1.internalGetDeltaAngularVelocity().mVec128 ,_mm_mul_ps(c.m_angularComponentA.mVec128,impulseMagnitude)); - body2.internalGetDeltaLinearVelocity().mVec128 = _mm_sub_ps(body2.internalGetDeltaLinearVelocity().mVec128,_mm_mul_ps(linearComponentB,impulseMagnitude)); + body2.internalGetDeltaLinearVelocity().mVec128 = _mm_add_ps(body2.internalGetDeltaLinearVelocity().mVec128,_mm_mul_ps(linearComponentB,impulseMagnitude)); body2.internalGetDeltaAngularVelocity().mVec128 = _mm_add_ps(body2.internalGetDeltaAngularVelocity().mVec128 ,_mm_mul_ps(c.m_angularComponentB.mVec128,impulseMagnitude)); #else resolveSingleConstraintRowGeneric(body1,body2,c); @@ -93,8 +95,8 @@ void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowGenericSIMD( void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowGeneric(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& c) { btScalar deltaImpulse = c.m_rhs-btScalar(c.m_appliedImpulse)*c.m_cfm; - const btScalar deltaVel1Dotn = c.m_contactNormal.dot(body1.internalGetDeltaLinearVelocity()) + c.m_relpos1CrossNormal.dot(body1.internalGetDeltaAngularVelocity()); - const btScalar deltaVel2Dotn = -c.m_contactNormal.dot(body2.internalGetDeltaLinearVelocity()) + c.m_relpos2CrossNormal.dot(body2.internalGetDeltaAngularVelocity()); + const btScalar deltaVel1Dotn = c.m_contactNormal1.dot(body1.internalGetDeltaLinearVelocity()) + c.m_relpos1CrossNormal.dot(body1.internalGetDeltaAngularVelocity()); + const btScalar deltaVel2Dotn = c.m_contactNormal2.dot(body2.internalGetDeltaLinearVelocity()) + c.m_relpos2CrossNormal.dot(body2.internalGetDeltaAngularVelocity()); // const btScalar delta_rel_vel = deltaVel1Dotn-deltaVel2Dotn; deltaImpulse -= deltaVel1Dotn*c.m_jacDiagABInv; @@ -116,8 +118,8 @@ void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowGenericSIMD( c.m_appliedImpulse = sum; } - body1.internalApplyImpulse(c.m_contactNormal*body1.internalGetInvMass(),c.m_angularComponentA,deltaImpulse); - body2.internalApplyImpulse(-c.m_contactNormal*body2.internalGetInvMass(),c.m_angularComponentB,deltaImpulse); + body1.internalApplyImpulse(c.m_contactNormal1*body1.internalGetInvMass(),c.m_angularComponentA,deltaImpulse); + body2.internalApplyImpulse(c.m_contactNormal2*body2.internalGetInvMass(),c.m_angularComponentB,deltaImpulse); } void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowLowerLimitSIMD(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& c) @@ -127,8 +129,8 @@ void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowGenericSIMD( __m128 lowerLimit1 = _mm_set1_ps(c.m_lowerLimit); __m128 upperLimit1 = _mm_set1_ps(c.m_upperLimit); __m128 deltaImpulse = _mm_sub_ps(_mm_set1_ps(c.m_rhs), _mm_mul_ps(_mm_set1_ps(c.m_appliedImpulse),_mm_set1_ps(c.m_cfm))); - __m128 deltaVel1Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal.mVec128,body1.internalGetDeltaLinearVelocity().mVec128), btSimdDot3(c.m_relpos1CrossNormal.mVec128,body1.internalGetDeltaAngularVelocity().mVec128)); - __m128 deltaVel2Dotn = _mm_sub_ps(btSimdDot3(c.m_relpos2CrossNormal.mVec128,body2.internalGetDeltaAngularVelocity().mVec128),btSimdDot3((c.m_contactNormal).mVec128,body2.internalGetDeltaLinearVelocity().mVec128)); + __m128 deltaVel1Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal1.mVec128,body1.internalGetDeltaLinearVelocity().mVec128), btSimdDot3(c.m_relpos1CrossNormal.mVec128,body1.internalGetDeltaAngularVelocity().mVec128)); + __m128 deltaVel2Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal2.mVec128,body2.internalGetDeltaLinearVelocity().mVec128), btSimdDot3(c.m_relpos2CrossNormal.mVec128,body2.internalGetDeltaAngularVelocity().mVec128)); deltaImpulse = _mm_sub_ps(deltaImpulse,_mm_mul_ps(deltaVel1Dotn,_mm_set1_ps(c.m_jacDiagABInv))); deltaImpulse = _mm_sub_ps(deltaImpulse,_mm_mul_ps(deltaVel2Dotn,_mm_set1_ps(c.m_jacDiagABInv))); btSimdScalar sum = _mm_add_ps(cpAppliedImp,deltaImpulse); @@ -138,24 +140,24 @@ void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowGenericSIMD( __m128 lowMinApplied = _mm_sub_ps(lowerLimit1,cpAppliedImp); deltaImpulse = _mm_or_ps( _mm_and_ps(resultLowerLess, lowMinApplied), _mm_andnot_ps(resultLowerLess, deltaImpulse) ); c.m_appliedImpulse = _mm_or_ps( _mm_and_ps(resultLowerLess, lowerLimit1), _mm_andnot_ps(resultLowerLess, sum) ); - __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal.mVec128,body1.internalGetInvMass().mVec128); - __m128 linearComponentB = _mm_mul_ps((c.m_contactNormal).mVec128,body2.internalGetInvMass().mVec128); + __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal1.mVec128,body1.internalGetInvMass().mVec128); + __m128 linearComponentB = _mm_mul_ps(c.m_contactNormal2.mVec128,body2.internalGetInvMass().mVec128); __m128 impulseMagnitude = deltaImpulse; body1.internalGetDeltaLinearVelocity().mVec128 = _mm_add_ps(body1.internalGetDeltaLinearVelocity().mVec128,_mm_mul_ps(linearComponentA,impulseMagnitude)); body1.internalGetDeltaAngularVelocity().mVec128 = _mm_add_ps(body1.internalGetDeltaAngularVelocity().mVec128 ,_mm_mul_ps(c.m_angularComponentA.mVec128,impulseMagnitude)); - body2.internalGetDeltaLinearVelocity().mVec128 = _mm_sub_ps(body2.internalGetDeltaLinearVelocity().mVec128,_mm_mul_ps(linearComponentB,impulseMagnitude)); + body2.internalGetDeltaLinearVelocity().mVec128 = _mm_add_ps(body2.internalGetDeltaLinearVelocity().mVec128,_mm_mul_ps(linearComponentB,impulseMagnitude)); body2.internalGetDeltaAngularVelocity().mVec128 = _mm_add_ps(body2.internalGetDeltaAngularVelocity().mVec128 ,_mm_mul_ps(c.m_angularComponentB.mVec128,impulseMagnitude)); #else resolveSingleConstraintRowLowerLimit(body1,body2,c); #endif } -// Project Gauss Seidel or the equivalent Sequential Impulse +// Projected Gauss Seidel or the equivalent Sequential Impulse void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowLowerLimit(btSolverBody& body1,btSolverBody& body2,const btSolverConstraint& c) { btScalar deltaImpulse = c.m_rhs-btScalar(c.m_appliedImpulse)*c.m_cfm; - const btScalar deltaVel1Dotn = c.m_contactNormal.dot(body1.internalGetDeltaLinearVelocity()) + c.m_relpos1CrossNormal.dot(body1.internalGetDeltaAngularVelocity()); - const btScalar deltaVel2Dotn = -c.m_contactNormal.dot(body2.internalGetDeltaLinearVelocity()) + c.m_relpos2CrossNormal.dot(body2.internalGetDeltaAngularVelocity()); + const btScalar deltaVel1Dotn = c.m_contactNormal1.dot(body1.internalGetDeltaLinearVelocity()) + c.m_relpos1CrossNormal.dot(body1.internalGetDeltaAngularVelocity()); + const btScalar deltaVel2Dotn = c.m_contactNormal2.dot(body2.internalGetDeltaLinearVelocity()) + c.m_relpos2CrossNormal.dot(body2.internalGetDeltaAngularVelocity()); deltaImpulse -= deltaVel1Dotn*c.m_jacDiagABInv; deltaImpulse -= deltaVel2Dotn*c.m_jacDiagABInv; @@ -169,8 +171,8 @@ void btSequentialImpulseConstraintSolver::resolveSingleConstraintRowGenericSIMD( { c.m_appliedImpulse = sum; } - body1.internalApplyImpulse(c.m_contactNormal*body1.internalGetInvMass(),c.m_angularComponentA,deltaImpulse); - body2.internalApplyImpulse(-c.m_contactNormal*body2.internalGetInvMass(),c.m_angularComponentB,deltaImpulse); + body1.internalApplyImpulse(c.m_contactNormal1*body1.internalGetInvMass(),c.m_angularComponentA,deltaImpulse); + body2.internalApplyImpulse(c.m_contactNormal2*body2.internalGetInvMass(),c.m_angularComponentB,deltaImpulse); } @@ -183,8 +185,8 @@ void btSequentialImpulseConstraintSolver::resolveSplitPenetrationImpulseCacheFri { gNumSplitImpulseRecoveries++; btScalar deltaImpulse = c.m_rhsPenetration-btScalar(c.m_appliedPushImpulse)*c.m_cfm; - const btScalar deltaVel1Dotn = c.m_contactNormal.dot(body1.internalGetPushVelocity()) + c.m_relpos1CrossNormal.dot(body1.internalGetTurnVelocity()); - const btScalar deltaVel2Dotn = -c.m_contactNormal.dot(body2.internalGetPushVelocity()) + c.m_relpos2CrossNormal.dot(body2.internalGetTurnVelocity()); + const btScalar deltaVel1Dotn = c.m_contactNormal1.dot(body1.internalGetPushVelocity()) + c.m_relpos1CrossNormal.dot(body1.internalGetTurnVelocity()); + const btScalar deltaVel2Dotn = c.m_contactNormal2.dot(body2.internalGetPushVelocity()) + c.m_relpos2CrossNormal.dot(body2.internalGetTurnVelocity()); deltaImpulse -= deltaVel1Dotn*c.m_jacDiagABInv; deltaImpulse -= deltaVel2Dotn*c.m_jacDiagABInv; @@ -198,8 +200,8 @@ void btSequentialImpulseConstraintSolver::resolveSplitPenetrationImpulseCacheFri { c.m_appliedPushImpulse = sum; } - body1.internalApplyPushImpulse(c.m_contactNormal*body1.internalGetInvMass(),c.m_angularComponentA,deltaImpulse); - body2.internalApplyPushImpulse(-c.m_contactNormal*body2.internalGetInvMass(),c.m_angularComponentB,deltaImpulse); + body1.internalApplyPushImpulse(c.m_contactNormal1*body1.internalGetInvMass(),c.m_angularComponentA,deltaImpulse); + body2.internalApplyPushImpulse(c.m_contactNormal2*body2.internalGetInvMass(),c.m_angularComponentB,deltaImpulse); } } @@ -215,8 +217,8 @@ void btSequentialImpulseConstraintSolver::resolveSplitPenetrationImpulseCacheFri __m128 lowerLimit1 = _mm_set1_ps(c.m_lowerLimit); __m128 upperLimit1 = _mm_set1_ps(c.m_upperLimit); __m128 deltaImpulse = _mm_sub_ps(_mm_set1_ps(c.m_rhsPenetration), _mm_mul_ps(_mm_set1_ps(c.m_appliedPushImpulse),_mm_set1_ps(c.m_cfm))); - __m128 deltaVel1Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal.mVec128,body1.internalGetPushVelocity().mVec128), btSimdDot3(c.m_relpos1CrossNormal.mVec128,body1.internalGetTurnVelocity().mVec128)); - __m128 deltaVel2Dotn = _mm_sub_ps(btSimdDot3(c.m_relpos2CrossNormal.mVec128,body2.internalGetTurnVelocity().mVec128),btSimdDot3((c.m_contactNormal).mVec128,body2.internalGetPushVelocity().mVec128)); + __m128 deltaVel1Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal1.mVec128,body1.internalGetPushVelocity().mVec128), btSimdDot3(c.m_relpos1CrossNormal.mVec128,body1.internalGetTurnVelocity().mVec128)); + __m128 deltaVel2Dotn = _mm_add_ps(btSimdDot3(c.m_contactNormal2.mVec128,body2.internalGetPushVelocity().mVec128), btSimdDot3(c.m_relpos2CrossNormal.mVec128,body2.internalGetTurnVelocity().mVec128)); deltaImpulse = _mm_sub_ps(deltaImpulse,_mm_mul_ps(deltaVel1Dotn,_mm_set1_ps(c.m_jacDiagABInv))); deltaImpulse = _mm_sub_ps(deltaImpulse,_mm_mul_ps(deltaVel2Dotn,_mm_set1_ps(c.m_jacDiagABInv))); btSimdScalar sum = _mm_add_ps(cpAppliedImp,deltaImpulse); @@ -226,12 +228,12 @@ void btSequentialImpulseConstraintSolver::resolveSplitPenetrationImpulseCacheFri __m128 lowMinApplied = _mm_sub_ps(lowerLimit1,cpAppliedImp); deltaImpulse = _mm_or_ps( _mm_and_ps(resultLowerLess, lowMinApplied), _mm_andnot_ps(resultLowerLess, deltaImpulse) ); c.m_appliedPushImpulse = _mm_or_ps( _mm_and_ps(resultLowerLess, lowerLimit1), _mm_andnot_ps(resultLowerLess, sum) ); - __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal.mVec128,body1.internalGetInvMass().mVec128); - __m128 linearComponentB = _mm_mul_ps((c.m_contactNormal).mVec128,body2.internalGetInvMass().mVec128); + __m128 linearComponentA = _mm_mul_ps(c.m_contactNormal1.mVec128,body1.internalGetInvMass().mVec128); + __m128 linearComponentB = _mm_mul_ps(c.m_contactNormal2.mVec128,body2.internalGetInvMass().mVec128); __m128 impulseMagnitude = deltaImpulse; body1.internalGetPushVelocity().mVec128 = _mm_add_ps(body1.internalGetPushVelocity().mVec128,_mm_mul_ps(linearComponentA,impulseMagnitude)); body1.internalGetTurnVelocity().mVec128 = _mm_add_ps(body1.internalGetTurnVelocity().mVec128 ,_mm_mul_ps(c.m_angularComponentA.mVec128,impulseMagnitude)); - body2.internalGetPushVelocity().mVec128 = _mm_sub_ps(body2.internalGetPushVelocity().mVec128,_mm_mul_ps(linearComponentB,impulseMagnitude)); + body2.internalGetPushVelocity().mVec128 = _mm_add_ps(body2.internalGetPushVelocity().mVec128,_mm_mul_ps(linearComponentB,impulseMagnitude)); body2.internalGetTurnVelocity().mVec128 = _mm_add_ps(body2.internalGetTurnVelocity().mVec128 ,_mm_mul_ps(c.m_angularComponentB.mVec128,impulseMagnitude)); #else resolveSplitPenetrationImpulseCacheFriendly(body1,body2,c); @@ -278,7 +280,7 @@ int btSequentialImpulseConstraintSolver::btRandInt2 (int n) -void btSequentialImpulseConstraintSolver::initSolverBody(btSolverBody* solverBody, btCollisionObject* collisionObject) +void btSequentialImpulseConstraintSolver::initSolverBody(btSolverBody* solverBody, btCollisionObject* collisionObject, btScalar timeStep) { btRigidBody* rb = collisionObject? btRigidBody::upcast(collisionObject) : 0; @@ -297,6 +299,9 @@ void btSequentialImpulseConstraintSolver::initSolverBody(btSolverBody* solverBod solverBody->m_linearFactor = rb->getLinearFactor(); solverBody->m_linearVelocity = rb->getLinearVelocity(); solverBody->m_angularVelocity = rb->getAngularVelocity(); + solverBody->m_externalForceImpulse = rb->getTotalForce()*rb->getInvMass()*timeStep; + solverBody->m_externalTorqueImpulse = rb->getTotalTorque()*rb->getInvInertiaTensorWorld()*timeStep ; + } else { solverBody->m_worldTransform.setIdentity(); @@ -306,6 +311,8 @@ void btSequentialImpulseConstraintSolver::initSolverBody(btSolverBody* solverBod solverBody->m_linearFactor.setValue(1,1,1); solverBody->m_linearVelocity.setValue(0,0,0); solverBody->m_angularVelocity.setValue(0,0,0); + solverBody->m_externalForceImpulse.setValue(0,0,0); + solverBody->m_externalTorqueImpulse.setValue(0,0,0); } @@ -324,8 +331,7 @@ btScalar btSequentialImpulseConstraintSolver::restitutionCurve(btScalar rel_vel, -static void applyAnisotropicFriction(btCollisionObject* colObj,btVector3& frictionDirection, int frictionMode); -static void applyAnisotropicFriction(btCollisionObject* colObj,btVector3& frictionDirection, int frictionMode) +void btSequentialImpulseConstraintSolver::applyAnisotropicFriction(btCollisionObject* colObj,btVector3& frictionDirection, int frictionMode) { @@ -349,7 +355,6 @@ void btSequentialImpulseConstraintSolver::setupFrictionConstraint(btSolverConstr { - solverConstraint.m_contactNormal = normalAxis; btSolverBody& solverBodyA = m_tmpSolverBodyPool[solverBodyIdA]; btSolverBody& solverBodyB = m_tmpSolverBodyPool[solverBodyIdB]; @@ -365,15 +370,30 @@ void btSequentialImpulseConstraintSolver::setupFrictionConstraint(btSolverConstr solverConstraint.m_appliedImpulse = 0.f; solverConstraint.m_appliedPushImpulse = 0.f; + if (body0) { - btVector3 ftorqueAxis1 = rel_pos1.cross(solverConstraint.m_contactNormal); + solverConstraint.m_contactNormal1 = normalAxis; + btVector3 ftorqueAxis1 = rel_pos1.cross(solverConstraint.m_contactNormal1); solverConstraint.m_relpos1CrossNormal = ftorqueAxis1; - solverConstraint.m_angularComponentA = body0 ? body0->getInvInertiaTensorWorld()*ftorqueAxis1*body0->getAngularFactor() : btVector3(0,0,0); - } + solverConstraint.m_angularComponentA = body0->getInvInertiaTensorWorld()*ftorqueAxis1*body0->getAngularFactor(); + }else { - btVector3 ftorqueAxis1 = rel_pos2.cross(-solverConstraint.m_contactNormal); + solverConstraint.m_contactNormal1.setZero(); + solverConstraint.m_relpos1CrossNormal.setZero(); + solverConstraint.m_angularComponentA .setZero(); + } + + if (body1) + { + solverConstraint.m_contactNormal2 = -normalAxis; + btVector3 ftorqueAxis1 = rel_pos2.cross(solverConstraint.m_contactNormal2); solverConstraint.m_relpos2CrossNormal = ftorqueAxis1; - solverConstraint.m_angularComponentB = body1 ? body1->getInvInertiaTensorWorld()*ftorqueAxis1*body1->getAngularFactor() : btVector3(0,0,0); + solverConstraint.m_angularComponentB = body1->getInvInertiaTensorWorld()*ftorqueAxis1*body1->getAngularFactor(); + } else + { + solverConstraint.m_contactNormal2.setZero(); + solverConstraint.m_relpos2CrossNormal.setZero(); + solverConstraint.m_angularComponentB.setZero(); } { @@ -398,9 +418,9 @@ void btSequentialImpulseConstraintSolver::setupFrictionConstraint(btSolverConstr btScalar rel_vel; - btScalar vel1Dotn = solverConstraint.m_contactNormal.dot(body0?solverBodyA.m_linearVelocity:btVector3(0,0,0)) + btScalar vel1Dotn = solverConstraint.m_contactNormal1.dot(body0?solverBodyA.m_linearVelocity+solverBodyA.m_externalForceImpulse:btVector3(0,0,0)) + solverConstraint.m_relpos1CrossNormal.dot(body0?solverBodyA.m_angularVelocity:btVector3(0,0,0)); - btScalar vel2Dotn = -solverConstraint.m_contactNormal.dot(body1?solverBodyB.m_linearVelocity:btVector3(0,0,0)) + btScalar vel2Dotn = solverConstraint.m_contactNormal2.dot(body1?solverBodyB.m_linearVelocity+solverBodyB.m_externalForceImpulse:btVector3(0,0,0)) + solverConstraint.m_relpos2CrossNormal.dot(body1?solverBodyB.m_angularVelocity:btVector3(0,0,0)); rel_vel = vel1Dotn+vel2Dotn; @@ -411,8 +431,8 @@ void btSequentialImpulseConstraintSolver::setupFrictionConstraint(btSolverConstr btSimdScalar velocityImpulse = velocityError * btSimdScalar(solverConstraint.m_jacDiagABInv); solverConstraint.m_rhs = velocityImpulse; solverConstraint.m_cfm = cfmSlip; - solverConstraint.m_lowerLimit = 0; - solverConstraint.m_upperLimit = 1e10f; + solverConstraint.m_lowerLimit = -solverConstraint.m_friction; + solverConstraint.m_upperLimit = solverConstraint.m_friction; } } @@ -436,7 +456,8 @@ void btSequentialImpulseConstraintSolver::setupRollingFrictionConstraint( btSolv btVector3 normalAxis(0,0,0); - solverConstraint.m_contactNormal = normalAxis; + solverConstraint.m_contactNormal1 = normalAxis; + solverConstraint.m_contactNormal2 = -normalAxis; btSolverBody& solverBodyA = m_tmpSolverBodyPool[solverBodyIdA]; btSolverBody& solverBodyB = m_tmpSolverBodyPool[solverBodyIdB]; @@ -477,9 +498,9 @@ void btSequentialImpulseConstraintSolver::setupRollingFrictionConstraint( btSolv btScalar rel_vel; - btScalar vel1Dotn = solverConstraint.m_contactNormal.dot(body0?solverBodyA.m_linearVelocity:btVector3(0,0,0)) + btScalar vel1Dotn = solverConstraint.m_contactNormal1.dot(body0?solverBodyA.m_linearVelocity+solverBodyA.m_externalForceImpulse:btVector3(0,0,0)) + solverConstraint.m_relpos1CrossNormal.dot(body0?solverBodyA.m_angularVelocity:btVector3(0,0,0)); - btScalar vel2Dotn = -solverConstraint.m_contactNormal.dot(body1?solverBodyB.m_linearVelocity:btVector3(0,0,0)) + btScalar vel2Dotn = solverConstraint.m_contactNormal2.dot(body1?solverBodyB.m_linearVelocity+solverBodyB.m_externalForceImpulse:btVector3(0,0,0)) + solverConstraint.m_relpos2CrossNormal.dot(body1?solverBodyB.m_angularVelocity:btVector3(0,0,0)); rel_vel = vel1Dotn+vel2Dotn; @@ -490,8 +511,8 @@ void btSequentialImpulseConstraintSolver::setupRollingFrictionConstraint( btSolv btSimdScalar velocityImpulse = velocityError * btSimdScalar(solverConstraint.m_jacDiagABInv); solverConstraint.m_rhs = velocityImpulse; solverConstraint.m_cfm = cfmSlip; - solverConstraint.m_lowerLimit = 0; - solverConstraint.m_upperLimit = 1e10f; + solverConstraint.m_lowerLimit = -solverConstraint.m_friction; + solverConstraint.m_upperLimit = solverConstraint.m_friction; } } @@ -513,7 +534,7 @@ btSolverConstraint& btSequentialImpulseConstraintSolver::addRollingFrictionConst } -int btSequentialImpulseConstraintSolver::getOrInitSolverBody(btCollisionObject& body) +int btSequentialImpulseConstraintSolver::getOrInitSolverBody(btCollisionObject& body,btScalar timeStep) { int solverBodyIdA = -1; @@ -531,11 +552,19 @@ int btSequentialImpulseConstraintSolver::getOrInitSolverBody(btCollisionObject& { solverBodyIdA = m_tmpSolverBodyPool.size(); btSolverBody& solverBody = m_tmpSolverBodyPool.expand(); - initSolverBody(&solverBody,&body); + initSolverBody(&solverBody,&body,timeStep); body.setCompanionId(solverBodyIdA); } else { - return 0;//assume first one is a fixed solver body + + if (m_fixedBodyId<0) + { + m_fixedBodyId = m_tmpSolverBodyPool.size(); + btSolverBody& fixedBody = m_tmpSolverBodyPool.expand(); + initSolverBody(&fixedBody,0,timeStep); + } + return m_fixedBodyId; +// return 0;//assume first one is a fixed solver body } } @@ -548,8 +577,8 @@ int btSequentialImpulseConstraintSolver::getOrInitSolverBody(btCollisionObject& void btSequentialImpulseConstraintSolver::setupContactConstraint(btSolverConstraint& solverConstraint, int solverBodyIdA, int solverBodyIdB, btManifoldPoint& cp, const btContactSolverInfo& infoGlobal, - btVector3& vel, btScalar& rel_vel, btScalar& relaxation, - btVector3& rel_pos1, btVector3& rel_pos2) + btScalar& relaxation, + const btVector3& rel_pos1, const btVector3& rel_pos2) { const btVector3& pos1 = cp.getPositionWorldOnA(); @@ -563,8 +592,8 @@ void btSequentialImpulseConstraintSolver::setupContactConstraint(btSolverConstra // btVector3 rel_pos1 = pos1 - colObj0->getWorldTransform().getOrigin(); // btVector3 rel_pos2 = pos2 - colObj1->getWorldTransform().getOrigin(); - rel_pos1 = pos1 - bodyA->getWorldTransform().getOrigin(); - rel_pos2 = pos2 - bodyB->getWorldTransform().getOrigin(); + //rel_pos1 = pos1 - bodyA->getWorldTransform().getOrigin(); + //rel_pos2 = pos2 - bodyB->getWorldTransform().getOrigin(); relaxation = 1.f; @@ -597,9 +626,24 @@ void btSequentialImpulseConstraintSolver::setupContactConstraint(btSolverConstra solverConstraint.m_jacDiagABInv = denom; } - solverConstraint.m_contactNormal = cp.m_normalWorldOnB; - solverConstraint.m_relpos1CrossNormal = torqueAxis0; - solverConstraint.m_relpos2CrossNormal = -torqueAxis1; + if (rb0) + { + solverConstraint.m_contactNormal1 = cp.m_normalWorldOnB; + solverConstraint.m_relpos1CrossNormal = torqueAxis0; + } else + { + solverConstraint.m_contactNormal1.setZero(); + solverConstraint.m_relpos1CrossNormal.setZero(); + } + if (rb1) + { + solverConstraint.m_contactNormal2 = -cp.m_normalWorldOnB; + solverConstraint.m_relpos2CrossNormal = -torqueAxis1; + }else + { + solverConstraint.m_contactNormal2.setZero(); + solverConstraint.m_relpos2CrossNormal.setZero(); + } btScalar restitution = 0.f; btScalar penetration = cp.getDistance()+infoGlobal.m_linearSlop; @@ -611,8 +655,8 @@ void btSequentialImpulseConstraintSolver::setupContactConstraint(btSolverConstra vel2 = rb1? rb1->getVelocityInLocalPoint(rel_pos2) : btVector3(0,0,0); // btVector3 vel2 = rb1 ? rb1->getVelocityInLocalPoint(rel_pos2) : btVector3(0,0,0); - vel = vel1 - vel2; - rel_vel = cp.m_normalWorldOnB.dot(vel); + btVector3 vel = vel1 - vel2; + btScalar rel_vel = cp.m_normalWorldOnB.dot(vel); @@ -632,9 +676,9 @@ void btSequentialImpulseConstraintSolver::setupContactConstraint(btSolverConstra { solverConstraint.m_appliedImpulse = cp.m_appliedImpulse * infoGlobal.m_warmstartingFactor; if (rb0) - bodyA->internalApplyImpulse(solverConstraint.m_contactNormal*bodyA->internalGetInvMass()*rb0->getLinearFactor(),solverConstraint.m_angularComponentA,solverConstraint.m_appliedImpulse); + bodyA->internalApplyImpulse(solverConstraint.m_contactNormal1*bodyA->internalGetInvMass()*rb0->getLinearFactor(),solverConstraint.m_angularComponentA,solverConstraint.m_appliedImpulse); if (rb1) - bodyB->internalApplyImpulse(solverConstraint.m_contactNormal*bodyB->internalGetInvMass()*rb1->getLinearFactor(),-solverConstraint.m_angularComponentB,-(btScalar)solverConstraint.m_appliedImpulse); + bodyB->internalApplyImpulse(-solverConstraint.m_contactNormal2*bodyB->internalGetInvMass()*rb1->getLinearFactor(),-solverConstraint.m_angularComponentB,-(btScalar)solverConstraint.m_appliedImpulse); } else { solverConstraint.m_appliedImpulse = 0.f; @@ -643,10 +687,17 @@ void btSequentialImpulseConstraintSolver::setupContactConstraint(btSolverConstra solverConstraint.m_appliedPushImpulse = 0.f; { - btScalar vel1Dotn = solverConstraint.m_contactNormal.dot(rb0?bodyA->m_linearVelocity:btVector3(0,0,0)) - + solverConstraint.m_relpos1CrossNormal.dot(rb0?bodyA->m_angularVelocity:btVector3(0,0,0)); - btScalar vel2Dotn = -solverConstraint.m_contactNormal.dot(rb1?bodyB->m_linearVelocity:btVector3(0,0,0)) - + solverConstraint.m_relpos2CrossNormal.dot(rb1?bodyB->m_angularVelocity:btVector3(0,0,0)); + + btVector3 externalForceImpulseA = bodyA->m_originalBody ? bodyA->m_externalForceImpulse: btVector3(0,0,0); + btVector3 externalTorqueImpulseA = bodyA->m_originalBody ? bodyA->m_externalTorqueImpulse: btVector3(0,0,0); + btVector3 externalForceImpulseB = bodyB->m_originalBody ? bodyB->m_externalForceImpulse: btVector3(0,0,0); + btVector3 externalTorqueImpulseB = bodyB->m_originalBody ?bodyB->m_externalTorqueImpulse : btVector3(0,0,0); + + + btScalar vel1Dotn = solverConstraint.m_contactNormal1.dot(bodyA->m_linearVelocity+externalForceImpulseA) + + solverConstraint.m_relpos1CrossNormal.dot(bodyA->m_angularVelocity+externalTorqueImpulseA); + btScalar vel2Dotn = solverConstraint.m_contactNormal2.dot(bodyB->m_linearVelocity+externalForceImpulseB) + + solverConstraint.m_relpos2CrossNormal.dot(bodyB->m_angularVelocity+externalTorqueImpulseB); btScalar rel_vel = vel1Dotn+vel2Dotn; btScalar positionalError = 0.f; @@ -675,7 +726,7 @@ void btSequentialImpulseConstraintSolver::setupContactConstraint(btSolverConstra if (!infoGlobal.m_splitImpulse || (penetration > infoGlobal.m_splitImpulsePenetrationThreshold)) { //combine position and velocity into rhs - solverConstraint.m_rhs = penetrationImpulse+velocityImpulse; + solverConstraint.m_rhs = penetrationImpulse+velocityImpulse;//-solverConstraint.m_contactNormal1.dot(bodyA->m_externalForce*bodyA->m_invMass-bodyB->m_externalForce/bodyB->m_invMass)*solverConstraint.m_jacDiagABInv; solverConstraint.m_rhsPenetration = 0.f; } else @@ -713,9 +764,9 @@ void btSequentialImpulseConstraintSolver::setFrictionConstraintImpulse( btSolver { frictionConstraint1.m_appliedImpulse = cp.m_appliedImpulseLateral1 * infoGlobal.m_warmstartingFactor; if (rb0) - bodyA->internalApplyImpulse(frictionConstraint1.m_contactNormal*rb0->getInvMass()*rb0->getLinearFactor(),frictionConstraint1.m_angularComponentA,frictionConstraint1.m_appliedImpulse); + bodyA->internalApplyImpulse(frictionConstraint1.m_contactNormal1*rb0->getInvMass()*rb0->getLinearFactor(),frictionConstraint1.m_angularComponentA,frictionConstraint1.m_appliedImpulse); if (rb1) - bodyB->internalApplyImpulse(frictionConstraint1.m_contactNormal*rb1->getInvMass()*rb1->getLinearFactor(),-frictionConstraint1.m_angularComponentB,-(btScalar)frictionConstraint1.m_appliedImpulse); + bodyB->internalApplyImpulse(-frictionConstraint1.m_contactNormal2*rb1->getInvMass()*rb1->getLinearFactor(),-frictionConstraint1.m_angularComponentB,-(btScalar)frictionConstraint1.m_appliedImpulse); } else { frictionConstraint1.m_appliedImpulse = 0.f; @@ -729,9 +780,9 @@ void btSequentialImpulseConstraintSolver::setFrictionConstraintImpulse( btSolver { frictionConstraint2.m_appliedImpulse = cp.m_appliedImpulseLateral2 * infoGlobal.m_warmstartingFactor; if (rb0) - bodyA->internalApplyImpulse(frictionConstraint2.m_contactNormal*rb0->getInvMass(),frictionConstraint2.m_angularComponentA,frictionConstraint2.m_appliedImpulse); + bodyA->internalApplyImpulse(frictionConstraint2.m_contactNormal1*rb0->getInvMass(),frictionConstraint2.m_angularComponentA,frictionConstraint2.m_appliedImpulse); if (rb1) - bodyB->internalApplyImpulse(frictionConstraint2.m_contactNormal*rb1->getInvMass(),-frictionConstraint2.m_angularComponentB,-(btScalar)frictionConstraint2.m_appliedImpulse); + bodyB->internalApplyImpulse(-frictionConstraint2.m_contactNormal2*rb1->getInvMass(),-frictionConstraint2.m_angularComponentB,-(btScalar)frictionConstraint2.m_appliedImpulse); } else { frictionConstraint2.m_appliedImpulse = 0.f; @@ -749,8 +800,8 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m colObj0 = (btCollisionObject*)manifold->getBody0(); colObj1 = (btCollisionObject*)manifold->getBody1(); - int solverBodyIdA = getOrInitSolverBody(*colObj0); - int solverBodyIdB = getOrInitSolverBody(*colObj1); + int solverBodyIdA = getOrInitSolverBody(*colObj0,infoGlobal.m_timeStep); + int solverBodyIdB = getOrInitSolverBody(*colObj1,infoGlobal.m_timeStep); // btRigidBody* bodyA = btRigidBody::upcast(colObj0); // btRigidBody* bodyB = btRigidBody::upcast(colObj1); @@ -761,7 +812,7 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m ///avoid collision response between two static objects - if (!solverBodyA || (!solverBodyA->m_originalBody && (!solverBodyB || !solverBodyB->m_originalBody))) + if (!solverBodyA || (solverBodyA->m_invMass.isZero() && (!solverBodyB || solverBodyB->m_invMass.isZero()))) return; int rollingFriction=1; @@ -775,19 +826,35 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m btVector3 rel_pos1; btVector3 rel_pos2; btScalar relaxation; - btScalar rel_vel; - btVector3 vel; + int frictionIndex = m_tmpSolverContactConstraintPool.size(); btSolverConstraint& solverConstraint = m_tmpSolverContactConstraintPool.expandNonInitializing(); -// btRigidBody* rb0 = btRigidBody::upcast(colObj0); -// btRigidBody* rb1 = btRigidBody::upcast(colObj1); + btRigidBody* rb0 = btRigidBody::upcast(colObj0); + btRigidBody* rb1 = btRigidBody::upcast(colObj1); solverConstraint.m_solverBodyIdA = solverBodyIdA; solverConstraint.m_solverBodyIdB = solverBodyIdB; solverConstraint.m_originalContactPoint = &cp; - setupContactConstraint(solverConstraint, solverBodyIdA, solverBodyIdB, cp, infoGlobal, vel, rel_vel, relaxation, rel_pos1, rel_pos2); + const btVector3& pos1 = cp.getPositionWorldOnA(); + const btVector3& pos2 = cp.getPositionWorldOnB(); + + rel_pos1 = pos1 - colObj0->getWorldTransform().getOrigin(); + rel_pos2 = pos2 - colObj1->getWorldTransform().getOrigin(); + + btVector3 vel1;// = rb0 ? rb0->getVelocityInLocalPoint(rel_pos1) : btVector3(0,0,0); + btVector3 vel2;// = rb1 ? rb1->getVelocityInLocalPoint(rel_pos2) : btVector3(0,0,0); + + solverBodyA->getVelocityInLocalPointNoDelta(rel_pos1,vel1); + solverBodyB->getVelocityInLocalPointNoDelta(rel_pos2,vel2 ); + + btVector3 vel = vel1 - vel2; + btScalar rel_vel = cp.m_normalWorldOnB.dot(vel); + + setupContactConstraint(solverConstraint, solverBodyIdA, solverBodyIdB, cp, infoGlobal, relaxation, rel_pos1, rel_pos2); + + // const btVector3& pos1 = cp.getPositionWorldOnA(); // const btVector3& pos2 = cp.getPositionWorldOnB(); @@ -796,9 +863,11 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m solverConstraint.m_frictionIndex = m_tmpSolverContactFrictionConstraintPool.size(); - btVector3 angVelA,angVelB; - solverBodyA->getAngularVelocity(angVelA); - solverBodyB->getAngularVelocity(angVelB); + btVector3 angVelA(0,0,0),angVelB(0,0,0); + if (rb0) + angVelA = rb0->getAngularVelocity(); + if (rb1) + angVelB = rb1->getAngularVelocity(); btVector3 relAngVel = angVelB-angVelA; if ((cp.m_combinedRollingFriction>0.f) && (rollingFriction>0)) @@ -852,6 +921,10 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m if (!(infoGlobal.m_solverMode & SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION) && lat_rel_vel > SIMD_EPSILON) { cp.m_lateralFrictionDir1 *= 1.f/btSqrt(lat_rel_vel); + applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); + applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); + addFrictionConstraint(cp.m_lateralFrictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); + if((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) { cp.m_lateralFrictionDir2 = cp.m_lateralFrictionDir1.cross(cp.m_normalWorldOnB); @@ -859,17 +932,16 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION); applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION); addFrictionConstraint(cp.m_lateralFrictionDir2,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); - } - applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); - applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); - addFrictionConstraint(cp.m_lateralFrictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); - } else { btPlaneSpace1(cp.m_normalWorldOnB,cp.m_lateralFrictionDir1,cp.m_lateralFrictionDir2); + applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); + applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); + addFrictionConstraint(cp.m_lateralFrictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); + if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) { applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION); @@ -877,9 +949,6 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m addFrictionConstraint(cp.m_lateralFrictionDir2,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); } - applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); - applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); - addFrictionConstraint(cp.m_lateralFrictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS) && (infoGlobal.m_solverMode & SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION)) { @@ -894,8 +963,8 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) addFrictionConstraint(cp.m_lateralFrictionDir2,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation, cp.m_contactMotion2, cp.m_contactCFM2); - setFrictionConstraintImpulse( solverConstraint, solverBodyIdA, solverBodyIdB, cp, infoGlobal); } + setFrictionConstraintImpulse( solverConstraint, solverBodyIdA, solverBodyIdB, cp, infoGlobal); @@ -904,15 +973,29 @@ void btSequentialImpulseConstraintSolver::convertContact(btPersistentManifold* m } } -btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(btCollisionObject** bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer,btStackAlloc* stackAlloc) +void btSequentialImpulseConstraintSolver::convertContacts(btPersistentManifold** manifoldPtr,int numManifolds, const btContactSolverInfo& infoGlobal) { + int i; + btPersistentManifold* manifold = 0; +// btCollisionObject* colObj0=0,*colObj1=0; + + + for (i=0;igetInvMass()) { @@ -1007,9 +1091,8 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(btCol if (body->getFlags()&BT_ENABLE_GYROPSCOPIC_FORCE) { gyroForce = body->computeGyroscopicForce(infoGlobal.m_maxGyroscopicForce); + solverBody.m_externalTorqueImpulse -= gyroForce*body->getInvInertiaTensorWorld()*infoGlobal.m_timeStep; } - solverBody.m_linearVelocity += body->getTotalForce()*body->getInvMass()*infoGlobal.m_timeStep; - solverBody.m_angularVelocity += (body->getTotalTorque()-gyroForce)*body->getInvInertiaTensorWorld()*infoGlobal.m_timeStep; } } @@ -1079,8 +1162,8 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(btCol btRigidBody& rbA = constraint->getRigidBodyA(); btRigidBody& rbB = constraint->getRigidBodyB(); - int solverBodyIdA = getOrInitSolverBody(rbA); - int solverBodyIdB = getOrInitSolverBody(rbB); + int solverBodyIdA = getOrInitSolverBody(rbA,infoGlobal.m_timeStep); + int solverBodyIdB = getOrInitSolverBody(rbB,infoGlobal.m_timeStep); btSolverBody* bodyAPtr = &m_tmpSolverBodyPool[solverBodyIdA]; btSolverBody* bodyBPtr = &m_tmpSolverBodyPool[solverBodyIdB]; @@ -1119,9 +1202,9 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(btCol btTypedConstraint::btConstraintInfo2 info2; info2.fps = 1.f/infoGlobal.m_timeStep; info2.erp = infoGlobal.m_erp; - info2.m_J1linearAxis = currentConstraintRow->m_contactNormal; + info2.m_J1linearAxis = currentConstraintRow->m_contactNormal1; info2.m_J1angularAxis = currentConstraintRow->m_relpos1CrossNormal; - info2.m_J2linearAxis = 0; + info2.m_J2linearAxis = currentConstraintRow->m_contactNormal2; info2.m_J2angularAxis = currentConstraintRow->m_relpos2CrossNormal; info2.rowskip = sizeof(btSolverConstraint)/sizeof(btScalar);//check this ///the size of btSolverConstraint needs be a multiple of btScalar @@ -1162,14 +1245,14 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(btCol } { - btVector3 iMJlA = solverConstraint.m_contactNormal*rbA.getInvMass(); + btVector3 iMJlA = solverConstraint.m_contactNormal1*rbA.getInvMass(); btVector3 iMJaA = rbA.getInvInertiaTensorWorld()*solverConstraint.m_relpos1CrossNormal; - btVector3 iMJlB = solverConstraint.m_contactNormal*rbB.getInvMass();//sign of normal? + btVector3 iMJlB = solverConstraint.m_contactNormal2*rbB.getInvMass();//sign of normal? btVector3 iMJaB = rbB.getInvInertiaTensorWorld()*solverConstraint.m_relpos2CrossNormal; - btScalar sum = iMJlA.dot(solverConstraint.m_contactNormal); + btScalar sum = iMJlA.dot(solverConstraint.m_contactNormal1); sum += iMJaA.dot(solverConstraint.m_relpos1CrossNormal); - sum += iMJlB.dot(solverConstraint.m_contactNormal); + sum += iMJlB.dot(solverConstraint.m_contactNormal2); sum += iMJaB.dot(solverConstraint.m_relpos2CrossNormal); btScalar fsum = btFabs(sum); btAssert(fsum > SIMD_EPSILON); @@ -1177,15 +1260,22 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(btCol } - ///fix rhs - ///todo: add force/torque accelerators + { btScalar rel_vel; - btScalar vel1Dotn = solverConstraint.m_contactNormal.dot(rbA.getLinearVelocity()) + solverConstraint.m_relpos1CrossNormal.dot(rbA.getAngularVelocity()); - btScalar vel2Dotn = -solverConstraint.m_contactNormal.dot(rbB.getLinearVelocity()) + solverConstraint.m_relpos2CrossNormal.dot(rbB.getAngularVelocity()); + btVector3 externalForceImpulseA = bodyAPtr->m_originalBody ? bodyAPtr->m_externalForceImpulse : btVector3(0,0,0); + btVector3 externalTorqueImpulseA = bodyAPtr->m_originalBody ? bodyAPtr->m_externalTorqueImpulse : btVector3(0,0,0); + + btVector3 externalForceImpulseB = bodyBPtr->m_originalBody ? bodyBPtr->m_externalForceImpulse : btVector3(0,0,0); + btVector3 externalTorqueImpulseB = bodyBPtr->m_originalBody ?bodyBPtr->m_externalTorqueImpulse : btVector3(0,0,0); + + btScalar vel1Dotn = solverConstraint.m_contactNormal1.dot(rbA.getLinearVelocity()+externalForceImpulseA) + + solverConstraint.m_relpos1CrossNormal.dot(rbA.getAngularVelocity()+externalTorqueImpulseA); + + btScalar vel2Dotn = solverConstraint.m_contactNormal2.dot(rbB.getLinearVelocity()+externalForceImpulseB) + + solverConstraint.m_relpos2CrossNormal.dot(rbB.getAngularVelocity()+externalTorqueImpulseB); rel_vel = vel1Dotn+vel2Dotn; - btScalar restitution = 0.f; btScalar positionalError = solverConstraint.m_rhs;//already filled in by getConstraintInfo2 btScalar velocityError = restitution - rel_vel * info2.m_damping; @@ -1194,6 +1284,7 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(btCol solverConstraint.m_rhs = penetrationImpulse+velocityImpulse; solverConstraint.m_appliedImpulse = 0.f; + } } } @@ -1201,18 +1292,8 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup(btCol } } - { - int i; - btPersistentManifold* manifold = 0; -// btCollisionObject* colObj0=0,*colObj1=0; + convertContacts(manifoldPtr,numManifolds,infoGlobal); - - for (i=0;iisEnabled()) - { - int bodyAid = getOrInitSolverBody(constraints[j]->getRigidBodyA()); - int bodyBid = getOrInitSolverBody(constraints[j]->getRigidBodyB()); - btSolverBody& bodyA = m_tmpSolverBodyPool[bodyAid]; - btSolverBody& bodyB = m_tmpSolverBodyPool[bodyBid]; - constraints[j]->solveConstraintObsolete(bodyA,bodyB,infoGlobal.m_timeStep); - } + if (constraints[j]->isEnabled()) + { + int bodyAid = getOrInitSolverBody(constraints[j]->getRigidBodyA(),infoGlobal.m_timeStep); + int bodyBid = getOrInitSolverBody(constraints[j]->getRigidBodyB(),infoGlobal.m_timeStep); + btSolverBody& bodyA = m_tmpSolverBodyPool[bodyAid]; + btSolverBody& bodyB = m_tmpSolverBodyPool[bodyBid]; + constraints[j]->solveConstraintObsolete(bodyA,bodyB,infoGlobal.m_timeStep); + } } ///solve all contact constraints using SIMD, if available @@ -1371,7 +1452,8 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration for (j=0;jisEnabled()) - { - int bodyAid = getOrInitSolverBody(constraints[j]->getRigidBodyA()); - int bodyBid = getOrInitSolverBody(constraints[j]->getRigidBodyB()); - btSolverBody& bodyA = m_tmpSolverBodyPool[bodyAid]; - btSolverBody& bodyB = m_tmpSolverBodyPool[bodyBid]; - constraints[j]->solveConstraintObsolete(bodyA,bodyB,infoGlobal.m_timeStep); - } + if (constraints[j]->isEnabled()) + { + int bodyAid = getOrInitSolverBody(constraints[j]->getRigidBodyA(),infoGlobal.m_timeStep); + int bodyBid = getOrInitSolverBody(constraints[j]->getRigidBodyB(),infoGlobal.m_timeStep); + btSolverBody& bodyA = m_tmpSolverBodyPool[bodyAid]; + btSolverBody& bodyB = m_tmpSolverBodyPool[bodyBid]; + constraints[j]->solveConstraintObsolete(bodyA,bodyB,infoGlobal.m_timeStep); + } } ///solve all contact constraints int numPoolConstraints = m_tmpSolverContactConstraintPool.size(); @@ -1487,7 +1570,7 @@ btScalar btSequentialImpulseConstraintSolver::solveSingleIteration(int iteration } -void btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySplitImpulseIterations(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer,btStackAlloc* stackAlloc) +void btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySplitImpulseIterations(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer) { int iteration; if (infoGlobal.m_splitImpulse) @@ -1527,20 +1610,20 @@ void btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySplitImpulseIte } } -btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyIterations(btCollisionObject** bodies ,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer,btStackAlloc* stackAlloc) +btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyIterations(btCollisionObject** bodies ,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer) { BT_PROFILE("solveGroupCacheFriendlyIterations"); { ///this is a special step to resolve penetrations (just for contacts) - solveGroupCacheFriendlySplitImpulseIterations(bodies ,numBodies,manifoldPtr, numManifolds,constraints,numConstraints,infoGlobal,debugDrawer,stackAlloc); + solveGroupCacheFriendlySplitImpulseIterations(bodies ,numBodies,manifoldPtr, numManifolds,constraints,numConstraints,infoGlobal,debugDrawer); int maxIterations = m_maxOverrideNumSolverIterations > infoGlobal.m_numIterations? m_maxOverrideNumSolverIterations : infoGlobal.m_numIterations; for ( int iteration = 0 ; iteration< maxIterations ; iteration++) //for ( int iteration = maxIterations-1 ; iteration >= 0;iteration--) { - solveSingleIteration(iteration, bodies ,numBodies,manifoldPtr, numManifolds,constraints,numConstraints,infoGlobal,debugDrawer,stackAlloc); + solveSingleIteration(iteration, bodies ,numBodies,manifoldPtr, numManifolds,constraints,numConstraints,infoGlobal,debugDrawer); } } @@ -1580,10 +1663,10 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyFinish(btCo btJointFeedback* fb = constr->getJointFeedback(); if (fb) { - fb->m_appliedForceBodyA += solverConstr.m_contactNormal*solverConstr.m_appliedImpulse*constr->getRigidBodyA().getLinearFactor()/infoGlobal.m_timeStep; - fb->m_appliedForceBodyB += -solverConstr.m_contactNormal*solverConstr.m_appliedImpulse*constr->getRigidBodyB().getLinearFactor()/infoGlobal.m_timeStep; + fb->m_appliedForceBodyA += solverConstr.m_contactNormal1*solverConstr.m_appliedImpulse*constr->getRigidBodyA().getLinearFactor()/infoGlobal.m_timeStep; + fb->m_appliedForceBodyB += solverConstr.m_contactNormal2*solverConstr.m_appliedImpulse*constr->getRigidBodyB().getLinearFactor()/infoGlobal.m_timeStep; fb->m_appliedTorqueBodyA += solverConstr.m_relpos1CrossNormal* constr->getRigidBodyA().getAngularFactor()*solverConstr.m_appliedImpulse/infoGlobal.m_timeStep; - fb->m_appliedTorqueBodyB += -solverConstr.m_relpos1CrossNormal* constr->getRigidBodyB().getAngularFactor()*solverConstr.m_appliedImpulse/infoGlobal.m_timeStep; + fb->m_appliedTorqueBodyB += solverConstr.m_relpos2CrossNormal* constr->getRigidBodyB().getAngularFactor()*solverConstr.m_appliedImpulse/infoGlobal.m_timeStep; /*RGM ???? */ } @@ -1605,9 +1688,15 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyFinish(btCo m_tmpSolverBodyPool[i].writebackVelocityAndTransform(infoGlobal.m_timeStep, infoGlobal.m_splitImpulseTurnErp); else m_tmpSolverBodyPool[i].writebackVelocity(); + + m_tmpSolverBodyPool[i].m_originalBody->setLinearVelocity( + m_tmpSolverBodyPool[i].m_linearVelocity+ + m_tmpSolverBodyPool[i].m_externalForceImpulse); + + m_tmpSolverBodyPool[i].m_originalBody->setAngularVelocity( + m_tmpSolverBodyPool[i].m_angularVelocity+ + m_tmpSolverBodyPool[i].m_externalTorqueImpulse); - m_tmpSolverBodyPool[i].m_originalBody->setLinearVelocity(m_tmpSolverBodyPool[i].m_linearVelocity); - m_tmpSolverBodyPool[i].m_originalBody->setAngularVelocity(m_tmpSolverBodyPool[i].m_angularVelocity); if (infoGlobal.m_splitImpulse) m_tmpSolverBodyPool[i].m_originalBody->setWorldTransform(m_tmpSolverBodyPool[i].m_worldTransform); @@ -1627,15 +1716,15 @@ btScalar btSequentialImpulseConstraintSolver::solveGroupCacheFriendlyFinish(btCo /// btSequentialImpulseConstraintSolver Sequentially applies impulses -btScalar btSequentialImpulseConstraintSolver::solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer,btStackAlloc* stackAlloc,btDispatcher* /*dispatcher*/) +btScalar btSequentialImpulseConstraintSolver::solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer,btDispatcher* /*dispatcher*/) { BT_PROFILE("solveGroup"); //you need to provide at least some bodies - solveGroupCacheFriendlySetup( bodies, numBodies, manifoldPtr, numManifolds,constraints, numConstraints,infoGlobal,debugDrawer, stackAlloc); + solveGroupCacheFriendlySetup( bodies, numBodies, manifoldPtr, numManifolds,constraints, numConstraints,infoGlobal,debugDrawer); - solveGroupCacheFriendlyIterations(bodies, numBodies, manifoldPtr, numManifolds,constraints, numConstraints,infoGlobal,debugDrawer, stackAlloc); + solveGroupCacheFriendlyIterations(bodies, numBodies, manifoldPtr, numManifolds,constraints, numConstraints,infoGlobal,debugDrawer); solveGroupCacheFriendlyFinish(bodies, numBodies, infoGlobal); diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h index 2eea6be0d..180d2a385 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h @@ -18,7 +18,6 @@ subject to the following restrictions: class btIDebugDraw; class btPersistentManifold; -class btStackAlloc; class btDispatcher; class btCollisionObject; #include "BulletDynamics/ConstraintSolver/btTypedConstraint.h" @@ -43,7 +42,7 @@ protected: btAlignedObjectArray m_orderFrictionConstraintPool; btAlignedObjectArray m_tmpConstraintSizesPool; int m_maxOverrideNumSolverIterations; - + int m_fixedBodyId; void setupFrictionConstraint( btSolverConstraint& solverConstraint, const btVector3& normalAxis,int solverBodyIdA,int solverBodyIdB, btManifoldPoint& cp,const btVector3& rel_pos1,const btVector3& rel_pos2, btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation, @@ -57,10 +56,11 @@ protected: btSolverConstraint& addFrictionConstraint(const btVector3& normalAxis,int solverBodyIdA,int solverBodyIdB,int frictionIndex,btManifoldPoint& cp,const btVector3& rel_pos1,const btVector3& rel_pos2,btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation, btScalar desiredVelocity=0., btScalar cfmSlip=0.); btSolverConstraint& addRollingFrictionConstraint(const btVector3& normalAxis,int solverBodyIdA,int solverBodyIdB,int frictionIndex,btManifoldPoint& cp,const btVector3& rel_pos1,const btVector3& rel_pos2,btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation, btScalar desiredVelocity=0, btScalar cfmSlip=0.f); - + void setupContactConstraint(btSolverConstraint& solverConstraint, int solverBodyIdA, int solverBodyIdB, btManifoldPoint& cp, - const btContactSolverInfo& infoGlobal, btVector3& vel, btScalar& rel_vel, btScalar& relaxation, - btVector3& rel_pos1, btVector3& rel_pos2); + const btContactSolverInfo& infoGlobal,btScalar& relaxation, const btVector3& rel_pos1, const btVector3& rel_pos2); + + static void applyAnisotropicFriction(btCollisionObject* colObj,btVector3& frictionDirection, int frictionMode); void setFrictionConstraintImpulse( btSolverConstraint& solverConstraint, int solverBodyIdA,int solverBodyIdB, btManifoldPoint& cp, const btContactSolverInfo& infoGlobal); @@ -71,6 +71,8 @@ protected: btScalar restitutionCurve(btScalar rel_vel, btScalar restitution); + virtual void convertContacts(btPersistentManifold** manifoldPtr, int numManifolds, const btContactSolverInfo& infoGlobal); + void convertContact(btPersistentManifold* manifold,const btContactSolverInfo& infoGlobal); @@ -83,8 +85,8 @@ protected: const btSolverConstraint& contactConstraint); //internal method - int getOrInitSolverBody(btCollisionObject& body); - void initSolverBody(btSolverBody* solverBody, btCollisionObject* collisionObject); + int getOrInitSolverBody(btCollisionObject& body,btScalar timeStep); + void initSolverBody(btSolverBody* solverBody, btCollisionObject* collisionObject, btScalar timeStep); void resolveSingleConstraintRowGeneric(btSolverBody& bodyA,btSolverBody& bodyB,const btSolverConstraint& contactConstraint); @@ -97,12 +99,12 @@ protected: protected: - virtual void solveGroupCacheFriendlySplitImpulseIterations(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer,btStackAlloc* stackAlloc); + virtual void solveGroupCacheFriendlySplitImpulseIterations(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); virtual btScalar solveGroupCacheFriendlyFinish(btCollisionObject** bodies,int numBodies,const btContactSolverInfo& infoGlobal); - btScalar solveSingleIteration(int iteration, btCollisionObject** bodies ,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer,btStackAlloc* stackAlloc); + virtual btScalar solveSingleIteration(int iteration, btCollisionObject** bodies ,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); - virtual btScalar solveGroupCacheFriendlySetup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer,btStackAlloc* stackAlloc); - virtual btScalar solveGroupCacheFriendlyIterations(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer,btStackAlloc* stackAlloc); + virtual btScalar solveGroupCacheFriendlySetup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); + virtual btScalar solveGroupCacheFriendlyIterations(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); public: @@ -112,7 +114,7 @@ public: btSequentialImpulseConstraintSolver(); virtual ~btSequentialImpulseConstraintSolver(); - virtual btScalar solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& info, btIDebugDraw* debugDrawer, btStackAlloc* stackAlloc,btDispatcher* dispatcher); + virtual btScalar solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& info, btIDebugDraw* debugDrawer,btDispatcher* dispatcher); @@ -132,6 +134,11 @@ public: return m_btSeed2; } + + virtual btConstraintSolverType getSolverType() const + { + return BT_SEQUENTIAL_IMPULSE_SOLVER; + } }; diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp index b69f46da1..aff9f27f5 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.cpp @@ -426,6 +426,8 @@ void btSliderConstraint::getInfo2NonVirtual(btConstraintInfo2* info, const btTra for (i=0; i<3; i++) info->m_J2angularAxis[s3+i] = -tmpB[i]; for (i=0; i<3; i++) info->m_J1linearAxis[s2+i] = p[i]; for (i=0; i<3; i++) info->m_J1linearAxis[s3+i] = q[i]; + for (i=0; i<3; i++) info->m_J2linearAxis[s2+i] = -p[i]; + for (i=0; i<3; i++) info->m_J2linearAxis[s3+i] = -q[i]; } else { // old way - maybe incorrect if bodies are not on the slider axis @@ -440,6 +442,8 @@ void btSliderConstraint::getInfo2NonVirtual(btConstraintInfo2* info, const btTra for (i=0; i<3; i++) info->m_J1linearAxis[s2+i] = p[i]; for (i=0; i<3; i++) info->m_J1linearAxis[s3+i] = q[i]; + for (i=0; i<3; i++) info->m_J2linearAxis[s2+i] = -p[i]; + for (i=0; i<3; i++) info->m_J2linearAxis[s3+i] = -q[i]; } // compute two elements of right hand side @@ -479,6 +483,9 @@ void btSliderConstraint::getInfo2NonVirtual(btConstraintInfo2* info, const btTra info->m_J1linearAxis[srow+0] = ax1[0]; info->m_J1linearAxis[srow+1] = ax1[1]; info->m_J1linearAxis[srow+2] = ax1[2]; + info->m_J2linearAxis[srow+0] = -ax1[0]; + info->m_J2linearAxis[srow+1] = -ax1[1]; + info->m_J2linearAxis[srow+2] = -ax1[2]; // linear torque decoupling step: // // we have to be careful that the linear constraint forces (+/- ax1) applied to the two bodies diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.h index ca8e715bc..57ebb47d8 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.h +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSliderConstraint.h @@ -25,7 +25,13 @@ TODO: #ifndef BT_SLIDER_CONSTRAINT_H #define BT_SLIDER_CONSTRAINT_H - +#ifdef BT_USE_DOUBLE_PRECISION +#define btSliderConstraintData2 btSliderConstraintDoubleData +#define btSliderConstraintDataName "btSliderConstraintDoubleData" +#else +#define btSliderConstraintData2 btSliderConstraintData +#define btSliderConstraintDataName "btSliderConstraintData" +#endif //BT_USE_DOUBLE_PRECISION #include "LinearMath/btVector3.h" #include "btJacobianEntry.h" @@ -283,7 +289,10 @@ public: }; + ///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 + + struct btSliderConstraintData { btTypedConstraintData m_typeConstraintData; @@ -302,31 +311,48 @@ struct btSliderConstraintData }; +struct btSliderConstraintDoubleData +{ + btTypedConstraintDoubleData m_typeConstraintData; + btTransformDoubleData m_rbAFrame; // constraint axii. Assumes z is hinge axis. + btTransformDoubleData m_rbBFrame; + + double m_linearUpperLimit; + double m_linearLowerLimit; + + double m_angularUpperLimit; + double m_angularLowerLimit; + + int m_useLinearReferenceFrameA; + int m_useOffsetForConstraintFrame; + +}; + SIMD_FORCE_INLINE int btSliderConstraint::calculateSerializeBufferSize() const { - return sizeof(btSliderConstraintData); + return sizeof(btSliderConstraintData2); } ///fills the dataBuffer and returns the struct name (and 0 on failure) SIMD_FORCE_INLINE const char* btSliderConstraint::serialize(void* dataBuffer, btSerializer* serializer) const { - btSliderConstraintData* sliderData = (btSliderConstraintData*) dataBuffer; + btSliderConstraintData2* sliderData = (btSliderConstraintData2*) dataBuffer; btTypedConstraint::serialize(&sliderData->m_typeConstraintData,serializer); - m_frameInA.serializeFloat(sliderData->m_rbAFrame); - m_frameInB.serializeFloat(sliderData->m_rbBFrame); + m_frameInA.serialize(sliderData->m_rbAFrame); + m_frameInB.serialize(sliderData->m_rbBFrame); - sliderData->m_linearUpperLimit = float(m_upperLinLimit); - sliderData->m_linearLowerLimit = float(m_lowerLinLimit); + sliderData->m_linearUpperLimit = m_upperLinLimit; + sliderData->m_linearLowerLimit = m_lowerLinLimit; - sliderData->m_angularUpperLimit = float(m_upperAngLimit); - sliderData->m_angularLowerLimit = float(m_lowerAngLimit); + sliderData->m_angularUpperLimit = m_upperAngLimit; + sliderData->m_angularLowerLimit = m_lowerAngLimit; sliderData->m_useLinearReferenceFrameA = m_useLinearReferenceFrameA; sliderData->m_useOffsetForConstraintFrame = m_useOffsetForConstraintFrame; - return "btSliderConstraintData"; + return btSliderConstraintDataName; } diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverBody.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverBody.h index 4e1a8df6a..27ccefe41 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverBody.h +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverBody.h @@ -105,7 +105,7 @@ operator+(const btSimdScalar& v1, const btSimdScalar& v2) #endif ///The btSolverBody is an internal datastructure for the constraint solver. Only necessary data is packed to increase cache coherence/performance. -ATTRIBUTE_ALIGNED64 (struct) btSolverBody +ATTRIBUTE_ALIGNED16 (struct) btSolverBody { BT_DECLARE_ALIGNED_ALLOCATOR(); btTransform m_worldTransform; @@ -118,6 +118,8 @@ ATTRIBUTE_ALIGNED64 (struct) btSolverBody btVector3 m_turnVelocity; btVector3 m_linearVelocity; btVector3 m_angularVelocity; + btVector3 m_externalForceImpulse; + btVector3 m_externalTorqueImpulse; btRigidBody* m_originalBody; void setWorldTransform(const btTransform& worldTransform) @@ -130,6 +132,17 @@ ATTRIBUTE_ALIGNED64 (struct) btSolverBody return m_worldTransform; } + + + SIMD_FORCE_INLINE void getVelocityInLocalPointNoDelta(const btVector3& rel_pos, btVector3& velocity ) const + { + if (m_originalBody) + velocity = m_linearVelocity + m_externalForceImpulse + (m_angularVelocity+m_externalTorqueImpulse).cross(rel_pos); + else + velocity.setValue(0,0,0); + } + + SIMD_FORCE_INLINE void getVelocityInLocalPointObsolete(const btVector3& rel_pos, btVector3& velocity ) const { if (m_originalBody) diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverConstraint.h index c3951f664..5515e6b31 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverConstraint.h +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btSolverConstraint.h @@ -32,10 +32,10 @@ ATTRIBUTE_ALIGNED16 (struct) btSolverConstraint BT_DECLARE_ALIGNED_ALLOCATOR(); btVector3 m_relpos1CrossNormal; - btVector3 m_contactNormal; + btVector3 m_contactNormal1; btVector3 m_relpos2CrossNormal; - //btVector3 m_contactNormal2;//usually m_contactNormal2 == -m_contactNormal + btVector3 m_contactNormal2; //usually m_contactNormal2 == -m_contactNormal1, but not always btVector3 m_angularComponentA; btVector3 m_angularComponentB; @@ -55,6 +55,7 @@ ATTRIBUTE_ALIGNED16 (struct) btSolverConstraint { void* m_originalContactPoint; btScalar m_unusedPadding4; + int m_numRowsForNonContactConstraint; }; int m_overrideNumSolverIterations; diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.cpp index 465c0746c..27fdd9d3d 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.cpp +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.cpp @@ -109,7 +109,7 @@ btScalar btTypedConstraint::getMotorFactor(btScalar pos, btScalar lowLim, btScal ///fills the dataBuffer and returns the struct name (and 0 on failure) const char* btTypedConstraint::serialize(void* dataBuffer, btSerializer* serializer) const { - btTypedConstraintData* tcd = (btTypedConstraintData*) dataBuffer; + btTypedConstraintData2* tcd = (btTypedConstraintData2*) dataBuffer; tcd->m_rbA = (btRigidBodyData*)serializer->getUniquePointer(&m_rbA); tcd->m_rbB = (btRigidBodyData*)serializer->getUniquePointer(&m_rbB); @@ -123,14 +123,14 @@ const char* btTypedConstraint::serialize(void* dataBuffer, btSerializer* seriali tcd->m_objectType = m_objectType; tcd->m_needsFeedback = m_needsFeedback; tcd->m_overrideNumSolverIterations = m_overrideNumSolverIterations; - tcd->m_breakingImpulseThreshold = float(m_breakingImpulseThreshold); + tcd->m_breakingImpulseThreshold = m_breakingImpulseThreshold; tcd->m_isEnabled = m_isEnabled? 1: 0; tcd->m_userConstraintId =m_userConstraintId; tcd->m_userConstraintType =m_userConstraintType; - tcd->m_appliedImpulse = float(m_appliedImpulse); - tcd->m_dbgDrawSize = float(m_dbgDrawSize ); + tcd->m_appliedImpulse = m_appliedImpulse; + tcd->m_dbgDrawSize = m_dbgDrawSize; tcd->m_disableCollisionsBetweenLinkedBodies = false; @@ -142,7 +142,7 @@ const char* btTypedConstraint::serialize(void* dataBuffer, btSerializer* seriali if (m_rbB.getConstraintRef(i) == this) tcd->m_disableCollisionsBetweenLinkedBodies = true; - return "btTypedConstraintData"; + return btTypedConstraintDataName; } btRigidBody& btTypedConstraint::getFixedBody() diff --git a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.h b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.h index 441fa3750..b58f984d0 100644 --- a/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.h +++ b/Engine/lib/bullet/src/BulletDynamics/ConstraintSolver/btTypedConstraint.h @@ -21,6 +21,15 @@ subject to the following restrictions: #include "btSolverConstraint.h" #include "BulletDynamics/Dynamics/btRigidBody.h" +#ifdef BT_USE_DOUBLE_PRECISION +#define btTypedConstraintData2 btTypedConstraintDoubleData +#define btTypedConstraintDataName "btTypedConstraintDoubleData" +#else +#define btTypedConstraintData2 btTypedConstraintFloatData +#define btTypedConstraintDataName "btTypedConstraintFloatData" +#endif //BT_USE_DOUBLE_PRECISION + + class btSerializer; //Don't change any of the existing enum values, so add enum types at the end for serialization compatibility @@ -34,6 +43,7 @@ enum btTypedConstraintType CONTACT_CONSTRAINT_TYPE, D6_SPRING_CONSTRAINT_TYPE, GEAR_CONSTRAINT_TYPE, + FIXED_CONSTRAINT_TYPE, MAX_CONSTRAINT_TYPE }; @@ -356,6 +366,33 @@ SIMD_FORCE_INLINE btScalar btAdjustAngleToLimits(btScalar angleInRadians, btScal } ///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 +struct btTypedConstraintFloatData +{ + btRigidBodyFloatData *m_rbA; + btRigidBodyFloatData *m_rbB; + char *m_name; + + int m_objectType; + int m_userConstraintType; + int m_userConstraintId; + int m_needsFeedback; + + float m_appliedImpulse; + float m_dbgDrawSize; + + int m_disableCollisionsBetweenLinkedBodies; + int m_overrideNumSolverIterations; + + float m_breakingImpulseThreshold; + int m_isEnabled; + +}; + +///do not change those serialization structures, it requires an updated sBulletDNAstr/sBulletDNAstr64 + +#define BT_BACKWARDS_COMPATIBLE_SERIALIZATION +#ifdef BT_BACKWARDS_COMPATIBLE_SERIALIZATION +///this structure is not used, except for loading pre-2.82 .bullet files struct btTypedConstraintData { btRigidBodyData *m_rbA; @@ -377,10 +414,35 @@ struct btTypedConstraintData int m_isEnabled; }; +#endif //BACKWARDS_COMPATIBLE + +struct btTypedConstraintDoubleData +{ + btRigidBodyDoubleData *m_rbA; + btRigidBodyDoubleData *m_rbB; + char *m_name; + + int m_objectType; + int m_userConstraintType; + int m_userConstraintId; + int m_needsFeedback; + + double m_appliedImpulse; + double m_dbgDrawSize; + + int m_disableCollisionsBetweenLinkedBodies; + int m_overrideNumSolverIterations; + + double m_breakingImpulseThreshold; + int m_isEnabled; + char padding[4]; + +}; + SIMD_FORCE_INLINE int btTypedConstraint::calculateSerializeBufferSize() const { - return sizeof(btTypedConstraintData); + return sizeof(btTypedConstraintData2); } diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp index 9ff2d9f11..fb8a4068e 100644 --- a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp @@ -87,7 +87,6 @@ struct InplaceSolverIslandCallback : public btSimulationIslandManager::IslandCal btTypedConstraint** m_sortedConstraints; int m_numConstraints; btIDebugDraw* m_debugDrawer; - btStackAlloc* m_stackAlloc; btDispatcher* m_dispatcher; btAlignedObjectArray m_bodies; @@ -104,7 +103,6 @@ struct InplaceSolverIslandCallback : public btSimulationIslandManager::IslandCal m_sortedConstraints(NULL), m_numConstraints(0), m_debugDrawer(NULL), - m_stackAlloc(stackAlloc), m_dispatcher(dispatcher) { @@ -135,7 +133,7 @@ struct InplaceSolverIslandCallback : public btSimulationIslandManager::IslandCal if (islandId<0) { ///we don't split islands, so all constraints/contact manifolds/bodies are passed into the solver regardless the island id - m_solver->solveGroup( bodies,numBodies,manifolds, numManifolds,&m_sortedConstraints[0],m_numConstraints,*m_solverInfo,m_debugDrawer,m_stackAlloc,m_dispatcher); + m_solver->solveGroup( bodies,numBodies,manifolds, numManifolds,&m_sortedConstraints[0],m_numConstraints,*m_solverInfo,m_debugDrawer,m_dispatcher); } else { //also add all non-contact constraints/joints for this island @@ -163,7 +161,7 @@ struct InplaceSolverIslandCallback : public btSimulationIslandManager::IslandCal if (m_solverInfo->m_minimumSolverBatchSize<=1) { - m_solver->solveGroup( bodies,numBodies,manifolds, numManifolds,startConstraint,numCurConstraints,*m_solverInfo,m_debugDrawer,m_stackAlloc,m_dispatcher); + m_solver->solveGroup( bodies,numBodies,manifolds, numManifolds,startConstraint,numCurConstraints,*m_solverInfo,m_debugDrawer,m_dispatcher); } else { @@ -190,7 +188,7 @@ struct InplaceSolverIslandCallback : public btSimulationIslandManager::IslandCal btPersistentManifold** manifold = m_manifolds.size()?&m_manifolds[0]:0; btTypedConstraint** constraints = m_constraints.size()?&m_constraints[0]:0; - m_solver->solveGroup( bodies,m_bodies.size(),manifold, m_manifolds.size(),constraints, m_constraints.size() ,*m_solverInfo,m_debugDrawer,m_stackAlloc,m_dispatcher); + m_solver->solveGroup( bodies,m_bodies.size(),manifold, m_manifolds.size(),constraints, m_constraints.size() ,*m_solverInfo,m_debugDrawer,m_dispatcher); m_bodies.resize(0); m_manifolds.resize(0); m_constraints.resize(0); @@ -210,7 +208,9 @@ m_gravity(0,-10,0), m_localTime(0), m_synchronizeAllMotionStates(false), m_applySpeculativeContactRestitution(false), -m_profileTimings(0) +m_profileTimings(0), +m_fixedTimeStep(0), +m_latencyMotionStateInterpolation(true) { if (!m_constraintSolver) @@ -232,7 +232,7 @@ m_profileTimings(0) { void* mem = btAlignedAlloc(sizeof(InplaceSolverIslandCallback),16); - m_solverIslandCallback = new (mem) InplaceSolverIslandCallback (m_constraintSolver, m_stackAlloc, dispatcher); + m_solverIslandCallback = new (mem) InplaceSolverIslandCallback (m_constraintSolver, 0, dispatcher); } } @@ -359,7 +359,9 @@ void btDiscreteDynamicsWorld::synchronizeSingleMotionState(btRigidBody* body) { btTransform interpolatedTransform; btTransformUtil::integrateTransform(body->getInterpolationWorldTransform(), - body->getInterpolationLinearVelocity(),body->getInterpolationAngularVelocity(),m_localTime*body->getHitFraction(),interpolatedTransform); + body->getInterpolationLinearVelocity(),body->getInterpolationAngularVelocity(), + (m_latencyMotionStateInterpolation && m_fixedTimeStep) ? m_localTime - m_fixedTimeStep : m_localTime*body->getHitFraction(), + interpolatedTransform); body->getMotionState()->setWorldTransform(interpolatedTransform); } } @@ -403,6 +405,7 @@ int btDiscreteDynamicsWorld::stepSimulation( btScalar timeStep,int maxSubSteps, if (maxSubSteps) { //fixed timestep with interpolation + m_fixedTimeStep = fixedTimeStep; m_localTime += timeStep; if (m_localTime >= fixedTimeStep) { @@ -413,7 +416,8 @@ int btDiscreteDynamicsWorld::stepSimulation( btScalar timeStep,int maxSubSteps, { //variable timestep fixedTimeStep = timeStep; - m_localTime = timeStep; + m_localTime = m_latencyMotionStateInterpolation ? 0 : timeStep; + m_fixedTimeStep = 0; if (btFuzzyZero(timeStep)) { numSimulationSubSteps = 0; @@ -724,7 +728,7 @@ void btDiscreteDynamicsWorld::solveConstraints(btContactSolverInfo& solverInfo) m_solverIslandCallback->processConstraints(); - m_constraintSolver->allSolved(solverInfo, m_debugDrawer, m_stackAlloc); + m_constraintSolver->allSolved(solverInfo, m_debugDrawer); } @@ -746,12 +750,7 @@ void btDiscreteDynamicsWorld::calculateSimulationIslands() if (((colObj0) && (!(colObj0)->isStaticOrKinematicObject())) && ((colObj1) && (!(colObj1)->isStaticOrKinematicObject()))) { - if (colObj0->isActive() || colObj1->isActive()) - { - - getSimulationIslandManager()->getUnionFind().unite((colObj0)->getIslandTag(), - (colObj1)->getIslandTag()); - } + getSimulationIslandManager()->getUnionFind().unite((colObj0)->getIslandTag(),(colObj1)->getIslandTag()); } } } @@ -770,12 +769,7 @@ void btDiscreteDynamicsWorld::calculateSimulationIslands() if (((colObj0) && (!(colObj0)->isStaticOrKinematicObject())) && ((colObj1) && (!(colObj1)->isStaticOrKinematicObject()))) { - if (colObj0->isActive() || colObj1->isActive()) - { - - getSimulationIslandManager()->getUnionFind().unite((colObj0)->getIslandTag(), - (colObj1)->getIslandTag()); - } + getSimulationIslandManager()->getUnionFind().unite((colObj0)->getIslandTag(),(colObj1)->getIslandTag()); } } } @@ -1131,7 +1125,6 @@ void btDiscreteDynamicsWorld::predictUnconstraintMotion(btScalar timeStep) { //don't integrate/update velocities here, it happens in the constraint solver - //damping body->applyDamping(timeStep); body->predictIntegratedTransform(timeStep,body->getInterpolationWorldTransform()); diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h index fa934c49d..d8a34b7da 100644 --- a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h @@ -53,6 +53,7 @@ protected: //for variable timesteps btScalar m_localTime; + btScalar m_fixedTimeStep; //for variable timesteps bool m_ownsIslandManager; @@ -64,6 +65,8 @@ protected: int m_profileTimings; + bool m_latencyMotionStateInterpolation; + btAlignedObjectArray m_predictiveManifolds; virtual void predictUnconstraintMotion(btScalar timeStep); @@ -74,7 +77,7 @@ protected: virtual void solveConstraints(btContactSolverInfo& solverInfo); - void updateActivationState(btScalar timeStep); + virtual void updateActivationState(btScalar timeStep); void updateActions(btScalar timeStep); @@ -216,6 +219,16 @@ public: ///Preliminary serialization test for Bullet 2.76. Loading those files requires a separate parser (see Bullet/Demos/SerializeDemo) virtual void serialize(btSerializer* serializer); + ///Interpolate motion state between previous and current transform, instead of current and next transform. + ///This can relieve discontinuities in the rendering, due to penetrations + void setLatencyMotionStateInterpolation(bool latencyInterpolation ) + { + m_latencyMotionStateInterpolation = latencyInterpolation; + } + bool getLatencyMotionStateInterpolation() const + { + return m_latencyMotionStateInterpolation; + } }; #endif //BT_DISCRETE_DYNAMICS_WORLD_H diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDynamicsWorld.h b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDynamicsWorld.h index 7d5c621f8..35dd1400f 100644 --- a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDynamicsWorld.h +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btDynamicsWorld.h @@ -33,7 +33,8 @@ enum btDynamicsWorldType BT_SIMPLE_DYNAMICS_WORLD=1, BT_DISCRETE_DYNAMICS_WORLD=2, BT_CONTINUOUS_DYNAMICS_WORLD=3, - BT_SOFT_RIGID_DYNAMICS_WORLD=4 + BT_SOFT_RIGID_DYNAMICS_WORLD=4, + BT_GPU_DYNAMICS_WORLD=5 }; ///The btDynamicsWorld is the interface class for several dynamics implementation, basic, discrete, parallel, and continuous etc. diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h index c5bf63b4c..ed90fb441 100644 --- a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btRigidBody.h @@ -97,7 +97,7 @@ class btRigidBody : public btCollisionObject protected: - ATTRIBUTE_ALIGNED64(btVector3 m_deltaLinearVelocity); + ATTRIBUTE_ALIGNED16(btVector3 m_deltaLinearVelocity); btVector3 m_deltaAngularVelocity; btVector3 m_angularFactor; btVector3 m_invMass; @@ -363,11 +363,13 @@ public: inline void setLinearVelocity(const btVector3& lin_vel) { + m_updateRevision++; m_linearVelocity = lin_vel; } inline void setAngularVelocity(const btVector3& ang_vel) { + m_updateRevision++; m_angularVelocity = ang_vel; } @@ -484,11 +486,13 @@ public: void setAngularFactor(const btVector3& angFac) { + m_updateRevision++; m_angularFactor = angFac; } void setAngularFactor(btScalar angFac) { + m_updateRevision++; m_angularFactor.setValue(angFac,angFac,angFac); } const btVector3& getAngularFactor() const diff --git a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp index 5fc2f3cf8..35dd38840 100644 --- a/Engine/lib/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp +++ b/Engine/lib/bullet/src/BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp @@ -78,8 +78,8 @@ int btSimpleDynamicsWorld::stepSimulation( btScalar timeStep,int maxSubSteps, b btContactSolverInfo infoGlobal; infoGlobal.m_timeStep = timeStep; m_constraintSolver->prepareSolve(0,numManifolds); - m_constraintSolver->solveGroup(&getCollisionObjectArray()[0],getNumCollisionObjects(),manifoldPtr, numManifolds,0,0,infoGlobal,m_debugDrawer, m_stackAlloc,m_dispatcher1); - m_constraintSolver->allSolved(infoGlobal,m_debugDrawer, m_stackAlloc); + m_constraintSolver->solveGroup(&getCollisionObjectArray()[0],getNumCollisionObjects(),manifoldPtr, numManifolds,0,0,infoGlobal,m_debugDrawer, m_dispatcher1); + m_constraintSolver->allSolved(infoGlobal,m_debugDrawer); } ///integrate transforms diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.cpp b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.cpp new file mode 100644 index 000000000..56a1c55d9 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.cpp @@ -0,0 +1,1009 @@ +/* + * PURPOSE: + * Class representing an articulated rigid body. Stores the body's + * current state, allows forces and torques to be set, handles + * timestepping and implements Featherstone's algorithm. + * + * COPYRIGHT: + * Copyright (C) Stephen Thompson, , 2011-2013 + * Portions written By Erwin Coumans: replacing Eigen math library by Bullet LinearMath and a dedicated 6x6 matrix inverse (solveImatrix) + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the use of this software. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + */ + + +#include "btMultiBody.h" +#include "btMultiBodyLink.h" +#include "btMultiBodyLinkCollider.h" + +// #define INCLUDE_GYRO_TERM + +namespace { + const btScalar SLEEP_EPSILON = btScalar(0.05); // this is a squared velocity (m^2 s^-2) + const btScalar SLEEP_TIMEOUT = btScalar(2); // in seconds +} + + + + +// +// Various spatial helper functions +// + +namespace { + void SpatialTransform(const btMatrix3x3 &rotation_matrix, // rotates vectors in 'from' frame to vectors in 'to' frame + const btVector3 &displacement, // vector from origin of 'from' frame to origin of 'to' frame, in 'to' coordinates + const btVector3 &top_in, // top part of input vector + const btVector3 &bottom_in, // bottom part of input vector + btVector3 &top_out, // top part of output vector + btVector3 &bottom_out) // bottom part of output vector + { + top_out = rotation_matrix * top_in; + bottom_out = -displacement.cross(top_out) + rotation_matrix * bottom_in; + } + + void InverseSpatialTransform(const btMatrix3x3 &rotation_matrix, + const btVector3 &displacement, + const btVector3 &top_in, + const btVector3 &bottom_in, + btVector3 &top_out, + btVector3 &bottom_out) + { + top_out = rotation_matrix.transpose() * top_in; + bottom_out = rotation_matrix.transpose() * (bottom_in + displacement.cross(top_in)); + } + + btScalar SpatialDotProduct(const btVector3 &a_top, + const btVector3 &a_bottom, + const btVector3 &b_top, + const btVector3 &b_bottom) + { + return a_bottom.dot(b_top) + a_top.dot(b_bottom); + } +} + + +// +// Implementation of class btMultiBody +// + +btMultiBody::btMultiBody(int n_links, + btScalar mass, + const btVector3 &inertia, + bool fixed_base_, + bool can_sleep_) + : base_quat(0, 0, 0, 1), + base_mass(mass), + base_inertia(inertia), + + fixed_base(fixed_base_), + awake(true), + can_sleep(can_sleep_), + sleep_timer(0), + m_baseCollider(0), + m_linearDamping(0.04f), + m_angularDamping(0.04f), + m_useGyroTerm(true), + m_maxAppliedImpulse(1000.f), + m_hasSelfCollision(true) +{ + links.resize(n_links); + + vector_buf.resize(2*n_links); + matrix_buf.resize(n_links + 1); + m_real_buf.resize(6 + 2*n_links); + base_pos.setValue(0, 0, 0); + base_force.setValue(0, 0, 0); + base_torque.setValue(0, 0, 0); +} + +btMultiBody::~btMultiBody() +{ +} + +void btMultiBody::setupPrismatic(int i, + btScalar mass, + const btVector3 &inertia, + int parent, + const btQuaternion &rot_parent_to_this, + const btVector3 &joint_axis, + const btVector3 &r_vector_when_q_zero, + bool disableParentCollision) +{ + links[i].mass = mass; + links[i].inertia = inertia; + links[i].parent = parent; + links[i].zero_rot_parent_to_this = rot_parent_to_this; + links[i].axis_top.setValue(0,0,0); + links[i].axis_bottom = joint_axis; + links[i].e_vector = r_vector_when_q_zero; + links[i].is_revolute = false; + links[i].cached_rot_parent_to_this = rot_parent_to_this; + if (disableParentCollision) + links[i].m_flags |=BT_MULTIBODYLINKFLAGS_DISABLE_PARENT_COLLISION; + + links[i].updateCache(); +} + +void btMultiBody::setupRevolute(int i, + btScalar mass, + const btVector3 &inertia, + int parent, + const btQuaternion &zero_rot_parent_to_this, + const btVector3 &joint_axis, + const btVector3 &parent_axis_position, + const btVector3 &my_axis_position, + bool disableParentCollision) +{ + links[i].mass = mass; + links[i].inertia = inertia; + links[i].parent = parent; + links[i].zero_rot_parent_to_this = zero_rot_parent_to_this; + links[i].axis_top = joint_axis; + links[i].axis_bottom = joint_axis.cross(my_axis_position); + links[i].d_vector = my_axis_position; + links[i].e_vector = parent_axis_position; + links[i].is_revolute = true; + if (disableParentCollision) + links[i].m_flags |=BT_MULTIBODYLINKFLAGS_DISABLE_PARENT_COLLISION; + links[i].updateCache(); +} + + + + + +int btMultiBody::getParent(int i) const +{ + return links[i].parent; +} + +btScalar btMultiBody::getLinkMass(int i) const +{ + return links[i].mass; +} + +const btVector3 & btMultiBody::getLinkInertia(int i) const +{ + return links[i].inertia; +} + +btScalar btMultiBody::getJointPos(int i) const +{ + return links[i].joint_pos; +} + +btScalar btMultiBody::getJointVel(int i) const +{ + return m_real_buf[6 + i]; +} + +void btMultiBody::setJointPos(int i, btScalar q) +{ + links[i].joint_pos = q; + links[i].updateCache(); +} + +void btMultiBody::setJointVel(int i, btScalar qdot) +{ + m_real_buf[6 + i] = qdot; +} + +const btVector3 & btMultiBody::getRVector(int i) const +{ + return links[i].cached_r_vector; +} + +const btQuaternion & btMultiBody::getParentToLocalRot(int i) const +{ + return links[i].cached_rot_parent_to_this; +} + +btVector3 btMultiBody::localPosToWorld(int i, const btVector3 &local_pos) const +{ + btVector3 result = local_pos; + while (i != -1) { + // 'result' is in frame i. transform it to frame parent(i) + result += getRVector(i); + result = quatRotate(getParentToLocalRot(i).inverse(),result); + i = getParent(i); + } + + // 'result' is now in the base frame. transform it to world frame + result = quatRotate(getWorldToBaseRot().inverse() ,result); + result += getBasePos(); + + return result; +} + +btVector3 btMultiBody::worldPosToLocal(int i, const btVector3 &world_pos) const +{ + if (i == -1) { + // world to base + return quatRotate(getWorldToBaseRot(),(world_pos - getBasePos())); + } else { + // find position in parent frame, then transform to current frame + return quatRotate(getParentToLocalRot(i),worldPosToLocal(getParent(i), world_pos)) - getRVector(i); + } +} + +btVector3 btMultiBody::localDirToWorld(int i, const btVector3 &local_dir) const +{ + btVector3 result = local_dir; + while (i != -1) { + result = quatRotate(getParentToLocalRot(i).inverse() , result); + i = getParent(i); + } + result = quatRotate(getWorldToBaseRot().inverse() , result); + return result; +} + +btVector3 btMultiBody::worldDirToLocal(int i, const btVector3 &world_dir) const +{ + if (i == -1) { + return quatRotate(getWorldToBaseRot(), world_dir); + } else { + return quatRotate(getParentToLocalRot(i) ,worldDirToLocal(getParent(i), world_dir)); + } +} + +void btMultiBody::compTreeLinkVelocities(btVector3 *omega, btVector3 *vel) const +{ + int num_links = getNumLinks(); + // Calculates the velocities of each link (and the base) in its local frame + omega[0] = quatRotate(base_quat ,getBaseOmega()); + vel[0] = quatRotate(base_quat ,getBaseVel()); + + for (int i = 0; i < num_links; ++i) { + const int parent = links[i].parent; + + // transform parent vel into this frame, store in omega[i+1], vel[i+1] + SpatialTransform(btMatrix3x3(links[i].cached_rot_parent_to_this), links[i].cached_r_vector, + omega[parent+1], vel[parent+1], + omega[i+1], vel[i+1]); + + // now add qidot * shat_i + omega[i+1] += getJointVel(i) * links[i].axis_top; + vel[i+1] += getJointVel(i) * links[i].axis_bottom; + } +} + +btScalar btMultiBody::getKineticEnergy() const +{ + int num_links = getNumLinks(); + // TODO: would be better not to allocate memory here + btAlignedObjectArray omega;omega.resize(num_links+1); + btAlignedObjectArray vel;vel.resize(num_links+1); + compTreeLinkVelocities(&omega[0], &vel[0]); + + // we will do the factor of 0.5 at the end + btScalar result = base_mass * vel[0].dot(vel[0]); + result += omega[0].dot(base_inertia * omega[0]); + + for (int i = 0; i < num_links; ++i) { + result += links[i].mass * vel[i+1].dot(vel[i+1]); + result += omega[i+1].dot(links[i].inertia * omega[i+1]); + } + + return 0.5f * result; +} + +btVector3 btMultiBody::getAngularMomentum() const +{ + int num_links = getNumLinks(); + // TODO: would be better not to allocate memory here + btAlignedObjectArray omega;omega.resize(num_links+1); + btAlignedObjectArray vel;vel.resize(num_links+1); + btAlignedObjectArray rot_from_world;rot_from_world.resize(num_links+1); + compTreeLinkVelocities(&omega[0], &vel[0]); + + rot_from_world[0] = base_quat; + btVector3 result = quatRotate(rot_from_world[0].inverse() , (base_inertia * omega[0])); + + for (int i = 0; i < num_links; ++i) { + rot_from_world[i+1] = links[i].cached_rot_parent_to_this * rot_from_world[links[i].parent+1]; + result += (quatRotate(rot_from_world[i+1].inverse() , (links[i].inertia * omega[i+1]))); + } + + return result; +} + + +void btMultiBody::clearForcesAndTorques() +{ + base_force.setValue(0, 0, 0); + base_torque.setValue(0, 0, 0); + + for (int i = 0; i < getNumLinks(); ++i) { + links[i].applied_force.setValue(0, 0, 0); + links[i].applied_torque.setValue(0, 0, 0); + links[i].joint_torque = 0; + } +} + +void btMultiBody::clearVelocities() +{ + for (int i = 0; i < 6 + getNumLinks(); ++i) + { + m_real_buf[i] = 0.f; + } +} +void btMultiBody::addLinkForce(int i, const btVector3 &f) +{ + links[i].applied_force += f; +} + +void btMultiBody::addLinkTorque(int i, const btVector3 &t) +{ + links[i].applied_torque += t; +} + +void btMultiBody::addJointTorque(int i, btScalar Q) +{ + links[i].joint_torque += Q; +} + +const btVector3 & btMultiBody::getLinkForce(int i) const +{ + return links[i].applied_force; +} + +const btVector3 & btMultiBody::getLinkTorque(int i) const +{ + return links[i].applied_torque; +} + +btScalar btMultiBody::getJointTorque(int i) const +{ + return links[i].joint_torque; +} + + +inline btMatrix3x3 vecMulVecTranspose(const btVector3& v0, const btVector3& v1Transposed) +{ + btVector3 row0 = btVector3( + v0.x() * v1Transposed.x(), + v0.x() * v1Transposed.y(), + v0.x() * v1Transposed.z()); + btVector3 row1 = btVector3( + v0.y() * v1Transposed.x(), + v0.y() * v1Transposed.y(), + v0.y() * v1Transposed.z()); + btVector3 row2 = btVector3( + v0.z() * v1Transposed.x(), + v0.z() * v1Transposed.y(), + v0.z() * v1Transposed.z()); + + btMatrix3x3 m(row0[0],row0[1],row0[2], + row1[0],row1[1],row1[2], + row2[0],row2[1],row2[2]); + return m; +} + + +void btMultiBody::stepVelocities(btScalar dt, + btAlignedObjectArray &scratch_r, + btAlignedObjectArray &scratch_v, + btAlignedObjectArray &scratch_m) +{ + // Implement Featherstone's algorithm to calculate joint accelerations (q_double_dot) + // and the base linear & angular accelerations. + + // We apply damping forces in this routine as well as any external forces specified by the + // caller (via addBaseForce etc). + + // output should point to an array of 6 + num_links reals. + // Format is: 3 angular accelerations (in world frame), 3 linear accelerations (in world frame), + // num_links joint acceleration values. + + int num_links = getNumLinks(); + + const btScalar DAMPING_K1_LINEAR = m_linearDamping; + const btScalar DAMPING_K2_LINEAR = m_linearDamping; + + const btScalar DAMPING_K1_ANGULAR = m_angularDamping; + const btScalar DAMPING_K2_ANGULAR= m_angularDamping; + + btVector3 base_vel = getBaseVel(); + btVector3 base_omega = getBaseOmega(); + + // Temporary matrices/vectors -- use scratch space from caller + // so that we don't have to keep reallocating every frame + + scratch_r.resize(2*num_links + 6); + scratch_v.resize(8*num_links + 6); + scratch_m.resize(4*num_links + 4); + + btScalar * r_ptr = &scratch_r[0]; + btScalar * output = &scratch_r[num_links]; // "output" holds the q_double_dot results + btVector3 * v_ptr = &scratch_v[0]; + + // vhat_i (top = angular, bottom = linear part) + btVector3 * vel_top_angular = v_ptr; v_ptr += num_links + 1; + btVector3 * vel_bottom_linear = v_ptr; v_ptr += num_links + 1; + + // zhat_i^A + btVector3 * zero_acc_top_angular = v_ptr; v_ptr += num_links + 1; + btVector3 * zero_acc_bottom_linear = v_ptr; v_ptr += num_links + 1; + + // chat_i (note NOT defined for the base) + btVector3 * coriolis_top_angular = v_ptr; v_ptr += num_links; + btVector3 * coriolis_bottom_linear = v_ptr; v_ptr += num_links; + + // top left, top right and bottom left blocks of Ihat_i^A. + // bottom right block = transpose of top left block and is not stored. + // Note: the top right and bottom left blocks are always symmetric matrices, but we don't make use of this fact currently. + btMatrix3x3 * inertia_top_left = &scratch_m[num_links + 1]; + btMatrix3x3 * inertia_top_right = &scratch_m[2*num_links + 2]; + btMatrix3x3 * inertia_bottom_left = &scratch_m[3*num_links + 3]; + + // Cached 3x3 rotation matrices from parent frame to this frame. + btMatrix3x3 * rot_from_parent = &matrix_buf[0]; + btMatrix3x3 * rot_from_world = &scratch_m[0]; + + // hhat_i, ahat_i + // hhat is NOT stored for the base (but ahat is) + btVector3 * h_top = num_links > 0 ? &vector_buf[0] : 0; + btVector3 * h_bottom = num_links > 0 ? &vector_buf[num_links] : 0; + btVector3 * accel_top = v_ptr; v_ptr += num_links + 1; + btVector3 * accel_bottom = v_ptr; v_ptr += num_links + 1; + + // Y_i, D_i + btScalar * Y = r_ptr; r_ptr += num_links; + btScalar * D = num_links > 0 ? &m_real_buf[6 + num_links] : 0; + + // ptr to the joint accel part of the output + btScalar * joint_accel = output + 6; + + + // Start of the algorithm proper. + + // First 'upward' loop. + // Combines CompTreeLinkVelocities and InitTreeLinks from Mirtich. + + rot_from_parent[0] = btMatrix3x3(base_quat); + + vel_top_angular[0] = rot_from_parent[0] * base_omega; + vel_bottom_linear[0] = rot_from_parent[0] * base_vel; + + if (fixed_base) { + zero_acc_top_angular[0] = zero_acc_bottom_linear[0] = btVector3(0,0,0); + } else { + zero_acc_top_angular[0] = - (rot_from_parent[0] * (base_force + - base_mass*(DAMPING_K1_LINEAR+DAMPING_K2_LINEAR*base_vel.norm())*base_vel)); + + zero_acc_bottom_linear[0] = + - (rot_from_parent[0] * base_torque); + + if (m_useGyroTerm) + zero_acc_bottom_linear[0]+=vel_top_angular[0].cross( base_inertia * vel_top_angular[0] ); + + zero_acc_bottom_linear[0] += base_inertia * vel_top_angular[0] * (DAMPING_K1_ANGULAR + DAMPING_K2_ANGULAR*vel_top_angular[0].norm()); + + } + + + + inertia_top_left[0] = btMatrix3x3(0,0,0,0,0,0,0,0,0);//::Zero(); + + + inertia_top_right[0].setValue(base_mass, 0, 0, + 0, base_mass, 0, + 0, 0, base_mass); + inertia_bottom_left[0].setValue(base_inertia[0], 0, 0, + 0, base_inertia[1], 0, + 0, 0, base_inertia[2]); + + rot_from_world[0] = rot_from_parent[0]; + + for (int i = 0; i < num_links; ++i) { + const int parent = links[i].parent; + rot_from_parent[i+1] = btMatrix3x3(links[i].cached_rot_parent_to_this); + + + rot_from_world[i+1] = rot_from_parent[i+1] * rot_from_world[parent+1]; + + // vhat_i = i_xhat_p(i) * vhat_p(i) + SpatialTransform(rot_from_parent[i+1], links[i].cached_r_vector, + vel_top_angular[parent+1], vel_bottom_linear[parent+1], + vel_top_angular[i+1], vel_bottom_linear[i+1]); + + // we can now calculate chat_i + // remember vhat_i is really vhat_p(i) (but in current frame) at this point + coriolis_bottom_linear[i] = vel_top_angular[i+1].cross(vel_top_angular[i+1].cross(links[i].cached_r_vector)) + + 2 * vel_top_angular[i+1].cross(links[i].axis_bottom) * getJointVel(i); + if (links[i].is_revolute) { + coriolis_top_angular[i] = vel_top_angular[i+1].cross(links[i].axis_top) * getJointVel(i); + coriolis_bottom_linear[i] += (getJointVel(i) * getJointVel(i)) * links[i].axis_top.cross(links[i].axis_bottom); + } else { + coriolis_top_angular[i] = btVector3(0,0,0); + } + + // now set vhat_i to its true value by doing + // vhat_i += qidot * shat_i + vel_top_angular[i+1] += getJointVel(i) * links[i].axis_top; + vel_bottom_linear[i+1] += getJointVel(i) * links[i].axis_bottom; + + // calculate zhat_i^A + zero_acc_top_angular[i+1] = - (rot_from_world[i+1] * (links[i].applied_force)); + zero_acc_top_angular[i+1] += links[i].mass * (DAMPING_K1_LINEAR + DAMPING_K2_LINEAR*vel_bottom_linear[i+1].norm()) * vel_bottom_linear[i+1]; + + zero_acc_bottom_linear[i+1] = + - (rot_from_world[i+1] * links[i].applied_torque); + if (m_useGyroTerm) + { + zero_acc_bottom_linear[i+1] += vel_top_angular[i+1].cross( links[i].inertia * vel_top_angular[i+1] ); + } + + zero_acc_bottom_linear[i+1] += links[i].inertia * vel_top_angular[i+1] * (DAMPING_K1_ANGULAR + DAMPING_K2_ANGULAR*vel_top_angular[i+1].norm()); + + // calculate Ihat_i^A + inertia_top_left[i+1] = btMatrix3x3(0,0,0,0,0,0,0,0,0);//::Zero(); + inertia_top_right[i+1].setValue(links[i].mass, 0, 0, + 0, links[i].mass, 0, + 0, 0, links[i].mass); + inertia_bottom_left[i+1].setValue(links[i].inertia[0], 0, 0, + 0, links[i].inertia[1], 0, + 0, 0, links[i].inertia[2]); + } + + + // 'Downward' loop. + // (part of TreeForwardDynamics in Mirtich.) + for (int i = num_links - 1; i >= 0; --i) { + + h_top[i] = inertia_top_left[i+1] * links[i].axis_top + inertia_top_right[i+1] * links[i].axis_bottom; + h_bottom[i] = inertia_bottom_left[i+1] * links[i].axis_top + inertia_top_left[i+1].transpose() * links[i].axis_bottom; + btScalar val = SpatialDotProduct(links[i].axis_top, links[i].axis_bottom, h_top[i], h_bottom[i]); + D[i] = val; + Y[i] = links[i].joint_torque + - SpatialDotProduct(links[i].axis_top, links[i].axis_bottom, zero_acc_top_angular[i+1], zero_acc_bottom_linear[i+1]) + - SpatialDotProduct(h_top[i], h_bottom[i], coriolis_top_angular[i], coriolis_bottom_linear[i]); + + const int parent = links[i].parent; + + + // Ip += pXi * (Ii - hi hi' / Di) * iXp + const btScalar one_over_di = 1.0f / D[i]; + + + + + const btMatrix3x3 TL = inertia_top_left[i+1] - vecMulVecTranspose(one_over_di * h_top[i] , h_bottom[i]); + const btMatrix3x3 TR = inertia_top_right[i+1] - vecMulVecTranspose(one_over_di * h_top[i] , h_top[i]); + const btMatrix3x3 BL = inertia_bottom_left[i+1]- vecMulVecTranspose(one_over_di * h_bottom[i] , h_bottom[i]); + + + btMatrix3x3 r_cross; + r_cross.setValue( + 0, -links[i].cached_r_vector[2], links[i].cached_r_vector[1], + links[i].cached_r_vector[2], 0, -links[i].cached_r_vector[0], + -links[i].cached_r_vector[1], links[i].cached_r_vector[0], 0); + + inertia_top_left[parent+1] += rot_from_parent[i+1].transpose() * ( TL - TR * r_cross ) * rot_from_parent[i+1]; + inertia_top_right[parent+1] += rot_from_parent[i+1].transpose() * TR * rot_from_parent[i+1]; + inertia_bottom_left[parent+1] += rot_from_parent[i+1].transpose() * + (r_cross * (TL - TR * r_cross) + BL - TL.transpose() * r_cross) * rot_from_parent[i+1]; + + + // Zp += pXi * (Zi + Ii*ci + hi*Yi/Di) + btVector3 in_top, in_bottom, out_top, out_bottom; + const btScalar Y_over_D = Y[i] * one_over_di; + in_top = zero_acc_top_angular[i+1] + + inertia_top_left[i+1] * coriolis_top_angular[i] + + inertia_top_right[i+1] * coriolis_bottom_linear[i] + + Y_over_D * h_top[i]; + in_bottom = zero_acc_bottom_linear[i+1] + + inertia_bottom_left[i+1] * coriolis_top_angular[i] + + inertia_top_left[i+1].transpose() * coriolis_bottom_linear[i] + + Y_over_D * h_bottom[i]; + InverseSpatialTransform(rot_from_parent[i+1], links[i].cached_r_vector, + in_top, in_bottom, out_top, out_bottom); + zero_acc_top_angular[parent+1] += out_top; + zero_acc_bottom_linear[parent+1] += out_bottom; + } + + + // Second 'upward' loop + // (part of TreeForwardDynamics in Mirtich) + + if (fixed_base) + { + accel_top[0] = accel_bottom[0] = btVector3(0,0,0); + } + else + { + if (num_links > 0) + { + //Matrix Imatrix; + //Imatrix.block<3,3>(0,0) = inertia_top_left[0]; + //Imatrix.block<3,3>(3,0) = inertia_bottom_left[0]; + //Imatrix.block<3,3>(0,3) = inertia_top_right[0]; + //Imatrix.block<3,3>(3,3) = inertia_top_left[0].transpose(); + //cached_imatrix_lu.reset(new Eigen::LU >(Imatrix)); // TODO: Avoid memory allocation here? + + cached_inertia_top_left = inertia_top_left[0]; + cached_inertia_top_right = inertia_top_right[0]; + cached_inertia_lower_left = inertia_bottom_left[0]; + cached_inertia_lower_right= inertia_top_left[0].transpose(); + + } + btVector3 rhs_top (zero_acc_top_angular[0][0], zero_acc_top_angular[0][1], zero_acc_top_angular[0][2]); + btVector3 rhs_bot (zero_acc_bottom_linear[0][0], zero_acc_bottom_linear[0][1], zero_acc_bottom_linear[0][2]); + float result[6]; + + solveImatrix(rhs_top, rhs_bot, result); +// printf("result=%f,%f,%f,%f,%f,%f\n",result[0],result[0],result[0],result[0],result[0],result[0]); + for (int i = 0; i < 3; ++i) { + accel_top[0][i] = -result[i]; + accel_bottom[0][i] = -result[i+3]; + } + + } + + // now do the loop over the links + for (int i = 0; i < num_links; ++i) { + const int parent = links[i].parent; + SpatialTransform(rot_from_parent[i+1], links[i].cached_r_vector, + accel_top[parent+1], accel_bottom[parent+1], + accel_top[i+1], accel_bottom[i+1]); + joint_accel[i] = (Y[i] - SpatialDotProduct(h_top[i], h_bottom[i], accel_top[i+1], accel_bottom[i+1])) / D[i]; + accel_top[i+1] += coriolis_top_angular[i] + joint_accel[i] * links[i].axis_top; + accel_bottom[i+1] += coriolis_bottom_linear[i] + joint_accel[i] * links[i].axis_bottom; + } + + // transform base accelerations back to the world frame. + btVector3 omegadot_out = rot_from_parent[0].transpose() * accel_top[0]; + output[0] = omegadot_out[0]; + output[1] = omegadot_out[1]; + output[2] = omegadot_out[2]; + + btVector3 vdot_out = rot_from_parent[0].transpose() * accel_bottom[0]; + output[3] = vdot_out[0]; + output[4] = vdot_out[1]; + output[5] = vdot_out[2]; + // Final step: add the accelerations (times dt) to the velocities. + applyDeltaVee(output, dt); + + +} + + + +void btMultiBody::solveImatrix(const btVector3& rhs_top, const btVector3& rhs_bot, float result[6]) const +{ + int num_links = getNumLinks(); + ///solve I * x = rhs, so the result = invI * rhs + if (num_links == 0) + { + // in the case of 0 links (i.e. a plain rigid body, not a multibody) rhs * invI is easier + result[0] = rhs_bot[0] / base_inertia[0]; + result[1] = rhs_bot[1] / base_inertia[1]; + result[2] = rhs_bot[2] / base_inertia[2]; + result[3] = rhs_top[0] / base_mass; + result[4] = rhs_top[1] / base_mass; + result[5] = rhs_top[2] / base_mass; + } else + { + /// Special routine for calculating the inverse of a spatial inertia matrix + ///the 6x6 matrix is stored as 4 blocks of 3x3 matrices + btMatrix3x3 Binv = cached_inertia_top_right.inverse()*-1.f; + btMatrix3x3 tmp = cached_inertia_lower_right * Binv; + btMatrix3x3 invIupper_right = (tmp * cached_inertia_top_left + cached_inertia_lower_left).inverse(); + tmp = invIupper_right * cached_inertia_lower_right; + btMatrix3x3 invI_upper_left = (tmp * Binv); + btMatrix3x3 invI_lower_right = (invI_upper_left).transpose(); + tmp = cached_inertia_top_left * invI_upper_left; + tmp[0][0]-= 1.0; + tmp[1][1]-= 1.0; + tmp[2][2]-= 1.0; + btMatrix3x3 invI_lower_left = (Binv * tmp); + + //multiply result = invI * rhs + { + btVector3 vtop = invI_upper_left*rhs_top; + btVector3 tmp; + tmp = invIupper_right * rhs_bot; + vtop += tmp; + btVector3 vbot = invI_lower_left*rhs_top; + tmp = invI_lower_right * rhs_bot; + vbot += tmp; + result[0] = vtop[0]; + result[1] = vtop[1]; + result[2] = vtop[2]; + result[3] = vbot[0]; + result[4] = vbot[1]; + result[5] = vbot[2]; + } + + } +} + + +void btMultiBody::calcAccelerationDeltas(const btScalar *force, btScalar *output, + btAlignedObjectArray &scratch_r, btAlignedObjectArray &scratch_v) const +{ + // Temporary matrices/vectors -- use scratch space from caller + // so that we don't have to keep reallocating every frame + int num_links = getNumLinks(); + scratch_r.resize(num_links); + scratch_v.resize(4*num_links + 4); + + btScalar * r_ptr = num_links == 0 ? 0 : &scratch_r[0]; + btVector3 * v_ptr = &scratch_v[0]; + + // zhat_i^A (scratch space) + btVector3 * zero_acc_top_angular = v_ptr; v_ptr += num_links + 1; + btVector3 * zero_acc_bottom_linear = v_ptr; v_ptr += num_links + 1; + + // rot_from_parent (cached from calcAccelerations) + const btMatrix3x3 * rot_from_parent = &matrix_buf[0]; + + // hhat (cached), accel (scratch) + const btVector3 * h_top = num_links > 0 ? &vector_buf[0] : 0; + const btVector3 * h_bottom = num_links > 0 ? &vector_buf[num_links] : 0; + btVector3 * accel_top = v_ptr; v_ptr += num_links + 1; + btVector3 * accel_bottom = v_ptr; v_ptr += num_links + 1; + + // Y_i (scratch), D_i (cached) + btScalar * Y = r_ptr; r_ptr += num_links; + const btScalar * D = num_links > 0 ? &m_real_buf[6 + num_links] : 0; + + btAssert(num_links == 0 || r_ptr - &scratch_r[0] == scratch_r.size()); + btAssert(v_ptr - &scratch_v[0] == scratch_v.size()); + + + + // First 'upward' loop. + // Combines CompTreeLinkVelocities and InitTreeLinks from Mirtich. + + btVector3 input_force(force[3],force[4],force[5]); + btVector3 input_torque(force[0],force[1],force[2]); + + // Fill in zero_acc + // -- set to force/torque on the base, zero otherwise + if (fixed_base) + { + zero_acc_top_angular[0] = zero_acc_bottom_linear[0] = btVector3(0,0,0); + } else + { + zero_acc_top_angular[0] = - (rot_from_parent[0] * input_force); + zero_acc_bottom_linear[0] = - (rot_from_parent[0] * input_torque); + } + for (int i = 0; i < num_links; ++i) + { + zero_acc_top_angular[i+1] = zero_acc_bottom_linear[i+1] = btVector3(0,0,0); + } + + // 'Downward' loop. + for (int i = num_links - 1; i >= 0; --i) + { + + Y[i] = - SpatialDotProduct(links[i].axis_top, links[i].axis_bottom, zero_acc_top_angular[i+1], zero_acc_bottom_linear[i+1]); + Y[i] += force[6 + i]; // add joint torque + + const int parent = links[i].parent; + + // Zp += pXi * (Zi + hi*Yi/Di) + btVector3 in_top, in_bottom, out_top, out_bottom; + const btScalar Y_over_D = Y[i] / D[i]; + in_top = zero_acc_top_angular[i+1] + Y_over_D * h_top[i]; + in_bottom = zero_acc_bottom_linear[i+1] + Y_over_D * h_bottom[i]; + InverseSpatialTransform(rot_from_parent[i+1], links[i].cached_r_vector, + in_top, in_bottom, out_top, out_bottom); + zero_acc_top_angular[parent+1] += out_top; + zero_acc_bottom_linear[parent+1] += out_bottom; + } + + // ptr to the joint accel part of the output + btScalar * joint_accel = output + 6; + + // Second 'upward' loop + if (fixed_base) + { + accel_top[0] = accel_bottom[0] = btVector3(0,0,0); + } else + { + btVector3 rhs_top (zero_acc_top_angular[0][0], zero_acc_top_angular[0][1], zero_acc_top_angular[0][2]); + btVector3 rhs_bot (zero_acc_bottom_linear[0][0], zero_acc_bottom_linear[0][1], zero_acc_bottom_linear[0][2]); + + float result[6]; + solveImatrix(rhs_top,rhs_bot, result); + // printf("result=%f,%f,%f,%f,%f,%f\n",result[0],result[0],result[0],result[0],result[0],result[0]); + + for (int i = 0; i < 3; ++i) { + accel_top[0][i] = -result[i]; + accel_bottom[0][i] = -result[i+3]; + } + + } + + // now do the loop over the links + for (int i = 0; i < num_links; ++i) { + const int parent = links[i].parent; + SpatialTransform(rot_from_parent[i+1], links[i].cached_r_vector, + accel_top[parent+1], accel_bottom[parent+1], + accel_top[i+1], accel_bottom[i+1]); + joint_accel[i] = (Y[i] - SpatialDotProduct(h_top[i], h_bottom[i], accel_top[i+1], accel_bottom[i+1])) / D[i]; + accel_top[i+1] += joint_accel[i] * links[i].axis_top; + accel_bottom[i+1] += joint_accel[i] * links[i].axis_bottom; + } + + // transform base accelerations back to the world frame. + btVector3 omegadot_out; + omegadot_out = rot_from_parent[0].transpose() * accel_top[0]; + output[0] = omegadot_out[0]; + output[1] = omegadot_out[1]; + output[2] = omegadot_out[2]; + + btVector3 vdot_out; + vdot_out = rot_from_parent[0].transpose() * accel_bottom[0]; + + output[3] = vdot_out[0]; + output[4] = vdot_out[1]; + output[5] = vdot_out[2]; +} + +void btMultiBody::stepPositions(btScalar dt) +{ + int num_links = getNumLinks(); + // step position by adding dt * velocity + btVector3 v = getBaseVel(); + base_pos += dt * v; + + // "exponential map" method for the rotation + btVector3 base_omega = getBaseOmega(); + const btScalar omega_norm = base_omega.norm(); + const btScalar omega_times_dt = omega_norm * dt; + const btScalar SMALL_ROTATION_ANGLE = 0.02f; // Theoretically this should be ~ pow(FLT_EPSILON,0.25) which is ~ 0.0156 + if (fabs(omega_times_dt) < SMALL_ROTATION_ANGLE) + { + const btScalar xsq = omega_times_dt * omega_times_dt; // |omega|^2 * dt^2 + const btScalar sin_term = dt * (xsq / 48.0f - 0.5f); // -sin(0.5*dt*|omega|) / |omega| + const btScalar cos_term = 1.0f - xsq / 8.0f; // cos(0.5*dt*|omega|) + base_quat = base_quat * btQuaternion(sin_term * base_omega[0],sin_term * base_omega[1],sin_term * base_omega[2],cos_term); + } else + { + base_quat = base_quat * btQuaternion(base_omega / omega_norm,-omega_times_dt); + } + + // Make sure the quaternion represents a valid rotation. + // (Not strictly necessary, but helps prevent any round-off errors from building up.) + base_quat.normalize(); + + // Finally we can update joint_pos for each of the links + for (int i = 0; i < num_links; ++i) + { + float jointVel = getJointVel(i); + links[i].joint_pos += dt * jointVel; + links[i].updateCache(); + } +} + +void btMultiBody::fillContactJacobian(int link, + const btVector3 &contact_point, + const btVector3 &normal, + btScalar *jac, + btAlignedObjectArray &scratch_r, + btAlignedObjectArray &scratch_v, + btAlignedObjectArray &scratch_m) const +{ + // temporary space + int num_links = getNumLinks(); + scratch_v.resize(2*num_links + 2); + scratch_m.resize(num_links + 1); + + btVector3 * v_ptr = &scratch_v[0]; + btVector3 * p_minus_com = v_ptr; v_ptr += num_links + 1; + btVector3 * n_local = v_ptr; v_ptr += num_links + 1; + btAssert(v_ptr - &scratch_v[0] == scratch_v.size()); + + scratch_r.resize(num_links); + btScalar * results = num_links > 0 ? &scratch_r[0] : 0; + + btMatrix3x3 * rot_from_world = &scratch_m[0]; + + const btVector3 p_minus_com_world = contact_point - base_pos; + + rot_from_world[0] = btMatrix3x3(base_quat); + + p_minus_com[0] = rot_from_world[0] * p_minus_com_world; + n_local[0] = rot_from_world[0] * normal; + + // omega coeffients first. + btVector3 omega_coeffs; + omega_coeffs = p_minus_com_world.cross(normal); + jac[0] = omega_coeffs[0]; + jac[1] = omega_coeffs[1]; + jac[2] = omega_coeffs[2]; + // then v coefficients + jac[3] = normal[0]; + jac[4] = normal[1]; + jac[5] = normal[2]; + + // Set remaining jac values to zero for now. + for (int i = 6; i < 6 + num_links; ++i) { + jac[i] = 0; + } + + // Qdot coefficients, if necessary. + if (num_links > 0 && link > -1) { + + // TODO: speed this up -- don't calculate for links we don't need. + // (Also, we are making 3 separate calls to this function, for the normal & the 2 friction directions, + // which is resulting in repeated work being done...) + + // calculate required normals & positions in the local frames. + for (int i = 0; i < num_links; ++i) { + + // transform to local frame + const int parent = links[i].parent; + const btMatrix3x3 mtx(links[i].cached_rot_parent_to_this); + rot_from_world[i+1] = mtx * rot_from_world[parent+1]; + + n_local[i+1] = mtx * n_local[parent+1]; + p_minus_com[i+1] = mtx * p_minus_com[parent+1] - links[i].cached_r_vector; + + // calculate the jacobian entry + if (links[i].is_revolute) { + results[i] = n_local[i+1].dot( links[i].axis_top.cross(p_minus_com[i+1]) + links[i].axis_bottom ); + } else { + results[i] = n_local[i+1].dot( links[i].axis_bottom ); + } + } + + // Now copy through to output. + while (link != -1) { + jac[6 + link] = results[link]; + link = links[link].parent; + } + } +} + +void btMultiBody::wakeUp() +{ + awake = true; +} + +void btMultiBody::goToSleep() +{ + awake = false; +} + +void btMultiBody::checkMotionAndSleepIfRequired(btScalar timestep) +{ + int num_links = getNumLinks(); + extern bool gDisableDeactivation; + if (!can_sleep || gDisableDeactivation) + { + awake = true; + sleep_timer = 0; + return; + } + + // motion is computed as omega^2 + v^2 + (sum of squares of joint velocities) + btScalar motion = 0; + for (int i = 0; i < 6 + num_links; ++i) { + motion += m_real_buf[i] * m_real_buf[i]; + } + + if (motion < SLEEP_EPSILON) { + sleep_timer += timestep; + if (sleep_timer > SLEEP_TIMEOUT) { + goToSleep(); + } + } else { + sleep_timer = 0; + if (!awake) + wakeUp(); + } +} diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.h b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.h new file mode 100644 index 000000000..7177bebbf --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.h @@ -0,0 +1,466 @@ +/* + * PURPOSE: + * Class representing an articulated rigid body. Stores the body's + * current state, allows forces and torques to be set, handles + * timestepping and implements Featherstone's algorithm. + * + * COPYRIGHT: + * Copyright (C) Stephen Thompson, , 2011-2013 + * Portions written By Erwin Coumans: replacing Eigen math library by Bullet LinearMath and a dedicated 6x6 matrix inverse (solveImatrix) + + This software is provided 'as-is', without any express or implied warranty. + In no event will the authors be held liable for any damages arising from the use of this software. + Permission is granted to anyone to use this software for any purpose, + including commercial applications, and to alter it and redistribute it freely, + subject to the following restrictions: + + 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. + 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. + 3. This notice may not be removed or altered from any source distribution. + + */ + + +#ifndef BT_MULTIBODY_H +#define BT_MULTIBODY_H + +#include "LinearMath/btScalar.h" +#include "LinearMath/btVector3.h" +#include "LinearMath/btQuaternion.h" +#include "LinearMath/btMatrix3x3.h" +#include "LinearMath/btAlignedObjectArray.h" + + +#include "btMultiBodyLink.h" +class btMultiBodyLinkCollider; + +class btMultiBody +{ +public: + + + BT_DECLARE_ALIGNED_ALLOCATOR(); + + // + // initialization + // + + btMultiBody(int n_links, // NOT including the base + btScalar mass, // mass of base + const btVector3 &inertia, // inertia of base, in base frame; assumed diagonal + bool fixed_base_, // whether the base is fixed (true) or can move (false) + bool can_sleep_); + + ~btMultiBody(); + + void setupPrismatic(int i, // 0 to num_links-1 + btScalar mass, + const btVector3 &inertia, // in my frame; assumed diagonal + int parent, + const btQuaternion &rot_parent_to_this, // rotate points in parent frame to my frame. + const btVector3 &joint_axis, // in my frame + const btVector3 &r_vector_when_q_zero, // vector from parent COM to my COM, in my frame, when q = 0. + bool disableParentCollision=false + ); + + void setupRevolute(int i, // 0 to num_links-1 + btScalar mass, + const btVector3 &inertia, + int parent, + const btQuaternion &zero_rot_parent_to_this, // rotate points in parent frame to this frame, when q = 0 + const btVector3 &joint_axis, // in my frame + const btVector3 &parent_axis_position, // vector from parent COM to joint axis, in PARENT frame + const btVector3 &my_axis_position, // vector from joint axis to my COM, in MY frame + bool disableParentCollision=false); + + const btMultibodyLink& getLink(int index) const + { + return links[index]; + } + + btMultibodyLink& getLink(int index) + { + return links[index]; + } + + + void setBaseCollider(btMultiBodyLinkCollider* collider)//collider can be NULL to disable collision for the base + { + m_baseCollider = collider; + } + const btMultiBodyLinkCollider* getBaseCollider() const + { + return m_baseCollider; + } + btMultiBodyLinkCollider* getBaseCollider() + { + return m_baseCollider; + } + + // + // get parent + // input: link num from 0 to num_links-1 + // output: link num from 0 to num_links-1, OR -1 to mean the base. + // + int getParent(int link_num) const; + + + // + // get number of links, masses, moments of inertia + // + + int getNumLinks() const { return links.size(); } + btScalar getBaseMass() const { return base_mass; } + const btVector3 & getBaseInertia() const { return base_inertia; } + btScalar getLinkMass(int i) const; + const btVector3 & getLinkInertia(int i) const; + + + // + // change mass (incomplete: can only change base mass and inertia at present) + // + + void setBaseMass(btScalar mass) { base_mass = mass; } + void setBaseInertia(const btVector3 &inertia) { base_inertia = inertia; } + + + // + // get/set pos/vel/rot/omega for the base link + // + + const btVector3 & getBasePos() const { return base_pos; } // in world frame + const btVector3 getBaseVel() const + { + return btVector3(m_real_buf[3],m_real_buf[4],m_real_buf[5]); + } // in world frame + const btQuaternion & getWorldToBaseRot() const + { + return base_quat; + } // rotates world vectors into base frame + btVector3 getBaseOmega() const { return btVector3(m_real_buf[0],m_real_buf[1],m_real_buf[2]); } // in world frame + + void setBasePos(const btVector3 &pos) + { + base_pos = pos; + } + void setBaseVel(const btVector3 &vel) + { + + m_real_buf[3]=vel[0]; m_real_buf[4]=vel[1]; m_real_buf[5]=vel[2]; + } + void setWorldToBaseRot(const btQuaternion &rot) + { + base_quat = rot; + } + void setBaseOmega(const btVector3 &omega) + { + m_real_buf[0]=omega[0]; + m_real_buf[1]=omega[1]; + m_real_buf[2]=omega[2]; + } + + + // + // get/set pos/vel for child links (i = 0 to num_links-1) + // + + btScalar getJointPos(int i) const; + btScalar getJointVel(int i) const; + + void setJointPos(int i, btScalar q); + void setJointVel(int i, btScalar qdot); + + // + // direct access to velocities as a vector of 6 + num_links elements. + // (omega first, then v, then joint velocities.) + // + const btScalar * getVelocityVector() const + { + return &m_real_buf[0]; + } +/* btScalar * getVelocityVector() + { + return &real_buf[0]; + } + */ + + // + // get the frames of reference (positions and orientations) of the child links + // (i = 0 to num_links-1) + // + + const btVector3 & getRVector(int i) const; // vector from COM(parent(i)) to COM(i), in frame i's coords + const btQuaternion & getParentToLocalRot(int i) const; // rotates vectors in frame parent(i) to vectors in frame i. + + + // + // transform vectors in local frame of link i to world frame (or vice versa) + // + btVector3 localPosToWorld(int i, const btVector3 &vec) const; + btVector3 localDirToWorld(int i, const btVector3 &vec) const; + btVector3 worldPosToLocal(int i, const btVector3 &vec) const; + btVector3 worldDirToLocal(int i, const btVector3 &vec) const; + + + // + // calculate kinetic energy and angular momentum + // useful for debugging. + // + + btScalar getKineticEnergy() const; + btVector3 getAngularMomentum() const; + + + // + // set external forces and torques. Note all external forces/torques are given in the WORLD frame. + // + + void clearForcesAndTorques(); + void clearVelocities(); + + void addBaseForce(const btVector3 &f) + { + base_force += f; + } + void addBaseTorque(const btVector3 &t) { base_torque += t; } + void addLinkForce(int i, const btVector3 &f); + void addLinkTorque(int i, const btVector3 &t); + void addJointTorque(int i, btScalar Q); + + const btVector3 & getBaseForce() const { return base_force; } + const btVector3 & getBaseTorque() const { return base_torque; } + const btVector3 & getLinkForce(int i) const; + const btVector3 & getLinkTorque(int i) const; + btScalar getJointTorque(int i) const; + + + // + // dynamics routines. + // + + // timestep the velocities (given the external forces/torques set using addBaseForce etc). + // also sets up caches for calcAccelerationDeltas. + // + // Note: the caller must provide three vectors which are used as + // temporary scratch space. The idea here is to reduce dynamic + // memory allocation: the same scratch vectors can be re-used + // again and again for different Multibodies, instead of each + // btMultiBody allocating (and then deallocating) their own + // individual scratch buffers. This gives a considerable speed + // improvement, at least on Windows (where dynamic memory + // allocation appears to be fairly slow). + // + void stepVelocities(btScalar dt, + btAlignedObjectArray &scratch_r, + btAlignedObjectArray &scratch_v, + btAlignedObjectArray &scratch_m); + + // calcAccelerationDeltas + // input: force vector (in same format as jacobian, i.e.: + // 3 torque values, 3 force values, num_links joint torque values) + // output: 3 omegadot values, 3 vdot values, num_links q_double_dot values + // (existing contents of output array are replaced) + // stepVelocities must have been called first. + void calcAccelerationDeltas(const btScalar *force, btScalar *output, + btAlignedObjectArray &scratch_r, + btAlignedObjectArray &scratch_v) const; + + // apply a delta-vee directly. used in sequential impulses code. + void applyDeltaVee(const btScalar * delta_vee) + { + + for (int i = 0; i < 6 + getNumLinks(); ++i) + { + m_real_buf[i] += delta_vee[i]; + } + + } + void applyDeltaVee(const btScalar * delta_vee, btScalar multiplier) + { + btScalar sum = 0; + for (int i = 0; i < 6 + getNumLinks(); ++i) + { + sum += delta_vee[i]*multiplier*delta_vee[i]*multiplier; + } + btScalar l = btSqrt(sum); + /* + static btScalar maxl = -1e30f; + if (l>maxl) + { + maxl=l; + // printf("maxl=%f\n",maxl); + } + */ + if (l>m_maxAppliedImpulse) + { +// printf("exceeds 100: l=%f\n",maxl); + multiplier *= m_maxAppliedImpulse/l; + } + + for (int i = 0; i < 6 + getNumLinks(); ++i) + { + sum += delta_vee[i]*multiplier*delta_vee[i]*multiplier; + m_real_buf[i] += delta_vee[i] * multiplier; + } + } + + // timestep the positions (given current velocities). + void stepPositions(btScalar dt); + + + // + // contacts + // + + // This routine fills out a contact constraint jacobian for this body. + // the 'normal' supplied must be -n for body1 or +n for body2 of the contact. + // 'normal' & 'contact_point' are both given in world coordinates. + void fillContactJacobian(int link, + const btVector3 &contact_point, + const btVector3 &normal, + btScalar *jac, + btAlignedObjectArray &scratch_r, + btAlignedObjectArray &scratch_v, + btAlignedObjectArray &scratch_m) const; + + + // + // sleeping + // + void setCanSleep(bool canSleep) + { + can_sleep = canSleep; + } + + bool isAwake() const { return awake; } + void wakeUp(); + void goToSleep(); + void checkMotionAndSleepIfRequired(btScalar timestep); + + bool hasFixedBase() const + { + return fixed_base; + } + + int getCompanionId() const + { + return m_companionId; + } + void setCompanionId(int id) + { + //printf("for %p setCompanionId(%d)\n",this, id); + m_companionId = id; + } + + void setNumLinks(int numLinks)//careful: when changing the number of links, make sure to re-initialize or update existing links + { + links.resize(numLinks); + } + + btScalar getLinearDamping() const + { + return m_linearDamping; + } + void setLinearDamping( btScalar damp) + { + m_linearDamping = damp; + } + btScalar getAngularDamping() const + { + return m_angularDamping; + } + + bool getUseGyroTerm() const + { + return m_useGyroTerm; + } + void setUseGyroTerm(bool useGyro) + { + m_useGyroTerm = useGyro; + } + btScalar getMaxAppliedImpulse() const + { + return m_maxAppliedImpulse; + } + void setMaxAppliedImpulse(btScalar maxImp) + { + m_maxAppliedImpulse = maxImp; + } + + void setHasSelfCollision(bool hasSelfCollision) + { + m_hasSelfCollision = hasSelfCollision; + } + bool hasSelfCollision() const + { + return m_hasSelfCollision; + } + +private: + btMultiBody(const btMultiBody &); // not implemented + void operator=(const btMultiBody &); // not implemented + + void compTreeLinkVelocities(btVector3 *omega, btVector3 *vel) const; + + void solveImatrix(const btVector3& rhs_top, const btVector3& rhs_bot, float result[6]) const; + + +private: + + btMultiBodyLinkCollider* m_baseCollider;//can be NULL + + btVector3 base_pos; // position of COM of base (world frame) + btQuaternion base_quat; // rotates world points into base frame + + btScalar base_mass; // mass of the base + btVector3 base_inertia; // inertia of the base (in local frame; diagonal) + + btVector3 base_force; // external force applied to base. World frame. + btVector3 base_torque; // external torque applied to base. World frame. + + btAlignedObjectArray links; // array of links, excluding the base. index from 0 to num_links-1. + btAlignedObjectArray m_colliders; + + // + // real_buf: + // offset size array + // 0 6 + num_links v (base_omega; base_vel; joint_vels) + // 6+num_links num_links D + // + // vector_buf: + // offset size array + // 0 num_links h_top + // num_links num_links h_bottom + // + // matrix_buf: + // offset size array + // 0 num_links+1 rot_from_parent + // + + btAlignedObjectArray m_real_buf; + btAlignedObjectArray vector_buf; + btAlignedObjectArray matrix_buf; + + //std::auto_ptr > > cached_imatrix_lu; + + btMatrix3x3 cached_inertia_top_left; + btMatrix3x3 cached_inertia_top_right; + btMatrix3x3 cached_inertia_lower_left; + btMatrix3x3 cached_inertia_lower_right; + + bool fixed_base; + + // Sleep parameters. + bool awake; + bool can_sleep; + btScalar sleep_timer; + + int m_companionId; + btScalar m_linearDamping; + btScalar m_angularDamping; + bool m_useGyroTerm; + btScalar m_maxAppliedImpulse; + bool m_hasSelfCollision; +}; + +#endif diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp new file mode 100644 index 000000000..44e04c3a1 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp @@ -0,0 +1,527 @@ +#include "btMultiBodyConstraint.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" + +btMultiBodyConstraint::btMultiBodyConstraint(btMultiBody* bodyA,btMultiBody* bodyB,int linkA, int linkB, int numRows, bool isUnilateral) + :m_bodyA(bodyA), + m_bodyB(bodyB), + m_linkA(linkA), + m_linkB(linkB), + m_num_rows(numRows), + m_isUnilateral(isUnilateral), + m_maxAppliedImpulse(100) +{ + m_jac_size_A = (6 + bodyA->getNumLinks()); + m_jac_size_both = (m_jac_size_A + (bodyB ? 6 + bodyB->getNumLinks() : 0)); + m_pos_offset = ((1 + m_jac_size_both)*m_num_rows); + m_data.resize((2 + m_jac_size_both) * m_num_rows); +} + +btMultiBodyConstraint::~btMultiBodyConstraint() +{ +} + + + +btScalar btMultiBodyConstraint::fillConstraintRowMultiBodyMultiBody(btMultiBodySolverConstraint& constraintRow, + btMultiBodyJacobianData& data, + btScalar* jacOrgA,btScalar* jacOrgB, + const btContactSolverInfo& infoGlobal, + btScalar desiredVelocity, + btScalar lowerLimit, + btScalar upperLimit) +{ + + + + constraintRow.m_multiBodyA = m_bodyA; + constraintRow.m_multiBodyB = m_bodyB; + + btMultiBody* multiBodyA = constraintRow.m_multiBodyA; + btMultiBody* multiBodyB = constraintRow.m_multiBodyB; + + if (multiBodyA) + { + + const int ndofA = multiBodyA->getNumLinks() + 6; + + constraintRow.m_deltaVelAindex = multiBodyA->getCompanionId(); + + if (constraintRow.m_deltaVelAindex <0) + { + constraintRow.m_deltaVelAindex = data.m_deltaVelocities.size(); + multiBodyA->setCompanionId(constraintRow.m_deltaVelAindex); + data.m_deltaVelocities.resize(data.m_deltaVelocities.size()+ndofA); + } else + { + btAssert(data.m_deltaVelocities.size() >= constraintRow.m_deltaVelAindex+ndofA); + } + + constraintRow.m_jacAindex = data.m_jacobians.size(); + data.m_jacobians.resize(data.m_jacobians.size()+ndofA); + data.m_deltaVelocitiesUnitImpulse.resize(data.m_deltaVelocitiesUnitImpulse.size()+ndofA); + btAssert(data.m_jacobians.size() == data.m_deltaVelocitiesUnitImpulse.size()); + for (int i=0;icalcAccelerationDeltas(&data.m_jacobians[constraintRow.m_jacAindex],delta,data.scratch_r, data.scratch_v); + } + + if (multiBodyB) + { + const int ndofB = multiBodyB->getNumLinks() + 6; + + constraintRow.m_deltaVelBindex = multiBodyB->getCompanionId(); + if (constraintRow.m_deltaVelBindex <0) + { + constraintRow.m_deltaVelBindex = data.m_deltaVelocities.size(); + multiBodyB->setCompanionId(constraintRow.m_deltaVelBindex); + data.m_deltaVelocities.resize(data.m_deltaVelocities.size()+ndofB); + } + + constraintRow.m_jacBindex = data.m_jacobians.size(); + data.m_jacobians.resize(data.m_jacobians.size()+ndofB); + + for (int i=0;icalcAccelerationDeltas(&data.m_jacobians[constraintRow.m_jacBindex],&data.m_deltaVelocitiesUnitImpulse[constraintRow.m_jacBindex],data.scratch_r, data.scratch_v); + } + { + + btVector3 vec; + btScalar denom0 = 0.f; + btScalar denom1 = 0.f; + btScalar* jacB = 0; + btScalar* jacA = 0; + btScalar* lambdaA =0; + btScalar* lambdaB =0; + int ndofA = 0; + if (multiBodyA) + { + ndofA = multiBodyA->getNumLinks() + 6; + jacA = &data.m_jacobians[constraintRow.m_jacAindex]; + lambdaA = &data.m_deltaVelocitiesUnitImpulse[constraintRow.m_jacAindex]; + for (int i = 0; i < ndofA; ++i) + { + btScalar j = jacA[i] ; + btScalar l =lambdaA[i]; + denom0 += j*l; + } + } + if (multiBodyB) + { + const int ndofB = multiBodyB->getNumLinks() + 6; + jacB = &data.m_jacobians[constraintRow.m_jacBindex]; + lambdaB = &data.m_deltaVelocitiesUnitImpulse[constraintRow.m_jacBindex]; + for (int i = 0; i < ndofB; ++i) + { + btScalar j = jacB[i] ; + btScalar l =lambdaB[i]; + denom1 += j*l; + } + + } + + if (multiBodyA && (multiBodyA==multiBodyB)) + { + // ndof1 == ndof2 in this case + for (int i = 0; i < ndofA; ++i) + { + denom1 += jacB[i] * lambdaA[i]; + denom1 += jacA[i] * lambdaB[i]; + } + } + + btScalar d = denom0+denom1; + if (btFabs(d)>SIMD_EPSILON) + { + + constraintRow.m_jacDiagABInv = 1.f/(d); + } else + { + constraintRow.m_jacDiagABInv = 1.f; + } + + } + + + //compute rhs and remaining constraintRow fields + + + + + btScalar rel_vel = 0.f; + int ndofA = 0; + int ndofB = 0; + { + + btVector3 vel1,vel2; + if (multiBodyA) + { + ndofA = multiBodyA->getNumLinks() + 6; + btScalar* jacA = &data.m_jacobians[constraintRow.m_jacAindex]; + for (int i = 0; i < ndofA ; ++i) + rel_vel += multiBodyA->getVelocityVector()[i] * jacA[i]; + } + if (multiBodyB) + { + ndofB = multiBodyB->getNumLinks() + 6; + btScalar* jacB = &data.m_jacobians[constraintRow.m_jacBindex]; + for (int i = 0; i < ndofB ; ++i) + rel_vel += multiBodyB->getVelocityVector()[i] * jacB[i]; + + } + + constraintRow.m_friction = 0.f; + + constraintRow.m_appliedImpulse = 0.f; + constraintRow.m_appliedPushImpulse = 0.f; + + btScalar velocityError = desiredVelocity - rel_vel;// * damping; + + btScalar erp = infoGlobal.m_erp2; + + btScalar velocityImpulse = velocityError *constraintRow.m_jacDiagABInv; + + if (!infoGlobal.m_splitImpulse) + { + //combine position and velocity into rhs + constraintRow.m_rhs = velocityImpulse; + constraintRow.m_rhsPenetration = 0.f; + + } else + { + //split position and velocity into rhs and m_rhsPenetration + constraintRow.m_rhs = velocityImpulse; + constraintRow.m_rhsPenetration = 0.f; + } + + + constraintRow.m_cfm = 0.f; + constraintRow.m_lowerLimit = lowerLimit; + constraintRow.m_upperLimit = upperLimit; + + } + return rel_vel; +} + + +void btMultiBodyConstraint::applyDeltaVee(btMultiBodyJacobianData& data, btScalar* delta_vee, btScalar impulse, int velocityIndex, int ndof) +{ + for (int i = 0; i < ndof; ++i) + data.m_deltaVelocities[velocityIndex+i] += delta_vee[i] * impulse; +} + + +void btMultiBodyConstraint::fillMultiBodyConstraintMixed(btMultiBodySolverConstraint& solverConstraint, + btMultiBodyJacobianData& data, + const btVector3& contactNormalOnB, + const btVector3& posAworld, const btVector3& posBworld, + btScalar position, + const btContactSolverInfo& infoGlobal, + btScalar& relaxation, + bool isFriction, btScalar desiredVelocity, btScalar cfmSlip) +{ + + + btVector3 rel_pos1 = posAworld; + btVector3 rel_pos2 = posBworld; + + solverConstraint.m_multiBodyA = m_bodyA; + solverConstraint.m_multiBodyB = m_bodyB; + solverConstraint.m_linkA = m_linkA; + solverConstraint.m_linkB = m_linkB; + + + btMultiBody* multiBodyA = solverConstraint.m_multiBodyA; + btMultiBody* multiBodyB = solverConstraint.m_multiBodyB; + + const btVector3& pos1 = posAworld; + const btVector3& pos2 = posBworld; + + btSolverBody* bodyA = multiBodyA ? 0 : &data.m_solverBodyPool->at(solverConstraint.m_solverBodyIdA); + btSolverBody* bodyB = multiBodyB ? 0 : &data.m_solverBodyPool->at(solverConstraint.m_solverBodyIdB); + + btRigidBody* rb0 = multiBodyA ? 0 : bodyA->m_originalBody; + btRigidBody* rb1 = multiBodyB ? 0 : bodyB->m_originalBody; + + if (bodyA) + rel_pos1 = pos1 - bodyA->getWorldTransform().getOrigin(); + if (bodyB) + rel_pos2 = pos2 - bodyB->getWorldTransform().getOrigin(); + + relaxation = 1.f; + + if (multiBodyA) + { + const int ndofA = multiBodyA->getNumLinks() + 6; + + solverConstraint.m_deltaVelAindex = multiBodyA->getCompanionId(); + + if (solverConstraint.m_deltaVelAindex <0) + { + solverConstraint.m_deltaVelAindex = data.m_deltaVelocities.size(); + multiBodyA->setCompanionId(solverConstraint.m_deltaVelAindex); + data.m_deltaVelocities.resize(data.m_deltaVelocities.size()+ndofA); + } else + { + btAssert(data.m_deltaVelocities.size() >= solverConstraint.m_deltaVelAindex+ndofA); + } + + solverConstraint.m_jacAindex = data.m_jacobians.size(); + data.m_jacobians.resize(data.m_jacobians.size()+ndofA); + data.m_deltaVelocitiesUnitImpulse.resize(data.m_deltaVelocitiesUnitImpulse.size()+ndofA); + btAssert(data.m_jacobians.size() == data.m_deltaVelocitiesUnitImpulse.size()); + + btScalar* jac1=&data.m_jacobians[solverConstraint.m_jacAindex]; + multiBodyA->fillContactJacobian(solverConstraint.m_linkA, posAworld, contactNormalOnB, jac1, data.scratch_r, data.scratch_v, data.scratch_m); + btScalar* delta = &data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacAindex]; + multiBodyA->calcAccelerationDeltas(&data.m_jacobians[solverConstraint.m_jacAindex],delta,data.scratch_r, data.scratch_v); + } else + { + btVector3 torqueAxis0 = rel_pos1.cross(contactNormalOnB); + solverConstraint.m_angularComponentA = rb0 ? rb0->getInvInertiaTensorWorld()*torqueAxis0*rb0->getAngularFactor() : btVector3(0,0,0); + solverConstraint.m_relpos1CrossNormal = torqueAxis0; + solverConstraint.m_contactNormal1 = contactNormalOnB; + } + + if (multiBodyB) + { + const int ndofB = multiBodyB->getNumLinks() + 6; + + solverConstraint.m_deltaVelBindex = multiBodyB->getCompanionId(); + if (solverConstraint.m_deltaVelBindex <0) + { + solverConstraint.m_deltaVelBindex = data.m_deltaVelocities.size(); + multiBodyB->setCompanionId(solverConstraint.m_deltaVelBindex); + data.m_deltaVelocities.resize(data.m_deltaVelocities.size()+ndofB); + } + + solverConstraint.m_jacBindex = data.m_jacobians.size(); + + data.m_jacobians.resize(data.m_jacobians.size()+ndofB); + data.m_deltaVelocitiesUnitImpulse.resize(data.m_deltaVelocitiesUnitImpulse.size()+ndofB); + btAssert(data.m_jacobians.size() == data.m_deltaVelocitiesUnitImpulse.size()); + + multiBodyB->fillContactJacobian(solverConstraint.m_linkB, posBworld, -contactNormalOnB, &data.m_jacobians[solverConstraint.m_jacBindex], data.scratch_r, data.scratch_v, data.scratch_m); + multiBodyB->calcAccelerationDeltas(&data.m_jacobians[solverConstraint.m_jacBindex],&data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacBindex],data.scratch_r, data.scratch_v); + } else + { + btVector3 torqueAxis1 = rel_pos2.cross(contactNormalOnB); + solverConstraint.m_angularComponentB = rb1 ? rb1->getInvInertiaTensorWorld()*-torqueAxis1*rb1->getAngularFactor() : btVector3(0,0,0); + solverConstraint.m_relpos2CrossNormal = -torqueAxis1; + solverConstraint.m_contactNormal2 = -contactNormalOnB; + } + + { + + btVector3 vec; + btScalar denom0 = 0.f; + btScalar denom1 = 0.f; + btScalar* jacB = 0; + btScalar* jacA = 0; + btScalar* lambdaA =0; + btScalar* lambdaB =0; + int ndofA = 0; + if (multiBodyA) + { + ndofA = multiBodyA->getNumLinks() + 6; + jacA = &data.m_jacobians[solverConstraint.m_jacAindex]; + lambdaA = &data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacAindex]; + for (int i = 0; i < ndofA; ++i) + { + btScalar j = jacA[i] ; + btScalar l =lambdaA[i]; + denom0 += j*l; + } + } else + { + if (rb0) + { + vec = ( solverConstraint.m_angularComponentA).cross(rel_pos1); + denom0 = rb0->getInvMass() + contactNormalOnB.dot(vec); + } + } + if (multiBodyB) + { + const int ndofB = multiBodyB->getNumLinks() + 6; + jacB = &data.m_jacobians[solverConstraint.m_jacBindex]; + lambdaB = &data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacBindex]; + for (int i = 0; i < ndofB; ++i) + { + btScalar j = jacB[i] ; + btScalar l =lambdaB[i]; + denom1 += j*l; + } + + } else + { + if (rb1) + { + vec = ( -solverConstraint.m_angularComponentB).cross(rel_pos2); + denom1 = rb1->getInvMass() + contactNormalOnB.dot(vec); + } + } + + if (multiBodyA && (multiBodyA==multiBodyB)) + { + // ndof1 == ndof2 in this case + for (int i = 0; i < ndofA; ++i) + { + denom1 += jacB[i] * lambdaA[i]; + denom1 += jacA[i] * lambdaB[i]; + } + } + + btScalar d = denom0+denom1; + if (btFabs(d)>SIMD_EPSILON) + { + + solverConstraint.m_jacDiagABInv = relaxation/(d); + } else + { + solverConstraint.m_jacDiagABInv = 1.f; + } + + } + + + //compute rhs and remaining solverConstraint fields + + + + btScalar restitution = 0.f; + btScalar penetration = isFriction? 0 : position+infoGlobal.m_linearSlop; + + btScalar rel_vel = 0.f; + int ndofA = 0; + int ndofB = 0; + { + + btVector3 vel1,vel2; + if (multiBodyA) + { + ndofA = multiBodyA->getNumLinks() + 6; + btScalar* jacA = &data.m_jacobians[solverConstraint.m_jacAindex]; + for (int i = 0; i < ndofA ; ++i) + rel_vel += multiBodyA->getVelocityVector()[i] * jacA[i]; + } else + { + if (rb0) + { + rel_vel += rb0->getVelocityInLocalPoint(rel_pos1).dot(solverConstraint.m_contactNormal1); + } + } + if (multiBodyB) + { + ndofB = multiBodyB->getNumLinks() + 6; + btScalar* jacB = &data.m_jacobians[solverConstraint.m_jacBindex]; + for (int i = 0; i < ndofB ; ++i) + rel_vel += multiBodyB->getVelocityVector()[i] * jacB[i]; + + } else + { + if (rb1) + { + rel_vel += rb1->getVelocityInLocalPoint(rel_pos2).dot(solverConstraint.m_contactNormal2); + } + } + + solverConstraint.m_friction = 0.f;//cp.m_combinedFriction; + + + restitution = restitution * -rel_vel;//restitutionCurve(rel_vel, cp.m_combinedRestitution); + if (restitution <= btScalar(0.)) + { + restitution = 0.f; + }; + } + + + ///warm starting (or zero if disabled) + /* + if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING) + { + solverConstraint.m_appliedImpulse = isFriction ? 0 : cp.m_appliedImpulse * infoGlobal.m_warmstartingFactor; + + if (solverConstraint.m_appliedImpulse) + { + if (multiBodyA) + { + btScalar impulse = solverConstraint.m_appliedImpulse; + btScalar* deltaV = &data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacAindex]; + multiBodyA->applyDeltaVee(deltaV,impulse); + applyDeltaVee(data,deltaV,impulse,solverConstraint.m_deltaVelAindex,ndofA); + } else + { + if (rb0) + bodyA->internalApplyImpulse(solverConstraint.m_contactNormal1*bodyA->internalGetInvMass()*rb0->getLinearFactor(),solverConstraint.m_angularComponentA,solverConstraint.m_appliedImpulse); + } + if (multiBodyB) + { + btScalar impulse = solverConstraint.m_appliedImpulse; + btScalar* deltaV = &data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacBindex]; + multiBodyB->applyDeltaVee(deltaV,impulse); + applyDeltaVee(data,deltaV,impulse,solverConstraint.m_deltaVelBindex,ndofB); + } else + { + if (rb1) + bodyB->internalApplyImpulse(-solverConstraint.m_contactNormal2*bodyB->internalGetInvMass()*rb1->getLinearFactor(),-solverConstraint.m_angularComponentB,-(btScalar)solverConstraint.m_appliedImpulse); + } + } + } else + */ + { + solverConstraint.m_appliedImpulse = 0.f; + } + + solverConstraint.m_appliedPushImpulse = 0.f; + + { + + + btScalar positionalError = 0.f; + btScalar velocityError = restitution - rel_vel;// * damping; + + + btScalar erp = infoGlobal.m_erp2; + if (!infoGlobal.m_splitImpulse || (penetration > infoGlobal.m_splitImpulsePenetrationThreshold)) + { + erp = infoGlobal.m_erp; + } + + if (penetration>0) + { + positionalError = 0; + velocityError = -penetration / infoGlobal.m_timeStep; + + } else + { + positionalError = -penetration * erp/infoGlobal.m_timeStep; + } + + btScalar penetrationImpulse = positionalError*solverConstraint.m_jacDiagABInv; + btScalar velocityImpulse = velocityError *solverConstraint.m_jacDiagABInv; + + if (!infoGlobal.m_splitImpulse || (penetration > infoGlobal.m_splitImpulsePenetrationThreshold)) + { + //combine position and velocity into rhs + solverConstraint.m_rhs = penetrationImpulse+velocityImpulse; + solverConstraint.m_rhsPenetration = 0.f; + + } else + { + //split position and velocity into rhs and m_rhsPenetration + solverConstraint.m_rhs = velocityImpulse; + solverConstraint.m_rhsPenetration = penetrationImpulse; + } + + solverConstraint.m_cfm = 0.f; + solverConstraint.m_lowerLimit = -m_maxAppliedImpulse; + solverConstraint.m_upperLimit = m_maxAppliedImpulse; + } + +} diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.h b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.h new file mode 100644 index 000000000..9fa317330 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.h @@ -0,0 +1,166 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_MULTIBODY_CONSTRAINT_H +#define BT_MULTIBODY_CONSTRAINT_H + +#include "LinearMath/btScalar.h" +#include "LinearMath/btAlignedObjectArray.h" +#include "btMultiBody.h" + +class btMultiBody; +struct btSolverInfo; + +#include "btMultiBodySolverConstraint.h" + +struct btMultiBodyJacobianData +{ + btAlignedObjectArray m_jacobians; + btAlignedObjectArray m_deltaVelocitiesUnitImpulse; + btAlignedObjectArray m_deltaVelocities; + btAlignedObjectArray scratch_r; + btAlignedObjectArray scratch_v; + btAlignedObjectArray scratch_m; + btAlignedObjectArray* m_solverBodyPool; + int m_fixedBodyId; + +}; + + +class btMultiBodyConstraint +{ +protected: + + btMultiBody* m_bodyA; + btMultiBody* m_bodyB; + int m_linkA; + int m_linkB; + + int m_num_rows; + int m_jac_size_A; + int m_jac_size_both; + int m_pos_offset; + + bool m_isUnilateral; + + btScalar m_maxAppliedImpulse; + + + // data block laid out as follows: + // cached impulses. (one per row.) + // jacobians. (interleaved, row1 body1 then row1 body2 then row2 body 1 etc) + // positions. (one per row.) + btAlignedObjectArray m_data; + + void applyDeltaVee(btMultiBodyJacobianData& data, btScalar* delta_vee, btScalar impulse, int velocityIndex, int ndof); + + void fillMultiBodyConstraintMixed(btMultiBodySolverConstraint& solverConstraint, + btMultiBodyJacobianData& data, + const btVector3& contactNormalOnB, + const btVector3& posAworld, const btVector3& posBworld, + btScalar position, + const btContactSolverInfo& infoGlobal, + btScalar& relaxation, + bool isFriction, btScalar desiredVelocity=0, btScalar cfmSlip=0); + + btScalar fillConstraintRowMultiBodyMultiBody(btMultiBodySolverConstraint& constraintRow, + btMultiBodyJacobianData& data, + btScalar* jacOrgA,btScalar* jacOrgB, + const btContactSolverInfo& infoGlobal, + btScalar desiredVelocity, + btScalar lowerLimit, + btScalar upperLimit); + +public: + + btMultiBodyConstraint(btMultiBody* bodyA,btMultiBody* bodyB,int linkA, int linkB, int numRows, bool isUnilateral); + virtual ~btMultiBodyConstraint(); + + + + virtual int getIslandIdA() const =0; + virtual int getIslandIdB() const =0; + + virtual void createConstraintRows(btMultiBodyConstraintArray& constraintRows, + btMultiBodyJacobianData& data, + const btContactSolverInfo& infoGlobal)=0; + + int getNumRows() const + { + return m_num_rows; + } + + btMultiBody* getMultiBodyA() + { + return m_bodyA; + } + btMultiBody* getMultiBodyB() + { + return m_bodyB; + } + + // current constraint position + // constraint is pos >= 0 for unilateral, or pos = 0 for bilateral + // NOTE: ignored position for friction rows. + btScalar getPosition(int row) const + { + return m_data[m_pos_offset + row]; + } + + void setPosition(int row, btScalar pos) + { + m_data[m_pos_offset + row] = pos; + } + + + bool isUnilateral() const + { + return m_isUnilateral; + } + + // jacobian blocks. + // each of size 6 + num_links. (jacobian2 is null if no body2.) + // format: 3 'omega' coefficients, 3 'v' coefficients, then the 'qdot' coefficients. + btScalar* jacobianA(int row) + { + return &m_data[m_num_rows + row * m_jac_size_both]; + } + const btScalar* jacobianA(int row) const + { + return &m_data[m_num_rows + (row * m_jac_size_both)]; + } + btScalar* jacobianB(int row) + { + return &m_data[m_num_rows + (row * m_jac_size_both) + m_jac_size_A]; + } + const btScalar* jacobianB(int row) const + { + return &m_data[m_num_rows + (row * m_jac_size_both) + m_jac_size_A]; + } + + btScalar getMaxAppliedImpulse() const + { + return m_maxAppliedImpulse; + } + void setMaxAppliedImpulse(btScalar maxImp) + { + m_maxAppliedImpulse = maxImp; + } + + +}; + +#endif //BT_MULTIBODY_CONSTRAINT_H + diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp new file mode 100644 index 000000000..577f84622 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp @@ -0,0 +1,795 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#include "btMultiBodyConstraintSolver.h" +#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" +#include "btMultiBodyLinkCollider.h" + +#include "BulletDynamics/ConstraintSolver/btSolverBody.h" +#include "btMultiBodyConstraint.h" +#include "BulletDynamics/ConstraintSolver/btContactSolverInfo.h" + +#include "LinearMath/btQuickprof.h" + +btScalar btMultiBodyConstraintSolver::solveSingleIteration(int iteration, btCollisionObject** bodies ,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer) +{ + btScalar val = btSequentialImpulseConstraintSolver::solveSingleIteration(iteration, bodies ,numBodies,manifoldPtr, numManifolds,constraints,numConstraints,infoGlobal,debugDrawer); + + //solve featherstone non-contact constraints + + //printf("m_multiBodyNonContactConstraints = %d\n",m_multiBodyNonContactConstraints.size()); + for (int j=0;jm_multiBodyFrictionContactConstraints.size();j++) + { + if (iteration < infoGlobal.m_numIterations) + { + btMultiBodySolverConstraint& frictionConstraint = m_multiBodyFrictionContactConstraints[j]; + btScalar totalImpulse = m_multiBodyNormalContactConstraints[frictionConstraint.m_frictionIndex].m_appliedImpulse; + //adjust friction limits here + if (totalImpulse>btScalar(0)) + { + frictionConstraint.m_lowerLimit = -(frictionConstraint.m_friction*totalImpulse); + frictionConstraint.m_upperLimit = frictionConstraint.m_friction*totalImpulse; + resolveSingleConstraintRowGeneric(frictionConstraint); + } + } + } + return val; +} + +btScalar btMultiBodyConstraintSolver::solveGroupCacheFriendlySetup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer) +{ + m_multiBodyNonContactConstraints.resize(0); + m_multiBodyNormalContactConstraints.resize(0); + m_multiBodyFrictionContactConstraints.resize(0); + m_data.m_jacobians.resize(0); + m_data.m_deltaVelocitiesUnitImpulse.resize(0); + m_data.m_deltaVelocities.resize(0); + + for (int i=0;im_multiBody->setCompanionId(-1); + } + } + + btScalar val = btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup( bodies,numBodies,manifoldPtr, numManifolds, constraints,numConstraints,infoGlobal,debugDrawer); + + return val; +} + +void btMultiBodyConstraintSolver::applyDeltaVee(btScalar* delta_vee, btScalar impulse, int velocityIndex, int ndof) +{ + for (int i = 0; i < ndof; ++i) + m_data.m_deltaVelocities[velocityIndex+i] += delta_vee[i] * impulse; +} + +void btMultiBodyConstraintSolver::resolveSingleConstraintRowGeneric(const btMultiBodySolverConstraint& c) +{ + + btScalar deltaImpulse = c.m_rhs-btScalar(c.m_appliedImpulse)*c.m_cfm; + btScalar deltaVelADotn=0; + btScalar deltaVelBDotn=0; + btSolverBody* bodyA = 0; + btSolverBody* bodyB = 0; + int ndofA=0; + int ndofB=0; + + if (c.m_multiBodyA) + { + ndofA = c.m_multiBodyA->getNumLinks() + 6; + for (int i = 0; i < ndofA; ++i) + deltaVelADotn += m_data.m_jacobians[c.m_jacAindex+i] * m_data.m_deltaVelocities[c.m_deltaVelAindex+i]; + } else + { + bodyA = &m_tmpSolverBodyPool[c.m_solverBodyIdA]; + deltaVelADotn += c.m_contactNormal1.dot(bodyA->internalGetDeltaLinearVelocity()) + c.m_relpos1CrossNormal.dot(bodyA->internalGetDeltaAngularVelocity()); + } + + if (c.m_multiBodyB) + { + ndofB = c.m_multiBodyB->getNumLinks() + 6; + for (int i = 0; i < ndofB; ++i) + deltaVelBDotn += m_data.m_jacobians[c.m_jacBindex+i] * m_data.m_deltaVelocities[c.m_deltaVelBindex+i]; + } else + { + bodyB = &m_tmpSolverBodyPool[c.m_solverBodyIdB]; + deltaVelBDotn += c.m_contactNormal2.dot(bodyB->internalGetDeltaLinearVelocity()) + c.m_relpos2CrossNormal.dot(bodyB->internalGetDeltaAngularVelocity()); + } + + + deltaImpulse -= deltaVelADotn*c.m_jacDiagABInv;//m_jacDiagABInv = 1./denom + deltaImpulse -= deltaVelBDotn*c.m_jacDiagABInv; + const btScalar sum = btScalar(c.m_appliedImpulse) + deltaImpulse; + + if (sum < c.m_lowerLimit) + { + deltaImpulse = c.m_lowerLimit-c.m_appliedImpulse; + c.m_appliedImpulse = c.m_lowerLimit; + } + else if (sum > c.m_upperLimit) + { + deltaImpulse = c.m_upperLimit-c.m_appliedImpulse; + c.m_appliedImpulse = c.m_upperLimit; + } + else + { + c.m_appliedImpulse = sum; + } + + if (c.m_multiBodyA) + { + applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex],deltaImpulse,c.m_deltaVelAindex,ndofA); + c.m_multiBodyA->applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex],deltaImpulse); + } else + { + bodyA->internalApplyImpulse(c.m_contactNormal1*bodyA->internalGetInvMass(),c.m_angularComponentA,deltaImpulse); + + } + if (c.m_multiBodyB) + { + applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex],deltaImpulse,c.m_deltaVelBindex,ndofB); + c.m_multiBodyB->applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex],deltaImpulse); + } else + { + bodyB->internalApplyImpulse(c.m_contactNormal2*bodyB->internalGetInvMass(),c.m_angularComponentB,deltaImpulse); + } + +} + + +void btMultiBodyConstraintSolver::resolveSingleConstraintRowGenericMultiBody(const btMultiBodySolverConstraint& c) +{ + + btScalar deltaImpulse = c.m_rhs-btScalar(c.m_appliedImpulse)*c.m_cfm; + btScalar deltaVelADotn=0; + btScalar deltaVelBDotn=0; + int ndofA=0; + int ndofB=0; + + if (c.m_multiBodyA) + { + ndofA = c.m_multiBodyA->getNumLinks() + 6; + for (int i = 0; i < ndofA; ++i) + deltaVelADotn += m_data.m_jacobians[c.m_jacAindex+i] * m_data.m_deltaVelocities[c.m_deltaVelAindex+i]; + } + + if (c.m_multiBodyB) + { + ndofB = c.m_multiBodyB->getNumLinks() + 6; + for (int i = 0; i < ndofB; ++i) + deltaVelBDotn += m_data.m_jacobians[c.m_jacBindex+i] * m_data.m_deltaVelocities[c.m_deltaVelBindex+i]; + } + + + deltaImpulse -= deltaVelADotn*c.m_jacDiagABInv;//m_jacDiagABInv = 1./denom + deltaImpulse -= deltaVelBDotn*c.m_jacDiagABInv; + const btScalar sum = btScalar(c.m_appliedImpulse) + deltaImpulse; + + if (sum < c.m_lowerLimit) + { + deltaImpulse = c.m_lowerLimit-c.m_appliedImpulse; + c.m_appliedImpulse = c.m_lowerLimit; + } + else if (sum > c.m_upperLimit) + { + deltaImpulse = c.m_upperLimit-c.m_appliedImpulse; + c.m_appliedImpulse = c.m_upperLimit; + } + else + { + c.m_appliedImpulse = sum; + } + + if (c.m_multiBodyA) + { + applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex],deltaImpulse,c.m_deltaVelAindex,ndofA); + c.m_multiBodyA->applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex],deltaImpulse); + } + if (c.m_multiBodyB) + { + applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex],deltaImpulse,c.m_deltaVelBindex,ndofB); + c.m_multiBodyB->applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex],deltaImpulse); + } +} + + +void btMultiBodyConstraintSolver::setupMultiBodyContactConstraint(btMultiBodySolverConstraint& solverConstraint, + const btVector3& contactNormal, + btManifoldPoint& cp, const btContactSolverInfo& infoGlobal, + btScalar& relaxation, + bool isFriction, btScalar desiredVelocity, btScalar cfmSlip) +{ + + BT_PROFILE("setupMultiBodyContactConstraint"); + btVector3 rel_pos1; + btVector3 rel_pos2; + + btMultiBody* multiBodyA = solverConstraint.m_multiBodyA; + btMultiBody* multiBodyB = solverConstraint.m_multiBodyB; + + const btVector3& pos1 = cp.getPositionWorldOnA(); + const btVector3& pos2 = cp.getPositionWorldOnB(); + + btSolverBody* bodyA = multiBodyA ? 0 : &m_tmpSolverBodyPool[solverConstraint.m_solverBodyIdA]; + btSolverBody* bodyB = multiBodyB ? 0 : &m_tmpSolverBodyPool[solverConstraint.m_solverBodyIdB]; + + btRigidBody* rb0 = multiBodyA ? 0 : bodyA->m_originalBody; + btRigidBody* rb1 = multiBodyB ? 0 : bodyB->m_originalBody; + + if (bodyA) + rel_pos1 = pos1 - bodyA->getWorldTransform().getOrigin(); + if (bodyB) + rel_pos2 = pos2 - bodyB->getWorldTransform().getOrigin(); + + relaxation = 1.f; + + if (multiBodyA) + { + const int ndofA = multiBodyA->getNumLinks() + 6; + + solverConstraint.m_deltaVelAindex = multiBodyA->getCompanionId(); + + if (solverConstraint.m_deltaVelAindex <0) + { + solverConstraint.m_deltaVelAindex = m_data.m_deltaVelocities.size(); + multiBodyA->setCompanionId(solverConstraint.m_deltaVelAindex); + m_data.m_deltaVelocities.resize(m_data.m_deltaVelocities.size()+ndofA); + } else + { + btAssert(m_data.m_deltaVelocities.size() >= solverConstraint.m_deltaVelAindex+ndofA); + } + + solverConstraint.m_jacAindex = m_data.m_jacobians.size(); + m_data.m_jacobians.resize(m_data.m_jacobians.size()+ndofA); + m_data.m_deltaVelocitiesUnitImpulse.resize(m_data.m_deltaVelocitiesUnitImpulse.size()+ndofA); + btAssert(m_data.m_jacobians.size() == m_data.m_deltaVelocitiesUnitImpulse.size()); + + btScalar* jac1=&m_data.m_jacobians[solverConstraint.m_jacAindex]; + multiBodyA->fillContactJacobian(solverConstraint.m_linkA, cp.getPositionWorldOnA(), contactNormal, jac1, m_data.scratch_r, m_data.scratch_v, m_data.scratch_m); + btScalar* delta = &m_data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacAindex]; + multiBodyA->calcAccelerationDeltas(&m_data.m_jacobians[solverConstraint.m_jacAindex],delta,m_data.scratch_r, m_data.scratch_v); + } else + { + btVector3 torqueAxis0 = rel_pos1.cross(contactNormal); + solverConstraint.m_angularComponentA = rb0 ? rb0->getInvInertiaTensorWorld()*torqueAxis0*rb0->getAngularFactor() : btVector3(0,0,0); + solverConstraint.m_relpos1CrossNormal = torqueAxis0; + solverConstraint.m_contactNormal1 = contactNormal; + } + + if (multiBodyB) + { + const int ndofB = multiBodyB->getNumLinks() + 6; + + solverConstraint.m_deltaVelBindex = multiBodyB->getCompanionId(); + if (solverConstraint.m_deltaVelBindex <0) + { + solverConstraint.m_deltaVelBindex = m_data.m_deltaVelocities.size(); + multiBodyB->setCompanionId(solverConstraint.m_deltaVelBindex); + m_data.m_deltaVelocities.resize(m_data.m_deltaVelocities.size()+ndofB); + } + + solverConstraint.m_jacBindex = m_data.m_jacobians.size(); + + m_data.m_jacobians.resize(m_data.m_jacobians.size()+ndofB); + m_data.m_deltaVelocitiesUnitImpulse.resize(m_data.m_deltaVelocitiesUnitImpulse.size()+ndofB); + btAssert(m_data.m_jacobians.size() == m_data.m_deltaVelocitiesUnitImpulse.size()); + + multiBodyB->fillContactJacobian(solverConstraint.m_linkB, cp.getPositionWorldOnB(), -contactNormal, &m_data.m_jacobians[solverConstraint.m_jacBindex], m_data.scratch_r, m_data.scratch_v, m_data.scratch_m); + multiBodyB->calcAccelerationDeltas(&m_data.m_jacobians[solverConstraint.m_jacBindex],&m_data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacBindex],m_data.scratch_r, m_data.scratch_v); + } else + { + btVector3 torqueAxis1 = rel_pos2.cross(contactNormal); + solverConstraint.m_angularComponentB = rb1 ? rb1->getInvInertiaTensorWorld()*-torqueAxis1*rb1->getAngularFactor() : btVector3(0,0,0); + solverConstraint.m_relpos2CrossNormal = -torqueAxis1; + solverConstraint.m_contactNormal2 = -contactNormal; + } + + { + + btVector3 vec; + btScalar denom0 = 0.f; + btScalar denom1 = 0.f; + btScalar* jacB = 0; + btScalar* jacA = 0; + btScalar* lambdaA =0; + btScalar* lambdaB =0; + int ndofA = 0; + if (multiBodyA) + { + ndofA = multiBodyA->getNumLinks() + 6; + jacA = &m_data.m_jacobians[solverConstraint.m_jacAindex]; + lambdaA = &m_data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacAindex]; + for (int i = 0; i < ndofA; ++i) + { + btScalar j = jacA[i] ; + btScalar l =lambdaA[i]; + denom0 += j*l; + } + } else + { + if (rb0) + { + vec = ( solverConstraint.m_angularComponentA).cross(rel_pos1); + denom0 = rb0->getInvMass() + contactNormal.dot(vec); + } + } + if (multiBodyB) + { + const int ndofB = multiBodyB->getNumLinks() + 6; + jacB = &m_data.m_jacobians[solverConstraint.m_jacBindex]; + lambdaB = &m_data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacBindex]; + for (int i = 0; i < ndofB; ++i) + { + btScalar j = jacB[i] ; + btScalar l =lambdaB[i]; + denom1 += j*l; + } + + } else + { + if (rb1) + { + vec = ( -solverConstraint.m_angularComponentB).cross(rel_pos2); + denom1 = rb1->getInvMass() + contactNormal.dot(vec); + } + } + + if (multiBodyA && (multiBodyA==multiBodyB)) + { + // ndof1 == ndof2 in this case + for (int i = 0; i < ndofA; ++i) + { + denom1 += jacB[i] * lambdaA[i]; + denom1 += jacA[i] * lambdaB[i]; + } + } + + btScalar d = denom0+denom1; + if (btFabs(d)>SIMD_EPSILON) + { + + solverConstraint.m_jacDiagABInv = relaxation/(d); + } else + { + solverConstraint.m_jacDiagABInv = 1.f; + } + + } + + + //compute rhs and remaining solverConstraint fields + + + + btScalar restitution = 0.f; + btScalar penetration = isFriction? 0 : cp.getDistance()+infoGlobal.m_linearSlop; + + btScalar rel_vel = 0.f; + int ndofA = 0; + int ndofB = 0; + { + + btVector3 vel1,vel2; + if (multiBodyA) + { + ndofA = multiBodyA->getNumLinks() + 6; + btScalar* jacA = &m_data.m_jacobians[solverConstraint.m_jacAindex]; + for (int i = 0; i < ndofA ; ++i) + rel_vel += multiBodyA->getVelocityVector()[i] * jacA[i]; + } else + { + if (rb0) + { + rel_vel += rb0->getVelocityInLocalPoint(rel_pos1).dot(solverConstraint.m_contactNormal1); + } + } + if (multiBodyB) + { + ndofB = multiBodyB->getNumLinks() + 6; + btScalar* jacB = &m_data.m_jacobians[solverConstraint.m_jacBindex]; + for (int i = 0; i < ndofB ; ++i) + rel_vel += multiBodyB->getVelocityVector()[i] * jacB[i]; + + } else + { + if (rb1) + { + rel_vel += rb1->getVelocityInLocalPoint(rel_pos2).dot(solverConstraint.m_contactNormal2); + } + } + + solverConstraint.m_friction = cp.m_combinedFriction; + + + restitution = restitutionCurve(rel_vel, cp.m_combinedRestitution); + if (restitution <= btScalar(0.)) + { + restitution = 0.f; + }; + } + + + ///warm starting (or zero if disabled) + if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING) + { + solverConstraint.m_appliedImpulse = isFriction ? 0 : cp.m_appliedImpulse * infoGlobal.m_warmstartingFactor; + + if (solverConstraint.m_appliedImpulse) + { + if (multiBodyA) + { + btScalar impulse = solverConstraint.m_appliedImpulse; + btScalar* deltaV = &m_data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacAindex]; + multiBodyA->applyDeltaVee(deltaV,impulse); + applyDeltaVee(deltaV,impulse,solverConstraint.m_deltaVelAindex,ndofA); + } else + { + if (rb0) + bodyA->internalApplyImpulse(solverConstraint.m_contactNormal1*bodyA->internalGetInvMass()*rb0->getLinearFactor(),solverConstraint.m_angularComponentA,solverConstraint.m_appliedImpulse); + } + if (multiBodyB) + { + btScalar impulse = solverConstraint.m_appliedImpulse; + btScalar* deltaV = &m_data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacBindex]; + multiBodyB->applyDeltaVee(deltaV,impulse); + applyDeltaVee(deltaV,impulse,solverConstraint.m_deltaVelBindex,ndofB); + } else + { + if (rb1) + bodyB->internalApplyImpulse(-solverConstraint.m_contactNormal2*bodyB->internalGetInvMass()*rb1->getLinearFactor(),-solverConstraint.m_angularComponentB,-(btScalar)solverConstraint.m_appliedImpulse); + } + } + } else + { + solverConstraint.m_appliedImpulse = 0.f; + } + + solverConstraint.m_appliedPushImpulse = 0.f; + + { + + + btScalar positionalError = 0.f; + btScalar velocityError = restitution - rel_vel;// * damping; + + + btScalar erp = infoGlobal.m_erp2; + if (!infoGlobal.m_splitImpulse || (penetration > infoGlobal.m_splitImpulsePenetrationThreshold)) + { + erp = infoGlobal.m_erp; + } + + if (penetration>0) + { + positionalError = 0; + velocityError = -penetration / infoGlobal.m_timeStep; + + } else + { + positionalError = -penetration * erp/infoGlobal.m_timeStep; + } + + btScalar penetrationImpulse = positionalError*solverConstraint.m_jacDiagABInv; + btScalar velocityImpulse = velocityError *solverConstraint.m_jacDiagABInv; + + if (!infoGlobal.m_splitImpulse || (penetration > infoGlobal.m_splitImpulsePenetrationThreshold)) + { + //combine position and velocity into rhs + solverConstraint.m_rhs = penetrationImpulse+velocityImpulse; + solverConstraint.m_rhsPenetration = 0.f; + + } else + { + //split position and velocity into rhs and m_rhsPenetration + solverConstraint.m_rhs = velocityImpulse; + solverConstraint.m_rhsPenetration = penetrationImpulse; + } + + solverConstraint.m_cfm = 0.f; + solverConstraint.m_lowerLimit = 0; + solverConstraint.m_upperLimit = 1e10f; + } + +} + + + + +btMultiBodySolverConstraint& btMultiBodyConstraintSolver::addMultiBodyFrictionConstraint(const btVector3& normalAxis,btPersistentManifold* manifold,int frictionIndex,btManifoldPoint& cp,btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity, btScalar cfmSlip) +{ + BT_PROFILE("addMultiBodyFrictionConstraint"); + btMultiBodySolverConstraint& solverConstraint = m_multiBodyFrictionContactConstraints.expandNonInitializing(); + solverConstraint.m_frictionIndex = frictionIndex; + bool isFriction = true; + + const btMultiBodyLinkCollider* fcA = btMultiBodyLinkCollider::upcast(manifold->getBody0()); + const btMultiBodyLinkCollider* fcB = btMultiBodyLinkCollider::upcast(manifold->getBody1()); + + btMultiBody* mbA = fcA? fcA->m_multiBody : 0; + btMultiBody* mbB = fcB? fcB->m_multiBody : 0; + + int solverBodyIdA = mbA? -1 : getOrInitSolverBody(*colObj0,infoGlobal.m_timeStep); + int solverBodyIdB = mbB ? -1 : getOrInitSolverBody(*colObj1,infoGlobal.m_timeStep); + + solverConstraint.m_solverBodyIdA = solverBodyIdA; + solverConstraint.m_solverBodyIdB = solverBodyIdB; + solverConstraint.m_multiBodyA = mbA; + if (mbA) + solverConstraint.m_linkA = fcA->m_link; + + solverConstraint.m_multiBodyB = mbB; + if (mbB) + solverConstraint.m_linkB = fcB->m_link; + + solverConstraint.m_originalContactPoint = &cp; + + setupMultiBodyContactConstraint(solverConstraint, normalAxis, cp, infoGlobal,relaxation,isFriction, desiredVelocity, cfmSlip); + return solverConstraint; +} + +void btMultiBodyConstraintSolver::convertMultiBodyContact(btPersistentManifold* manifold,const btContactSolverInfo& infoGlobal) +{ + const btMultiBodyLinkCollider* fcA = btMultiBodyLinkCollider::upcast(manifold->getBody0()); + const btMultiBodyLinkCollider* fcB = btMultiBodyLinkCollider::upcast(manifold->getBody1()); + + btMultiBody* mbA = fcA? fcA->m_multiBody : 0; + btMultiBody* mbB = fcB? fcB->m_multiBody : 0; + + btCollisionObject* colObj0=0,*colObj1=0; + + colObj0 = (btCollisionObject*)manifold->getBody0(); + colObj1 = (btCollisionObject*)manifold->getBody1(); + + int solverBodyIdA = mbA? -1 : getOrInitSolverBody(*colObj0,infoGlobal.m_timeStep); + int solverBodyIdB = mbB ? -1 : getOrInitSolverBody(*colObj1,infoGlobal.m_timeStep); + + btSolverBody* solverBodyA = mbA ? 0 : &m_tmpSolverBodyPool[solverBodyIdA]; + btSolverBody* solverBodyB = mbB ? 0 : &m_tmpSolverBodyPool[solverBodyIdB]; + + + ///avoid collision response between two static objects +// if (!solverBodyA || (solverBodyA->m_invMass.isZero() && (!solverBodyB || solverBodyB->m_invMass.isZero()))) + // return; + + int rollingFriction=1; + + for (int j=0;jgetNumContacts();j++) + { + + btManifoldPoint& cp = manifold->getContactPoint(j); + + if (cp.getDistance() <= manifold->getContactProcessingThreshold()) + { + + btScalar relaxation; + + int frictionIndex = m_multiBodyNormalContactConstraints.size(); + + btMultiBodySolverConstraint& solverConstraint = m_multiBodyNormalContactConstraints.expandNonInitializing(); + + btRigidBody* rb0 = btRigidBody::upcast(colObj0); + btRigidBody* rb1 = btRigidBody::upcast(colObj1); + solverConstraint.m_solverBodyIdA = solverBodyIdA; + solverConstraint.m_solverBodyIdB = solverBodyIdB; + solverConstraint.m_multiBodyA = mbA; + if (mbA) + solverConstraint.m_linkA = fcA->m_link; + + solverConstraint.m_multiBodyB = mbB; + if (mbB) + solverConstraint.m_linkB = fcB->m_link; + + solverConstraint.m_originalContactPoint = &cp; + + bool isFriction = false; + setupMultiBodyContactConstraint(solverConstraint, cp.m_normalWorldOnB,cp, infoGlobal, relaxation, isFriction); + +// const btVector3& pos1 = cp.getPositionWorldOnA(); +// const btVector3& pos2 = cp.getPositionWorldOnB(); + + /////setup the friction constraints +#define ENABLE_FRICTION +#ifdef ENABLE_FRICTION + solverConstraint.m_frictionIndex = frictionIndex; +#if ROLLING_FRICTION + btVector3 angVelA(0,0,0),angVelB(0,0,0); + if (rb0) + angVelA = rb0->getAngularVelocity(); + if (rb1) + angVelB = rb1->getAngularVelocity(); + btVector3 relAngVel = angVelB-angVelA; + + if ((cp.m_combinedRollingFriction>0.f) && (rollingFriction>0)) + { + //only a single rollingFriction per manifold + rollingFriction--; + if (relAngVel.length()>infoGlobal.m_singleAxisRollingFrictionThreshold) + { + relAngVel.normalize(); + applyAnisotropicFriction(colObj0,relAngVel,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION); + applyAnisotropicFriction(colObj1,relAngVel,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION); + if (relAngVel.length()>0.001) + addRollingFrictionConstraint(relAngVel,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); + + } else + { + addRollingFrictionConstraint(cp.m_normalWorldOnB,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); + btVector3 axis0,axis1; + btPlaneSpace1(cp.m_normalWorldOnB,axis0,axis1); + applyAnisotropicFriction(colObj0,axis0,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION); + applyAnisotropicFriction(colObj1,axis0,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION); + applyAnisotropicFriction(colObj0,axis1,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION); + applyAnisotropicFriction(colObj1,axis1,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION); + if (axis0.length()>0.001) + addRollingFrictionConstraint(axis0,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); + if (axis1.length()>0.001) + addRollingFrictionConstraint(axis1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); + + } + } +#endif //ROLLING_FRICTION + + ///Bullet has several options to set the friction directions + ///By default, each contact has only a single friction direction that is recomputed automatically very frame + ///based on the relative linear velocity. + ///If the relative velocity it zero, it will automatically compute a friction direction. + + ///You can also enable two friction directions, using the SOLVER_USE_2_FRICTION_DIRECTIONS. + ///In that case, the second friction direction will be orthogonal to both contact normal and first friction direction. + /// + ///If you choose SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION, then the friction will be independent from the relative projected velocity. + /// + ///The user can manually override the friction directions for certain contacts using a contact callback, + ///and set the cp.m_lateralFrictionInitialized to true + ///In that case, you can set the target relative motion in each friction direction (cp.m_contactMotion1 and cp.m_contactMotion2) + ///this will give a conveyor belt effect + /// + if (!(infoGlobal.m_solverMode & SOLVER_ENABLE_FRICTION_DIRECTION_CACHING) || !cp.m_lateralFrictionInitialized) + {/* + cp.m_lateralFrictionDir1 = vel - cp.m_normalWorldOnB * rel_vel; + btScalar lat_rel_vel = cp.m_lateralFrictionDir1.length2(); + if (!(infoGlobal.m_solverMode & SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION) && lat_rel_vel > SIMD_EPSILON) + { + cp.m_lateralFrictionDir1 *= 1.f/btSqrt(lat_rel_vel); + if((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) + { + cp.m_lateralFrictionDir2 = cp.m_lateralFrictionDir1.cross(cp.m_normalWorldOnB); + cp.m_lateralFrictionDir2.normalize();//?? + applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION); + applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION); + addMultiBodyFrictionConstraint(cp.m_lateralFrictionDir2,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); + + } + + applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); + applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); + addMultiBodyFrictionConstraint(cp.m_lateralFrictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); + + } else + */ + { + btPlaneSpace1(cp.m_normalWorldOnB,cp.m_lateralFrictionDir1,cp.m_lateralFrictionDir2); + + if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) + { + applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION); + applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION); + addMultiBodyFrictionConstraint(cp.m_lateralFrictionDir2,manifold,frictionIndex,cp,colObj0,colObj1, relaxation,infoGlobal); + } + + applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); + applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); + addMultiBodyFrictionConstraint(cp.m_lateralFrictionDir1,manifold,frictionIndex,cp,colObj0,colObj1, relaxation,infoGlobal); + + if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS) && (infoGlobal.m_solverMode & SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION)) + { + cp.m_lateralFrictionInitialized = true; + } + } + + } else + { + addMultiBodyFrictionConstraint(cp.m_lateralFrictionDir1,manifold,frictionIndex,cp,colObj0,colObj1, relaxation,infoGlobal,cp.m_contactMotion1, cp.m_contactCFM1); + + if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) + addMultiBodyFrictionConstraint(cp.m_lateralFrictionDir2,manifold,frictionIndex,cp,colObj0,colObj1, relaxation, infoGlobal,cp.m_contactMotion2, cp.m_contactCFM2); + + //setMultiBodyFrictionConstraintImpulse( solverConstraint, solverBodyIdA, solverBodyIdB, cp, infoGlobal); + //todo: + solverConstraint.m_appliedImpulse = 0.f; + solverConstraint.m_appliedPushImpulse = 0.f; + } + + +#endif //ENABLE_FRICTION + + } + } +} + +void btMultiBodyConstraintSolver::convertContacts(btPersistentManifold** manifoldPtr,int numManifolds, const btContactSolverInfo& infoGlobal) +{ + btPersistentManifold* manifold = 0; + + for (int i=0;igetBody0()); + const btMultiBodyLinkCollider* fcB = btMultiBodyLinkCollider::upcast(manifold->getBody1()); + if (!fcA && !fcB) + { + //the contact doesn't involve any Featherstone btMultiBody, so deal with the regular btRigidBody/btCollisionObject case + convertContact(manifold,infoGlobal); + } else + { + convertMultiBodyContact(manifold,infoGlobal); + } + } + + //also convert the multibody constraints, if any + + + for (int i=0;icreateConstraintRows(m_multiBodyNonContactConstraints,m_data, infoGlobal); + } + +} + + + +btScalar btMultiBodyConstraintSolver::solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& info, btIDebugDraw* debugDrawer,btDispatcher* dispatcher) +{ + return btSequentialImpulseConstraintSolver::solveGroup(bodies,numBodies,manifold,numManifolds,constraints,numConstraints,info,debugDrawer,dispatcher); +} + + +void btMultiBodyConstraintSolver::solveMultiBodyGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,btMultiBodyConstraint** multiBodyConstraints, int numMultiBodyConstraints, const btContactSolverInfo& info, btIDebugDraw* debugDrawer,btDispatcher* dispatcher) +{ + //printf("solveMultiBodyGroup start\n"); + m_tmpMultiBodyConstraints = multiBodyConstraints; + m_tmpNumMultiBodyConstraints = numMultiBodyConstraints; + + btSequentialImpulseConstraintSolver::solveGroup(bodies,numBodies,manifold,numManifolds,constraints,numConstraints,info,debugDrawer,dispatcher); + + m_tmpMultiBodyConstraints = 0; + m_tmpNumMultiBodyConstraints = 0; + + +} diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h new file mode 100644 index 000000000..0f4cd69c0 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h @@ -0,0 +1,85 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_MULTIBODY_CONSTRAINT_SOLVER_H +#define BT_MULTIBODY_CONSTRAINT_SOLVER_H + +#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h" +#include "btMultiBodySolverConstraint.h" + + +class btMultiBody; + +#include "btMultiBodyConstraint.h" + + + +ATTRIBUTE_ALIGNED16(class) btMultiBodyConstraintSolver : public btSequentialImpulseConstraintSolver +{ + +protected: + + btMultiBodyConstraintArray m_multiBodyNonContactConstraints; + + btMultiBodyConstraintArray m_multiBodyNormalContactConstraints; + btMultiBodyConstraintArray m_multiBodyFrictionContactConstraints; + + btMultiBodyJacobianData m_data; + + //temp storage for multi body constraints for a specific island/group called by 'solveGroup' + btMultiBodyConstraint** m_tmpMultiBodyConstraints; + int m_tmpNumMultiBodyConstraints; + + void resolveSingleConstraintRowGeneric(const btMultiBodySolverConstraint& c); + void resolveSingleConstraintRowGenericMultiBody(const btMultiBodySolverConstraint& c); + + void convertContacts(btPersistentManifold** manifoldPtr,int numManifolds, const btContactSolverInfo& infoGlobal); + btMultiBodySolverConstraint& addMultiBodyFrictionConstraint(const btVector3& normalAxis,btPersistentManifold* manifold,int frictionIndex,btManifoldPoint& cp,btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity=0, btScalar cfmSlip=0); + + + void setupMultiBodyJointLimitConstraint(btMultiBodySolverConstraint& constraintRow, + btScalar* jacA,btScalar* jacB, + btScalar penetration,btScalar combinedFrictionCoeff, btScalar combinedRestitutionCoeff, + const btContactSolverInfo& infoGlobal); + + void setupMultiBodyContactConstraint(btMultiBodySolverConstraint& solverConstraint, + const btVector3& contactNormal, + btManifoldPoint& cp, const btContactSolverInfo& infoGlobal, + btScalar& relaxation, + bool isFriction, btScalar desiredVelocity=0, btScalar cfmSlip=0); + + void convertMultiBodyContact(btPersistentManifold* manifold,const btContactSolverInfo& infoGlobal); + virtual btScalar solveGroupCacheFriendlySetup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); +// virtual btScalar solveGroupCacheFriendlyIterations(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); + + virtual btScalar solveSingleIteration(int iteration, btCollisionObject** bodies ,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); + void applyDeltaVee(btScalar* deltaV, btScalar impulse, int velocityIndex, int ndof); + +public: + + BT_DECLARE_ALIGNED_ALLOCATOR(); + + ///this method should not be called, it was just used during porting/integration of Featherstone btMultiBody, providing backwards compatibility but no support for btMultiBodyConstraint (only contact constraints) + virtual btScalar solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& info, btIDebugDraw* debugDrawer,btDispatcher* dispatcher); + + virtual void solveMultiBodyGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,btMultiBodyConstraint** multiBodyConstraints, int numMultiBodyConstraints, const btContactSolverInfo& info, btIDebugDraw* debugDrawer,btDispatcher* dispatcher); +}; + + + + + +#endif //BT_MULTIBODY_CONSTRAINT_SOLVER_H + diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp new file mode 100644 index 000000000..0910f8f6a --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp @@ -0,0 +1,578 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#include "btMultiBodyDynamicsWorld.h" +#include "btMultiBodyConstraintSolver.h" +#include "btMultiBody.h" +#include "btMultiBodyLinkCollider.h" +#include "BulletCollision/CollisionDispatch/btSimulationIslandManager.h" +#include "LinearMath/btQuickprof.h" +#include "btMultiBodyConstraint.h" + + + + +void btMultiBodyDynamicsWorld::addMultiBody(btMultiBody* body, short group, short mask) +{ + m_multiBodies.push_back(body); + +} + +void btMultiBodyDynamicsWorld::removeMultiBody(btMultiBody* body) +{ + m_multiBodies.remove(body); +} + +void btMultiBodyDynamicsWorld::calculateSimulationIslands() +{ + BT_PROFILE("calculateSimulationIslands"); + + getSimulationIslandManager()->updateActivationState(getCollisionWorld(),getCollisionWorld()->getDispatcher()); + + { + //merge islands based on speculative contact manifolds too + for (int i=0;im_predictiveManifolds.size();i++) + { + btPersistentManifold* manifold = m_predictiveManifolds[i]; + + const btCollisionObject* colObj0 = manifold->getBody0(); + const btCollisionObject* colObj1 = manifold->getBody1(); + + if (((colObj0) && (!(colObj0)->isStaticOrKinematicObject())) && + ((colObj1) && (!(colObj1)->isStaticOrKinematicObject()))) + { + getSimulationIslandManager()->getUnionFind().unite((colObj0)->getIslandTag(),(colObj1)->getIslandTag()); + } + } + } + + { + int i; + int numConstraints = int(m_constraints.size()); + for (i=0;i< numConstraints ; i++ ) + { + btTypedConstraint* constraint = m_constraints[i]; + if (constraint->isEnabled()) + { + const btRigidBody* colObj0 = &constraint->getRigidBodyA(); + const btRigidBody* colObj1 = &constraint->getRigidBodyB(); + + if (((colObj0) && (!(colObj0)->isStaticOrKinematicObject())) && + ((colObj1) && (!(colObj1)->isStaticOrKinematicObject()))) + { + getSimulationIslandManager()->getUnionFind().unite((colObj0)->getIslandTag(),(colObj1)->getIslandTag()); + } + } + } + } + + //merge islands linked by Featherstone link colliders + for (int i=0;igetBaseCollider(); + + for (int b=0;bgetNumLinks();b++) + { + btMultiBodyLinkCollider* cur = body->getLink(b).m_collider; + + if (((cur) && (!(cur)->isStaticOrKinematicObject())) && + ((prev) && (!(prev)->isStaticOrKinematicObject()))) + { + int tagPrev = prev->getIslandTag(); + int tagCur = cur->getIslandTag(); + getSimulationIslandManager()->getUnionFind().unite(tagPrev, tagCur); + } + if (cur && !cur->isStaticOrKinematicObject()) + prev = cur; + + } + } + } + + //merge islands linked by multibody constraints + { + for (int i=0;im_multiBodyConstraints.size();i++) + { + btMultiBodyConstraint* c = m_multiBodyConstraints[i]; + int tagA = c->getIslandIdA(); + int tagB = c->getIslandIdB(); + if (tagA>=0 && tagB>=0) + getSimulationIslandManager()->getUnionFind().unite(tagA, tagB); + } + } + + //Store the island id in each body + getSimulationIslandManager()->storeIslandActivationState(getCollisionWorld()); + +} + + +void btMultiBodyDynamicsWorld::updateActivationState(btScalar timeStep) +{ + BT_PROFILE("btMultiBodyDynamicsWorld::updateActivationState"); + + + + for ( int i=0;icheckMotionAndSleepIfRequired(timeStep); + if (!body->isAwake()) + { + btMultiBodyLinkCollider* col = body->getBaseCollider(); + if (col && col->getActivationState() == ACTIVE_TAG) + { + col->setActivationState( WANTS_DEACTIVATION); + col->setDeactivationTime(0.f); + } + for (int b=0;bgetNumLinks();b++) + { + btMultiBodyLinkCollider* col = body->getLink(b).m_collider; + if (col && col->getActivationState() == ACTIVE_TAG) + { + col->setActivationState( WANTS_DEACTIVATION); + col->setDeactivationTime(0.f); + } + } + } else + { + btMultiBodyLinkCollider* col = body->getBaseCollider(); + if (col && col->getActivationState() != DISABLE_DEACTIVATION) + col->setActivationState( ACTIVE_TAG ); + + for (int b=0;bgetNumLinks();b++) + { + btMultiBodyLinkCollider* col = body->getLink(b).m_collider; + if (col && col->getActivationState() != DISABLE_DEACTIVATION) + col->setActivationState( ACTIVE_TAG ); + } + } + + } + } + + btDiscreteDynamicsWorld::updateActivationState(timeStep); +} + + +SIMD_FORCE_INLINE int btGetConstraintIslandId2(const btTypedConstraint* lhs) +{ + int islandId; + + const btCollisionObject& rcolObj0 = lhs->getRigidBodyA(); + const btCollisionObject& rcolObj1 = lhs->getRigidBodyB(); + islandId= rcolObj0.getIslandTag()>=0?rcolObj0.getIslandTag():rcolObj1.getIslandTag(); + return islandId; + +} + + +class btSortConstraintOnIslandPredicate2 +{ + public: + + bool operator() ( const btTypedConstraint* lhs, const btTypedConstraint* rhs ) const + { + int rIslandId0,lIslandId0; + rIslandId0 = btGetConstraintIslandId2(rhs); + lIslandId0 = btGetConstraintIslandId2(lhs); + return lIslandId0 < rIslandId0; + } +}; + + + +SIMD_FORCE_INLINE int btGetMultiBodyConstraintIslandId(const btMultiBodyConstraint* lhs) +{ + int islandId; + + int islandTagA = lhs->getIslandIdA(); + int islandTagB = lhs->getIslandIdB(); + islandId= islandTagA>=0?islandTagA:islandTagB; + return islandId; + +} + + +class btSortMultiBodyConstraintOnIslandPredicate +{ + public: + + bool operator() ( const btMultiBodyConstraint* lhs, const btMultiBodyConstraint* rhs ) const + { + int rIslandId0,lIslandId0; + rIslandId0 = btGetMultiBodyConstraintIslandId(rhs); + lIslandId0 = btGetMultiBodyConstraintIslandId(lhs); + return lIslandId0 < rIslandId0; + } +}; + +struct MultiBodyInplaceSolverIslandCallback : public btSimulationIslandManager::IslandCallback +{ + btContactSolverInfo* m_solverInfo; + btMultiBodyConstraintSolver* m_solver; + btMultiBodyConstraint** m_multiBodySortedConstraints; + int m_numMultiBodyConstraints; + + btTypedConstraint** m_sortedConstraints; + int m_numConstraints; + btIDebugDraw* m_debugDrawer; + btDispatcher* m_dispatcher; + + btAlignedObjectArray m_bodies; + btAlignedObjectArray m_manifolds; + btAlignedObjectArray m_constraints; + btAlignedObjectArray m_multiBodyConstraints; + + + MultiBodyInplaceSolverIslandCallback( btMultiBodyConstraintSolver* solver, + btDispatcher* dispatcher) + :m_solverInfo(NULL), + m_solver(solver), + m_multiBodySortedConstraints(NULL), + m_numConstraints(0), + m_debugDrawer(NULL), + m_dispatcher(dispatcher) + { + + } + + MultiBodyInplaceSolverIslandCallback& operator=(MultiBodyInplaceSolverIslandCallback& other) + { + btAssert(0); + (void)other; + return *this; + } + + SIMD_FORCE_INLINE void setup ( btContactSolverInfo* solverInfo, btTypedConstraint** sortedConstraints, int numConstraints, btMultiBodyConstraint** sortedMultiBodyConstraints, int numMultiBodyConstraints, btIDebugDraw* debugDrawer) + { + btAssert(solverInfo); + m_solverInfo = solverInfo; + + m_multiBodySortedConstraints = sortedMultiBodyConstraints; + m_numMultiBodyConstraints = numMultiBodyConstraints; + m_sortedConstraints = sortedConstraints; + m_numConstraints = numConstraints; + + m_debugDrawer = debugDrawer; + m_bodies.resize (0); + m_manifolds.resize (0); + m_constraints.resize (0); + m_multiBodyConstraints.resize(0); + } + + + virtual void processIsland(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifolds,int numManifolds, int islandId) + { + if (islandId<0) + { + ///we don't split islands, so all constraints/contact manifolds/bodies are passed into the solver regardless the island id + m_solver->solveMultiBodyGroup( bodies,numBodies,manifolds, numManifolds,m_sortedConstraints, m_numConstraints, &m_multiBodySortedConstraints[0],m_numConstraints,*m_solverInfo,m_debugDrawer,m_dispatcher); + } else + { + //also add all non-contact constraints/joints for this island + btTypedConstraint** startConstraint = 0; + btMultiBodyConstraint** startMultiBodyConstraint = 0; + + int numCurConstraints = 0; + int numCurMultiBodyConstraints = 0; + + int i; + + //find the first constraint for this island + + for (i=0;im_minimumSolverBatchSize<=1) + { + m_solver->solveGroup( bodies,numBodies,manifolds, numManifolds,startConstraint,numCurConstraints,*m_solverInfo,m_debugDrawer,m_dispatcher); + } else + { + + for (i=0;im_solverInfo->m_minimumSolverBatchSize) + { + processConstraints(); + } else + { + //printf("deferred\n"); + } + } + } + } + void processConstraints() + { + + btCollisionObject** bodies = m_bodies.size()? &m_bodies[0]:0; + btPersistentManifold** manifold = m_manifolds.size()?&m_manifolds[0]:0; + btTypedConstraint** constraints = m_constraints.size()?&m_constraints[0]:0; + btMultiBodyConstraint** multiBodyConstraints = m_multiBodyConstraints.size() ? &m_multiBodyConstraints[0] : 0; + + m_solver->solveMultiBodyGroup( bodies,m_bodies.size(),manifold, m_manifolds.size(),constraints, m_constraints.size() ,multiBodyConstraints, m_multiBodyConstraints.size(), *m_solverInfo,m_debugDrawer,m_dispatcher); + m_bodies.resize(0); + m_manifolds.resize(0); + m_constraints.resize(0); + m_multiBodyConstraints.resize(0); + } + +}; + + + +btMultiBodyDynamicsWorld::btMultiBodyDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btMultiBodyConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration) + :btDiscreteDynamicsWorld(dispatcher,pairCache,constraintSolver,collisionConfiguration), + m_multiBodyConstraintSolver(constraintSolver) +{ + //split impulse is not yet supported for Featherstone hierarchies + getSolverInfo().m_splitImpulse = false; + getSolverInfo().m_solverMode |=SOLVER_USE_2_FRICTION_DIRECTIONS; + m_solverMultiBodyIslandCallback = new MultiBodyInplaceSolverIslandCallback(constraintSolver,dispatcher); +} + +btMultiBodyDynamicsWorld::~btMultiBodyDynamicsWorld () +{ + delete m_solverMultiBodyIslandCallback; +} + + + + +void btMultiBodyDynamicsWorld::solveConstraints(btContactSolverInfo& solverInfo) +{ + + btAlignedObjectArray scratch_r; + btAlignedObjectArray scratch_v; + btAlignedObjectArray scratch_m; + + + BT_PROFILE("solveConstraints"); + + m_sortedConstraints.resize( m_constraints.size()); + int i; + for (i=0;isetup(&solverInfo,constraintsPtr,m_sortedConstraints.size(),sortedMultiBodyConstraints,m_sortedMultiBodyConstraints.size(), getDebugDrawer()); + m_constraintSolver->prepareSolve(getCollisionWorld()->getNumCollisionObjects(), getCollisionWorld()->getDispatcher()->getNumManifolds()); + + /// solve all the constraints for this island + m_islandManager->buildAndProcessIslands(getCollisionWorld()->getDispatcher(),getCollisionWorld(),m_solverMultiBodyIslandCallback); + + + { + BT_PROFILE("btMultiBody addForce and stepVelocities"); + for (int i=0;im_multiBodies.size();i++) + { + btMultiBody* bod = m_multiBodies[i]; + + bool isSleeping = false; + + if (bod->getBaseCollider() && bod->getBaseCollider()->getActivationState() == ISLAND_SLEEPING) + { + isSleeping = true; + } + for (int b=0;bgetNumLinks();b++) + { + if (bod->getLink(b).m_collider && bod->getLink(b).m_collider->getActivationState()==ISLAND_SLEEPING) + isSleeping = true; + } + + if (!isSleeping) + { + scratch_r.resize(bod->getNumLinks()+1); + scratch_v.resize(bod->getNumLinks()+1); + scratch_m.resize(bod->getNumLinks()+1); + + bod->clearForcesAndTorques(); + bod->addBaseForce(m_gravity * bod->getBaseMass()); + + for (int j = 0; j < bod->getNumLinks(); ++j) + { + bod->addLinkForce(j, m_gravity * bod->getLinkMass(j)); + } + + bod->stepVelocities(solverInfo.m_timeStep, scratch_r, scratch_v, scratch_m); + } + } + } + + m_solverMultiBodyIslandCallback->processConstraints(); + + m_constraintSolver->allSolved(solverInfo, m_debugDrawer); + +} + +void btMultiBodyDynamicsWorld::integrateTransforms(btScalar timeStep) +{ + btDiscreteDynamicsWorld::integrateTransforms(timeStep); + + { + BT_PROFILE("btMultiBody stepPositions"); + //integrate and update the Featherstone hierarchies + btAlignedObjectArray world_to_local; + btAlignedObjectArray local_origin; + + for (int b=0;bgetBaseCollider() && bod->getBaseCollider()->getActivationState() == ISLAND_SLEEPING) + { + isSleeping = true; + } + for (int b=0;bgetNumLinks();b++) + { + if (bod->getLink(b).m_collider && bod->getLink(b).m_collider->getActivationState()==ISLAND_SLEEPING) + isSleeping = true; + } + + + if (!isSleeping) + { + int nLinks = bod->getNumLinks(); + + ///base + num links + world_to_local.resize(nLinks+1); + local_origin.resize(nLinks+1); + + bod->stepPositions(timeStep); + + + + world_to_local[0] = bod->getWorldToBaseRot(); + local_origin[0] = bod->getBasePos(); + + if (bod->getBaseCollider()) + { + btVector3 posr = local_origin[0]; + float pos[4]={posr.x(),posr.y(),posr.z(),1}; + float quat[4]={-world_to_local[0].x(),-world_to_local[0].y(),-world_to_local[0].z(),world_to_local[0].w()}; + btTransform tr; + tr.setIdentity(); + tr.setOrigin(posr); + tr.setRotation(btQuaternion(quat[0],quat[1],quat[2],quat[3])); + + bod->getBaseCollider()->setWorldTransform(tr); + + } + + for (int k=0;kgetNumLinks();k++) + { + const int parent = bod->getParent(k); + world_to_local[k+1] = bod->getParentToLocalRot(k) * world_to_local[parent+1]; + local_origin[k+1] = local_origin[parent+1] + (quatRotate(world_to_local[k+1].inverse() , bod->getRVector(k))); + } + + + for (int m=0;mgetNumLinks();m++) + { + btMultiBodyLinkCollider* col = bod->getLink(m).m_collider; + if (col) + { + int link = col->m_link; + btAssert(link == m); + + int index = link+1; + + btVector3 posr = local_origin[index]; + float pos[4]={posr.x(),posr.y(),posr.z(),1}; + float quat[4]={-world_to_local[index].x(),-world_to_local[index].y(),-world_to_local[index].z(),world_to_local[index].w()}; + btTransform tr; + tr.setIdentity(); + tr.setOrigin(posr); + tr.setRotation(btQuaternion(quat[0],quat[1],quat[2],quat[3])); + + col->setWorldTransform(tr); + } + } + } else + { + bod->clearVelocities(); + } + } + } +} + + + +void btMultiBodyDynamicsWorld::addMultiBodyConstraint( btMultiBodyConstraint* constraint) +{ + m_multiBodyConstraints.push_back(constraint); +} + +void btMultiBodyDynamicsWorld::removeMultiBodyConstraint( btMultiBodyConstraint* constraint) +{ + m_multiBodyConstraints.remove(constraint); +} diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h new file mode 100644 index 000000000..ad57a346d --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h @@ -0,0 +1,56 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_MULTIBODY_DYNAMICS_WORLD_H +#define BT_MULTIBODY_DYNAMICS_WORLD_H + +#include "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h" + + +class btMultiBody; +class btMultiBodyConstraint; +class btMultiBodyConstraintSolver; +struct MultiBodyInplaceSolverIslandCallback; + +///The btMultiBodyDynamicsWorld adds Featherstone multi body dynamics to Bullet +///This implementation is still preliminary/experimental. +class btMultiBodyDynamicsWorld : public btDiscreteDynamicsWorld +{ +protected: + btAlignedObjectArray m_multiBodies; + btAlignedObjectArray m_multiBodyConstraints; + btAlignedObjectArray m_sortedMultiBodyConstraints; + btMultiBodyConstraintSolver* m_multiBodyConstraintSolver; + MultiBodyInplaceSolverIslandCallback* m_solverMultiBodyIslandCallback; + + virtual void calculateSimulationIslands(); + virtual void updateActivationState(btScalar timeStep); + virtual void solveConstraints(btContactSolverInfo& solverInfo); + virtual void integrateTransforms(btScalar timeStep); +public: + + btMultiBodyDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btMultiBodyConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration); + + virtual ~btMultiBodyDynamicsWorld (); + + virtual void addMultiBody(btMultiBody* body, short group= btBroadphaseProxy::DefaultFilter, short mask=btBroadphaseProxy::AllFilter); + + virtual void removeMultiBody(btMultiBody* body); + + virtual void addMultiBodyConstraint( btMultiBodyConstraint* constraint); + + virtual void removeMultiBodyConstraint( btMultiBodyConstraint* constraint); +}; +#endif //BT_MULTIBODY_DYNAMICS_WORLD_H diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp new file mode 100644 index 000000000..ea309e885 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp @@ -0,0 +1,133 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +///This file was written by Erwin Coumans + +#include "btMultiBodyJointLimitConstraint.h" +#include "btMultiBody.h" +#include "btMultiBodyLinkCollider.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" + + +btMultiBodyJointLimitConstraint::btMultiBodyJointLimitConstraint(btMultiBody* body, int link, btScalar lower, btScalar upper) + :btMultiBodyConstraint(body,body,link,link,2,true), + m_lowerBound(lower), + m_upperBound(upper) +{ + // the data.m_jacobians never change, so may as well + // initialize them here + + // note: we rely on the fact that data.m_jacobians are + // always initialized to zero by the Constraint ctor + + // row 0: the lower bound + jacobianA(0)[6 + link] = 1; + + // row 1: the upper bound + jacobianB(1)[6 + link] = -1; +} +btMultiBodyJointLimitConstraint::~btMultiBodyJointLimitConstraint() +{ +} + +int btMultiBodyJointLimitConstraint::getIslandIdA() const +{ + btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); + if (col) + return col->getIslandTag(); + for (int i=0;igetNumLinks();i++) + { + if (m_bodyA->getLink(i).m_collider) + return m_bodyA->getLink(i).m_collider->getIslandTag(); + } + return -1; +} + +int btMultiBodyJointLimitConstraint::getIslandIdB() const +{ + btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); + if (col) + return col->getIslandTag(); + + for (int i=0;igetNumLinks();i++) + { + col = m_bodyB->getLink(i).m_collider; + if (col) + return col->getIslandTag(); + } + return -1; +} + + +void btMultiBodyJointLimitConstraint::createConstraintRows(btMultiBodyConstraintArray& constraintRows, + btMultiBodyJacobianData& data, + const btContactSolverInfo& infoGlobal) +{ + // only positions need to be updated -- data.m_jacobians and force + // directions were set in the ctor and never change. + + // row 0: the lower bound + setPosition(0, m_bodyA->getJointPos(m_linkA) - m_lowerBound); + + // row 1: the upper bound + setPosition(1, m_upperBound - m_bodyA->getJointPos(m_linkA)); + + for (int row=0;row infoGlobal.m_splitImpulsePenetrationThreshold)) + { + erp = infoGlobal.m_erp; + } + if (penetration>0) + { + positionalError = 0; + velocityError = -penetration / infoGlobal.m_timeStep; + } else + { + positionalError = -penetration * erp/infoGlobal.m_timeStep; + } + + btScalar penetrationImpulse = positionalError*constraintRow.m_jacDiagABInv; + btScalar velocityImpulse = velocityError *constraintRow.m_jacDiagABInv; + if (!infoGlobal.m_splitImpulse || (penetration > infoGlobal.m_splitImpulsePenetrationThreshold)) + { + //combine position and velocity into rhs + constraintRow.m_rhs = penetrationImpulse+velocityImpulse; + constraintRow.m_rhsPenetration = 0.f; + + } else + { + //split position and velocity into rhs and m_rhsPenetration + constraintRow.m_rhs = velocityImpulse; + constraintRow.m_rhsPenetration = penetrationImpulse; + } + } + } + +} + + + + diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.h b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.h new file mode 100644 index 000000000..0c7fc1708 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.h @@ -0,0 +1,44 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_MULTIBODY_JOINT_LIMIT_CONSTRAINT_H +#define BT_MULTIBODY_JOINT_LIMIT_CONSTRAINT_H + +#include "btMultiBodyConstraint.h" +struct btSolverInfo; + +class btMultiBodyJointLimitConstraint : public btMultiBodyConstraint +{ +protected: + + btScalar m_lowerBound; + btScalar m_upperBound; +public: + + btMultiBodyJointLimitConstraint(btMultiBody* body, int link, btScalar lower, btScalar upper); + virtual ~btMultiBodyJointLimitConstraint(); + + virtual int getIslandIdA() const; + virtual int getIslandIdB() const; + + virtual void createConstraintRows(btMultiBodyConstraintArray& constraintRows, + btMultiBodyJacobianData& data, + const btContactSolverInfo& infoGlobal); + + +}; + +#endif //BT_MULTIBODY_JOINT_LIMIT_CONSTRAINT_H + diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp new file mode 100644 index 000000000..ab5a43023 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp @@ -0,0 +1,89 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +///This file was written by Erwin Coumans + +#include "btMultiBodyJointMotor.h" +#include "btMultiBody.h" +#include "btMultiBodyLinkCollider.h" +#include "BulletCollision/CollisionDispatch/btCollisionObject.h" + + +btMultiBodyJointMotor::btMultiBodyJointMotor(btMultiBody* body, int link, btScalar desiredVelocity, btScalar maxMotorImpulse) + :btMultiBodyConstraint(body,body,link,link,1,true), + m_desiredVelocity(desiredVelocity) +{ + m_maxAppliedImpulse = maxMotorImpulse; + // the data.m_jacobians never change, so may as well + // initialize them here + + // note: we rely on the fact that data.m_jacobians are + // always initialized to zero by the Constraint ctor + + // row 0: the lower bound + jacobianA(0)[6 + link] = 1; +} +btMultiBodyJointMotor::~btMultiBodyJointMotor() +{ +} + +int btMultiBodyJointMotor::getIslandIdA() const +{ + btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); + if (col) + return col->getIslandTag(); + for (int i=0;igetNumLinks();i++) + { + if (m_bodyA->getLink(i).m_collider) + return m_bodyA->getLink(i).m_collider->getIslandTag(); + } + return -1; +} + +int btMultiBodyJointMotor::getIslandIdB() const +{ + btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); + if (col) + return col->getIslandTag(); + + for (int i=0;igetNumLinks();i++) + { + col = m_bodyB->getLink(i).m_collider; + if (col) + return col->getIslandTag(); + } + return -1; +} + + +void btMultiBodyJointMotor::createConstraintRows(btMultiBodyConstraintArray& constraintRows, + btMultiBodyJacobianData& data, + const btContactSolverInfo& infoGlobal) +{ + // only positions need to be updated -- data.m_jacobians and force + // directions were set in the ctor and never change. + + + + for (int row=0;row=0 || (multiBody && !multiBody->hasFixedBase())) + { + m_collisionFlags &= (~btCollisionObject::CF_STATIC_OBJECT); + } + // else + //{ + // m_collisionFlags |= (btCollisionObject::CF_STATIC_OBJECT); + //} + + m_internalType = CO_FEATHERSTONE_LINK; + } + static btMultiBodyLinkCollider* upcast(btCollisionObject* colObj) + { + if (colObj->getInternalType()&btCollisionObject::CO_FEATHERSTONE_LINK) + return (btMultiBodyLinkCollider*)colObj; + return 0; + } + static const btMultiBodyLinkCollider* upcast(const btCollisionObject* colObj) + { + if (colObj->getInternalType()&btCollisionObject::CO_FEATHERSTONE_LINK) + return (btMultiBodyLinkCollider*)colObj; + return 0; + } + + virtual bool checkCollideWithOverride(const btCollisionObject* co) const + { + const btMultiBodyLinkCollider* other = btMultiBodyLinkCollider::upcast(co); + if (!other) + return true; + if (other->m_multiBody != this->m_multiBody) + return true; + if (!m_multiBody->hasSelfCollision()) + return false; + + //check if 'link' has collision disabled + if (m_link>=0) + { + const btMultibodyLink& link = m_multiBody->getLink(this->m_link); + if ((link.m_flags&BT_MULTIBODYLINKFLAGS_DISABLE_PARENT_COLLISION) && link.parent == other->m_link) + return false; + } + + if (other->m_link>=0) + { + const btMultibodyLink& otherLink = other->m_multiBody->getLink(other->m_link); + if ((otherLink.m_flags& BT_MULTIBODYLINKFLAGS_DISABLE_PARENT_COLLISION) && otherLink.parent == this->m_link) + return false; + } + return true; + } +}; + +#endif //BT_FEATHERSTONE_LINK_COLLIDER_H + diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp new file mode 100644 index 000000000..f66900491 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp @@ -0,0 +1,143 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +///This file was written by Erwin Coumans + +#include "btMultiBodyPoint2Point.h" +#include "btMultiBodyLinkCollider.h" +#include "BulletDynamics/Dynamics/btRigidBody.h" + +btMultiBodyPoint2Point::btMultiBodyPoint2Point(btMultiBody* body, int link, btRigidBody* bodyB, const btVector3& pivotInA, const btVector3& pivotInB) + :btMultiBodyConstraint(body,0,link,-1,3,false), + m_rigidBodyA(0), + m_rigidBodyB(bodyB), + m_pivotInA(pivotInA), + m_pivotInB(pivotInB) +{ +} + +btMultiBodyPoint2Point::btMultiBodyPoint2Point(btMultiBody* bodyA, int linkA, btMultiBody* bodyB, int linkB, const btVector3& pivotInA, const btVector3& pivotInB) + :btMultiBodyConstraint(bodyA,bodyB,linkA,linkB,3,false), + m_rigidBodyA(0), + m_rigidBodyB(0), + m_pivotInA(pivotInA), + m_pivotInB(pivotInB) +{ +} + + +btMultiBodyPoint2Point::~btMultiBodyPoint2Point() +{ +} + + +int btMultiBodyPoint2Point::getIslandIdA() const +{ + if (m_rigidBodyA) + return m_rigidBodyA->getIslandTag(); + + if (m_bodyA) + { + btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); + if (col) + return col->getIslandTag(); + for (int i=0;igetNumLinks();i++) + { + if (m_bodyA->getLink(i).m_collider) + return m_bodyA->getLink(i).m_collider->getIslandTag(); + } + } + return -1; +} + +int btMultiBodyPoint2Point::getIslandIdB() const +{ + if (m_rigidBodyB) + return m_rigidBodyB->getIslandTag(); + if (m_bodyB) + { + btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); + if (col) + return col->getIslandTag(); + + for (int i=0;igetNumLinks();i++) + { + col = m_bodyB->getLink(i).m_collider; + if (col) + return col->getIslandTag(); + } + } + return -1; +} + + + +void btMultiBodyPoint2Point::createConstraintRows(btMultiBodyConstraintArray& constraintRows, + btMultiBodyJacobianData& data, + const btContactSolverInfo& infoGlobal) +{ + +// int i=1; + for (int i=0;i<3;i++) + { + + btMultiBodySolverConstraint& constraintRow = constraintRows.expandNonInitializing(); + + constraintRow.m_solverBodyIdA = data.m_fixedBodyId; + constraintRow.m_solverBodyIdB = data.m_fixedBodyId; + + + btVector3 contactNormalOnB(0,0,0); + contactNormalOnB[i] = -1; + + btScalar penetration = 0; + + // Convert local points back to world + btVector3 pivotAworld = m_pivotInA; + if (m_rigidBodyA) + { + + constraintRow.m_solverBodyIdA = m_rigidBodyA->getCompanionId(); + pivotAworld = m_rigidBodyA->getCenterOfMassTransform()*m_pivotInA; + } else + { + if (m_bodyA) + pivotAworld = m_bodyA->localPosToWorld(m_linkA, m_pivotInA); + } + btVector3 pivotBworld = m_pivotInB; + if (m_rigidBodyB) + { + constraintRow.m_solverBodyIdB = m_rigidBodyB->getCompanionId(); + pivotBworld = m_rigidBodyB->getCenterOfMassTransform()*m_pivotInB; + } else + { + if (m_bodyB) + pivotBworld = m_bodyB->localPosToWorld(m_linkB, m_pivotInB); + + } + btScalar position = (pivotAworld-pivotBworld).dot(contactNormalOnB); + btScalar relaxation = 1.f; + fillMultiBodyConstraintMixed(constraintRow, data, + contactNormalOnB, + pivotAworld, pivotBworld, + position, + infoGlobal, + relaxation, + false); + constraintRow.m_lowerLimit = -m_maxAppliedImpulse; + constraintRow.m_upperLimit = m_maxAppliedImpulse; + + } +} diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.h b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.h new file mode 100644 index 000000000..26ca12b40 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.h @@ -0,0 +1,60 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +///This file was written by Erwin Coumans + +#ifndef BT_MULTIBODY_POINT2POINT_H +#define BT_MULTIBODY_POINT2POINT_H + +#include "btMultiBodyConstraint.h" + +class btMultiBodyPoint2Point : public btMultiBodyConstraint +{ +protected: + + btRigidBody* m_rigidBodyA; + btRigidBody* m_rigidBodyB; + btVector3 m_pivotInA; + btVector3 m_pivotInB; + + +public: + + btMultiBodyPoint2Point(btMultiBody* body, int link, btRigidBody* bodyB, const btVector3& pivotInA, const btVector3& pivotInB); + btMultiBodyPoint2Point(btMultiBody* bodyA, int linkA, btMultiBody* bodyB, int linkB, const btVector3& pivotInA, const btVector3& pivotInB); + + virtual ~btMultiBodyPoint2Point(); + + virtual int getIslandIdA() const; + virtual int getIslandIdB() const; + + virtual void createConstraintRows(btMultiBodyConstraintArray& constraintRows, + btMultiBodyJacobianData& data, + const btContactSolverInfo& infoGlobal); + + const btVector3& getPivotInB() const + { + return m_pivotInB; + } + + void setPivotInB(const btVector3& pivotInB) + { + m_pivotInB = pivotInB; + } + + +}; + +#endif //BT_MULTIBODY_POINT2POINT_H diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodySolverConstraint.h b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodySolverConstraint.h new file mode 100644 index 000000000..cf06dfb9e --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodySolverConstraint.h @@ -0,0 +1,82 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef BT_MULTIBODY_SOLVER_CONSTRAINT_H +#define BT_MULTIBODY_SOLVER_CONSTRAINT_H + +#include "LinearMath/btVector3.h" +#include "LinearMath/btAlignedObjectArray.h" + +class btMultiBody; +#include "BulletDynamics/ConstraintSolver/btSolverBody.h" +#include "BulletDynamics/ConstraintSolver/btContactSolverInfo.h" + +///1D constraint along a normal axis between bodyA and bodyB. It can be combined to solve contact and friction constraints. +ATTRIBUTE_ALIGNED16 (struct) btMultiBodySolverConstraint +{ + BT_DECLARE_ALIGNED_ALLOCATOR(); + + + int m_deltaVelAindex;//more generic version of m_relpos1CrossNormal/m_contactNormal1 + btVector3 m_relpos1CrossNormal; + btVector3 m_contactNormal1; + int m_jacAindex; + + int m_deltaVelBindex; + btVector3 m_relpos2CrossNormal; + btVector3 m_contactNormal2; //usually m_contactNormal2 == -m_contactNormal1, but not always + int m_jacBindex; + + btVector3 m_angularComponentA; + btVector3 m_angularComponentB; + + mutable btSimdScalar m_appliedPushImpulse; + mutable btSimdScalar m_appliedImpulse; + + btScalar m_friction; + btScalar m_jacDiagABInv; + btScalar m_rhs; + btScalar m_cfm; + + btScalar m_lowerLimit; + btScalar m_upperLimit; + btScalar m_rhsPenetration; + union + { + void* m_originalContactPoint; + btScalar m_unusedPadding4; + }; + + int m_overrideNumSolverIterations; + int m_frictionIndex; + + int m_solverBodyIdA; + btMultiBody* m_multiBodyA; + int m_linkA; + + int m_solverBodyIdB; + btMultiBody* m_multiBodyB; + int m_linkB; + + enum btSolverConstraintType + { + BT_SOLVER_CONTACT_1D = 0, + BT_SOLVER_FRICTION_1D + }; +}; + +typedef btAlignedObjectArray btMultiBodyConstraintArray; + +#endif //BT_MULTIBODY_SOLVER_CONSTRAINT_H diff --git a/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigLCP.cpp b/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigLCP.cpp new file mode 100644 index 000000000..3bf7b5c13 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigLCP.cpp @@ -0,0 +1,2079 @@ +/************************************************************************* +* * +* Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * +* All rights reserved. Email: russ@q12.org Web: www.q12.org * +* * +* This library is free software; you can redistribute it and/or * +* modify it under the terms of EITHER: * +* (1) The GNU Lesser General Public License as published by the Free * +* Software Foundation; either version 2.1 of the License, or (at * +* your option) any later version. The text of the GNU Lesser * +* General Public License is included with this library in the * +* file LICENSE.TXT. * +* (2) The BSD-style license that is included with this library in * +* the file LICENSE-BSD.TXT. * +* * +* This library is distributed in the hope that it will be useful, * +* but WITHOUT ANY WARRANTY; without even the implied warranty of * +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files * +* LICENSE.TXT and LICENSE-BSD.TXT for more details. * +* * +*************************************************************************/ + +/* + + +THE ALGORITHM +------------- + +solve A*x = b+w, with x and w subject to certain LCP conditions. +each x(i),w(i) must lie on one of the three line segments in the following +diagram. each line segment corresponds to one index set : + + w(i) + /|\ | : + | | : + | |i in N : + w>0 | |state[i]=0 : + | | : + | | : i in C + w=0 + +-----------------------+ + | : | + | : | + w<0 | : |i in N + | : |state[i]=1 + | : | + | : | + +-------|-----------|-----------|----------> x(i) + lo 0 hi + +the Dantzig algorithm proceeds as follows: + for i=1:n + * if (x(i),w(i)) is not on the line, push x(i) and w(i) positive or + negative towards the line. as this is done, the other (x(j),w(j)) + for j= 0. this makes the algorithm a bit +simpler, because the starting point for x(i),w(i) is always on the dotted +line x=0 and x will only ever increase in one direction, so it can only hit +two out of the three line segments. + + +NOTES +----- + +this is an implementation of "lcp_dantzig2_ldlt.m" and "lcp_dantzig_lohi.m". +the implementation is split into an LCP problem object (btLCP) and an LCP +driver function. most optimization occurs in the btLCP object. + +a naive implementation of the algorithm requires either a lot of data motion +or a lot of permutation-array lookup, because we are constantly re-ordering +rows and columns. to avoid this and make a more optimized algorithm, a +non-trivial data structure is used to represent the matrix A (this is +implemented in the fast version of the btLCP object). + +during execution of this algorithm, some indexes in A are clamped (set C), +some are non-clamped (set N), and some are "don't care" (where x=0). +A,x,b,w (and other problem vectors) are permuted such that the clamped +indexes are first, the unclamped indexes are next, and the don't-care +indexes are last. this permutation is recorded in the array `p'. +initially p = 0..n-1, and as the rows and columns of A,x,b,w are swapped, +the corresponding elements of p are swapped. + +because the C and N elements are grouped together in the rows of A, we can do +lots of work with a fast dot product function. if A,x,etc were not permuted +and we only had a permutation array, then those dot products would be much +slower as we would have a permutation array lookup in some inner loops. + +A is accessed through an array of row pointers, so that element (i,j) of the +permuted matrix is A[i][j]. this makes row swapping fast. for column swapping +we still have to actually move the data. + +during execution of this algorithm we maintain an L*D*L' factorization of +the clamped submatrix of A (call it `AC') which is the top left nC*nC +submatrix of A. there are two ways we could arrange the rows/columns in AC. + +(1) AC is always permuted such that L*D*L' = AC. this causes a problem +when a row/column is removed from C, because then all the rows/columns of A +between the deleted index and the end of C need to be rotated downward. +this results in a lot of data motion and slows things down. +(2) L*D*L' is actually a factorization of a *permutation* of AC (which is +itself a permutation of the underlying A). this is what we do - the +permutation is recorded in the vector C. call this permutation A[C,C]. +when a row/column is removed from C, all we have to do is swap two +rows/columns and manipulate C. + +*/ + + +#include "btDantzigLCP.h" + +#include //memcpy + +bool s_error = false; + +//*************************************************************************** +// code generation parameters + + +#define btLCP_FAST // use fast btLCP object + +// option 1 : matrix row pointers (less data copying) +#define BTROWPTRS +#define BTATYPE btScalar ** +#define BTAROW(i) (m_A[i]) + +// option 2 : no matrix row pointers (slightly faster inner loops) +//#define NOROWPTRS +//#define BTATYPE btScalar * +//#define BTAROW(i) (m_A+(i)*m_nskip) + +#define BTNUB_OPTIMIZATIONS + + + +/* solve L*X=B, with B containing 1 right hand sides. + * L is an n*n lower triangular matrix with ones on the diagonal. + * L is stored by rows and its leading dimension is lskip. + * B is an n*1 matrix that contains the right hand sides. + * B is stored by columns and its leading dimension is also lskip. + * B is overwritten with X. + * this processes blocks of 2*2. + * if this is in the factorizer source file, n must be a multiple of 2. + */ + +static void btSolveL1_1 (const btScalar *L, btScalar *B, int n, int lskip1) +{ + /* declare variables - Z matrix, p and q vectors, etc */ + btScalar Z11,m11,Z21,m21,p1,q1,p2,*ex; + const btScalar *ell; + int i,j; + /* compute all 2 x 1 blocks of X */ + for (i=0; i < n; i+=2) { + /* compute all 2 x 1 block of X, from rows i..i+2-1 */ + /* set the Z matrix to 0 */ + Z11=0; + Z21=0; + ell = L + i*lskip1; + ex = B; + /* the inner loop that computes outer products and adds them to Z */ + for (j=i-2; j >= 0; j -= 2) { + /* compute outer product and add it to the Z matrix */ + p1=ell[0]; + q1=ex[0]; + m11 = p1 * q1; + p2=ell[lskip1]; + m21 = p2 * q1; + Z11 += m11; + Z21 += m21; + /* compute outer product and add it to the Z matrix */ + p1=ell[1]; + q1=ex[1]; + m11 = p1 * q1; + p2=ell[1+lskip1]; + m21 = p2 * q1; + /* advance pointers */ + ell += 2; + ex += 2; + Z11 += m11; + Z21 += m21; + /* end of inner loop */ + } + /* compute left-over iterations */ + j += 2; + for (; j > 0; j--) { + /* compute outer product and add it to the Z matrix */ + p1=ell[0]; + q1=ex[0]; + m11 = p1 * q1; + p2=ell[lskip1]; + m21 = p2 * q1; + /* advance pointers */ + ell += 1; + ex += 1; + Z11 += m11; + Z21 += m21; + } + /* finish computing the X(i) block */ + Z11 = ex[0] - Z11; + ex[0] = Z11; + p1 = ell[lskip1]; + Z21 = ex[1] - Z21 - p1*Z11; + ex[1] = Z21; + /* end of outer loop */ + } +} + +/* solve L*X=B, with B containing 2 right hand sides. + * L is an n*n lower triangular matrix with ones on the diagonal. + * L is stored by rows and its leading dimension is lskip. + * B is an n*2 matrix that contains the right hand sides. + * B is stored by columns and its leading dimension is also lskip. + * B is overwritten with X. + * this processes blocks of 2*2. + * if this is in the factorizer source file, n must be a multiple of 2. + */ + +static void btSolveL1_2 (const btScalar *L, btScalar *B, int n, int lskip1) +{ + /* declare variables - Z matrix, p and q vectors, etc */ + btScalar Z11,m11,Z12,m12,Z21,m21,Z22,m22,p1,q1,p2,q2,*ex; + const btScalar *ell; + int i,j; + /* compute all 2 x 2 blocks of X */ + for (i=0; i < n; i+=2) { + /* compute all 2 x 2 block of X, from rows i..i+2-1 */ + /* set the Z matrix to 0 */ + Z11=0; + Z12=0; + Z21=0; + Z22=0; + ell = L + i*lskip1; + ex = B; + /* the inner loop that computes outer products and adds them to Z */ + for (j=i-2; j >= 0; j -= 2) { + /* compute outer product and add it to the Z matrix */ + p1=ell[0]; + q1=ex[0]; + m11 = p1 * q1; + q2=ex[lskip1]; + m12 = p1 * q2; + p2=ell[lskip1]; + m21 = p2 * q1; + m22 = p2 * q2; + Z11 += m11; + Z12 += m12; + Z21 += m21; + Z22 += m22; + /* compute outer product and add it to the Z matrix */ + p1=ell[1]; + q1=ex[1]; + m11 = p1 * q1; + q2=ex[1+lskip1]; + m12 = p1 * q2; + p2=ell[1+lskip1]; + m21 = p2 * q1; + m22 = p2 * q2; + /* advance pointers */ + ell += 2; + ex += 2; + Z11 += m11; + Z12 += m12; + Z21 += m21; + Z22 += m22; + /* end of inner loop */ + } + /* compute left-over iterations */ + j += 2; + for (; j > 0; j--) { + /* compute outer product and add it to the Z matrix */ + p1=ell[0]; + q1=ex[0]; + m11 = p1 * q1; + q2=ex[lskip1]; + m12 = p1 * q2; + p2=ell[lskip1]; + m21 = p2 * q1; + m22 = p2 * q2; + /* advance pointers */ + ell += 1; + ex += 1; + Z11 += m11; + Z12 += m12; + Z21 += m21; + Z22 += m22; + } + /* finish computing the X(i) block */ + Z11 = ex[0] - Z11; + ex[0] = Z11; + Z12 = ex[lskip1] - Z12; + ex[lskip1] = Z12; + p1 = ell[lskip1]; + Z21 = ex[1] - Z21 - p1*Z11; + ex[1] = Z21; + Z22 = ex[1+lskip1] - Z22 - p1*Z12; + ex[1+lskip1] = Z22; + /* end of outer loop */ + } +} + + +void btFactorLDLT (btScalar *A, btScalar *d, int n, int nskip1) +{ + int i,j; + btScalar sum,*ell,*dee,dd,p1,p2,q1,q2,Z11,m11,Z21,m21,Z22,m22; + if (n < 1) return; + + for (i=0; i<=n-2; i += 2) { + /* solve L*(D*l)=a, l is scaled elements in 2 x i block at A(i,0) */ + btSolveL1_2 (A,A+i*nskip1,i,nskip1); + /* scale the elements in a 2 x i block at A(i,0), and also */ + /* compute Z = the outer product matrix that we'll need. */ + Z11 = 0; + Z21 = 0; + Z22 = 0; + ell = A+i*nskip1; + dee = d; + for (j=i-6; j >= 0; j -= 6) { + p1 = ell[0]; + p2 = ell[nskip1]; + dd = dee[0]; + q1 = p1*dd; + q2 = p2*dd; + ell[0] = q1; + ell[nskip1] = q2; + m11 = p1*q1; + m21 = p2*q1; + m22 = p2*q2; + Z11 += m11; + Z21 += m21; + Z22 += m22; + p1 = ell[1]; + p2 = ell[1+nskip1]; + dd = dee[1]; + q1 = p1*dd; + q2 = p2*dd; + ell[1] = q1; + ell[1+nskip1] = q2; + m11 = p1*q1; + m21 = p2*q1; + m22 = p2*q2; + Z11 += m11; + Z21 += m21; + Z22 += m22; + p1 = ell[2]; + p2 = ell[2+nskip1]; + dd = dee[2]; + q1 = p1*dd; + q2 = p2*dd; + ell[2] = q1; + ell[2+nskip1] = q2; + m11 = p1*q1; + m21 = p2*q1; + m22 = p2*q2; + Z11 += m11; + Z21 += m21; + Z22 += m22; + p1 = ell[3]; + p2 = ell[3+nskip1]; + dd = dee[3]; + q1 = p1*dd; + q2 = p2*dd; + ell[3] = q1; + ell[3+nskip1] = q2; + m11 = p1*q1; + m21 = p2*q1; + m22 = p2*q2; + Z11 += m11; + Z21 += m21; + Z22 += m22; + p1 = ell[4]; + p2 = ell[4+nskip1]; + dd = dee[4]; + q1 = p1*dd; + q2 = p2*dd; + ell[4] = q1; + ell[4+nskip1] = q2; + m11 = p1*q1; + m21 = p2*q1; + m22 = p2*q2; + Z11 += m11; + Z21 += m21; + Z22 += m22; + p1 = ell[5]; + p2 = ell[5+nskip1]; + dd = dee[5]; + q1 = p1*dd; + q2 = p2*dd; + ell[5] = q1; + ell[5+nskip1] = q2; + m11 = p1*q1; + m21 = p2*q1; + m22 = p2*q2; + Z11 += m11; + Z21 += m21; + Z22 += m22; + ell += 6; + dee += 6; + } + /* compute left-over iterations */ + j += 6; + for (; j > 0; j--) { + p1 = ell[0]; + p2 = ell[nskip1]; + dd = dee[0]; + q1 = p1*dd; + q2 = p2*dd; + ell[0] = q1; + ell[nskip1] = q2; + m11 = p1*q1; + m21 = p2*q1; + m22 = p2*q2; + Z11 += m11; + Z21 += m21; + Z22 += m22; + ell++; + dee++; + } + /* solve for diagonal 2 x 2 block at A(i,i) */ + Z11 = ell[0] - Z11; + Z21 = ell[nskip1] - Z21; + Z22 = ell[1+nskip1] - Z22; + dee = d + i; + /* factorize 2 x 2 block Z,dee */ + /* factorize row 1 */ + dee[0] = btRecip(Z11); + /* factorize row 2 */ + sum = 0; + q1 = Z21; + q2 = q1 * dee[0]; + Z21 = q2; + sum += q1*q2; + dee[1] = btRecip(Z22 - sum); + /* done factorizing 2 x 2 block */ + ell[nskip1] = Z21; + } + /* compute the (less than 2) rows at the bottom */ + switch (n-i) { + case 0: + break; + + case 1: + btSolveL1_1 (A,A+i*nskip1,i,nskip1); + /* scale the elements in a 1 x i block at A(i,0), and also */ + /* compute Z = the outer product matrix that we'll need. */ + Z11 = 0; + ell = A+i*nskip1; + dee = d; + for (j=i-6; j >= 0; j -= 6) { + p1 = ell[0]; + dd = dee[0]; + q1 = p1*dd; + ell[0] = q1; + m11 = p1*q1; + Z11 += m11; + p1 = ell[1]; + dd = dee[1]; + q1 = p1*dd; + ell[1] = q1; + m11 = p1*q1; + Z11 += m11; + p1 = ell[2]; + dd = dee[2]; + q1 = p1*dd; + ell[2] = q1; + m11 = p1*q1; + Z11 += m11; + p1 = ell[3]; + dd = dee[3]; + q1 = p1*dd; + ell[3] = q1; + m11 = p1*q1; + Z11 += m11; + p1 = ell[4]; + dd = dee[4]; + q1 = p1*dd; + ell[4] = q1; + m11 = p1*q1; + Z11 += m11; + p1 = ell[5]; + dd = dee[5]; + q1 = p1*dd; + ell[5] = q1; + m11 = p1*q1; + Z11 += m11; + ell += 6; + dee += 6; + } + /* compute left-over iterations */ + j += 6; + for (; j > 0; j--) { + p1 = ell[0]; + dd = dee[0]; + q1 = p1*dd; + ell[0] = q1; + m11 = p1*q1; + Z11 += m11; + ell++; + dee++; + } + /* solve for diagonal 1 x 1 block at A(i,i) */ + Z11 = ell[0] - Z11; + dee = d + i; + /* factorize 1 x 1 block Z,dee */ + /* factorize row 1 */ + dee[0] = btRecip(Z11); + /* done factorizing 1 x 1 block */ + break; + + //default: *((char*)0)=0; /* this should never happen! */ + } +} + +/* solve L*X=B, with B containing 1 right hand sides. + * L is an n*n lower triangular matrix with ones on the diagonal. + * L is stored by rows and its leading dimension is lskip. + * B is an n*1 matrix that contains the right hand sides. + * B is stored by columns and its leading dimension is also lskip. + * B is overwritten with X. + * this processes blocks of 4*4. + * if this is in the factorizer source file, n must be a multiple of 4. + */ + +void btSolveL1 (const btScalar *L, btScalar *B, int n, int lskip1) +{ + /* declare variables - Z matrix, p and q vectors, etc */ + btScalar Z11,Z21,Z31,Z41,p1,q1,p2,p3,p4,*ex; + const btScalar *ell; + int lskip2,lskip3,i,j; + /* compute lskip values */ + lskip2 = 2*lskip1; + lskip3 = 3*lskip1; + /* compute all 4 x 1 blocks of X */ + for (i=0; i <= n-4; i+=4) { + /* compute all 4 x 1 block of X, from rows i..i+4-1 */ + /* set the Z matrix to 0 */ + Z11=0; + Z21=0; + Z31=0; + Z41=0; + ell = L + i*lskip1; + ex = B; + /* the inner loop that computes outer products and adds them to Z */ + for (j=i-12; j >= 0; j -= 12) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + p2=ell[lskip1]; + p3=ell[lskip2]; + p4=ell[lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[1]; + q1=ex[1]; + p2=ell[1+lskip1]; + p3=ell[1+lskip2]; + p4=ell[1+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[2]; + q1=ex[2]; + p2=ell[2+lskip1]; + p3=ell[2+lskip2]; + p4=ell[2+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[3]; + q1=ex[3]; + p2=ell[3+lskip1]; + p3=ell[3+lskip2]; + p4=ell[3+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[4]; + q1=ex[4]; + p2=ell[4+lskip1]; + p3=ell[4+lskip2]; + p4=ell[4+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[5]; + q1=ex[5]; + p2=ell[5+lskip1]; + p3=ell[5+lskip2]; + p4=ell[5+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[6]; + q1=ex[6]; + p2=ell[6+lskip1]; + p3=ell[6+lskip2]; + p4=ell[6+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[7]; + q1=ex[7]; + p2=ell[7+lskip1]; + p3=ell[7+lskip2]; + p4=ell[7+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[8]; + q1=ex[8]; + p2=ell[8+lskip1]; + p3=ell[8+lskip2]; + p4=ell[8+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[9]; + q1=ex[9]; + p2=ell[9+lskip1]; + p3=ell[9+lskip2]; + p4=ell[9+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[10]; + q1=ex[10]; + p2=ell[10+lskip1]; + p3=ell[10+lskip2]; + p4=ell[10+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* load p and q values */ + p1=ell[11]; + q1=ex[11]; + p2=ell[11+lskip1]; + p3=ell[11+lskip2]; + p4=ell[11+lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* advance pointers */ + ell += 12; + ex += 12; + /* end of inner loop */ + } + /* compute left-over iterations */ + j += 12; + for (; j > 0; j--) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + p2=ell[lskip1]; + p3=ell[lskip2]; + p4=ell[lskip3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + Z21 += p2 * q1; + Z31 += p3 * q1; + Z41 += p4 * q1; + /* advance pointers */ + ell += 1; + ex += 1; + } + /* finish computing the X(i) block */ + Z11 = ex[0] - Z11; + ex[0] = Z11; + p1 = ell[lskip1]; + Z21 = ex[1] - Z21 - p1*Z11; + ex[1] = Z21; + p1 = ell[lskip2]; + p2 = ell[1+lskip2]; + Z31 = ex[2] - Z31 - p1*Z11 - p2*Z21; + ex[2] = Z31; + p1 = ell[lskip3]; + p2 = ell[1+lskip3]; + p3 = ell[2+lskip3]; + Z41 = ex[3] - Z41 - p1*Z11 - p2*Z21 - p3*Z31; + ex[3] = Z41; + /* end of outer loop */ + } + /* compute rows at end that are not a multiple of block size */ + for (; i < n; i++) { + /* compute all 1 x 1 block of X, from rows i..i+1-1 */ + /* set the Z matrix to 0 */ + Z11=0; + ell = L + i*lskip1; + ex = B; + /* the inner loop that computes outer products and adds them to Z */ + for (j=i-12; j >= 0; j -= 12) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[1]; + q1=ex[1]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[2]; + q1=ex[2]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[3]; + q1=ex[3]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[4]; + q1=ex[4]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[5]; + q1=ex[5]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[6]; + q1=ex[6]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[7]; + q1=ex[7]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[8]; + q1=ex[8]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[9]; + q1=ex[9]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[10]; + q1=ex[10]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* load p and q values */ + p1=ell[11]; + q1=ex[11]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* advance pointers */ + ell += 12; + ex += 12; + /* end of inner loop */ + } + /* compute left-over iterations */ + j += 12; + for (; j > 0; j--) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + /* compute outer product and add it to the Z matrix */ + Z11 += p1 * q1; + /* advance pointers */ + ell += 1; + ex += 1; + } + /* finish computing the X(i) block */ + Z11 = ex[0] - Z11; + ex[0] = Z11; + } +} + +/* solve L^T * x=b, with b containing 1 right hand side. + * L is an n*n lower triangular matrix with ones on the diagonal. + * L is stored by rows and its leading dimension is lskip. + * b is an n*1 matrix that contains the right hand side. + * b is overwritten with x. + * this processes blocks of 4. + */ + +void btSolveL1T (const btScalar *L, btScalar *B, int n, int lskip1) +{ + /* declare variables - Z matrix, p and q vectors, etc */ + btScalar Z11,m11,Z21,m21,Z31,m31,Z41,m41,p1,q1,p2,p3,p4,*ex; + const btScalar *ell; + int lskip2,lskip3,i,j; + /* special handling for L and B because we're solving L1 *transpose* */ + L = L + (n-1)*(lskip1+1); + B = B + n-1; + lskip1 = -lskip1; + /* compute lskip values */ + lskip2 = 2*lskip1; + lskip3 = 3*lskip1; + /* compute all 4 x 1 blocks of X */ + for (i=0; i <= n-4; i+=4) { + /* compute all 4 x 1 block of X, from rows i..i+4-1 */ + /* set the Z matrix to 0 */ + Z11=0; + Z21=0; + Z31=0; + Z41=0; + ell = L - i; + ex = B; + /* the inner loop that computes outer products and adds them to Z */ + for (j=i-4; j >= 0; j -= 4) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + p2=ell[-1]; + p3=ell[-2]; + p4=ell[-3]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + m21 = p2 * q1; + m31 = p3 * q1; + m41 = p4 * q1; + ell += lskip1; + Z11 += m11; + Z21 += m21; + Z31 += m31; + Z41 += m41; + /* load p and q values */ + p1=ell[0]; + q1=ex[-1]; + p2=ell[-1]; + p3=ell[-2]; + p4=ell[-3]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + m21 = p2 * q1; + m31 = p3 * q1; + m41 = p4 * q1; + ell += lskip1; + Z11 += m11; + Z21 += m21; + Z31 += m31; + Z41 += m41; + /* load p and q values */ + p1=ell[0]; + q1=ex[-2]; + p2=ell[-1]; + p3=ell[-2]; + p4=ell[-3]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + m21 = p2 * q1; + m31 = p3 * q1; + m41 = p4 * q1; + ell += lskip1; + Z11 += m11; + Z21 += m21; + Z31 += m31; + Z41 += m41; + /* load p and q values */ + p1=ell[0]; + q1=ex[-3]; + p2=ell[-1]; + p3=ell[-2]; + p4=ell[-3]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + m21 = p2 * q1; + m31 = p3 * q1; + m41 = p4 * q1; + ell += lskip1; + ex -= 4; + Z11 += m11; + Z21 += m21; + Z31 += m31; + Z41 += m41; + /* end of inner loop */ + } + /* compute left-over iterations */ + j += 4; + for (; j > 0; j--) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + p2=ell[-1]; + p3=ell[-2]; + p4=ell[-3]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + m21 = p2 * q1; + m31 = p3 * q1; + m41 = p4 * q1; + ell += lskip1; + ex -= 1; + Z11 += m11; + Z21 += m21; + Z31 += m31; + Z41 += m41; + } + /* finish computing the X(i) block */ + Z11 = ex[0] - Z11; + ex[0] = Z11; + p1 = ell[-1]; + Z21 = ex[-1] - Z21 - p1*Z11; + ex[-1] = Z21; + p1 = ell[-2]; + p2 = ell[-2+lskip1]; + Z31 = ex[-2] - Z31 - p1*Z11 - p2*Z21; + ex[-2] = Z31; + p1 = ell[-3]; + p2 = ell[-3+lskip1]; + p3 = ell[-3+lskip2]; + Z41 = ex[-3] - Z41 - p1*Z11 - p2*Z21 - p3*Z31; + ex[-3] = Z41; + /* end of outer loop */ + } + /* compute rows at end that are not a multiple of block size */ + for (; i < n; i++) { + /* compute all 1 x 1 block of X, from rows i..i+1-1 */ + /* set the Z matrix to 0 */ + Z11=0; + ell = L - i; + ex = B; + /* the inner loop that computes outer products and adds them to Z */ + for (j=i-4; j >= 0; j -= 4) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + ell += lskip1; + Z11 += m11; + /* load p and q values */ + p1=ell[0]; + q1=ex[-1]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + ell += lskip1; + Z11 += m11; + /* load p and q values */ + p1=ell[0]; + q1=ex[-2]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + ell += lskip1; + Z11 += m11; + /* load p and q values */ + p1=ell[0]; + q1=ex[-3]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + ell += lskip1; + ex -= 4; + Z11 += m11; + /* end of inner loop */ + } + /* compute left-over iterations */ + j += 4; + for (; j > 0; j--) { + /* load p and q values */ + p1=ell[0]; + q1=ex[0]; + /* compute outer product and add it to the Z matrix */ + m11 = p1 * q1; + ell += lskip1; + ex -= 1; + Z11 += m11; + } + /* finish computing the X(i) block */ + Z11 = ex[0] - Z11; + ex[0] = Z11; + } +} + + + +void btVectorScale (btScalar *a, const btScalar *d, int n) +{ + btAssert (a && d && n >= 0); + for (int i=0; i 0 && nskip >= n); + btSolveL1 (L,b,n,nskip); + btVectorScale (b,d,n); + btSolveL1T (L,b,n,nskip); +} + + + +//*************************************************************************** + +// swap row/column i1 with i2 in the n*n matrix A. the leading dimension of +// A is nskip. this only references and swaps the lower triangle. +// if `do_fast_row_swaps' is nonzero and row pointers are being used, then +// rows will be swapped by exchanging row pointers. otherwise the data will +// be copied. + +static void btSwapRowsAndCols (BTATYPE A, int n, int i1, int i2, int nskip, + int do_fast_row_swaps) +{ + btAssert (A && n > 0 && i1 >= 0 && i2 >= 0 && i1 < n && i2 < n && + nskip >= n && i1 < i2); + +# ifdef BTROWPTRS + btScalar *A_i1 = A[i1]; + btScalar *A_i2 = A[i2]; + for (int i=i1+1; i0 && i1 >=0 && i2 >= 0 && i1 < n && i2 < n && nskip >= n && i1 <= i2); + if (i1==i2) return; + + btSwapRowsAndCols (A,n,i1,i2,nskip,do_fast_row_swaps); + + tmpr = x[i1]; + x[i1] = x[i2]; + x[i2] = tmpr; + + tmpr = b[i1]; + b[i1] = b[i2]; + b[i2] = tmpr; + + tmpr = w[i1]; + w[i1] = w[i2]; + w[i2] = tmpr; + + tmpr = lo[i1]; + lo[i1] = lo[i2]; + lo[i2] = tmpr; + + tmpr = hi[i1]; + hi[i1] = hi[i2]; + hi[i2] = tmpr; + + tmpi = p[i1]; + p[i1] = p[i2]; + p[i2] = tmpi; + + tmpb = state[i1]; + state[i1] = state[i2]; + state[i2] = tmpb; + + if (findex) { + tmpi = findex[i1]; + findex[i1] = findex[i2]; + findex[i2] = tmpi; + } +} + + + + +//*************************************************************************** +// btLCP manipulator object. this represents an n*n LCP problem. +// +// two index sets C and N are kept. each set holds a subset of +// the variable indexes 0..n-1. an index can only be in one set. +// initially both sets are empty. +// +// the index set C is special: solutions to A(C,C)\A(C,i) can be generated. + +//*************************************************************************** +// fast implementation of btLCP. see the above definition of btLCP for +// interface comments. +// +// `p' records the permutation of A,x,b,w,etc. p is initially 1:n and is +// permuted as the other vectors/matrices are permuted. +// +// A,x,b,w,lo,hi,state,findex,p,c are permuted such that sets C,N have +// contiguous indexes. the don't-care indexes follow N. +// +// an L*D*L' factorization is maintained of A(C,C), and whenever indexes are +// added or removed from the set C the factorization is updated. +// thus L*D*L'=A[C,C], i.e. a permuted top left nC*nC submatrix of A. +// the leading dimension of the matrix L is always `nskip'. +// +// at the start there may be other indexes that are unbounded but are not +// included in `nub'. btLCP will permute the matrix so that absolutely all +// unbounded vectors are at the start. thus there may be some initial +// permutation. +// +// the algorithms here assume certain patterns, particularly with respect to +// index transfer. + +#ifdef btLCP_FAST + +struct btLCP +{ + const int m_n; + const int m_nskip; + int m_nub; + int m_nC, m_nN; // size of each index set + BTATYPE const m_A; // A rows + btScalar *const m_x, * const m_b, *const m_w, *const m_lo,* const m_hi; // permuted LCP problem data + btScalar *const m_L, *const m_d; // L*D*L' factorization of set C + btScalar *const m_Dell, *const m_ell, *const m_tmp; + bool *const m_state; + int *const m_findex, *const m_p, *const m_C; + + btLCP (int _n, int _nskip, int _nub, btScalar *_Adata, btScalar *_x, btScalar *_b, btScalar *_w, + btScalar *_lo, btScalar *_hi, btScalar *_L, btScalar *_d, + btScalar *_Dell, btScalar *_ell, btScalar *_tmp, + bool *_state, int *_findex, int *_p, int *_C, btScalar **Arows); + int getNub() const { return m_nub; } + void transfer_i_to_C (int i); + void transfer_i_to_N (int i) { m_nN++; } // because we can assume C and N span 1:i-1 + void transfer_i_from_N_to_C (int i); + void transfer_i_from_C_to_N (int i, btAlignedObjectArray& scratch); + int numC() const { return m_nC; } + int numN() const { return m_nN; } + int indexC (int i) const { return i; } + int indexN (int i) const { return i+m_nC; } + btScalar Aii (int i) const { return BTAROW(i)[i]; } + btScalar AiC_times_qC (int i, btScalar *q) const { return btLargeDot (BTAROW(i), q, m_nC); } + btScalar AiN_times_qN (int i, btScalar *q) const { return btLargeDot (BTAROW(i)+m_nC, q+m_nC, m_nN); } + void pN_equals_ANC_times_qC (btScalar *p, btScalar *q); + void pN_plusequals_ANi (btScalar *p, int i, int sign=1); + void pC_plusequals_s_times_qC (btScalar *p, btScalar s, btScalar *q); + void pN_plusequals_s_times_qN (btScalar *p, btScalar s, btScalar *q); + void solve1 (btScalar *a, int i, int dir=1, int only_transfer=0); + void unpermute(); +}; + + +btLCP::btLCP (int _n, int _nskip, int _nub, btScalar *_Adata, btScalar *_x, btScalar *_b, btScalar *_w, + btScalar *_lo, btScalar *_hi, btScalar *_L, btScalar *_d, + btScalar *_Dell, btScalar *_ell, btScalar *_tmp, + bool *_state, int *_findex, int *_p, int *_C, btScalar **Arows): + m_n(_n), m_nskip(_nskip), m_nub(_nub), m_nC(0), m_nN(0), +# ifdef BTROWPTRS + m_A(Arows), +#else + m_A(_Adata), +#endif + m_x(_x), m_b(_b), m_w(_w), m_lo(_lo), m_hi(_hi), + m_L(_L), m_d(_d), m_Dell(_Dell), m_ell(_ell), m_tmp(_tmp), + m_state(_state), m_findex(_findex), m_p(_p), m_C(_C) +{ + { + btSetZero (m_x,m_n); + } + + { +# ifdef BTROWPTRS + // make matrix row pointers + btScalar *aptr = _Adata; + BTATYPE A = m_A; + const int n = m_n, nskip = m_nskip; + for (int k=0; k nub + { + const int n = m_n; + const int nub = m_nub; + if (nub < n) { + for (int k=0; k<100; k++) { + int i1,i2; + do { + i1 = dRandInt(n-nub)+nub; + i2 = dRandInt(n-nub)+nub; + } + while (i1 > i2); + //printf ("--> %d %d\n",i1,i2); + btSwapProblem (m_A,m_x,m_b,m_w,m_lo,m_hi,m_p,m_state,m_findex,n,i1,i2,m_nskip,0); + } + } + */ + + // permute the problem so that *all* the unbounded variables are at the + // start, i.e. look for unbounded variables not included in `nub'. we can + // potentially push up `nub' this way and get a bigger initial factorization. + // note that when we swap rows/cols here we must not just swap row pointers, + // as the initial factorization relies on the data being all in one chunk. + // variables that have findex >= 0 are *not* considered to be unbounded even + // if lo=-inf and hi=inf - this is because these limits may change during the + // solution process. + + { + int *findex = m_findex; + btScalar *lo = m_lo, *hi = m_hi; + const int n = m_n; + for (int k = m_nub; k= 0) continue; + if (lo[k]==-BT_INFINITY && hi[k]==BT_INFINITY) { + btSwapProblem (m_A,m_x,m_b,m_w,lo,hi,m_p,m_state,findex,n,m_nub,k,m_nskip,0); + m_nub++; + } + } + } + + // if there are unbounded variables at the start, factorize A up to that + // point and solve for x. this puts all indexes 0..nub-1 into C. + if (m_nub > 0) { + const int nub = m_nub; + { + btScalar *Lrow = m_L; + const int nskip = m_nskip; + for (int j=0; j nub such that all findex variables are at the end + if (m_findex) { + const int nub = m_nub; + int *findex = m_findex; + int num_at_end = 0; + for (int k=m_n-1; k >= nub; k--) { + if (findex[k] >= 0) { + btSwapProblem (m_A,m_x,m_b,m_w,m_lo,m_hi,m_p,m_state,findex,m_n,k,m_n-1-num_at_end,m_nskip,1); + num_at_end++; + } + } + } + + // print info about indexes + /* + { + const int n = m_n; + const int nub = m_nub; + for (int k=0; k 0) { + // ell,Dell were computed by solve1(). note, ell = D \ L1solve (L,A(i,C)) + { + const int nC = m_nC; + btScalar *const Ltgt = m_L + nC*m_nskip, *ell = m_ell; + for (int j=0; j 0) { + { + btScalar *const aptr = BTAROW(i); + btScalar *Dell = m_Dell; + const int *C = m_C; +# ifdef BTNUB_OPTIMIZATIONS + // if nub>0, initial part of aptr unpermuted + const int nub = m_nub; + int j=0; + for ( ; j 0 && nskip >= n && r >= 0 && r < n); + if (r >= n-1) return; + if (r > 0) { + { + const size_t move_size = (n-r-1)*sizeof(btScalar); + btScalar *Adst = A + r; + for (int i=0; i& scratch) +{ + btAssert (L && d && a && n > 0 && nskip >= n); + + if (n < 2) return; + scratch.resize(2*nskip); + btScalar *W1 = &scratch[0]; + + btScalar *W2 = W1 + nskip; + + W1[0] = btScalar(0.0); + W2[0] = btScalar(0.0); + for (int j=1; j j) ? _BTGETA(i,j) : _BTGETA(j,i)) + +inline size_t btEstimateLDLTAddTLTmpbufSize(int nskip) +{ + return nskip * 2 * sizeof(btScalar); +} + + +void btLDLTRemove (btScalar **A, const int *p, btScalar *L, btScalar *d, + int n1, int n2, int r, int nskip, btAlignedObjectArray& scratch) +{ + btAssert(A && p && L && d && n1 > 0 && n2 > 0 && r >= 0 && r < n2 && + n1 >= n2 && nskip >= n1); + #ifdef BT_DEBUG + for (int i=0; i= 0 && p[i] < n1); + #endif + + if (r==n2-1) { + return; // deleting last row/col is easy + } + else { + size_t LDLTAddTL_size = btEstimateLDLTAddTLTmpbufSize(nskip); + btAssert(LDLTAddTL_size % sizeof(btScalar) == 0); + scratch.resize(nskip * 2+n2); + btScalar *tmp = &scratch[0]; + if (r==0) { + btScalar *a = (btScalar *)((char *)tmp + LDLTAddTL_size); + const int p_0 = p[0]; + for (int i=0; i& scratch) +{ + { + int *C = m_C; + // remove a row/column from the factorization, and adjust the + // indexes (black magic!) + int last_idx = -1; + const int nC = m_nC; + int j = 0; + for ( ; j 0) { + const int nN = m_nN; + for (int j=0; j 0) { + { + btScalar *Dell = m_Dell; + int *C = m_C; + btScalar *aptr = BTAROW(i); +# ifdef BTNUB_OPTIMIZATIONS + // if nub>0, initial part of aptr[] is guaranteed unpermuted + const int nub = m_nub; + int j=0; + for ( ; j 0) { + int *C = m_C; + btScalar *tmp = m_tmp; + const int nC = m_nC; + for (int j=0; j0 && A && x && b && lo && hi && nub >= 0 && nub <= n); + btAssert(outer_w); + +#ifdef BT_DEBUG + { + // check restrictions on lo and hi + for (int k=0; k= 0); + } +# endif + + + // if all the variables are unbounded then we can just factor, solve, + // and return + if (nub >= n) + { + + + int nskip = (n); + btFactorLDLT (A, outer_w, n, nskip); + btSolveLDLT (A, outer_w, b, n, nskip); + memcpy (x, b, n*sizeof(btScalar)); + + return !s_error; + } + + const int nskip = (n); + scratchMem.L.resize(n*nskip); + + scratchMem.d.resize(n); + + btScalar *w = outer_w; + scratchMem.delta_w.resize(n); + scratchMem.delta_x.resize(n); + scratchMem.Dell.resize(n); + scratchMem.ell.resize(n); + scratchMem.Arows.resize(n); + scratchMem.p.resize(n); + scratchMem.C.resize(n); + + // for i in N, state[i] is 0 if x(i)==lo(i) or 1 if x(i)==hi(i) + scratchMem.state.resize(n); + + + // create LCP object. note that tmp is set to delta_w to save space, this + // optimization relies on knowledge of how tmp is used, so be careful! + btLCP lcp(n,nskip,nub,A,x,b,w,lo,hi,&scratchMem.L[0],&scratchMem.d[0],&scratchMem.Dell[0],&scratchMem.ell[0],&scratchMem.delta_w[0],&scratchMem.state[0],findex,&scratchMem.p[0],&scratchMem.C[0],&scratchMem.Arows[0]); + int adj_nub = lcp.getNub(); + + // loop over all indexes adj_nub..n-1. for index i, if x(i),w(i) satisfy the + // LCP conditions then i is added to the appropriate index set. otherwise + // x(i),w(i) is driven either +ve or -ve to force it to the valid region. + // as we drive x(i), x(C) is also adjusted to keep w(C) at zero. + // while driving x(i) we maintain the LCP conditions on the other variables + // 0..i-1. we do this by watching out for other x(i),w(i) values going + // outside the valid region, and then switching them between index sets + // when that happens. + + bool hit_first_friction_index = false; + for (int i=adj_nub; i= 0) { + // un-permute x into delta_w, which is not being used at the moment + for (int j=0; j= 0) { + lcp.transfer_i_to_N (i); + scratchMem.state[i] = false; + } + else if (hi[i]==0 && w[i] <= 0) { + lcp.transfer_i_to_N (i); + scratchMem.state[i] = true; + } + else if (w[i]==0) { + // this is a degenerate case. by the time we get to this test we know + // that lo != 0, which means that lo < 0 as lo is not allowed to be +ve, + // and similarly that hi > 0. this means that the line segment + // corresponding to set C is at least finite in extent, and we are on it. + // NOTE: we must call lcp.solve1() before lcp.transfer_i_to_C() + lcp.solve1 (&scratchMem.delta_x[0],i,0,1); + + lcp.transfer_i_to_C (i); + } + else { + // we must push x(i) and w(i) + for (;;) { + int dir; + btScalar dirf; + // find direction to push on x(i) + if (w[i] <= 0) { + dir = 1; + dirf = btScalar(1.0); + } + else { + dir = -1; + dirf = btScalar(-1.0); + } + + // compute: delta_x(C) = -dir*A(C,C)\A(C,i) + lcp.solve1 (&scratchMem.delta_x[0],i,dir); + + // note that delta_x[i] = dirf, but we wont bother to set it + + // compute: delta_w = A*delta_x ... note we only care about + // delta_w(N) and delta_w(i), the rest is ignored + lcp.pN_equals_ANC_times_qC (&scratchMem.delta_w[0],&scratchMem.delta_x[0]); + lcp.pN_plusequals_ANi (&scratchMem.delta_w[0],i,dir); + scratchMem.delta_w[i] = lcp.AiC_times_qC (i,&scratchMem.delta_x[0]) + lcp.Aii(i)*dirf; + + // find largest step we can take (size=s), either to drive x(i),w(i) + // to the valid LCP region or to drive an already-valid variable + // outside the valid region. + + int cmd = 1; // index switching command + int si = 0; // si = index to switch if cmd>3 + btScalar s = -w[i]/scratchMem.delta_w[i]; + if (dir > 0) { + if (hi[i] < BT_INFINITY) { + btScalar s2 = (hi[i]-x[i])*dirf; // was (hi[i]-x[i])/dirf // step to x(i)=hi(i) + if (s2 < s) { + s = s2; + cmd = 3; + } + } + } + else { + if (lo[i] > -BT_INFINITY) { + btScalar s2 = (lo[i]-x[i])*dirf; // was (lo[i]-x[i])/dirf // step to x(i)=lo(i) + if (s2 < s) { + s = s2; + cmd = 2; + } + } + } + + { + const int numN = lcp.numN(); + for (int k=0; k < numN; ++k) { + const int indexN_k = lcp.indexN(k); + if (!scratchMem.state[indexN_k] ? scratchMem.delta_w[indexN_k] < 0 : scratchMem.delta_w[indexN_k] > 0) { + // don't bother checking if lo=hi=0 + if (lo[indexN_k] == 0 && hi[indexN_k] == 0) continue; + btScalar s2 = -w[indexN_k] / scratchMem.delta_w[indexN_k]; + if (s2 < s) { + s = s2; + cmd = 4; + si = indexN_k; + } + } + } + } + + { + const int numC = lcp.numC(); + for (int k=adj_nub; k < numC; ++k) { + const int indexC_k = lcp.indexC(k); + if (scratchMem.delta_x[indexC_k] < 0 && lo[indexC_k] > -BT_INFINITY) { + btScalar s2 = (lo[indexC_k]-x[indexC_k]) / scratchMem.delta_x[indexC_k]; + if (s2 < s) { + s = s2; + cmd = 5; + si = indexC_k; + } + } + if (scratchMem.delta_x[indexC_k] > 0 && hi[indexC_k] < BT_INFINITY) { + btScalar s2 = (hi[indexC_k]-x[indexC_k]) / scratchMem.delta_x[indexC_k]; + if (s2 < s) { + s = s2; + cmd = 6; + si = indexC_k; + } + } + } + } + + //static char* cmdstring[8] = {0,"->C","->NL","->NH","N->C", + // "C->NL","C->NH"}; + //printf ("cmd=%d (%s), si=%d\n",cmd,cmdstring[cmd],(cmd>3) ? si : i); + + // if s <= 0 then we've got a problem. if we just keep going then + // we're going to get stuck in an infinite loop. instead, just cross + // our fingers and exit with the current solution. + if (s <= btScalar(0.0)) + { +// printf("LCP internal error, s <= 0 (s=%.4e)",(double)s); + if (i < n) { + btSetZero (x+i,n-i); + btSetZero (w+i,n-i); + } + s_error = true; + break; + } + + // apply x = x + s * delta_x + lcp.pC_plusequals_s_times_qC (x, s, &scratchMem.delta_x[0]); + x[i] += s * dirf; + + // apply w = w + s * delta_w + lcp.pN_plusequals_s_times_qN (w, s, &scratchMem.delta_w[0]); + w[i] += s * scratchMem.delta_w[i]; + +// void *tmpbuf; + // switch indexes between sets if necessary + switch (cmd) { + case 1: // done + w[i] = 0; + lcp.transfer_i_to_C (i); + break; + case 2: // done + x[i] = lo[i]; + scratchMem.state[i] = false; + lcp.transfer_i_to_N (i); + break; + case 3: // done + x[i] = hi[i]; + scratchMem.state[i] = true; + lcp.transfer_i_to_N (i); + break; + case 4: // keep going + w[si] = 0; + lcp.transfer_i_from_N_to_C (si); + break; + case 5: // keep going + x[si] = lo[si]; + scratchMem.state[si] = false; + lcp.transfer_i_from_C_to_N (si, scratchMem.m_scratch); + break; + case 6: // keep going + x[si] = hi[si]; + scratchMem.state[si] = true; + lcp.transfer_i_from_C_to_N (si, scratchMem.m_scratch); + break; + } + + if (cmd <= 3) break; + } // for (;;) + } // else + + if (s_error) + { + break; + } + } // for (int i=adj_nub; i= 0 + (2) x = hi, w <= 0 + (3) lo < x < hi, w = 0 +A is a matrix of dimension n*n, everything else is a vector of size n*1. +lo and hi can be +/- dInfinity as needed. the first `nub' variables are +unbounded, i.e. hi and lo are assumed to be +/- dInfinity. + +we restrict lo(i) <= 0 and hi(i) >= 0. + +the original data (A,b) may be modified by this function. + +if the `findex' (friction index) parameter is nonzero, it points to an array +of index values. in this case constraints that have findex[i] >= 0 are +special. all non-special constraints are solved for, then the lo and hi values +for the special constraints are set: + hi[i] = abs( hi[i] * x[findex[i]] ) + lo[i] = -hi[i] +and the solution continues. this mechanism allows a friction approximation +to be implemented. the first `nub' variables are assumed to have findex < 0. + +*/ + + +#ifndef _BT_LCP_H_ +#define _BT_LCP_H_ + +#include +#include +#include + + +#include "LinearMath/btScalar.h" +#include "LinearMath/btAlignedObjectArray.h" + +struct btDantzigScratchMemory +{ + btAlignedObjectArray m_scratch; + btAlignedObjectArray L; + btAlignedObjectArray d; + btAlignedObjectArray delta_w; + btAlignedObjectArray delta_x; + btAlignedObjectArray Dell; + btAlignedObjectArray ell; + btAlignedObjectArray Arows; + btAlignedObjectArray p; + btAlignedObjectArray C; + btAlignedObjectArray state; +}; + +//return false if solving failed +bool btSolveDantzigLCP (int n, btScalar *A, btScalar *x, btScalar *b, btScalar *w, + int nub, btScalar *lo, btScalar *hi, int *findex,btDantzigScratchMemory& scratch); + + + +#endif //_BT_LCP_H_ diff --git a/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigSolver.h b/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigSolver.h new file mode 100644 index 000000000..2a2f2d3d3 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigSolver.h @@ -0,0 +1,112 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ +///original version written by Erwin Coumans, October 2013 + +#ifndef BT_DANTZIG_SOLVER_H +#define BT_DANTZIG_SOLVER_H + +#include "btMLCPSolverInterface.h" +#include "btDantzigLCP.h" + + +class btDantzigSolver : public btMLCPSolverInterface +{ +protected: + + btScalar m_acceptableUpperLimitSolution; + + btAlignedObjectArray m_tempBuffer; + + btAlignedObjectArray m_A; + btAlignedObjectArray m_b; + btAlignedObjectArray m_x; + btAlignedObjectArray m_lo; + btAlignedObjectArray m_hi; + btAlignedObjectArray m_dependencies; + btDantzigScratchMemory m_scratchMemory; +public: + + btDantzigSolver() + :m_acceptableUpperLimitSolution(btScalar(1000)) + { + } + + virtual bool solveMLCP(const btMatrixXu & A, const btVectorXu & b, btVectorXu& x, const btVectorXu & lo,const btVectorXu & hi,const btAlignedObjectArray& limitDependency, int numIterations, bool useSparsity = true) + { + bool result = true; + int n = b.rows(); + if (n) + { + int nub = 0; + btAlignedObjectArray ww; + ww.resize(n); + + + const btScalar* Aptr = A.getBufferPointer(); + m_A.resize(n*n); + for (int i=0;i= m_acceptableUpperLimitSolution) + { + return false; + } + + if (x[i] <= -m_acceptableUpperLimitSolution) + { + return false; + } + } + + for (int i=0;i limitDependenciesCopy = m_limitDependencies; +// printf("solve first LCP\n"); + result = m_solver->solveMLCP(m_A, m_b, m_x, m_lo,m_hi, m_limitDependencies,infoGlobal.m_numIterations ); + if (result) + result = m_solver->solveMLCP(Acopy, m_bSplit, m_xSplit, m_lo,m_hi, limitDependenciesCopy,infoGlobal.m_numIterations ); + + } else + { + result = m_solver->solveMLCP(m_A, m_b, m_x, m_lo,m_hi, m_limitDependencies,infoGlobal.m_numIterations ); + } + return result; +} + +struct btJointNode +{ + int jointIndex; // pointer to enclosing dxJoint object + int otherBodyIndex; // *other* body this joint is connected to + int nextJointNodeIndex;//-1 for null + int constraintRowIndex; +}; + + + +void btMLCPSolver::createMLCPFast(const btContactSolverInfo& infoGlobal) +{ + int numContactRows = interleaveContactAndFriction ? 3 : 1; + + int numConstraintRows = m_allConstraintArray.size(); + int n = numConstraintRows; + { + BT_PROFILE("init b (rhs)"); + m_b.resize(numConstraintRows); + m_bSplit.resize(numConstraintRows); + //m_b.setZero(); + for (int i=0;i=0) + { + m_lo[i] = -BT_INFINITY; + m_hi[i] = BT_INFINITY; + } else + { + m_lo[i] = m_allConstraintArray[i].m_lowerLimit; + m_hi[i] = m_allConstraintArray[i].m_upperLimit; + } + } + } + + // + int m=m_allConstraintArray.size(); + + int numBodies = m_tmpSolverBodyPool.size(); + btAlignedObjectArray bodyJointNodeArray; + { + BT_PROFILE("bodyJointNodeArray.resize"); + bodyJointNodeArray.resize(numBodies,-1); + } + btAlignedObjectArray jointNodeArray; + { + BT_PROFILE("jointNodeArray.reserve"); + jointNodeArray.reserve(2*m_allConstraintArray.size()); + } + + static btMatrixXu J3; + { + BT_PROFILE("J3.resize"); + J3.resize(2*m,8); + } + static btMatrixXu JinvM3; + { + BT_PROFILE("JinvM3.resize/setZero"); + + JinvM3.resize(2*m,8); + JinvM3.setZero(); + J3.setZero(); + } + int cur=0; + int rowOffset = 0; + static btAlignedObjectArray ofs; + { + BT_PROFILE("ofs resize"); + ofs.resize(0); + ofs.resizeNoInitialize(m_allConstraintArray.size()); + } + { + BT_PROFILE("Compute J and JinvM"); + int c=0; + + int numRows = 0; + + for (int i=0;igetInvMass(); + btVector3 relPosCrossNormalInvInertia = m_allConstraintArray[i+row].m_relpos1CrossNormal * orgBodyA->getInvInertiaTensorWorld(); + + for (int r=0;r<3;r++) + { + J3.setElem(cur,r,m_allConstraintArray[i+row].m_contactNormal1[r]); + J3.setElem(cur,r+4,m_allConstraintArray[i+row].m_relpos1CrossNormal[r]); + JinvM3.setElem(cur,r,normalInvMass[r]); + JinvM3.setElem(cur,r+4,relPosCrossNormalInvInertia[r]); + } + J3.setElem(cur,3,0); + JinvM3.setElem(cur,3,0); + J3.setElem(cur,7,0); + JinvM3.setElem(cur,7,0); + } + } else + { + cur += numRows; + } + if (orgBodyB) + { + + { + int slotB=-1; + //find free jointNode slot for sbA + slotB =jointNodeArray.size(); + jointNodeArray.expand();//NonInitializing(); + int prevSlot = bodyJointNodeArray[sbB]; + bodyJointNodeArray[sbB] = slotB; + jointNodeArray[slotB].nextJointNodeIndex = prevSlot; + jointNodeArray[slotB].jointIndex = c; + jointNodeArray[slotB].otherBodyIndex = orgBodyA ? sbA : -1; + jointNodeArray[slotB].constraintRowIndex = i; + } + + for (int row=0;rowgetInvMass(); + btVector3 relPosInvInertiaB = m_allConstraintArray[i+row].m_relpos2CrossNormal * orgBodyB->getInvInertiaTensorWorld(); + + for (int r=0;r<3;r++) + { + J3.setElem(cur,r,m_allConstraintArray[i+row].m_contactNormal2[r]); + J3.setElem(cur,r+4,m_allConstraintArray[i+row].m_relpos2CrossNormal[r]); + JinvM3.setElem(cur,r,normalInvMassB[r]); + JinvM3.setElem(cur,r+4,relPosInvInertiaB[r]); + } + J3.setElem(cur,3,0); + JinvM3.setElem(cur,3,0); + J3.setElem(cur,7,0); + JinvM3.setElem(cur,7,0); + } + } + else + { + cur += numRows; + } + rowOffset+=numRows; + + } + + } + + + //compute JinvM = J*invM. + const btScalar* JinvM = JinvM3.getBufferPointer(); + + const btScalar* Jptr = J3.getBufferPointer(); + { + BT_PROFILE("m_A.resize"); + m_A.resize(n,n); + } + + { + BT_PROFILE("m_A.setZero"); + m_A.setZero(); + } + int c=0; + { + int numRows = 0; + BT_PROFILE("Compute A"); + for (int i=0;i=0) + { + int j0 = jointNodeArray[startJointNodeA].jointIndex; + int cr0 = jointNodeArray[startJointNodeA].constraintRowIndex; + if (j0=0) + { + int j1 = jointNodeArray[startJointNodeB].jointIndex; + int cj1 = jointNodeArray[startJointNodeB].constraintRowIndex; + + if (j1m_tmpSolverBodyPool.size(); + int numConstraintRows = m_allConstraintArray.size(); + + m_b.resize(numConstraintRows); + if (infoGlobal.m_splitImpulse) + m_bSplit.resize(numConstraintRows); + + for (int i=0;igetInvInertiaTensorWorld()[r][c] : 0); + } + + static btMatrixXu J; + J.resize(numConstraintRows,6*numBodies); + J.setZero(); + + m_lo.resize(numConstraintRows); + m_hi.resize(numConstraintRows); + + for (int i=0;i m_limitDependencies; + btConstraintArray m_allConstraintArray; + btMLCPSolverInterface* m_solver; + int m_fallback; + + virtual btScalar solveGroupCacheFriendlySetup(btCollisionObject** bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); + virtual btScalar solveGroupCacheFriendlyIterations(btCollisionObject** bodies ,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); + virtual void createMLCP(const btContactSolverInfo& infoGlobal); + virtual void createMLCPFast(const btContactSolverInfo& infoGlobal); + + //return true is it solves the problem successfully + virtual bool solveMLCP(const btContactSolverInfo& infoGlobal); + +public: + + btMLCPSolver( btMLCPSolverInterface* solver); + virtual ~btMLCPSolver(); + + void setMLCPSolver(btMLCPSolverInterface* solver) + { + m_solver = solver; + } + + int getNumFallbacks() const + { + return m_fallback; + } + void setNumFallbacks(int num) + { + m_fallback = num; + } + + virtual btConstraintSolverType getSolverType() const + { + return BT_MLCP_SOLVER; + } + +}; + + +#endif //BT_MLCP_SOLVER_H diff --git a/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btMLCPSolverInterface.h b/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btMLCPSolverInterface.h new file mode 100644 index 000000000..25bb3f6d3 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btMLCPSolverInterface.h @@ -0,0 +1,33 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ +///original version written by Erwin Coumans, October 2013 + +#ifndef BT_MLCP_SOLVER_INTERFACE_H +#define BT_MLCP_SOLVER_INTERFACE_H + +#include "LinearMath/btMatrixX.h" + +class btMLCPSolverInterface +{ +public: + virtual ~btMLCPSolverInterface() + { + } + + //return true is it solves the problem successfully + virtual bool solveMLCP(const btMatrixXu & A, const btVectorXu & b, btVectorXu& x, const btVectorXu & lo,const btVectorXu & hi,const btAlignedObjectArray& limitDependency, int numIterations, bool useSparsity = true)=0; +}; + +#endif //BT_MLCP_SOLVER_INTERFACE_H diff --git a/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btPATHSolver.h b/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btPATHSolver.h new file mode 100644 index 000000000..9ec31a6d4 --- /dev/null +++ b/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btPATHSolver.h @@ -0,0 +1,151 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ +///original version written by Erwin Coumans, October 2013 + + +#ifndef BT_PATH_SOLVER_H +#define BT_PATH_SOLVER_H + +//#define BT_USE_PATH +#ifdef BT_USE_PATH + +extern "C" { +#include "PATH/SimpleLCP.h" +#include "PATH/License.h" +#include "PATH/Error_Interface.h" +}; + void __stdcall MyError(Void *data, Char *msg) +{ + printf("Path Error: %s\n",msg); +} + void __stdcall MyWarning(Void *data, Char *msg) +{ + printf("Path Warning: %s\n",msg); +} + +Error_Interface e; + + + +#include "btMLCPSolverInterface.h" +#include "Dantzig/lcp.h" + +class btPathSolver : public btMLCPSolverInterface +{ +public: + + btPathSolver() + { + License_SetString("2069810742&Courtesy_License&&&USR&2013&14_12_2011&1000&PATH&GEN&31_12_2013&0_0_0&0&0_0"); + e.error_data = 0; + e.warning = MyWarning; + e.error = MyError; + Error_SetInterface(&e); + } + + + virtual bool solveMLCP(const btMatrixXu & A, const btVectorXu & b, btVectorXu& x, const btVectorXu & lo,const btVectorXu & hi,const btAlignedObjectArray& limitDependency, int numIterations, bool useSparsity = true) + { + MCP_Termination status; + + + int numVariables = b.rows(); + if (0==numVariables) + return true; + + /* - variables - the number of variables in the problem + - m_nnz - the number of nonzeros in the M matrix + - m_i - a vector of size m_nnz containing the row indices for M + - m_j - a vector of size m_nnz containing the column indices for M + - m_ij - a vector of size m_nnz containing the data for M + - q - a vector of size variables + - lb - a vector of size variables containing the lower bounds on x + - ub - a vector of size variables containing the upper bounds on x + */ + btAlignedObjectArray values; + btAlignedObjectArray rowIndices; + btAlignedObjectArray colIndices; + + for (int i=0;i zResult; + zResult.resize(numVariables); + btAlignedObjectArray rhs; + btAlignedObjectArray upperBounds; + btAlignedObjectArray lowerBounds; + for (int i=0;i& limitDependency, int numIterations, bool useSparsity = true) + { + //A is a m-n matrix, m rows, n columns + btAssert(A.rows() == b.rows()); + + int i, j, numRows = A.rows(); + + float delta; + + for (int k = 0; k =0) + { + s = x[limitDependency[i]]; + if (s<0) + s=1; + } + + if (x[i]hi[i]*s) + x[i]=hi[i]*s; + } + } + return true; + } + +}; + +#endif //BT_SOLVE_PROJECTED_GAUSS_SEIDEL_H diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/CMakeLists.txt b/Engine/lib/bullet/src/BulletMultiThreaded/CMakeLists.txt index 6eeeb6d20..dcc542770 100644 --- a/Engine/lib/bullet/src/BulletMultiThreaded/CMakeLists.txt +++ b/Engine/lib/bullet/src/BulletMultiThreaded/CMakeLists.txt @@ -102,7 +102,10 @@ IF (INSTALL_LIBS) IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) INSTALL(TARGETS BulletMultiThreaded DESTINATION .) ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) - INSTALL(TARGETS BulletMultiThreaded DESTINATION lib${LIB_SUFFIX}) + INSTALL(TARGETS BulletMultiThreaded + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ARCHIVE DESTINATION lib${LIB_SUFFIX}) INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION ${INCLUDE_INSTALL_DIR} FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE PATTERN "CMakeFiles" EXCLUDE) diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/DX11/CMakeLists.txt b/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/DX11/CMakeLists.txt index e7492b542..52f335d23 100644 --- a/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/DX11/CMakeLists.txt +++ b/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/DX11/CMakeLists.txt @@ -70,7 +70,10 @@ IF (INSTALL_LIBS) IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) INSTALL(TARGETS BulletSoftBodySolvers_DX11 DESTINATION .) ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) - INSTALL(TARGETS BulletSoftBodySolvers_DX11 DESTINATION lib${LIB_SUFFIX}) + INSTALL(TARGETS BulletSoftBodySolvers_DX11 + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ARCHIVE DESTINATION lib${LIB_SUFFIX}) #headers are already installed by BulletMultiThreaded library ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) ENDIF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/AMD/CMakeLists.txt b/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/AMD/CMakeLists.txt index 9826f6378..1fc07328e 100644 --- a/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/AMD/CMakeLists.txt +++ b/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/AMD/CMakeLists.txt @@ -49,7 +49,10 @@ IF (INSTALL_LIBS) IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_AMD DESTINATION .) ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) - INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_AMD DESTINATION lib${LIB_SUFFIX}) + INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_AMD + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ARCHIVE DESTINATION lib${LIB_SUFFIX}) #headers are already installed by BulletMultiThreaded library ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) ENDIF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/Apple/CMakeLists.txt b/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/Apple/CMakeLists.txt index 35dd4eb2d..6e593a998 100644 --- a/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/Apple/CMakeLists.txt +++ b/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/Apple/CMakeLists.txt @@ -64,7 +64,10 @@ IF (INSTALL_LIBS) IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_Apple DESTINATION .) ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) - INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_Apple DESTINATION lib${LIB_SUFFIX}) + INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_Apple + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ARCHIVE DESTINATION lib${LIB_SUFFIX}) #headers are already installed by BulletMultiThreaded library ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) ENDIF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/Intel/CMakeLists.txt b/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/Intel/CMakeLists.txt index 9503a5f40..ecca18130 100644 --- a/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/Intel/CMakeLists.txt +++ b/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/Intel/CMakeLists.txt @@ -69,7 +69,10 @@ IF (INSTALL_LIBS) IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_Intel DESTINATION .) ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) - INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_Intel DESTINATION lib${LIB_SUFFIX}) + INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_Intel + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ARCHIVE DESTINATION lib${LIB_SUFFIX}) #headers are already installed by BulletMultiThreaded library ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) ENDIF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/MiniCL/CMakeLists.txt b/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/MiniCL/CMakeLists.txt index 2ca3ca087..97deb7e46 100644 --- a/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/MiniCL/CMakeLists.txt +++ b/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/MiniCL/CMakeLists.txt @@ -62,7 +62,10 @@ IF (INSTALL_LIBS) IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_Mini DESTINATION .) ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) - INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_Mini DESTINATION lib${LIB_SUFFIX}) + INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_Mini + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ARCHIVE DESTINATION lib${LIB_SUFFIX}) #headers are already installed by BulletMultiThreaded library ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) ENDIF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/NVidia/CMakeLists.txt b/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/NVidia/CMakeLists.txt index ea0406390..884a0ffea 100644 --- a/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/NVidia/CMakeLists.txt +++ b/Engine/lib/bullet/src/BulletMultiThreaded/GpuSoftBodySolvers/OpenCL/NVidia/CMakeLists.txt @@ -68,7 +68,10 @@ IF (INSTALL_LIBS) IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_NVidia DESTINATION .) ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) - INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_NVidia DESTINATION lib${LIB_SUFFIX}) + INSTALL(TARGETS BulletSoftBodySolvers_OpenCL_NVidia + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ARCHIVE DESTINATION lib${LIB_SUFFIX}) #headers are already installed by BulletMultiThreaded library ENDIF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) ENDIF (${CMAKE_MAJOR_VERSION}.${CMAKE_MINOR_VERSION} GREATER 2.5) diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/PlatformDefinitions.h b/Engine/lib/bullet/src/BulletMultiThreaded/PlatformDefinitions.h index 142103a09..9bf8c96f5 100644 --- a/Engine/lib/bullet/src/BulletMultiThreaded/PlatformDefinitions.h +++ b/Engine/lib/bullet/src/BulletMultiThreaded/PlatformDefinitions.h @@ -38,7 +38,11 @@ typedef union #ifndef __PHYSICS_COMMON_H__ #ifndef PFX_USE_FREE_VECTORMATH #ifndef __BT_SKIP_UINT64_H +#if defined(_WIN64) && defined(_MSC_VER) + typedef unsigned __int64 uint64_t; +#else typedef unsigned long int uint64_t; +#endif #endif //__BT_SKIP_UINT64_H #endif //PFX_USE_FREE_VECTORMATH typedef unsigned int uint32_t; diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuGatheringCollisionDispatcher.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SpuGatheringCollisionDispatcher.cpp index b9e88a07f..c8712dab7 100644 --- a/Engine/lib/bullet/src/BulletMultiThreaded/SpuGatheringCollisionDispatcher.cpp +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuGatheringCollisionDispatcher.cpp @@ -166,8 +166,8 @@ public: collisionPair.m_internalTmpValue = 2; } else { - btCollisionObjectWrapper ob0(0,colObj0->getCollisionShape(),colObj0,colObj0->getWorldTransform()); - btCollisionObjectWrapper ob1(0,colObj1->getCollisionShape(),colObj1,colObj1->getWorldTransform()); + btCollisionObjectWrapper ob0(0,colObj0->getCollisionShape(),colObj0,colObj0->getWorldTransform(),-1,-1); + btCollisionObjectWrapper ob1(0,colObj1->getCollisionShape(),colObj1,colObj1->getWorldTransform(),-1,-1); collisionPair.m_algorithm = m_dispatcher->findAlgorithm(&ob0,&ob1); collisionPair.m_internalTmpValue = 3; @@ -245,8 +245,8 @@ void SpuGatheringCollisionDispatcher::dispatchAllCollisionPairs(btOverlappingPai if (dispatcher->needsCollision(colObj0,colObj1)) { //discrete collision detection query - btCollisionObjectWrapper ob0(0,colObj0->getCollisionShape(),colObj0,colObj0->getWorldTransform()); - btCollisionObjectWrapper ob1(0,colObj1->getCollisionShape(),colObj1,colObj1->getWorldTransform()); + btCollisionObjectWrapper ob0(0,colObj0->getCollisionShape(),colObj0,colObj0->getWorldTransform(),-1,-1); + btCollisionObjectWrapper ob1(0,colObj1->getCollisionShape(),colObj1,colObj1->getWorldTransform(),-1,-1); btManifoldResult contactPointResult(&ob0,&ob1); diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuConvexPenetrationDepthSolver.h b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuConvexPenetrationDepthSolver.h index 449f19288..b1bd53d94 100644 --- a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuConvexPenetrationDepthSolver.h +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuConvexPenetrationDepthSolver.h @@ -20,7 +20,6 @@ subject to the following restrictions: -class btStackAlloc; class btIDebugDraw; #include "BulletCollision/NarrowphaseCollision/btConvexPenetrationDepthSolver.h" @@ -37,7 +36,7 @@ public: void* convexA,void* convexB,int shapeTypeA, int shapeTypeB, float marginA, float marginB, btTransform& transA,const btTransform& transB, btVector3& v, btVector3& pa, btVector3& pb, - class btIDebugDraw* debugDraw,btStackAlloc* stackAlloc, + class btIDebugDraw* debugDraw, struct SpuConvexPolyhedronVertexData* convexVertexDataA, struct SpuConvexPolyhedronVertexData* convexVertexDataB ) const = 0; diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.cpp b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.cpp index 46e4d98c1..c2fe2905c 100644 --- a/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.cpp +++ b/Engine/lib/bullet/src/BulletMultiThreaded/SpuNarrowPhaseCollisionTask/SpuGatheringCollisionTask.cpp @@ -127,7 +127,7 @@ bool gUseEpa = false; //int gNumConvexPoints0=0; ///Make sure no destructors are called on this memory -struct CollisionTask_LocalStoreMemory +ATTRIBUTE_ALIGNED16(struct) CollisionTask_LocalStoreMemory { ///This CollisionTask_LocalStoreMemory is mainly used for the SPU version, using explicit DMA ///Other platforms can use other memory programming models. @@ -142,7 +142,7 @@ struct CollisionTask_LocalStoreMemory btPersistentManifold gPersistentManifoldBuffer; CollisionShape_LocalStoreMemory gCollisionShapes[2]; bvhMeshShape_LocalStoreMemory bvhShapeData; - SpuConvexPolyhedronVertexData convexVertexData[2]; + ATTRIBUTE_ALIGNED16(SpuConvexPolyhedronVertexData convexVertexData[2]); CompoundShape_LocalStoreMemory compoundShapeData[2]; ///The following pointers might either point into this local store memory, or to the original/other memory locations. @@ -199,7 +199,7 @@ btAlignedObjectArray sLocalStorePointers; void* createCollisionLocalStoreMemory() { - CollisionTask_LocalStoreMemory* localStore = new CollisionTask_LocalStoreMemory; + CollisionTask_LocalStoreMemory* localStore = (CollisionTask_LocalStoreMemory*)btAlignedAlloc( sizeof(CollisionTask_LocalStoreMemory),16); sLocalStorePointers.push_back(localStore); return localStore; } @@ -208,7 +208,7 @@ void deleteCollisionLocalStoreMemory() { for (int i=0;igetAngularVelocity()+rb->getTotalTorque()*rb->getInvInertiaTensorWorld()*infoGlobal.m_timeStep; btVector3 linVelPlusForces = rb->getLinearVelocity()+rb->getTotalForce()*rb->getInvMass()*infoGlobal.m_timeStep; - state.setAngularVelocity((const vmVector3&)angVelPlusForces); - state.setLinearVelocity((const vmVector3&) linVelPlusForces); + state.setAngularVelocity(btReadVector3(angVelPlusForces)); + state.setLinearVelocity(btReadVector3(linVelPlusForces)); state.setMotionType(PfxMotionTypeActive); vmMatrix3 ori(solverBody.mOrientation); @@ -1381,9 +1381,9 @@ btScalar btParallelConstraintSolver::solveGroup(btCollisionObject** bodies1,int btTypedConstraint::btConstraintInfo2 info2; info2.fps = 1.f/infoGlobal.m_timeStep; info2.erp = infoGlobal.m_erp; - info2.m_J1linearAxis = currentConstraintRow->m_contactNormal; + info2.m_J1linearAxis = currentConstraintRow->m_contactNormal1; info2.m_J1angularAxis = currentConstraintRow->m_relpos1CrossNormal; - info2.m_J2linearAxis = 0; + info2.m_J2linearAxis = currentConstraintRow->m_contactNormal2; info2.m_J2angularAxis = currentConstraintRow->m_relpos2CrossNormal; info2.rowskip = sizeof(btSolverConstraint)/sizeof(btScalar);//check this ///the size of btSolverConstraint needs be a multiple of btScalar @@ -1418,14 +1418,14 @@ btScalar btParallelConstraintSolver::solveGroup(btCollisionObject** bodies1,int } { - btVector3 iMJlA = solverConstraint.m_contactNormal*rbA.getInvMass(); + btVector3 iMJlA = solverConstraint.m_contactNormal1*rbA.getInvMass(); btVector3 iMJaA = rbA.getInvInertiaTensorWorld()*solverConstraint.m_relpos1CrossNormal; - btVector3 iMJlB = solverConstraint.m_contactNormal*rbB.getInvMass();//sign of normal? + btVector3 iMJlB = solverConstraint.m_contactNormal2*rbB.getInvMass();//sign of normal? btVector3 iMJaB = rbB.getInvInertiaTensorWorld()*solverConstraint.m_relpos2CrossNormal; - btScalar sum = iMJlA.dot(solverConstraint.m_contactNormal); + btScalar sum = iMJlA.dot(solverConstraint.m_contactNormal1); sum += iMJaA.dot(solverConstraint.m_relpos1CrossNormal); - sum += iMJlB.dot(solverConstraint.m_contactNormal); + sum += iMJlB.dot(solverConstraint.m_contactNormal2); sum += iMJaB.dot(solverConstraint.m_relpos2CrossNormal); solverConstraint.m_jacDiagABInv = btScalar(1.)/sum; @@ -1436,8 +1436,8 @@ btScalar btParallelConstraintSolver::solveGroup(btCollisionObject** bodies1,int ///todo: add force/torque accelerators { btScalar rel_vel; - btScalar vel1Dotn = solverConstraint.m_contactNormal.dot(rbA.getLinearVelocity()) + solverConstraint.m_relpos1CrossNormal.dot(rbA.getAngularVelocity()); - btScalar vel2Dotn = -solverConstraint.m_contactNormal.dot(rbB.getLinearVelocity()) + solverConstraint.m_relpos2CrossNormal.dot(rbB.getAngularVelocity()); + btScalar vel1Dotn = solverConstraint.m_contactNormal1.dot(rbA.getLinearVelocity()) + solverConstraint.m_relpos1CrossNormal.dot(rbA.getAngularVelocity()); + btScalar vel2Dotn = solverConstraint.m_contactNormal2.dot(rbB.getLinearVelocity()) + solverConstraint.m_relpos2CrossNormal.dot(rbB.getAngularVelocity()); rel_vel = vel1Dotn+vel2Dotn; diff --git a/Engine/lib/bullet/src/BulletMultiThreaded/btParallelConstraintSolver.h b/Engine/lib/bullet/src/BulletMultiThreaded/btParallelConstraintSolver.h index af42a8380..b5b475a1b 100644 --- a/Engine/lib/bullet/src/BulletMultiThreaded/btParallelConstraintSolver.h +++ b/Engine/lib/bullet/src/BulletMultiThreaded/btParallelConstraintSolver.h @@ -279,7 +279,7 @@ public: virtual ~btParallelConstraintSolver(); - virtual btScalar solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& info, btIDebugDraw* debugDrawer, btStackAlloc* stackAlloc,btDispatcher* dispatcher); + virtual btScalar solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& info, btIDebugDraw* debugDrawer,btDispatcher* dispatcher); }; diff --git a/Engine/lib/bullet/src/BulletSoftBody/CMakeLists.txt b/Engine/lib/bullet/src/BulletSoftBody/CMakeLists.txt index 105379582..e66bd02d4 100644 --- a/Engine/lib/bullet/src/BulletSoftBody/CMakeLists.txt +++ b/Engine/lib/bullet/src/BulletSoftBody/CMakeLists.txt @@ -50,7 +50,9 @@ IF (INSTALL_LIBS) IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) INSTALL(TARGETS BulletSoftBody DESTINATION .) ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) - INSTALL(TARGETS BulletSoftBody DESTINATION lib${LIB_SUFFIX}) + INSTALL(TARGETS BulletSoftBody RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ARCHIVE DESTINATION lib${LIB_SUFFIX}) INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION ${INCLUDE_INSTALL_DIR} FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE PATTERN "CMakeFiles" EXCLUDE) diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftBody.cpp b/Engine/lib/bullet/src/BulletSoftBody/btSoftBody.cpp index 8fe34658e..a0c8cca4a 100644 --- a/Engine/lib/bullet/src/BulletSoftBody/btSoftBody.cpp +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftBody.cpp @@ -1767,7 +1767,23 @@ void btSoftBody::predictMotion(btScalar dt) { Node& n=m_nodes[i]; n.m_q = n.m_x; - n.m_v += n.m_f*n.m_im*m_sst.sdt; + btVector3 deltaV = n.m_f*n.m_im*m_sst.sdt; + { + btScalar maxDisplacement = m_worldInfo->m_maxDisplacement; + btScalar clampDeltaV = maxDisplacement/m_sst.sdt; + for (int c=0;c<3;c++) + { + if (deltaV[c]>clampDeltaV) + { + deltaV[c] = clampDeltaV; + } + if (deltaV[c]<-clampDeltaV) + { + deltaV[c]=-clampDeltaV; + } + } + } + n.m_v += deltaV; n.m_x += n.m_v*m_sst.sdt; n.m_f = btVector3(0,0,0); } diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftBody.h b/Engine/lib/bullet/src/BulletSoftBody/btSoftBody.h index 2116c34f0..ee1a3d952 100644 --- a/Engine/lib/bullet/src/BulletSoftBody/btSoftBody.h +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftBody.h @@ -45,6 +45,7 @@ struct btSoftBodyWorldInfo btScalar air_density; btScalar water_density; btScalar water_offset; + btScalar m_maxDisplacement; btVector3 water_normal; btBroadphaseInterface* m_broadphase; btDispatcher* m_dispatcher; @@ -55,6 +56,7 @@ struct btSoftBodyWorldInfo :air_density((btScalar)1.2), water_density(0), water_offset(0), + m_maxDisplacement(1000.f),//avoid soft body from 'exploding' so use some upper threshold of maximum motion that a node can travel per frame water_normal(0,0,0), m_broadphase(0), m_dispatcher(0), diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp index 6e94d0a81..9f0d44526 100644 --- a/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyConcaveCollisionAlgorithm.cpp @@ -117,9 +117,9 @@ void btSoftBodyTriangleCallback::processTriangle(btVector3* triangle,int partId, //copy over user pointers to temporary shape tm->setUserPointer(m_triBody->getCollisionShape()->getUserPointer()); - btCollisionObjectWrapper softBody(0,m_softBody->getCollisionShape(),m_softBody,m_softBody->getWorldTransform()); + btCollisionObjectWrapper softBody(0,m_softBody->getCollisionShape(),m_softBody,m_softBody->getWorldTransform(),-1,-1); //btCollisionObjectWrapper triBody(0,tm, ob, btTransform::getIdentity());//ob->getWorldTransform());//?? - btCollisionObjectWrapper triBody(0,tm, m_triBody, m_triBody->getWorldTransform()); + btCollisionObjectWrapper triBody(0,tm, m_triBody, m_triBody->getWorldTransform(),partId, triangleIndex); btCollisionAlgorithm* colAlgo = ci.m_dispatcher1->findAlgorithm(&softBody,&triBody,0);//m_manifoldPtr); @@ -161,8 +161,8 @@ void btSoftBodyTriangleCallback::processTriangle(btVector3* triangle,int partId, tm->setUserPointer(m_triBody->getCollisionShape()->getUserPointer()); - btCollisionObjectWrapper softBody(0,m_softBody->getCollisionShape(),m_softBody,m_softBody->getWorldTransform()); - btCollisionObjectWrapper triBody(0,tm, m_triBody, m_triBody->getWorldTransform());//btTransform::getIdentity());//?? + btCollisionObjectWrapper softBody(0,m_softBody->getCollisionShape(),m_softBody,m_softBody->getWorldTransform(),-1,-1); + btCollisionObjectWrapper triBody(0,tm, m_triBody, m_triBody->getWorldTransform(),partId, triangleIndex);//btTransform::getIdentity());//?? btCollisionAlgorithm* colAlgo = ci.m_dispatcher1->findAlgorithm(&softBody,&triBody,0);//m_manifoldPtr); diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyHelpers.cpp b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyHelpers.cpp index 0fb3560e9..36f675a6c 100644 --- a/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyHelpers.cpp +++ b/Engine/lib/bullet/src/BulletSoftBody/btSoftBodyHelpers.cpp @@ -911,9 +911,9 @@ btSoftBody* btSoftBodyHelpers::CreateFromConvexHull(btSoftBodyWorldInfo& worldI &hres.m_OutputVertices[0],0); for(int i=0;i<(int)hres.mNumFaces;++i) { - const int idx[]={ hres.m_Indices[i*3+0], - hres.m_Indices[i*3+1], - hres.m_Indices[i*3+2]}; + const int idx[]={ static_cast(hres.m_Indices[i*3+0]), + static_cast(hres.m_Indices[i*3+1]), + static_cast(hres.m_Indices[i*3+2])}; if(idx[0]appendLink( idx[0],idx[1]); if(idx[1]appendLink( idx[1],idx[2]); if(idx[2]appendLink( idx[2],idx[0]); diff --git a/Engine/lib/bullet/src/BulletSoftBody/btSparseSDF.h b/Engine/lib/bullet/src/BulletSoftBody/btSparseSDF.h index 180e3c218..bcf0c7982 100644 --- a/Engine/lib/bullet/src/BulletSoftBody/btSparseSDF.h +++ b/Engine/lib/bullet/src/BulletSoftBody/btSparseSDF.h @@ -69,6 +69,7 @@ struct btSparseSdf btScalar voxelsz; int puid; int ncells; + int m_clampCells; int nprobes; int nqueries; @@ -77,10 +78,13 @@ struct btSparseSdf // // - void Initialize(int hashsize=2383) + void Initialize(int hashsize=2383, int clampCells = 256*1024) { + //avoid a crash due to running out of memory, so clamp the maximum number of cells allocated + //if this limit is reached, the SDF is reset (at the cost of some performance during the reset) + m_clampCells = clampCells; cells.resize(hashsize,0); - Reset(); + Reset(); } // void Reset() @@ -181,6 +185,15 @@ struct btSparseSdf { ++nprobes; ++ncells; + int sz = sizeof(Cell); + if (ncells>m_clampCells) + { + static int numResets=0; + numResets++; +// printf("numResets=%d\n",numResets); + Reset(); + } + c=new Cell(); c->next=root;root=c; c->pclient=shape; diff --git a/Engine/lib/bullet/src/LinearMath/CMakeLists.txt b/Engine/lib/bullet/src/LinearMath/CMakeLists.txt index cc77583a0..8d8a54b9e 100644 --- a/Engine/lib/bullet/src/LinearMath/CMakeLists.txt +++ b/Engine/lib/bullet/src/LinearMath/CMakeLists.txt @@ -54,7 +54,10 @@ IF (INSTALL_LIBS) IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) INSTALL(TARGETS LinearMath DESTINATION .) ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) - INSTALL(TARGETS LinearMath DESTINATION lib${LIB_SUFFIX}) + INSTALL(TARGETS LinearMath + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ARCHIVE DESTINATION lib${LIB_SUFFIX}) INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION ${INCLUDE_INSTALL_DIR} FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE PATTERN "CMakeFiles" EXCLUDE) diff --git a/Engine/lib/bullet/src/LinearMath/btConvexHullComputer.cpp b/Engine/lib/bullet/src/LinearMath/btConvexHullComputer.cpp index c03c901c0..d58ac955f 100644 --- a/Engine/lib/bullet/src/LinearMath/btConvexHullComputer.cpp +++ b/Engine/lib/bullet/src/LinearMath/btConvexHullComputer.cpp @@ -1931,11 +1931,15 @@ void btConvexHullInternal::merge(IntermediateHull& h0, IntermediateHull& h1) } } - -static bool pointCmp(const btConvexHullInternal::Point32& p, const btConvexHullInternal::Point32& q) +class pointCmp { - return (p.y < q.y) || ((p.y == q.y) && ((p.x < q.x) || ((p.x == q.x) && (p.z < q.z)))); -} + public: + + bool operator() ( const btConvexHullInternal::Point32& p, const btConvexHullInternal::Point32& q ) const + { + return (p.y < q.y) || ((p.y == q.y) && ((p.x < q.x) || ((p.x == q.x) && (p.z < q.z)))); + } +}; void btConvexHullInternal::compute(const void* coords, bool doubleCoords, int stride, int count) { @@ -2026,7 +2030,7 @@ void btConvexHullInternal::compute(const void* coords, bool doubleCoords, int st points[i].index = i; } } - points.quickSort(pointCmp); + points.quickSort(pointCmp()); vertexPool.reset(); vertexPool.setArraySize(count); diff --git a/Engine/lib/bullet/src/LinearMath/btIDebugDraw.h b/Engine/lib/bullet/src/LinearMath/btIDebugDraw.h index a00d7763a..de97c3f87 100644 --- a/Engine/lib/bullet/src/LinearMath/btIDebugDraw.h +++ b/Engine/lib/bullet/src/LinearMath/btIDebugDraw.h @@ -62,29 +62,17 @@ class btIDebugDraw virtual void drawSphere(btScalar radius, const btTransform& transform, const btVector3& color) { - btVector3 start = transform.getOrigin(); - - const btVector3 xoffs = transform.getBasis() * btVector3(radius,0,0); - const btVector3 yoffs = transform.getBasis() * btVector3(0,radius,0); - const btVector3 zoffs = transform.getBasis() * btVector3(0,0,radius); - - // XY - drawLine(start-xoffs, start+yoffs, color); - drawLine(start+yoffs, start+xoffs, color); - drawLine(start+xoffs, start-yoffs, color); - drawLine(start-yoffs, start-xoffs, color); - - // XZ - drawLine(start-xoffs, start+zoffs, color); - drawLine(start+zoffs, start+xoffs, color); - drawLine(start+xoffs, start-zoffs, color); - drawLine(start-zoffs, start-xoffs, color); - - // YZ - drawLine(start-yoffs, start+zoffs, color); - drawLine(start+zoffs, start+yoffs, color); - drawLine(start+yoffs, start-zoffs, color); - drawLine(start-zoffs, start-yoffs, color); + + btVector3 center = transform.getOrigin(); + btVector3 up = transform.getBasis().getColumn(1); + btVector3 axis = transform.getBasis().getColumn(0); + btScalar minTh = -SIMD_HALF_PI; + btScalar maxTh = SIMD_HALF_PI; + btScalar minPs = -SIMD_HALF_PI; + btScalar maxPs = SIMD_HALF_PI; + btScalar stepDegrees = 30.f; + drawSpherePatch(center, up, axis, radius,minTh, maxTh, minPs, maxPs, color, stepDegrees ,false); + drawSpherePatch(center, up, -axis, radius,minTh, maxTh, minPs, maxPs, color, stepDegrees,false ); } virtual void drawSphere (const btVector3& p, btScalar radius, const btVector3& color) @@ -179,7 +167,7 @@ class btIDebugDraw } } virtual void drawSpherePatch(const btVector3& center, const btVector3& up, const btVector3& axis, btScalar radius, - btScalar minTh, btScalar maxTh, btScalar minPs, btScalar maxPs, const btVector3& color, btScalar stepDegrees = btScalar(10.f)) + btScalar minTh, btScalar maxTh, btScalar minPs, btScalar maxPs, const btVector3& color, btScalar stepDegrees = btScalar(10.f),bool drawCenter = true) { btVector3 vA[74]; btVector3 vB[74]; @@ -261,18 +249,22 @@ class btIDebugDraw { drawLine(npole, pvB[j], color); } - if(isClosed) + + if (drawCenter) { - if(j == (n_vert-1)) + if(isClosed) { - drawLine(arcStart, pvB[j], color); + if(j == (n_vert-1)) + { + drawLine(arcStart, pvB[j], color); + } } - } - else - { - if(((!i) || (i == (n_hor-1))) && ((!j) || (j == (n_vert-1)))) + else { - drawLine(center, pvB[j], color); + if(((!i) || (i == (n_hor-1))) && ((!j) || (j == (n_vert-1)))) + { + drawLine(center, pvB[j], color); + } } } } @@ -314,6 +306,8 @@ class btIDebugDraw virtual void drawCapsule(btScalar radius, btScalar halfHeight, int upAxis, const btTransform& transform, const btVector3& color) { + int stepDegrees = 30; + btVector3 capStart(0.f,0.f,0.f); capStart[upAxis] = -halfHeight; @@ -325,34 +319,47 @@ class btIDebugDraw btTransform childTransform = transform; childTransform.getOrigin() = transform * capStart; - drawSphere(radius, childTransform, color); + { + btVector3 center = childTransform.getOrigin(); + btVector3 up = childTransform.getBasis().getColumn((upAxis+1)%3); + btVector3 axis = -childTransform.getBasis().getColumn(upAxis); + btScalar minTh = -SIMD_HALF_PI; + btScalar maxTh = SIMD_HALF_PI; + btScalar minPs = -SIMD_HALF_PI; + btScalar maxPs = SIMD_HALF_PI; + + drawSpherePatch(center, up, axis, radius,minTh, maxTh, minPs, maxPs, color, btScalar(stepDegrees) ,false); + } + + + } { btTransform childTransform = transform; childTransform.getOrigin() = transform * capEnd; - drawSphere(radius, childTransform, color); + { + btVector3 center = childTransform.getOrigin(); + btVector3 up = childTransform.getBasis().getColumn((upAxis+1)%3); + btVector3 axis = childTransform.getBasis().getColumn(upAxis); + btScalar minTh = -SIMD_HALF_PI; + btScalar maxTh = SIMD_HALF_PI; + btScalar minPs = -SIMD_HALF_PI; + btScalar maxPs = SIMD_HALF_PI; + drawSpherePatch(center, up, axis, radius,minTh, maxTh, minPs, maxPs, color, btScalar(stepDegrees) ,false); + } } // Draw some additional lines btVector3 start = transform.getOrigin(); - capStart[(upAxis+1)%3] = radius; - capEnd[(upAxis+1)%3] = radius; - drawLine(start+transform.getBasis() * capStart,start+transform.getBasis() * capEnd, color); - capStart[(upAxis+1)%3] = -radius; - capEnd[(upAxis+1)%3] = -radius; - drawLine(start+transform.getBasis() * capStart,start+transform.getBasis() * capEnd, color); - - capStart[(upAxis+1)%3] = 0.f; - capEnd[(upAxis+1)%3] = 0.f; - - capStart[(upAxis+2)%3] = radius; - capEnd[(upAxis+2)%3] = radius; - drawLine(start+transform.getBasis() * capStart,start+transform.getBasis() * capEnd, color); - capStart[(upAxis+2)%3] = -radius; - capEnd[(upAxis+2)%3] = -radius; - drawLine(start+transform.getBasis() * capStart,start+transform.getBasis() * capEnd, color); + for (int i=0;i<360;i+=stepDegrees) + { + capEnd[(upAxis+1)%3] = capStart[(upAxis+1)%3] = btSin(btScalar(i)*SIMD_RADS_PER_DEG)*radius; + capEnd[(upAxis+2)%3] = capStart[(upAxis+2)%3] = btCos(btScalar(i)*SIMD_RADS_PER_DEG)*radius; + drawLine(start+transform.getBasis() * capStart,start+transform.getBasis() * capEnd, color); + } + } virtual void drawCylinder(btScalar radius, btScalar halfHeight, int upAxis, const btTransform& transform, const btVector3& color) @@ -360,11 +367,18 @@ class btIDebugDraw btVector3 start = transform.getOrigin(); btVector3 offsetHeight(0,0,0); offsetHeight[upAxis] = halfHeight; - btVector3 offsetRadius(0,0,0); - offsetRadius[(upAxis+1)%3] = radius; - drawLine(start+transform.getBasis() * (offsetHeight+offsetRadius),start+transform.getBasis() * (-offsetHeight+offsetRadius),color); - drawLine(start+transform.getBasis() * (offsetHeight-offsetRadius),start+transform.getBasis() * (-offsetHeight-offsetRadius),color); + int stepDegrees=30; + btVector3 capStart(0.f,0.f,0.f); + capStart[upAxis] = -halfHeight; + btVector3 capEnd(0.f,0.f,0.f); + capEnd[upAxis] = halfHeight; + for (int i=0;i<360;i+=stepDegrees) + { + capEnd[(upAxis+1)%3] = capStart[(upAxis+1)%3] = btSin(btScalar(i)*SIMD_RADS_PER_DEG)*radius; + capEnd[(upAxis+2)%3] = capStart[(upAxis+2)%3] = btCos(btScalar(i)*SIMD_RADS_PER_DEG)*radius; + drawLine(start+transform.getBasis() * capStart,start+transform.getBasis() * capEnd, color); + } // Drawing top and bottom caps of the cylinder btVector3 yaxis(0,0,0); yaxis[upAxis] = btScalar(1.0); @@ -376,16 +390,28 @@ class btIDebugDraw virtual void drawCone(btScalar radius, btScalar height, int upAxis, const btTransform& transform, const btVector3& color) { - + int stepDegrees = 30; btVector3 start = transform.getOrigin(); btVector3 offsetHeight(0,0,0); - offsetHeight[upAxis] = height * btScalar(0.5); + btScalar halfHeight = height * btScalar(0.5); + offsetHeight[upAxis] = halfHeight; btVector3 offsetRadius(0,0,0); offsetRadius[(upAxis+1)%3] = radius; btVector3 offset2Radius(0,0,0); offset2Radius[(upAxis+2)%3] = radius; + + btVector3 capEnd(0.f,0.f,0.f); + capEnd[upAxis] = -halfHeight; + + for (int i=0;i<360;i+=stepDegrees) + { + capEnd[(upAxis+1)%3] = btSin(btScalar(i)*SIMD_RADS_PER_DEG)*radius; + capEnd[(upAxis+2)%3] = btCos(btScalar(i)*SIMD_RADS_PER_DEG)*radius; + drawLine(start+transform.getBasis() * (offsetHeight),start+transform.getBasis() * capEnd, color); + } + drawLine(start+transform.getBasis() * (offsetHeight),start+transform.getBasis() * (-offsetHeight+offsetRadius),color); drawLine(start+transform.getBasis() * (offsetHeight),start+transform.getBasis() * (-offsetHeight-offsetRadius),color); drawLine(start+transform.getBasis() * (offsetHeight),start+transform.getBasis() * (-offsetHeight+offset2Radius),color); diff --git a/Engine/lib/bullet/src/LinearMath/btMatrix3x3.h b/Engine/lib/bullet/src/LinearMath/btMatrix3x3.h index d4f5c95aa..14fe704f8 100644 --- a/Engine/lib/bullet/src/LinearMath/btMatrix3x3.h +++ b/Engine/lib/bullet/src/LinearMath/btMatrix3x3.h @@ -22,10 +22,15 @@ subject to the following restrictions: #ifdef BT_USE_SSE //const __m128 ATTRIBUTE_ALIGNED16(v2220) = {2.0f, 2.0f, 2.0f, 0.0f}; -const __m128 ATTRIBUTE_ALIGNED16(vMPPP) = {-0.0f, +0.0f, +0.0f, +0.0f}; +//const __m128 ATTRIBUTE_ALIGNED16(vMPPP) = {-0.0f, +0.0f, +0.0f, +0.0f}; +#define vMPPP (_mm_set_ps (+0.0f, +0.0f, +0.0f, -0.0f)) #endif -#if defined(BT_USE_SSE) || defined(BT_USE_NEON) +#if defined(BT_USE_SSE) +#define v1000 (_mm_set_ps(0.0f,0.0f,0.0f,1.0f)) +#define v0100 (_mm_set_ps(0.0f,0.0f,1.0f,0.0f)) +#define v0010 (_mm_set_ps(0.0f,1.0f,0.0f,0.0f)) +#elif defined(BT_USE_NEON) const btSimdFloat4 ATTRIBUTE_ALIGNED16(v1000) = {1.0f, 0.0f, 0.0f, 0.0f}; const btSimdFloat4 ATTRIBUTE_ALIGNED16(v0100) = {0.0f, 1.0f, 0.0f, 0.0f}; const btSimdFloat4 ATTRIBUTE_ALIGNED16(v0010) = {0.0f, 0.0f, 1.0f, 0.0f}; @@ -207,7 +212,7 @@ public: btFullAssert(d != btScalar(0.0)); btScalar s = btScalar(2.0) / d; - #if defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) + #if defined BT_USE_SIMD_VECTOR3 && defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) __m128 vs, Q = q.get128(); __m128i Qi = btCastfTo128i(Q); __m128 Y, Z; @@ -341,7 +346,7 @@ public: * @param m The array to be filled */ void getOpenGLSubMatrix(btScalar *m) const { -#if defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) +#if defined BT_USE_SIMD_VECTOR3 && defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) __m128 v0 = m_el[0].mVec128; __m128 v1 = m_el[1].mVec128; __m128 v2 = m_el[2].mVec128; // x2 y2 z2 w2 @@ -362,7 +367,7 @@ public: vm[2] = v2; #elif defined(BT_USE_NEON) // note: zeros the w channel. We can preserve it at the cost of two more vtrn instructions. - static const uint32x2_t zMask = (const uint32x2_t) {-1, 0 }; + static const uint32x2_t zMask = (const uint32x2_t) {static_cast(-1), 0 }; float32x4_t *vm = (float32x4_t *)m; float32x4x2_t top = vtrnq_f32( m_el[0].mVec128, m_el[1].mVec128 ); // {x0 x1 z0 z1}, {y0 y1 w0 w1} float32x2x2_t bl = vtrn_f32( vget_low_f32(m_el[2].mVec128), vdup_n_f32(0.0f) ); // {x2 0 }, {y2 0} @@ -740,7 +745,7 @@ public: SIMD_FORCE_INLINE btMatrix3x3& btMatrix3x3::operator*=(const btMatrix3x3& m) { -#if defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) +#if defined BT_USE_SIMD_VECTOR3 && defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) __m128 rv00, rv01, rv02; __m128 rv10, rv11, rv12; __m128 rv20, rv21, rv22; @@ -953,7 +958,7 @@ btMatrix3x3::determinant() const SIMD_FORCE_INLINE btMatrix3x3 btMatrix3x3::absolute() const { -#if (defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE)) +#if defined BT_USE_SIMD_VECTOR3 && (defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE)) return btMatrix3x3( _mm_and_ps(m_el[0].mVec128, btvAbsfMask), _mm_and_ps(m_el[1].mVec128, btvAbsfMask), @@ -974,7 +979,7 @@ btMatrix3x3::absolute() const SIMD_FORCE_INLINE btMatrix3x3 btMatrix3x3::transpose() const { -#if (defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE)) +#if defined BT_USE_SIMD_VECTOR3 && (defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE)) __m128 v0 = m_el[0].mVec128; __m128 v1 = m_el[1].mVec128; __m128 v2 = m_el[2].mVec128; // x2 y2 z2 w2 @@ -993,7 +998,7 @@ btMatrix3x3::transpose() const return btMatrix3x3( v0, v1, v2 ); #elif defined(BT_USE_NEON) // note: zeros the w channel. We can preserve it at the cost of two more vtrn instructions. - static const uint32x2_t zMask = (const uint32x2_t) {-1, 0 }; + static const uint32x2_t zMask = (const uint32x2_t) {static_cast(-1), 0 }; float32x4x2_t top = vtrnq_f32( m_el[0].mVec128, m_el[1].mVec128 ); // {x0 x1 z0 z1}, {y0 y1 w0 w1} float32x2x2_t bl = vtrn_f32( vget_low_f32(m_el[2].mVec128), vdup_n_f32(0.0f) ); // {x2 0 }, {y2 0} float32x4_t v0 = vcombine_f32( vget_low_f32(top.val[0]), bl.val[0] ); @@ -1031,7 +1036,7 @@ btMatrix3x3::inverse() const SIMD_FORCE_INLINE btMatrix3x3 btMatrix3x3::transposeTimes(const btMatrix3x3& m) const { -#if (defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE)) +#if defined BT_USE_SIMD_VECTOR3 && (defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE)) // zeros w // static const __m128i xyzMask = (const __m128i){ -1ULL, 0xffffffffULL }; __m128 row = m_el[0].mVec128; @@ -1053,7 +1058,7 @@ btMatrix3x3::transposeTimes(const btMatrix3x3& m) const #elif defined BT_USE_NEON // zeros w - static const uint32x4_t xyzMask = (const uint32x4_t){ -1, -1, -1, 0 }; + static const uint32x4_t xyzMask = (const uint32x4_t){ static_cast(-1), static_cast(-1), static_cast(-1), 0 }; float32x4_t m0 = (float32x4_t) vandq_u32( (uint32x4_t) m.getRow(0).mVec128, xyzMask ); float32x4_t m1 = (float32x4_t) vandq_u32( (uint32x4_t) m.getRow(1).mVec128, xyzMask ); float32x4_t m2 = (float32x4_t) vandq_u32( (uint32x4_t) m.getRow(2).mVec128, xyzMask ); @@ -1151,7 +1156,7 @@ operator*(const btMatrix3x3& m, const btVector3& v) SIMD_FORCE_INLINE btVector3 operator*(const btVector3& v, const btMatrix3x3& m) { -#if (defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE)) +#if defined BT_USE_SIMD_VECTOR3 && (defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE)) const __m128 vv = v.mVec128; @@ -1191,7 +1196,7 @@ operator*(const btVector3& v, const btMatrix3x3& m) SIMD_FORCE_INLINE btMatrix3x3 operator*(const btMatrix3x3& m1, const btMatrix3x3& m2) { -#if (defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE)) +#if defined BT_USE_SIMD_VECTOR3 && (defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE)) __m128 m10 = m1[0].mVec128; __m128 m11 = m1[1].mVec128; diff --git a/Engine/lib/bullet/src/LinearMath/btMatrixX.h b/Engine/lib/bullet/src/LinearMath/btMatrixX.h new file mode 100644 index 000000000..1c29632c5 --- /dev/null +++ b/Engine/lib/bullet/src/LinearMath/btMatrixX.h @@ -0,0 +1,504 @@ +/* +Bullet Continuous Collision Detection and Physics Library +Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org + +This software is provided 'as-is', without any express or implied warranty. +In no event will the authors be held liable for any damages arising from the use of this software. +Permission is granted to anyone to use this software for any purpose, +including commercial applications, and to alter it and redistribute it freely, +subject to the following restrictions: + +1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. +2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. +3. This notice may not be removed or altered from any source distribution. +*/ +///original version written by Erwin Coumans, October 2013 + +#ifndef BT_MATRIX_X_H +#define BT_MATRIX_X_H + +#include "LinearMath/btQuickprof.h" +#include "LinearMath/btAlignedObjectArray.h" + +class btIntSortPredicate +{ + public: + bool operator() ( const int& a, const int& b ) const + { + return a < b; + } +}; + + +template +struct btMatrixX +{ + int m_rows; + int m_cols; + int m_operations; + int m_resizeOperations; + int m_setElemOperations; + + btAlignedObjectArray m_storage; + btAlignedObjectArray< btAlignedObjectArray > m_rowNonZeroElements1; + btAlignedObjectArray< btAlignedObjectArray > m_colNonZeroElements; + + T* getBufferPointerWritable() + { + return m_storage.size() ? &m_storage[0] : 0; + } + + const T* getBufferPointer() const + { + return m_storage.size() ? &m_storage[0] : 0; + } + btMatrixX() + :m_rows(0), + m_cols(0), + m_operations(0), + m_resizeOperations(0), + m_setElemOperations(0) + { + } + btMatrixX(int rows,int cols) + :m_rows(rows), + m_cols(cols), + m_operations(0), + m_resizeOperations(0), + m_setElemOperations(0) + { + resize(rows,cols); + } + void resize(int rows, int cols) + { + m_resizeOperations++; + m_rows = rows; + m_cols = cols; + { + BT_PROFILE("m_storage.resize"); + m_storage.resize(rows*cols); + } + clearSparseInfo(); + } + int cols() const + { + return m_cols; + } + int rows() const + { + return m_rows; + } + ///we don't want this read/write operator(), because we cannot keep track of non-zero elements, use setElem instead + /*T& operator() (int row,int col) + { + return m_storage[col*m_rows+row]; + } + */ + + void addElem(int row,int col, T val) + { + if (val) + { + if (m_storage[col+row*m_cols]==0.f) + { + setElem(row,col,val); + } else + { + m_storage[row*m_cols+col] += val; + } + } + } + + void copyLowerToUpperTriangle() + { + int count=0; + for (int row=0;row0 && numRowsOther>0 && B && C); + const btScalar *bb = B; + for ( int i = 0;i +struct btVectorX +{ + btAlignedObjectArray m_storage; + + btVectorX() + { + } + btVectorX(int numRows) + { + m_storage.resize(numRows); + } + + void resize(int rows) + { + m_storage.resize(rows); + } + int cols() const + { + return 1; + } + int rows() const + { + return m_storage.size(); + } + int size() const + { + return rows(); + } + void setZero() + { + // for (int i=0;i +void setElem(btMatrixX& mat, int row, int col, T val) +{ + mat.setElem(row,col,val); +} +*/ + + +typedef btMatrixX btMatrixXf; +typedef btVectorX btVectorXf; + +typedef btMatrixX btMatrixXd; +typedef btVectorX btVectorXd; + + + +inline void setElem(btMatrixXd& mat, int row, int col, double val) +{ + mat.setElem(row,col,val); +} + +inline void setElem(btMatrixXf& mat, int row, int col, float val) +{ + mat.setElem(row,col,val); +} + +#ifdef BT_USE_DOUBLE_PRECISION + #define btVectorXu btVectorXd + #define btMatrixXu btMatrixXd +#else + #define btVectorXu btVectorXf + #define btMatrixXu btMatrixXf +#endif //BT_USE_DOUBLE_PRECISION + + + +#endif//BT_MATRIX_H_H diff --git a/Engine/lib/bullet/src/LinearMath/btPolarDecomposition.cpp b/Engine/lib/bullet/src/LinearMath/btPolarDecomposition.cpp index d7de20408..a4dca7fdd 100644 --- a/Engine/lib/bullet/src/LinearMath/btPolarDecomposition.cpp +++ b/Engine/lib/bullet/src/LinearMath/btPolarDecomposition.cpp @@ -60,10 +60,10 @@ unsigned int btPolarDecomposition::decompose(const btMatrix3x3& a, btMatrix3x3& break; const btScalar gamma = btPow(h_norm / u_norm, 0.25f); - const btScalar inv_gamma = 1.0 / gamma; + const btScalar inv_gamma = btScalar(1.0) / gamma; // Determine the delta to 'u' - const btMatrix3x3 delta = (u * (gamma - 2.0) + h.transpose() * inv_gamma) * 0.5; + const btMatrix3x3 delta = (u * (gamma - btScalar(2.0)) + h.transpose() * inv_gamma) * btScalar(0.5); // Update the matrices u += delta; diff --git a/Engine/lib/bullet/src/LinearMath/btQuaternion.h b/Engine/lib/bullet/src/LinearMath/btQuaternion.h index a1db819de..665421de1 100644 --- a/Engine/lib/bullet/src/LinearMath/btQuaternion.h +++ b/Engine/lib/bullet/src/LinearMath/btQuaternion.h @@ -27,11 +27,17 @@ subject to the following restrictions: #ifdef BT_USE_SSE -const __m128 ATTRIBUTE_ALIGNED16(vOnes) = {1.0f, 1.0f, 1.0f, 1.0f}; +//const __m128 ATTRIBUTE_ALIGNED16(vOnes) = {1.0f, 1.0f, 1.0f, 1.0f}; +#define vOnes (_mm_set_ps(1.0f, 1.0f, 1.0f, 1.0f)) #endif -#if defined(BT_USE_SSE) || defined(BT_USE_NEON) +#if defined(BT_USE_SSE) + +#define vQInv (_mm_set_ps(+0.0f, -0.0f, -0.0f, -0.0f)) +#define vPPPM (_mm_set_ps(-0.0f, +0.0f, +0.0f, +0.0f)) + +#elif defined(BT_USE_NEON) const btSimdFloat4 ATTRIBUTE_ALIGNED16(vQInv) = {-0.0f, -0.0f, -0.0f, +0.0f}; const btSimdFloat4 ATTRIBUTE_ALIGNED16(vPPPM) = {+0.0f, +0.0f, +0.0f, -0.0f}; @@ -285,7 +291,7 @@ public: * @param q The other quaternion */ btScalar dot(const btQuaternion& q) const { -#if defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) +#if defined BT_USE_SIMD_VECTOR3 && defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) __m128 vd; vd = _mm_mul_ps(mVec128, q.mVec128); @@ -384,7 +390,7 @@ public: { return *this / length(); } - /**@brief Return the angle between this quaternion and the other + /**@brief Return the ***half*** angle between this quaternion and the other * @param q The other quaternion */ btScalar angle(const btQuaternion& q) const { @@ -392,6 +398,19 @@ public: btAssert(s != btScalar(0.0)); return btAcos(dot(q) / s); } + + /**@brief Return the angle between this quaternion and the other along the shortest path + * @param q The other quaternion */ + btScalar angleShortestPath(const btQuaternion& q) const + { + btScalar s = btSqrt(length2() * q.length2()); + btAssert(s != btScalar(0.0)); + if (dot(q) < 0) // Take care of long angle case see http://en.wikipedia.org/wiki/Slerp + return btAcos(dot(-q) / s) * btScalar(2.0); + else + return btAcos(dot(q) / s) * btScalar(2.0); + } + /**@brief Return the angle of rotation represented by this quaternion */ btScalar getAngle() const { @@ -399,6 +418,19 @@ public: return s; } + /**@brief Return the angle of rotation represented by this quaternion along the shortest path*/ + btScalar getAngleShortestPath() const + { + btScalar s; + if (dot(*this) < 0) + s = btScalar(2.) * btAcos(m_floats[3]); + else + s = btScalar(2.) * btAcos(-m_floats[3]); + + return s; + } + + /**@brief Return the axis of the rotation represented by this quaternion */ btVector3 getAxis() const { @@ -498,7 +530,7 @@ public: btAssert(magnitude > btScalar(0)); btScalar product = dot(q) / magnitude; - if (btFabs(product) != btScalar(1)) + if (btFabs(product) < btScalar(1)) { // Take care of long angle case see http://en.wikipedia.org/wiki/Slerp const btScalar sign = (product < 0) ? btScalar(-1) : btScalar(1); @@ -835,7 +867,7 @@ quatRotate(const btQuaternion& rotation, const btVector3& v) { btQuaternion q = rotation * v; q *= rotation.inverse(); -#if defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) +#if defined BT_USE_SIMD_VECTOR3 && defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) return btVector3(_mm_and_ps(q.get128(), btvFFF0fMask)); #elif defined(BT_USE_NEON) return btVector3((float32x4_t)vandq_s32((int32x4_t)q.get128(), btvFFF0Mask)); diff --git a/Engine/lib/bullet/src/LinearMath/btScalar.h b/Engine/lib/bullet/src/LinearMath/btScalar.h index 7154b8f0b..37c6dec19 100644 --- a/Engine/lib/bullet/src/LinearMath/btScalar.h +++ b/Engine/lib/bullet/src/LinearMath/btScalar.h @@ -28,7 +28,7 @@ subject to the following restrictions: #include /* SVN $Revision$ on $Date$ from http://bullet.googlecode.com*/ -#define BT_BULLET_VERSION 281 +#define BT_BULLET_VERSION 282 inline int btGetVersion() { @@ -68,6 +68,10 @@ inline int btGetVersion() #else #if (defined (_WIN32) && (_MSC_VER) && _MSC_VER >= 1400) && (!defined (BT_USE_DOUBLE_PRECISION)) + #if _MSC_VER>1400 + #define BT_USE_SIMD_VECTOR3 + #endif + #define BT_USE_SSE #ifdef BT_USE_SSE //BT_USE_SSE_IN_API is disabled under Windows by default, because @@ -159,7 +163,8 @@ inline int btGetVersion() #if (defined (__APPLE__) && (!defined (BT_USE_DOUBLE_PRECISION))) #if defined (__i386__) || defined (__x86_64__) - #define BT_USE_SSE + #define BT_USE_SIMD_VECTOR3 + #define BT_USE_SSE //BT_USE_SSE_IN_API is enabled on Mac OSX by default, because memory is automatically aligned on 16-byte boundaries //if apps run into issues, we will disable the next line #define BT_USE_SSE_IN_API @@ -175,10 +180,11 @@ inline int btGetVersion() #include #endif #endif //BT_USE_SSE - #elif defined( __armv7__ ) + #elif defined( __ARM_NEON__ ) #ifdef __clang__ #define BT_USE_NEON 1 - + #define BT_USE_SIMD_VECTOR3 + #if defined BT_USE_NEON && defined (__clang__) #include #endif//BT_USE_NEON @@ -207,8 +213,7 @@ inline int btGetVersion() } #else//defined (__i386__) || defined (__x86_64__) #define btAssert assert - #end//defined (__i386__) || defined (__x86_64__) - #endif + #endif//defined (__i386__) || defined (__x86_64__) #else//defined(DEBUG) || defined (_DEBUG) #define btAssert(x) #endif//defined(DEBUG) || defined (_DEBUG) @@ -252,10 +257,12 @@ inline int btGetVersion() ///The btScalar type abstracts floating point numbers, to easily switch between double and single floating point precision. #if defined(BT_USE_DOUBLE_PRECISION) + typedef double btScalar; //this number could be bigger in double precision #define BT_LARGE_FLOAT 1e30 #else + typedef float btScalar; //keep BT_LARGE_FLOAT*BT_LARGE_FLOAT < FLT_MAX #define BT_LARGE_FLOAT 1e18f @@ -265,7 +272,8 @@ typedef float btScalar; typedef __m128 btSimdFloat4; #endif//BT_USE_SSE -#if defined BT_USE_SSE_IN_API && defined (BT_USE_SSE) +#if defined (BT_USE_SSE) +//#if defined BT_USE_SSE_IN_API && defined (BT_USE_SSE) #ifdef _WIN32 #ifndef BT_NAN @@ -278,6 +286,8 @@ static int btInfinityMask = 0x7F800000; #define BT_INFINITY (*(float*)&btInfinityMask) #endif +//use this, in case there are clashes (such as xnamath.h) +#ifndef BT_NO_SIMD_OPERATOR_OVERLOADS inline __m128 operator + (const __m128 A, const __m128 B) { return _mm_add_ps(A, B); @@ -292,6 +302,7 @@ inline __m128 operator * (const __m128 A, const __m128 B) { return _mm_mul_ps(A, B); } +#endif //BT_NO_SIMD_OPERATOR_OVERLOADS #define btCastfTo128i(a) (_mm_castps_si128(a)) #define btCastfTo128d(a) (_mm_castps_pd(a)) @@ -311,7 +322,24 @@ inline __m128 operator * (const __m128 A, const __m128 B) #define BT_INFINITY INFINITY #define BT_NAN NAN #endif//_WIN32 -#endif //BT_USE_SSE_IN_API +#else + +#ifdef BT_USE_NEON + #include + + typedef float32x4_t btSimdFloat4; + #define BT_INFINITY INFINITY + #define BT_NAN NAN + #define btAssign128(r0,r1,r2,r3) (float32x4_t){r0,r1,r2,r3} +#else//BT_USE_NEON + + #ifndef BT_INFINITY + static int btInfinityMask = 0x7F800000; + #define BT_INFINITY (*(float*)&btInfinityMask) + #endif +#endif//BT_USE_NEON + +#endif //BT_USE_SSE #ifdef BT_USE_NEON #include @@ -403,15 +431,15 @@ SIMD_FORCE_INLINE btScalar btFmod(btScalar x,btScalar y) { return fmodf(x,y); } #endif -#define SIMD_2_PI btScalar(6.283185307179586232) -#define SIMD_PI (SIMD_2_PI * btScalar(0.5)) -#define SIMD_HALF_PI (SIMD_2_PI * btScalar(0.25)) +#define SIMD_PI btScalar(3.1415926535897932384626433832795029) +#define SIMD_2_PI btScalar(2.0) * SIMD_PI +#define SIMD_HALF_PI (SIMD_PI * btScalar(0.5)) #define SIMD_RADS_PER_DEG (SIMD_2_PI / btScalar(360.0)) #define SIMD_DEGS_PER_RAD (btScalar(360.0) / SIMD_2_PI) #define SIMDSQRT12 btScalar(0.7071067811865475244008443621048490) #define btRecipSqrt(x) ((btScalar)(btScalar(1.0)/btSqrt(btScalar(x)))) /* reciprocal square root */ - +#define btRecip(x) (btScalar(1.0)/btScalar(x)) #ifdef BT_USE_DOUBLE_PRECISION #define SIMD_EPSILON DBL_EPSILON @@ -602,6 +630,46 @@ SIMD_FORCE_INLINE double btUnswapEndianDouble(const unsigned char *src) return d; } +template +SIMD_FORCE_INLINE void btSetZero(T* a, int n) +{ + T* acurr = a; + size_t ncurr = n; + while (ncurr > 0) + { + *(acurr++) = 0; + --ncurr; + } +} + + +SIMD_FORCE_INLINE btScalar btLargeDot(const btScalar *a, const btScalar *b, int n) +{ + btScalar p0,q0,m0,p1,q1,m1,sum; + sum = 0; + n -= 2; + while (n >= 0) { + p0 = a[0]; q0 = b[0]; + m0 = p0 * q0; + p1 = a[1]; q1 = b[1]; + m1 = p1 * q1; + sum += m0; + sum += m1; + a += 2; + b += 2; + n -= 2; + } + n += 2; + while (n > 0) { + sum += (*a) * (*b); + a++; + b++; + n--; + } + return sum; +} + + // returns normalized value in range [-SIMD_PI, SIMD_PI] SIMD_FORCE_INLINE btScalar btNormalizeAngle(btScalar angleInRadians) { @@ -620,6 +688,8 @@ SIMD_FORCE_INLINE btScalar btNormalizeAngle(btScalar angleInRadians) } } + + ///rudimentary class to provide type info struct btTypedObject { diff --git a/Engine/lib/bullet/src/LinearMath/btSerializer.cpp b/Engine/lib/bullet/src/LinearMath/btSerializer.cpp index d6b2b3a5a..ba3449395 100644 --- a/Engine/lib/bullet/src/LinearMath/btSerializer.cpp +++ b/Engine/lib/bullet/src/LinearMath/btSerializer.cpp @@ -1,5 +1,5 @@ char sBulletDNAstr[]= { -char(83),char(68),char(78),char(65),char(78),char(65),char(77),char(69),char(63),char(1),char(0),char(0),char(109),char(95),char(115),char(105),char(122),char(101),char(0),char(109), +char(83),char(68),char(78),char(65),char(78),char(65),char(77),char(69),char(69),char(1),char(0),char(0),char(109),char(95),char(115),char(105),char(122),char(101),char(0),char(109), char(95),char(99),char(97),char(112),char(97),char(99),char(105),char(116),char(121),char(0),char(42),char(109),char(95),char(100),char(97),char(116),char(97),char(0),char(109),char(95), char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(115),char(0),char(109),char(95),char(99),char(111), char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116),char(115),char(0),char(109),char(95),char(99),char(111),char(110), @@ -58,238 +58,242 @@ char(102),char(111),char(114),char(109),char(0),char(42),char(109),char(95),char char(95),char(99),char(104),char(105),char(108),char(100),char(83),char(104),char(97),char(112),char(101),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(99),char(104), char(105),char(108),char(100),char(77),char(97),char(114),char(103),char(105),char(110),char(0),char(42),char(109),char(95),char(99),char(104),char(105),char(108),char(100),char(83),char(104), char(97),char(112),char(101),char(80),char(116),char(114),char(0),char(109),char(95),char(110),char(117),char(109),char(67),char(104),char(105),char(108),char(100),char(83),char(104),char(97), -char(112),char(101),char(115),char(0),char(109),char(95),char(117),char(112),char(65),char(120),char(105),char(115),char(0),char(109),char(95),char(102),char(108),char(97),char(103),char(115), -char(0),char(109),char(95),char(101),char(100),char(103),char(101),char(86),char(48),char(86),char(49),char(65),char(110),char(103),char(108),char(101),char(0),char(109),char(95),char(101), -char(100),char(103),char(101),char(86),char(49),char(86),char(50),char(65),char(110),char(103),char(108),char(101),char(0),char(109),char(95),char(101),char(100),char(103),char(101),char(86), -char(50),char(86),char(48),char(65),char(110),char(103),char(108),char(101),char(0),char(42),char(109),char(95),char(104),char(97),char(115),char(104),char(84),char(97),char(98),char(108), -char(101),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(110),char(101),char(120),char(116),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(118), -char(97),char(108),char(117),char(101),char(65),char(114),char(114),char(97),char(121),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(107),char(101),char(121),char(65), -char(114),char(114),char(97),char(121),char(80),char(116),char(114),char(0),char(109),char(95),char(99),char(111),char(110),char(118),char(101),char(120),char(69),char(112),char(115),char(105), -char(108),char(111),char(110),char(0),char(109),char(95),char(112),char(108),char(97),char(110),char(97),char(114),char(69),char(112),char(115),char(105),char(108),char(111),char(110),char(0), -char(109),char(95),char(101),char(113),char(117),char(97),char(108),char(86),char(101),char(114),char(116),char(101),char(120),char(84),char(104),char(114),char(101),char(115),char(104),char(111), -char(108),char(100),char(0),char(109),char(95),char(101),char(100),char(103),char(101),char(68),char(105),char(115),char(116),char(97),char(110),char(99),char(101),char(84),char(104),char(114), -char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(122),char(101),char(114),char(111),char(65),char(114),char(101),char(97),char(84),char(104),char(114), -char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(110),char(101),char(120),char(116),char(83),char(105),char(122),char(101),char(0),char(109),char(95), -char(104),char(97),char(115),char(104),char(84),char(97),char(98),char(108),char(101),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(110),char(117),char(109),char(86), -char(97),char(108),char(117),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(75),char(101),char(121),char(115),char(0),char(109),char(95),char(103),char(105), -char(109),char(112),char(97),char(99),char(116),char(83),char(117),char(98),char(84),char(121),char(112),char(101),char(0),char(42),char(109),char(95),char(117),char(110),char(115),char(99), -char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115),char(70),char(108),char(111),char(97),char(116),char(80),char(116),char(114),char(0),char(42), -char(109),char(95),char(117),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115),char(68),char(111),char(117),char(98), -char(108),char(101),char(80),char(116),char(114),char(0),char(109),char(95),char(110),char(117),char(109),char(85),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80), -char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(112),char(97),char(100),char(100),char(105),char(110),char(103),char(51),char(91),char(52),char(93),char(0), -char(42),char(109),char(95),char(98),char(114),char(111),char(97),char(100),char(112),char(104),char(97),char(115),char(101),char(72),char(97),char(110),char(100),char(108),char(101),char(0), -char(42),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0),char(42),char(109), -char(95),char(114),char(111),char(111),char(116),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0), -char(109),char(95),char(119),char(111),char(114),char(108),char(100),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(105), -char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105),char(111),char(110),char(87),char(111),char(114),char(108),char(100),char(84),char(114),char(97), +char(112),char(101),char(115),char(0),char(109),char(95),char(117),char(112),char(65),char(120),char(105),char(115),char(0),char(109),char(95),char(117),char(112),char(73),char(110),char(100), +char(101),char(120),char(0),char(109),char(95),char(102),char(108),char(97),char(103),char(115),char(0),char(109),char(95),char(101),char(100),char(103),char(101),char(86),char(48),char(86), +char(49),char(65),char(110),char(103),char(108),char(101),char(0),char(109),char(95),char(101),char(100),char(103),char(101),char(86),char(49),char(86),char(50),char(65),char(110),char(103), +char(108),char(101),char(0),char(109),char(95),char(101),char(100),char(103),char(101),char(86),char(50),char(86),char(48),char(65),char(110),char(103),char(108),char(101),char(0),char(42), +char(109),char(95),char(104),char(97),char(115),char(104),char(84),char(97),char(98),char(108),char(101),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(110),char(101), +char(120),char(116),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(118),char(97),char(108),char(117),char(101),char(65),char(114),char(114),char(97),char(121),char(80), +char(116),char(114),char(0),char(42),char(109),char(95),char(107),char(101),char(121),char(65),char(114),char(114),char(97),char(121),char(80),char(116),char(114),char(0),char(109),char(95), +char(99),char(111),char(110),char(118),char(101),char(120),char(69),char(112),char(115),char(105),char(108),char(111),char(110),char(0),char(109),char(95),char(112),char(108),char(97),char(110), +char(97),char(114),char(69),char(112),char(115),char(105),char(108),char(111),char(110),char(0),char(109),char(95),char(101),char(113),char(117),char(97),char(108),char(86),char(101),char(114), +char(116),char(101),char(120),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(101),char(100),char(103),char(101),char(68), +char(105),char(115),char(116),char(97),char(110),char(99),char(101),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(122), +char(101),char(114),char(111),char(65),char(114),char(101),char(97),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(110), +char(101),char(120),char(116),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(104),char(97),char(115),char(104),char(84),char(97),char(98),char(108),char(101),char(83), +char(105),char(122),char(101),char(0),char(109),char(95),char(110),char(117),char(109),char(86),char(97),char(108),char(117),char(101),char(115),char(0),char(109),char(95),char(110),char(117), +char(109),char(75),char(101),char(121),char(115),char(0),char(109),char(95),char(103),char(105),char(109),char(112),char(97),char(99),char(116),char(83),char(117),char(98),char(84),char(121), +char(112),char(101),char(0),char(42),char(109),char(95),char(117),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115), +char(70),char(108),char(111),char(97),char(116),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(117),char(110),char(115),char(99),char(97),char(108),char(101),char(100), +char(80),char(111),char(105),char(110),char(116),char(115),char(68),char(111),char(117),char(98),char(108),char(101),char(80),char(116),char(114),char(0),char(109),char(95),char(110),char(117), +char(109),char(85),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(112),char(97), +char(100),char(100),char(105),char(110),char(103),char(51),char(91),char(52),char(93),char(0),char(42),char(109),char(95),char(98),char(114),char(111),char(97),char(100),char(112),char(104), +char(97),char(115),char(101),char(72),char(97),char(110),char(100),char(108),char(101),char(0),char(42),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105), +char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0),char(42),char(109),char(95),char(114),char(111),char(111),char(116),char(67),char(111),char(108),char(108),char(105), +char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0),char(109),char(95),char(119),char(111),char(114),char(108),char(100),char(84),char(114),char(97), char(110),char(115),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105), -char(111),char(110),char(76),char(105),char(110),char(101),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(105), -char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105),char(111),char(110),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(86), -char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(105),char(115),char(111),char(116),char(114),char(111),char(112),char(105), -char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(99),char(116),char(80), -char(114),char(111),char(99),char(101),char(115),char(115),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109), -char(95),char(100),char(101),char(97),char(99),char(116),char(105),char(118),char(97),char(116),char(105),char(111),char(110),char(84),char(105),char(109),char(101),char(0),char(109),char(95), -char(102),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(114),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114), -char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(105),char(116),char(117),char(116),char(105),char(111),char(110), -char(0),char(109),char(95),char(104),char(105),char(116),char(70),char(114),char(97),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(99),char(100), -char(83),char(119),char(101),char(112),char(116),char(83),char(112),char(104),char(101),char(114),char(101),char(82),char(97),char(100),char(105),char(117),char(115),char(0),char(109),char(95), -char(99),char(99),char(100),char(77),char(111),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109), -char(95),char(104),char(97),char(115),char(65),char(110),char(105),char(115),char(111),char(116),char(114),char(111),char(112),char(105),char(99),char(70),char(114),char(105),char(99),char(116), -char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(70),char(108),char(97),char(103),char(115), -char(0),char(109),char(95),char(105),char(115),char(108),char(97),char(110),char(100),char(84),char(97),char(103),char(49),char(0),char(109),char(95),char(99),char(111),char(109),char(112), -char(97),char(110),char(105),char(111),char(110),char(73),char(100),char(0),char(109),char(95),char(97),char(99),char(116),char(105),char(118),char(97),char(116),char(105),char(111),char(110), -char(83),char(116),char(97),char(116),char(101),char(49),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(110),char(97),char(108),char(84),char(121),char(112), -char(101),char(0),char(109),char(95),char(99),char(104),char(101),char(99),char(107),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(87),char(105),char(116),char(104), -char(0),char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114),char(73),char(110),char(102),char(111),char(0),char(109),char(95),char(103),char(114),char(97),char(118), -char(105),char(116),char(121),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99), -char(116),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(105),char(110),char(118),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(84),char(101), -char(110),char(115),char(111),char(114),char(87),char(111),char(114),char(108),char(100),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(86),char(101), -char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111), -char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(70),char(97),char(99),char(116),char(111),char(114), -char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(103),char(114), -char(97),char(118),char(105),char(116),char(121),char(95),char(97),char(99),char(99),char(101),char(108),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(0),char(109), -char(95),char(105),char(110),char(118),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(76),char(111),char(99),char(97),char(108),char(0),char(109),char(95),char(116), -char(111),char(116),char(97),char(108),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(116),char(111),char(116),char(97),char(108),char(84),char(111),char(114), -char(113),char(117),char(101),char(0),char(109),char(95),char(105),char(110),char(118),char(101),char(114),char(115),char(101),char(77),char(97),char(115),char(115),char(0),char(109),char(95), -char(108),char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(110),char(103),char(117), -char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111), -char(110),char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(97), -char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(76),char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109),char(112),char(105), -char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100), -char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110), -char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105), -char(116),char(105),char(111),char(110),char(97),char(108),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103), -char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(108),char(101),char(101),char(112), -char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108), -char(97),char(114),char(83),char(108),char(101),char(101),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0), -char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0), -char(109),char(95),char(110),char(117),char(109),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(82),char(111),char(119),char(115),char(0), -char(110),char(117),char(98),char(0),char(42),char(109),char(95),char(114),char(98),char(65),char(0),char(42),char(109),char(95),char(114),char(98),char(66),char(0),char(109),char(95), -char(111),char(98),char(106),char(101),char(99),char(116),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111),char(110), -char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111), -char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(73),char(100),char(0),char(109),char(95),char(110),char(101),char(101),char(100),char(115),char(70),char(101), -char(101),char(100),char(98),char(97),char(99),char(107),char(0),char(109),char(95),char(97),char(112),char(112),char(108),char(105),char(101),char(100),char(73),char(109),char(112),char(117), -char(108),char(115),char(101),char(0),char(109),char(95),char(100),char(98),char(103),char(68),char(114),char(97),char(119),char(83),char(105),char(122),char(101),char(0),char(109),char(95), -char(100),char(105),char(115),char(97),char(98),char(108),char(101),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(115),char(66),char(101),char(116), -char(119),char(101),char(101),char(110),char(76),char(105),char(110),char(107),char(101),char(100),char(66),char(111),char(100),char(105),char(101),char(115),char(0),char(109),char(95),char(111), -char(118),char(101),char(114),char(114),char(105),char(100),char(101),char(78),char(117),char(109),char(83),char(111),char(108),char(118),char(101),char(114),char(73),char(116),char(101),char(114), -char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(98),char(114),char(101),char(97),char(107),char(105),char(110),char(103),char(73),char(109),char(112), -char(117),char(108),char(115),char(101),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(105),char(115),char(69),char(110), -char(97),char(98),char(108),char(101),char(100),char(0),char(109),char(95),char(116),char(121),char(112),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105), -char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(112),char(105),char(118),char(111),char(116),char(73),char(110),char(65),char(0),char(109),char(95), -char(112),char(105),char(118),char(111),char(116),char(73),char(110),char(66),char(0),char(109),char(95),char(114),char(98),char(65),char(70),char(114),char(97),char(109),char(101),char(0), -char(109),char(95),char(114),char(98),char(66),char(70),char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(82),char(101),char(102),char(101), -char(114),char(101),char(110),char(99),char(101),char(70),char(114),char(97),char(109),char(101),char(65),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97), -char(114),char(79),char(110),char(108),char(121),char(0),char(109),char(95),char(101),char(110),char(97),char(98),char(108),char(101),char(65),char(110),char(103),char(117),char(108),char(97), -char(114),char(77),char(111),char(116),char(111),char(114),char(0),char(109),char(95),char(109),char(111),char(116),char(111),char(114),char(84),char(97),char(114),char(103),char(101),char(116), -char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(109),char(97),char(120),char(77),char(111),char(116),char(111),char(114),char(73), -char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(108),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0), -char(109),char(95),char(117),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(108),char(105),char(109),char(105),char(116), -char(83),char(111),char(102),char(116),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(98),char(105),char(97),char(115),char(70),char(97),char(99),char(116),char(111), -char(114),char(0),char(109),char(95),char(114),char(101),char(108),char(97),char(120),char(97),char(116),char(105),char(111),char(110),char(70),char(97),char(99),char(116),char(111),char(114), -char(0),char(109),char(95),char(115),char(119),char(105),char(110),char(103),char(83),char(112),char(97),char(110),char(49),char(0),char(109),char(95),char(115),char(119),char(105),char(110), -char(103),char(83),char(112),char(97),char(110),char(50),char(0),char(109),char(95),char(116),char(119),char(105),char(115),char(116),char(83),char(112),char(97),char(110),char(0),char(109), -char(95),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(85),char(112),char(112), -char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(76),char(111),char(119),char(101), -char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(85),char(112),char(112),char(101), -char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(76),char(111),char(119),char(101), -char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(117),char(115),char(101),char(76),char(105),char(110),char(101),char(97),char(114),char(82),char(101), -char(102),char(101),char(114),char(101),char(110),char(99),char(101),char(70),char(114),char(97),char(109),char(101),char(65),char(0),char(109),char(95),char(117),char(115),char(101),char(79), -char(102),char(102),char(115),char(101),char(116),char(70),char(111),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(114), -char(97),char(109),char(101),char(0),char(109),char(95),char(54),char(100),char(111),char(102),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(115),char(112),char(114), -char(105),char(110),char(103),char(69),char(110),char(97),char(98),char(108),char(101),char(100),char(91),char(54),char(93),char(0),char(109),char(95),char(101),char(113),char(117),char(105), -char(108),char(105),char(98),char(114),char(105),char(117),char(109),char(80),char(111),char(105),char(110),char(116),char(91),char(54),char(93),char(0),char(109),char(95),char(115),char(112), -char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(91),char(54),char(93),char(0),char(109),char(95),char(115), -char(112),char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(91),char(54),char(93),char(0),char(109),char(95),char(116),char(97), -char(117),char(0),char(109),char(95),char(116),char(105),char(109),char(101),char(83),char(116),char(101),char(112),char(0),char(109),char(95),char(109),char(97),char(120),char(69),char(114), -char(114),char(111),char(114),char(82),char(101),char(100),char(117),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(115),char(111),char(114),char(0),char(109), -char(95),char(101),char(114),char(112),char(0),char(109),char(95),char(101),char(114),char(112),char(50),char(0),char(109),char(95),char(103),char(108),char(111),char(98),char(97),char(108), -char(67),char(102),char(109),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(80),char(101), -char(110),char(101),char(116),char(114),char(97),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109), -char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(84),char(117),char(114),char(110),char(69),char(114),char(112), -char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(108),char(111),char(112),char(0),char(109),char(95),char(119),char(97),char(114),char(109), -char(115),char(116),char(97),char(114),char(116),char(105),char(110),char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(109),char(97),char(120), -char(71),char(121),char(114),char(111),char(115),char(99),char(111),char(112),char(105),char(99),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(115),char(105), -char(110),char(103),char(108),char(101),char(65),char(120),char(105),char(115),char(82),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114),char(105),char(99),char(116), -char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(110),char(117),char(109),char(73),char(116), -char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114),char(77),char(111),char(100), -char(101),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(105),char(110),char(103),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(82),char(101), -char(115),char(116),char(105),char(116),char(117),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109), -char(95),char(109),char(105),char(110),char(105),char(109),char(117),char(109),char(83),char(111),char(108),char(118),char(101),char(114),char(66),char(97),char(116),char(99),char(104),char(83), -char(105),char(122),char(101),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109), -char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(97), -char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(118),char(111), -char(108),char(117),char(109),char(101),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(42),char(109),char(95),char(109),char(97),char(116), -char(101),char(114),char(105),char(97),char(108),char(0),char(109),char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(112), -char(114),char(101),char(118),char(105),char(111),char(117),char(115),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(118),char(101), -char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(99),char(99),char(117),char(109),char(117),char(108),char(97),char(116),char(101),char(100), -char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(110),char(111),char(114),char(109),char(97),char(108),char(0),char(109),char(95),char(97),char(114),char(101), -char(97),char(0),char(109),char(95),char(97),char(116),char(116),char(97),char(99),char(104),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100), -char(105),char(99),char(101),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(76),char(101),char(110),char(103),char(116),char(104), -char(0),char(109),char(95),char(98),char(98),char(101),char(110),char(100),char(105),char(110),char(103),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110), -char(100),char(105),char(99),char(101),char(115),char(91),char(51),char(93),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(65),char(114),char(101),char(97),char(0), -char(109),char(95),char(99),char(48),char(91),char(52),char(93),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101), -char(115),char(91),char(52),char(93),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(86),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95), -char(99),char(49),char(0),char(109),char(95),char(99),char(50),char(0),char(109),char(95),char(99),char(48),char(0),char(109),char(95),char(108),char(111),char(99),char(97),char(108), -char(70),char(114),char(97),char(109),char(101),char(0),char(42),char(109),char(95),char(114),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(0),char(109), -char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(101),char(120),char(0),char(109),char(95),char(97),char(101),char(114),char(111),char(77),char(111),char(100), -char(101),char(108),char(0),char(109),char(95),char(98),char(97),char(117),char(109),char(103),char(97),char(114),char(116),char(101),char(0),char(109),char(95),char(100),char(114),char(97), -char(103),char(0),char(109),char(95),char(108),char(105),char(102),char(116),char(0),char(109),char(95),char(112),char(114),char(101),char(115),char(115),char(117),char(114),char(101),char(0), -char(109),char(95),char(118),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(100),char(121),char(110),char(97),char(109),char(105),char(99),char(70),char(114), -char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(112),char(111),char(115),char(101),char(77),char(97),char(116),char(99),char(104),char(0),char(109), -char(95),char(114),char(105),char(103),char(105),char(100),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115), -char(115),char(0),char(109),char(95),char(107),char(105),char(110),char(101),char(116),char(105),char(99),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97), -char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(67),char(111),char(110),char(116),char(97),char(99),char(116), -char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(97),char(110),char(99),char(104),char(111),char(114),char(72),char(97),char(114), -char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(67),char(108),char(117), -char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(75), -char(105),char(110),char(101),char(116),char(105),char(99),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115), -char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(83),char(111),char(102),char(116),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72), -char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(67), -char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105),char(116),char(0),char(109), -char(95),char(115),char(111),char(102),char(116),char(75),char(105),char(110),char(101),char(116),char(105),char(99),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73), -char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(83),char(111), -char(102),char(116),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105), -char(116),char(0),char(109),char(95),char(109),char(97),char(120),char(86),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(116),char(105),char(109),char(101), -char(83),char(99),char(97),char(108),char(101),char(0),char(109),char(95),char(118),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(73),char(116),char(101),char(114), -char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(73),char(116),char(101), -char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(100),char(114),char(105),char(102),char(116),char(73),char(116),char(101),char(114),char(97), -char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(116),char(101),char(114),char(97), -char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(114),char(111),char(116),char(0),char(109),char(95),char(115),char(99),char(97),char(108),char(101),char(0), -char(109),char(95),char(97),char(113),char(113),char(0),char(109),char(95),char(99),char(111),char(109),char(0),char(42),char(109),char(95),char(112),char(111),char(115),char(105),char(116), -char(105),char(111),char(110),char(115),char(0),char(42),char(109),char(95),char(119),char(101),char(105),char(103),char(104),char(116),char(115),char(0),char(109),char(95),char(110),char(117), -char(109),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(87),char(101),char(105),char(103), -char(116),char(115),char(0),char(109),char(95),char(98),char(118),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(98),char(102),char(114),char(97),char(109), -char(101),char(0),char(109),char(95),char(102),char(114),char(97),char(109),char(101),char(120),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(108),char(111),char(99), -char(105),char(105),char(0),char(109),char(95),char(105),char(110),char(118),char(119),char(105),char(0),char(109),char(95),char(118),char(105),char(109),char(112),char(117),char(108),char(115), -char(101),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(100),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(91),char(50),char(93), -char(0),char(109),char(95),char(108),char(118),char(0),char(109),char(95),char(97),char(118),char(0),char(42),char(109),char(95),char(102),char(114),char(97),char(109),char(101),char(114), -char(101),char(102),char(115),char(0),char(42),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(0),char(42), -char(109),char(95),char(109),char(97),char(115),char(115),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(70),char(114),char(97),char(109),char(101),char(82), -char(101),char(102),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(78),char(111),char(100),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109), -char(77),char(97),char(115),char(115),char(101),char(115),char(0),char(109),char(95),char(105),char(100),char(109),char(97),char(115),char(115),char(0),char(109),char(95),char(105),char(109), -char(97),char(115),char(115),char(0),char(109),char(95),char(110),char(118),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(0),char(109),char(95),char(110), -char(100),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(0),char(109),char(95),char(110),char(100),char(97),char(109),char(112),char(105),char(110),char(103), -char(0),char(109),char(95),char(108),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(100),char(97),char(109),char(112),char(105), -char(110),char(103),char(0),char(109),char(95),char(109),char(97),char(116),char(99),char(104),char(105),char(110),char(103),char(0),char(109),char(95),char(109),char(97),char(120),char(83), -char(101),char(108),char(102),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0), -char(109),char(95),char(115),char(101),char(108),char(102),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(73),char(109),char(112),char(117),char(108), -char(115),char(101),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(105),char(110),char(115),char(65), -char(110),char(99),char(104),char(111),char(114),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(100),char(101),char(0),char(109),char(95),char(99),char(108), -char(117),char(115),char(116),char(101),char(114),char(73),char(110),char(100),char(101),char(120),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121),char(65),char(0), -char(42),char(109),char(95),char(98),char(111),char(100),char(121),char(66),char(0),char(109),char(95),char(114),char(101),char(102),char(115),char(91),char(50),char(93),char(0),char(109), -char(95),char(99),char(102),char(109),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(100),char(101),char(108),char(101),char(116), -char(101),char(0),char(109),char(95),char(114),char(101),char(108),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(91),char(50),char(93),char(0),char(109), -char(95),char(98),char(111),char(100),char(121),char(65),char(116),char(121),char(112),char(101),char(0),char(109),char(95),char(98),char(111),char(100),char(121),char(66),char(116),char(121), -char(112),char(101),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(84),char(121),char(112),char(101),char(0),char(42),char(109),char(95),char(112),char(111), -char(115),char(101),char(0),char(42),char(42),char(109),char(95),char(109),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(115),char(0),char(42),char(109),char(95), -char(110),char(111),char(100),char(101),char(115),char(0),char(42),char(109),char(95),char(108),char(105),char(110),char(107),char(115),char(0),char(42),char(109),char(95),char(102),char(97), -char(99),char(101),char(115),char(0),char(42),char(109),char(95),char(116),char(101),char(116),char(114),char(97),char(104),char(101),char(100),char(114),char(97),char(0),char(42),char(109), -char(95),char(97),char(110),char(99),char(104),char(111),char(114),char(115),char(0),char(42),char(109),char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(115), -char(0),char(42),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(77),char(97),char(116),char(101), -char(114),char(105),char(97),char(108),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(76),char(105),char(110),char(107),char(115),char(0),char(109),char(95),char(110), -char(117),char(109),char(70),char(97),char(99),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(84),char(101),char(116),char(114),char(97),char(104),char(101), -char(100),char(114),char(97),char(0),char(109),char(95),char(110),char(117),char(109),char(65),char(110),char(99),char(104),char(111),char(114),char(115),char(0),char(109),char(95),char(110), -char(117),char(109),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(74),char(111),char(105),char(110), -char(116),char(115),char(0),char(109),char(95),char(99),char(111),char(110),char(102),char(105),char(103),char(0),char(84),char(89),char(80),char(69),char(76),char(0),char(0),char(0), -char(99),char(104),char(97),char(114),char(0),char(117),char(99),char(104),char(97),char(114),char(0),char(115),char(104),char(111),char(114),char(116),char(0),char(117),char(115),char(104), -char(111),char(114),char(116),char(0),char(105),char(110),char(116),char(0),char(108),char(111),char(110),char(103),char(0),char(117),char(108),char(111),char(110),char(103),char(0),char(102), -char(108),char(111),char(97),char(116),char(0),char(100),char(111),char(117),char(98),char(108),char(101),char(0),char(118),char(111),char(105),char(100),char(0),char(80),char(111),char(105), -char(110),char(116),char(101),char(114),char(65),char(114),char(114),char(97),char(121),char(0),char(98),char(116),char(80),char(104),char(121),char(115),char(105),char(99),char(115),char(83), -char(121),char(115),char(116),char(101),char(109),char(0),char(76),char(105),char(115),char(116),char(66),char(97),char(115),char(101),char(0),char(98),char(116),char(86),char(101),char(99), -char(116),char(111),char(114),char(51),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(86),char(101),char(99),char(116), -char(111),char(114),char(51),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(97),char(116),char(114), -char(105),char(120),char(51),char(120),char(51),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(97),char(116), -char(114),char(105),char(120),char(51),char(120),char(51),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84), -char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0), -char(98),char(116),char(66),char(118),char(104),char(83),char(117),char(98),char(116),char(114),char(101),char(101),char(73),char(110),char(102),char(111),char(68),char(97),char(116),char(97), -char(0),char(98),char(116),char(79),char(112),char(116),char(105),char(109),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(70), -char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(79),char(112),char(116),char(105),char(109),char(105),char(122),char(101),char(100), -char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(68),char(97),char(116),char(97), -char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(70),char(108),char(111),char(97),char(116), -char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(68), -char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111), -char(110),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(116),char(97),char(116),char(105),char(99),char(80), -char(108),char(97),char(110),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(118), -char(101),char(120),char(73),char(110),char(116),char(101),char(114),char(110),char(97),char(108),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0), -char(98),char(116),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(65),char(110),char(100),char(82),char(97),char(100),char(105),char(117),char(115),char(0), -char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(83),char(112),char(104),char(101),char(114),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97), -char(116),char(97),char(0),char(98),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(83),char(104),char(111),char(114),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(83),char(104),char(111),char(114),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(84),char(114),char(105),char(112),char(108),char(101),char(116), -char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(104),char(97),char(114),char(73),char(110),char(100),char(101),char(120),char(84),char(114),char(105),char(112), -char(108),char(101),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(101),char(115),char(104),char(80),char(97),char(114),char(116),char(68),char(97), -char(116),char(97),char(0),char(98),char(116),char(83),char(116),char(114),char(105),char(100),char(105),char(110),char(103),char(77),char(101),char(115),char(104),char(73),char(110),char(116), -char(101),char(114),char(102),char(97),char(99),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(105),char(97),char(110),char(103),char(108), -char(101),char(77),char(101),char(115),char(104),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(105), -char(97),char(110),char(103),char(108),char(101),char(73),char(110),char(102),char(111),char(77),char(97),char(112),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83), -char(99),char(97),char(108),char(101),char(100),char(84),char(114),char(105),char(97),char(110),char(103),char(108),char(101),char(77),char(101),char(115),char(104),char(83),char(104),char(97), -char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(109),char(112),char(111),char(117),char(110),char(100),char(83),char(104),char(97), -char(112),char(101),char(67),char(104),char(105),char(108),char(100),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(109),char(112),char(111),char(117), -char(110),char(100),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(121),char(108),char(105),char(110),char(100), -char(101),char(114),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(97),char(112),char(115),char(117),char(108), +char(111),char(110),char(87),char(111),char(114),char(108),char(100),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(105), +char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105),char(111),char(110),char(76),char(105),char(110),char(101),char(97),char(114),char(86),char(101), +char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105), +char(111),char(110),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95), +char(97),char(110),char(105),char(115),char(111),char(116),char(114),char(111),char(112),char(105),char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0), +char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(99),char(116),char(80),char(114),char(111),char(99),char(101),char(115),char(115),char(105),char(110),char(103),char(84), +char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(100),char(101),char(97),char(99),char(116),char(105),char(118),char(97),char(116), +char(105),char(111),char(110),char(84),char(105),char(109),char(101),char(0),char(109),char(95),char(102),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109), +char(95),char(114),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(114), +char(101),char(115),char(116),char(105),char(116),char(117),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(104),char(105),char(116),char(70),char(114),char(97),char(99), +char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(99),char(100),char(83),char(119),char(101),char(112),char(116),char(83),char(112),char(104),char(101),char(114), +char(101),char(82),char(97),char(100),char(105),char(117),char(115),char(0),char(109),char(95),char(99),char(99),char(100),char(77),char(111),char(116),char(105),char(111),char(110),char(84), +char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(104),char(97),char(115),char(65),char(110),char(105),char(115),char(111),char(116), +char(114),char(111),char(112),char(105),char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(111),char(108),char(108), +char(105),char(115),char(105),char(111),char(110),char(70),char(108),char(97),char(103),char(115),char(0),char(109),char(95),char(105),char(115),char(108),char(97),char(110),char(100),char(84), +char(97),char(103),char(49),char(0),char(109),char(95),char(99),char(111),char(109),char(112),char(97),char(110),char(105),char(111),char(110),char(73),char(100),char(0),char(109),char(95), +char(97),char(99),char(116),char(105),char(118),char(97),char(116),char(105),char(111),char(110),char(83),char(116),char(97),char(116),char(101),char(49),char(0),char(109),char(95),char(105), +char(110),char(116),char(101),char(114),char(110),char(97),char(108),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(99),char(104),char(101),char(99),char(107),char(67), +char(111),char(108),char(108),char(105),char(100),char(101),char(87),char(105),char(116),char(104),char(0),char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114),char(73), +char(110),char(102),char(111),char(0),char(109),char(95),char(103),char(114),char(97),char(118),char(105),char(116),char(121),char(0),char(109),char(95),char(99),char(111),char(108),char(108), +char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(105),char(110), +char(118),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(84),char(101),char(110),char(115),char(111),char(114),char(87),char(111),char(114),char(108),char(100),char(0), +char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97), +char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(103), +char(117),char(108),char(97),char(114),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(70), +char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(103),char(114),char(97),char(118),char(105),char(116),char(121),char(95),char(97),char(99),char(99),char(101), +char(108),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(105),char(110),char(118),char(73),char(110),char(101),char(114),char(116),char(105), +char(97),char(76),char(111),char(99),char(97),char(108),char(0),char(109),char(95),char(116),char(111),char(116),char(97),char(108),char(70),char(111),char(114),char(99),char(101),char(0), +char(109),char(95),char(116),char(111),char(116),char(97),char(108),char(84),char(111),char(114),char(113),char(117),char(101),char(0),char(109),char(95),char(105),char(110),char(118),char(101), +char(114),char(115),char(101),char(77),char(97),char(115),char(115),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109),char(112), +char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103), +char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110),char(103), +char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(76), +char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108), +char(100),char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(65),char(110),char(103), +char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100), +char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(65),char(110),char(103),char(117), +char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(108), +char(105),char(110),char(101),char(97),char(114),char(83),char(108),char(101),char(101),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111), +char(108),char(100),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(108),char(101),char(101),char(112),char(105),char(110),char(103), +char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110), +char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(110),char(117),char(109),char(67),char(111),char(110),char(115),char(116), +char(114),char(97),char(105),char(110),char(116),char(82),char(111),char(119),char(115),char(0),char(110),char(117),char(98),char(0),char(42),char(109),char(95),char(114),char(98),char(65), +char(0),char(42),char(109),char(95),char(114),char(98),char(66),char(0),char(109),char(95),char(111),char(98),char(106),char(101),char(99),char(116),char(84),char(121),char(112),char(101), +char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(84),char(121),char(112), +char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(73),char(100), +char(0),char(109),char(95),char(110),char(101),char(101),char(100),char(115),char(70),char(101),char(101),char(100),char(98),char(97),char(99),char(107),char(0),char(109),char(95),char(97), +char(112),char(112),char(108),char(105),char(101),char(100),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(100),char(98),char(103),char(68), +char(114),char(97),char(119),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(100),char(105),char(115),char(97),char(98),char(108),char(101),char(67),char(111),char(108), +char(108),char(105),char(115),char(105),char(111),char(110),char(115),char(66),char(101),char(116),char(119),char(101),char(101),char(110),char(76),char(105),char(110),char(107),char(101),char(100), +char(66),char(111),char(100),char(105),char(101),char(115),char(0),char(109),char(95),char(111),char(118),char(101),char(114),char(114),char(105),char(100),char(101),char(78),char(117),char(109), +char(83),char(111),char(108),char(118),char(101),char(114),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(98), +char(114),char(101),char(97),char(107),char(105),char(110),char(103),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(84),char(104),char(114),char(101),char(115),char(104), +char(111),char(108),char(100),char(0),char(109),char(95),char(105),char(115),char(69),char(110),char(97),char(98),char(108),char(101),char(100),char(0),char(112),char(97),char(100),char(100), +char(105),char(110),char(103),char(91),char(52),char(93),char(0),char(109),char(95),char(116),char(121),char(112),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97), +char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(112),char(105),char(118),char(111),char(116),char(73),char(110),char(65),char(0),char(109), +char(95),char(112),char(105),char(118),char(111),char(116),char(73),char(110),char(66),char(0),char(109),char(95),char(114),char(98),char(65),char(70),char(114),char(97),char(109),char(101), +char(0),char(109),char(95),char(114),char(98),char(66),char(70),char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(82),char(101),char(102), +char(101),char(114),char(101),char(110),char(99),char(101),char(70),char(114),char(97),char(109),char(101),char(65),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108), +char(97),char(114),char(79),char(110),char(108),char(121),char(0),char(109),char(95),char(101),char(110),char(97),char(98),char(108),char(101),char(65),char(110),char(103),char(117),char(108), +char(97),char(114),char(77),char(111),char(116),char(111),char(114),char(0),char(109),char(95),char(109),char(111),char(116),char(111),char(114),char(84),char(97),char(114),char(103),char(101), +char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(109),char(97),char(120),char(77),char(111),char(116),char(111),char(114), +char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(108),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116), +char(0),char(109),char(95),char(117),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(108),char(105),char(109),char(105), +char(116),char(83),char(111),char(102),char(116),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(98),char(105),char(97),char(115),char(70),char(97),char(99),char(116), +char(111),char(114),char(0),char(109),char(95),char(114),char(101),char(108),char(97),char(120),char(97),char(116),char(105),char(111),char(110),char(70),char(97),char(99),char(116),char(111), +char(114),char(0),char(109),char(95),char(112),char(97),char(100),char(100),char(105),char(110),char(103),char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(115),char(119), +char(105),char(110),char(103),char(83),char(112),char(97),char(110),char(49),char(0),char(109),char(95),char(115),char(119),char(105),char(110),char(103),char(83),char(112),char(97),char(110), +char(50),char(0),char(109),char(95),char(116),char(119),char(105),char(115),char(116),char(83),char(112),char(97),char(110),char(0),char(109),char(95),char(100),char(97),char(109),char(112), +char(105),char(110),char(103),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109), +char(105),char(116),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(76),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105), +char(116),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105), +char(116),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(76),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105), +char(116),char(0),char(109),char(95),char(117),char(115),char(101),char(76),char(105),char(110),char(101),char(97),char(114),char(82),char(101),char(102),char(101),char(114),char(101),char(110), +char(99),char(101),char(70),char(114),char(97),char(109),char(101),char(65),char(0),char(109),char(95),char(117),char(115),char(101),char(79),char(102),char(102),char(115),char(101),char(116), +char(70),char(111),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(114),char(97),char(109),char(101),char(0),char(109), +char(95),char(54),char(100),char(111),char(102),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(69),char(110), +char(97),char(98),char(108),char(101),char(100),char(91),char(54),char(93),char(0),char(109),char(95),char(101),char(113),char(117),char(105),char(108),char(105),char(98),char(114),char(105), +char(117),char(109),char(80),char(111),char(105),char(110),char(116),char(91),char(54),char(93),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(83), +char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(91),char(54),char(93),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103), +char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(91),char(54),char(93),char(0),char(109),char(95),char(97),char(120),char(105),char(115),char(73),char(110),char(65), +char(0),char(109),char(95),char(97),char(120),char(105),char(115),char(73),char(110),char(66),char(0),char(109),char(95),char(114),char(97),char(116),char(105),char(111),char(0),char(109), +char(95),char(116),char(97),char(117),char(0),char(109),char(95),char(116),char(105),char(109),char(101),char(83),char(116),char(101),char(112),char(0),char(109),char(95),char(109),char(97), +char(120),char(69),char(114),char(114),char(111),char(114),char(82),char(101),char(100),char(117),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(115),char(111), +char(114),char(0),char(109),char(95),char(101),char(114),char(112),char(0),char(109),char(95),char(101),char(114),char(112),char(50),char(0),char(109),char(95),char(103),char(108),char(111), +char(98),char(97),char(108),char(67),char(102),char(109),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115), +char(101),char(80),char(101),char(110),char(101),char(116),char(114),char(97),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108), +char(100),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(84),char(117),char(114),char(110), +char(69),char(114),char(112),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(108),char(111),char(112),char(0),char(109),char(95),char(119), +char(97),char(114),char(109),char(115),char(116),char(97),char(114),char(116),char(105),char(110),char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95), +char(109),char(97),char(120),char(71),char(121),char(114),char(111),char(115),char(99),char(111),char(112),char(105),char(99),char(70),char(111),char(114),char(99),char(101),char(0),char(109), +char(95),char(115),char(105),char(110),char(103),char(108),char(101),char(65),char(120),char(105),char(115),char(82),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114), +char(105),char(99),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(110),char(117), +char(109),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114), +char(77),char(111),char(100),char(101),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(105),char(110),char(103),char(67),char(111),char(110),char(116),char(97),char(99), +char(116),char(82),char(101),char(115),char(116),char(105),char(116),char(117),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108), +char(100),char(0),char(109),char(95),char(109),char(105),char(110),char(105),char(109),char(117),char(109),char(83),char(111),char(108),char(118),char(101),char(114),char(66),char(97),char(116), +char(99),char(104),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115), +char(101),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0), +char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109), +char(95),char(118),char(111),char(108),char(117),char(109),char(101),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(42),char(109),char(95), +char(109),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(0),char(109),char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0), +char(109),char(95),char(112),char(114),char(101),char(118),char(105),char(111),char(117),char(115),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109), +char(95),char(118),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(99),char(99),char(117),char(109),char(117),char(108),char(97), +char(116),char(101),char(100),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(110),char(111),char(114),char(109),char(97),char(108),char(0),char(109),char(95), +char(97),char(114),char(101),char(97),char(0),char(109),char(95),char(97),char(116),char(116),char(97),char(99),char(104),char(0),char(109),char(95),char(110),char(111),char(100),char(101), +char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(76),char(101),char(110), +char(103),char(116),char(104),char(0),char(109),char(95),char(98),char(98),char(101),char(110),char(100),char(105),char(110),char(103),char(0),char(109),char(95),char(110),char(111),char(100), +char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(51),char(93),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(65),char(114), +char(101),char(97),char(0),char(109),char(95),char(99),char(48),char(91),char(52),char(93),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100), +char(105),char(99),char(101),char(115),char(91),char(52),char(93),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(86),char(111),char(108),char(117),char(109),char(101), +char(0),char(109),char(95),char(99),char(49),char(0),char(109),char(95),char(99),char(50),char(0),char(109),char(95),char(99),char(48),char(0),char(109),char(95),char(108),char(111), +char(99),char(97),char(108),char(70),char(114),char(97),char(109),char(101),char(0),char(42),char(109),char(95),char(114),char(105),char(103),char(105),char(100),char(66),char(111),char(100), +char(121),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(101),char(120),char(0),char(109),char(95),char(97),char(101),char(114),char(111), +char(77),char(111),char(100),char(101),char(108),char(0),char(109),char(95),char(98),char(97),char(117),char(109),char(103),char(97),char(114),char(116),char(101),char(0),char(109),char(95), +char(100),char(114),char(97),char(103),char(0),char(109),char(95),char(108),char(105),char(102),char(116),char(0),char(109),char(95),char(112),char(114),char(101),char(115),char(115),char(117), +char(114),char(101),char(0),char(109),char(95),char(118),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(100),char(121),char(110),char(97),char(109),char(105), +char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(112),char(111),char(115),char(101),char(77),char(97),char(116),char(99), +char(104),char(0),char(109),char(95),char(114),char(105),char(103),char(105),char(100),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100), +char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(107),char(105),char(110),char(101),char(116),char(105),char(99),char(67),char(111),char(110),char(116),char(97),char(99), +char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(67),char(111),char(110),char(116), +char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(97),char(110),char(99),char(104),char(111),char(114), +char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100), +char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111), +char(102),char(116),char(75),char(105),char(110),char(101),char(116),char(105),char(99),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100), +char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(83),char(111),char(102),char(116),char(67),char(108),char(117),char(115),char(116), +char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(82),char(105),char(103), +char(105),char(100),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105), +char(116),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(75),char(105),char(110),char(101),char(116),char(105),char(99),char(67),char(108),char(117),char(115),char(116), +char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(115),char(111),char(102), +char(116),char(83),char(111),char(102),char(116),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83), +char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(109),char(97),char(120),char(86),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(116), +char(105),char(109),char(101),char(83),char(99),char(97),char(108),char(101),char(0),char(109),char(95),char(118),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(73), +char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110), +char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(100),char(114),char(105),char(102),char(116),char(73),char(116), +char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(116), +char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(114),char(111),char(116),char(0),char(109),char(95),char(115),char(99),char(97), +char(108),char(101),char(0),char(109),char(95),char(97),char(113),char(113),char(0),char(109),char(95),char(99),char(111),char(109),char(0),char(42),char(109),char(95),char(112),char(111), +char(115),char(105),char(116),char(105),char(111),char(110),char(115),char(0),char(42),char(109),char(95),char(119),char(101),char(105),char(103),char(104),char(116),char(115),char(0),char(109), +char(95),char(110),char(117),char(109),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(87), +char(101),char(105),char(103),char(116),char(115),char(0),char(109),char(95),char(98),char(118),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(98),char(102), +char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(102),char(114),char(97),char(109),char(101),char(120),char(102),char(111),char(114),char(109),char(0),char(109),char(95), +char(108),char(111),char(99),char(105),char(105),char(0),char(109),char(95),char(105),char(110),char(118),char(119),char(105),char(0),char(109),char(95),char(118),char(105),char(109),char(112), +char(117),char(108),char(115),char(101),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(100),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115), +char(91),char(50),char(93),char(0),char(109),char(95),char(108),char(118),char(0),char(109),char(95),char(97),char(118),char(0),char(42),char(109),char(95),char(102),char(114),char(97), +char(109),char(101),char(114),char(101),char(102),char(115),char(0),char(42),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101), +char(115),char(0),char(42),char(109),char(95),char(109),char(97),char(115),char(115),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(70),char(114),char(97), +char(109),char(101),char(82),char(101),char(102),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(78),char(111),char(100),char(101),char(115),char(0),char(109),char(95), +char(110),char(117),char(109),char(77),char(97),char(115),char(115),char(101),char(115),char(0),char(109),char(95),char(105),char(100),char(109),char(97),char(115),char(115),char(0),char(109), +char(95),char(105),char(109),char(97),char(115),char(115),char(0),char(109),char(95),char(110),char(118),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(0), +char(109),char(95),char(110),char(100),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(0),char(109),char(95),char(110),char(100),char(97),char(109),char(112), +char(105),char(110),char(103),char(0),char(109),char(95),char(108),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(100),char(97), +char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(109),char(97),char(116),char(99),char(104),char(105),char(110),char(103),char(0),char(109),char(95),char(109), +char(97),char(120),char(83),char(101),char(108),char(102),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(73),char(109),char(112),char(117),char(108), +char(115),char(101),char(0),char(109),char(95),char(115),char(101),char(108),char(102),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(73),char(109), +char(112),char(117),char(108),char(115),char(101),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(105), +char(110),char(115),char(65),char(110),char(99),char(104),char(111),char(114),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(100),char(101),char(0),char(109), +char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(110),char(100),char(101),char(120),char(0),char(42),char(109),char(95),char(98),char(111),char(100), +char(121),char(65),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121),char(66),char(0),char(109),char(95),char(114),char(101),char(102),char(115),char(91),char(50), +char(93),char(0),char(109),char(95),char(99),char(102),char(109),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(100),char(101), +char(108),char(101),char(116),char(101),char(0),char(109),char(95),char(114),char(101),char(108),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(91),char(50), +char(93),char(0),char(109),char(95),char(98),char(111),char(100),char(121),char(65),char(116),char(121),char(112),char(101),char(0),char(109),char(95),char(98),char(111),char(100),char(121), +char(66),char(116),char(121),char(112),char(101),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(84),char(121),char(112),char(101),char(0),char(42),char(109), +char(95),char(112),char(111),char(115),char(101),char(0),char(42),char(42),char(109),char(95),char(109),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(115),char(0), +char(42),char(109),char(95),char(110),char(111),char(100),char(101),char(115),char(0),char(42),char(109),char(95),char(108),char(105),char(110),char(107),char(115),char(0),char(42),char(109), +char(95),char(102),char(97),char(99),char(101),char(115),char(0),char(42),char(109),char(95),char(116),char(101),char(116),char(114),char(97),char(104),char(101),char(100),char(114),char(97), +char(0),char(42),char(109),char(95),char(97),char(110),char(99),char(104),char(111),char(114),char(115),char(0),char(42),char(109),char(95),char(99),char(108),char(117),char(115),char(116), +char(101),char(114),char(115),char(0),char(42),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(77), +char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(76),char(105),char(110),char(107),char(115),char(0), +char(109),char(95),char(110),char(117),char(109),char(70),char(97),char(99),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(84),char(101),char(116),char(114), +char(97),char(104),char(101),char(100),char(114),char(97),char(0),char(109),char(95),char(110),char(117),char(109),char(65),char(110),char(99),char(104),char(111),char(114),char(115),char(0), +char(109),char(95),char(110),char(117),char(109),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(74), +char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(99),char(111),char(110),char(102),char(105),char(103),char(0),char(0),char(84),char(89),char(80),char(69), +char(87),char(0),char(0),char(0),char(99),char(104),char(97),char(114),char(0),char(117),char(99),char(104),char(97),char(114),char(0),char(115),char(104),char(111),char(114),char(116), +char(0),char(117),char(115),char(104),char(111),char(114),char(116),char(0),char(105),char(110),char(116),char(0),char(108),char(111),char(110),char(103),char(0),char(117),char(108),char(111), +char(110),char(103),char(0),char(102),char(108),char(111),char(97),char(116),char(0),char(100),char(111),char(117),char(98),char(108),char(101),char(0),char(118),char(111),char(105),char(100), +char(0),char(80),char(111),char(105),char(110),char(116),char(101),char(114),char(65),char(114),char(114),char(97),char(121),char(0),char(98),char(116),char(80),char(104),char(121),char(115), +char(105),char(99),char(115),char(83),char(121),char(115),char(116),char(101),char(109),char(0),char(76),char(105),char(115),char(116),char(66),char(97),char(115),char(101),char(0),char(98), +char(116),char(86),char(101),char(99),char(116),char(111),char(114),char(51),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(86),char(101),char(99),char(116),char(111),char(114),char(51),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(77),char(97),char(116),char(114),char(105),char(120),char(51),char(120),char(51),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(77),char(97),char(116),char(114),char(105),char(120),char(51),char(120),char(51),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97), +char(0),char(98),char(116),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(68),char(111),char(117),char(98),char(108),char(101),char(68), +char(97),char(116),char(97),char(0),char(98),char(116),char(66),char(118),char(104),char(83),char(117),char(98),char(116),char(114),char(101),char(101),char(73),char(110),char(102),char(111), +char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(79),char(112),char(116),char(105),char(109),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78), +char(111),char(100),char(101),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(79),char(112),char(116),char(105),char(109), +char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101), +char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(70), +char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100), +char(66),char(118),char(104),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(108),char(108), +char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(116),char(97), +char(116),char(105),char(99),char(80),char(108),char(97),char(110),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(67),char(111),char(110),char(118),char(101),char(120),char(73),char(110),char(116),char(101),char(114),char(110),char(97),char(108),char(83),char(104),char(97),char(112),char(101),char(68), +char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(65),char(110),char(100),char(82),char(97),char(100), +char(105),char(117),char(115),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(83),char(112),char(104),char(101),char(114),char(101),char(83),char(104),char(97), +char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(83),char(104),char(111),char(114),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(83),char(104),char(111),char(114),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(84),char(114),char(105), +char(112),char(108),char(101),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(104),char(97),char(114),char(73),char(110),char(100),char(101),char(120), +char(84),char(114),char(105),char(112),char(108),char(101),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(101),char(115),char(104),char(80),char(97), +char(114),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(116),char(114),char(105),char(100),char(105),char(110),char(103),char(77),char(101),char(115), +char(104),char(73),char(110),char(116),char(101),char(114),char(102),char(97),char(99),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(105), +char(97),char(110),char(103),char(108),char(101),char(77),char(101),char(115),char(104),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(84),char(114),char(105),char(97),char(110),char(103),char(108),char(101),char(73),char(110),char(102),char(111),char(77),char(97),char(112),char(68),char(97),char(116),char(97), +char(0),char(98),char(116),char(83),char(99),char(97),char(108),char(101),char(100),char(84),char(114),char(105),char(97),char(110),char(103),char(108),char(101),char(77),char(101),char(115), +char(104),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(109),char(112),char(111),char(117),char(110), +char(100),char(83),char(104),char(97),char(112),char(101),char(67),char(104),char(105),char(108),char(100),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111), +char(109),char(112),char(111),char(117),char(110),char(100),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(121), +char(108),char(105),char(110),char(100),char(101),char(114),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111), +char(110),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(97),char(112),char(115),char(117),char(108), char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(105),char(97),char(110),char(103),char(108), char(101),char(73),char(110),char(102),char(111),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(73),char(109),char(112),char(97),char(99),char(116),char(77), char(101),char(115),char(104),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(118),char(101), @@ -305,155 +309,193 @@ char(83),char(111),char(108),char(118),char(101),char(114),char(73),char(110),ch char(98),char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97), char(0),char(98),char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97), char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(73),char(110),char(102),char(111),char(49), -char(0),char(98),char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97), -char(116),char(97),char(0),char(98),char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(68),char(97),char(116),char(97),char(0),char(98), -char(116),char(80),char(111),char(105),char(110),char(116),char(50),char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105), -char(110),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50), -char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108), -char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97), -char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103), -char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97), -char(0),char(98),char(116),char(67),char(111),char(110),char(101),char(84),char(119),char(105),char(115),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105), -char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102), +char(0),char(98),char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108), +char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116), +char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100), +char(121),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97), +char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(105),char(110), +char(116),char(50),char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111), +char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50),char(80),char(111),char(105),char(110),char(116), +char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97), +char(50),char(0),char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50),char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116), +char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105), +char(110),char(103),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68), +char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110), +char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103),char(101),char(67),char(111), +char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0), +char(98),char(116),char(67),char(111),char(110),char(101),char(84),char(119),char(105),char(115),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110), +char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(101),char(84),char(119), +char(105),char(115),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110), +char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(67), +char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50), +char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103), char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110), char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103),char(67),char(111),char(110),char(115),char(116),char(114), -char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(108),char(105),char(100),char(101),char(114),char(67),char(111),char(110), -char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121), -char(77),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100), -char(121),char(78),char(111),char(100),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(76),char(105), -char(110),char(107),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(70),char(97),char(99),char(101),char(68), -char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(84),char(101),char(116),char(114),char(97),char(68),char(97),char(116), -char(97),char(0),char(83),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(65),char(110),char(99),char(104),char(111),char(114),char(68),char(97),char(116), -char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(67),char(111),char(110),char(102),char(105),char(103),char(68),char(97),char(116),char(97), -char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(80),char(111),char(115),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111), -char(102),char(116),char(66),char(111),char(100),char(121),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(74),char(111),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(0),char(0), -char(84),char(76),char(69),char(78),char(1),char(0),char(1),char(0),char(2),char(0),char(2),char(0),char(4),char(0),char(4),char(0),char(4),char(0),char(4),char(0), -char(8),char(0),char(0),char(0),char(12),char(0),char(36),char(0),char(8),char(0),char(16),char(0),char(32),char(0),char(48),char(0),char(96),char(0),char(64),char(0), -char(-128),char(0),char(20),char(0),char(48),char(0),char(80),char(0),char(16),char(0),char(84),char(0),char(-124),char(0),char(12),char(0),char(52),char(0),char(52),char(0), -char(20),char(0),char(64),char(0),char(4),char(0),char(4),char(0),char(8),char(0),char(4),char(0),char(32),char(0),char(28),char(0),char(60),char(0),char(56),char(0), -char(76),char(0),char(76),char(0),char(24),char(0),char(60),char(0),char(60),char(0),char(16),char(0),char(64),char(0),char(68),char(0),char(-48),char(1),char(0),char(1), -char(-72),char(0),char(-104),char(0),char(104),char(0),char(88),char(0),char(-24),char(1),char(-96),char(3),char(8),char(0),char(52),char(0),char(0),char(0),char(84),char(0), -char(116),char(0),char(92),char(1),char(-36),char(0),char(-44),char(0),char(-4),char(0),char(92),char(1),char(-52),char(0),char(16),char(0),char(100),char(0),char(20),char(0), -char(36),char(0),char(100),char(0),char(92),char(0),char(104),char(0),char(-64),char(0),char(92),char(1),char(104),char(0),char(-84),char(1),char(83),char(84),char(82),char(67), -char(65),char(0),char(0),char(0),char(10),char(0),char(3),char(0),char(4),char(0),char(0),char(0),char(4),char(0),char(1),char(0),char(9),char(0),char(2),char(0), -char(11),char(0),char(3),char(0),char(10),char(0),char(3),char(0),char(10),char(0),char(4),char(0),char(10),char(0),char(5),char(0),char(12),char(0),char(2),char(0), -char(9),char(0),char(6),char(0),char(9),char(0),char(7),char(0),char(13),char(0),char(1),char(0),char(7),char(0),char(8),char(0),char(14),char(0),char(1),char(0), -char(8),char(0),char(8),char(0),char(15),char(0),char(1),char(0),char(13),char(0),char(9),char(0),char(16),char(0),char(1),char(0),char(14),char(0),char(9),char(0), -char(17),char(0),char(2),char(0),char(15),char(0),char(10),char(0),char(13),char(0),char(11),char(0),char(18),char(0),char(2),char(0),char(16),char(0),char(10),char(0), -char(14),char(0),char(11),char(0),char(19),char(0),char(4),char(0),char(4),char(0),char(12),char(0),char(4),char(0),char(13),char(0),char(2),char(0),char(14),char(0), -char(2),char(0),char(15),char(0),char(20),char(0),char(6),char(0),char(13),char(0),char(16),char(0),char(13),char(0),char(17),char(0),char(4),char(0),char(18),char(0), -char(4),char(0),char(19),char(0),char(4),char(0),char(20),char(0),char(0),char(0),char(21),char(0),char(21),char(0),char(6),char(0),char(14),char(0),char(16),char(0), -char(14),char(0),char(17),char(0),char(4),char(0),char(18),char(0),char(4),char(0),char(19),char(0),char(4),char(0),char(20),char(0),char(0),char(0),char(21),char(0), -char(22),char(0),char(3),char(0),char(2),char(0),char(14),char(0),char(2),char(0),char(15),char(0),char(4),char(0),char(22),char(0),char(23),char(0),char(12),char(0), -char(13),char(0),char(23),char(0),char(13),char(0),char(24),char(0),char(13),char(0),char(25),char(0),char(4),char(0),char(26),char(0),char(4),char(0),char(27),char(0), -char(4),char(0),char(28),char(0),char(4),char(0),char(29),char(0),char(20),char(0),char(30),char(0),char(22),char(0),char(31),char(0),char(19),char(0),char(32),char(0), -char(4),char(0),char(33),char(0),char(4),char(0),char(34),char(0),char(24),char(0),char(12),char(0),char(14),char(0),char(23),char(0),char(14),char(0),char(24),char(0), -char(14),char(0),char(25),char(0),char(4),char(0),char(26),char(0),char(4),char(0),char(27),char(0),char(4),char(0),char(28),char(0),char(4),char(0),char(29),char(0), -char(21),char(0),char(30),char(0),char(22),char(0),char(31),char(0),char(4),char(0),char(33),char(0),char(4),char(0),char(34),char(0),char(19),char(0),char(32),char(0), -char(25),char(0),char(3),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(36),char(0),char(0),char(0),char(37),char(0),char(26),char(0),char(5),char(0), -char(25),char(0),char(38),char(0),char(13),char(0),char(39),char(0),char(13),char(0),char(40),char(0),char(7),char(0),char(41),char(0),char(0),char(0),char(21),char(0), -char(27),char(0),char(5),char(0),char(25),char(0),char(38),char(0),char(13),char(0),char(39),char(0),char(13),char(0),char(42),char(0),char(7),char(0),char(43),char(0), -char(4),char(0),char(44),char(0),char(28),char(0),char(2),char(0),char(13),char(0),char(45),char(0),char(7),char(0),char(46),char(0),char(29),char(0),char(4),char(0), -char(27),char(0),char(47),char(0),char(28),char(0),char(48),char(0),char(4),char(0),char(49),char(0),char(0),char(0),char(37),char(0),char(30),char(0),char(1),char(0), -char(4),char(0),char(50),char(0),char(31),char(0),char(2),char(0),char(2),char(0),char(50),char(0),char(0),char(0),char(51),char(0),char(32),char(0),char(2),char(0), -char(2),char(0),char(52),char(0),char(0),char(0),char(51),char(0),char(33),char(0),char(2),char(0),char(0),char(0),char(52),char(0),char(0),char(0),char(53),char(0), -char(34),char(0),char(8),char(0),char(13),char(0),char(54),char(0),char(14),char(0),char(55),char(0),char(30),char(0),char(56),char(0),char(32),char(0),char(57),char(0), -char(33),char(0),char(58),char(0),char(31),char(0),char(59),char(0),char(4),char(0),char(60),char(0),char(4),char(0),char(61),char(0),char(35),char(0),char(4),char(0), -char(34),char(0),char(62),char(0),char(13),char(0),char(63),char(0),char(4),char(0),char(64),char(0),char(0),char(0),char(37),char(0),char(36),char(0),char(7),char(0), -char(25),char(0),char(38),char(0),char(35),char(0),char(65),char(0),char(23),char(0),char(66),char(0),char(24),char(0),char(67),char(0),char(37),char(0),char(68),char(0), -char(7),char(0),char(43),char(0),char(0),char(0),char(69),char(0),char(38),char(0),char(2),char(0),char(36),char(0),char(70),char(0),char(13),char(0),char(39),char(0), -char(39),char(0),char(4),char(0),char(17),char(0),char(71),char(0),char(25),char(0),char(72),char(0),char(4),char(0),char(73),char(0),char(7),char(0),char(74),char(0), -char(40),char(0),char(4),char(0),char(25),char(0),char(38),char(0),char(39),char(0),char(75),char(0),char(4),char(0),char(76),char(0),char(7),char(0),char(43),char(0), -char(41),char(0),char(3),char(0),char(27),char(0),char(47),char(0),char(4),char(0),char(77),char(0),char(0),char(0),char(37),char(0),char(42),char(0),char(3),char(0), -char(27),char(0),char(47),char(0),char(4),char(0),char(77),char(0),char(0),char(0),char(37),char(0),char(43),char(0),char(4),char(0),char(4),char(0),char(78),char(0), -char(7),char(0),char(79),char(0),char(7),char(0),char(80),char(0),char(7),char(0),char(81),char(0),char(37),char(0),char(14),char(0),char(4),char(0),char(82),char(0), -char(4),char(0),char(83),char(0),char(43),char(0),char(84),char(0),char(4),char(0),char(85),char(0),char(7),char(0),char(86),char(0),char(7),char(0),char(87),char(0), -char(7),char(0),char(88),char(0),char(7),char(0),char(89),char(0),char(7),char(0),char(90),char(0),char(4),char(0),char(91),char(0),char(4),char(0),char(92),char(0), -char(4),char(0),char(93),char(0),char(4),char(0),char(94),char(0),char(0),char(0),char(37),char(0),char(44),char(0),char(5),char(0),char(25),char(0),char(38),char(0), -char(35),char(0),char(65),char(0),char(13),char(0),char(39),char(0),char(7),char(0),char(43),char(0),char(4),char(0),char(95),char(0),char(45),char(0),char(5),char(0), -char(27),char(0),char(47),char(0),char(13),char(0),char(96),char(0),char(14),char(0),char(97),char(0),char(4),char(0),char(98),char(0),char(0),char(0),char(99),char(0), -char(46),char(0),char(25),char(0),char(9),char(0),char(100),char(0),char(9),char(0),char(101),char(0),char(25),char(0),char(102),char(0),char(0),char(0),char(35),char(0), -char(18),char(0),char(103),char(0),char(18),char(0),char(104),char(0),char(14),char(0),char(105),char(0),char(14),char(0),char(106),char(0),char(14),char(0),char(107),char(0), -char(8),char(0),char(108),char(0),char(8),char(0),char(109),char(0),char(8),char(0),char(110),char(0),char(8),char(0),char(111),char(0),char(8),char(0),char(112),char(0), -char(8),char(0),char(113),char(0),char(8),char(0),char(114),char(0),char(8),char(0),char(115),char(0),char(4),char(0),char(116),char(0),char(4),char(0),char(117),char(0), -char(4),char(0),char(118),char(0),char(4),char(0),char(119),char(0),char(4),char(0),char(120),char(0),char(4),char(0),char(121),char(0),char(4),char(0),char(122),char(0), -char(0),char(0),char(37),char(0),char(47),char(0),char(25),char(0),char(9),char(0),char(100),char(0),char(9),char(0),char(101),char(0),char(25),char(0),char(102),char(0), -char(0),char(0),char(35),char(0),char(17),char(0),char(103),char(0),char(17),char(0),char(104),char(0),char(13),char(0),char(105),char(0),char(13),char(0),char(106),char(0), -char(13),char(0),char(107),char(0),char(7),char(0),char(108),char(0),char(7),char(0),char(109),char(0),char(7),char(0),char(110),char(0),char(7),char(0),char(111),char(0), -char(7),char(0),char(112),char(0),char(7),char(0),char(113),char(0),char(7),char(0),char(114),char(0),char(7),char(0),char(115),char(0),char(4),char(0),char(116),char(0), +char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98),char(116),char(83),char(108), +char(105),char(100),char(101),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(83),char(108),char(105),char(100),char(101),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117), +char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(97),char(114),char(67),char(111),char(110),char(115),char(116),char(114), +char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(97),char(114), +char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97), +char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(77),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(68),char(97),char(116), +char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(78),char(111),char(100),char(101),char(68),char(97),char(116),char(97),char(0),char(83), +char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(76),char(105),char(110),char(107),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116), +char(66),char(111),char(100),char(121),char(70),char(97),char(99),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100), +char(121),char(84),char(101),char(116),char(114),char(97),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100), +char(65),char(110),char(99),char(104),char(111),char(114),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(67), +char(111),char(110),char(102),char(105),char(103),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(80),char(111), +char(115),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(67),char(108),char(117),char(115),char(116), +char(101),char(114),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(74),char(111),char(105), +char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(70),char(108),char(111), +char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(0),char(84),char(76),char(69),char(78),char(1),char(0),char(1),char(0),char(2),char(0),char(2),char(0), +char(4),char(0),char(4),char(0),char(4),char(0),char(4),char(0),char(8),char(0),char(0),char(0),char(12),char(0),char(36),char(0),char(8),char(0),char(16),char(0), +char(32),char(0),char(48),char(0),char(96),char(0),char(64),char(0),char(-128),char(0),char(20),char(0),char(48),char(0),char(80),char(0),char(16),char(0),char(84),char(0), +char(-124),char(0),char(12),char(0),char(52),char(0),char(52),char(0),char(20),char(0),char(64),char(0),char(4),char(0),char(4),char(0),char(8),char(0),char(4),char(0), +char(32),char(0),char(28),char(0),char(60),char(0),char(56),char(0),char(76),char(0),char(76),char(0),char(24),char(0),char(60),char(0),char(60),char(0),char(60),char(0), +char(16),char(0),char(64),char(0),char(68),char(0),char(-48),char(1),char(0),char(1),char(-72),char(0),char(-104),char(0),char(104),char(0),char(88),char(0),char(-24),char(1), +char(-96),char(3),char(8),char(0),char(52),char(0),char(52),char(0),char(0),char(0),char(68),char(0),char(84),char(0),char(-124),char(0),char(116),char(0),char(92),char(1), +char(-36),char(0),char(-116),char(1),char(124),char(1),char(-44),char(0),char(-4),char(0),char(-52),char(1),char(92),char(1),char(116),char(2),char(-52),char(0),char(108),char(1), +char(92),char(0),char(-116),char(0),char(16),char(0),char(100),char(0),char(20),char(0),char(36),char(0),char(100),char(0),char(92),char(0),char(104),char(0),char(-64),char(0), +char(92),char(1),char(104),char(0),char(-84),char(1),char(0),char(0),char(83),char(84),char(82),char(67),char(76),char(0),char(0),char(0),char(10),char(0),char(3),char(0), +char(4),char(0),char(0),char(0),char(4),char(0),char(1),char(0),char(9),char(0),char(2),char(0),char(11),char(0),char(3),char(0),char(10),char(0),char(3),char(0), +char(10),char(0),char(4),char(0),char(10),char(0),char(5),char(0),char(12),char(0),char(2),char(0),char(9),char(0),char(6),char(0),char(9),char(0),char(7),char(0), +char(13),char(0),char(1),char(0),char(7),char(0),char(8),char(0),char(14),char(0),char(1),char(0),char(8),char(0),char(8),char(0),char(15),char(0),char(1),char(0), +char(13),char(0),char(9),char(0),char(16),char(0),char(1),char(0),char(14),char(0),char(9),char(0),char(17),char(0),char(2),char(0),char(15),char(0),char(10),char(0), +char(13),char(0),char(11),char(0),char(18),char(0),char(2),char(0),char(16),char(0),char(10),char(0),char(14),char(0),char(11),char(0),char(19),char(0),char(4),char(0), +char(4),char(0),char(12),char(0),char(4),char(0),char(13),char(0),char(2),char(0),char(14),char(0),char(2),char(0),char(15),char(0),char(20),char(0),char(6),char(0), +char(13),char(0),char(16),char(0),char(13),char(0),char(17),char(0),char(4),char(0),char(18),char(0),char(4),char(0),char(19),char(0),char(4),char(0),char(20),char(0), +char(0),char(0),char(21),char(0),char(21),char(0),char(6),char(0),char(14),char(0),char(16),char(0),char(14),char(0),char(17),char(0),char(4),char(0),char(18),char(0), +char(4),char(0),char(19),char(0),char(4),char(0),char(20),char(0),char(0),char(0),char(21),char(0),char(22),char(0),char(3),char(0),char(2),char(0),char(14),char(0), +char(2),char(0),char(15),char(0),char(4),char(0),char(22),char(0),char(23),char(0),char(12),char(0),char(13),char(0),char(23),char(0),char(13),char(0),char(24),char(0), +char(13),char(0),char(25),char(0),char(4),char(0),char(26),char(0),char(4),char(0),char(27),char(0),char(4),char(0),char(28),char(0),char(4),char(0),char(29),char(0), +char(20),char(0),char(30),char(0),char(22),char(0),char(31),char(0),char(19),char(0),char(32),char(0),char(4),char(0),char(33),char(0),char(4),char(0),char(34),char(0), +char(24),char(0),char(12),char(0),char(14),char(0),char(23),char(0),char(14),char(0),char(24),char(0),char(14),char(0),char(25),char(0),char(4),char(0),char(26),char(0), +char(4),char(0),char(27),char(0),char(4),char(0),char(28),char(0),char(4),char(0),char(29),char(0),char(21),char(0),char(30),char(0),char(22),char(0),char(31),char(0), +char(4),char(0),char(33),char(0),char(4),char(0),char(34),char(0),char(19),char(0),char(32),char(0),char(25),char(0),char(3),char(0),char(0),char(0),char(35),char(0), +char(4),char(0),char(36),char(0),char(0),char(0),char(37),char(0),char(26),char(0),char(5),char(0),char(25),char(0),char(38),char(0),char(13),char(0),char(39),char(0), +char(13),char(0),char(40),char(0),char(7),char(0),char(41),char(0),char(0),char(0),char(21),char(0),char(27),char(0),char(5),char(0),char(25),char(0),char(38),char(0), +char(13),char(0),char(39),char(0),char(13),char(0),char(42),char(0),char(7),char(0),char(43),char(0),char(4),char(0),char(44),char(0),char(28),char(0),char(2),char(0), +char(13),char(0),char(45),char(0),char(7),char(0),char(46),char(0),char(29),char(0),char(4),char(0),char(27),char(0),char(47),char(0),char(28),char(0),char(48),char(0), +char(4),char(0),char(49),char(0),char(0),char(0),char(37),char(0),char(30),char(0),char(1),char(0),char(4),char(0),char(50),char(0),char(31),char(0),char(2),char(0), +char(2),char(0),char(50),char(0),char(0),char(0),char(51),char(0),char(32),char(0),char(2),char(0),char(2),char(0),char(52),char(0),char(0),char(0),char(51),char(0), +char(33),char(0),char(2),char(0),char(0),char(0),char(52),char(0),char(0),char(0),char(53),char(0),char(34),char(0),char(8),char(0),char(13),char(0),char(54),char(0), +char(14),char(0),char(55),char(0),char(30),char(0),char(56),char(0),char(32),char(0),char(57),char(0),char(33),char(0),char(58),char(0),char(31),char(0),char(59),char(0), +char(4),char(0),char(60),char(0),char(4),char(0),char(61),char(0),char(35),char(0),char(4),char(0),char(34),char(0),char(62),char(0),char(13),char(0),char(63),char(0), +char(4),char(0),char(64),char(0),char(0),char(0),char(37),char(0),char(36),char(0),char(7),char(0),char(25),char(0),char(38),char(0),char(35),char(0),char(65),char(0), +char(23),char(0),char(66),char(0),char(24),char(0),char(67),char(0),char(37),char(0),char(68),char(0),char(7),char(0),char(43),char(0),char(0),char(0),char(69),char(0), +char(38),char(0),char(2),char(0),char(36),char(0),char(70),char(0),char(13),char(0),char(39),char(0),char(39),char(0),char(4),char(0),char(17),char(0),char(71),char(0), +char(25),char(0),char(72),char(0),char(4),char(0),char(73),char(0),char(7),char(0),char(74),char(0),char(40),char(0),char(4),char(0),char(25),char(0),char(38),char(0), +char(39),char(0),char(75),char(0),char(4),char(0),char(76),char(0),char(7),char(0),char(43),char(0),char(41),char(0),char(3),char(0),char(27),char(0),char(47),char(0), +char(4),char(0),char(77),char(0),char(0),char(0),char(37),char(0),char(42),char(0),char(3),char(0),char(27),char(0),char(47),char(0),char(4),char(0),char(78),char(0), +char(0),char(0),char(37),char(0),char(43),char(0),char(3),char(0),char(27),char(0),char(47),char(0),char(4),char(0),char(77),char(0),char(0),char(0),char(37),char(0), +char(44),char(0),char(4),char(0),char(4),char(0),char(79),char(0),char(7),char(0),char(80),char(0),char(7),char(0),char(81),char(0),char(7),char(0),char(82),char(0), +char(37),char(0),char(14),char(0),char(4),char(0),char(83),char(0),char(4),char(0),char(84),char(0),char(44),char(0),char(85),char(0),char(4),char(0),char(86),char(0), +char(7),char(0),char(87),char(0),char(7),char(0),char(88),char(0),char(7),char(0),char(89),char(0),char(7),char(0),char(90),char(0),char(7),char(0),char(91),char(0), +char(4),char(0),char(92),char(0),char(4),char(0),char(93),char(0),char(4),char(0),char(94),char(0),char(4),char(0),char(95),char(0),char(0),char(0),char(37),char(0), +char(45),char(0),char(5),char(0),char(25),char(0),char(38),char(0),char(35),char(0),char(65),char(0),char(13),char(0),char(39),char(0),char(7),char(0),char(43),char(0), +char(4),char(0),char(96),char(0),char(46),char(0),char(5),char(0),char(27),char(0),char(47),char(0),char(13),char(0),char(97),char(0),char(14),char(0),char(98),char(0), +char(4),char(0),char(99),char(0),char(0),char(0),char(100),char(0),char(47),char(0),char(25),char(0),char(9),char(0),char(101),char(0),char(9),char(0),char(102),char(0), +char(25),char(0),char(103),char(0),char(0),char(0),char(35),char(0),char(18),char(0),char(104),char(0),char(18),char(0),char(105),char(0),char(14),char(0),char(106),char(0), +char(14),char(0),char(107),char(0),char(14),char(0),char(108),char(0),char(8),char(0),char(109),char(0),char(8),char(0),char(110),char(0),char(8),char(0),char(111),char(0), +char(8),char(0),char(112),char(0),char(8),char(0),char(113),char(0),char(8),char(0),char(114),char(0),char(8),char(0),char(115),char(0),char(8),char(0),char(116),char(0), char(4),char(0),char(117),char(0),char(4),char(0),char(118),char(0),char(4),char(0),char(119),char(0),char(4),char(0),char(120),char(0),char(4),char(0),char(121),char(0), -char(4),char(0),char(122),char(0),char(0),char(0),char(37),char(0),char(48),char(0),char(2),char(0),char(49),char(0),char(123),char(0),char(14),char(0),char(124),char(0), -char(50),char(0),char(2),char(0),char(51),char(0),char(123),char(0),char(13),char(0),char(124),char(0),char(52),char(0),char(21),char(0),char(47),char(0),char(125),char(0), -char(15),char(0),char(126),char(0),char(13),char(0),char(127),char(0),char(13),char(0),char(-128),char(0),char(13),char(0),char(-127),char(0),char(13),char(0),char(-126),char(0), -char(13),char(0),char(124),char(0),char(13),char(0),char(-125),char(0),char(13),char(0),char(-124),char(0),char(13),char(0),char(-123),char(0),char(13),char(0),char(-122),char(0), -char(7),char(0),char(-121),char(0),char(7),char(0),char(-120),char(0),char(7),char(0),char(-119),char(0),char(7),char(0),char(-118),char(0),char(7),char(0),char(-117),char(0), -char(7),char(0),char(-116),char(0),char(7),char(0),char(-115),char(0),char(7),char(0),char(-114),char(0),char(7),char(0),char(-113),char(0),char(4),char(0),char(-112),char(0), -char(53),char(0),char(22),char(0),char(46),char(0),char(125),char(0),char(16),char(0),char(126),char(0),char(14),char(0),char(127),char(0),char(14),char(0),char(-128),char(0), -char(14),char(0),char(-127),char(0),char(14),char(0),char(-126),char(0),char(14),char(0),char(124),char(0),char(14),char(0),char(-125),char(0),char(14),char(0),char(-124),char(0), -char(14),char(0),char(-123),char(0),char(14),char(0),char(-122),char(0),char(8),char(0),char(-121),char(0),char(8),char(0),char(-120),char(0),char(8),char(0),char(-119),char(0), -char(8),char(0),char(-118),char(0),char(8),char(0),char(-117),char(0),char(8),char(0),char(-116),char(0),char(8),char(0),char(-115),char(0),char(8),char(0),char(-114),char(0), -char(8),char(0),char(-113),char(0),char(4),char(0),char(-112),char(0),char(0),char(0),char(37),char(0),char(54),char(0),char(2),char(0),char(4),char(0),char(-111),char(0), -char(4),char(0),char(-110),char(0),char(55),char(0),char(13),char(0),char(56),char(0),char(-109),char(0),char(56),char(0),char(-108),char(0),char(0),char(0),char(35),char(0), -char(4),char(0),char(-107),char(0),char(4),char(0),char(-106),char(0),char(4),char(0),char(-105),char(0),char(4),char(0),char(-104),char(0),char(7),char(0),char(-103),char(0), -char(7),char(0),char(-102),char(0),char(4),char(0),char(-101),char(0),char(4),char(0),char(-100),char(0),char(7),char(0),char(-99),char(0),char(4),char(0),char(-98),char(0), -char(57),char(0),char(3),char(0),char(55),char(0),char(-97),char(0),char(13),char(0),char(-96),char(0),char(13),char(0),char(-95),char(0),char(58),char(0),char(3),char(0), -char(55),char(0),char(-97),char(0),char(14),char(0),char(-96),char(0),char(14),char(0),char(-95),char(0),char(59),char(0),char(13),char(0),char(55),char(0),char(-97),char(0), -char(18),char(0),char(-94),char(0),char(18),char(0),char(-93),char(0),char(4),char(0),char(-92),char(0),char(4),char(0),char(-91),char(0),char(4),char(0),char(-90),char(0), -char(7),char(0),char(-89),char(0),char(7),char(0),char(-88),char(0),char(7),char(0),char(-87),char(0),char(7),char(0),char(-86),char(0),char(7),char(0),char(-85),char(0), -char(7),char(0),char(-84),char(0),char(7),char(0),char(-83),char(0),char(60),char(0),char(13),char(0),char(55),char(0),char(-97),char(0),char(17),char(0),char(-94),char(0), -char(17),char(0),char(-93),char(0),char(4),char(0),char(-92),char(0),char(4),char(0),char(-91),char(0),char(4),char(0),char(-90),char(0),char(7),char(0),char(-89),char(0), -char(7),char(0),char(-88),char(0),char(7),char(0),char(-87),char(0),char(7),char(0),char(-86),char(0),char(7),char(0),char(-85),char(0),char(7),char(0),char(-84),char(0), -char(7),char(0),char(-83),char(0),char(61),char(0),char(11),char(0),char(55),char(0),char(-97),char(0),char(17),char(0),char(-94),char(0),char(17),char(0),char(-93),char(0), -char(7),char(0),char(-82),char(0),char(7),char(0),char(-81),char(0),char(7),char(0),char(-80),char(0),char(7),char(0),char(-85),char(0),char(7),char(0),char(-84),char(0), -char(7),char(0),char(-83),char(0),char(7),char(0),char(-79),char(0),char(0),char(0),char(21),char(0),char(62),char(0),char(9),char(0),char(55),char(0),char(-97),char(0), -char(17),char(0),char(-94),char(0),char(17),char(0),char(-93),char(0),char(13),char(0),char(-78),char(0),char(13),char(0),char(-77),char(0),char(13),char(0),char(-76),char(0), -char(13),char(0),char(-75),char(0),char(4),char(0),char(-74),char(0),char(4),char(0),char(-73),char(0),char(63),char(0),char(5),char(0),char(62),char(0),char(-72),char(0), -char(4),char(0),char(-71),char(0),char(7),char(0),char(-70),char(0),char(7),char(0),char(-69),char(0),char(7),char(0),char(-68),char(0),char(64),char(0),char(9),char(0), -char(55),char(0),char(-97),char(0),char(17),char(0),char(-94),char(0),char(17),char(0),char(-93),char(0),char(7),char(0),char(-78),char(0),char(7),char(0),char(-77),char(0), -char(7),char(0),char(-76),char(0),char(7),char(0),char(-75),char(0),char(4),char(0),char(-74),char(0),char(4),char(0),char(-73),char(0),char(49),char(0),char(22),char(0), -char(8),char(0),char(-67),char(0),char(8),char(0),char(-79),char(0),char(8),char(0),char(110),char(0),char(8),char(0),char(-66),char(0),char(8),char(0),char(112),char(0), -char(8),char(0),char(-65),char(0),char(8),char(0),char(-64),char(0),char(8),char(0),char(-63),char(0),char(8),char(0),char(-62),char(0),char(8),char(0),char(-61),char(0), -char(8),char(0),char(-60),char(0),char(8),char(0),char(-59),char(0),char(8),char(0),char(-58),char(0),char(8),char(0),char(-57),char(0),char(8),char(0),char(-56),char(0), -char(8),char(0),char(-55),char(0),char(4),char(0),char(-54),char(0),char(4),char(0),char(-53),char(0),char(4),char(0),char(-52),char(0),char(4),char(0),char(-51),char(0), -char(4),char(0),char(-50),char(0),char(0),char(0),char(37),char(0),char(51),char(0),char(22),char(0),char(7),char(0),char(-67),char(0),char(7),char(0),char(-79),char(0), -char(7),char(0),char(110),char(0),char(7),char(0),char(-66),char(0),char(7),char(0),char(112),char(0),char(7),char(0),char(-65),char(0),char(7),char(0),char(-64),char(0), -char(7),char(0),char(-63),char(0),char(7),char(0),char(-62),char(0),char(7),char(0),char(-61),char(0),char(7),char(0),char(-60),char(0),char(7),char(0),char(-59),char(0), -char(7),char(0),char(-58),char(0),char(7),char(0),char(-57),char(0),char(7),char(0),char(-56),char(0),char(7),char(0),char(-55),char(0),char(4),char(0),char(-54),char(0), -char(4),char(0),char(-53),char(0),char(4),char(0),char(-52),char(0),char(4),char(0),char(-51),char(0),char(4),char(0),char(-50),char(0),char(0),char(0),char(37),char(0), -char(65),char(0),char(4),char(0),char(7),char(0),char(-49),char(0),char(7),char(0),char(-48),char(0),char(7),char(0),char(-47),char(0),char(4),char(0),char(78),char(0), -char(66),char(0),char(10),char(0),char(65),char(0),char(-46),char(0),char(13),char(0),char(-45),char(0),char(13),char(0),char(-44),char(0),char(13),char(0),char(-43),char(0), -char(13),char(0),char(-42),char(0),char(13),char(0),char(-41),char(0),char(7),char(0),char(-121),char(0),char(7),char(0),char(-40),char(0),char(4),char(0),char(-39),char(0), -char(4),char(0),char(53),char(0),char(67),char(0),char(4),char(0),char(65),char(0),char(-46),char(0),char(4),char(0),char(-38),char(0),char(7),char(0),char(-37),char(0), -char(4),char(0),char(-36),char(0),char(68),char(0),char(4),char(0),char(13),char(0),char(-41),char(0),char(65),char(0),char(-46),char(0),char(4),char(0),char(-35),char(0), -char(7),char(0),char(-34),char(0),char(69),char(0),char(7),char(0),char(13),char(0),char(-33),char(0),char(65),char(0),char(-46),char(0),char(4),char(0),char(-32),char(0), -char(7),char(0),char(-31),char(0),char(7),char(0),char(-30),char(0),char(7),char(0),char(-29),char(0),char(4),char(0),char(53),char(0),char(70),char(0),char(6),char(0), -char(15),char(0),char(-28),char(0),char(13),char(0),char(-30),char(0),char(13),char(0),char(-27),char(0),char(56),char(0),char(-26),char(0),char(4),char(0),char(-25),char(0), -char(7),char(0),char(-29),char(0),char(71),char(0),char(26),char(0),char(4),char(0),char(-24),char(0),char(7),char(0),char(-23),char(0),char(7),char(0),char(-79),char(0), -char(7),char(0),char(-22),char(0),char(7),char(0),char(-21),char(0),char(7),char(0),char(-20),char(0),char(7),char(0),char(-19),char(0),char(7),char(0),char(-18),char(0), -char(7),char(0),char(-17),char(0),char(7),char(0),char(-16),char(0),char(7),char(0),char(-15),char(0),char(7),char(0),char(-14),char(0),char(7),char(0),char(-13),char(0), -char(7),char(0),char(-12),char(0),char(7),char(0),char(-11),char(0),char(7),char(0),char(-10),char(0),char(7),char(0),char(-9),char(0),char(7),char(0),char(-8),char(0), -char(7),char(0),char(-7),char(0),char(7),char(0),char(-6),char(0),char(7),char(0),char(-5),char(0),char(4),char(0),char(-4),char(0),char(4),char(0),char(-3),char(0), -char(4),char(0),char(-2),char(0),char(4),char(0),char(-1),char(0),char(4),char(0),char(117),char(0),char(72),char(0),char(12),char(0),char(15),char(0),char(0),char(1), -char(15),char(0),char(1),char(1),char(15),char(0),char(2),char(1),char(13),char(0),char(3),char(1),char(13),char(0),char(4),char(1),char(7),char(0),char(5),char(1), -char(4),char(0),char(6),char(1),char(4),char(0),char(7),char(1),char(4),char(0),char(8),char(1),char(4),char(0),char(9),char(1),char(7),char(0),char(-31),char(0), -char(4),char(0),char(53),char(0),char(73),char(0),char(27),char(0),char(17),char(0),char(10),char(1),char(15),char(0),char(11),char(1),char(15),char(0),char(12),char(1), -char(13),char(0),char(3),char(1),char(13),char(0),char(13),char(1),char(13),char(0),char(14),char(1),char(13),char(0),char(15),char(1),char(13),char(0),char(16),char(1), -char(13),char(0),char(17),char(1),char(4),char(0),char(18),char(1),char(7),char(0),char(19),char(1),char(4),char(0),char(20),char(1),char(4),char(0),char(21),char(1), -char(4),char(0),char(22),char(1),char(7),char(0),char(23),char(1),char(7),char(0),char(24),char(1),char(4),char(0),char(25),char(1),char(4),char(0),char(26),char(1), -char(7),char(0),char(27),char(1),char(7),char(0),char(28),char(1),char(7),char(0),char(29),char(1),char(7),char(0),char(30),char(1),char(7),char(0),char(31),char(1), -char(7),char(0),char(32),char(1),char(4),char(0),char(33),char(1),char(4),char(0),char(34),char(1),char(4),char(0),char(35),char(1),char(74),char(0),char(12),char(0), -char(9),char(0),char(36),char(1),char(9),char(0),char(37),char(1),char(13),char(0),char(38),char(1),char(7),char(0),char(39),char(1),char(7),char(0),char(-63),char(0), -char(7),char(0),char(40),char(1),char(4),char(0),char(41),char(1),char(13),char(0),char(42),char(1),char(4),char(0),char(43),char(1),char(4),char(0),char(44),char(1), -char(4),char(0),char(45),char(1),char(4),char(0),char(53),char(0),char(75),char(0),char(19),char(0),char(47),char(0),char(125),char(0),char(72),char(0),char(46),char(1), -char(65),char(0),char(47),char(1),char(66),char(0),char(48),char(1),char(67),char(0),char(49),char(1),char(68),char(0),char(50),char(1),char(69),char(0),char(51),char(1), -char(70),char(0),char(52),char(1),char(73),char(0),char(53),char(1),char(74),char(0),char(54),char(1),char(4),char(0),char(55),char(1),char(4),char(0),char(21),char(1), -char(4),char(0),char(56),char(1),char(4),char(0),char(57),char(1),char(4),char(0),char(58),char(1),char(4),char(0),char(59),char(1),char(4),char(0),char(60),char(1), -char(4),char(0),char(61),char(1),char(71),char(0),char(62),char(1),}; +char(4),char(0),char(122),char(0),char(4),char(0),char(123),char(0),char(0),char(0),char(37),char(0),char(48),char(0),char(25),char(0),char(9),char(0),char(101),char(0), +char(9),char(0),char(102),char(0),char(25),char(0),char(103),char(0),char(0),char(0),char(35),char(0),char(17),char(0),char(104),char(0),char(17),char(0),char(105),char(0), +char(13),char(0),char(106),char(0),char(13),char(0),char(107),char(0),char(13),char(0),char(108),char(0),char(7),char(0),char(109),char(0),char(7),char(0),char(110),char(0), +char(7),char(0),char(111),char(0),char(7),char(0),char(112),char(0),char(7),char(0),char(113),char(0),char(7),char(0),char(114),char(0),char(7),char(0),char(115),char(0), +char(7),char(0),char(116),char(0),char(4),char(0),char(117),char(0),char(4),char(0),char(118),char(0),char(4),char(0),char(119),char(0),char(4),char(0),char(120),char(0), +char(4),char(0),char(121),char(0),char(4),char(0),char(122),char(0),char(4),char(0),char(123),char(0),char(0),char(0),char(37),char(0),char(49),char(0),char(2),char(0), +char(50),char(0),char(124),char(0),char(14),char(0),char(125),char(0),char(51),char(0),char(2),char(0),char(52),char(0),char(124),char(0),char(13),char(0),char(125),char(0), +char(53),char(0),char(21),char(0),char(48),char(0),char(126),char(0),char(15),char(0),char(127),char(0),char(13),char(0),char(-128),char(0),char(13),char(0),char(-127),char(0), +char(13),char(0),char(-126),char(0),char(13),char(0),char(-125),char(0),char(13),char(0),char(125),char(0),char(13),char(0),char(-124),char(0),char(13),char(0),char(-123),char(0), +char(13),char(0),char(-122),char(0),char(13),char(0),char(-121),char(0),char(7),char(0),char(-120),char(0),char(7),char(0),char(-119),char(0),char(7),char(0),char(-118),char(0), +char(7),char(0),char(-117),char(0),char(7),char(0),char(-116),char(0),char(7),char(0),char(-115),char(0),char(7),char(0),char(-114),char(0),char(7),char(0),char(-113),char(0), +char(7),char(0),char(-112),char(0),char(4),char(0),char(-111),char(0),char(54),char(0),char(22),char(0),char(47),char(0),char(126),char(0),char(16),char(0),char(127),char(0), +char(14),char(0),char(-128),char(0),char(14),char(0),char(-127),char(0),char(14),char(0),char(-126),char(0),char(14),char(0),char(-125),char(0),char(14),char(0),char(125),char(0), +char(14),char(0),char(-124),char(0),char(14),char(0),char(-123),char(0),char(14),char(0),char(-122),char(0),char(14),char(0),char(-121),char(0),char(8),char(0),char(-120),char(0), +char(8),char(0),char(-119),char(0),char(8),char(0),char(-118),char(0),char(8),char(0),char(-117),char(0),char(8),char(0),char(-116),char(0),char(8),char(0),char(-115),char(0), +char(8),char(0),char(-114),char(0),char(8),char(0),char(-113),char(0),char(8),char(0),char(-112),char(0),char(4),char(0),char(-111),char(0),char(0),char(0),char(37),char(0), +char(55),char(0),char(2),char(0),char(4),char(0),char(-110),char(0),char(4),char(0),char(-109),char(0),char(56),char(0),char(13),char(0),char(53),char(0),char(-108),char(0), +char(53),char(0),char(-107),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(-106),char(0),char(4),char(0),char(-105),char(0),char(4),char(0),char(-104),char(0), +char(4),char(0),char(-103),char(0),char(7),char(0),char(-102),char(0),char(7),char(0),char(-101),char(0),char(4),char(0),char(-100),char(0),char(4),char(0),char(-99),char(0), +char(7),char(0),char(-98),char(0),char(4),char(0),char(-97),char(0),char(57),char(0),char(13),char(0),char(58),char(0),char(-108),char(0),char(58),char(0),char(-107),char(0), +char(0),char(0),char(35),char(0),char(4),char(0),char(-106),char(0),char(4),char(0),char(-105),char(0),char(4),char(0),char(-104),char(0),char(4),char(0),char(-103),char(0), +char(7),char(0),char(-102),char(0),char(7),char(0),char(-101),char(0),char(4),char(0),char(-100),char(0),char(4),char(0),char(-99),char(0),char(7),char(0),char(-98),char(0), +char(4),char(0),char(-97),char(0),char(59),char(0),char(14),char(0),char(54),char(0),char(-108),char(0),char(54),char(0),char(-107),char(0),char(0),char(0),char(35),char(0), +char(4),char(0),char(-106),char(0),char(4),char(0),char(-105),char(0),char(4),char(0),char(-104),char(0),char(4),char(0),char(-103),char(0),char(8),char(0),char(-102),char(0), +char(8),char(0),char(-101),char(0),char(4),char(0),char(-100),char(0),char(4),char(0),char(-99),char(0),char(8),char(0),char(-98),char(0),char(4),char(0),char(-97),char(0), +char(0),char(0),char(-96),char(0),char(60),char(0),char(3),char(0),char(57),char(0),char(-95),char(0),char(13),char(0),char(-94),char(0),char(13),char(0),char(-93),char(0), +char(61),char(0),char(3),char(0),char(59),char(0),char(-95),char(0),char(14),char(0),char(-94),char(0),char(14),char(0),char(-93),char(0),char(62),char(0),char(3),char(0), +char(57),char(0),char(-95),char(0),char(14),char(0),char(-94),char(0),char(14),char(0),char(-93),char(0),char(63),char(0),char(13),char(0),char(57),char(0),char(-95),char(0), +char(18),char(0),char(-92),char(0),char(18),char(0),char(-91),char(0),char(4),char(0),char(-90),char(0),char(4),char(0),char(-89),char(0),char(4),char(0),char(-88),char(0), +char(7),char(0),char(-87),char(0),char(7),char(0),char(-86),char(0),char(7),char(0),char(-85),char(0),char(7),char(0),char(-84),char(0),char(7),char(0),char(-83),char(0), +char(7),char(0),char(-82),char(0),char(7),char(0),char(-81),char(0),char(64),char(0),char(13),char(0),char(57),char(0),char(-95),char(0),char(17),char(0),char(-92),char(0), +char(17),char(0),char(-91),char(0),char(4),char(0),char(-90),char(0),char(4),char(0),char(-89),char(0),char(4),char(0),char(-88),char(0),char(7),char(0),char(-87),char(0), +char(7),char(0),char(-86),char(0),char(7),char(0),char(-85),char(0),char(7),char(0),char(-84),char(0),char(7),char(0),char(-83),char(0),char(7),char(0),char(-82),char(0), +char(7),char(0),char(-81),char(0),char(65),char(0),char(14),char(0),char(59),char(0),char(-95),char(0),char(18),char(0),char(-92),char(0),char(18),char(0),char(-91),char(0), +char(4),char(0),char(-90),char(0),char(4),char(0),char(-89),char(0),char(4),char(0),char(-88),char(0),char(8),char(0),char(-87),char(0),char(8),char(0),char(-86),char(0), +char(8),char(0),char(-85),char(0),char(8),char(0),char(-84),char(0),char(8),char(0),char(-83),char(0),char(8),char(0),char(-82),char(0),char(8),char(0),char(-81),char(0), +char(0),char(0),char(-80),char(0),char(66),char(0),char(10),char(0),char(59),char(0),char(-95),char(0),char(18),char(0),char(-92),char(0),char(18),char(0),char(-91),char(0), +char(8),char(0),char(-79),char(0),char(8),char(0),char(-78),char(0),char(8),char(0),char(-77),char(0),char(8),char(0),char(-83),char(0),char(8),char(0),char(-82),char(0), +char(8),char(0),char(-81),char(0),char(8),char(0),char(-76),char(0),char(67),char(0),char(11),char(0),char(57),char(0),char(-95),char(0),char(17),char(0),char(-92),char(0), +char(17),char(0),char(-91),char(0),char(7),char(0),char(-79),char(0),char(7),char(0),char(-78),char(0),char(7),char(0),char(-77),char(0),char(7),char(0),char(-83),char(0), +char(7),char(0),char(-82),char(0),char(7),char(0),char(-81),char(0),char(7),char(0),char(-76),char(0),char(0),char(0),char(21),char(0),char(68),char(0),char(9),char(0), +char(57),char(0),char(-95),char(0),char(17),char(0),char(-92),char(0),char(17),char(0),char(-91),char(0),char(13),char(0),char(-75),char(0),char(13),char(0),char(-74),char(0), +char(13),char(0),char(-73),char(0),char(13),char(0),char(-72),char(0),char(4),char(0),char(-71),char(0),char(4),char(0),char(-70),char(0),char(69),char(0),char(9),char(0), +char(59),char(0),char(-95),char(0),char(18),char(0),char(-92),char(0),char(18),char(0),char(-91),char(0),char(14),char(0),char(-75),char(0),char(14),char(0),char(-74),char(0), +char(14),char(0),char(-73),char(0),char(14),char(0),char(-72),char(0),char(4),char(0),char(-71),char(0),char(4),char(0),char(-70),char(0),char(70),char(0),char(5),char(0), +char(68),char(0),char(-69),char(0),char(4),char(0),char(-68),char(0),char(7),char(0),char(-67),char(0),char(7),char(0),char(-66),char(0),char(7),char(0),char(-65),char(0), +char(71),char(0),char(5),char(0),char(69),char(0),char(-69),char(0),char(4),char(0),char(-68),char(0),char(8),char(0),char(-67),char(0),char(8),char(0),char(-66),char(0), +char(8),char(0),char(-65),char(0),char(72),char(0),char(9),char(0),char(57),char(0),char(-95),char(0),char(17),char(0),char(-92),char(0),char(17),char(0),char(-91),char(0), +char(7),char(0),char(-75),char(0),char(7),char(0),char(-74),char(0),char(7),char(0),char(-73),char(0),char(7),char(0),char(-72),char(0),char(4),char(0),char(-71),char(0), +char(4),char(0),char(-70),char(0),char(73),char(0),char(9),char(0),char(59),char(0),char(-95),char(0),char(18),char(0),char(-92),char(0),char(18),char(0),char(-91),char(0), +char(8),char(0),char(-75),char(0),char(8),char(0),char(-74),char(0),char(8),char(0),char(-73),char(0),char(8),char(0),char(-72),char(0),char(4),char(0),char(-71),char(0), +char(4),char(0),char(-70),char(0),char(74),char(0),char(5),char(0),char(56),char(0),char(-95),char(0),char(13),char(0),char(-64),char(0),char(13),char(0),char(-63),char(0), +char(7),char(0),char(-62),char(0),char(0),char(0),char(37),char(0),char(75),char(0),char(4),char(0),char(59),char(0),char(-95),char(0),char(14),char(0),char(-64),char(0), +char(14),char(0),char(-63),char(0),char(8),char(0),char(-62),char(0),char(50),char(0),char(22),char(0),char(8),char(0),char(-61),char(0),char(8),char(0),char(-76),char(0), +char(8),char(0),char(111),char(0),char(8),char(0),char(-60),char(0),char(8),char(0),char(113),char(0),char(8),char(0),char(-59),char(0),char(8),char(0),char(-58),char(0), +char(8),char(0),char(-57),char(0),char(8),char(0),char(-56),char(0),char(8),char(0),char(-55),char(0),char(8),char(0),char(-54),char(0),char(8),char(0),char(-53),char(0), +char(8),char(0),char(-52),char(0),char(8),char(0),char(-51),char(0),char(8),char(0),char(-50),char(0),char(8),char(0),char(-49),char(0),char(4),char(0),char(-48),char(0), +char(4),char(0),char(-47),char(0),char(4),char(0),char(-46),char(0),char(4),char(0),char(-45),char(0),char(4),char(0),char(-44),char(0),char(0),char(0),char(37),char(0), +char(52),char(0),char(22),char(0),char(7),char(0),char(-61),char(0),char(7),char(0),char(-76),char(0),char(7),char(0),char(111),char(0),char(7),char(0),char(-60),char(0), +char(7),char(0),char(113),char(0),char(7),char(0),char(-59),char(0),char(7),char(0),char(-58),char(0),char(7),char(0),char(-57),char(0),char(7),char(0),char(-56),char(0), +char(7),char(0),char(-55),char(0),char(7),char(0),char(-54),char(0),char(7),char(0),char(-53),char(0),char(7),char(0),char(-52),char(0),char(7),char(0),char(-51),char(0), +char(7),char(0),char(-50),char(0),char(7),char(0),char(-49),char(0),char(4),char(0),char(-48),char(0),char(4),char(0),char(-47),char(0),char(4),char(0),char(-46),char(0), +char(4),char(0),char(-45),char(0),char(4),char(0),char(-44),char(0),char(0),char(0),char(37),char(0),char(76),char(0),char(4),char(0),char(7),char(0),char(-43),char(0), +char(7),char(0),char(-42),char(0),char(7),char(0),char(-41),char(0),char(4),char(0),char(79),char(0),char(77),char(0),char(10),char(0),char(76),char(0),char(-40),char(0), +char(13),char(0),char(-39),char(0),char(13),char(0),char(-38),char(0),char(13),char(0),char(-37),char(0),char(13),char(0),char(-36),char(0),char(13),char(0),char(-35),char(0), +char(7),char(0),char(-120),char(0),char(7),char(0),char(-34),char(0),char(4),char(0),char(-33),char(0),char(4),char(0),char(53),char(0),char(78),char(0),char(4),char(0), +char(76),char(0),char(-40),char(0),char(4),char(0),char(-32),char(0),char(7),char(0),char(-31),char(0),char(4),char(0),char(-30),char(0),char(79),char(0),char(4),char(0), +char(13),char(0),char(-35),char(0),char(76),char(0),char(-40),char(0),char(4),char(0),char(-29),char(0),char(7),char(0),char(-28),char(0),char(80),char(0),char(7),char(0), +char(13),char(0),char(-27),char(0),char(76),char(0),char(-40),char(0),char(4),char(0),char(-26),char(0),char(7),char(0),char(-25),char(0),char(7),char(0),char(-24),char(0), +char(7),char(0),char(-23),char(0),char(4),char(0),char(53),char(0),char(81),char(0),char(6),char(0),char(15),char(0),char(-22),char(0),char(13),char(0),char(-24),char(0), +char(13),char(0),char(-21),char(0),char(58),char(0),char(-20),char(0),char(4),char(0),char(-19),char(0),char(7),char(0),char(-23),char(0),char(82),char(0),char(26),char(0), +char(4),char(0),char(-18),char(0),char(7),char(0),char(-17),char(0),char(7),char(0),char(-76),char(0),char(7),char(0),char(-16),char(0),char(7),char(0),char(-15),char(0), +char(7),char(0),char(-14),char(0),char(7),char(0),char(-13),char(0),char(7),char(0),char(-12),char(0),char(7),char(0),char(-11),char(0),char(7),char(0),char(-10),char(0), +char(7),char(0),char(-9),char(0),char(7),char(0),char(-8),char(0),char(7),char(0),char(-7),char(0),char(7),char(0),char(-6),char(0),char(7),char(0),char(-5),char(0), +char(7),char(0),char(-4),char(0),char(7),char(0),char(-3),char(0),char(7),char(0),char(-2),char(0),char(7),char(0),char(-1),char(0),char(7),char(0),char(0),char(1), +char(7),char(0),char(1),char(1),char(4),char(0),char(2),char(1),char(4),char(0),char(3),char(1),char(4),char(0),char(4),char(1),char(4),char(0),char(5),char(1), +char(4),char(0),char(118),char(0),char(83),char(0),char(12),char(0),char(15),char(0),char(6),char(1),char(15),char(0),char(7),char(1),char(15),char(0),char(8),char(1), +char(13),char(0),char(9),char(1),char(13),char(0),char(10),char(1),char(7),char(0),char(11),char(1),char(4),char(0),char(12),char(1),char(4),char(0),char(13),char(1), +char(4),char(0),char(14),char(1),char(4),char(0),char(15),char(1),char(7),char(0),char(-25),char(0),char(4),char(0),char(53),char(0),char(84),char(0),char(27),char(0), +char(17),char(0),char(16),char(1),char(15),char(0),char(17),char(1),char(15),char(0),char(18),char(1),char(13),char(0),char(9),char(1),char(13),char(0),char(19),char(1), +char(13),char(0),char(20),char(1),char(13),char(0),char(21),char(1),char(13),char(0),char(22),char(1),char(13),char(0),char(23),char(1),char(4),char(0),char(24),char(1), +char(7),char(0),char(25),char(1),char(4),char(0),char(26),char(1),char(4),char(0),char(27),char(1),char(4),char(0),char(28),char(1),char(7),char(0),char(29),char(1), +char(7),char(0),char(30),char(1),char(4),char(0),char(31),char(1),char(4),char(0),char(32),char(1),char(7),char(0),char(33),char(1),char(7),char(0),char(34),char(1), +char(7),char(0),char(35),char(1),char(7),char(0),char(36),char(1),char(7),char(0),char(37),char(1),char(7),char(0),char(38),char(1),char(4),char(0),char(39),char(1), +char(4),char(0),char(40),char(1),char(4),char(0),char(41),char(1),char(85),char(0),char(12),char(0),char(9),char(0),char(42),char(1),char(9),char(0),char(43),char(1), +char(13),char(0),char(44),char(1),char(7),char(0),char(45),char(1),char(7),char(0),char(-57),char(0),char(7),char(0),char(46),char(1),char(4),char(0),char(47),char(1), +char(13),char(0),char(48),char(1),char(4),char(0),char(49),char(1),char(4),char(0),char(50),char(1),char(4),char(0),char(51),char(1),char(4),char(0),char(53),char(0), +char(86),char(0),char(19),char(0),char(48),char(0),char(126),char(0),char(83),char(0),char(52),char(1),char(76),char(0),char(53),char(1),char(77),char(0),char(54),char(1), +char(78),char(0),char(55),char(1),char(79),char(0),char(56),char(1),char(80),char(0),char(57),char(1),char(81),char(0),char(58),char(1),char(84),char(0),char(59),char(1), +char(85),char(0),char(60),char(1),char(4),char(0),char(61),char(1),char(4),char(0),char(27),char(1),char(4),char(0),char(62),char(1),char(4),char(0),char(63),char(1), +char(4),char(0),char(64),char(1),char(4),char(0),char(65),char(1),char(4),char(0),char(66),char(1),char(4),char(0),char(67),char(1),char(82),char(0),char(68),char(1), +}; int sBulletDNAlen= sizeof(sBulletDNAstr); + char sBulletDNAstr64[]= { -char(83),char(68),char(78),char(65),char(78),char(65),char(77),char(69),char(63),char(1),char(0),char(0),char(109),char(95),char(115),char(105),char(122),char(101),char(0),char(109), +char(83),char(68),char(78),char(65),char(78),char(65),char(77),char(69),char(69),char(1),char(0),char(0),char(109),char(95),char(115),char(105),char(122),char(101),char(0),char(109), char(95),char(99),char(97),char(112),char(97),char(99),char(105),char(116),char(121),char(0),char(42),char(109),char(95),char(100),char(97),char(116),char(97),char(0),char(109),char(95), char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(115),char(0),char(109),char(95),char(99),char(111), char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116),char(115),char(0),char(109),char(95),char(99),char(111),char(110), @@ -512,238 +554,242 @@ char(102),char(111),char(114),char(109),char(0),char(42),char(109),char(95),char char(95),char(99),char(104),char(105),char(108),char(100),char(83),char(104),char(97),char(112),char(101),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(99),char(104), char(105),char(108),char(100),char(77),char(97),char(114),char(103),char(105),char(110),char(0),char(42),char(109),char(95),char(99),char(104),char(105),char(108),char(100),char(83),char(104), char(97),char(112),char(101),char(80),char(116),char(114),char(0),char(109),char(95),char(110),char(117),char(109),char(67),char(104),char(105),char(108),char(100),char(83),char(104),char(97), -char(112),char(101),char(115),char(0),char(109),char(95),char(117),char(112),char(65),char(120),char(105),char(115),char(0),char(109),char(95),char(102),char(108),char(97),char(103),char(115), -char(0),char(109),char(95),char(101),char(100),char(103),char(101),char(86),char(48),char(86),char(49),char(65),char(110),char(103),char(108),char(101),char(0),char(109),char(95),char(101), -char(100),char(103),char(101),char(86),char(49),char(86),char(50),char(65),char(110),char(103),char(108),char(101),char(0),char(109),char(95),char(101),char(100),char(103),char(101),char(86), -char(50),char(86),char(48),char(65),char(110),char(103),char(108),char(101),char(0),char(42),char(109),char(95),char(104),char(97),char(115),char(104),char(84),char(97),char(98),char(108), -char(101),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(110),char(101),char(120),char(116),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(118), -char(97),char(108),char(117),char(101),char(65),char(114),char(114),char(97),char(121),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(107),char(101),char(121),char(65), -char(114),char(114),char(97),char(121),char(80),char(116),char(114),char(0),char(109),char(95),char(99),char(111),char(110),char(118),char(101),char(120),char(69),char(112),char(115),char(105), -char(108),char(111),char(110),char(0),char(109),char(95),char(112),char(108),char(97),char(110),char(97),char(114),char(69),char(112),char(115),char(105),char(108),char(111),char(110),char(0), -char(109),char(95),char(101),char(113),char(117),char(97),char(108),char(86),char(101),char(114),char(116),char(101),char(120),char(84),char(104),char(114),char(101),char(115),char(104),char(111), -char(108),char(100),char(0),char(109),char(95),char(101),char(100),char(103),char(101),char(68),char(105),char(115),char(116),char(97),char(110),char(99),char(101),char(84),char(104),char(114), -char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(122),char(101),char(114),char(111),char(65),char(114),char(101),char(97),char(84),char(104),char(114), -char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(110),char(101),char(120),char(116),char(83),char(105),char(122),char(101),char(0),char(109),char(95), -char(104),char(97),char(115),char(104),char(84),char(97),char(98),char(108),char(101),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(110),char(117),char(109),char(86), -char(97),char(108),char(117),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(75),char(101),char(121),char(115),char(0),char(109),char(95),char(103),char(105), -char(109),char(112),char(97),char(99),char(116),char(83),char(117),char(98),char(84),char(121),char(112),char(101),char(0),char(42),char(109),char(95),char(117),char(110),char(115),char(99), -char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115),char(70),char(108),char(111),char(97),char(116),char(80),char(116),char(114),char(0),char(42), -char(109),char(95),char(117),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115),char(68),char(111),char(117),char(98), -char(108),char(101),char(80),char(116),char(114),char(0),char(109),char(95),char(110),char(117),char(109),char(85),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80), -char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(112),char(97),char(100),char(100),char(105),char(110),char(103),char(51),char(91),char(52),char(93),char(0), -char(42),char(109),char(95),char(98),char(114),char(111),char(97),char(100),char(112),char(104),char(97),char(115),char(101),char(72),char(97),char(110),char(100),char(108),char(101),char(0), -char(42),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0),char(42),char(109), -char(95),char(114),char(111),char(111),char(116),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0), -char(109),char(95),char(119),char(111),char(114),char(108),char(100),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(105), -char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105),char(111),char(110),char(87),char(111),char(114),char(108),char(100),char(84),char(114),char(97), +char(112),char(101),char(115),char(0),char(109),char(95),char(117),char(112),char(65),char(120),char(105),char(115),char(0),char(109),char(95),char(117),char(112),char(73),char(110),char(100), +char(101),char(120),char(0),char(109),char(95),char(102),char(108),char(97),char(103),char(115),char(0),char(109),char(95),char(101),char(100),char(103),char(101),char(86),char(48),char(86), +char(49),char(65),char(110),char(103),char(108),char(101),char(0),char(109),char(95),char(101),char(100),char(103),char(101),char(86),char(49),char(86),char(50),char(65),char(110),char(103), +char(108),char(101),char(0),char(109),char(95),char(101),char(100),char(103),char(101),char(86),char(50),char(86),char(48),char(65),char(110),char(103),char(108),char(101),char(0),char(42), +char(109),char(95),char(104),char(97),char(115),char(104),char(84),char(97),char(98),char(108),char(101),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(110),char(101), +char(120),char(116),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(118),char(97),char(108),char(117),char(101),char(65),char(114),char(114),char(97),char(121),char(80), +char(116),char(114),char(0),char(42),char(109),char(95),char(107),char(101),char(121),char(65),char(114),char(114),char(97),char(121),char(80),char(116),char(114),char(0),char(109),char(95), +char(99),char(111),char(110),char(118),char(101),char(120),char(69),char(112),char(115),char(105),char(108),char(111),char(110),char(0),char(109),char(95),char(112),char(108),char(97),char(110), +char(97),char(114),char(69),char(112),char(115),char(105),char(108),char(111),char(110),char(0),char(109),char(95),char(101),char(113),char(117),char(97),char(108),char(86),char(101),char(114), +char(116),char(101),char(120),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(101),char(100),char(103),char(101),char(68), +char(105),char(115),char(116),char(97),char(110),char(99),char(101),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(122), +char(101),char(114),char(111),char(65),char(114),char(101),char(97),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(110), +char(101),char(120),char(116),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(104),char(97),char(115),char(104),char(84),char(97),char(98),char(108),char(101),char(83), +char(105),char(122),char(101),char(0),char(109),char(95),char(110),char(117),char(109),char(86),char(97),char(108),char(117),char(101),char(115),char(0),char(109),char(95),char(110),char(117), +char(109),char(75),char(101),char(121),char(115),char(0),char(109),char(95),char(103),char(105),char(109),char(112),char(97),char(99),char(116),char(83),char(117),char(98),char(84),char(121), +char(112),char(101),char(0),char(42),char(109),char(95),char(117),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115), +char(70),char(108),char(111),char(97),char(116),char(80),char(116),char(114),char(0),char(42),char(109),char(95),char(117),char(110),char(115),char(99),char(97),char(108),char(101),char(100), +char(80),char(111),char(105),char(110),char(116),char(115),char(68),char(111),char(117),char(98),char(108),char(101),char(80),char(116),char(114),char(0),char(109),char(95),char(110),char(117), +char(109),char(85),char(110),char(115),char(99),char(97),char(108),char(101),char(100),char(80),char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(112),char(97), +char(100),char(100),char(105),char(110),char(103),char(51),char(91),char(52),char(93),char(0),char(42),char(109),char(95),char(98),char(114),char(111),char(97),char(100),char(112),char(104), +char(97),char(115),char(101),char(72),char(97),char(110),char(100),char(108),char(101),char(0),char(42),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105), +char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0),char(42),char(109),char(95),char(114),char(111),char(111),char(116),char(67),char(111),char(108),char(108),char(105), +char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(0),char(109),char(95),char(119),char(111),char(114),char(108),char(100),char(84),char(114),char(97), char(110),char(115),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105), -char(111),char(110),char(76),char(105),char(110),char(101),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(105), -char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105),char(111),char(110),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(86), -char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(105),char(115),char(111),char(116),char(114),char(111),char(112),char(105), -char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(99),char(116),char(80), -char(114),char(111),char(99),char(101),char(115),char(115),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109), -char(95),char(100),char(101),char(97),char(99),char(116),char(105),char(118),char(97),char(116),char(105),char(111),char(110),char(84),char(105),char(109),char(101),char(0),char(109),char(95), -char(102),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(114),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114), -char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(105),char(116),char(117),char(116),char(105),char(111),char(110), -char(0),char(109),char(95),char(104),char(105),char(116),char(70),char(114),char(97),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(99),char(100), -char(83),char(119),char(101),char(112),char(116),char(83),char(112),char(104),char(101),char(114),char(101),char(82),char(97),char(100),char(105),char(117),char(115),char(0),char(109),char(95), -char(99),char(99),char(100),char(77),char(111),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109), -char(95),char(104),char(97),char(115),char(65),char(110),char(105),char(115),char(111),char(116),char(114),char(111),char(112),char(105),char(99),char(70),char(114),char(105),char(99),char(116), -char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(70),char(108),char(97),char(103),char(115), -char(0),char(109),char(95),char(105),char(115),char(108),char(97),char(110),char(100),char(84),char(97),char(103),char(49),char(0),char(109),char(95),char(99),char(111),char(109),char(112), -char(97),char(110),char(105),char(111),char(110),char(73),char(100),char(0),char(109),char(95),char(97),char(99),char(116),char(105),char(118),char(97),char(116),char(105),char(111),char(110), -char(83),char(116),char(97),char(116),char(101),char(49),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(110),char(97),char(108),char(84),char(121),char(112), -char(101),char(0),char(109),char(95),char(99),char(104),char(101),char(99),char(107),char(67),char(111),char(108),char(108),char(105),char(100),char(101),char(87),char(105),char(116),char(104), -char(0),char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114),char(73),char(110),char(102),char(111),char(0),char(109),char(95),char(103),char(114),char(97),char(118), -char(105),char(116),char(121),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99), -char(116),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(105),char(110),char(118),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(84),char(101), -char(110),char(115),char(111),char(114),char(87),char(111),char(114),char(108),char(100),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(86),char(101), -char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111), -char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(70),char(97),char(99),char(116),char(111),char(114), -char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(103),char(114), -char(97),char(118),char(105),char(116),char(121),char(95),char(97),char(99),char(99),char(101),char(108),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(0),char(109), -char(95),char(105),char(110),char(118),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(76),char(111),char(99),char(97),char(108),char(0),char(109),char(95),char(116), -char(111),char(116),char(97),char(108),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(116),char(111),char(116),char(97),char(108),char(84),char(111),char(114), -char(113),char(117),char(101),char(0),char(109),char(95),char(105),char(110),char(118),char(101),char(114),char(115),char(101),char(77),char(97),char(115),char(115),char(0),char(109),char(95), -char(108),char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(110),char(103),char(117), -char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111), -char(110),char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(97), -char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(76),char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109),char(112),char(105), -char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100), -char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110), -char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105), -char(116),char(105),char(111),char(110),char(97),char(108),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103), -char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(108),char(101),char(101),char(112), -char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108), -char(97),char(114),char(83),char(108),char(101),char(101),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0), -char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0), -char(109),char(95),char(110),char(117),char(109),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(82),char(111),char(119),char(115),char(0), -char(110),char(117),char(98),char(0),char(42),char(109),char(95),char(114),char(98),char(65),char(0),char(42),char(109),char(95),char(114),char(98),char(66),char(0),char(109),char(95), -char(111),char(98),char(106),char(101),char(99),char(116),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111),char(110), -char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111), -char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(73),char(100),char(0),char(109),char(95),char(110),char(101),char(101),char(100),char(115),char(70),char(101), -char(101),char(100),char(98),char(97),char(99),char(107),char(0),char(109),char(95),char(97),char(112),char(112),char(108),char(105),char(101),char(100),char(73),char(109),char(112),char(117), -char(108),char(115),char(101),char(0),char(109),char(95),char(100),char(98),char(103),char(68),char(114),char(97),char(119),char(83),char(105),char(122),char(101),char(0),char(109),char(95), -char(100),char(105),char(115),char(97),char(98),char(108),char(101),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(115),char(66),char(101),char(116), -char(119),char(101),char(101),char(110),char(76),char(105),char(110),char(107),char(101),char(100),char(66),char(111),char(100),char(105),char(101),char(115),char(0),char(109),char(95),char(111), -char(118),char(101),char(114),char(114),char(105),char(100),char(101),char(78),char(117),char(109),char(83),char(111),char(108),char(118),char(101),char(114),char(73),char(116),char(101),char(114), -char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(98),char(114),char(101),char(97),char(107),char(105),char(110),char(103),char(73),char(109),char(112), -char(117),char(108),char(115),char(101),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(105),char(115),char(69),char(110), -char(97),char(98),char(108),char(101),char(100),char(0),char(109),char(95),char(116),char(121),char(112),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105), -char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(112),char(105),char(118),char(111),char(116),char(73),char(110),char(65),char(0),char(109),char(95), -char(112),char(105),char(118),char(111),char(116),char(73),char(110),char(66),char(0),char(109),char(95),char(114),char(98),char(65),char(70),char(114),char(97),char(109),char(101),char(0), -char(109),char(95),char(114),char(98),char(66),char(70),char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(82),char(101),char(102),char(101), -char(114),char(101),char(110),char(99),char(101),char(70),char(114),char(97),char(109),char(101),char(65),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97), -char(114),char(79),char(110),char(108),char(121),char(0),char(109),char(95),char(101),char(110),char(97),char(98),char(108),char(101),char(65),char(110),char(103),char(117),char(108),char(97), -char(114),char(77),char(111),char(116),char(111),char(114),char(0),char(109),char(95),char(109),char(111),char(116),char(111),char(114),char(84),char(97),char(114),char(103),char(101),char(116), -char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(109),char(97),char(120),char(77),char(111),char(116),char(111),char(114),char(73), -char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(108),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0), -char(109),char(95),char(117),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(108),char(105),char(109),char(105),char(116), -char(83),char(111),char(102),char(116),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(98),char(105),char(97),char(115),char(70),char(97),char(99),char(116),char(111), -char(114),char(0),char(109),char(95),char(114),char(101),char(108),char(97),char(120),char(97),char(116),char(105),char(111),char(110),char(70),char(97),char(99),char(116),char(111),char(114), -char(0),char(109),char(95),char(115),char(119),char(105),char(110),char(103),char(83),char(112),char(97),char(110),char(49),char(0),char(109),char(95),char(115),char(119),char(105),char(110), -char(103),char(83),char(112),char(97),char(110),char(50),char(0),char(109),char(95),char(116),char(119),char(105),char(115),char(116),char(83),char(112),char(97),char(110),char(0),char(109), -char(95),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(85),char(112),char(112), -char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(76),char(111),char(119),char(101), -char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(85),char(112),char(112),char(101), -char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(76),char(111),char(119),char(101), -char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(117),char(115),char(101),char(76),char(105),char(110),char(101),char(97),char(114),char(82),char(101), -char(102),char(101),char(114),char(101),char(110),char(99),char(101),char(70),char(114),char(97),char(109),char(101),char(65),char(0),char(109),char(95),char(117),char(115),char(101),char(79), -char(102),char(102),char(115),char(101),char(116),char(70),char(111),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(114), -char(97),char(109),char(101),char(0),char(109),char(95),char(54),char(100),char(111),char(102),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(115),char(112),char(114), -char(105),char(110),char(103),char(69),char(110),char(97),char(98),char(108),char(101),char(100),char(91),char(54),char(93),char(0),char(109),char(95),char(101),char(113),char(117),char(105), -char(108),char(105),char(98),char(114),char(105),char(117),char(109),char(80),char(111),char(105),char(110),char(116),char(91),char(54),char(93),char(0),char(109),char(95),char(115),char(112), -char(114),char(105),char(110),char(103),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(91),char(54),char(93),char(0),char(109),char(95),char(115), -char(112),char(114),char(105),char(110),char(103),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(91),char(54),char(93),char(0),char(109),char(95),char(116),char(97), -char(117),char(0),char(109),char(95),char(116),char(105),char(109),char(101),char(83),char(116),char(101),char(112),char(0),char(109),char(95),char(109),char(97),char(120),char(69),char(114), -char(114),char(111),char(114),char(82),char(101),char(100),char(117),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(115),char(111),char(114),char(0),char(109), -char(95),char(101),char(114),char(112),char(0),char(109),char(95),char(101),char(114),char(112),char(50),char(0),char(109),char(95),char(103),char(108),char(111),char(98),char(97),char(108), -char(67),char(102),char(109),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(80),char(101), -char(110),char(101),char(116),char(114),char(97),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109), -char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(84),char(117),char(114),char(110),char(69),char(114),char(112), -char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(108),char(111),char(112),char(0),char(109),char(95),char(119),char(97),char(114),char(109), -char(115),char(116),char(97),char(114),char(116),char(105),char(110),char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(109),char(97),char(120), -char(71),char(121),char(114),char(111),char(115),char(99),char(111),char(112),char(105),char(99),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(115),char(105), -char(110),char(103),char(108),char(101),char(65),char(120),char(105),char(115),char(82),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114),char(105),char(99),char(116), -char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(110),char(117),char(109),char(73),char(116), -char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114),char(77),char(111),char(100), -char(101),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(105),char(110),char(103),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(82),char(101), -char(115),char(116),char(105),char(116),char(117),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109), -char(95),char(109),char(105),char(110),char(105),char(109),char(117),char(109),char(83),char(111),char(108),char(118),char(101),char(114),char(66),char(97),char(116),char(99),char(104),char(83), -char(105),char(122),char(101),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109), -char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(97), -char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(118),char(111), -char(108),char(117),char(109),char(101),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(42),char(109),char(95),char(109),char(97),char(116), -char(101),char(114),char(105),char(97),char(108),char(0),char(109),char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(112), -char(114),char(101),char(118),char(105),char(111),char(117),char(115),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(118),char(101), -char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(99),char(99),char(117),char(109),char(117),char(108),char(97),char(116),char(101),char(100), -char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(110),char(111),char(114),char(109),char(97),char(108),char(0),char(109),char(95),char(97),char(114),char(101), -char(97),char(0),char(109),char(95),char(97),char(116),char(116),char(97),char(99),char(104),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100), -char(105),char(99),char(101),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(76),char(101),char(110),char(103),char(116),char(104), -char(0),char(109),char(95),char(98),char(98),char(101),char(110),char(100),char(105),char(110),char(103),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110), -char(100),char(105),char(99),char(101),char(115),char(91),char(51),char(93),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(65),char(114),char(101),char(97),char(0), -char(109),char(95),char(99),char(48),char(91),char(52),char(93),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101), -char(115),char(91),char(52),char(93),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(86),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95), -char(99),char(49),char(0),char(109),char(95),char(99),char(50),char(0),char(109),char(95),char(99),char(48),char(0),char(109),char(95),char(108),char(111),char(99),char(97),char(108), -char(70),char(114),char(97),char(109),char(101),char(0),char(42),char(109),char(95),char(114),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(0),char(109), -char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(101),char(120),char(0),char(109),char(95),char(97),char(101),char(114),char(111),char(77),char(111),char(100), -char(101),char(108),char(0),char(109),char(95),char(98),char(97),char(117),char(109),char(103),char(97),char(114),char(116),char(101),char(0),char(109),char(95),char(100),char(114),char(97), -char(103),char(0),char(109),char(95),char(108),char(105),char(102),char(116),char(0),char(109),char(95),char(112),char(114),char(101),char(115),char(115),char(117),char(114),char(101),char(0), -char(109),char(95),char(118),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(100),char(121),char(110),char(97),char(109),char(105),char(99),char(70),char(114), -char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(112),char(111),char(115),char(101),char(77),char(97),char(116),char(99),char(104),char(0),char(109), -char(95),char(114),char(105),char(103),char(105),char(100),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115), -char(115),char(0),char(109),char(95),char(107),char(105),char(110),char(101),char(116),char(105),char(99),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97), -char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(67),char(111),char(110),char(116),char(97),char(99),char(116), -char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(97),char(110),char(99),char(104),char(111),char(114),char(72),char(97),char(114), -char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(67),char(108),char(117), -char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(75), -char(105),char(110),char(101),char(116),char(105),char(99),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115), -char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(83),char(111),char(102),char(116),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72), -char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(67), -char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105),char(116),char(0),char(109), -char(95),char(115),char(111),char(102),char(116),char(75),char(105),char(110),char(101),char(116),char(105),char(99),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73), -char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(83),char(111), -char(102),char(116),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105), -char(116),char(0),char(109),char(95),char(109),char(97),char(120),char(86),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(116),char(105),char(109),char(101), -char(83),char(99),char(97),char(108),char(101),char(0),char(109),char(95),char(118),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(73),char(116),char(101),char(114), -char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(73),char(116),char(101), -char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(100),char(114),char(105),char(102),char(116),char(73),char(116),char(101),char(114),char(97), -char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(116),char(101),char(114),char(97), -char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(114),char(111),char(116),char(0),char(109),char(95),char(115),char(99),char(97),char(108),char(101),char(0), -char(109),char(95),char(97),char(113),char(113),char(0),char(109),char(95),char(99),char(111),char(109),char(0),char(42),char(109),char(95),char(112),char(111),char(115),char(105),char(116), -char(105),char(111),char(110),char(115),char(0),char(42),char(109),char(95),char(119),char(101),char(105),char(103),char(104),char(116),char(115),char(0),char(109),char(95),char(110),char(117), -char(109),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(87),char(101),char(105),char(103), -char(116),char(115),char(0),char(109),char(95),char(98),char(118),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(98),char(102),char(114),char(97),char(109), -char(101),char(0),char(109),char(95),char(102),char(114),char(97),char(109),char(101),char(120),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(108),char(111),char(99), -char(105),char(105),char(0),char(109),char(95),char(105),char(110),char(118),char(119),char(105),char(0),char(109),char(95),char(118),char(105),char(109),char(112),char(117),char(108),char(115), -char(101),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(100),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(91),char(50),char(93), -char(0),char(109),char(95),char(108),char(118),char(0),char(109),char(95),char(97),char(118),char(0),char(42),char(109),char(95),char(102),char(114),char(97),char(109),char(101),char(114), -char(101),char(102),char(115),char(0),char(42),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(0),char(42), -char(109),char(95),char(109),char(97),char(115),char(115),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(70),char(114),char(97),char(109),char(101),char(82), -char(101),char(102),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(78),char(111),char(100),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109), -char(77),char(97),char(115),char(115),char(101),char(115),char(0),char(109),char(95),char(105),char(100),char(109),char(97),char(115),char(115),char(0),char(109),char(95),char(105),char(109), -char(97),char(115),char(115),char(0),char(109),char(95),char(110),char(118),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(0),char(109),char(95),char(110), -char(100),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(0),char(109),char(95),char(110),char(100),char(97),char(109),char(112),char(105),char(110),char(103), -char(0),char(109),char(95),char(108),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(100),char(97),char(109),char(112),char(105), -char(110),char(103),char(0),char(109),char(95),char(109),char(97),char(116),char(99),char(104),char(105),char(110),char(103),char(0),char(109),char(95),char(109),char(97),char(120),char(83), -char(101),char(108),char(102),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0), -char(109),char(95),char(115),char(101),char(108),char(102),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(73),char(109),char(112),char(117),char(108), -char(115),char(101),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(105),char(110),char(115),char(65), -char(110),char(99),char(104),char(111),char(114),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(100),char(101),char(0),char(109),char(95),char(99),char(108), -char(117),char(115),char(116),char(101),char(114),char(73),char(110),char(100),char(101),char(120),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121),char(65),char(0), -char(42),char(109),char(95),char(98),char(111),char(100),char(121),char(66),char(0),char(109),char(95),char(114),char(101),char(102),char(115),char(91),char(50),char(93),char(0),char(109), -char(95),char(99),char(102),char(109),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(100),char(101),char(108),char(101),char(116), -char(101),char(0),char(109),char(95),char(114),char(101),char(108),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(91),char(50),char(93),char(0),char(109), -char(95),char(98),char(111),char(100),char(121),char(65),char(116),char(121),char(112),char(101),char(0),char(109),char(95),char(98),char(111),char(100),char(121),char(66),char(116),char(121), -char(112),char(101),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(84),char(121),char(112),char(101),char(0),char(42),char(109),char(95),char(112),char(111), -char(115),char(101),char(0),char(42),char(42),char(109),char(95),char(109),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(115),char(0),char(42),char(109),char(95), -char(110),char(111),char(100),char(101),char(115),char(0),char(42),char(109),char(95),char(108),char(105),char(110),char(107),char(115),char(0),char(42),char(109),char(95),char(102),char(97), -char(99),char(101),char(115),char(0),char(42),char(109),char(95),char(116),char(101),char(116),char(114),char(97),char(104),char(101),char(100),char(114),char(97),char(0),char(42),char(109), -char(95),char(97),char(110),char(99),char(104),char(111),char(114),char(115),char(0),char(42),char(109),char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(115), -char(0),char(42),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(77),char(97),char(116),char(101), -char(114),char(105),char(97),char(108),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(76),char(105),char(110),char(107),char(115),char(0),char(109),char(95),char(110), -char(117),char(109),char(70),char(97),char(99),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(84),char(101),char(116),char(114),char(97),char(104),char(101), -char(100),char(114),char(97),char(0),char(109),char(95),char(110),char(117),char(109),char(65),char(110),char(99),char(104),char(111),char(114),char(115),char(0),char(109),char(95),char(110), -char(117),char(109),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(74),char(111),char(105),char(110), -char(116),char(115),char(0),char(109),char(95),char(99),char(111),char(110),char(102),char(105),char(103),char(0),char(84),char(89),char(80),char(69),char(76),char(0),char(0),char(0), -char(99),char(104),char(97),char(114),char(0),char(117),char(99),char(104),char(97),char(114),char(0),char(115),char(104),char(111),char(114),char(116),char(0),char(117),char(115),char(104), -char(111),char(114),char(116),char(0),char(105),char(110),char(116),char(0),char(108),char(111),char(110),char(103),char(0),char(117),char(108),char(111),char(110),char(103),char(0),char(102), -char(108),char(111),char(97),char(116),char(0),char(100),char(111),char(117),char(98),char(108),char(101),char(0),char(118),char(111),char(105),char(100),char(0),char(80),char(111),char(105), -char(110),char(116),char(101),char(114),char(65),char(114),char(114),char(97),char(121),char(0),char(98),char(116),char(80),char(104),char(121),char(115),char(105),char(99),char(115),char(83), -char(121),char(115),char(116),char(101),char(109),char(0),char(76),char(105),char(115),char(116),char(66),char(97),char(115),char(101),char(0),char(98),char(116),char(86),char(101),char(99), -char(116),char(111),char(114),char(51),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(86),char(101),char(99),char(116), -char(111),char(114),char(51),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(97),char(116),char(114), -char(105),char(120),char(51),char(120),char(51),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(97),char(116), -char(114),char(105),char(120),char(51),char(120),char(51),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84), -char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0), -char(98),char(116),char(66),char(118),char(104),char(83),char(117),char(98),char(116),char(114),char(101),char(101),char(73),char(110),char(102),char(111),char(68),char(97),char(116),char(97), -char(0),char(98),char(116),char(79),char(112),char(116),char(105),char(109),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(70), -char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(79),char(112),char(116),char(105),char(109),char(105),char(122),char(101),char(100), -char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(68),char(97),char(116),char(97), -char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(70),char(108),char(111),char(97),char(116), -char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(68), -char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111), -char(110),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(116),char(97),char(116),char(105),char(99),char(80), -char(108),char(97),char(110),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(118), -char(101),char(120),char(73),char(110),char(116),char(101),char(114),char(110),char(97),char(108),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0), -char(98),char(116),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(65),char(110),char(100),char(82),char(97),char(100),char(105),char(117),char(115),char(0), -char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(83),char(112),char(104),char(101),char(114),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97), -char(116),char(97),char(0),char(98),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(83),char(104),char(111),char(114),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(83),char(104),char(111),char(114),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(84),char(114),char(105),char(112),char(108),char(101),char(116), -char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(104),char(97),char(114),char(73),char(110),char(100),char(101),char(120),char(84),char(114),char(105),char(112), -char(108),char(101),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(101),char(115),char(104),char(80),char(97),char(114),char(116),char(68),char(97), -char(116),char(97),char(0),char(98),char(116),char(83),char(116),char(114),char(105),char(100),char(105),char(110),char(103),char(77),char(101),char(115),char(104),char(73),char(110),char(116), -char(101),char(114),char(102),char(97),char(99),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(105),char(97),char(110),char(103),char(108), -char(101),char(77),char(101),char(115),char(104),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(105), -char(97),char(110),char(103),char(108),char(101),char(73),char(110),char(102),char(111),char(77),char(97),char(112),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83), -char(99),char(97),char(108),char(101),char(100),char(84),char(114),char(105),char(97),char(110),char(103),char(108),char(101),char(77),char(101),char(115),char(104),char(83),char(104),char(97), -char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(109),char(112),char(111),char(117),char(110),char(100),char(83),char(104),char(97), -char(112),char(101),char(67),char(104),char(105),char(108),char(100),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(109),char(112),char(111),char(117), -char(110),char(100),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(121),char(108),char(105),char(110),char(100), -char(101),char(114),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(97),char(112),char(115),char(117),char(108), +char(111),char(110),char(87),char(111),char(114),char(108),char(100),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(0),char(109),char(95),char(105), +char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105),char(111),char(110),char(76),char(105),char(110),char(101),char(97),char(114),char(86),char(101), +char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(105),char(110),char(116),char(101),char(114),char(112),char(111),char(108),char(97),char(116),char(105), +char(111),char(110),char(65),char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95), +char(97),char(110),char(105),char(115),char(111),char(116),char(114),char(111),char(112),char(105),char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0), +char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(99),char(116),char(80),char(114),char(111),char(99),char(101),char(115),char(115),char(105),char(110),char(103),char(84), +char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(100),char(101),char(97),char(99),char(116),char(105),char(118),char(97),char(116), +char(105),char(111),char(110),char(84),char(105),char(109),char(101),char(0),char(109),char(95),char(102),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109), +char(95),char(114),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(114), +char(101),char(115),char(116),char(105),char(116),char(117),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(104),char(105),char(116),char(70),char(114),char(97),char(99), +char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(99),char(100),char(83),char(119),char(101),char(112),char(116),char(83),char(112),char(104),char(101),char(114), +char(101),char(82),char(97),char(100),char(105),char(117),char(115),char(0),char(109),char(95),char(99),char(99),char(100),char(77),char(111),char(116),char(105),char(111),char(110),char(84), +char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(104),char(97),char(115),char(65),char(110),char(105),char(115),char(111),char(116), +char(114),char(111),char(112),char(105),char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(99),char(111),char(108),char(108), +char(105),char(115),char(105),char(111),char(110),char(70),char(108),char(97),char(103),char(115),char(0),char(109),char(95),char(105),char(115),char(108),char(97),char(110),char(100),char(84), +char(97),char(103),char(49),char(0),char(109),char(95),char(99),char(111),char(109),char(112),char(97),char(110),char(105),char(111),char(110),char(73),char(100),char(0),char(109),char(95), +char(97),char(99),char(116),char(105),char(118),char(97),char(116),char(105),char(111),char(110),char(83),char(116),char(97),char(116),char(101),char(49),char(0),char(109),char(95),char(105), +char(110),char(116),char(101),char(114),char(110),char(97),char(108),char(84),char(121),char(112),char(101),char(0),char(109),char(95),char(99),char(104),char(101),char(99),char(107),char(67), +char(111),char(108),char(108),char(105),char(100),char(101),char(87),char(105),char(116),char(104),char(0),char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114),char(73), +char(110),char(102),char(111),char(0),char(109),char(95),char(103),char(114),char(97),char(118),char(105),char(116),char(121),char(0),char(109),char(95),char(99),char(111),char(108),char(108), +char(105),char(115),char(105),char(111),char(110),char(79),char(98),char(106),char(101),char(99),char(116),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(105),char(110), +char(118),char(73),char(110),char(101),char(114),char(116),char(105),char(97),char(84),char(101),char(110),char(115),char(111),char(114),char(87),char(111),char(114),char(108),char(100),char(0), +char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97), +char(110),char(103),char(117),char(108),char(97),char(114),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(110),char(103), +char(117),char(108),char(97),char(114),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(70), +char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(103),char(114),char(97),char(118),char(105),char(116),char(121),char(95),char(97),char(99),char(99),char(101), +char(108),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(105),char(110),char(118),char(73),char(110),char(101),char(114),char(116),char(105), +char(97),char(76),char(111),char(99),char(97),char(108),char(0),char(109),char(95),char(116),char(111),char(116),char(97),char(108),char(70),char(111),char(114),char(99),char(101),char(0), +char(109),char(95),char(116),char(111),char(116),char(97),char(108),char(84),char(111),char(114),char(113),char(117),char(101),char(0),char(109),char(95),char(105),char(110),char(118),char(101), +char(114),char(115),char(101),char(77),char(97),char(115),char(115),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109),char(112), +char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103), +char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110),char(103), +char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(76), +char(105),char(110),char(101),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108), +char(100),char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(65),char(110),char(103), +char(117),char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100), +char(83),char(113),char(114),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110),char(97),char(108),char(65),char(110),char(103),char(117), +char(108),char(97),char(114),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(108), +char(105),char(110),char(101),char(97),char(114),char(83),char(108),char(101),char(101),char(112),char(105),char(110),char(103),char(84),char(104),char(114),char(101),char(115),char(104),char(111), +char(108),char(100),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(108),char(101),char(101),char(112),char(105),char(110),char(103), +char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(97),char(100),char(100),char(105),char(116),char(105),char(111),char(110), +char(97),char(108),char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(110),char(117),char(109),char(67),char(111),char(110),char(115),char(116), +char(114),char(97),char(105),char(110),char(116),char(82),char(111),char(119),char(115),char(0),char(110),char(117),char(98),char(0),char(42),char(109),char(95),char(114),char(98),char(65), +char(0),char(42),char(109),char(95),char(114),char(98),char(66),char(0),char(109),char(95),char(111),char(98),char(106),char(101),char(99),char(116),char(84),char(121),char(112),char(101), +char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(84),char(121),char(112), +char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(73),char(100), +char(0),char(109),char(95),char(110),char(101),char(101),char(100),char(115),char(70),char(101),char(101),char(100),char(98),char(97),char(99),char(107),char(0),char(109),char(95),char(97), +char(112),char(112),char(108),char(105),char(101),char(100),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(100),char(98),char(103),char(68), +char(114),char(97),char(119),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(100),char(105),char(115),char(97),char(98),char(108),char(101),char(67),char(111),char(108), +char(108),char(105),char(115),char(105),char(111),char(110),char(115),char(66),char(101),char(116),char(119),char(101),char(101),char(110),char(76),char(105),char(110),char(107),char(101),char(100), +char(66),char(111),char(100),char(105),char(101),char(115),char(0),char(109),char(95),char(111),char(118),char(101),char(114),char(114),char(105),char(100),char(101),char(78),char(117),char(109), +char(83),char(111),char(108),char(118),char(101),char(114),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(98), +char(114),char(101),char(97),char(107),char(105),char(110),char(103),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(84),char(104),char(114),char(101),char(115),char(104), +char(111),char(108),char(100),char(0),char(109),char(95),char(105),char(115),char(69),char(110),char(97),char(98),char(108),char(101),char(100),char(0),char(112),char(97),char(100),char(100), +char(105),char(110),char(103),char(91),char(52),char(93),char(0),char(109),char(95),char(116),char(121),char(112),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97), +char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(112),char(105),char(118),char(111),char(116),char(73),char(110),char(65),char(0),char(109), +char(95),char(112),char(105),char(118),char(111),char(116),char(73),char(110),char(66),char(0),char(109),char(95),char(114),char(98),char(65),char(70),char(114),char(97),char(109),char(101), +char(0),char(109),char(95),char(114),char(98),char(66),char(70),char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(117),char(115),char(101),char(82),char(101),char(102), +char(101),char(114),char(101),char(110),char(99),char(101),char(70),char(114),char(97),char(109),char(101),char(65),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108), +char(97),char(114),char(79),char(110),char(108),char(121),char(0),char(109),char(95),char(101),char(110),char(97),char(98),char(108),char(101),char(65),char(110),char(103),char(117),char(108), +char(97),char(114),char(77),char(111),char(116),char(111),char(114),char(0),char(109),char(95),char(109),char(111),char(116),char(111),char(114),char(84),char(97),char(114),char(103),char(101), +char(116),char(86),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(109),char(97),char(120),char(77),char(111),char(116),char(111),char(114), +char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(0),char(109),char(95),char(108),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105),char(116), +char(0),char(109),char(95),char(117),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105),char(116),char(0),char(109),char(95),char(108),char(105),char(109),char(105), +char(116),char(83),char(111),char(102),char(116),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(98),char(105),char(97),char(115),char(70),char(97),char(99),char(116), +char(111),char(114),char(0),char(109),char(95),char(114),char(101),char(108),char(97),char(120),char(97),char(116),char(105),char(111),char(110),char(70),char(97),char(99),char(116),char(111), +char(114),char(0),char(109),char(95),char(112),char(97),char(100),char(100),char(105),char(110),char(103),char(49),char(91),char(52),char(93),char(0),char(109),char(95),char(115),char(119), +char(105),char(110),char(103),char(83),char(112),char(97),char(110),char(49),char(0),char(109),char(95),char(115),char(119),char(105),char(110),char(103),char(83),char(112),char(97),char(110), +char(50),char(0),char(109),char(95),char(116),char(119),char(105),char(115),char(116),char(83),char(112),char(97),char(110),char(0),char(109),char(95),char(100),char(97),char(109),char(112), +char(105),char(110),char(103),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109), +char(105),char(116),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(76),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105), +char(116),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(85),char(112),char(112),char(101),char(114),char(76),char(105),char(109),char(105), +char(116),char(0),char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(76),char(111),char(119),char(101),char(114),char(76),char(105),char(109),char(105), +char(116),char(0),char(109),char(95),char(117),char(115),char(101),char(76),char(105),char(110),char(101),char(97),char(114),char(82),char(101),char(102),char(101),char(114),char(101),char(110), +char(99),char(101),char(70),char(114),char(97),char(109),char(101),char(65),char(0),char(109),char(95),char(117),char(115),char(101),char(79),char(102),char(102),char(115),char(101),char(116), +char(70),char(111),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(114),char(97),char(109),char(101),char(0),char(109), +char(95),char(54),char(100),char(111),char(102),char(68),char(97),char(116),char(97),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(69),char(110), +char(97),char(98),char(108),char(101),char(100),char(91),char(54),char(93),char(0),char(109),char(95),char(101),char(113),char(117),char(105),char(108),char(105),char(98),char(114),char(105), +char(117),char(109),char(80),char(111),char(105),char(110),char(116),char(91),char(54),char(93),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103),char(83), +char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(91),char(54),char(93),char(0),char(109),char(95),char(115),char(112),char(114),char(105),char(110),char(103), +char(68),char(97),char(109),char(112),char(105),char(110),char(103),char(91),char(54),char(93),char(0),char(109),char(95),char(97),char(120),char(105),char(115),char(73),char(110),char(65), +char(0),char(109),char(95),char(97),char(120),char(105),char(115),char(73),char(110),char(66),char(0),char(109),char(95),char(114),char(97),char(116),char(105),char(111),char(0),char(109), +char(95),char(116),char(97),char(117),char(0),char(109),char(95),char(116),char(105),char(109),char(101),char(83),char(116),char(101),char(112),char(0),char(109),char(95),char(109),char(97), +char(120),char(69),char(114),char(114),char(111),char(114),char(82),char(101),char(100),char(117),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(115),char(111), +char(114),char(0),char(109),char(95),char(101),char(114),char(112),char(0),char(109),char(95),char(101),char(114),char(112),char(50),char(0),char(109),char(95),char(103),char(108),char(111), +char(98),char(97),char(108),char(67),char(102),char(109),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115), +char(101),char(80),char(101),char(110),char(101),char(116),char(114),char(97),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108), +char(100),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(84),char(117),char(114),char(110), +char(69),char(114),char(112),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(108),char(111),char(112),char(0),char(109),char(95),char(119), +char(97),char(114),char(109),char(115),char(116),char(97),char(114),char(116),char(105),char(110),char(103),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95), +char(109),char(97),char(120),char(71),char(121),char(114),char(111),char(115),char(99),char(111),char(112),char(105),char(99),char(70),char(111),char(114),char(99),char(101),char(0),char(109), +char(95),char(115),char(105),char(110),char(103),char(108),char(101),char(65),char(120),char(105),char(115),char(82),char(111),char(108),char(108),char(105),char(110),char(103),char(70),char(114), +char(105),char(99),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108),char(100),char(0),char(109),char(95),char(110),char(117), +char(109),char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(115),char(111),char(108),char(118),char(101),char(114), +char(77),char(111),char(100),char(101),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(105),char(110),char(103),char(67),char(111),char(110),char(116),char(97),char(99), +char(116),char(82),char(101),char(115),char(116),char(105),char(116),char(117),char(116),char(105),char(111),char(110),char(84),char(104),char(114),char(101),char(115),char(104),char(111),char(108), +char(100),char(0),char(109),char(95),char(109),char(105),char(110),char(105),char(109),char(117),char(109),char(83),char(111),char(108),char(118),char(101),char(114),char(66),char(97),char(116), +char(99),char(104),char(83),char(105),char(122),char(101),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(73),char(109),char(112),char(117),char(108),char(115), +char(101),char(0),char(109),char(95),char(108),char(105),char(110),char(101),char(97),char(114),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0), +char(109),char(95),char(97),char(110),char(103),char(117),char(108),char(97),char(114),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(109), +char(95),char(118),char(111),char(108),char(117),char(109),char(101),char(83),char(116),char(105),char(102),char(102),char(110),char(101),char(115),char(115),char(0),char(42),char(109),char(95), +char(109),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(0),char(109),char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0), +char(109),char(95),char(112),char(114),char(101),char(118),char(105),char(111),char(117),char(115),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(0),char(109), +char(95),char(118),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(0),char(109),char(95),char(97),char(99),char(99),char(117),char(109),char(117),char(108),char(97), +char(116),char(101),char(100),char(70),char(111),char(114),char(99),char(101),char(0),char(109),char(95),char(110),char(111),char(114),char(109),char(97),char(108),char(0),char(109),char(95), +char(97),char(114),char(101),char(97),char(0),char(109),char(95),char(97),char(116),char(116),char(97),char(99),char(104),char(0),char(109),char(95),char(110),char(111),char(100),char(101), +char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(76),char(101),char(110), +char(103),char(116),char(104),char(0),char(109),char(95),char(98),char(98),char(101),char(110),char(100),char(105),char(110),char(103),char(0),char(109),char(95),char(110),char(111),char(100), +char(101),char(73),char(110),char(100),char(105),char(99),char(101),char(115),char(91),char(51),char(93),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(65),char(114), +char(101),char(97),char(0),char(109),char(95),char(99),char(48),char(91),char(52),char(93),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100), +char(105),char(99),char(101),char(115),char(91),char(52),char(93),char(0),char(109),char(95),char(114),char(101),char(115),char(116),char(86),char(111),char(108),char(117),char(109),char(101), +char(0),char(109),char(95),char(99),char(49),char(0),char(109),char(95),char(99),char(50),char(0),char(109),char(95),char(99),char(48),char(0),char(109),char(95),char(108),char(111), +char(99),char(97),char(108),char(70),char(114),char(97),char(109),char(101),char(0),char(42),char(109),char(95),char(114),char(105),char(103),char(105),char(100),char(66),char(111),char(100), +char(121),char(0),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(101),char(120),char(0),char(109),char(95),char(97),char(101),char(114),char(111), +char(77),char(111),char(100),char(101),char(108),char(0),char(109),char(95),char(98),char(97),char(117),char(109),char(103),char(97),char(114),char(116),char(101),char(0),char(109),char(95), +char(100),char(114),char(97),char(103),char(0),char(109),char(95),char(108),char(105),char(102),char(116),char(0),char(109),char(95),char(112),char(114),char(101),char(115),char(115),char(117), +char(114),char(101),char(0),char(109),char(95),char(118),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(100),char(121),char(110),char(97),char(109),char(105), +char(99),char(70),char(114),char(105),char(99),char(116),char(105),char(111),char(110),char(0),char(109),char(95),char(112),char(111),char(115),char(101),char(77),char(97),char(116),char(99), +char(104),char(0),char(109),char(95),char(114),char(105),char(103),char(105),char(100),char(67),char(111),char(110),char(116),char(97),char(99),char(116),char(72),char(97),char(114),char(100), +char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(107),char(105),char(110),char(101),char(116),char(105),char(99),char(67),char(111),char(110),char(116),char(97),char(99), +char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(67),char(111),char(110),char(116), +char(97),char(99),char(116),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(97),char(110),char(99),char(104),char(111),char(114), +char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100), +char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111), +char(102),char(116),char(75),char(105),char(110),char(101),char(116),char(105),char(99),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(72),char(97),char(114),char(100), +char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(83),char(111),char(102),char(116),char(67),char(108),char(117),char(115),char(116), +char(101),char(114),char(72),char(97),char(114),char(100),char(110),char(101),char(115),char(115),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(82),char(105),char(103), +char(105),char(100),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105), +char(116),char(0),char(109),char(95),char(115),char(111),char(102),char(116),char(75),char(105),char(110),char(101),char(116),char(105),char(99),char(67),char(108),char(117),char(115),char(116), +char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(115),char(111),char(102), +char(116),char(83),char(111),char(102),char(116),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(109),char(112),char(117),char(108),char(115),char(101),char(83), +char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(109),char(97),char(120),char(86),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(116), +char(105),char(109),char(101),char(83),char(99),char(97),char(108),char(101),char(0),char(109),char(95),char(118),char(101),char(108),char(111),char(99),char(105),char(116),char(121),char(73), +char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(112),char(111),char(115),char(105),char(116),char(105),char(111),char(110), +char(73),char(116),char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(100),char(114),char(105),char(102),char(116),char(73),char(116), +char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(116), +char(101),char(114),char(97),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(114),char(111),char(116),char(0),char(109),char(95),char(115),char(99),char(97), +char(108),char(101),char(0),char(109),char(95),char(97),char(113),char(113),char(0),char(109),char(95),char(99),char(111),char(109),char(0),char(42),char(109),char(95),char(112),char(111), +char(115),char(105),char(116),char(105),char(111),char(110),char(115),char(0),char(42),char(109),char(95),char(119),char(101),char(105),char(103),char(104),char(116),char(115),char(0),char(109), +char(95),char(110),char(117),char(109),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(87), +char(101),char(105),char(103),char(116),char(115),char(0),char(109),char(95),char(98),char(118),char(111),char(108),char(117),char(109),char(101),char(0),char(109),char(95),char(98),char(102), +char(114),char(97),char(109),char(101),char(0),char(109),char(95),char(102),char(114),char(97),char(109),char(101),char(120),char(102),char(111),char(114),char(109),char(0),char(109),char(95), +char(108),char(111),char(99),char(105),char(105),char(0),char(109),char(95),char(105),char(110),char(118),char(119),char(105),char(0),char(109),char(95),char(118),char(105),char(109),char(112), +char(117),char(108),char(115),char(101),char(115),char(91),char(50),char(93),char(0),char(109),char(95),char(100),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115), +char(91),char(50),char(93),char(0),char(109),char(95),char(108),char(118),char(0),char(109),char(95),char(97),char(118),char(0),char(42),char(109),char(95),char(102),char(114),char(97), +char(109),char(101),char(114),char(101),char(102),char(115),char(0),char(42),char(109),char(95),char(110),char(111),char(100),char(101),char(73),char(110),char(100),char(105),char(99),char(101), +char(115),char(0),char(42),char(109),char(95),char(109),char(97),char(115),char(115),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(70),char(114),char(97), +char(109),char(101),char(82),char(101),char(102),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(78),char(111),char(100),char(101),char(115),char(0),char(109),char(95), +char(110),char(117),char(109),char(77),char(97),char(115),char(115),char(101),char(115),char(0),char(109),char(95),char(105),char(100),char(109),char(97),char(115),char(115),char(0),char(109), +char(95),char(105),char(109),char(97),char(115),char(115),char(0),char(109),char(95),char(110),char(118),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(0), +char(109),char(95),char(110),char(100),char(105),char(109),char(112),char(117),char(108),char(115),char(101),char(115),char(0),char(109),char(95),char(110),char(100),char(97),char(109),char(112), +char(105),char(110),char(103),char(0),char(109),char(95),char(108),char(100),char(97),char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(97),char(100),char(97), +char(109),char(112),char(105),char(110),char(103),char(0),char(109),char(95),char(109),char(97),char(116),char(99),char(104),char(105),char(110),char(103),char(0),char(109),char(95),char(109), +char(97),char(120),char(83),char(101),char(108),char(102),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(73),char(109),char(112),char(117),char(108), +char(115),char(101),char(0),char(109),char(95),char(115),char(101),char(108),char(102),char(67),char(111),char(108),char(108),char(105),char(115),char(105),char(111),char(110),char(73),char(109), +char(112),char(117),char(108),char(115),char(101),char(70),char(97),char(99),char(116),char(111),char(114),char(0),char(109),char(95),char(99),char(111),char(110),char(116),char(97),char(105), +char(110),char(115),char(65),char(110),char(99),char(104),char(111),char(114),char(0),char(109),char(95),char(99),char(111),char(108),char(108),char(105),char(100),char(101),char(0),char(109), +char(95),char(99),char(108),char(117),char(115),char(116),char(101),char(114),char(73),char(110),char(100),char(101),char(120),char(0),char(42),char(109),char(95),char(98),char(111),char(100), +char(121),char(65),char(0),char(42),char(109),char(95),char(98),char(111),char(100),char(121),char(66),char(0),char(109),char(95),char(114),char(101),char(102),char(115),char(91),char(50), +char(93),char(0),char(109),char(95),char(99),char(102),char(109),char(0),char(109),char(95),char(115),char(112),char(108),char(105),char(116),char(0),char(109),char(95),char(100),char(101), +char(108),char(101),char(116),char(101),char(0),char(109),char(95),char(114),char(101),char(108),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(91),char(50), +char(93),char(0),char(109),char(95),char(98),char(111),char(100),char(121),char(65),char(116),char(121),char(112),char(101),char(0),char(109),char(95),char(98),char(111),char(100),char(121), +char(66),char(116),char(121),char(112),char(101),char(0),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(84),char(121),char(112),char(101),char(0),char(42),char(109), +char(95),char(112),char(111),char(115),char(101),char(0),char(42),char(42),char(109),char(95),char(109),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(115),char(0), +char(42),char(109),char(95),char(110),char(111),char(100),char(101),char(115),char(0),char(42),char(109),char(95),char(108),char(105),char(110),char(107),char(115),char(0),char(42),char(109), +char(95),char(102),char(97),char(99),char(101),char(115),char(0),char(42),char(109),char(95),char(116),char(101),char(116),char(114),char(97),char(104),char(101),char(100),char(114),char(97), +char(0),char(42),char(109),char(95),char(97),char(110),char(99),char(104),char(111),char(114),char(115),char(0),char(42),char(109),char(95),char(99),char(108),char(117),char(115),char(116), +char(101),char(114),char(115),char(0),char(42),char(109),char(95),char(106),char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(77), +char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(76),char(105),char(110),char(107),char(115),char(0), +char(109),char(95),char(110),char(117),char(109),char(70),char(97),char(99),char(101),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(84),char(101),char(116),char(114), +char(97),char(104),char(101),char(100),char(114),char(97),char(0),char(109),char(95),char(110),char(117),char(109),char(65),char(110),char(99),char(104),char(111),char(114),char(115),char(0), +char(109),char(95),char(110),char(117),char(109),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(115),char(0),char(109),char(95),char(110),char(117),char(109),char(74), +char(111),char(105),char(110),char(116),char(115),char(0),char(109),char(95),char(99),char(111),char(110),char(102),char(105),char(103),char(0),char(0),char(84),char(89),char(80),char(69), +char(87),char(0),char(0),char(0),char(99),char(104),char(97),char(114),char(0),char(117),char(99),char(104),char(97),char(114),char(0),char(115),char(104),char(111),char(114),char(116), +char(0),char(117),char(115),char(104),char(111),char(114),char(116),char(0),char(105),char(110),char(116),char(0),char(108),char(111),char(110),char(103),char(0),char(117),char(108),char(111), +char(110),char(103),char(0),char(102),char(108),char(111),char(97),char(116),char(0),char(100),char(111),char(117),char(98),char(108),char(101),char(0),char(118),char(111),char(105),char(100), +char(0),char(80),char(111),char(105),char(110),char(116),char(101),char(114),char(65),char(114),char(114),char(97),char(121),char(0),char(98),char(116),char(80),char(104),char(121),char(115), +char(105),char(99),char(115),char(83),char(121),char(115),char(116),char(101),char(109),char(0),char(76),char(105),char(115),char(116),char(66),char(97),char(115),char(101),char(0),char(98), +char(116),char(86),char(101),char(99),char(116),char(111),char(114),char(51),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(86),char(101),char(99),char(116),char(111),char(114),char(51),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(77),char(97),char(116),char(114),char(105),char(120),char(51),char(120),char(51),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(77),char(97),char(116),char(114),char(105),char(120),char(51),char(120),char(51),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97), +char(0),char(98),char(116),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(84),char(114),char(97),char(110),char(115),char(102),char(111),char(114),char(109),char(68),char(111),char(117),char(98),char(108),char(101),char(68), +char(97),char(116),char(97),char(0),char(98),char(116),char(66),char(118),char(104),char(83),char(117),char(98),char(116),char(114),char(101),char(101),char(73),char(110),char(102),char(111), +char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(79),char(112),char(116),char(105),char(109),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78), +char(111),char(100),char(101),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(79),char(112),char(116),char(105),char(109), +char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(78),char(111),char(100),char(101), +char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100),char(66),char(118),char(104),char(70), +char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(81),char(117),char(97),char(110),char(116),char(105),char(122),char(101),char(100), +char(66),char(118),char(104),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(108),char(108), +char(105),char(115),char(105),char(111),char(110),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(116),char(97), +char(116),char(105),char(99),char(80),char(108),char(97),char(110),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(67),char(111),char(110),char(118),char(101),char(120),char(73),char(110),char(116),char(101),char(114),char(110),char(97),char(108),char(83),char(104),char(97),char(112),char(101),char(68), +char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(115),char(105),char(116),char(105),char(111),char(110),char(65),char(110),char(100),char(82),char(97),char(100), +char(105),char(117),char(115),char(0),char(98),char(116),char(77),char(117),char(108),char(116),char(105),char(83),char(112),char(104),char(101),char(114),char(101),char(83),char(104),char(97), +char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(83),char(104),char(111),char(114),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(68),char(97),char(116), +char(97),char(0),char(98),char(116),char(83),char(104),char(111),char(114),char(116),char(73),char(110),char(116),char(73),char(110),char(100),char(101),char(120),char(84),char(114),char(105), +char(112),char(108),char(101),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(104),char(97),char(114),char(73),char(110),char(100),char(101),char(120), +char(84),char(114),char(105),char(112),char(108),char(101),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(77),char(101),char(115),char(104),char(80),char(97), +char(114),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(116),char(114),char(105),char(100),char(105),char(110),char(103),char(77),char(101),char(115), +char(104),char(73),char(110),char(116),char(101),char(114),char(102),char(97),char(99),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(105), +char(97),char(110),char(103),char(108),char(101),char(77),char(101),char(115),char(104),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(84),char(114),char(105),char(97),char(110),char(103),char(108),char(101),char(73),char(110),char(102),char(111),char(77),char(97),char(112),char(68),char(97),char(116),char(97), +char(0),char(98),char(116),char(83),char(99),char(97),char(108),char(101),char(100),char(84),char(114),char(105),char(97),char(110),char(103),char(108),char(101),char(77),char(101),char(115), +char(104),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(109),char(112),char(111),char(117),char(110), +char(100),char(83),char(104),char(97),char(112),char(101),char(67),char(104),char(105),char(108),char(100),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111), +char(109),char(112),char(111),char(117),char(110),char(100),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(121), +char(108),char(105),char(110),char(100),char(101),char(114),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111), +char(110),char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(97),char(112),char(115),char(117),char(108), char(101),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(114),char(105),char(97),char(110),char(103),char(108), char(101),char(73),char(110),char(102),char(111),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(73),char(109),char(112),char(97),char(99),char(116),char(77), char(101),char(115),char(104),char(83),char(104),char(97),char(112),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(118),char(101), @@ -759,150 +805,187 @@ char(83),char(111),char(108),char(118),char(101),char(114),char(73),char(110),ch char(98),char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97), char(0),char(98),char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97), char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(73),char(110),char(102),char(111),char(49), -char(0),char(98),char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97), -char(116),char(97),char(0),char(98),char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100),char(121),char(68),char(97),char(116),char(97),char(0),char(98), -char(116),char(80),char(111),char(105),char(110),char(116),char(50),char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105), -char(110),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50), -char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108), -char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97), -char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103), -char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97), -char(0),char(98),char(116),char(67),char(111),char(110),char(101),char(84),char(119),char(105),char(115),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105), -char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102), +char(0),char(98),char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108), +char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116), +char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(82),char(105),char(103),char(105),char(100),char(66),char(111),char(100), +char(121),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(84),char(121),char(112),char(101),char(100),char(67),char(111),char(110),char(115),char(116),char(114),char(97), +char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(105),char(110), +char(116),char(50),char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(70),char(108),char(111), +char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50),char(80),char(111),char(105),char(110),char(116), +char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97), +char(50),char(0),char(98),char(116),char(80),char(111),char(105),char(110),char(116),char(50),char(80),char(111),char(105),char(110),char(116),char(67),char(111),char(110),char(115),char(116), +char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105), +char(110),char(103),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68), +char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103),char(101),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110), +char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(72),char(105),char(110),char(103),char(101),char(67),char(111), +char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0), +char(98),char(116),char(67),char(111),char(110),char(101),char(84),char(119),char(105),char(115),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110), +char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(67),char(111),char(110),char(101),char(84),char(119), +char(105),char(115),char(116),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), +char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110), +char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(67), +char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50), +char(0),char(98),char(116),char(71),char(101),char(110),char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103), char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(110), char(101),char(114),char(105),char(99),char(54),char(68),char(111),char(102),char(83),char(112),char(114),char(105),char(110),char(103),char(67),char(111),char(110),char(115),char(116),char(114), -char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(108),char(105),char(100),char(101),char(114),char(67),char(111),char(110), -char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121), -char(77),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100), -char(121),char(78),char(111),char(100),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(76),char(105), -char(110),char(107),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(70),char(97),char(99),char(101),char(68), -char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(84),char(101),char(116),char(114),char(97),char(68),char(97),char(116), -char(97),char(0),char(83),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100),char(65),char(110),char(99),char(104),char(111),char(114),char(68),char(97),char(116), -char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(67),char(111),char(110),char(102),char(105),char(103),char(68),char(97),char(116),char(97), -char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(80),char(111),char(115),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111), -char(102),char(116),char(66),char(111),char(100),char(121),char(67),char(108),char(117),char(115),char(116),char(101),char(114),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(74),char(111),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116), -char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(0),char(0), -char(84),char(76),char(69),char(78),char(1),char(0),char(1),char(0),char(2),char(0),char(2),char(0),char(4),char(0),char(4),char(0),char(4),char(0),char(4),char(0), -char(8),char(0),char(0),char(0),char(16),char(0),char(48),char(0),char(16),char(0),char(16),char(0),char(32),char(0),char(48),char(0),char(96),char(0),char(64),char(0), -char(-128),char(0),char(20),char(0),char(48),char(0),char(80),char(0),char(16),char(0),char(96),char(0),char(-112),char(0),char(16),char(0),char(56),char(0),char(56),char(0), -char(20),char(0),char(72),char(0),char(4),char(0),char(4),char(0),char(8),char(0),char(4),char(0),char(56),char(0),char(32),char(0),char(80),char(0),char(72),char(0), -char(96),char(0),char(80),char(0),char(32),char(0),char(64),char(0),char(64),char(0),char(16),char(0),char(72),char(0),char(80),char(0),char(-32),char(1),char(16),char(1), -char(-72),char(0),char(-104),char(0),char(104),char(0),char(88),char(0),char(-8),char(1),char(-80),char(3),char(8),char(0),char(64),char(0),char(0),char(0),char(96),char(0), -char(-128),char(0),char(104),char(1),char(-24),char(0),char(-32),char(0),char(8),char(1),char(104),char(1),char(-40),char(0),char(16),char(0),char(104),char(0),char(24),char(0), -char(40),char(0),char(104),char(0),char(96),char(0),char(104),char(0),char(-56),char(0),char(104),char(1),char(112),char(0),char(-32),char(1),char(83),char(84),char(82),char(67), -char(65),char(0),char(0),char(0),char(10),char(0),char(3),char(0),char(4),char(0),char(0),char(0),char(4),char(0),char(1),char(0),char(9),char(0),char(2),char(0), -char(11),char(0),char(3),char(0),char(10),char(0),char(3),char(0),char(10),char(0),char(4),char(0),char(10),char(0),char(5),char(0),char(12),char(0),char(2),char(0), -char(9),char(0),char(6),char(0),char(9),char(0),char(7),char(0),char(13),char(0),char(1),char(0),char(7),char(0),char(8),char(0),char(14),char(0),char(1),char(0), -char(8),char(0),char(8),char(0),char(15),char(0),char(1),char(0),char(13),char(0),char(9),char(0),char(16),char(0),char(1),char(0),char(14),char(0),char(9),char(0), -char(17),char(0),char(2),char(0),char(15),char(0),char(10),char(0),char(13),char(0),char(11),char(0),char(18),char(0),char(2),char(0),char(16),char(0),char(10),char(0), -char(14),char(0),char(11),char(0),char(19),char(0),char(4),char(0),char(4),char(0),char(12),char(0),char(4),char(0),char(13),char(0),char(2),char(0),char(14),char(0), -char(2),char(0),char(15),char(0),char(20),char(0),char(6),char(0),char(13),char(0),char(16),char(0),char(13),char(0),char(17),char(0),char(4),char(0),char(18),char(0), -char(4),char(0),char(19),char(0),char(4),char(0),char(20),char(0),char(0),char(0),char(21),char(0),char(21),char(0),char(6),char(0),char(14),char(0),char(16),char(0), -char(14),char(0),char(17),char(0),char(4),char(0),char(18),char(0),char(4),char(0),char(19),char(0),char(4),char(0),char(20),char(0),char(0),char(0),char(21),char(0), -char(22),char(0),char(3),char(0),char(2),char(0),char(14),char(0),char(2),char(0),char(15),char(0),char(4),char(0),char(22),char(0),char(23),char(0),char(12),char(0), -char(13),char(0),char(23),char(0),char(13),char(0),char(24),char(0),char(13),char(0),char(25),char(0),char(4),char(0),char(26),char(0),char(4),char(0),char(27),char(0), -char(4),char(0),char(28),char(0),char(4),char(0),char(29),char(0),char(20),char(0),char(30),char(0),char(22),char(0),char(31),char(0),char(19),char(0),char(32),char(0), -char(4),char(0),char(33),char(0),char(4),char(0),char(34),char(0),char(24),char(0),char(12),char(0),char(14),char(0),char(23),char(0),char(14),char(0),char(24),char(0), -char(14),char(0),char(25),char(0),char(4),char(0),char(26),char(0),char(4),char(0),char(27),char(0),char(4),char(0),char(28),char(0),char(4),char(0),char(29),char(0), -char(21),char(0),char(30),char(0),char(22),char(0),char(31),char(0),char(4),char(0),char(33),char(0),char(4),char(0),char(34),char(0),char(19),char(0),char(32),char(0), -char(25),char(0),char(3),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(36),char(0),char(0),char(0),char(37),char(0),char(26),char(0),char(5),char(0), -char(25),char(0),char(38),char(0),char(13),char(0),char(39),char(0),char(13),char(0),char(40),char(0),char(7),char(0),char(41),char(0),char(0),char(0),char(21),char(0), -char(27),char(0),char(5),char(0),char(25),char(0),char(38),char(0),char(13),char(0),char(39),char(0),char(13),char(0),char(42),char(0),char(7),char(0),char(43),char(0), -char(4),char(0),char(44),char(0),char(28),char(0),char(2),char(0),char(13),char(0),char(45),char(0),char(7),char(0),char(46),char(0),char(29),char(0),char(4),char(0), -char(27),char(0),char(47),char(0),char(28),char(0),char(48),char(0),char(4),char(0),char(49),char(0),char(0),char(0),char(37),char(0),char(30),char(0),char(1),char(0), -char(4),char(0),char(50),char(0),char(31),char(0),char(2),char(0),char(2),char(0),char(50),char(0),char(0),char(0),char(51),char(0),char(32),char(0),char(2),char(0), -char(2),char(0),char(52),char(0),char(0),char(0),char(51),char(0),char(33),char(0),char(2),char(0),char(0),char(0),char(52),char(0),char(0),char(0),char(53),char(0), -char(34),char(0),char(8),char(0),char(13),char(0),char(54),char(0),char(14),char(0),char(55),char(0),char(30),char(0),char(56),char(0),char(32),char(0),char(57),char(0), -char(33),char(0),char(58),char(0),char(31),char(0),char(59),char(0),char(4),char(0),char(60),char(0),char(4),char(0),char(61),char(0),char(35),char(0),char(4),char(0), -char(34),char(0),char(62),char(0),char(13),char(0),char(63),char(0),char(4),char(0),char(64),char(0),char(0),char(0),char(37),char(0),char(36),char(0),char(7),char(0), -char(25),char(0),char(38),char(0),char(35),char(0),char(65),char(0),char(23),char(0),char(66),char(0),char(24),char(0),char(67),char(0),char(37),char(0),char(68),char(0), -char(7),char(0),char(43),char(0),char(0),char(0),char(69),char(0),char(38),char(0),char(2),char(0),char(36),char(0),char(70),char(0),char(13),char(0),char(39),char(0), -char(39),char(0),char(4),char(0),char(17),char(0),char(71),char(0),char(25),char(0),char(72),char(0),char(4),char(0),char(73),char(0),char(7),char(0),char(74),char(0), -char(40),char(0),char(4),char(0),char(25),char(0),char(38),char(0),char(39),char(0),char(75),char(0),char(4),char(0),char(76),char(0),char(7),char(0),char(43),char(0), -char(41),char(0),char(3),char(0),char(27),char(0),char(47),char(0),char(4),char(0),char(77),char(0),char(0),char(0),char(37),char(0),char(42),char(0),char(3),char(0), -char(27),char(0),char(47),char(0),char(4),char(0),char(77),char(0),char(0),char(0),char(37),char(0),char(43),char(0),char(4),char(0),char(4),char(0),char(78),char(0), -char(7),char(0),char(79),char(0),char(7),char(0),char(80),char(0),char(7),char(0),char(81),char(0),char(37),char(0),char(14),char(0),char(4),char(0),char(82),char(0), -char(4),char(0),char(83),char(0),char(43),char(0),char(84),char(0),char(4),char(0),char(85),char(0),char(7),char(0),char(86),char(0),char(7),char(0),char(87),char(0), -char(7),char(0),char(88),char(0),char(7),char(0),char(89),char(0),char(7),char(0),char(90),char(0),char(4),char(0),char(91),char(0),char(4),char(0),char(92),char(0), -char(4),char(0),char(93),char(0),char(4),char(0),char(94),char(0),char(0),char(0),char(37),char(0),char(44),char(0),char(5),char(0),char(25),char(0),char(38),char(0), -char(35),char(0),char(65),char(0),char(13),char(0),char(39),char(0),char(7),char(0),char(43),char(0),char(4),char(0),char(95),char(0),char(45),char(0),char(5),char(0), -char(27),char(0),char(47),char(0),char(13),char(0),char(96),char(0),char(14),char(0),char(97),char(0),char(4),char(0),char(98),char(0),char(0),char(0),char(99),char(0), -char(46),char(0),char(25),char(0),char(9),char(0),char(100),char(0),char(9),char(0),char(101),char(0),char(25),char(0),char(102),char(0),char(0),char(0),char(35),char(0), -char(18),char(0),char(103),char(0),char(18),char(0),char(104),char(0),char(14),char(0),char(105),char(0),char(14),char(0),char(106),char(0),char(14),char(0),char(107),char(0), -char(8),char(0),char(108),char(0),char(8),char(0),char(109),char(0),char(8),char(0),char(110),char(0),char(8),char(0),char(111),char(0),char(8),char(0),char(112),char(0), -char(8),char(0),char(113),char(0),char(8),char(0),char(114),char(0),char(8),char(0),char(115),char(0),char(4),char(0),char(116),char(0),char(4),char(0),char(117),char(0), -char(4),char(0),char(118),char(0),char(4),char(0),char(119),char(0),char(4),char(0),char(120),char(0),char(4),char(0),char(121),char(0),char(4),char(0),char(122),char(0), -char(0),char(0),char(37),char(0),char(47),char(0),char(25),char(0),char(9),char(0),char(100),char(0),char(9),char(0),char(101),char(0),char(25),char(0),char(102),char(0), -char(0),char(0),char(35),char(0),char(17),char(0),char(103),char(0),char(17),char(0),char(104),char(0),char(13),char(0),char(105),char(0),char(13),char(0),char(106),char(0), -char(13),char(0),char(107),char(0),char(7),char(0),char(108),char(0),char(7),char(0),char(109),char(0),char(7),char(0),char(110),char(0),char(7),char(0),char(111),char(0), -char(7),char(0),char(112),char(0),char(7),char(0),char(113),char(0),char(7),char(0),char(114),char(0),char(7),char(0),char(115),char(0),char(4),char(0),char(116),char(0), +char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(50),char(0),char(98),char(116),char(83),char(108), +char(105),char(100),char(101),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98), +char(116),char(83),char(108),char(105),char(100),char(101),char(114),char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117), +char(98),char(108),char(101),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(97),char(114),char(67),char(111),char(110),char(115),char(116),char(114), +char(97),char(105),char(110),char(116),char(70),char(108),char(111),char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(71),char(101),char(97),char(114), +char(67),char(111),char(110),char(115),char(116),char(114),char(97),char(105),char(110),char(116),char(68),char(111),char(117),char(98),char(108),char(101),char(68),char(97),char(116),char(97), +char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(77),char(97),char(116),char(101),char(114),char(105),char(97),char(108),char(68),char(97),char(116), +char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(78),char(111),char(100),char(101),char(68),char(97),char(116),char(97),char(0),char(83), +char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(76),char(105),char(110),char(107),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116), +char(66),char(111),char(100),char(121),char(70),char(97),char(99),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100), +char(121),char(84),char(101),char(116),char(114),char(97),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(82),char(105),char(103),char(105),char(100), +char(65),char(110),char(99),char(104),char(111),char(114),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(67), +char(111),char(110),char(102),char(105),char(103),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(80),char(111), +char(115),char(101),char(68),char(97),char(116),char(97),char(0),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(67),char(108),char(117),char(115),char(116), +char(101),char(114),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(74),char(111),char(105), +char(110),char(116),char(68),char(97),char(116),char(97),char(0),char(98),char(116),char(83),char(111),char(102),char(116),char(66),char(111),char(100),char(121),char(70),char(108),char(111), +char(97),char(116),char(68),char(97),char(116),char(97),char(0),char(0),char(84),char(76),char(69),char(78),char(1),char(0),char(1),char(0),char(2),char(0),char(2),char(0), +char(4),char(0),char(4),char(0),char(4),char(0),char(4),char(0),char(8),char(0),char(0),char(0),char(16),char(0),char(48),char(0),char(16),char(0),char(16),char(0), +char(32),char(0),char(48),char(0),char(96),char(0),char(64),char(0),char(-128),char(0),char(20),char(0),char(48),char(0),char(80),char(0),char(16),char(0),char(96),char(0), +char(-112),char(0),char(16),char(0),char(56),char(0),char(56),char(0),char(20),char(0),char(72),char(0),char(4),char(0),char(4),char(0),char(8),char(0),char(4),char(0), +char(56),char(0),char(32),char(0),char(80),char(0),char(72),char(0),char(96),char(0),char(80),char(0),char(32),char(0),char(64),char(0),char(64),char(0),char(64),char(0), +char(16),char(0),char(72),char(0),char(80),char(0),char(-32),char(1),char(16),char(1),char(-72),char(0),char(-104),char(0),char(104),char(0),char(88),char(0),char(-8),char(1), +char(-80),char(3),char(8),char(0),char(64),char(0),char(64),char(0),char(0),char(0),char(80),char(0),char(96),char(0),char(-112),char(0),char(-128),char(0),char(104),char(1), +char(-24),char(0),char(-104),char(1),char(-120),char(1),char(-32),char(0),char(8),char(1),char(-40),char(1),char(104),char(1),char(-128),char(2),char(-40),char(0),char(120),char(1), +char(104),char(0),char(-104),char(0),char(16),char(0),char(104),char(0),char(24),char(0),char(40),char(0),char(104),char(0),char(96),char(0),char(104),char(0),char(-56),char(0), +char(104),char(1),char(112),char(0),char(-32),char(1),char(0),char(0),char(83),char(84),char(82),char(67),char(76),char(0),char(0),char(0),char(10),char(0),char(3),char(0), +char(4),char(0),char(0),char(0),char(4),char(0),char(1),char(0),char(9),char(0),char(2),char(0),char(11),char(0),char(3),char(0),char(10),char(0),char(3),char(0), +char(10),char(0),char(4),char(0),char(10),char(0),char(5),char(0),char(12),char(0),char(2),char(0),char(9),char(0),char(6),char(0),char(9),char(0),char(7),char(0), +char(13),char(0),char(1),char(0),char(7),char(0),char(8),char(0),char(14),char(0),char(1),char(0),char(8),char(0),char(8),char(0),char(15),char(0),char(1),char(0), +char(13),char(0),char(9),char(0),char(16),char(0),char(1),char(0),char(14),char(0),char(9),char(0),char(17),char(0),char(2),char(0),char(15),char(0),char(10),char(0), +char(13),char(0),char(11),char(0),char(18),char(0),char(2),char(0),char(16),char(0),char(10),char(0),char(14),char(0),char(11),char(0),char(19),char(0),char(4),char(0), +char(4),char(0),char(12),char(0),char(4),char(0),char(13),char(0),char(2),char(0),char(14),char(0),char(2),char(0),char(15),char(0),char(20),char(0),char(6),char(0), +char(13),char(0),char(16),char(0),char(13),char(0),char(17),char(0),char(4),char(0),char(18),char(0),char(4),char(0),char(19),char(0),char(4),char(0),char(20),char(0), +char(0),char(0),char(21),char(0),char(21),char(0),char(6),char(0),char(14),char(0),char(16),char(0),char(14),char(0),char(17),char(0),char(4),char(0),char(18),char(0), +char(4),char(0),char(19),char(0),char(4),char(0),char(20),char(0),char(0),char(0),char(21),char(0),char(22),char(0),char(3),char(0),char(2),char(0),char(14),char(0), +char(2),char(0),char(15),char(0),char(4),char(0),char(22),char(0),char(23),char(0),char(12),char(0),char(13),char(0),char(23),char(0),char(13),char(0),char(24),char(0), +char(13),char(0),char(25),char(0),char(4),char(0),char(26),char(0),char(4),char(0),char(27),char(0),char(4),char(0),char(28),char(0),char(4),char(0),char(29),char(0), +char(20),char(0),char(30),char(0),char(22),char(0),char(31),char(0),char(19),char(0),char(32),char(0),char(4),char(0),char(33),char(0),char(4),char(0),char(34),char(0), +char(24),char(0),char(12),char(0),char(14),char(0),char(23),char(0),char(14),char(0),char(24),char(0),char(14),char(0),char(25),char(0),char(4),char(0),char(26),char(0), +char(4),char(0),char(27),char(0),char(4),char(0),char(28),char(0),char(4),char(0),char(29),char(0),char(21),char(0),char(30),char(0),char(22),char(0),char(31),char(0), +char(4),char(0),char(33),char(0),char(4),char(0),char(34),char(0),char(19),char(0),char(32),char(0),char(25),char(0),char(3),char(0),char(0),char(0),char(35),char(0), +char(4),char(0),char(36),char(0),char(0),char(0),char(37),char(0),char(26),char(0),char(5),char(0),char(25),char(0),char(38),char(0),char(13),char(0),char(39),char(0), +char(13),char(0),char(40),char(0),char(7),char(0),char(41),char(0),char(0),char(0),char(21),char(0),char(27),char(0),char(5),char(0),char(25),char(0),char(38),char(0), +char(13),char(0),char(39),char(0),char(13),char(0),char(42),char(0),char(7),char(0),char(43),char(0),char(4),char(0),char(44),char(0),char(28),char(0),char(2),char(0), +char(13),char(0),char(45),char(0),char(7),char(0),char(46),char(0),char(29),char(0),char(4),char(0),char(27),char(0),char(47),char(0),char(28),char(0),char(48),char(0), +char(4),char(0),char(49),char(0),char(0),char(0),char(37),char(0),char(30),char(0),char(1),char(0),char(4),char(0),char(50),char(0),char(31),char(0),char(2),char(0), +char(2),char(0),char(50),char(0),char(0),char(0),char(51),char(0),char(32),char(0),char(2),char(0),char(2),char(0),char(52),char(0),char(0),char(0),char(51),char(0), +char(33),char(0),char(2),char(0),char(0),char(0),char(52),char(0),char(0),char(0),char(53),char(0),char(34),char(0),char(8),char(0),char(13),char(0),char(54),char(0), +char(14),char(0),char(55),char(0),char(30),char(0),char(56),char(0),char(32),char(0),char(57),char(0),char(33),char(0),char(58),char(0),char(31),char(0),char(59),char(0), +char(4),char(0),char(60),char(0),char(4),char(0),char(61),char(0),char(35),char(0),char(4),char(0),char(34),char(0),char(62),char(0),char(13),char(0),char(63),char(0), +char(4),char(0),char(64),char(0),char(0),char(0),char(37),char(0),char(36),char(0),char(7),char(0),char(25),char(0),char(38),char(0),char(35),char(0),char(65),char(0), +char(23),char(0),char(66),char(0),char(24),char(0),char(67),char(0),char(37),char(0),char(68),char(0),char(7),char(0),char(43),char(0),char(0),char(0),char(69),char(0), +char(38),char(0),char(2),char(0),char(36),char(0),char(70),char(0),char(13),char(0),char(39),char(0),char(39),char(0),char(4),char(0),char(17),char(0),char(71),char(0), +char(25),char(0),char(72),char(0),char(4),char(0),char(73),char(0),char(7),char(0),char(74),char(0),char(40),char(0),char(4),char(0),char(25),char(0),char(38),char(0), +char(39),char(0),char(75),char(0),char(4),char(0),char(76),char(0),char(7),char(0),char(43),char(0),char(41),char(0),char(3),char(0),char(27),char(0),char(47),char(0), +char(4),char(0),char(77),char(0),char(0),char(0),char(37),char(0),char(42),char(0),char(3),char(0),char(27),char(0),char(47),char(0),char(4),char(0),char(78),char(0), +char(0),char(0),char(37),char(0),char(43),char(0),char(3),char(0),char(27),char(0),char(47),char(0),char(4),char(0),char(77),char(0),char(0),char(0),char(37),char(0), +char(44),char(0),char(4),char(0),char(4),char(0),char(79),char(0),char(7),char(0),char(80),char(0),char(7),char(0),char(81),char(0),char(7),char(0),char(82),char(0), +char(37),char(0),char(14),char(0),char(4),char(0),char(83),char(0),char(4),char(0),char(84),char(0),char(44),char(0),char(85),char(0),char(4),char(0),char(86),char(0), +char(7),char(0),char(87),char(0),char(7),char(0),char(88),char(0),char(7),char(0),char(89),char(0),char(7),char(0),char(90),char(0),char(7),char(0),char(91),char(0), +char(4),char(0),char(92),char(0),char(4),char(0),char(93),char(0),char(4),char(0),char(94),char(0),char(4),char(0),char(95),char(0),char(0),char(0),char(37),char(0), +char(45),char(0),char(5),char(0),char(25),char(0),char(38),char(0),char(35),char(0),char(65),char(0),char(13),char(0),char(39),char(0),char(7),char(0),char(43),char(0), +char(4),char(0),char(96),char(0),char(46),char(0),char(5),char(0),char(27),char(0),char(47),char(0),char(13),char(0),char(97),char(0),char(14),char(0),char(98),char(0), +char(4),char(0),char(99),char(0),char(0),char(0),char(100),char(0),char(47),char(0),char(25),char(0),char(9),char(0),char(101),char(0),char(9),char(0),char(102),char(0), +char(25),char(0),char(103),char(0),char(0),char(0),char(35),char(0),char(18),char(0),char(104),char(0),char(18),char(0),char(105),char(0),char(14),char(0),char(106),char(0), +char(14),char(0),char(107),char(0),char(14),char(0),char(108),char(0),char(8),char(0),char(109),char(0),char(8),char(0),char(110),char(0),char(8),char(0),char(111),char(0), +char(8),char(0),char(112),char(0),char(8),char(0),char(113),char(0),char(8),char(0),char(114),char(0),char(8),char(0),char(115),char(0),char(8),char(0),char(116),char(0), char(4),char(0),char(117),char(0),char(4),char(0),char(118),char(0),char(4),char(0),char(119),char(0),char(4),char(0),char(120),char(0),char(4),char(0),char(121),char(0), -char(4),char(0),char(122),char(0),char(0),char(0),char(37),char(0),char(48),char(0),char(2),char(0),char(49),char(0),char(123),char(0),char(14),char(0),char(124),char(0), -char(50),char(0),char(2),char(0),char(51),char(0),char(123),char(0),char(13),char(0),char(124),char(0),char(52),char(0),char(21),char(0),char(47),char(0),char(125),char(0), -char(15),char(0),char(126),char(0),char(13),char(0),char(127),char(0),char(13),char(0),char(-128),char(0),char(13),char(0),char(-127),char(0),char(13),char(0),char(-126),char(0), -char(13),char(0),char(124),char(0),char(13),char(0),char(-125),char(0),char(13),char(0),char(-124),char(0),char(13),char(0),char(-123),char(0),char(13),char(0),char(-122),char(0), -char(7),char(0),char(-121),char(0),char(7),char(0),char(-120),char(0),char(7),char(0),char(-119),char(0),char(7),char(0),char(-118),char(0),char(7),char(0),char(-117),char(0), -char(7),char(0),char(-116),char(0),char(7),char(0),char(-115),char(0),char(7),char(0),char(-114),char(0),char(7),char(0),char(-113),char(0),char(4),char(0),char(-112),char(0), -char(53),char(0),char(22),char(0),char(46),char(0),char(125),char(0),char(16),char(0),char(126),char(0),char(14),char(0),char(127),char(0),char(14),char(0),char(-128),char(0), -char(14),char(0),char(-127),char(0),char(14),char(0),char(-126),char(0),char(14),char(0),char(124),char(0),char(14),char(0),char(-125),char(0),char(14),char(0),char(-124),char(0), -char(14),char(0),char(-123),char(0),char(14),char(0),char(-122),char(0),char(8),char(0),char(-121),char(0),char(8),char(0),char(-120),char(0),char(8),char(0),char(-119),char(0), -char(8),char(0),char(-118),char(0),char(8),char(0),char(-117),char(0),char(8),char(0),char(-116),char(0),char(8),char(0),char(-115),char(0),char(8),char(0),char(-114),char(0), -char(8),char(0),char(-113),char(0),char(4),char(0),char(-112),char(0),char(0),char(0),char(37),char(0),char(54),char(0),char(2),char(0),char(4),char(0),char(-111),char(0), -char(4),char(0),char(-110),char(0),char(55),char(0),char(13),char(0),char(56),char(0),char(-109),char(0),char(56),char(0),char(-108),char(0),char(0),char(0),char(35),char(0), -char(4),char(0),char(-107),char(0),char(4),char(0),char(-106),char(0),char(4),char(0),char(-105),char(0),char(4),char(0),char(-104),char(0),char(7),char(0),char(-103),char(0), -char(7),char(0),char(-102),char(0),char(4),char(0),char(-101),char(0),char(4),char(0),char(-100),char(0),char(7),char(0),char(-99),char(0),char(4),char(0),char(-98),char(0), -char(57),char(0),char(3),char(0),char(55),char(0),char(-97),char(0),char(13),char(0),char(-96),char(0),char(13),char(0),char(-95),char(0),char(58),char(0),char(3),char(0), -char(55),char(0),char(-97),char(0),char(14),char(0),char(-96),char(0),char(14),char(0),char(-95),char(0),char(59),char(0),char(13),char(0),char(55),char(0),char(-97),char(0), -char(18),char(0),char(-94),char(0),char(18),char(0),char(-93),char(0),char(4),char(0),char(-92),char(0),char(4),char(0),char(-91),char(0),char(4),char(0),char(-90),char(0), -char(7),char(0),char(-89),char(0),char(7),char(0),char(-88),char(0),char(7),char(0),char(-87),char(0),char(7),char(0),char(-86),char(0),char(7),char(0),char(-85),char(0), -char(7),char(0),char(-84),char(0),char(7),char(0),char(-83),char(0),char(60),char(0),char(13),char(0),char(55),char(0),char(-97),char(0),char(17),char(0),char(-94),char(0), -char(17),char(0),char(-93),char(0),char(4),char(0),char(-92),char(0),char(4),char(0),char(-91),char(0),char(4),char(0),char(-90),char(0),char(7),char(0),char(-89),char(0), -char(7),char(0),char(-88),char(0),char(7),char(0),char(-87),char(0),char(7),char(0),char(-86),char(0),char(7),char(0),char(-85),char(0),char(7),char(0),char(-84),char(0), -char(7),char(0),char(-83),char(0),char(61),char(0),char(11),char(0),char(55),char(0),char(-97),char(0),char(17),char(0),char(-94),char(0),char(17),char(0),char(-93),char(0), -char(7),char(0),char(-82),char(0),char(7),char(0),char(-81),char(0),char(7),char(0),char(-80),char(0),char(7),char(0),char(-85),char(0),char(7),char(0),char(-84),char(0), -char(7),char(0),char(-83),char(0),char(7),char(0),char(-79),char(0),char(0),char(0),char(21),char(0),char(62),char(0),char(9),char(0),char(55),char(0),char(-97),char(0), -char(17),char(0),char(-94),char(0),char(17),char(0),char(-93),char(0),char(13),char(0),char(-78),char(0),char(13),char(0),char(-77),char(0),char(13),char(0),char(-76),char(0), -char(13),char(0),char(-75),char(0),char(4),char(0),char(-74),char(0),char(4),char(0),char(-73),char(0),char(63),char(0),char(5),char(0),char(62),char(0),char(-72),char(0), -char(4),char(0),char(-71),char(0),char(7),char(0),char(-70),char(0),char(7),char(0),char(-69),char(0),char(7),char(0),char(-68),char(0),char(64),char(0),char(9),char(0), -char(55),char(0),char(-97),char(0),char(17),char(0),char(-94),char(0),char(17),char(0),char(-93),char(0),char(7),char(0),char(-78),char(0),char(7),char(0),char(-77),char(0), -char(7),char(0),char(-76),char(0),char(7),char(0),char(-75),char(0),char(4),char(0),char(-74),char(0),char(4),char(0),char(-73),char(0),char(49),char(0),char(22),char(0), -char(8),char(0),char(-67),char(0),char(8),char(0),char(-79),char(0),char(8),char(0),char(110),char(0),char(8),char(0),char(-66),char(0),char(8),char(0),char(112),char(0), -char(8),char(0),char(-65),char(0),char(8),char(0),char(-64),char(0),char(8),char(0),char(-63),char(0),char(8),char(0),char(-62),char(0),char(8),char(0),char(-61),char(0), -char(8),char(0),char(-60),char(0),char(8),char(0),char(-59),char(0),char(8),char(0),char(-58),char(0),char(8),char(0),char(-57),char(0),char(8),char(0),char(-56),char(0), -char(8),char(0),char(-55),char(0),char(4),char(0),char(-54),char(0),char(4),char(0),char(-53),char(0),char(4),char(0),char(-52),char(0),char(4),char(0),char(-51),char(0), -char(4),char(0),char(-50),char(0),char(0),char(0),char(37),char(0),char(51),char(0),char(22),char(0),char(7),char(0),char(-67),char(0),char(7),char(0),char(-79),char(0), -char(7),char(0),char(110),char(0),char(7),char(0),char(-66),char(0),char(7),char(0),char(112),char(0),char(7),char(0),char(-65),char(0),char(7),char(0),char(-64),char(0), -char(7),char(0),char(-63),char(0),char(7),char(0),char(-62),char(0),char(7),char(0),char(-61),char(0),char(7),char(0),char(-60),char(0),char(7),char(0),char(-59),char(0), -char(7),char(0),char(-58),char(0),char(7),char(0),char(-57),char(0),char(7),char(0),char(-56),char(0),char(7),char(0),char(-55),char(0),char(4),char(0),char(-54),char(0), -char(4),char(0),char(-53),char(0),char(4),char(0),char(-52),char(0),char(4),char(0),char(-51),char(0),char(4),char(0),char(-50),char(0),char(0),char(0),char(37),char(0), -char(65),char(0),char(4),char(0),char(7),char(0),char(-49),char(0),char(7),char(0),char(-48),char(0),char(7),char(0),char(-47),char(0),char(4),char(0),char(78),char(0), -char(66),char(0),char(10),char(0),char(65),char(0),char(-46),char(0),char(13),char(0),char(-45),char(0),char(13),char(0),char(-44),char(0),char(13),char(0),char(-43),char(0), -char(13),char(0),char(-42),char(0),char(13),char(0),char(-41),char(0),char(7),char(0),char(-121),char(0),char(7),char(0),char(-40),char(0),char(4),char(0),char(-39),char(0), -char(4),char(0),char(53),char(0),char(67),char(0),char(4),char(0),char(65),char(0),char(-46),char(0),char(4),char(0),char(-38),char(0),char(7),char(0),char(-37),char(0), -char(4),char(0),char(-36),char(0),char(68),char(0),char(4),char(0),char(13),char(0),char(-41),char(0),char(65),char(0),char(-46),char(0),char(4),char(0),char(-35),char(0), -char(7),char(0),char(-34),char(0),char(69),char(0),char(7),char(0),char(13),char(0),char(-33),char(0),char(65),char(0),char(-46),char(0),char(4),char(0),char(-32),char(0), -char(7),char(0),char(-31),char(0),char(7),char(0),char(-30),char(0),char(7),char(0),char(-29),char(0),char(4),char(0),char(53),char(0),char(70),char(0),char(6),char(0), -char(15),char(0),char(-28),char(0),char(13),char(0),char(-30),char(0),char(13),char(0),char(-27),char(0),char(56),char(0),char(-26),char(0),char(4),char(0),char(-25),char(0), -char(7),char(0),char(-29),char(0),char(71),char(0),char(26),char(0),char(4),char(0),char(-24),char(0),char(7),char(0),char(-23),char(0),char(7),char(0),char(-79),char(0), -char(7),char(0),char(-22),char(0),char(7),char(0),char(-21),char(0),char(7),char(0),char(-20),char(0),char(7),char(0),char(-19),char(0),char(7),char(0),char(-18),char(0), -char(7),char(0),char(-17),char(0),char(7),char(0),char(-16),char(0),char(7),char(0),char(-15),char(0),char(7),char(0),char(-14),char(0),char(7),char(0),char(-13),char(0), -char(7),char(0),char(-12),char(0),char(7),char(0),char(-11),char(0),char(7),char(0),char(-10),char(0),char(7),char(0),char(-9),char(0),char(7),char(0),char(-8),char(0), -char(7),char(0),char(-7),char(0),char(7),char(0),char(-6),char(0),char(7),char(0),char(-5),char(0),char(4),char(0),char(-4),char(0),char(4),char(0),char(-3),char(0), -char(4),char(0),char(-2),char(0),char(4),char(0),char(-1),char(0),char(4),char(0),char(117),char(0),char(72),char(0),char(12),char(0),char(15),char(0),char(0),char(1), -char(15),char(0),char(1),char(1),char(15),char(0),char(2),char(1),char(13),char(0),char(3),char(1),char(13),char(0),char(4),char(1),char(7),char(0),char(5),char(1), -char(4),char(0),char(6),char(1),char(4),char(0),char(7),char(1),char(4),char(0),char(8),char(1),char(4),char(0),char(9),char(1),char(7),char(0),char(-31),char(0), -char(4),char(0),char(53),char(0),char(73),char(0),char(27),char(0),char(17),char(0),char(10),char(1),char(15),char(0),char(11),char(1),char(15),char(0),char(12),char(1), -char(13),char(0),char(3),char(1),char(13),char(0),char(13),char(1),char(13),char(0),char(14),char(1),char(13),char(0),char(15),char(1),char(13),char(0),char(16),char(1), -char(13),char(0),char(17),char(1),char(4),char(0),char(18),char(1),char(7),char(0),char(19),char(1),char(4),char(0),char(20),char(1),char(4),char(0),char(21),char(1), -char(4),char(0),char(22),char(1),char(7),char(0),char(23),char(1),char(7),char(0),char(24),char(1),char(4),char(0),char(25),char(1),char(4),char(0),char(26),char(1), -char(7),char(0),char(27),char(1),char(7),char(0),char(28),char(1),char(7),char(0),char(29),char(1),char(7),char(0),char(30),char(1),char(7),char(0),char(31),char(1), -char(7),char(0),char(32),char(1),char(4),char(0),char(33),char(1),char(4),char(0),char(34),char(1),char(4),char(0),char(35),char(1),char(74),char(0),char(12),char(0), -char(9),char(0),char(36),char(1),char(9),char(0),char(37),char(1),char(13),char(0),char(38),char(1),char(7),char(0),char(39),char(1),char(7),char(0),char(-63),char(0), -char(7),char(0),char(40),char(1),char(4),char(0),char(41),char(1),char(13),char(0),char(42),char(1),char(4),char(0),char(43),char(1),char(4),char(0),char(44),char(1), -char(4),char(0),char(45),char(1),char(4),char(0),char(53),char(0),char(75),char(0),char(19),char(0),char(47),char(0),char(125),char(0),char(72),char(0),char(46),char(1), -char(65),char(0),char(47),char(1),char(66),char(0),char(48),char(1),char(67),char(0),char(49),char(1),char(68),char(0),char(50),char(1),char(69),char(0),char(51),char(1), -char(70),char(0),char(52),char(1),char(73),char(0),char(53),char(1),char(74),char(0),char(54),char(1),char(4),char(0),char(55),char(1),char(4),char(0),char(21),char(1), -char(4),char(0),char(56),char(1),char(4),char(0),char(57),char(1),char(4),char(0),char(58),char(1),char(4),char(0),char(59),char(1),char(4),char(0),char(60),char(1), -char(4),char(0),char(61),char(1),char(71),char(0),char(62),char(1),}; +char(4),char(0),char(122),char(0),char(4),char(0),char(123),char(0),char(0),char(0),char(37),char(0),char(48),char(0),char(25),char(0),char(9),char(0),char(101),char(0), +char(9),char(0),char(102),char(0),char(25),char(0),char(103),char(0),char(0),char(0),char(35),char(0),char(17),char(0),char(104),char(0),char(17),char(0),char(105),char(0), +char(13),char(0),char(106),char(0),char(13),char(0),char(107),char(0),char(13),char(0),char(108),char(0),char(7),char(0),char(109),char(0),char(7),char(0),char(110),char(0), +char(7),char(0),char(111),char(0),char(7),char(0),char(112),char(0),char(7),char(0),char(113),char(0),char(7),char(0),char(114),char(0),char(7),char(0),char(115),char(0), +char(7),char(0),char(116),char(0),char(4),char(0),char(117),char(0),char(4),char(0),char(118),char(0),char(4),char(0),char(119),char(0),char(4),char(0),char(120),char(0), +char(4),char(0),char(121),char(0),char(4),char(0),char(122),char(0),char(4),char(0),char(123),char(0),char(0),char(0),char(37),char(0),char(49),char(0),char(2),char(0), +char(50),char(0),char(124),char(0),char(14),char(0),char(125),char(0),char(51),char(0),char(2),char(0),char(52),char(0),char(124),char(0),char(13),char(0),char(125),char(0), +char(53),char(0),char(21),char(0),char(48),char(0),char(126),char(0),char(15),char(0),char(127),char(0),char(13),char(0),char(-128),char(0),char(13),char(0),char(-127),char(0), +char(13),char(0),char(-126),char(0),char(13),char(0),char(-125),char(0),char(13),char(0),char(125),char(0),char(13),char(0),char(-124),char(0),char(13),char(0),char(-123),char(0), +char(13),char(0),char(-122),char(0),char(13),char(0),char(-121),char(0),char(7),char(0),char(-120),char(0),char(7),char(0),char(-119),char(0),char(7),char(0),char(-118),char(0), +char(7),char(0),char(-117),char(0),char(7),char(0),char(-116),char(0),char(7),char(0),char(-115),char(0),char(7),char(0),char(-114),char(0),char(7),char(0),char(-113),char(0), +char(7),char(0),char(-112),char(0),char(4),char(0),char(-111),char(0),char(54),char(0),char(22),char(0),char(47),char(0),char(126),char(0),char(16),char(0),char(127),char(0), +char(14),char(0),char(-128),char(0),char(14),char(0),char(-127),char(0),char(14),char(0),char(-126),char(0),char(14),char(0),char(-125),char(0),char(14),char(0),char(125),char(0), +char(14),char(0),char(-124),char(0),char(14),char(0),char(-123),char(0),char(14),char(0),char(-122),char(0),char(14),char(0),char(-121),char(0),char(8),char(0),char(-120),char(0), +char(8),char(0),char(-119),char(0),char(8),char(0),char(-118),char(0),char(8),char(0),char(-117),char(0),char(8),char(0),char(-116),char(0),char(8),char(0),char(-115),char(0), +char(8),char(0),char(-114),char(0),char(8),char(0),char(-113),char(0),char(8),char(0),char(-112),char(0),char(4),char(0),char(-111),char(0),char(0),char(0),char(37),char(0), +char(55),char(0),char(2),char(0),char(4),char(0),char(-110),char(0),char(4),char(0),char(-109),char(0),char(56),char(0),char(13),char(0),char(53),char(0),char(-108),char(0), +char(53),char(0),char(-107),char(0),char(0),char(0),char(35),char(0),char(4),char(0),char(-106),char(0),char(4),char(0),char(-105),char(0),char(4),char(0),char(-104),char(0), +char(4),char(0),char(-103),char(0),char(7),char(0),char(-102),char(0),char(7),char(0),char(-101),char(0),char(4),char(0),char(-100),char(0),char(4),char(0),char(-99),char(0), +char(7),char(0),char(-98),char(0),char(4),char(0),char(-97),char(0),char(57),char(0),char(13),char(0),char(58),char(0),char(-108),char(0),char(58),char(0),char(-107),char(0), +char(0),char(0),char(35),char(0),char(4),char(0),char(-106),char(0),char(4),char(0),char(-105),char(0),char(4),char(0),char(-104),char(0),char(4),char(0),char(-103),char(0), +char(7),char(0),char(-102),char(0),char(7),char(0),char(-101),char(0),char(4),char(0),char(-100),char(0),char(4),char(0),char(-99),char(0),char(7),char(0),char(-98),char(0), +char(4),char(0),char(-97),char(0),char(59),char(0),char(14),char(0),char(54),char(0),char(-108),char(0),char(54),char(0),char(-107),char(0),char(0),char(0),char(35),char(0), +char(4),char(0),char(-106),char(0),char(4),char(0),char(-105),char(0),char(4),char(0),char(-104),char(0),char(4),char(0),char(-103),char(0),char(8),char(0),char(-102),char(0), +char(8),char(0),char(-101),char(0),char(4),char(0),char(-100),char(0),char(4),char(0),char(-99),char(0),char(8),char(0),char(-98),char(0),char(4),char(0),char(-97),char(0), +char(0),char(0),char(-96),char(0),char(60),char(0),char(3),char(0),char(57),char(0),char(-95),char(0),char(13),char(0),char(-94),char(0),char(13),char(0),char(-93),char(0), +char(61),char(0),char(3),char(0),char(59),char(0),char(-95),char(0),char(14),char(0),char(-94),char(0),char(14),char(0),char(-93),char(0),char(62),char(0),char(3),char(0), +char(57),char(0),char(-95),char(0),char(14),char(0),char(-94),char(0),char(14),char(0),char(-93),char(0),char(63),char(0),char(13),char(0),char(57),char(0),char(-95),char(0), +char(18),char(0),char(-92),char(0),char(18),char(0),char(-91),char(0),char(4),char(0),char(-90),char(0),char(4),char(0),char(-89),char(0),char(4),char(0),char(-88),char(0), +char(7),char(0),char(-87),char(0),char(7),char(0),char(-86),char(0),char(7),char(0),char(-85),char(0),char(7),char(0),char(-84),char(0),char(7),char(0),char(-83),char(0), +char(7),char(0),char(-82),char(0),char(7),char(0),char(-81),char(0),char(64),char(0),char(13),char(0),char(57),char(0),char(-95),char(0),char(17),char(0),char(-92),char(0), +char(17),char(0),char(-91),char(0),char(4),char(0),char(-90),char(0),char(4),char(0),char(-89),char(0),char(4),char(0),char(-88),char(0),char(7),char(0),char(-87),char(0), +char(7),char(0),char(-86),char(0),char(7),char(0),char(-85),char(0),char(7),char(0),char(-84),char(0),char(7),char(0),char(-83),char(0),char(7),char(0),char(-82),char(0), +char(7),char(0),char(-81),char(0),char(65),char(0),char(14),char(0),char(59),char(0),char(-95),char(0),char(18),char(0),char(-92),char(0),char(18),char(0),char(-91),char(0), +char(4),char(0),char(-90),char(0),char(4),char(0),char(-89),char(0),char(4),char(0),char(-88),char(0),char(8),char(0),char(-87),char(0),char(8),char(0),char(-86),char(0), +char(8),char(0),char(-85),char(0),char(8),char(0),char(-84),char(0),char(8),char(0),char(-83),char(0),char(8),char(0),char(-82),char(0),char(8),char(0),char(-81),char(0), +char(0),char(0),char(-80),char(0),char(66),char(0),char(10),char(0),char(59),char(0),char(-95),char(0),char(18),char(0),char(-92),char(0),char(18),char(0),char(-91),char(0), +char(8),char(0),char(-79),char(0),char(8),char(0),char(-78),char(0),char(8),char(0),char(-77),char(0),char(8),char(0),char(-83),char(0),char(8),char(0),char(-82),char(0), +char(8),char(0),char(-81),char(0),char(8),char(0),char(-76),char(0),char(67),char(0),char(11),char(0),char(57),char(0),char(-95),char(0),char(17),char(0),char(-92),char(0), +char(17),char(0),char(-91),char(0),char(7),char(0),char(-79),char(0),char(7),char(0),char(-78),char(0),char(7),char(0),char(-77),char(0),char(7),char(0),char(-83),char(0), +char(7),char(0),char(-82),char(0),char(7),char(0),char(-81),char(0),char(7),char(0),char(-76),char(0),char(0),char(0),char(21),char(0),char(68),char(0),char(9),char(0), +char(57),char(0),char(-95),char(0),char(17),char(0),char(-92),char(0),char(17),char(0),char(-91),char(0),char(13),char(0),char(-75),char(0),char(13),char(0),char(-74),char(0), +char(13),char(0),char(-73),char(0),char(13),char(0),char(-72),char(0),char(4),char(0),char(-71),char(0),char(4),char(0),char(-70),char(0),char(69),char(0),char(9),char(0), +char(59),char(0),char(-95),char(0),char(18),char(0),char(-92),char(0),char(18),char(0),char(-91),char(0),char(14),char(0),char(-75),char(0),char(14),char(0),char(-74),char(0), +char(14),char(0),char(-73),char(0),char(14),char(0),char(-72),char(0),char(4),char(0),char(-71),char(0),char(4),char(0),char(-70),char(0),char(70),char(0),char(5),char(0), +char(68),char(0),char(-69),char(0),char(4),char(0),char(-68),char(0),char(7),char(0),char(-67),char(0),char(7),char(0),char(-66),char(0),char(7),char(0),char(-65),char(0), +char(71),char(0),char(5),char(0),char(69),char(0),char(-69),char(0),char(4),char(0),char(-68),char(0),char(8),char(0),char(-67),char(0),char(8),char(0),char(-66),char(0), +char(8),char(0),char(-65),char(0),char(72),char(0),char(9),char(0),char(57),char(0),char(-95),char(0),char(17),char(0),char(-92),char(0),char(17),char(0),char(-91),char(0), +char(7),char(0),char(-75),char(0),char(7),char(0),char(-74),char(0),char(7),char(0),char(-73),char(0),char(7),char(0),char(-72),char(0),char(4),char(0),char(-71),char(0), +char(4),char(0),char(-70),char(0),char(73),char(0),char(9),char(0),char(59),char(0),char(-95),char(0),char(18),char(0),char(-92),char(0),char(18),char(0),char(-91),char(0), +char(8),char(0),char(-75),char(0),char(8),char(0),char(-74),char(0),char(8),char(0),char(-73),char(0),char(8),char(0),char(-72),char(0),char(4),char(0),char(-71),char(0), +char(4),char(0),char(-70),char(0),char(74),char(0),char(5),char(0),char(56),char(0),char(-95),char(0),char(13),char(0),char(-64),char(0),char(13),char(0),char(-63),char(0), +char(7),char(0),char(-62),char(0),char(0),char(0),char(37),char(0),char(75),char(0),char(4),char(0),char(59),char(0),char(-95),char(0),char(14),char(0),char(-64),char(0), +char(14),char(0),char(-63),char(0),char(8),char(0),char(-62),char(0),char(50),char(0),char(22),char(0),char(8),char(0),char(-61),char(0),char(8),char(0),char(-76),char(0), +char(8),char(0),char(111),char(0),char(8),char(0),char(-60),char(0),char(8),char(0),char(113),char(0),char(8),char(0),char(-59),char(0),char(8),char(0),char(-58),char(0), +char(8),char(0),char(-57),char(0),char(8),char(0),char(-56),char(0),char(8),char(0),char(-55),char(0),char(8),char(0),char(-54),char(0),char(8),char(0),char(-53),char(0), +char(8),char(0),char(-52),char(0),char(8),char(0),char(-51),char(0),char(8),char(0),char(-50),char(0),char(8),char(0),char(-49),char(0),char(4),char(0),char(-48),char(0), +char(4),char(0),char(-47),char(0),char(4),char(0),char(-46),char(0),char(4),char(0),char(-45),char(0),char(4),char(0),char(-44),char(0),char(0),char(0),char(37),char(0), +char(52),char(0),char(22),char(0),char(7),char(0),char(-61),char(0),char(7),char(0),char(-76),char(0),char(7),char(0),char(111),char(0),char(7),char(0),char(-60),char(0), +char(7),char(0),char(113),char(0),char(7),char(0),char(-59),char(0),char(7),char(0),char(-58),char(0),char(7),char(0),char(-57),char(0),char(7),char(0),char(-56),char(0), +char(7),char(0),char(-55),char(0),char(7),char(0),char(-54),char(0),char(7),char(0),char(-53),char(0),char(7),char(0),char(-52),char(0),char(7),char(0),char(-51),char(0), +char(7),char(0),char(-50),char(0),char(7),char(0),char(-49),char(0),char(4),char(0),char(-48),char(0),char(4),char(0),char(-47),char(0),char(4),char(0),char(-46),char(0), +char(4),char(0),char(-45),char(0),char(4),char(0),char(-44),char(0),char(0),char(0),char(37),char(0),char(76),char(0),char(4),char(0),char(7),char(0),char(-43),char(0), +char(7),char(0),char(-42),char(0),char(7),char(0),char(-41),char(0),char(4),char(0),char(79),char(0),char(77),char(0),char(10),char(0),char(76),char(0),char(-40),char(0), +char(13),char(0),char(-39),char(0),char(13),char(0),char(-38),char(0),char(13),char(0),char(-37),char(0),char(13),char(0),char(-36),char(0),char(13),char(0),char(-35),char(0), +char(7),char(0),char(-120),char(0),char(7),char(0),char(-34),char(0),char(4),char(0),char(-33),char(0),char(4),char(0),char(53),char(0),char(78),char(0),char(4),char(0), +char(76),char(0),char(-40),char(0),char(4),char(0),char(-32),char(0),char(7),char(0),char(-31),char(0),char(4),char(0),char(-30),char(0),char(79),char(0),char(4),char(0), +char(13),char(0),char(-35),char(0),char(76),char(0),char(-40),char(0),char(4),char(0),char(-29),char(0),char(7),char(0),char(-28),char(0),char(80),char(0),char(7),char(0), +char(13),char(0),char(-27),char(0),char(76),char(0),char(-40),char(0),char(4),char(0),char(-26),char(0),char(7),char(0),char(-25),char(0),char(7),char(0),char(-24),char(0), +char(7),char(0),char(-23),char(0),char(4),char(0),char(53),char(0),char(81),char(0),char(6),char(0),char(15),char(0),char(-22),char(0),char(13),char(0),char(-24),char(0), +char(13),char(0),char(-21),char(0),char(58),char(0),char(-20),char(0),char(4),char(0),char(-19),char(0),char(7),char(0),char(-23),char(0),char(82),char(0),char(26),char(0), +char(4),char(0),char(-18),char(0),char(7),char(0),char(-17),char(0),char(7),char(0),char(-76),char(0),char(7),char(0),char(-16),char(0),char(7),char(0),char(-15),char(0), +char(7),char(0),char(-14),char(0),char(7),char(0),char(-13),char(0),char(7),char(0),char(-12),char(0),char(7),char(0),char(-11),char(0),char(7),char(0),char(-10),char(0), +char(7),char(0),char(-9),char(0),char(7),char(0),char(-8),char(0),char(7),char(0),char(-7),char(0),char(7),char(0),char(-6),char(0),char(7),char(0),char(-5),char(0), +char(7),char(0),char(-4),char(0),char(7),char(0),char(-3),char(0),char(7),char(0),char(-2),char(0),char(7),char(0),char(-1),char(0),char(7),char(0),char(0),char(1), +char(7),char(0),char(1),char(1),char(4),char(0),char(2),char(1),char(4),char(0),char(3),char(1),char(4),char(0),char(4),char(1),char(4),char(0),char(5),char(1), +char(4),char(0),char(118),char(0),char(83),char(0),char(12),char(0),char(15),char(0),char(6),char(1),char(15),char(0),char(7),char(1),char(15),char(0),char(8),char(1), +char(13),char(0),char(9),char(1),char(13),char(0),char(10),char(1),char(7),char(0),char(11),char(1),char(4),char(0),char(12),char(1),char(4),char(0),char(13),char(1), +char(4),char(0),char(14),char(1),char(4),char(0),char(15),char(1),char(7),char(0),char(-25),char(0),char(4),char(0),char(53),char(0),char(84),char(0),char(27),char(0), +char(17),char(0),char(16),char(1),char(15),char(0),char(17),char(1),char(15),char(0),char(18),char(1),char(13),char(0),char(9),char(1),char(13),char(0),char(19),char(1), +char(13),char(0),char(20),char(1),char(13),char(0),char(21),char(1),char(13),char(0),char(22),char(1),char(13),char(0),char(23),char(1),char(4),char(0),char(24),char(1), +char(7),char(0),char(25),char(1),char(4),char(0),char(26),char(1),char(4),char(0),char(27),char(1),char(4),char(0),char(28),char(1),char(7),char(0),char(29),char(1), +char(7),char(0),char(30),char(1),char(4),char(0),char(31),char(1),char(4),char(0),char(32),char(1),char(7),char(0),char(33),char(1),char(7),char(0),char(34),char(1), +char(7),char(0),char(35),char(1),char(7),char(0),char(36),char(1),char(7),char(0),char(37),char(1),char(7),char(0),char(38),char(1),char(4),char(0),char(39),char(1), +char(4),char(0),char(40),char(1),char(4),char(0),char(41),char(1),char(85),char(0),char(12),char(0),char(9),char(0),char(42),char(1),char(9),char(0),char(43),char(1), +char(13),char(0),char(44),char(1),char(7),char(0),char(45),char(1),char(7),char(0),char(-57),char(0),char(7),char(0),char(46),char(1),char(4),char(0),char(47),char(1), +char(13),char(0),char(48),char(1),char(4),char(0),char(49),char(1),char(4),char(0),char(50),char(1),char(4),char(0),char(51),char(1),char(4),char(0),char(53),char(0), +char(86),char(0),char(19),char(0),char(48),char(0),char(126),char(0),char(83),char(0),char(52),char(1),char(76),char(0),char(53),char(1),char(77),char(0),char(54),char(1), +char(78),char(0),char(55),char(1),char(79),char(0),char(56),char(1),char(80),char(0),char(57),char(1),char(81),char(0),char(58),char(1),char(84),char(0),char(59),char(1), +char(85),char(0),char(60),char(1),char(4),char(0),char(61),char(1),char(4),char(0),char(27),char(1),char(4),char(0),char(62),char(1),char(4),char(0),char(63),char(1), +char(4),char(0),char(64),char(1),char(4),char(0),char(65),char(1),char(4),char(0),char(66),char(1),char(4),char(0),char(67),char(1),char(82),char(0),char(68),char(1), +}; int sBulletDNAlen64= sizeof(sBulletDNAstr64); diff --git a/Engine/lib/bullet/src/LinearMath/btSerializer.h b/Engine/lib/bullet/src/LinearMath/btSerializer.h index c5bc96b78..ff1dc574c 100644 --- a/Engine/lib/bullet/src/LinearMath/btSerializer.h +++ b/Engine/lib/bullet/src/LinearMath/btSerializer.h @@ -17,7 +17,6 @@ subject to the following restrictions: #define BT_SERIALIZER_H #include "btScalar.h" // has definitions like SIMD_FORCE_INLINE -#include "btStackAlloc.h" #include "btHashMap.h" #if !defined( __CELLOS_LV2__) && !defined(__MWERKS__) @@ -439,7 +438,7 @@ public: buffer[9] = '2'; buffer[10] = '8'; - buffer[11] = '1'; + buffer[11] = '2'; } diff --git a/Engine/lib/bullet/src/LinearMath/btVector3.cpp b/Engine/lib/bullet/src/LinearMath/btVector3.cpp index 24bd521a9..9389a25ca 100644 --- a/Engine/lib/bullet/src/LinearMath/btVector3.cpp +++ b/Engine/lib/bullet/src/LinearMath/btVector3.cpp @@ -19,9 +19,17 @@ #define BT_USE_SSE_IN_API #endif + #include "btVector3.h" -#if defined (BT_USE_SSE) || defined (BT_USE_NEON) + + +#if defined BT_USE_SIMD_VECTOR3 + +#if DEBUG +#include //for memset +#endif + #ifdef __APPLE__ #include @@ -43,7 +51,7 @@ long _maxdot_large( const float *vv, const float *vec, unsigned long count, floa long _maxdot_large( const float *vv, const float *vec, unsigned long count, float *dotResult ) { const float4 *vertices = (const float4*) vv; - static const unsigned char indexTable[16] = {-1, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; + static const unsigned char indexTable[16] = {(unsigned char)-1, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; float4 dotMax = btAssign128( -BT_INFINITY, -BT_INFINITY, -BT_INFINITY, -BT_INFINITY ); float4 vvec = _mm_loadu_ps( vec ); float4 vHi = btCastiTo128f(_mm_shuffle_epi32( btCastfTo128i( vvec), 0xaa )); /// zzzz @@ -428,7 +436,7 @@ long _mindot_large( const float *vv, const float *vec, unsigned long count, floa long _mindot_large( const float *vv, const float *vec, unsigned long count, float *dotResult ) { const float4 *vertices = (const float4*) vv; - static const unsigned char indexTable[16] = {-1, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; + static const unsigned char indexTable[16] = {(unsigned char)-1, 0, 1, 0, 2, 0, 1, 0, 3, 0, 1, 0, 2, 0, 1, 0 }; float4 dotmin = btAssign128( BT_INFINITY, BT_INFINITY, BT_INFINITY, BT_INFINITY ); float4 vvec = _mm_loadu_ps( vec ); float4 vHi = btCastiTo128f(_mm_shuffle_epi32( btCastfTo128i( vvec), 0xaa )); /// zzzz @@ -815,7 +823,8 @@ long _mindot_large( const float *vv, const float *vec, unsigned long count, floa #elif defined BT_USE_NEON #define ARM_NEON_GCC_COMPATIBILITY 1 #include - +#include +#include //for sysctlbyname static long _maxdot_large_v0( const float *vv, const float *vec, unsigned long count, float *dotResult ); static long _maxdot_large_v1( const float *vv, const float *vec, unsigned long count, float *dotResult ); @@ -827,11 +836,34 @@ static long _mindot_large_sel( const float *vv, const float *vec, unsigned long long (*_maxdot_large)( const float *vv, const float *vec, unsigned long count, float *dotResult ) = _maxdot_large_sel; long (*_mindot_large)( const float *vv, const float *vec, unsigned long count, float *dotResult ) = _mindot_large_sel; -extern "C" {int _get_cpu_capabilities( void );} + +static inline uint32_t btGetCpuCapabilities( void ) +{ + static uint32_t capabilities = 0; + static bool testedCapabilities = false; + + if( 0 == testedCapabilities) + { + uint32_t hasFeature = 0; + size_t featureSize = sizeof( hasFeature ); + int err = sysctlbyname( "hw.optional.neon_hpfp", &hasFeature, &featureSize, NULL, 0 ); + + if( 0 == err && hasFeature) + capabilities |= 0x2000; + + testedCapabilities = true; + } + + return capabilities; +} + + + static long _maxdot_large_sel( const float *vv, const float *vec, unsigned long count, float *dotResult ) { - if( _get_cpu_capabilities() & 0x2000 ) + + if( btGetCpuCapabilities() & 0x2000 ) _maxdot_large = _maxdot_large_v1; else _maxdot_large = _maxdot_large_v0; @@ -841,7 +873,8 @@ static long _maxdot_large_sel( const float *vv, const float *vec, unsigned long static long _mindot_large_sel( const float *vv, const float *vec, unsigned long count, float *dotResult ) { - if( _get_cpu_capabilities() & 0x2000 ) + + if( btGetCpuCapabilities() & 0x2000 ) _mindot_large = _mindot_large_v1; else _mindot_large = _mindot_large_v0; @@ -864,8 +897,8 @@ long _maxdot_large_v0( const float *vv, const float *vec, unsigned long count, f float32x2_t dotMaxHi = (float32x2_t) { -BT_INFINITY, -BT_INFINITY }; uint32x2_t indexLo = (uint32x2_t) {0, 1}; uint32x2_t indexHi = (uint32x2_t) {2, 3}; - uint32x2_t iLo = (uint32x2_t) {-1, -1}; - uint32x2_t iHi = (uint32x2_t) {-1, -1}; + uint32x2_t iLo = (uint32x2_t) {static_cast(-1), static_cast(-1)}; + uint32x2_t iHi = (uint32x2_t) {static_cast(-1), static_cast(-1)}; const uint32x2_t four = (uint32x2_t) {4,4}; for( ; i+8 <= count; i+= 8 ) @@ -1051,7 +1084,7 @@ long _maxdot_large_v1( const float *vv, const float *vec, unsigned long count, f float32x4_t vHi = vdupq_lane_f32(vget_high_f32(vvec), 0); const uint32x4_t four = (uint32x4_t){ 4, 4, 4, 4 }; uint32x4_t local_index = (uint32x4_t) {0, 1, 2, 3}; - uint32x4_t index = (uint32x4_t) { -1, -1, -1, -1 }; + uint32x4_t index = (uint32x4_t) { static_cast(-1), static_cast(-1), static_cast(-1), static_cast(-1) }; float32x4_t maxDot = (float32x4_t) { -BT_INFINITY, -BT_INFINITY, -BT_INFINITY, -BT_INFINITY }; unsigned long i = 0; @@ -1249,8 +1282,8 @@ long _mindot_large_v0( const float *vv, const float *vec, unsigned long count, f float32x2_t dotMinHi = (float32x2_t) { BT_INFINITY, BT_INFINITY }; uint32x2_t indexLo = (uint32x2_t) {0, 1}; uint32x2_t indexHi = (uint32x2_t) {2, 3}; - uint32x2_t iLo = (uint32x2_t) {-1, -1}; - uint32x2_t iHi = (uint32x2_t) {-1, -1}; + uint32x2_t iLo = (uint32x2_t) {static_cast(-1), static_cast(-1)}; + uint32x2_t iHi = (uint32x2_t) {static_cast(-1), static_cast(-1)}; const uint32x2_t four = (uint32x2_t) {4,4}; for( ; i+8 <= count; i+= 8 ) @@ -1434,7 +1467,7 @@ long _mindot_large_v1( const float *vv, const float *vec, unsigned long count, f float32x4_t vHi = vdupq_lane_f32(vget_high_f32(vvec), 0); const uint32x4_t four = (uint32x4_t){ 4, 4, 4, 4 }; uint32x4_t local_index = (uint32x4_t) {0, 1, 2, 3}; - uint32x4_t index = (uint32x4_t) { -1, -1, -1, -1 }; + uint32x4_t index = (uint32x4_t) { static_cast(-1), static_cast(-1), static_cast(-1), static_cast(-1) }; float32x4_t minDot = (float32x4_t) { BT_INFINITY, BT_INFINITY, BT_INFINITY, BT_INFINITY }; unsigned long i = 0; diff --git a/Engine/lib/bullet/src/LinearMath/btVector3.h b/Engine/lib/bullet/src/LinearMath/btVector3.h index 5001dfa9f..896859292 100644 --- a/Engine/lib/bullet/src/LinearMath/btVector3.h +++ b/Engine/lib/bullet/src/LinearMath/btVector3.h @@ -53,19 +53,24 @@ subject to the following restrictions: #define btvxyzMaskf btvFFF0fMask #define btvAbsfMask btCastiTo128f(btvAbsMask) +//there is an issue with XCode 3.2 (LCx errors) +#define btvMzeroMask (_mm_set_ps(-0.0f, -0.0f, -0.0f, -0.0f)) +#define v1110 (_mm_set_ps(0.0f, 1.0f, 1.0f, 1.0f)) +#define vHalf (_mm_set_ps(0.5f, 0.5f, 0.5f, 0.5f)) +#define v1_5 (_mm_set_ps(1.5f, 1.5f, 1.5f, 1.5f)) - -const __m128 ATTRIBUTE_ALIGNED16(btvMzeroMask) = {-0.0f, -0.0f, -0.0f, -0.0f}; -const __m128 ATTRIBUTE_ALIGNED16(v1110) = {1.0f, 1.0f, 1.0f, 0.0f}; -const __m128 ATTRIBUTE_ALIGNED16(vHalf) = {0.5f, 0.5f, 0.5f, 0.5f}; -const __m128 ATTRIBUTE_ALIGNED16(v1_5) = {1.5f, 1.5f, 1.5f, 1.5f}; +//const __m128 ATTRIBUTE_ALIGNED16(btvMzeroMask) = {-0.0f, -0.0f, -0.0f, -0.0f}; +//const __m128 ATTRIBUTE_ALIGNED16(v1110) = {1.0f, 1.0f, 1.0f, 0.0f}; +//const __m128 ATTRIBUTE_ALIGNED16(vHalf) = {0.5f, 0.5f, 0.5f, 0.5f}; +//const __m128 ATTRIBUTE_ALIGNED16(v1_5) = {1.5f, 1.5f, 1.5f, 1.5f}; #endif #ifdef BT_USE_NEON const float32x4_t ATTRIBUTE_ALIGNED16(btvMzeroMask) = (float32x4_t){-0.0f, -0.0f, -0.0f, -0.0f}; -const int32x4_t ATTRIBUTE_ALIGNED16(btvFFF0Mask) = (int32x4_t){0xFFFFFFFF, 0xFFFFFFFF, 0xFFFFFFFF, 0x0}; +const int32x4_t ATTRIBUTE_ALIGNED16(btvFFF0Mask) = (int32x4_t){static_cast(0xFFFFFFFF), + static_cast(0xFFFFFFFF), static_cast(0xFFFFFFFF), 0x0}; const int32x4_t ATTRIBUTE_ALIGNED16(btvAbsMask) = (int32x4_t){0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF}; const int32x4_t ATTRIBUTE_ALIGNED16(btv3AbsMask) = (int32x4_t){0x7FFFFFFF, 0x7FFFFFFF, 0x7FFFFFFF, 0x0}; @@ -229,7 +234,7 @@ public: * @param v The other vector in the dot product */ SIMD_FORCE_INLINE btScalar dot(const btVector3& v) const { -#if defined(BT_USE_SSE_IN_API) && defined (BT_USE_SSE) +#if defined BT_USE_SIMD_VECTOR3 && defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) __m128 vd = _mm_mul_ps(mVec128, v.mVec128); __m128 z = _mm_movehl_ps(vd, vd); __m128 y = _mm_shuffle_ps(vd, vd, 0x55); @@ -260,6 +265,12 @@ public: return btSqrt(length2()); } + /**@brief Return the norm (length) of the vector */ + SIMD_FORCE_INLINE btScalar norm() const + { + return length(); + } + /**@brief Return the distance squared between the ends of this and another vector * This is symantically treating the vector like a point */ SIMD_FORCE_INLINE btScalar distance2(const btVector3& v) const; @@ -285,6 +296,9 @@ public: * x^2 + y^2 + z^2 = 1 */ SIMD_FORCE_INLINE btVector3& normalize() { + + btAssert(length() != btScalar(0)); + #if defined(BT_USE_SSE_IN_API) && defined (BT_USE_SSE) // dot product first __m128 vd = _mm_mul_ps(mVec128, mVec128); @@ -345,7 +359,8 @@ public: /**@brief Return a vector will the absolute values of each element */ SIMD_FORCE_INLINE btVector3 absolute() const { -#if defined(BT_USE_SSE_IN_API) && defined (BT_USE_SSE) + +#if defined BT_USE_SIMD_VECTOR3 && defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) return btVector3(_mm_and_ps(mVec128, btv3AbsfMask)); #elif defined(BT_USE_NEON) return btVector3(vabsq_f32(mVec128)); @@ -400,7 +415,7 @@ public: SIMD_FORCE_INLINE btScalar triple(const btVector3& v1, const btVector3& v2) const { -#if defined(BT_USE_SSE_IN_API) && defined (BT_USE_SSE) +#if defined BT_USE_SIMD_VECTOR3 && defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) // cross: __m128 T = _mm_shuffle_ps(v1.mVec128, v1.mVec128, BT_SHUFFLE(1, 2, 0, 3)); // (Y Z X 0) __m128 V = _mm_shuffle_ps(v2.mVec128, v2.mVec128, BT_SHUFFLE(1, 2, 0, 3)); // (Y Z X 0) @@ -632,7 +647,7 @@ public: void getSkewSymmetricMatrix(btVector3* v0,btVector3* v1,btVector3* v2) const { -#if defined(BT_USE_SSE_IN_API) && defined (BT_USE_SSE) +#if defined BT_USE_SIMD_VECTOR3 && defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) __m128 V = _mm_and_ps(mVec128, btvFFF0fMask); __m128 V0 = _mm_xor_ps(btvMzeroMask, V); @@ -702,7 +717,7 @@ public: /* create a vector as btVector3( this->dot( btVector3 v0 ), this->dot( btVector3 v1), this->dot( btVector3 v2 )) */ SIMD_FORCE_INLINE btVector3 dot3( const btVector3 &v0, const btVector3 &v1, const btVector3 &v2 ) const { -#if defined(BT_USE_SSE_IN_API) && defined (BT_USE_SSE) +#if defined BT_USE_SIMD_VECTOR3 && defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) __m128 a0 = _mm_mul_ps( v0.mVec128, this->mVec128 ); __m128 a1 = _mm_mul_ps( v1.mVec128, this->mVec128 ); @@ -717,7 +732,7 @@ public: return btVector3(r); #elif defined(BT_USE_NEON) - static const uint32x4_t xyzMask = (const uint32x4_t){ -1, -1, -1, 0 }; + static const uint32x4_t xyzMask = (const uint32x4_t){ static_cast(-1), static_cast(-1), static_cast(-1), 0 }; float32x4_t a0 = vmulq_f32( v0.mVec128, this->mVec128); float32x4_t a1 = vmulq_f32( v1.mVec128, this->mVec128); float32x4_t a2 = vmulq_f32( v2.mVec128, this->mVec128); @@ -768,7 +783,7 @@ operator*(const btVector3& v1, const btVector3& v2) SIMD_FORCE_INLINE btVector3 operator-(const btVector3& v1, const btVector3& v2) { -#if (defined(BT_USE_SSE_IN_API) && defined(BT_USE_SSE)) +#if defined BT_USE_SIMD_VECTOR3 && (defined(BT_USE_SSE_IN_API) && defined(BT_USE_SSE)) // without _mm_and_ps this code causes slowdown in Concave moving __m128 r = _mm_sub_ps(v1.mVec128, v2.mVec128); @@ -788,7 +803,7 @@ operator-(const btVector3& v1, const btVector3& v2) SIMD_FORCE_INLINE btVector3 operator-(const btVector3& v) { -#if (defined(BT_USE_SSE_IN_API) && defined (BT_USE_SSE)) +#if defined BT_USE_SIMD_VECTOR3 && (defined(BT_USE_SSE_IN_API) && defined (BT_USE_SSE)) __m128 r = _mm_xor_ps(v.mVec128, btvMzeroMask); return btVector3(_mm_and_ps(r, btvFFF0fMask)); #elif defined(BT_USE_NEON) @@ -842,7 +857,7 @@ operator/(const btVector3& v, const btScalar& s) SIMD_FORCE_INLINE btVector3 operator/(const btVector3& v1, const btVector3& v2) { -#if (defined(BT_USE_SSE_IN_API)&& defined (BT_USE_SSE)) +#if defined BT_USE_SIMD_VECTOR3 && (defined(BT_USE_SSE_IN_API)&& defined (BT_USE_SSE)) __m128 vec = _mm_div_ps(v1.mVec128, v2.mVec128); vec = _mm_and_ps(vec, btvFFF0fMask); return btVector3(vec); @@ -935,20 +950,16 @@ SIMD_FORCE_INLINE btScalar btVector3::distance(const btVector3& v) const SIMD_FORCE_INLINE btVector3 btVector3::normalized() const { -#if defined(BT_USE_SSE_IN_API) && defined (BT_USE_SSE) btVector3 norm = *this; return norm.normalize(); -#else - return *this / length(); -#endif } SIMD_FORCE_INLINE btVector3 btVector3::rotate( const btVector3& wAxis, const btScalar _angle ) const { // wAxis must be a unit lenght vector -#if defined(BT_USE_SSE_IN_API) && defined (BT_USE_SSE) +#if defined BT_USE_SIMD_VECTOR3 && defined (BT_USE_SSE_IN_API) && defined (BT_USE_SSE) __m128 O = _mm_mul_ps(wAxis.mVec128, mVec128); btScalar ssin = btSin( _angle ); @@ -988,7 +999,7 @@ SIMD_FORCE_INLINE btVector3 btVector3::rotate( const btVector3& wAxis, const btS SIMD_FORCE_INLINE long btVector3::maxDot( const btVector3 *array, long array_count, btScalar &dotOut ) const { -#if defined (BT_USE_SSE) || defined (BT_USE_NEON) +#if (defined BT_USE_SSE && defined BT_USE_SIMD_VECTOR3 && defined BT_USE_SSE_IN_API) || defined (BT_USE_NEON) #if defined _WIN32 || defined (BT_USE_SSE) const long scalar_cutoff = 10; long _maxdot_large( const float *array, const float *vec, unsigned long array_count, float *dotOut ); @@ -996,10 +1007,8 @@ SIMD_FORCE_INLINE long btVector3::maxDot( const btVector3 *array, long arra const long scalar_cutoff = 4; extern long (*_maxdot_large)( const float *array, const float *vec, unsigned long array_count, float *dotOut ); #endif - if( array_count < scalar_cutoff ) -#else - -#endif//BT_USE_SSE || BT_USE_NEON + if( array_count < scalar_cutoff ) +#endif { btScalar maxDot = -SIMD_INFINITY; int i = 0; @@ -1018,14 +1027,14 @@ SIMD_FORCE_INLINE long btVector3::maxDot( const btVector3 *array, long arra dotOut = maxDot; return ptIndex; } -#if defined (BT_USE_SSE) || defined (BT_USE_NEON) +#if (defined BT_USE_SSE && defined BT_USE_SIMD_VECTOR3 && defined BT_USE_SSE_IN_API) || defined (BT_USE_NEON) return _maxdot_large( (float*) array, (float*) &m_floats[0], array_count, &dotOut ); #endif } SIMD_FORCE_INLINE long btVector3::minDot( const btVector3 *array, long array_count, btScalar &dotOut ) const { -#if defined (BT_USE_SSE) || defined (BT_USE_NEON) +#if (defined BT_USE_SSE && defined BT_USE_SIMD_VECTOR3 && defined BT_USE_SSE_IN_API) || defined (BT_USE_NEON) #if defined BT_USE_SSE const long scalar_cutoff = 10; long _mindot_large( const float *array, const float *vec, unsigned long array_count, float *dotOut ); @@ -1037,7 +1046,7 @@ SIMD_FORCE_INLINE long btVector3::minDot( const btVector3 *array, long arra #endif if( array_count < scalar_cutoff ) -#endif//BT_USE_SSE || BT_USE_NEON +#endif { btScalar minDot = SIMD_INFINITY; int i = 0; @@ -1058,9 +1067,9 @@ SIMD_FORCE_INLINE long btVector3::minDot( const btVector3 *array, long arra return ptIndex; } -#if defined (BT_USE_SSE) || defined (BT_USE_NEON) +#if (defined BT_USE_SSE && defined BT_USE_SIMD_VECTOR3 && defined BT_USE_SSE_IN_API) || defined (BT_USE_NEON) return _mindot_large( (float*) array, (float*) &m_floats[0], array_count, &dotOut ); -#endif +#endif//BT_USE_SIMD_VECTOR3 } @@ -1098,7 +1107,7 @@ public: SIMD_FORCE_INLINE btVector4 absolute4() const { -#if defined(BT_USE_SSE_IN_API) && defined (BT_USE_SSE) +#if defined BT_USE_SIMD_VECTOR3 && defined(BT_USE_SSE_IN_API) && defined (BT_USE_SSE) return btVector4(_mm_and_ps(mVec128, btvAbsfMask)); #elif defined(BT_USE_NEON) return btVector4(vabsq_f32(mVec128)); diff --git a/Engine/lib/bullet/src/Makefile.am b/Engine/lib/bullet/src/Makefile.am index ec39e7a05..0ecb5c9f5 100644 --- a/Engine/lib/bullet/src/Makefile.am +++ b/Engine/lib/bullet/src/Makefile.am @@ -160,6 +160,8 @@ libBulletCollision_la_SOURCES = \ BulletCollision/CollisionDispatch/btConvex2dConvex2dAlgorithm.cpp \ BulletCollision/CollisionDispatch/btUnionFind.cpp \ BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.cpp \ + BulletCollision/CollisionDispatch/btHashedSimplePairCache.cpp \ + BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.cpp \ BulletCollision/CollisionShapes/btTetrahedronShape.cpp \ BulletCollision/CollisionShapes/btShapeHull.cpp \ BulletCollision/CollisionShapes/btMinkowskiSumShape.cpp \ @@ -223,7 +225,7 @@ libBulletCollision_la_SOURCES = \ BulletCollision/NarrowPhaseCollision/btManifoldPoint.h \ BulletCollision/NarrowPhaseCollision/btDiscreteCollisionDetectorInterface.h \ BulletCollision/CollisionDispatch/btCollisionObject.h \ - BulletCollision/CollisionDispatch/btCollisionObjectWrapper.h \ + BulletCollision/CollisionDispatch/btCollisionObjectWrapper.h \ BulletCollision/CollisionDispatch/btGhostObject.h \ BulletCollision/CollisionDispatch/btSphereTriangleCollisionAlgorithm.h \ BulletCollision/CollisionDispatch/btBoxBoxCollisionAlgorithm.h \ @@ -239,6 +241,8 @@ libBulletCollision_la_SOURCES = \ BulletCollision/CollisionDispatch/btConvexConcaveCollisionAlgorithm.h \ BulletCollision/CollisionDispatch/btUnionFind.h \ BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btHashedSimplePairCache.h \ + BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.h \ BulletCollision/CollisionDispatch/btSimulationIslandManager.h \ BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h \ BulletCollision/CollisionDispatch/btCollisionWorld.h \ @@ -313,6 +317,7 @@ libBulletDynamics_la_SOURCES = \ BulletDynamics/Dynamics/btSimpleDynamicsWorld.cpp \ BulletDynamics/Dynamics/Bullet-C-API.cpp \ BulletDynamics/Dynamics/btDiscreteDynamicsWorld.cpp \ + BulletDynamics/ConstraintSolver/btFixedConstraint.cpp \ BulletDynamics/ConstraintSolver/btGearConstraint.cpp \ BulletDynamics/ConstraintSolver/btGeneric6DofConstraint.cpp \ BulletDynamics/ConstraintSolver/btGeneric6DofSpringConstraint.cpp \ @@ -356,7 +361,35 @@ libBulletDynamics_la_SOURCES = \ BulletDynamics/ConstraintSolver/btSolve2LinearConstraint.h \ BulletDynamics/Vehicle/btVehicleRaycaster.h \ BulletDynamics/Vehicle/btRaycastVehicle.h \ - BulletDynamics/Vehicle/btWheelInfo.h + BulletDynamics/Vehicle/btWheelInfo.h \ + BulletDynamics/Featherstone/btMultiBody.cpp \ + BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp \ + BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp \ + BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp \ + BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp \ + BulletDynamics/Featherstone/btMultiBodyJointMotor.h \ + BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.h \ + BulletDynamics/Featherstone/btMultiBody.h \ + BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h \ + BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h \ + BulletDynamics/Featherstone/btMultiBodyLink.h \ + BulletDynamics/Featherstone/btMultiBodyLinkCollider.h \ + BulletDynamics/Featherstone/btMultiBodySolverConstraint.h \ + BulletDynamics/Featherstone/btMultiBodyConstraint.h \ + BulletDynamics/Featherstone/btMultiBodyPoint2Point.h \ + BulletDynamics/Featherstone/btMultiBodyConstraint.cpp \ + BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp \ + BulletDynamics/MLCPSolvers/btDantzigLCP.cpp \ + BulletDynamics/MLCPSolvers/btMLCPSolver.cpp \ + BulletDynamics/MLCPSolvers/btDantzigLCP.h \ + BulletDynamics/MLCPSolvers/btDantzigSolver.h \ + BulletDynamics/MLCPSolvers/btMLCPSolver.h \ + BulletDynamics/MLCPSolvers/btMLCPSolverInterface.h \ + BulletDynamics/MLCPSolvers/btPATHSolver.h \ + BulletDynamics/MLCPSolvers/btSolveProjectedGaussSeidel.h + + + libBulletSoftBody_la_SOURCES = \ BulletSoftBody/btDefaultSoftBodySolver.cpp \ @@ -417,6 +450,20 @@ nobase_bullet_include_HEADERS += \ BulletDynamics/ConstraintSolver/btSolverBody.h \ BulletDynamics/Character/btCharacterControllerInterface.h \ BulletDynamics/Character/btKinematicCharacterController.h \ + BulletDynamics/Featherstone/btMultiBody.h \ + BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h \ + BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h \ + BulletDynamics/Featherstone/btMultiBodyLink.h \ + BulletDynamics/Featherstone/btMultiBodyLinkCollider.h \ + BulletDynamics/Featherstone/btMultiBodySolverConstraint.h \ + BulletDynamics/Featherstone/btMultiBodyConstraint.h \ + BulletDynamics/Featherstone/btMultiBodyPoint2Point.h \ + BulletDynamics/MLCPSolvers/btDantzigLCP.h \ + BulletDynamics/MLCPSolvers/btDantzigSolver.h \ + BulletDynamics/MLCPSolvers/btMLCPSolver.h \ + BulletDynamics/MLCPSolvers/btMLCPSolverInterface.h \ + BulletDynamics/MLCPSolvers/btPATHSolver.h \ + BulletDynamics/MLCPSolvers/btSolveProjectedGaussSeidel.h \ BulletCollision/CollisionShapes/btShapeHull.h \ BulletCollision/CollisionShapes/btConcaveShape.h \ BulletCollision/CollisionShapes/btCollisionMargin.h \ @@ -501,6 +548,8 @@ nobase_bullet_include_HEADERS += \ BulletCollision/CollisionDispatch/btBox2dBox2dCollisionAlgorithm.h \ BulletCollision/CollisionDispatch/btDefaultCollisionConfiguration.h \ BulletCollision/CollisionDispatch/btCompoundCollisionAlgorithm.h \ + BulletCollision/CollisionDispatch/btHashedSimplePairCache.h \ + BulletCollision/CollisionDispatch/btCompoundCompoundCollisionAlgorithm.h \ BulletCollision/CollisionDispatch/btSphereBoxCollisionAlgorithm.h \ BulletCollision/CollisionDispatch/btGhostObject.h \ BulletCollision/CollisionDispatch/btSimulationIslandManager.h \ diff --git a/Engine/lib/bullet/src/MiniCL/CMakeLists.txt b/Engine/lib/bullet/src/MiniCL/CMakeLists.txt index ed47db0cb..f351b1ce7 100644 --- a/Engine/lib/bullet/src/MiniCL/CMakeLists.txt +++ b/Engine/lib/bullet/src/MiniCL/CMakeLists.txt @@ -45,7 +45,10 @@ IF (INSTALL_LIBS) IF (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) INSTALL(TARGETS MiniCL DESTINATION .) ELSE (APPLE AND BUILD_SHARED_LIBS AND FRAMEWORK) - INSTALL(TARGETS MiniCL DESTINATION lib${LIB_SUFFIX}) + INSTALL(TARGETS MiniCL + RUNTIME DESTINATION bin + LIBRARY DESTINATION lib${LIB_SUFFIX} + ARCHIVE DESTINATION lib${LIB_SUFFIX}) INSTALL(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} DESTINATION ${INCLUDE_INSTALL_DIR} FILES_MATCHING PATTERN "*.h" PATTERN ".svn" EXCLUDE PATTERN "CMakeFiles" EXCLUDE) diff --git a/Engine/lib/bullet/src/btBulletDynamicsCommon.h b/Engine/lib/bullet/src/btBulletDynamicsCommon.h index dbd175c3f..50282bf21 100644 --- a/Engine/lib/bullet/src/btBulletDynamicsCommon.h +++ b/Engine/lib/bullet/src/btBulletDynamicsCommon.h @@ -33,6 +33,8 @@ subject to the following restrictions: #include "BulletDynamics/ConstraintSolver/btUniversalConstraint.h" #include "BulletDynamics/ConstraintSolver/btHinge2Constraint.h" #include "BulletDynamics/ConstraintSolver/btGearConstraint.h" +#include "BulletDynamics/ConstraintSolver/btFixedConstraint.h" + #include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h" From c514841ebbf35f723ddcb3288df0c5afd262fb4f Mon Sep 17 00:00:00 2001 From: Azaezel Date: Wed, 11 Jun 2014 03:09:41 -0500 Subject: [PATCH 083/317] Binds the full metrics display to CTRL+F2 while in-game. Escape Closes. --- Templates/Empty/game/core/art/gui/FrameOverlayGui.gui | 2 ++ Templates/Empty/game/scripts/client/default.bind.cs | 6 +++++- Templates/Full/game/core/art/gui/FrameOverlayGui.gui | 2 ++ Templates/Full/game/scripts/client/default.bind.cs | 6 +++++- 4 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Templates/Empty/game/core/art/gui/FrameOverlayGui.gui b/Templates/Empty/game/core/art/gui/FrameOverlayGui.gui index 04a3f1472..2736b7ade 100644 --- a/Templates/Empty/game/core/art/gui/FrameOverlayGui.gui +++ b/Templates/Empty/game/core/art/gui/FrameOverlayGui.gui @@ -23,6 +23,8 @@ modal = "True"; helpTag = "0"; expression = "10"; + command = "Canvas.popDialog(FrameOverlayGui);"; + accelerator = "escape"; }; }; //--- OBJECT WRITE END --- diff --git a/Templates/Empty/game/scripts/client/default.bind.cs b/Templates/Empty/game/scripts/client/default.bind.cs index fa7455271..05ced493e 100644 --- a/Templates/Empty/game/scripts/client/default.bind.cs +++ b/Templates/Empty/game/scripts/client/default.bind.cs @@ -441,7 +441,11 @@ GlobalActionMap.bind(keyboard, "ctrl o", bringUpOptions); //------------------------------------------------------------------------------ // Debugging Functions //------------------------------------------------------------------------------ - +function showmetrics(%val) +{ + metrics("FPS GFX SHADOW SFX TERRAIN GROUNDCOVER FOREST NET"); +} +GlobalActionMap.bind(keyboard, "ctrl F2", showmetrics); //------------------------------------------------------------------------------ // diff --git a/Templates/Full/game/core/art/gui/FrameOverlayGui.gui b/Templates/Full/game/core/art/gui/FrameOverlayGui.gui index 04a3f1472..2736b7ade 100644 --- a/Templates/Full/game/core/art/gui/FrameOverlayGui.gui +++ b/Templates/Full/game/core/art/gui/FrameOverlayGui.gui @@ -23,6 +23,8 @@ modal = "True"; helpTag = "0"; expression = "10"; + command = "Canvas.popDialog(FrameOverlayGui);"; + accelerator = "escape"; }; }; //--- OBJECT WRITE END --- diff --git a/Templates/Full/game/scripts/client/default.bind.cs b/Templates/Full/game/scripts/client/default.bind.cs index 803124bc0..2ea70cbee 100644 --- a/Templates/Full/game/scripts/client/default.bind.cs +++ b/Templates/Full/game/scripts/client/default.bind.cs @@ -615,7 +615,11 @@ GlobalActionMap.bind(keyboard, "ctrl o", bringUpOptions); //------------------------------------------------------------------------------ // Debugging Functions //------------------------------------------------------------------------------ - +function showmetrics(%val) +{ + metrics("FPS GFX SHADOW SFX TERRAIN GROUNDCOVER FOREST NET"); +} +GlobalActionMap.bind(keyboard, "ctrl F2", showmetrics); //------------------------------------------------------------------------------ // From f3fc84738bf4471d38ebf93b7ae2225d88d2ec61 Mon Sep 17 00:00:00 2001 From: bank Date: Thu, 15 May 2014 11:12:43 +0400 Subject: [PATCH 084/317] Use fixed buffer size var when allocating return buffer from console. Conflicts: Engine/source/T3D/missionArea.cpp Engine/source/gui/editor/guiDebugger.cpp --- Engine/source/T3D/aiClient.cpp | 15 ++-- Engine/source/T3D/gameFunctions.cpp | 10 +-- Engine/source/T3D/item.cpp | 10 +-- Engine/source/T3D/missionArea.cpp | 5 +- Engine/source/T3D/missionMarker.cpp | 5 +- Engine/source/T3D/player.cpp | 5 +- Engine/source/T3D/trigger.cpp | 5 +- Engine/source/app/net/net.cpp | 5 +- Engine/source/cinterface/cinterface.cpp | 5 +- Engine/source/console/console.cpp | 5 +- Engine/source/console/consoleFunctions.cpp | 7 +- Engine/source/console/consoleTypes.cpp | 45 +++++++----- Engine/source/console/dynamicTypes.h | 5 +- Engine/source/console/fileSystemFunctions.cpp | 10 +-- Engine/source/console/scriptFilename.cpp | 15 ++-- Engine/source/console/simObject.cpp | 5 +- Engine/source/core/fileObject.cpp | 5 +- Engine/source/core/stream/streamObject.cpp | 5 +- .../editors/guiMeshRoadEditorCtrl.cpp | 10 +-- .../editors/guiRiverEditorCtrl.cpp | 10 +-- .../environment/editors/guiRoadEditorCtrl.cpp | 5 +- Engine/source/forest/forestCollision.cpp | 5 +- Engine/source/gui/controls/guiColorPicker.cpp | 5 +- .../gui/controls/guiDirectoryFileListCtrl.cpp | 15 ++-- .../source/gui/controls/guiGradientCtrl.cpp | 15 ++-- Engine/source/gui/controls/guiListBoxCtrl.cpp | 5 +- Engine/source/gui/controls/guiPopUpCtrlEx.cpp | 5 +- .../source/gui/controls/guiTreeViewCtrl.cpp | 14 ++-- Engine/source/gui/core/guiTypes.cpp | 5 +- Engine/source/gui/editor/guiDebugger.cpp | 5 +- .../gui/editor/guiParticleGraphCtrl.cpp | 61 +++++++++------- .../source/gui/game/guiProgressBitmapCtrl.cpp | 5 +- Engine/source/gui/game/guiProgressCtrl.cpp | 5 +- .../gui/worldEditor/guiDecalEditorCtrl.cpp | 5 +- .../source/gui/worldEditor/terrainEditor.cpp | 10 +-- Engine/source/gui/worldEditor/worldEditor.cpp | 10 +-- .../gui/worldEditor/worldEditorSelection.cpp | 10 +-- .../advanced/advancedLightManager.cpp | 5 +- Engine/source/math/mConsoleFunctions.cpp | 20 +++--- Engine/source/math/mathTypes.cpp | 72 +++++++++++-------- Engine/source/scene/sceneContainer.cpp | 5 +- Engine/source/ts/tsShapeConstruct.cpp | 30 ++++---- 42 files changed, 300 insertions(+), 204 deletions(-) diff --git a/Engine/source/T3D/aiClient.cpp b/Engine/source/T3D/aiClient.cpp index cbcf78e1e..335ac6a15 100644 --- a/Engine/source/T3D/aiClient.cpp +++ b/Engine/source/T3D/aiClient.cpp @@ -457,8 +457,9 @@ ConsoleMethod( AIClient, getAimLocation, const char *, 2, 2, "ai.getAimLocation( AIClient *ai = static_cast( object ); Point3F aimPoint = ai->getAimLocation(); - char *returnBuffer = Con::getReturnBuffer( 256 ); - dSprintf( returnBuffer, 256, "%f %f %f", aimPoint.x, aimPoint.y, aimPoint.z ); + static const U32 bufSize = 256; + char *returnBuffer = Con::getReturnBuffer( bufSize ); + dSprintf( returnBuffer, bufSize, "%f %f %f", aimPoint.x, aimPoint.y, aimPoint.z ); return returnBuffer; } @@ -470,8 +471,9 @@ ConsoleMethod( AIClient, getMoveDestination, const char *, 2, 2, "ai.getMoveDest AIClient *ai = static_cast( object ); Point3F movePoint = ai->getMoveDestination(); - char *returnBuffer = Con::getReturnBuffer( 256 ); - dSprintf( returnBuffer, 256, "%f %f %f", movePoint.x, movePoint.y, movePoint.z ); + static const U32 bufSize = 256; + char *returnBuffer = Con::getReturnBuffer( bufSize ); + dSprintf( returnBuffer, bufSize, "%f %f %f", movePoint.x, movePoint.y, movePoint.z ); return returnBuffer; } @@ -522,8 +524,9 @@ ConsoleMethod( AIClient, getLocation, const char *, 2, 2, "ai.getLocation();" ) AIClient *ai = static_cast( object ); Point3F locPoint = ai->getLocation(); - char *returnBuffer = Con::getReturnBuffer( 256 ); - dSprintf( returnBuffer, 256, "%f %f %f", locPoint.x, locPoint.y, locPoint.z ); + static const U32 bufSize = 256; + char *returnBuffer = Con::getReturnBuffer( bufSize ); + dSprintf( returnBuffer, bufSize, "%f %f %f", locPoint.x, locPoint.y, locPoint.z ); return returnBuffer; } diff --git a/Engine/source/T3D/gameFunctions.cpp b/Engine/source/T3D/gameFunctions.cpp index 1196ea030..e16acf1ec 100644 --- a/Engine/source/T3D/gameFunctions.cpp +++ b/Engine/source/T3D/gameFunctions.cpp @@ -145,9 +145,10 @@ ConsoleFunction(containerFindFirst, const char*, 6, 6, "(int mask, Point3F point //return the first element sgServerQueryIndex = 0; - char *buff = Con::getReturnBuffer(100); + static const U32 bufSize = 100; + char *buff = Con::getReturnBuffer(bufSize); if (sgServerQueryList.mList.size()) - dSprintf(buff, 100, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId()); + dSprintf(buff, bufSize, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId()); else buff[0] = '\0'; @@ -162,9 +163,10 @@ ConsoleFunction( containerFindNext, const char*, 1, 1, "()" "@ingroup Game") { //return the next element - char *buff = Con::getReturnBuffer(100); + static const U32 bufSize = 100; + char *buff = Con::getReturnBuffer(bufSize); if (sgServerQueryIndex < sgServerQueryList.mList.size()) - dSprintf(buff, 100, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId()); + dSprintf(buff, bufSize, "%d", sgServerQueryList.mList[sgServerQueryIndex++]->getId()); else buff[0] = '\0'; diff --git a/Engine/source/T3D/item.cpp b/Engine/source/T3D/item.cpp index 1cd78fbb8..10ce867a9 100644 --- a/Engine/source/T3D/item.cpp +++ b/Engine/source/T3D/item.cpp @@ -1241,9 +1241,10 @@ DefineEngineMethod( Item, getLastStickyPos, const char*, (),, "@note Server side only.\n" ) { - char* ret = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char* ret = Con::getReturnBuffer(bufSize); if (object->isServerObject()) - dSprintf(ret, 255, "%g %g %g", + dSprintf(ret, bufSize, "%g %g %g", object->mStickyCollisionPos.x, object->mStickyCollisionPos.y, object->mStickyCollisionPos.z); @@ -1263,9 +1264,10 @@ DefineEngineMethod( Item, getLastStickyNormal, const char *, (),, "@note Server side only.\n" ) { - char* ret = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char* ret = Con::getReturnBuffer(bufSize); if (object->isServerObject()) - dSprintf(ret, 255, "%g %g %g", + dSprintf(ret, bufSize, "%g %g %g", object->mStickyCollisionNormal.x, object->mStickyCollisionNormal.y, object->mStickyCollisionNormal.z); diff --git a/Engine/source/T3D/missionArea.cpp b/Engine/source/T3D/missionArea.cpp index 1e592b7b2..4e66dde8a 100644 --- a/Engine/source/T3D/missionArea.cpp +++ b/Engine/source/T3D/missionArea.cpp @@ -176,10 +176,11 @@ DefineEngineFunction(getMissionAreaServerObject, MissionArea*, (),, DefineEngineMethod( MissionArea, getArea, const char *, (),, "Returns 4 fields: starting x, starting y, extents x, extents y.\n") { - char* returnBuffer = Con::getReturnBuffer(48); + static const U32 bufSize = 48; + char* returnBuffer = Con::getReturnBuffer(bufSize); RectI area = object->getArea(); - dSprintf(returnBuffer, 48, "%d %d %d %d", area.point.x, area.point.y, area.extent.x, area.extent.y); + dSprintf(returnBuffer, bufSize, "%d %d %d %d", area.point.x, area.point.y, area.extent.x, area.extent.y); return(returnBuffer); } diff --git a/Engine/source/T3D/missionMarker.cpp b/Engine/source/T3D/missionMarker.cpp index bff79e962..f7207df36 100644 --- a/Engine/source/T3D/missionMarker.cpp +++ b/Engine/source/T3D/missionMarker.cpp @@ -304,8 +304,9 @@ ConsoleType( WayPointTeam, TypeWayPointTeam, WayPointTeam ) ConsoleGetType( TypeWayPointTeam ) { - char * buf = Con::getReturnBuffer(32); - dSprintf(buf, 32, "%d", ((WayPointTeam*)dptr)->mTeamId); + static const U32 bufSize = 32; + char * buf = Con::getReturnBuffer(bufSize); + dSprintf(buf, bufSize, "%d", ((WayPointTeam*)dptr)->mTeamId); return(buf); } diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index e81238fb3..6ce9f9020 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -6510,8 +6510,9 @@ DefineEngineMethod( Player, getDamageLocation, const char*, ( Point3F pos ),, object->getDamageLocation(pos, buffer1, buffer2); - char *buff = Con::getReturnBuffer(128); - dSprintf(buff, 128, "%s %s", buffer1, buffer2); + static const U32 bufSize = 128; + char *buff = Con::getReturnBuffer(bufSize); + dSprintf(buff, bufSize, "%s %s", buffer1, buffer2); return buff; } diff --git a/Engine/source/T3D/trigger.cpp b/Engine/source/T3D/trigger.cpp index bbc598b0c..7dbcba618 100644 --- a/Engine/source/T3D/trigger.cpp +++ b/Engine/source/T3D/trigger.cpp @@ -259,8 +259,9 @@ ConsoleGetType( TypeTriggerPolyhedron ) AssertFatal(currVec == 3, "Internal error: Bad trigger polyhedron"); // Build output string. - char* retBuf = Con::getReturnBuffer(1024); - dSprintf(retBuf, 1023, "%7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f", + static const U32 bufSize = 1024; + char* retBuf = Con::getReturnBuffer(bufSize); + dSprintf(retBuf, bufSize, "%7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f %7.7f", origin.x, origin.y, origin.z, vecs[0].x, vecs[0].y, vecs[0].z, vecs[2].x, vecs[2].y, vecs[2].z, diff --git a/Engine/source/app/net/net.cpp b/Engine/source/app/net/net.cpp index 922a6b808..390799907 100644 --- a/Engine/source/app/net/net.cpp +++ b/Engine/source/app/net/net.cpp @@ -382,10 +382,11 @@ ConsoleFunction( buildTaggedString, const char*, 2, 11, "(string format, ...)" if (*indexPtr == StringTagPrefixByte) indexPtr++; const char *fmtString = gNetStringTable->lookupString(dAtoi(indexPtr)); - char *strBuffer = Con::getReturnBuffer(512); + static const U32 bufSize = 512; + char *strBuffer = Con::getReturnBuffer(bufSize); const char *fmtStrPtr = fmtString; char *strBufPtr = strBuffer; - S32 strMaxLength = 511; + S32 strMaxLength = bufSize - 1; if (!fmtString) goto done; diff --git a/Engine/source/cinterface/cinterface.cpp b/Engine/source/cinterface/cinterface.cpp index ecfdeaf97..2d9a729d3 100644 --- a/Engine/source/cinterface/cinterface.cpp +++ b/Engine/source/cinterface/cinterface.cpp @@ -468,9 +468,10 @@ ConsoleFunction(testJavaScriptBridge, const char *, 4, 4, "testBridge(arg1, arg2 if (dStrcmp(jret,"42")) failed = 3; - char *ret = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char *ret = Con::getReturnBuffer(bufSize); - dSprintf(ret, 256, "%i", failed); + dSprintf(ret, bufSize, "%i", failed); return ret; } diff --git a/Engine/source/console/console.cpp b/Engine/source/console/console.cpp index 838b3ab14..fa1e8b7e5 100644 --- a/Engine/source/console/console.cpp +++ b/Engine/source/console/console.cpp @@ -1313,8 +1313,9 @@ const char *getFormattedData(S32 type, const char *data, const EnumTable *tbl, B Con::setData(type, variable, 0, 1, &data, tbl, flag); const char* formattedVal = Con::getData(type, variable, 0, tbl, flag); - char* returnBuffer = Con::getReturnBuffer(2048); - dSprintf(returnBuffer, 2048, "%s\0", formattedVal ); + static const U32 bufSize = 2048; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%s\0", formattedVal ); cbt->deleteNativeVariable(variable); diff --git a/Engine/source/console/consoleFunctions.cpp b/Engine/source/console/consoleFunctions.cpp index e406ae9ce..f5c90194e 100644 --- a/Engine/source/console/consoleFunctions.cpp +++ b/Engine/source/console/consoleFunctions.cpp @@ -75,7 +75,8 @@ DefineConsoleFunction( strformat, const char*, ( const char* format, const char* "@ingroup Strings\n" "@see http://en.wikipedia.org/wiki/Printf" ) { - char* pBuffer = Con::getReturnBuffer(64); + static const U32 bufSize = 64; + char* pBuffer = Con::getReturnBuffer(bufSize); const char *pch = format; pBuffer[0] = '\0'; @@ -99,7 +100,7 @@ DefineConsoleFunction( strformat, const char*, ( const char* format, const char* case 'u': case 'x': case 'X': - dSprintf( pBuffer, 64, format, dAtoi( value ) ); + dSprintf( pBuffer, bufSize, format, dAtoi( value ) ); break; case 'e': @@ -107,7 +108,7 @@ DefineConsoleFunction( strformat, const char*, ( const char* format, const char* case 'f': case 'g': case 'G': - dSprintf( pBuffer, 64, format, dAtof( value ) ); + dSprintf( pBuffer, bufSize, format, dAtof( value ) ); break; default: diff --git a/Engine/source/console/consoleTypes.cpp b/Engine/source/console/consoleTypes.cpp index 059a9170c..5f0abcbb6 100644 --- a/Engine/source/console/consoleTypes.cpp +++ b/Engine/source/console/consoleTypes.cpp @@ -289,8 +289,9 @@ ImplementConsoleTypeCasters( TypeS8, S8 ) ConsoleGetType( TypeS8 ) { - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%d", *((U8 *) dptr) ); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%d", *((U8 *) dptr) ); return returnBuffer; } @@ -310,8 +311,9 @@ ImplementConsoleTypeCasters(TypeS32, S32) ConsoleGetType( TypeS32 ) { - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%d", *((S32 *) dptr) ); + static const U32 bufSize = 512; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%d", *((S32 *) dptr) ); return returnBuffer; } @@ -389,8 +391,9 @@ ImplementConsoleTypeCasters(TypeF32, F32) ConsoleGetType( TypeF32 ) { - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%g", *((F32 *) dptr) ); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%g", *((F32 *) dptr) ); return returnBuffer; } ConsoleSetType( TypeF32 ) @@ -487,8 +490,9 @@ ImplementConsoleTypeCasters( TypeBoolVector, Vector< bool > ) ConsoleGetType( TypeBoolVector ) { Vector *vec = (Vector*)dptr; - char* returnBuffer = Con::getReturnBuffer(1024); - S32 maxReturn = 1024; + static const U32 bufSize = 1024; + char* returnBuffer = Con::getReturnBuffer(bufSize); + S32 maxReturn = bufSize; returnBuffer[0] = '\0'; S32 returnLeng = 0; for (Vector::iterator itr = vec->begin(); itr < vec->end(); itr++) @@ -579,8 +583,9 @@ ConsoleGetType( TypeColorF ) return colorName; // Format as color components. - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%g %g %g %g", color->red, color->green, color->blue, color->alpha); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%g %g %g %g", color->red, color->green, color->blue, color->alpha); return(returnBuffer); } @@ -651,8 +656,9 @@ ConsoleGetType( TypeColorI ) return colorName; // Format as color components. - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%d %d %d %d", color->red, color->green, color->blue, color->alpha); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%d %d %d %d", color->red, color->green, color->blue, color->alpha); return returnBuffer; } @@ -723,8 +729,9 @@ ConsoleSetType( TypeSimObjectName ) ConsoleGetType( TypeSimObjectName ) { SimObject **obj = (SimObject**)dptr; - char* returnBuffer = Con::getReturnBuffer(128); - dSprintf(returnBuffer, 128, "%s", *obj && (*obj)->getName() ? (*obj)->getName() : ""); + static const U32 bufSize = 128; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%s", *obj && (*obj)->getName() ? (*obj)->getName() : ""); return returnBuffer; } @@ -791,8 +798,9 @@ ConsoleType( int, TypeTerrainMaterialIndex, S32 ) ConsoleGetType( TypeTerrainMaterialIndex ) { - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%d", *((S32 *) dptr) ); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%d", *((S32 *) dptr) ); return returnBuffer; } @@ -853,8 +861,9 @@ ConsoleType( RectF, TypeRectUV, RectF ) ConsoleGetType( TypeRectUV ) { RectF *rect = (RectF *) dptr; - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%g %g %g %g", rect->point.x, rect->point.y, + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%g %g %g %g", rect->point.x, rect->point.y, rect->extent.x, rect->extent.y); return returnBuffer; } diff --git a/Engine/source/console/dynamicTypes.h b/Engine/source/console/dynamicTypes.h index a10ab0def..105b2f1ef 100644 --- a/Engine/source/console/dynamicTypes.h +++ b/Engine/source/console/dynamicTypes.h @@ -211,8 +211,9 @@ class BitfieldConsoleBaseType : public ConsoleBaseType virtual const char* getData( void* dptr, const EnumTable*, BitSet32 ) { - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "0x%08x", *((S32 *) dptr) ); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "0x%08x", *((S32 *) dptr) ); return returnBuffer; } virtual void setData( void* dptr, S32 argc, const char** argv, const EnumTable*, BitSet32 ) diff --git a/Engine/source/console/fileSystemFunctions.cpp b/Engine/source/console/fileSystemFunctions.cpp index 7b159d30d..75b1ba9f7 100644 --- a/Engine/source/console/fileSystemFunctions.cpp +++ b/Engine/source/console/fileSystemFunctions.cpp @@ -695,8 +695,9 @@ DefineEngineFunction(makeFullPath, String, ( const char* path, const char* cwd ) "@return String containing non-relative directory of path\n" "@ingroup FileSystem") { - char *buf = Con::getReturnBuffer(512); - Platform::makeFullPathName(path, buf, 512, dStrlen(cwd) > 1 ? cwd : NULL); + static const U32 bufSize = 512; + char *buf = Con::getReturnBuffer(buf); + Platform::makeFullPathName(path, buf, bufSize, dStrlen(cwd) > 1 ? cwd : NULL); return buf; } @@ -721,8 +722,9 @@ DefineEngineFunction(pathConcat, String, ( const char* path, const char* file),, "@return String containing concatenated file name and path\n" "@ingroup FileSystem") { - char *buf = Con::getReturnBuffer(1024); - Platform::makeFullPathName(file, buf, 1024, path); + static const U32 bufSize = 1024; + char *buf = Con::getReturnBuffer(buf); + Platform::makeFullPathName(file, buf, bufSize, path); return buf; } diff --git a/Engine/source/console/scriptFilename.cpp b/Engine/source/console/scriptFilename.cpp index 35631e60b..649e109eb 100644 --- a/Engine/source/console/scriptFilename.cpp +++ b/Engine/source/console/scriptFilename.cpp @@ -344,8 +344,9 @@ ConsoleFunction(expandFilename, const char*, 2, 2, "(string filename)" "@ingroup FileSystem") { TORQUE_UNUSED(argc); - char* ret = Con::getReturnBuffer( 1024 ); - Con::expandScriptFilename(ret, 1024, argv[1]); + static const U32 bufSize = 1024; + char* ret = Con::getReturnBuffer( bufSize ); + Con::expandScriptFilename(ret, bufSize, argv[1]); return ret; } @@ -355,8 +356,9 @@ ConsoleFunction(expandOldFilename, const char*, 2, 2, "(string filename)" "@ingroup FileSystem") { TORQUE_UNUSED(argc); - char* ret = Con::getReturnBuffer( 1024 ); - Con::expandOldScriptFilename(ret, 1024, argv[1]); + static const U32 bufSize = 1024; + char* ret = Con::getReturnBuffer( bufSize ); + Con::expandOldScriptFilename(ret, bufSize, argv[1]); return ret; } @@ -368,8 +370,9 @@ ConsoleToolFunction(collapseFilename, const char*, 2, 2, "(string filename)" "@internal Editor use only") { TORQUE_UNUSED(argc); - char* ret = Con::getReturnBuffer( 1024 ); - Con::collapseScriptFilename(ret, 1024, argv[1]); + static const U32 bufSize = 1024; + char* ret = Con::getReturnBuffer( bufSize ); + Con::collapseScriptFilename(ret, bufSize, argv[1]); return ret; } diff --git a/Engine/source/console/simObject.cpp b/Engine/source/console/simObject.cpp index 9ad66e627..629542531 100644 --- a/Engine/source/console/simObject.cpp +++ b/Engine/source/console/simObject.cpp @@ -2680,11 +2680,12 @@ DefineConsoleMethod( SimObject, getDynamicField, const char*, ( S32 index ),, ++itr; } - char* buffer = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char* buffer = Con::getReturnBuffer(bufSize); if (*itr) { SimFieldDictionary::Entry* entry = *itr; - dSprintf(buffer, 256, "%s\t%s", entry->slotName, entry->value); + dSprintf(buffer, bufSize, "%s\t%s", entry->slotName, entry->value); return buffer; } diff --git a/Engine/source/core/fileObject.cpp b/Engine/source/core/fileObject.cpp index 4234d583d..7ab38ae00 100644 --- a/Engine/source/core/fileObject.cpp +++ b/Engine/source/core/fileObject.cpp @@ -343,8 +343,9 @@ DefineEngineMethod( FileObject, peekLine, const char*, (),, "@return String containing the line of data that was just peeked\n") { - char *line = Con::getReturnBuffer( 512 ); - object->peekLine( (U8*)line, 512 ); + static const U32 bufSize = 512; + char *line = Con::getReturnBuffer( bufSize ); + object->peekLine( (U8*)line, bufSize ); return line; } diff --git a/Engine/source/core/stream/streamObject.cpp b/Engine/source/core/stream/streamObject.cpp index bf684cf08..924df4bcd 100644 --- a/Engine/source/core/stream/streamObject.cpp +++ b/Engine/source/core/stream/streamObject.cpp @@ -113,8 +113,9 @@ const char * StreamObject::readLine() if(mStream == NULL) return NULL; - char *buffer = Con::getReturnBuffer(256); - mStream->readLine((U8 *)buffer, 256); + static const U32 bufSize = 256; + char *buffer = Con::getReturnBuffer(bufSize); + mStream->readLine((U8 *)buffer, bufSize); return buffer; } diff --git a/Engine/source/environment/editors/guiMeshRoadEditorCtrl.cpp b/Engine/source/environment/editors/guiMeshRoadEditorCtrl.cpp index ac4e4edc5..51fd2db7f 100644 --- a/Engine/source/environment/editors/guiMeshRoadEditorCtrl.cpp +++ b/Engine/source/environment/editors/guiMeshRoadEditorCtrl.cpp @@ -1223,9 +1223,10 @@ ConsoleMethod( GuiMeshRoadEditorCtrl, setNodeDepth, void, 3, 3, "" ) ConsoleMethod( GuiMeshRoadEditorCtrl, getNodePosition, const char*, 2, 2, "" ) { - char* returnBuffer = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, 256, "%f %f %f", + dSprintf(returnBuffer, bufSize, "%f %f %f", object->getNodePosition().x, object->getNodePosition().y, object->getNodePosition().z); return returnBuffer; @@ -1249,9 +1250,10 @@ ConsoleMethod( GuiMeshRoadEditorCtrl, setNodePosition, void, 3, 3, "" ) ConsoleMethod( GuiMeshRoadEditorCtrl, getNodeNormal, const char*, 2, 2, "" ) { - char* returnBuffer = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, 256, "%f %f %f", + dSprintf(returnBuffer, bufSize, "%f %f %f", object->getNodeNormal().x, object->getNodeNormal().y, object->getNodeNormal().z); return returnBuffer; diff --git a/Engine/source/environment/editors/guiRiverEditorCtrl.cpp b/Engine/source/environment/editors/guiRiverEditorCtrl.cpp index 18f49b0e6..ce7603643 100644 --- a/Engine/source/environment/editors/guiRiverEditorCtrl.cpp +++ b/Engine/source/environment/editors/guiRiverEditorCtrl.cpp @@ -1430,9 +1430,10 @@ ConsoleMethod( GuiRiverEditorCtrl, setNodeDepth, void, 3, 3, "" ) ConsoleMethod( GuiRiverEditorCtrl, getNodePosition, const char*, 2, 2, "" ) { - char* returnBuffer = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, 256, "%f %f %f", + dSprintf(returnBuffer, bufSize, "%f %f %f", object->getNodePosition().x, object->getNodePosition().y, object->getNodePosition().z); return returnBuffer; @@ -1456,9 +1457,10 @@ ConsoleMethod( GuiRiverEditorCtrl, setNodePosition, void, 3, 3, "" ) ConsoleMethod( GuiRiverEditorCtrl, getNodeNormal, const char*, 2, 2, "" ) { - char* returnBuffer = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, 256, "%f %f %f", + dSprintf(returnBuffer, bufSize, "%f %f %f", object->getNodeNormal().x, object->getNodeNormal().y, object->getNodeNormal().z); return returnBuffer; diff --git a/Engine/source/environment/editors/guiRoadEditorCtrl.cpp b/Engine/source/environment/editors/guiRoadEditorCtrl.cpp index a890c6ce2..c1f790de6 100644 --- a/Engine/source/environment/editors/guiRoadEditorCtrl.cpp +++ b/Engine/source/environment/editors/guiRoadEditorCtrl.cpp @@ -1064,9 +1064,10 @@ ConsoleMethod( GuiRoadEditorCtrl, setNodeWidth, void, 3, 3, "" ) ConsoleMethod( GuiRoadEditorCtrl, getNodePosition, const char*, 2, 2, "" ) { - char* returnBuffer = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); - dSprintf(returnBuffer, 256, "%f %f %f", + dSprintf(returnBuffer, bufSize, "%f %f %f", object->getNodePosition().x, object->getNodePosition().y, object->getNodePosition().z); return returnBuffer; diff --git a/Engine/source/forest/forestCollision.cpp b/Engine/source/forest/forestCollision.cpp index 8658f2474..ded08cd96 100644 --- a/Engine/source/forest/forestCollision.cpp +++ b/Engine/source/forest/forestCollision.cpp @@ -64,13 +64,14 @@ ScriptMethod( Forest, forestRayCast, const char*, 4, 4, "( Point3F start, Point3 dSscanf(argv[2], "%g %g %g", &start.x, &start.y, &start.z); dSscanf(argv[3], "%g %g %g", &end.x, &end.y, &end.z); - char *returnBuffer = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char *returnBuffer = Con::getReturnBuffer(bufSize); returnBuffer[0] = '0'; returnBuffer[1] = '\0'; ForestRayInfo rinfo; if ( object->castRayI( start, end, &rinfo ) ) - dSprintf( returnBuffer, 256, "%d %d %g", rinfo.item->getData()->getId(), rinfo.key, rinfo.t ); + dSprintf( returnBuffer, bufSize, "%d %d %g", rinfo.item->getData()->getId(), rinfo.key, rinfo.t ); return returnBuffer; } diff --git a/Engine/source/gui/controls/guiColorPicker.cpp b/Engine/source/gui/controls/guiColorPicker.cpp index c8c34eebe..e6ea1ad12 100644 --- a/Engine/source/gui/controls/guiColorPicker.cpp +++ b/Engine/source/gui/controls/guiColorPicker.cpp @@ -528,10 +528,11 @@ void GuiColorPickerCtrl::setScriptValue(const char *value) ConsoleMethod(GuiColorPickerCtrl, getSelectorPos, const char*, 2, 2, "Gets the current position of the selector") { - char *temp = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char *temp = Con::getReturnBuffer(bufSize); Point2I pos; pos = object->getSelectorPos(); - dSprintf(temp,256,"%d %d",pos.x, pos.y); + dSprintf(temp,bufSize,"%d %d",pos.x, pos.y); return temp; } diff --git a/Engine/source/gui/controls/guiDirectoryFileListCtrl.cpp b/Engine/source/gui/controls/guiDirectoryFileListCtrl.cpp index d5695b24d..c244af658 100644 --- a/Engine/source/gui/controls/guiDirectoryFileListCtrl.cpp +++ b/Engine/source/gui/controls/guiDirectoryFileListCtrl.cpp @@ -169,17 +169,18 @@ DefineEngineMethod( GuiDirectoryFileListCtrl, getSelectedFiles, const char*, (), return StringTable->insert( "" ); // Get an adequate buffer - char itemBuffer[256]; - dMemset( itemBuffer, 0, 256 ); + static const U32 itemBufSize = 256; + char itemBuffer[itemBufSize]; - char* returnBuffer = Con::getReturnBuffer( ItemVector.size() * 64 ); - dMemset( returnBuffer, 0, ItemVector.size() * 64 ); + static const U32 bufSize = ItemVector.size() * 64; + char* returnBuffer = Con::getReturnBuffer( bufSize ); + dMemset( returnBuffer, 0, bufSize ); // Fetch the first entry StringTableEntry itemText = object->getItemText( ItemVector[0] ); if( !itemText ) return StringTable->lookup(""); - dSprintf( returnBuffer, ItemVector.size() * 64, "%s", itemText ); + dSprintf( returnBuffer, bufSize, "%s", itemText ); // If only one entry, return it. if( ItemVector.size() == 1 ) @@ -192,8 +193,8 @@ DefineEngineMethod( GuiDirectoryFileListCtrl, getSelectedFiles, const char*, (), if( !itemText ) continue; - dMemset( itemBuffer, 0, 256 ); - dSprintf( itemBuffer, 256, " %s", itemText ); + dMemset( itemBuffer, 0, itemBufSize ); + dSprintf( itemBuffer, itemBufSize, " %s", itemText ); dStrcat( returnBuffer, itemBuffer ); } diff --git a/Engine/source/gui/controls/guiGradientCtrl.cpp b/Engine/source/gui/controls/guiGradientCtrl.cpp index 30d94c969..7fe919fd1 100644 --- a/Engine/source/gui/controls/guiGradientCtrl.cpp +++ b/Engine/source/gui/controls/guiGradientCtrl.cpp @@ -89,8 +89,9 @@ bool GuiGradientSwatchCtrl::onWake() if ( !Parent::onWake() ) return false; - char* altCommand = Con::getReturnBuffer(512); - dSprintf( altCommand, 512, "%s(%i.color, \"%i.setColor\");", mColorFunction, getId(), getId() ); + static const U32 bufSize = 512; + char* altCommand = Con::getReturnBuffer(bufSize); + dSprintf( altCommand, bufSize, "%s(%i.color, \"%i.setColor\");", mColorFunction, getId(), getId() ); setField( "altCommand", altCommand ); return true; @@ -616,10 +617,11 @@ ConsoleMethod(GuiGradientCtrl, getColor, const char*, 3, 3, "Get color value") { if ( idx >= 0 && idx < object->mColorRange.size() ) { - char* rColor = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char* rColor = Con::getReturnBuffer(bufSize); rColor[0] = 0; - dSprintf(rColor, 256, "%f %f %f %f", + dSprintf(rColor, bufSize, "%f %f %f %f", object->mColorRange[idx].swatch->getColor().red, object->mColorRange[idx].swatch->getColor().green, object->mColorRange[idx].swatch->getColor().blue, @@ -632,10 +634,11 @@ ConsoleMethod(GuiGradientCtrl, getColor, const char*, 3, 3, "Get color value") { if ( idx >= 0 && idx < object->mAlphaRange.size() ) { - char* rColor = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char* rColor = Con::getReturnBuffer(bufSize); rColor[0] = 0; - dSprintf(rColor, 256, "%f %f %f %f", + dSprintf(rColor, bufSize, "%f %f %f %f", object->mAlphaRange[idx].swatch->getColor().red, object->mAlphaRange[idx].swatch->getColor().green, object->mAlphaRange[idx].swatch->getColor().blue, diff --git a/Engine/source/gui/controls/guiListBoxCtrl.cpp b/Engine/source/gui/controls/guiListBoxCtrl.cpp index fda05f07c..6459d5004 100644 --- a/Engine/source/gui/controls/guiListBoxCtrl.cpp +++ b/Engine/source/gui/controls/guiListBoxCtrl.cpp @@ -450,8 +450,9 @@ DefineEngineMethod( GuiListBoxCtrl, getSelectedItems, const char*, (),, if( selItems.empty() ) return StringTable->lookup("-1"); - UTF8 *retBuffer = Con::getReturnBuffer( selItems.size() * 4 ); - dMemset( retBuffer, 0, selItems.size() * 4 ); + static const U32 bufSize = selItems.size() * 4; + UTF8 *retBuffer = Con::getReturnBuffer( bufSize ); + dMemset( retBuffer, 0, bufSize ); Vector::iterator i = selItems.begin(); for( ; i != selItems.end(); i++ ) { diff --git a/Engine/source/gui/controls/guiPopUpCtrlEx.cpp b/Engine/source/gui/controls/guiPopUpCtrlEx.cpp index 4db07ed33..97258c67c 100644 --- a/Engine/source/gui/controls/guiPopUpCtrlEx.cpp +++ b/Engine/source/gui/controls/guiPopUpCtrlEx.cpp @@ -579,8 +579,9 @@ ConsoleMethod( GuiPopUpMenuCtrlEx, getColorById, const char*, 3, 3, ColorI color; object->getColoredBox(color, dAtoi(argv[2])); - char *strBuffer = Con::getReturnBuffer(512); - dSprintf(strBuffer, 512, "%d %d %d %d", color.red, color.green, color.blue, color.alpha); + static const U32 bufSize = 512; + char *strBuffer = Con::getReturnBuffer(bufSize); + dSprintf(strBuffer, bufSize, "%d %d %d %d", color.red, color.green, color.blue, color.alpha); return strBuffer; } diff --git a/Engine/source/gui/controls/guiTreeViewCtrl.cpp b/Engine/source/gui/controls/guiTreeViewCtrl.cpp index bf55bf3a2..7f36a5903 100644 --- a/Engine/source/gui/controls/guiTreeViewCtrl.cpp +++ b/Engine/source/gui/controls/guiTreeViewCtrl.cpp @@ -5061,8 +5061,9 @@ ConsoleMethod(GuiTreeViewCtrl, getSelectedObject, S32, 2, 3, "( int index=0 ) - ConsoleMethod(GuiTreeViewCtrl, getSelectedObjectList, const char*, 2, 2, "Returns a space sperated list of all selected object ids.") { - char* buff = Con::getReturnBuffer(1024); - dSprintf(buff,1024,""); + static const U32 bufSize = 1024; + char* buff = Con::getReturnBuffer(bufSize); + dSprintf(buff,bufSize,""); const Vector< GuiTreeViewCtrl::Item* > selectedItems = object->getSelectedItems(); for(S32 i = 0; i < selectedItems.size(); i++) @@ -5077,7 +5078,7 @@ ConsoleMethod(GuiTreeViewCtrl, getSelectedObjectList, const char*, 2, 2, //the start of the buffer where we want to write char* buffPart = buff+len; //the size of the remaining buffer (-1 cause dStrlen doesn't count the \0) - S32 size = 1024-len-1; + S32 size = bufSize-len-1; //write it: if(size < 1) { @@ -5126,8 +5127,9 @@ ConsoleMethod(GuiTreeViewCtrl, getTextToRoot, const char*,4,4,"(TreeItemId item, ConsoleMethod(GuiTreeViewCtrl, getSelectedItemList,const char*, 2,2,"returns a space seperated list of mulitple item ids") { - char* buff = Con::getReturnBuffer(1024); - dSprintf(buff,1024,""); + static const U32 bufSize = 1024; + char* buff = Con::getReturnBuffer(bufSize); + dSprintf(buff,bufSize,""); const Vector< S32 >& selected = object->getSelected(); for(S32 i = 0; i < selected.size(); i++) @@ -5138,7 +5140,7 @@ ConsoleMethod(GuiTreeViewCtrl, getSelectedItemList,const char*, 2,2,"returns a s //the start of the buffer where we want to write char* buffPart = buff+len; //the size of the remaining buffer (-1 cause dStrlen doesn't count the \0) - S32 size = 1024-len-1; + S32 size = bufSize-len-1; //write it: if(size < 1) { diff --git a/Engine/source/gui/core/guiTypes.cpp b/Engine/source/gui/core/guiTypes.cpp index 7332459b4..369d9f963 100644 --- a/Engine/source/gui/core/guiTypes.cpp +++ b/Engine/source/gui/core/guiTypes.cpp @@ -719,8 +719,9 @@ ImplementConsoleTypeCasters( TypeRectSpacingI, RectSpacingI ) ConsoleGetType( TypeRectSpacingI ) { RectSpacingI *rect = (RectSpacingI *) dptr; - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%d %d %d %d", rect->top, rect->bottom, + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%d %d %d %d", rect->top, rect->bottom, rect->left, rect->right); return returnBuffer; } diff --git a/Engine/source/gui/editor/guiDebugger.cpp b/Engine/source/gui/editor/guiDebugger.cpp index dbebc4e83..b2640809d 100644 --- a/Engine/source/gui/editor/guiDebugger.cpp +++ b/Engine/source/gui/editor/guiDebugger.cpp @@ -78,8 +78,9 @@ ConsoleMethod(DbgFileView, getCurrentLine, const char *, 2, 2, "()" { S32 lineNum; const char *file = object->getCurrentLine(lineNum); - char* ret = Con::getReturnBuffer(256); - dSprintf(ret, 256, "%s\t%d", file, lineNum); + static const U32 bufSize = 256; + char* ret = Con::getReturnBuffer(bufSize); + dSprintf(ret, bufSize, "%s\t%d", file, lineNum); return ret; } diff --git a/Engine/source/gui/editor/guiParticleGraphCtrl.cpp b/Engine/source/gui/editor/guiParticleGraphCtrl.cpp index 2bfbbf467..7dfc5a7eb 100644 --- a/Engine/source/gui/editor/guiParticleGraphCtrl.cpp +++ b/Engine/source/gui/editor/guiParticleGraphCtrl.cpp @@ -1060,12 +1060,13 @@ ConsoleMethod(GuiParticleGraphCtrl, addPlotPoint, const char*, 5, 6, "(int plotI { S32 plotID = dAtoi(argv[2]); S32 pointAdded = 0; - char *retBuffer = Con::getReturnBuffer(32); + static const U32 bufSize = 32; + char *retBuffer = Con::getReturnBuffer(bufSize); if(plotID > object->MaxPlots) { Con::errorf("Invalid plotID."); - dSprintf(retBuffer, 32, "%d", -2); + dSprintf(retBuffer, bufSize, "%d", -2); return retBuffer; } @@ -1078,7 +1079,7 @@ ConsoleMethod(GuiParticleGraphCtrl, addPlotPoint, const char*, 5, 6, "(int plotI } - dSprintf(retBuffer, 32, "%d", pointAdded); + dSprintf(retBuffer, bufSize, "%d", pointAdded); return retBuffer; } @@ -1107,19 +1108,20 @@ ConsoleMethod(GuiParticleGraphCtrl, changePlotPoint, const char*, 6, 6, "(int pl "@return No return value.") { S32 plotID = dAtoi(argv[2]); + static const U32 bufSize = 64; if(plotID > object->MaxPlots) { Con::errorf("Invalid plotID."); - char *retBuffer = Con::getReturnBuffer(64); + char *retBuffer = Con::getReturnBuffer(bufSize); const S32 index = -1; - dSprintf(retBuffer, 64, "%d", index); + dSprintf(retBuffer, bufSize, "%d", index); return retBuffer; } - char *retBuffer = Con::getReturnBuffer(64); + char *retBuffer = Con::getReturnBuffer(bufSize); const S32 index = object->changePlotPoint( plotID, dAtoi(argv[3]), Point2F(dAtof(argv[4]), dAtof(argv[5]))); - dSprintf(retBuffer, 64, "%d", index); + dSprintf(retBuffer, bufSize, "%d", index); return retBuffer; } @@ -1127,9 +1129,10 @@ ConsoleMethod(GuiParticleGraphCtrl, getSelectedPlot, const char*, 2, 2, "() " "Gets the selected Plot (a.k.a. graph).\n" "@return The plot's ID.") { - char *retBuffer = Con::getReturnBuffer(32); + static const U32 bufSize = 32; + char *retBuffer = Con::getReturnBuffer(bufSize); const S32 plot = object->getSelectedPlot(); - dSprintf(retBuffer, 32, "%d", plot); + dSprintf(retBuffer, bufSize, "%d", plot); return retBuffer; } @@ -1137,9 +1140,10 @@ ConsoleMethod(GuiParticleGraphCtrl, getSelectedPoint, const char*, 2, 2, "()" "Gets the selected Point on the Plot (a.k.a. graph)." "@return The last selected point ID") { - char *retBuffer = Con::getReturnBuffer(32); + static const U32 bufSize = 32; + char *retBuffer = Con::getReturnBuffer(bufSize); const S32 point = object->getSelectedPoint(); - dSprintf(retBuffer, 32, "%d", point); + dSprintf(retBuffer, bufSize, "%d", point); return retBuffer; } @@ -1158,9 +1162,10 @@ ConsoleMethod(GuiParticleGraphCtrl, isExistingPoint, const char*, 4, 4, "(int pl Con::errorf("Invalid sample."); } - char *retBuffer = Con::getReturnBuffer(32); + static const U32 bufSize = 32; + char *retBuffer = Con::getReturnBuffer(bufSize); const bool isPoint = object->isExistingPoint(plotID, samples); - dSprintf(retBuffer, 32, "%d", isPoint); + dSprintf(retBuffer, bufSize, "%d", isPoint); return retBuffer; } @@ -1180,9 +1185,10 @@ ConsoleMethod(GuiParticleGraphCtrl, getPlotPoint, const char*, 4, 4, "(int plotI Con::errorf("Invalid sample."); } - char *retBuffer = Con::getReturnBuffer(64); + static const U32 bufSize = 64; + char *retBuffer = Con::getReturnBuffer(bufSize); const Point2F &pos = object->getPlotPoint(plotID, samples); - dSprintf(retBuffer, 64, "%f %f", pos.x, pos.y); + dSprintf(retBuffer, bufSize, "%f %f", pos.x, pos.y); return retBuffer; } @@ -1201,9 +1207,10 @@ ConsoleMethod(GuiParticleGraphCtrl, getPlotIndex, const char*, 5, 5, "(int plotI Con::errorf("Invalid plotID."); } - char *retBuffer = Con::getReturnBuffer(32); + static const U32 bufSize = 32; + char *retBuffer = Con::getReturnBuffer(bufSize); const S32 &index = object->getPlotIndex(plotID, x, y); - dSprintf(retBuffer, 32, "%d", index); + dSprintf(retBuffer, bufSize, "%d", index); return retBuffer; } @@ -1218,9 +1225,10 @@ ConsoleMethod(GuiParticleGraphCtrl, getGraphColor, const char*, 3, 3, "(int plot Con::errorf("Invalid plotID."); } - char *retBuffer = Con::getReturnBuffer(64); + static const U32 bufSize = 64; + char *retBuffer = Con::getReturnBuffer(bufSize); const ColorF &color = object->getGraphColor(plotID); - dSprintf(retBuffer, 64, "%f %f %f", color.red, color.green, color.blue); + dSprintf(retBuffer, bufSize, "%f %f %f", color.red, color.green, color.blue); return retBuffer; } @@ -1235,9 +1243,10 @@ ConsoleMethod(GuiParticleGraphCtrl, getGraphMin, const char*, 3, 3, "(int plotID Con::errorf("Invalid plotID."); } - char *retBuffer = Con::getReturnBuffer(64); + static const U32 bufSize = 64; + char *retBuffer = Con::getReturnBuffer(bufSize); const Point2F graphMin = object->getGraphMin(plotID); - dSprintf(retBuffer, 64, "%f %f", graphMin.x, graphMin.y); + dSprintf(retBuffer, bufSize, "%f %f", graphMin.x, graphMin.y); return retBuffer; } @@ -1252,9 +1261,10 @@ ConsoleMethod(GuiParticleGraphCtrl, getGraphMax, const char*, 3, 3, "(int plotID Con::errorf("Invalid plotID."); } - char *retBuffer = Con::getReturnBuffer(64); + static const U32 bufSize = 64; + char *retBuffer = Con::getReturnBuffer(bufSize); const Point2F graphMax = object->getGraphMax(plotID); - dSprintf(retBuffer, 64, "%f %f", graphMax.x, graphMax.y); + dSprintf(retBuffer, bufSize, "%f %f", graphMax.x, graphMax.y); return retBuffer; } @@ -1269,9 +1279,10 @@ ConsoleMethod(GuiParticleGraphCtrl, getGraphName, const char*, 3, 3, "(int plotI Con::errorf("Invalid plotID."); } - char *retBuffer = Con::getReturnBuffer(64); + static const U32 bufSize = 64; + char *retBuffer = Con::getReturnBuffer(bufSize); const StringTableEntry graphName = object->getGraphName(plotID); - dSprintf(retBuffer, 64, "%s", graphName); + dSprintf(retBuffer, bufSize, "%s", graphName); return retBuffer; } diff --git a/Engine/source/gui/game/guiProgressBitmapCtrl.cpp b/Engine/source/gui/game/guiProgressBitmapCtrl.cpp index bbdf20316..241270fd2 100644 --- a/Engine/source/gui/game/guiProgressBitmapCtrl.cpp +++ b/Engine/source/gui/game/guiProgressBitmapCtrl.cpp @@ -158,8 +158,9 @@ void GuiProgressBitmapCtrl::setBitmap( const char* name ) const char* GuiProgressBitmapCtrl::getScriptValue() { - char * ret = Con::getReturnBuffer(64); - dSprintf(ret, 64, "%g", mProgress); + static const U32 bufSize = 64; + char * ret = Con::getReturnBuffer(bufSize); + dSprintf(ret, bufSize, "%g", mProgress); return ret; } diff --git a/Engine/source/gui/game/guiProgressCtrl.cpp b/Engine/source/gui/game/guiProgressCtrl.cpp index 9b3901f84..06fee2502 100644 --- a/Engine/source/gui/game/guiProgressCtrl.cpp +++ b/Engine/source/gui/game/guiProgressCtrl.cpp @@ -59,8 +59,9 @@ GuiProgressCtrl::GuiProgressCtrl() const char* GuiProgressCtrl::getScriptValue() { - char * ret = Con::getReturnBuffer(64); - dSprintf(ret, 64, "%g", mProgress); + static const U32 bufSize = 64; + char * ret = Con::getReturnBuffer(bufSize); + dSprintf(ret, bufSize, "%g", mProgress); return ret; } diff --git a/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp b/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp index 93192a155..532b7cbf7 100644 --- a/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp +++ b/Engine/source/gui/worldEditor/guiDecalEditorCtrl.cpp @@ -821,12 +821,13 @@ ConsoleMethod( GuiDecalEditorCtrl, getDecalTransform, const char*, 3, 3, "getDec if( decalInstance == NULL ) return ""; - char* returnBuffer = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); returnBuffer[0] = 0; if ( decalInstance ) { - dSprintf(returnBuffer, 256, "%f %f %f %f %f %f %f", + dSprintf(returnBuffer, bufSize, "%f %f %f %f %f %f %f", decalInstance->mPosition.x, decalInstance->mPosition.y, decalInstance->mPosition.z, decalInstance->mTangent.x, decalInstance->mTangent.y, decalInstance->mTangent.z, decalInstance->mSize); diff --git a/Engine/source/gui/worldEditor/terrainEditor.cpp b/Engine/source/gui/worldEditor/terrainEditor.cpp index 3aa17e1be..1db3a5337 100644 --- a/Engine/source/gui/worldEditor/terrainEditor.cpp +++ b/Engine/source/gui/worldEditor/terrainEditor.cpp @@ -2111,8 +2111,9 @@ const char* TerrainEditor::getBrushPos() AssertFatal(mMouseBrush!=NULL, "TerrainEditor::getBrushPos: no mouse brush!"); Point2I pos = mMouseBrush->getPosition(); - char * ret = Con::getReturnBuffer(32); - dSprintf(ret, 32, "%d %d", pos.x, pos.y); + static const U32 bufSize = 32; + char * ret = Con::getReturnBuffer(bufSize); + dSprintf(ret, bufSize, "%d %d", pos.x, pos.y); return(ret); } @@ -2521,8 +2522,9 @@ ConsoleMethod( TerrainEditor, getBrushSize, const char*, 2, 2, "()") { Point2I size = object->getBrushSize(); - char * ret = Con::getReturnBuffer(32); - dSprintf(ret, 32, "%d %d", size.x, size.y); + static const U32 bufSize = 32; + char * ret = Con::getReturnBuffer(bufSize); + dSprintf(ret, bufSize, "%d %d", size.x, size.y); return ret; } diff --git a/Engine/source/gui/worldEditor/worldEditor.cpp b/Engine/source/gui/worldEditor/worldEditor.cpp index 338bb3c63..a51ff596a 100644 --- a/Engine/source/gui/worldEditor/worldEditor.cpp +++ b/Engine/source/gui/worldEditor/worldEditor.cpp @@ -2882,8 +2882,9 @@ const Point3F& WorldEditor::getSelectionCentroid() const char* WorldEditor::getSelectionCentroidText() { const Point3F & centroid = getSelectionCentroid(); - char * ret = Con::getReturnBuffer(100); - dSprintf(ret, 100, "%g %g %g", centroid.x, centroid.y, centroid.z); + static const U32 bufSize = 100; + char * ret = Con::getReturnBuffer(bufSize); + dSprintf(ret, bufSize, "%g %g %g", centroid.x, centroid.y, centroid.z); return ret; } @@ -3263,8 +3264,9 @@ ConsoleMethod( WorldEditor, getSelectionCentroid, const char *, 2, 2, "") ConsoleMethod( WorldEditor, getSelectionExtent, const char *, 2, 2, "") { Point3F bounds = object->getSelectionExtent(); - char * ret = Con::getReturnBuffer(100); - dSprintf(ret, 100, "%g %g %g", bounds.x, bounds.y, bounds.z); + static const U32 bufSize = 100; + char * ret = Con::getReturnBuffer(bufSize); + dSprintf(ret, bufSize, "%g %g %g", bounds.x, bounds.y, bounds.z); return ret; } diff --git a/Engine/source/gui/worldEditor/worldEditorSelection.cpp b/Engine/source/gui/worldEditor/worldEditorSelection.cpp index ed2e1f5a3..41f82122f 100644 --- a/Engine/source/gui/worldEditor/worldEditorSelection.cpp +++ b/Engine/source/gui/worldEditor/worldEditorSelection.cpp @@ -642,10 +642,11 @@ ConsoleMethod( WorldEditorSelection, containsGlobalBounds, bool, 2, 2, "() - Tru ConsoleMethod( WorldEditorSelection, getCentroid, const char*, 2, 2, "() - Return the median of all object positions in the selection." ) { - char* buffer = Con::getReturnBuffer( 256 ); + static const U32 bufSize = 256; + char* buffer = Con::getReturnBuffer( bufSize ); const Point3F& centroid = object->getCentroid(); - dSprintf( buffer, 256, "%g %g %g", centroid.x, centroid.y, centroid.z ); + dSprintf( buffer, bufSize, "%g %g %g", centroid.x, centroid.y, centroid.z ); return buffer; } @@ -653,10 +654,11 @@ ConsoleMethod( WorldEditorSelection, getCentroid, const char*, 2, 2, "() - Retur ConsoleMethod( WorldEditorSelection, getBoxCentroid, const char*, 2, 2, "() - Return the center of the bounding box around the selection." ) { - char* buffer = Con::getReturnBuffer( 256 ); + static const U32 bufSize = 256; + char* buffer = Con::getReturnBuffer( bufSize ); const Point3F& boxCentroid = object->getBoxCentroid(); - dSprintf( buffer, 256, "%g %g %g", boxCentroid.x, boxCentroid.y, boxCentroid.z ); + dSprintf( buffer, bufSize, "%g %g %g", boxCentroid.x, boxCentroid.y, boxCentroid.z ); return buffer; } diff --git a/Engine/source/lighting/advanced/advancedLightManager.cpp b/Engine/source/lighting/advanced/advancedLightManager.cpp index 7034545d7..96a4b926c 100644 --- a/Engine/source/lighting/advanced/advancedLightManager.cpp +++ b/Engine/source/lighting/advanced/advancedLightManager.cpp @@ -664,8 +664,9 @@ ConsoleFunction( setShadowVizLight, const char*, 2, 2, "" ) const Point3I &size = texObject->getSize(); F32 aspect = (F32)size.x / (F32)size.y; - char *result = Con::getReturnBuffer( 64 ); - dSprintf( result, 64, "%d %d %g", size.x, size.y, aspect ); + static const U32 bufSize = 64; + char *result = Con::getReturnBuffer( bufSize ); + dSprintf( result, bufSize, "%d %d %g", size.x, size.y, aspect ); return result; } diff --git a/Engine/source/math/mConsoleFunctions.cpp b/Engine/source/math/mConsoleFunctions.cpp index 0bb171fad..89d8bd6e2 100644 --- a/Engine/source/math/mConsoleFunctions.cpp +++ b/Engine/source/math/mConsoleFunctions.cpp @@ -41,8 +41,9 @@ DefineConsoleFunction( mSolveQuadratic, const char*, ( F32 a, F32 b, F32 c ),, F32 x[2]; U32 sol = mSolveQuadratic( a, b, c, x ); - char * retBuffer = Con::getReturnBuffer(256); - dSprintf(retBuffer, 256, "%d %g %g", sol, x[0], x[1]); + static const U32 bufSize = 256; + char * retBuffer = Con::getReturnBuffer(bufSize); + dSprintf(retBuffer, bufSize, "%d %g %g", sol, x[0], x[1]); return retBuffer; } @@ -59,8 +60,9 @@ DefineConsoleFunction( mSolveCubic, const char*, ( F32 a, F32 b, F32 c, F32 d ), F32 x[3]; U32 sol = mSolveCubic( a, b, c, d, x ); - char * retBuffer = Con::getReturnBuffer(256); - dSprintf(retBuffer, 256, "%d %g %g %g", sol, x[0], x[1], x[2]); + static const U32 bufSize = 256; + char * retBuffer = Con::getReturnBuffer(bufSize); + dSprintf(retBuffer, bufSize, "%d %g %g %g", sol, x[0], x[1], x[2]); return retBuffer; } @@ -76,9 +78,10 @@ DefineConsoleFunction( mSolveQuartic, const char*, ( F32 a, F32 b, F32 c, F32 d, "@ingroup Math" ) { F32 x[4]; - char * retBuffer = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char * retBuffer = Con::getReturnBuffer(bufSize); U32 sol = mSolveQuartic(a, b, c, d, e, x); - dSprintf(retBuffer, 256, "%d %g %g %g %g", sol, x[0], x[1], x[2], x[3]); + dSprintf(retBuffer, bufSize, "%d %g %g %g %g", sol, x[0], x[1], x[2], x[3]); return retBuffer; } @@ -121,8 +124,9 @@ DefineConsoleFunction( mFloatLength, const char*, ( F32 v, U32 precision ),, precision = 9; fmtString[2] = '0' + precision; - char * outBuffer = Con::getReturnBuffer(256); - dSprintf(outBuffer, 255, fmtString, v); + static const U32 bufSize = 256; + char * outBuffer = Con::getReturnBuffer(bufSize); + dSprintf(outBuffer, bufSize, fmtString, v); return outBuffer; } diff --git a/Engine/source/math/mathTypes.cpp b/Engine/source/math/mathTypes.cpp index cbe123452..7afa3ab2b 100644 --- a/Engine/source/math/mathTypes.cpp +++ b/Engine/source/math/mathTypes.cpp @@ -123,8 +123,9 @@ ImplementConsoleTypeCasters( TypePoint2I, Point2I ) ConsoleGetType( TypePoint2I ) { Point2I *pt = (Point2I *) dptr; - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%d %d", pt->x, pt->y); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%d %d", pt->x, pt->y); return returnBuffer; } @@ -147,8 +148,9 @@ ImplementConsoleTypeCasters( TypePoint2F, Point2F ) ConsoleGetType( TypePoint2F ) { Point2F *pt = (Point2F *) dptr; - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%g %g", pt->x, pt->y); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%g %g", pt->x, pt->y); return returnBuffer; } @@ -171,8 +173,9 @@ ImplementConsoleTypeCasters(TypePoint3I, Point3I) ConsoleGetType( TypePoint3I ) { Point3I *pt = (Point3I *) dptr; - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%d %d %d", pt->x, pt->y, pt->z); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%d %d %d", pt->x, pt->y, pt->z); return returnBuffer; } @@ -195,8 +198,9 @@ ImplementConsoleTypeCasters(TypePoint3F, Point3F) ConsoleGetType( TypePoint3F ) { Point3F *pt = (Point3F *) dptr; - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%g %g %g", pt->x, pt->y, pt->z); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%g %g %g", pt->x, pt->y, pt->z); return returnBuffer; } @@ -219,8 +223,9 @@ ImplementConsoleTypeCasters( TypePoint4F, Point4F ) ConsoleGetType( TypePoint4F ) { Point4F *pt = (Point4F *) dptr; - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%g %g %g %g", pt->x, pt->y, pt->z, pt->w); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%g %g %g %g", pt->x, pt->y, pt->z, pt->w); return returnBuffer; } @@ -243,8 +248,9 @@ ImplementConsoleTypeCasters( TypeRectI, RectI ) ConsoleGetType( TypeRectI ) { RectI *rect = (RectI *) dptr; - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%d %d %d %d", rect->point.x, rect->point.y, + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%d %d %d %d", rect->point.x, rect->point.y, rect->extent.x, rect->extent.y); return returnBuffer; } @@ -269,8 +275,9 @@ ImplementConsoleTypeCasters( TypeRectF, RectF ) ConsoleGetType( TypeRectF ) { RectF *rect = (RectF *) dptr; - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%g %g %g %g", rect->point.x, rect->point.y, + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%g %g %g %g", rect->point.x, rect->point.y, rect->extent.x, rect->extent.y); return returnBuffer; } @@ -303,8 +310,9 @@ ConsoleGetType( TypeMatrixF ) mat->getColumn(0, &col0); mat->getColumn(1, &col1); mat->getColumn(2, &col2); - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer,256,"%g %g %g %g %g %g %g %g %g", + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer,bufSize,"%g %g %g %g %g %g %g %g %g", col0.x, col0.y, col0.z, col1.x, col1.y, col1.z, col2.x, col2.y, col2.z); return returnBuffer; } @@ -336,11 +344,12 @@ ConsoleType( MatrixPosition, TypeMatrixPosition, MatrixF ) ConsoleGetType( TypeMatrixPosition ) { F32 *col = (F32 *) dptr + 3; - char* returnBuffer = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); if(col[12] == 1.f) - dSprintf(returnBuffer, 256, "%g %g %g", col[0], col[4], col[8]); + dSprintf(returnBuffer, bufSize, "%g %g %g", col[0], col[4], col[8]); else - dSprintf(returnBuffer, 256, "%g %g %g %g", col[0], col[4], col[8], col[12]); + dSprintf(returnBuffer, bufSize, "%g %g %g %g", col[0], col[4], col[8], col[12]); return returnBuffer; } @@ -371,8 +380,9 @@ ConsoleGetType( TypeMatrixRotation ) { AngAxisF aa(*(MatrixF *) dptr); aa.axis.normalize(); - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer,256,"%g %g %g %g",aa.axis.x,aa.axis.y,aa.axis.z,mRadToDeg(aa.angle)); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer,bufSize,"%g %g %g %g",aa.axis.x,aa.axis.y,aa.axis.z,mRadToDeg(aa.angle)); return returnBuffer; } @@ -415,8 +425,9 @@ ImplementConsoleTypeCasters( TypeAngAxisF, AngAxisF ) ConsoleGetType( TypeAngAxisF ) { AngAxisF* aa = ( AngAxisF* ) dptr; - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer,256,"%g %g %g %g",aa->axis.x,aa->axis.y,aa->axis.z,mRadToDeg(aa->angle)); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer,bufSize,"%g %g %g %g",aa->axis.x,aa->axis.y,aa->axis.z,mRadToDeg(aa->angle)); return returnBuffer; } @@ -453,8 +464,9 @@ ImplementConsoleTypeCasters( TypeTransformF, TransformF ) ConsoleGetType( TypeTransformF ) { TransformF* aa = ( TransformF* ) dptr; - char* returnBuffer = Con::getReturnBuffer( 256 ); - dSprintf( returnBuffer, 256, "%g %g %g %g %g %g %g", + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf( returnBuffer, bufSize, "%g %g %g %g %g %g %g", aa->mPosition.x, aa->mPosition.y, aa->mPosition.z, aa->mOrientation.axis.x, aa->mOrientation.axis.y, aa->mOrientation.axis.z, aa->mOrientation.angle ); return returnBuffer; @@ -497,8 +509,9 @@ ConsoleGetType( TypeBox3F ) { const Box3F* pBox = (const Box3F*)dptr; - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%g %g %g %g %g %g", + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%g %g %g %g %g %g", pBox->minExtents.x, pBox->minExtents.y, pBox->minExtents.z, pBox->maxExtents.x, pBox->maxExtents.y, pBox->maxExtents.z); @@ -533,8 +546,9 @@ ConsoleGetType( TypeEaseF ) { const EaseF* pEase = (const EaseF*)dptr; - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%d %d %g %g", + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%d %d %g %g", pEase->dir, pEase->type, pEase->param[0], pEase->param[1]); return returnBuffer; diff --git a/Engine/source/scene/sceneContainer.cpp b/Engine/source/scene/sceneContainer.cpp index 7270eaa1b..5d81cc0bd 100644 --- a/Engine/source/scene/sceneContainer.cpp +++ b/Engine/source/scene/sceneContainer.cpp @@ -1631,10 +1631,11 @@ DefineEngineFunction( containerRayCast, const char*, pExempt->enableCollision(); // add the hit position and normal? - char *returnBuffer = Con::getReturnBuffer(256); + static const U32 bufSize = 256; + char *returnBuffer = Con::getReturnBuffer(bufSize); if(ret) { - dSprintf(returnBuffer, 256, "%d %g %g %g %g %g %g %g", + dSprintf(returnBuffer, bufSize, "%d %g %g %g %g %g %g %g", ret, rinfo.point.x, rinfo.point.y, rinfo.point.z, rinfo.normal.x, rinfo.normal.y, rinfo.normal.z, rinfo.distance); } diff --git a/Engine/source/ts/tsShapeConstruct.cpp b/Engine/source/ts/tsShapeConstruct.cpp index aa23e22cf..da93d9691 100644 --- a/Engine/source/ts/tsShapeConstruct.cpp +++ b/Engine/source/ts/tsShapeConstruct.cpp @@ -1216,8 +1216,9 @@ DefineTSShapeConstructorMethod( getMeshName, const char*, ( const char* name, S3 CHECK_INDEX_IN_RANGE( getMeshName, index, objectDetails.size(), "" ); - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf(returnBuffer, 256, "%s %d", name, (S32)mShape->details[objectDetails[index]].size); + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%s %d", name, (S32)mShape->details[objectDetails[index]].size); return returnBuffer; }} @@ -1602,8 +1603,9 @@ DefineTSShapeConstructorMethod( getImposterSettings, const char*, ( S32 index ), // Return information about the detail level const TSShape::Detail& det = mShape->details[index]; - char* returnBuffer = Con::getReturnBuffer(512); - dSprintf(returnBuffer, 512, "%d\t%d\t%d\t%d\t%d\t%d\t%g", + static const U32 bufSize = 512; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%d\t%d\t%d\t%d\t%d\t%d\t%g", (S32)( det.subShapeNum < 0 ), // isImposter det.bbEquatorSteps, det.bbPolarSteps, @@ -1727,8 +1729,9 @@ DefineTSShapeConstructorMethod( getSequenceSource, const char*, ( const char* na GET_SEQUENCE( getSequenceSource, seq, name, "" ); // Return information about the source data for this sequence - char* returnBuffer = Con::getReturnBuffer(512); - dSprintf( returnBuffer, 512, "%s\t%d\t%d\t%d", + static const U32 bufSize = 512; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf( returnBuffer, bufSize, "%s\t%d\t%d\t%d", seq->sourceData.from.c_str(), seq->sourceData.start, seq->sourceData.end, seq->sourceData.total ); return returnBuffer; @@ -1802,8 +1805,9 @@ DefineTSShapeConstructorMethod( getSequenceGroundSpeed, const char*, ( const cha rot = mat.toEuler(); } - char* returnBuffer = Con::getReturnBuffer(256); - dSprintf( returnBuffer, 256, "%g %g %g %g %g %g", + static const U32 bufSize = 256; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf( returnBuffer, bufSize, "%g %g %g %g %g %g", trans.x, trans.y, trans.z, rot.x, rot.y, rot.z ); return returnBuffer; }} @@ -1896,8 +1900,9 @@ DefineTSShapeConstructorMethod( getSequenceBlend, const char*, ( const char* nam GET_SEQUENCE( getSequenceBlend, seq, name, "0" ); // Return the blend information (flag reference_sequence reference_frame) - char* returnBuffer = Con::getReturnBuffer(512); - dSprintf( returnBuffer, 512, "%d\t%s\t%d", (int)seq->isBlend(), + static const U32 bufSize = 512; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf( returnBuffer, bufSize, "%d\t%s\t%d", (int)seq->isBlend(), seq->sourceData.blendSeq.c_str(), seq->sourceData.blendFrame ); return returnBuffer; }} @@ -2038,8 +2043,9 @@ DefineTSShapeConstructorMethod( getTrigger, const char*, ( const char* name, S32 if (!(trig.state & TSShape::Trigger::StateOn)) state = -state; - char* returnBuffer = Con::getReturnBuffer(32); - dSprintf(returnBuffer, 32, "%d %d", frame, state); + static const U32 bufSize = 32; + char* returnBuffer = Con::getReturnBuffer(bufSize); + dSprintf(returnBuffer, bufSize, "%d %d", frame, state); return returnBuffer; }} From 33444e8a367590275ec6fc4bb36371b0ff4a83c0 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Mon, 5 May 2014 22:13:25 +1000 Subject: [PATCH 085/317] Added string functions from T2D. --- .../source/core/strings/stringFunctions.cpp | 29 +++++++++++++++++++ Engine/source/core/strings/stringFunctions.h | 3 ++ 2 files changed, 32 insertions(+) diff --git a/Engine/source/core/strings/stringFunctions.cpp b/Engine/source/core/strings/stringFunctions.cpp index 63377cfd4..476edf71b 100644 --- a/Engine/source/core/strings/stringFunctions.cpp +++ b/Engine/source/core/strings/stringFunctions.cpp @@ -532,3 +532,32 @@ const char* dStristr( const char* str1, const char* str2 ) { return dStristr( const_cast< char* >( str1 ), str2 ); } + +int dStrrev(char* str) +{ + int l=dStrlen(str)-1; //get the string length + for(int x=0;x < l;x++,l--) + { + str[x]^=str[l]; //triple XOR Trick + str[l]^=str[x]; //for not using a temp + str[x]^=str[l]; + } + return l; +} + +int dItoa(int n, char s[]) +{ + int i, sign; + + if ((sign = n) < 0) /* record sign */ + n = -n; /* make n positive */ + i = 0; + do { /* generate digits in reverse order */ + s[i++] = n % 10 + '0'; /* get next digit */ + } while ((n /= 10) > 0); /* delete it */ + if (sign < 0) + s[i++] = '-'; + s[i] = '\0'; + dStrrev(s); + return dStrlen(s); +} diff --git a/Engine/source/core/strings/stringFunctions.h b/Engine/source/core/strings/stringFunctions.h index e211454d0..92602fd21 100644 --- a/Engine/source/core/strings/stringFunctions.h +++ b/Engine/source/core/strings/stringFunctions.h @@ -217,6 +217,9 @@ bool dStrEndsWith(const char* str1, const char* str2); char* dStripPath(const char* filename); +int dStrrev(char* str); +int dItoa(int n, char s[]); + //------------------------------------------------------------------------------ // standard I/O functions [defined in platformString.cpp] From b94679aebc73567e341bc972b52cb7d0ea79f1e9 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 17 Jun 2014 16:01:25 +1000 Subject: [PATCH 086/317] Prevent call to dStrlen(NULL). --- Engine/source/gui/controls/guiGameListMenuCtrl.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/gui/controls/guiGameListMenuCtrl.cpp b/Engine/source/gui/controls/guiGameListMenuCtrl.cpp index d5cc8813d..fb6931d50 100644 --- a/Engine/source/gui/controls/guiGameListMenuCtrl.cpp +++ b/Engine/source/gui/controls/guiGameListMenuCtrl.cpp @@ -736,7 +736,7 @@ bool GuiGameListMenuProfile::onAdd() // We can't call enforceConstraints() here because incRefCount initializes // some of the things to enforce. Do a basic sanity check here instead. - if( !dStrlen(mBitmapName) ) + if( !mBitmapName || !dStrlen(mBitmapName) ) { Con::errorf( "GuiGameListMenuProfile: %s can't be created without a bitmap. Please add a 'Bitmap' property to the object definition.", getName() ); return false; From e442b87c0c4d6f9dd13d4156a3867aae8e6eb96c Mon Sep 17 00:00:00 2001 From: bank Date: Wed, 18 Jun 2014 11:33:38 +0400 Subject: [PATCH 087/317] Fix: Null'ed pointer usage, possible access violation. This fixes issue #700 --- Engine/source/T3D/debris.cpp | 6 +----- Engine/source/T3D/fx/explosion.cpp | 5 +---- Engine/source/T3D/fx/splash.cpp | 6 +----- 3 files changed, 3 insertions(+), 14 deletions(-) diff --git a/Engine/source/T3D/debris.cpp b/Engine/source/T3D/debris.cpp index 495b5dbc2..77d5fc603 100644 --- a/Engine/source/T3D/debris.cpp +++ b/Engine/source/T3D/debris.cpp @@ -659,11 +659,7 @@ void Debris::onRemove() } } - if( getSceneManager() ) - getSceneManager()->removeObjectFromScene(this); - - if( getContainer() ) - getContainer()->removeObject(this); + removeFromScene(); Parent::onRemove(); } diff --git a/Engine/source/T3D/fx/explosion.cpp b/Engine/source/T3D/fx/explosion.cpp index dd81dcba8..f23400232 100644 --- a/Engine/source/T3D/fx/explosion.cpp +++ b/Engine/source/T3D/fx/explosion.cpp @@ -964,10 +964,7 @@ void Explosion::onRemove() mMainEmitter = NULL; } - if (getSceneManager() != NULL) - getSceneManager()->removeObjectFromScene(this); - if (getContainer() != NULL) - getContainer()->removeObject(this); + removeFromScene(); Parent::onRemove(); } diff --git a/Engine/source/T3D/fx/splash.cpp b/Engine/source/T3D/fx/splash.cpp index 1558f2dbc..0e9a2c6f4 100644 --- a/Engine/source/T3D/fx/splash.cpp +++ b/Engine/source/T3D/fx/splash.cpp @@ -415,11 +415,7 @@ void Splash::onRemove() ringList.clear(); - if( getSceneManager() ) - getSceneManager()->removeObjectFromScene(this); - - if( getContainer() ) - getContainer()->removeObject(this); + removeFromScene(); Parent::onRemove(); } From ebbd8a2e0209e8c18de51c620bc8a073b48d8a56 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Fri, 20 Jun 2014 14:45:12 -0500 Subject: [PATCH 088/317] Instancing clones the results of a previously used material to it's next instance. As such, it and Dynamic Cube Mapping are mutually exclusive features. --- Engine/source/materials/processedShaderMaterial.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Engine/source/materials/processedShaderMaterial.cpp b/Engine/source/materials/processedShaderMaterial.cpp index 410773d7e..1b17c17ce 100644 --- a/Engine/source/materials/processedShaderMaterial.cpp +++ b/Engine/source/materials/processedShaderMaterial.cpp @@ -328,6 +328,7 @@ void ProcessedShaderMaterial::_determineFeatures( U32 stageNum, if ( features.hasFeature( MFT_UseInstancing ) && mMaxStages == 1 && !mMaterial->mGlow[0] && + !mMaterial->mDynamicCubemap && shaderVersion >= 3.0f ) fd.features.addFeature( MFT_UseInstancing ); From b661abaf838b6b1e3b2f635a23ffffec7711c80b Mon Sep 17 00:00:00 2001 From: Andrew Mac Date: Mon, 23 Jun 2014 14:58:06 -0300 Subject: [PATCH 089/317] Fix for unexpected behavior described in issue #704 --- Engine/source/console/compiledEval.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/Engine/source/console/compiledEval.cpp b/Engine/source/console/compiledEval.cpp index 37f621997..da1aa9f20 100644 --- a/Engine/source/console/compiledEval.cpp +++ b/Engine/source/console/compiledEval.cpp @@ -1572,6 +1572,7 @@ breakContinue: Con::warnf(ConsoleLogEntry::General,"%s: Unable to find object: '%s' attempting to call function '%s'", getFileLine(ip-4), callArgv[1], fnName); STR.popFrame(); + STR.setStringValue(""); break; } From 551a7b38204fa1a92087d6a56561b83c60e8767e Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Wed, 23 Apr 2014 09:54:04 +1000 Subject: [PATCH 090/317] Added Google test library. --- Engine/lib/gtest/CONTRIBUTORS | 37 + Engine/lib/gtest/LICENSE | 28 + Engine/lib/gtest/README | 435 + Engine/lib/gtest/fused-src/gtest/gtest-all.cc | 9592 ++++++++ Engine/lib/gtest/fused-src/gtest/gtest.h | 20061 ++++++++++++++++ .../lib/gtest/fused-src/gtest/gtest_main.cc | 38 + Engine/source/testing/unitTesting.cpp | 102 + Engine/source/testing/unitTesting.h | 32 + Tools/projectGenerator/libs/libgtest.conf | 34 + Tools/projectGenerator/modules/testing.inc | 35 + 10 files changed, 30394 insertions(+) create mode 100644 Engine/lib/gtest/CONTRIBUTORS create mode 100644 Engine/lib/gtest/LICENSE create mode 100644 Engine/lib/gtest/README create mode 100644 Engine/lib/gtest/fused-src/gtest/gtest-all.cc create mode 100644 Engine/lib/gtest/fused-src/gtest/gtest.h create mode 100644 Engine/lib/gtest/fused-src/gtest/gtest_main.cc create mode 100644 Engine/source/testing/unitTesting.cpp create mode 100644 Engine/source/testing/unitTesting.h create mode 100644 Tools/projectGenerator/libs/libgtest.conf create mode 100644 Tools/projectGenerator/modules/testing.inc diff --git a/Engine/lib/gtest/CONTRIBUTORS b/Engine/lib/gtest/CONTRIBUTORS new file mode 100644 index 000000000..feae2fc04 --- /dev/null +++ b/Engine/lib/gtest/CONTRIBUTORS @@ -0,0 +1,37 @@ +# This file contains a list of people who've made non-trivial +# contribution to the Google C++ Testing Framework project. People +# who commit code to the project are encouraged to add their names +# here. Please keep the list sorted by first names. + +Ajay Joshi +Balázs Dán +Bharat Mediratta +Chandler Carruth +Chris Prince +Chris Taylor +Dan Egnor +Eric Roman +Hady Zalek +Jeffrey Yasskin +Jói Sigurðsson +Keir Mierle +Keith Ray +Kenton Varda +Manuel Klimek +Markus Heule +Mika Raento +Miklós Fazekas +Pasi Valminen +Patrick Hanna +Patrick Riley +Peter Kaminski +Preston Jackson +Rainer Klaffenboeck +Russ Cox +Russ Rufer +Sean Mcafee +Sigurður Ásgeirsson +Tracy Bialik +Vadim Berman +Vlad Losev +Zhanyong Wan diff --git a/Engine/lib/gtest/LICENSE b/Engine/lib/gtest/LICENSE new file mode 100644 index 000000000..1941a11f8 --- /dev/null +++ b/Engine/lib/gtest/LICENSE @@ -0,0 +1,28 @@ +Copyright 2008, Google Inc. +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright +notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above +copyright notice, this list of conditions and the following disclaimer +in the documentation and/or other materials provided with the +distribution. + * Neither the name of Google Inc. nor the names of its +contributors may be used to endorse or promote products derived from +this software without specific prior written permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/Engine/lib/gtest/README b/Engine/lib/gtest/README new file mode 100644 index 000000000..26f35a847 --- /dev/null +++ b/Engine/lib/gtest/README @@ -0,0 +1,435 @@ +Google C++ Testing Framework +============================ + +http://code.google.com/p/googletest/ + +Overview +-------- + +Google's framework for writing C++ tests on a variety of platforms +(Linux, Mac OS X, Windows, Windows CE, Symbian, etc). Based on the +xUnit architecture. Supports automatic test discovery, a rich set of +assertions, user-defined assertions, death tests, fatal and non-fatal +failures, various options for running the tests, and XML test report +generation. + +Please see the project page above for more information as well as the +mailing list for questions, discussions, and development. There is +also an IRC channel on OFTC (irc.oftc.net) #gtest available. Please +join us! + +Requirements for End Users +-------------------------- + +Google Test is designed to have fairly minimal requirements to build +and use with your projects, but there are some. Currently, we support +Linux, Windows, Mac OS X, and Cygwin. We will also make our best +effort to support other platforms (e.g. Solaris, AIX, and z/OS). +However, since core members of the Google Test project have no access +to these platforms, Google Test may have outstanding issues there. If +you notice any problems on your platform, please notify +googletestframework@googlegroups.com. Patches for fixing them are +even more welcome! + +### Linux Requirements ### + +These are the base requirements to build and use Google Test from a source +package (as described below): + * GNU-compatible Make or gmake + * POSIX-standard shell + * POSIX(-2) Regular Expressions (regex.h) + * A C++98-standard-compliant compiler + +### Windows Requirements ### + + * Microsoft Visual C++ 7.1 or newer + +### Cygwin Requirements ### + + * Cygwin 1.5.25-14 or newer + +### Mac OS X Requirements ### + + * Mac OS X 10.4 Tiger or newer + * Developer Tools Installed + +Also, you'll need CMake 2.6.4 or higher if you want to build the +samples using the provided CMake script, regardless of the platform. + +Requirements for Contributors +----------------------------- + +We welcome patches. If you plan to contribute a patch, you need to +build Google Test and its own tests from an SVN checkout (described +below), which has further requirements: + + * Python version 2.3 or newer (for running some of the tests and + re-generating certain source files from templates) + * CMake 2.6.4 or newer + +Getting the Source +------------------ + +There are two primary ways of getting Google Test's source code: you +can download a stable source release in your preferred archive format, +or directly check out the source from our Subversion (SVN) repositary. +The SVN checkout requires a few extra steps and some extra software +packages on your system, but lets you track the latest development and +make patches much more easily, so we highly encourage it. + +### Source Package ### + +Google Test is released in versioned source packages which can be +downloaded from the download page [1]. Several different archive +formats are provided, but the only difference is the tools used to +manipulate them, and the size of the resulting file. Download +whichever you are most comfortable with. + + [1] http://code.google.com/p/googletest/downloads/list + +Once the package is downloaded, expand it using whichever tools you +prefer for that type. This will result in a new directory with the +name "gtest-X.Y.Z" which contains all of the source code. Here are +some examples on Linux: + + tar -xvzf gtest-X.Y.Z.tar.gz + tar -xvjf gtest-X.Y.Z.tar.bz2 + unzip gtest-X.Y.Z.zip + +### SVN Checkout ### + +To check out the main branch (also known as the "trunk") of Google +Test, run the following Subversion command: + + svn checkout http://googletest.googlecode.com/svn/trunk/ gtest-svn + +Setting up the Build +-------------------- + +To build Google Test and your tests that use it, you need to tell your +build system where to find its headers and source files. The exact +way to do it depends on which build system you use, and is usually +straightforward. + +### Generic Build Instructions ### + +Suppose you put Google Test in directory ${GTEST_DIR}. To build it, +create a library build target (or a project as called by Visual Studio +and Xcode) to compile + + ${GTEST_DIR}/src/gtest-all.cc + +with ${GTEST_DIR}/include in the system header search path and ${GTEST_DIR} +in the normal header search path. Assuming a Linux-like system and gcc, +something like the following will do: + + g++ -isystem ${GTEST_DIR}/include -I${GTEST_DIR} \ + -pthread -c ${GTEST_DIR}/src/gtest-all.cc + ar -rv libgtest.a gtest-all.o + +(We need -pthread as Google Test uses threads.) + +Next, you should compile your test source file with +${GTEST_DIR}/include in the system header search path, and link it +with gtest and any other necessary libraries: + + g++ -isystem ${GTEST_DIR}/include -pthread path/to/your_test.cc libgtest.a \ + -o your_test + +As an example, the make/ directory contains a Makefile that you can +use to build Google Test on systems where GNU make is available +(e.g. Linux, Mac OS X, and Cygwin). It doesn't try to build Google +Test's own tests. Instead, it just builds the Google Test library and +a sample test. You can use it as a starting point for your own build +script. + +If the default settings are correct for your environment, the +following commands should succeed: + + cd ${GTEST_DIR}/make + make + ./sample1_unittest + +If you see errors, try to tweak the contents of make/Makefile to make +them go away. There are instructions in make/Makefile on how to do +it. + +### Using CMake ### + +Google Test comes with a CMake build script (CMakeLists.txt) that can +be used on a wide range of platforms ("C" stands for cross-platofrm.). +If you don't have CMake installed already, you can download it for +free from http://www.cmake.org/. + +CMake works by generating native makefiles or build projects that can +be used in the compiler environment of your choice. The typical +workflow starts with: + + mkdir mybuild # Create a directory to hold the build output. + cd mybuild + cmake ${GTEST_DIR} # Generate native build scripts. + +If you want to build Google Test's samples, you should replace the +last command with + + cmake -Dgtest_build_samples=ON ${GTEST_DIR} + +If you are on a *nix system, you should now see a Makefile in the +current directory. Just type 'make' to build gtest. + +If you use Windows and have Vistual Studio installed, a gtest.sln file +and several .vcproj files will be created. You can then build them +using Visual Studio. + +On Mac OS X with Xcode installed, a .xcodeproj file will be generated. + +### Legacy Build Scripts ### + +Before settling on CMake, we have been providing hand-maintained build +projects/scripts for Visual Studio, Xcode, and Autotools. While we +continue to provide them for convenience, they are not actively +maintained any more. We highly recommend that you follow the +instructions in the previous two sections to integrate Google Test +with your existing build system. + +If you still need to use the legacy build scripts, here's how: + +The msvc\ folder contains two solutions with Visual C++ projects. +Open the gtest.sln or gtest-md.sln file using Visual Studio, and you +are ready to build Google Test the same way you build any Visual +Studio project. Files that have names ending with -md use DLL +versions of Microsoft runtime libraries (the /MD or the /MDd compiler +option). Files without that suffix use static versions of the runtime +libraries (the /MT or the /MTd option). Please note that one must use +the same option to compile both gtest and the test code. If you use +Visual Studio 2005 or above, we recommend the -md version as /MD is +the default for new projects in these versions of Visual Studio. + +On Mac OS X, open the gtest.xcodeproj in the xcode/ folder using +Xcode. Build the "gtest" target. The universal binary framework will +end up in your selected build directory (selected in the Xcode +"Preferences..." -> "Building" pane and defaults to xcode/build). +Alternatively, at the command line, enter: + + xcodebuild + +This will build the "Release" configuration of gtest.framework in your +default build location. See the "xcodebuild" man page for more +information about building different configurations and building in +different locations. + +If you wish to use the Google Test Xcode project with Xcode 4.x and +above, you need to either: + * update the SDK configuration options in xcode/Config/General.xconfig. + Comment options SDKROOT, MACOS_DEPLOYMENT_TARGET, and GCC_VERSION. If + you choose this route you lose the ability to target earlier versions + of MacOS X. + * Install an SDK for an earlier version. This doesn't appear to be + supported by Apple, but has been reported to work + (http://stackoverflow.com/questions/5378518). + +Tweaking Google Test +-------------------- + +Google Test can be used in diverse environments. The default +configuration may not work (or may not work well) out of the box in +some environments. However, you can easily tweak Google Test by +defining control macros on the compiler command line. Generally, +these macros are named like GTEST_XYZ and you define them to either 1 +or 0 to enable or disable a certain feature. + +We list the most frequently used macros below. For a complete list, +see file include/gtest/internal/gtest-port.h. + +### Choosing a TR1 Tuple Library ### + +Some Google Test features require the C++ Technical Report 1 (TR1) +tuple library, which is not yet available with all compilers. The +good news is that Google Test implements a subset of TR1 tuple that's +enough for its own need, and will automatically use this when the +compiler doesn't provide TR1 tuple. + +Usually you don't need to care about which tuple library Google Test +uses. However, if your project already uses TR1 tuple, you need to +tell Google Test to use the same TR1 tuple library the rest of your +project uses, or the two tuple implementations will clash. To do +that, add + + -DGTEST_USE_OWN_TR1_TUPLE=0 + +to the compiler flags while compiling Google Test and your tests. If +you want to force Google Test to use its own tuple library, just add + + -DGTEST_USE_OWN_TR1_TUPLE=1 + +to the compiler flags instead. + +If you don't want Google Test to use tuple at all, add + + -DGTEST_HAS_TR1_TUPLE=0 + +and all features using tuple will be disabled. + +### Multi-threaded Tests ### + +Google Test is thread-safe where the pthread library is available. +After #include "gtest/gtest.h", you can check the GTEST_IS_THREADSAFE +macro to see whether this is the case (yes if the macro is #defined to +1, no if it's undefined.). + +If Google Test doesn't correctly detect whether pthread is available +in your environment, you can force it with + + -DGTEST_HAS_PTHREAD=1 + +or + + -DGTEST_HAS_PTHREAD=0 + +When Google Test uses pthread, you may need to add flags to your +compiler and/or linker to select the pthread library, or you'll get +link errors. If you use the CMake script or the deprecated Autotools +script, this is taken care of for you. If you use your own build +script, you'll need to read your compiler and linker's manual to +figure out what flags to add. + +### As a Shared Library (DLL) ### + +Google Test is compact, so most users can build and link it as a +static library for the simplicity. You can choose to use Google Test +as a shared library (known as a DLL on Windows) if you prefer. + +To compile *gtest* as a shared library, add + + -DGTEST_CREATE_SHARED_LIBRARY=1 + +to the compiler flags. You'll also need to tell the linker to produce +a shared library instead - consult your linker's manual for how to do +it. + +To compile your *tests* that use the gtest shared library, add + + -DGTEST_LINKED_AS_SHARED_LIBRARY=1 + +to the compiler flags. + +Note: while the above steps aren't technically necessary today when +using some compilers (e.g. GCC), they may become necessary in the +future, if we decide to improve the speed of loading the library (see +http://gcc.gnu.org/wiki/Visibility for details). Therefore you are +recommended to always add the above flags when using Google Test as a +shared library. Otherwise a future release of Google Test may break +your build script. + +### Avoiding Macro Name Clashes ### + +In C++, macros don't obey namespaces. Therefore two libraries that +both define a macro of the same name will clash if you #include both +definitions. In case a Google Test macro clashes with another +library, you can force Google Test to rename its macro to avoid the +conflict. + +Specifically, if both Google Test and some other code define macro +FOO, you can add + + -DGTEST_DONT_DEFINE_FOO=1 + +to the compiler flags to tell Google Test to change the macro's name +from FOO to GTEST_FOO. Currently FOO can be FAIL, SUCCEED, or TEST. +For example, with -DGTEST_DONT_DEFINE_TEST=1, you'll need to write + + GTEST_TEST(SomeTest, DoesThis) { ... } + +instead of + + TEST(SomeTest, DoesThis) { ... } + +in order to define a test. + +Upgrating from an Earlier Version +--------------------------------- + +We strive to keep Google Test releases backward compatible. +Sometimes, though, we have to make some breaking changes for the +users' long-term benefits. This section describes what you'll need to +do if you are upgrading from an earlier version of Google Test. + +### Upgrading from 1.3.0 or Earlier ### + +You may need to explicitly enable or disable Google Test's own TR1 +tuple library. See the instructions in section "Choosing a TR1 Tuple +Library". + +### Upgrading from 1.4.0 or Earlier ### + +The Autotools build script (configure + make) is no longer officially +supportted. You are encouraged to migrate to your own build system or +use CMake. If you still need to use Autotools, you can find +instructions in the README file from Google Test 1.4.0. + +On platforms where the pthread library is available, Google Test uses +it in order to be thread-safe. See the "Multi-threaded Tests" section +for what this means to your build script. + +If you use Microsoft Visual C++ 7.1 with exceptions disabled, Google +Test will no longer compile. This should affect very few people, as a +large portion of STL (including ) doesn't compile in this mode +anyway. We decided to stop supporting it in order to greatly simplify +Google Test's implementation. + +Developing Google Test +---------------------- + +This section discusses how to make your own changes to Google Test. + +### Testing Google Test Itself ### + +To make sure your changes work as intended and don't break existing +functionality, you'll want to compile and run Google Test's own tests. +For that you can use CMake: + + mkdir mybuild + cd mybuild + cmake -Dgtest_build_tests=ON ${GTEST_DIR} + +Make sure you have Python installed, as some of Google Test's tests +are written in Python. If the cmake command complains about not being +able to find Python ("Could NOT find PythonInterp (missing: +PYTHON_EXECUTABLE)"), try telling it explicitly where your Python +executable can be found: + + cmake -DPYTHON_EXECUTABLE=path/to/python -Dgtest_build_tests=ON ${GTEST_DIR} + +Next, you can build Google Test and all of its own tests. On *nix, +this is usually done by 'make'. To run the tests, do + + make test + +All tests should pass. + +### Regenerating Source Files ### + +Some of Google Test's source files are generated from templates (not +in the C++ sense) using a script. A template file is named FOO.pump, +where FOO is the name of the file it will generate. For example, the +file include/gtest/internal/gtest-type-util.h.pump is used to generate +gtest-type-util.h in the same directory. + +Normally you don't need to worry about regenerating the source files, +unless you need to modify them. In that case, you should modify the +corresponding .pump files instead and run the pump.py Python script to +regenerate them. You can find pump.py in the scripts/ directory. +Read the Pump manual [2] for how to use it. + + [2] http://code.google.com/p/googletest/wiki/PumpManual + +### Contributing a Patch ### + +We welcome patches. Please read the Google Test developer's guide [3] +for how you can contribute. In particular, make sure you have signed +the Contributor License Agreement, or we won't be able to accept the +patch. + + [3] http://code.google.com/p/googletest/wiki/GoogleTestDevGuide + +Happy testing! diff --git a/Engine/lib/gtest/fused-src/gtest/gtest-all.cc b/Engine/lib/gtest/fused-src/gtest/gtest-all.cc new file mode 100644 index 000000000..a9a03b2e3 --- /dev/null +++ b/Engine/lib/gtest/fused-src/gtest/gtest-all.cc @@ -0,0 +1,9592 @@ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// +// Google C++ Testing Framework (Google Test) +// +// Sometimes it's desirable to build Google Test by compiling a single file. +// This file serves this purpose. + +// This line ensures that gtest.h can be compiled on its own, even +// when it's fused. +#include "gtest/gtest.h" + +// The following lines pull in the real gtest *.cc files. +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) + +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Utilities for testing Google Test itself and code that uses Google Test +// (e.g. frameworks built on top of Google Test). + +#ifndef GTEST_INCLUDE_GTEST_GTEST_SPI_H_ +#define GTEST_INCLUDE_GTEST_GTEST_SPI_H_ + + +namespace testing { + +// This helper class can be used to mock out Google Test failure reporting +// so that we can test Google Test or code that builds on Google Test. +// +// An object of this class appends a TestPartResult object to the +// TestPartResultArray object given in the constructor whenever a Google Test +// failure is reported. It can either intercept only failures that are +// generated in the same thread that created this object or it can intercept +// all generated failures. The scope of this mock object can be controlled with +// the second argument to the two arguments constructor. +class GTEST_API_ ScopedFakeTestPartResultReporter + : public TestPartResultReporterInterface { + public: + // The two possible mocking modes of this object. + enum InterceptMode { + INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures. + INTERCEPT_ALL_THREADS // Intercepts all failures. + }; + + // The c'tor sets this object as the test part result reporter used + // by Google Test. The 'result' parameter specifies where to report the + // results. This reporter will only catch failures generated in the current + // thread. DEPRECATED + explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result); + + // Same as above, but you can choose the interception scope of this object. + ScopedFakeTestPartResultReporter(InterceptMode intercept_mode, + TestPartResultArray* result); + + // The d'tor restores the previous test part result reporter. + virtual ~ScopedFakeTestPartResultReporter(); + + // Appends the TestPartResult object to the TestPartResultArray + // received in the constructor. + // + // This method is from the TestPartResultReporterInterface + // interface. + virtual void ReportTestPartResult(const TestPartResult& result); + private: + void Init(); + + const InterceptMode intercept_mode_; + TestPartResultReporterInterface* old_reporter_; + TestPartResultArray* const result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedFakeTestPartResultReporter); +}; + +namespace internal { + +// A helper class for implementing EXPECT_FATAL_FAILURE() and +// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +class GTEST_API_ SingleFailureChecker { + public: + // The constructor remembers the arguments. + SingleFailureChecker(const TestPartResultArray* results, + TestPartResult::Type type, + const string& substr); + ~SingleFailureChecker(); + private: + const TestPartResultArray* const results_; + const TestPartResult::Type type_; + const string substr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SingleFailureChecker); +}; + +} // namespace internal + +} // namespace testing + +// A set of macros for testing Google Test assertions or code that's expected +// to generate Google Test fatal failures. It verifies that the given +// statement will cause exactly one fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_FATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - 'statement' cannot reference local non-static variables or +// non-static members of the current object. +// - 'statement' cannot return a value. +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. The AcceptsMacroThatExpandsToUnprotectedComma test in +// gtest_unittest.cc will fail to compile if we do that. +#define EXPECT_FATAL_FAILURE(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do { \ + class GTestExpectFatalFailureHelper {\ + public:\ + static void Execute() { statement; }\ + };\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kFatalFailure, (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ALL_THREADS, >est_failures);\ + GTestExpectFatalFailureHelper::Execute();\ + }\ + } while (::testing::internal::AlwaysFalse()) + +// A macro for testing Google Test assertions or code that's expected to +// generate Google Test non-fatal failures. It asserts that the given +// statement will cause exactly one non-fatal Google Test failure with 'substr' +// being part of the failure message. +// +// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only +// affects and considers failures generated in the current thread and +// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads. +// +// 'statement' is allowed to reference local variables and members of +// the current object. +// +// The verification of the assertion is done correctly even when the statement +// throws an exception or aborts the current function. +// +// Known restrictions: +// - You cannot stream a failure message to this macro. +// +// Note that even though the implementations of the following two +// macros are much alike, we cannot refactor them to use a common +// helper macro, due to some peculiarity in how the preprocessor +// works. If we do that, the code won't compile when the user gives +// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that +// expands to code containing an unprotected comma. The +// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc +// catches that. +// +// For the same reason, we have to write +// if (::testing::internal::AlwaysTrue()) { statement; } +// instead of +// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) +// to avoid an MSVC warning on unreachable code. +#define EXPECT_NONFATAL_FAILURE(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter:: \ + INTERCEPT_ONLY_CURRENT_THREAD, >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \ + do {\ + ::testing::TestPartResultArray gtest_failures;\ + ::testing::internal::SingleFailureChecker gtest_checker(\ + >est_failures, ::testing::TestPartResult::kNonFatalFailure, \ + (substr));\ + {\ + ::testing::ScopedFakeTestPartResultReporter gtest_reporter(\ + ::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \ + >est_failures);\ + if (::testing::internal::AlwaysTrue()) { statement; }\ + }\ + } while (::testing::internal::AlwaysFalse()) + +#endif // GTEST_INCLUDE_GTEST_GTEST_SPI_H_ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include // NOLINT +#include +#include + +#if GTEST_OS_LINUX + +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +# define GTEST_HAS_GETTIMEOFDAY_ 1 + +# include // NOLINT +# include // NOLINT +# include // NOLINT +// Declares vsnprintf(). This header is not available on Windows. +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include + +#elif GTEST_OS_SYMBIAN +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT + +#elif GTEST_OS_ZOS +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT + +// On z/OS we additionally need strings.h for strcasecmp. +# include // NOLINT + +#elif GTEST_OS_WINDOWS_MOBILE // We are on Windows CE. + +# include // NOLINT + +#elif GTEST_OS_WINDOWS // We are on Windows proper. + +# include // NOLINT +# include // NOLINT +# include // NOLINT +# include // NOLINT + +# if GTEST_OS_WINDOWS_MINGW +// MinGW has gettimeofday() but not _ftime64(). +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +// TODO(kenton@google.com): There are other ways to get the time on +// Windows, like GetTickCount() or GetSystemTimeAsFileTime(). MinGW +// supports these. consider using them instead. +# define GTEST_HAS_GETTIMEOFDAY_ 1 +# include // NOLINT +# endif // GTEST_OS_WINDOWS_MINGW + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include // NOLINT + +#else + +// Assume other platforms have gettimeofday(). +// TODO(kenton@google.com): Use autoconf to detect availability of +// gettimeofday(). +# define GTEST_HAS_GETTIMEOFDAY_ 1 + +// cpplint thinks that the header is already included, so we want to +// silence it. +# include // NOLINT +# include // NOLINT + +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include +#endif + +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +#endif + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// Utility functions and classes used by the Google C++ testing framework. +// +// Author: wan@google.com (Zhanyong Wan) +// +// This file contains purely Google Test's internal implementation. Please +// DO NOT #INCLUDE IT IN A USER PROGRAM. + +#ifndef GTEST_SRC_GTEST_INTERNAL_INL_H_ +#define GTEST_SRC_GTEST_INTERNAL_INL_H_ + +// GTEST_IMPLEMENTATION_ is defined to 1 iff the current translation unit is +// part of Google Test's implementation; otherwise it's undefined. +#if !GTEST_IMPLEMENTATION_ +// A user is trying to include this from his code - just say no. +# error "gtest-internal-inl.h is part of Google Test's internal implementation." +# error "It must not be included except by Google Test itself." +#endif // GTEST_IMPLEMENTATION_ + +#ifndef _WIN32_WCE +# include +#endif // !_WIN32_WCE +#include +#include // For strtoll/_strtoul64/malloc/free. +#include // For memmove. + +#include +#include +#include + + +#if GTEST_CAN_STREAM_RESULTS_ +# include // NOLINT +# include // NOLINT +#endif + +#if GTEST_OS_WINDOWS +# include // NOLINT +#endif // GTEST_OS_WINDOWS + + +namespace testing { + +// Declares the flags. +// +// We don't want the users to modify this flag in the code, but want +// Google Test's own unit tests to be able to access it. Therefore we +// declare it here as opposed to in gtest.h. +GTEST_DECLARE_bool_(death_test_use_fork); + +namespace internal { + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +GTEST_API_ extern const TypeId kTestTypeIdInGoogleTest; + +// Names of the flags (needed for parsing Google Test flags). +const char kAlsoRunDisabledTestsFlag[] = "also_run_disabled_tests"; +const char kBreakOnFailureFlag[] = "break_on_failure"; +const char kCatchExceptionsFlag[] = "catch_exceptions"; +const char kColorFlag[] = "color"; +const char kFilterFlag[] = "filter"; +const char kListTestsFlag[] = "list_tests"; +const char kOutputFlag[] = "output"; +const char kPrintTimeFlag[] = "print_time"; +const char kRandomSeedFlag[] = "random_seed"; +const char kRepeatFlag[] = "repeat"; +const char kShuffleFlag[] = "shuffle"; +const char kStackTraceDepthFlag[] = "stack_trace_depth"; +const char kStreamResultToFlag[] = "stream_result_to"; +const char kThrowOnFailureFlag[] = "throw_on_failure"; + +// A valid random seed must be in [1, kMaxRandomSeed]. +const int kMaxRandomSeed = 99999; + +// g_help_flag is true iff the --help flag or an equivalent form is +// specified on the command line. +GTEST_API_ extern bool g_help_flag; + +// Returns the current time in milliseconds. +GTEST_API_ TimeInMillis GetTimeInMillis(); + +// Returns true iff Google Test should use colors in the output. +GTEST_API_ bool ShouldUseColor(bool stdout_is_tty); + +// Formats the given time in milliseconds as seconds. +GTEST_API_ std::string FormatTimeInMillisAsSeconds(TimeInMillis ms); + +// Converts the given time in milliseconds to a date string in the ISO 8601 +// format, without the timezone information. N.B.: due to the use the +// non-reentrant localtime() function, this function is not thread safe. Do +// not use it in any code that can be called from multiple threads. +GTEST_API_ std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms); + +// Parses a string for an Int32 flag, in the form of "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +GTEST_API_ bool ParseInt32Flag( + const char* str, const char* flag, Int32* value); + +// Returns a random seed in range [1, kMaxRandomSeed] based on the +// given --gtest_random_seed flag value. +inline int GetRandomSeedFromFlag(Int32 random_seed_flag) { + const unsigned int raw_seed = (random_seed_flag == 0) ? + static_cast(GetTimeInMillis()) : + static_cast(random_seed_flag); + + // Normalizes the actual seed to range [1, kMaxRandomSeed] such that + // it's easy to type. + const int normalized_seed = + static_cast((raw_seed - 1U) % + static_cast(kMaxRandomSeed)) + 1; + return normalized_seed; +} + +// Returns the first valid random seed after 'seed'. The behavior is +// undefined if 'seed' is invalid. The seed after kMaxRandomSeed is +// considered to be 1. +inline int GetNextRandomSeed(int seed) { + GTEST_CHECK_(1 <= seed && seed <= kMaxRandomSeed) + << "Invalid random seed " << seed << " - must be in [1, " + << kMaxRandomSeed << "]."; + const int next_seed = seed + 1; + return (next_seed > kMaxRandomSeed) ? 1 : next_seed; +} + +// This class saves the values of all Google Test flags in its c'tor, and +// restores them in its d'tor. +class GTestFlagSaver { + public: + // The c'tor. + GTestFlagSaver() { + also_run_disabled_tests_ = GTEST_FLAG(also_run_disabled_tests); + break_on_failure_ = GTEST_FLAG(break_on_failure); + catch_exceptions_ = GTEST_FLAG(catch_exceptions); + color_ = GTEST_FLAG(color); + death_test_style_ = GTEST_FLAG(death_test_style); + death_test_use_fork_ = GTEST_FLAG(death_test_use_fork); + filter_ = GTEST_FLAG(filter); + internal_run_death_test_ = GTEST_FLAG(internal_run_death_test); + list_tests_ = GTEST_FLAG(list_tests); + output_ = GTEST_FLAG(output); + print_time_ = GTEST_FLAG(print_time); + random_seed_ = GTEST_FLAG(random_seed); + repeat_ = GTEST_FLAG(repeat); + shuffle_ = GTEST_FLAG(shuffle); + stack_trace_depth_ = GTEST_FLAG(stack_trace_depth); + stream_result_to_ = GTEST_FLAG(stream_result_to); + throw_on_failure_ = GTEST_FLAG(throw_on_failure); + } + + // The d'tor is not virtual. DO NOT INHERIT FROM THIS CLASS. + ~GTestFlagSaver() { + GTEST_FLAG(also_run_disabled_tests) = also_run_disabled_tests_; + GTEST_FLAG(break_on_failure) = break_on_failure_; + GTEST_FLAG(catch_exceptions) = catch_exceptions_; + GTEST_FLAG(color) = color_; + GTEST_FLAG(death_test_style) = death_test_style_; + GTEST_FLAG(death_test_use_fork) = death_test_use_fork_; + GTEST_FLAG(filter) = filter_; + GTEST_FLAG(internal_run_death_test) = internal_run_death_test_; + GTEST_FLAG(list_tests) = list_tests_; + GTEST_FLAG(output) = output_; + GTEST_FLAG(print_time) = print_time_; + GTEST_FLAG(random_seed) = random_seed_; + GTEST_FLAG(repeat) = repeat_; + GTEST_FLAG(shuffle) = shuffle_; + GTEST_FLAG(stack_trace_depth) = stack_trace_depth_; + GTEST_FLAG(stream_result_to) = stream_result_to_; + GTEST_FLAG(throw_on_failure) = throw_on_failure_; + } + + private: + // Fields for saving the original values of flags. + bool also_run_disabled_tests_; + bool break_on_failure_; + bool catch_exceptions_; + std::string color_; + std::string death_test_style_; + bool death_test_use_fork_; + std::string filter_; + std::string internal_run_death_test_; + bool list_tests_; + std::string output_; + bool print_time_; + internal::Int32 random_seed_; + internal::Int32 repeat_; + bool shuffle_; + internal::Int32 stack_trace_depth_; + std::string stream_result_to_; + bool throw_on_failure_; +} GTEST_ATTRIBUTE_UNUSED_; + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +GTEST_API_ std::string CodePointToUtf8(UInt32 code_point); + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +GTEST_API_ std::string WideStringToUtf8(const wchar_t* str, int num_chars); + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded(); + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (e.g., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +GTEST_API_ bool ShouldShard(const char* total_shards_str, + const char* shard_index_str, + bool in_subprocess_for_death_test); + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error and +// and aborts. +GTEST_API_ Int32 Int32FromEnvOrDie(const char* env_var, Int32 default_val); + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +GTEST_API_ bool ShouldRunTestOnShard( + int total_shards, int shard_index, int test_id); + +// STL container utilities. + +// Returns the number of elements in the given container that satisfy +// the given predicate. +template +inline int CountIf(const Container& c, Predicate predicate) { + // Implemented as an explicit loop since std::count_if() in libCstd on + // Solaris has a non-standard signature. + int count = 0; + for (typename Container::const_iterator it = c.begin(); it != c.end(); ++it) { + if (predicate(*it)) + ++count; + } + return count; +} + +// Applies a function/functor to each element in the container. +template +void ForEach(const Container& c, Functor functor) { + std::for_each(c.begin(), c.end(), functor); +} + +// Returns the i-th element of the vector, or default_value if i is not +// in range [0, v.size()). +template +inline E GetElementOr(const std::vector& v, int i, E default_value) { + return (i < 0 || i >= static_cast(v.size())) ? default_value : v[i]; +} + +// Performs an in-place shuffle of a range of the vector's elements. +// 'begin' and 'end' are element indices as an STL-style range; +// i.e. [begin, end) are shuffled, where 'end' == size() means to +// shuffle to the end of the vector. +template +void ShuffleRange(internal::Random* random, int begin, int end, + std::vector* v) { + const int size = static_cast(v->size()); + GTEST_CHECK_(0 <= begin && begin <= size) + << "Invalid shuffle range start " << begin << ": must be in range [0, " + << size << "]."; + GTEST_CHECK_(begin <= end && end <= size) + << "Invalid shuffle range finish " << end << ": must be in range [" + << begin << ", " << size << "]."; + + // Fisher-Yates shuffle, from + // http://en.wikipedia.org/wiki/Fisher-Yates_shuffle + for (int range_width = end - begin; range_width >= 2; range_width--) { + const int last_in_range = begin + range_width - 1; + const int selected = begin + random->Generate(range_width); + std::swap((*v)[selected], (*v)[last_in_range]); + } +} + +// Performs an in-place shuffle of the vector's elements. +template +inline void Shuffle(internal::Random* random, std::vector* v) { + ShuffleRange(random, 0, static_cast(v->size()), v); +} + +// A function for deleting an object. Handy for being used as a +// functor. +template +static void Delete(T* x) { + delete x; +} + +// A predicate that checks the key of a TestProperty against a known key. +// +// TestPropertyKeyIs is copyable. +class TestPropertyKeyIs { + public: + // Constructor. + // + // TestPropertyKeyIs has NO default constructor. + explicit TestPropertyKeyIs(const std::string& key) : key_(key) {} + + // Returns true iff the test name of test property matches on key_. + bool operator()(const TestProperty& test_property) const { + return test_property.key() == key_; + } + + private: + std::string key_; +}; + +// Class UnitTestOptions. +// +// This class contains functions for processing options the user +// specifies when running the tests. It has only static members. +// +// In most cases, the user can specify an option using either an +// environment variable or a command line flag. E.g. you can set the +// test filter using either GTEST_FILTER or --gtest_filter. If both +// the variable and the flag are present, the latter overrides the +// former. +class GTEST_API_ UnitTestOptions { + public: + // Functions for processing the gtest_output flag. + + // Returns the output format, or "" for normal printed output. + static std::string GetOutputFormat(); + + // Returns the absolute path of the requested output file, or the + // default (test_detail.xml in the original working directory) if + // none was explicitly specified. + static std::string GetAbsolutePathToOutputFile(); + + // Functions for processing the gtest_filter flag. + + // Returns true iff the wildcard pattern matches the string. The + // first ':' or '\0' character in pattern marks the end of it. + // + // This recursive algorithm isn't very efficient, but is clear and + // works well enough for matching test names, which are short. + static bool PatternMatchesString(const char *pattern, const char *str); + + // Returns true iff the user-specified filter matches the test case + // name and the test name. + static bool FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name); + +#if GTEST_OS_WINDOWS + // Function for supporting the gtest_catch_exception flag. + + // Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the + // given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. + // This function is useful as an __except condition. + static int GTestShouldProcessSEH(DWORD exception_code); +#endif // GTEST_OS_WINDOWS + + // Returns true if "name" matches the ':' separated list of glob-style + // filters in "filter". + static bool MatchesFilter(const std::string& name, const char* filter); +}; + +// Returns the current application's name, removing directory path if that +// is present. Used by UnitTestOptions::GetOutputFile. +GTEST_API_ FilePath GetCurrentExecutableName(); + +// The role interface for getting the OS stack trace as a string. +class OsStackTraceGetterInterface { + public: + OsStackTraceGetterInterface() {} + virtual ~OsStackTraceGetterInterface() {} + + // Returns the current OS stack trace as an std::string. Parameters: + // + // max_depth - the maximum number of stack frames to be included + // in the trace. + // skip_count - the number of top frames to be skipped; doesn't count + // against max_depth. + virtual string CurrentStackTrace(int max_depth, int skip_count) = 0; + + // UponLeavingGTest() should be called immediately before Google Test calls + // user code. It saves some information about the current stack that + // CurrentStackTrace() will use to find and hide Google Test stack frames. + virtual void UponLeavingGTest() = 0; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetterInterface); +}; + +// A working implementation of the OsStackTraceGetterInterface interface. +class OsStackTraceGetter : public OsStackTraceGetterInterface { + public: + OsStackTraceGetter() : caller_frame_(NULL) {} + + virtual string CurrentStackTrace(int max_depth, int skip_count) + GTEST_LOCK_EXCLUDED_(mutex_); + + virtual void UponLeavingGTest() GTEST_LOCK_EXCLUDED_(mutex_); + + // This string is inserted in place of stack frames that are part of + // Google Test's implementation. + static const char* const kElidedFramesMarker; + + private: + Mutex mutex_; // protects all internal state + + // We save the stack frame below the frame that calls user code. + // We do this because the address of the frame immediately below + // the user code changes between the call to UponLeavingGTest() + // and any calls to CurrentStackTrace() from within the user code. + void* caller_frame_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(OsStackTraceGetter); +}; + +// Information about a Google Test trace point. +struct TraceInfo { + const char* file; + int line; + std::string message; +}; + +// This is the default global test part result reporter used in UnitTestImpl. +// This class should only be used by UnitTestImpl. +class DefaultGlobalTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultGlobalTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. Reports the test part + // result in the current test. + virtual void ReportTestPartResult(const TestPartResult& result); + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultGlobalTestPartResultReporter); +}; + +// This is the default per thread test part result reporter used in +// UnitTestImpl. This class should only be used by UnitTestImpl. +class DefaultPerThreadTestPartResultReporter + : public TestPartResultReporterInterface { + public: + explicit DefaultPerThreadTestPartResultReporter(UnitTestImpl* unit_test); + // Implements the TestPartResultReporterInterface. The implementation just + // delegates to the current global test part result reporter of *unit_test_. + virtual void ReportTestPartResult(const TestPartResult& result); + + private: + UnitTestImpl* const unit_test_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DefaultPerThreadTestPartResultReporter); +}; + +// The private implementation of the UnitTest class. We don't protect +// the methods under a mutex, as this class is not accessible by a +// user and the UnitTest class that delegates work to this class does +// proper locking. +class GTEST_API_ UnitTestImpl { + public: + explicit UnitTestImpl(UnitTest* parent); + virtual ~UnitTestImpl(); + + // There are two different ways to register your own TestPartResultReporter. + // You can register your own repoter to listen either only for test results + // from the current thread or for results from all threads. + // By default, each per-thread test result repoter just passes a new + // TestPartResult to the global test result reporter, which registers the + // test part result for the currently running test. + + // Returns the global test part result reporter. + TestPartResultReporterInterface* GetGlobalTestPartResultReporter(); + + // Sets the global test part result reporter. + void SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter); + + // Returns the test part result reporter for the current thread. + TestPartResultReporterInterface* GetTestPartResultReporterForCurrentThread(); + + // Sets the test part result reporter for the current thread. + void SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter); + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const { return start_timestamp_; } + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns true iff the unit test passed (i.e. all test cases passed). + bool Passed() const { return !Failed(); } + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const { + return failed_test_case_count() > 0 || ad_hoc_test_result()->Failed(); + } + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + const TestCase* GetTestCase(int i) const { + const int index = GetElementOr(test_case_indices_, i, -1); + return index < 0 ? NULL : test_cases_[i]; + } + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + TestCase* GetMutableTestCase(int i) { + const int index = GetElementOr(test_case_indices_, i, -1); + return index < 0 ? NULL : test_cases_[index]; + } + + // Provides access to the event listener list. + TestEventListeners* listeners() { return &listeners_; } + + // Returns the TestResult for the test that's currently running, or + // the TestResult for the ad hoc test if no test is running. + TestResult* current_test_result(); + + // Returns the TestResult for the ad hoc test. + const TestResult* ad_hoc_test_result() const { return &ad_hoc_test_result_; } + + // Sets the OS stack trace getter. + // + // Does nothing if the input and the current OS stack trace getter + // are the same; otherwise, deletes the old getter and makes the + // input the current getter. + void set_os_stack_trace_getter(OsStackTraceGetterInterface* getter); + + // Returns the current OS stack trace getter if it is not NULL; + // otherwise, creates an OsStackTraceGetter, makes it the current + // getter, and returns it. + OsStackTraceGetterInterface* os_stack_trace_getter(); + + // Returns the current OS stack trace as an std::string. + // + // The maximum number of stack frames to be included is specified by + // the gtest_stack_trace_depth flag. The skip_count parameter + // specifies the number of top frames to be skipped, which doesn't + // count against the number of frames to be included. + // + // For example, if Foo() calls Bar(), which in turn calls + // CurrentOsStackTraceExceptTop(1), Foo() will be included in the + // trace but Bar() and CurrentOsStackTraceExceptTop() won't. + std::string CurrentOsStackTraceExceptTop(int skip_count) GTEST_NO_INLINE_; + + // Finds and returns a TestCase with the given name. If one doesn't + // exist, creates one and returns it. + // + // Arguments: + // + // test_case_name: name of the test case + // type_param: the name of the test's type parameter, or NULL if + // this is not a typed or a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase* GetTestCase(const char* test_case_name, + const char* type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Adds a TestInfo to the unit test. + // + // Arguments: + // + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + // test_info: the TestInfo object + void AddTestInfo(Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + TestInfo* test_info) { + // In order to support thread-safe death tests, we need to + // remember the original working directory when the test program + // was first invoked. We cannot do this in RUN_ALL_TESTS(), as + // the user may have changed the current directory before calling + // RUN_ALL_TESTS(). Therefore we capture the current directory in + // AddTestInfo(), which is called to register a TEST or TEST_F + // before main() is reached. + if (original_working_dir_.IsEmpty()) { + original_working_dir_.Set(FilePath::GetCurrentDir()); + GTEST_CHECK_(!original_working_dir_.IsEmpty()) + << "Failed to get the current working directory."; + } + + GetTestCase(test_info->test_case_name(), + test_info->type_param(), + set_up_tc, + tear_down_tc)->AddTestInfo(test_info); + } + +#if GTEST_HAS_PARAM_TEST + // Returns ParameterizedTestCaseRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + internal::ParameterizedTestCaseRegistry& parameterized_test_registry() { + return parameterized_test_registry_; + } +#endif // GTEST_HAS_PARAM_TEST + + // Sets the TestCase object for the test that's currently running. + void set_current_test_case(TestCase* a_current_test_case) { + current_test_case_ = a_current_test_case; + } + + // Sets the TestInfo object for the test that's currently running. If + // current_test_info is NULL, the assertion results will be stored in + // ad_hoc_test_result_. + void set_current_test_info(TestInfo* a_current_test_info) { + current_test_info_ = a_current_test_info; + } + + // Registers all parameterized tests defined using TEST_P and + // INSTANTIATE_TEST_CASE_P, creating regular tests for each test/parameter + // combination. This method can be called more then once; it has guards + // protecting from registering the tests more then once. If + // value-parameterized tests are disabled, RegisterParameterizedTests is + // present but does nothing. + void RegisterParameterizedTests(); + + // Runs all tests in this UnitTest object, prints the result, and + // returns true if all tests are successful. If any exception is + // thrown during a test, this test is considered to be failed, but + // the rest of the tests will still be run. + bool RunAllTests(); + + // Clears the results of all tests, except the ad hoc tests. + void ClearNonAdHocTestResult() { + ForEach(test_cases_, TestCase::ClearTestCaseResult); + } + + // Clears the results of ad-hoc test assertions. + void ClearAdHocTestResult() { + ad_hoc_test_result_.Clear(); + } + + // Adds a TestProperty to the current TestResult object when invoked in a + // context of a test or a test case, or to the global property set. If the + // result already contains a property with the same key, the value will be + // updated. + void RecordProperty(const TestProperty& test_property); + + enum ReactionToSharding { + HONOR_SHARDING_PROTOCOL, + IGNORE_SHARDING_PROTOCOL + }; + + // Matches the full name of each test against the user-specified + // filter to decide whether the test should run, then records the + // result in each TestCase and TestInfo object. + // If shard_tests == HONOR_SHARDING_PROTOCOL, further filters tests + // based on sharding variables in the environment. + // Returns the number of tests that should run. + int FilterTests(ReactionToSharding shard_tests); + + // Prints the names of the tests matching the user-specified filter flag. + void ListTestsMatchingFilter(); + + const TestCase* current_test_case() const { return current_test_case_; } + TestInfo* current_test_info() { return current_test_info_; } + const TestInfo* current_test_info() const { return current_test_info_; } + + // Returns the vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector& environments() { return environments_; } + + // Getters for the per-thread Google Test trace stack. + std::vector& gtest_trace_stack() { + return *(gtest_trace_stack_.pointer()); + } + const std::vector& gtest_trace_stack() const { + return gtest_trace_stack_.get(); + } + +#if GTEST_HAS_DEATH_TEST + void InitDeathTestSubprocessControlInfo() { + internal_run_death_test_flag_.reset(ParseInternalRunDeathTestFlag()); + } + // Returns a pointer to the parsed --gtest_internal_run_death_test + // flag, or NULL if that flag was not specified. + // This information is useful only in a death test child process. + // Must not be called before a call to InitGoogleTest. + const InternalRunDeathTestFlag* internal_run_death_test_flag() const { + return internal_run_death_test_flag_.get(); + } + + // Returns a pointer to the current death test factory. + internal::DeathTestFactory* death_test_factory() { + return death_test_factory_.get(); + } + + void SuppressTestEventsIfInSubprocess(); + + friend class ReplaceDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + + // Initializes the event listener performing XML output as specified by + // UnitTestOptions. Must not be called before InitGoogleTest. + void ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Initializes the event listener for streaming test results to a socket. + // Must not be called before InitGoogleTest. + void ConfigureStreamingOutput(); +#endif + + // Performs initialization dependent upon flag values obtained in + // ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to + // ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest + // this function is also called from RunAllTests. Since this function can be + // called more than once, it has to be idempotent. + void PostFlagParsingInit(); + + // Gets the random seed used at the start of the current test iteration. + int random_seed() const { return random_seed_; } + + // Gets the random number generator. + internal::Random* random() { return &random_; } + + // Shuffles all test cases, and the tests within each test case, + // making sure that death tests are still run first. + void ShuffleTests(); + + // Restores the test cases and tests to their order before the first shuffle. + void UnshuffleTests(); + + // Returns the value of GTEST_FLAG(catch_exceptions) at the moment + // UnitTest::Run() starts. + bool catch_exceptions() const { return catch_exceptions_; } + + private: + friend class ::testing::UnitTest; + + // Used by UnitTest::Run() to capture the state of + // GTEST_FLAG(catch_exceptions) at the moment it starts. + void set_catch_exceptions(bool value) { catch_exceptions_ = value; } + + // The UnitTest object that owns this implementation object. + UnitTest* const parent_; + + // The working directory when the first TEST() or TEST_F() was + // executed. + internal::FilePath original_working_dir_; + + // The default test part result reporters. + DefaultGlobalTestPartResultReporter default_global_test_part_result_reporter_; + DefaultPerThreadTestPartResultReporter + default_per_thread_test_part_result_reporter_; + + // Points to (but doesn't own) the global test part result reporter. + TestPartResultReporterInterface* global_test_part_result_repoter_; + + // Protects read and write access to global_test_part_result_reporter_. + internal::Mutex global_test_part_result_reporter_mutex_; + + // Points to (but doesn't own) the per-thread test part result reporter. + internal::ThreadLocal + per_thread_test_part_result_reporter_; + + // The vector of environments that need to be set-up/torn-down + // before/after the tests are run. + std::vector environments_; + + // The vector of TestCases in their original order. It owns the + // elements in the vector. + std::vector test_cases_; + + // Provides a level of indirection for the test case list to allow + // easy shuffling and restoring the test case order. The i-th + // element of this vector is the index of the i-th test case in the + // shuffled order. + std::vector test_case_indices_; + +#if GTEST_HAS_PARAM_TEST + // ParameterizedTestRegistry object used to register value-parameterized + // tests. + internal::ParameterizedTestCaseRegistry parameterized_test_registry_; + + // Indicates whether RegisterParameterizedTests() has been called already. + bool parameterized_tests_registered_; +#endif // GTEST_HAS_PARAM_TEST + + // Index of the last death test case registered. Initially -1. + int last_death_test_case_; + + // This points to the TestCase for the currently running test. It + // changes as Google Test goes through one test case after another. + // When no test is running, this is set to NULL and Google Test + // stores assertion results in ad_hoc_test_result_. Initially NULL. + TestCase* current_test_case_; + + // This points to the TestInfo for the currently running test. It + // changes as Google Test goes through one test after another. When + // no test is running, this is set to NULL and Google Test stores + // assertion results in ad_hoc_test_result_. Initially NULL. + TestInfo* current_test_info_; + + // Normally, a user only writes assertions inside a TEST or TEST_F, + // or inside a function called by a TEST or TEST_F. Since Google + // Test keeps track of which test is current running, it can + // associate such an assertion with the test it belongs to. + // + // If an assertion is encountered when no TEST or TEST_F is running, + // Google Test attributes the assertion result to an imaginary "ad hoc" + // test, and records the result in ad_hoc_test_result_. + TestResult ad_hoc_test_result_; + + // The list of event listeners that can be used to track events inside + // Google Test. + TestEventListeners listeners_; + + // The OS stack trace getter. Will be deleted when the UnitTest + // object is destructed. By default, an OsStackTraceGetter is used, + // but the user can set this field to use a custom getter if that is + // desired. + OsStackTraceGetterInterface* os_stack_trace_getter_; + + // True iff PostFlagParsingInit() has been called. + bool post_flag_parse_init_performed_; + + // The random number seed used at the beginning of the test run. + int random_seed_; + + // Our random number generator. + internal::Random random_; + + // The time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp_; + + // How long the test took to run, in milliseconds. + TimeInMillis elapsed_time_; + +#if GTEST_HAS_DEATH_TEST + // The decomposed components of the gtest_internal_run_death_test flag, + // parsed when RUN_ALL_TESTS is called. + internal::scoped_ptr internal_run_death_test_flag_; + internal::scoped_ptr death_test_factory_; +#endif // GTEST_HAS_DEATH_TEST + + // A per-thread stack of traces created by the SCOPED_TRACE() macro. + internal::ThreadLocal > gtest_trace_stack_; + + // The value of GTEST_FLAG(catch_exceptions) at the moment RunAllTests() + // starts. + bool catch_exceptions_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTestImpl); +}; // class UnitTestImpl + +// Convenience function for accessing the global UnitTest +// implementation object. +inline UnitTestImpl* GetUnitTestImpl() { + return UnitTest::GetInstance()->impl(); +} + +#if GTEST_USES_SIMPLE_RE + +// Internal helper functions for implementing the simple regular +// expression matcher. +GTEST_API_ bool IsInSet(char ch, const char* str); +GTEST_API_ bool IsAsciiDigit(char ch); +GTEST_API_ bool IsAsciiPunct(char ch); +GTEST_API_ bool IsRepeat(char ch); +GTEST_API_ bool IsAsciiWhiteSpace(char ch); +GTEST_API_ bool IsAsciiWordChar(char ch); +GTEST_API_ bool IsValidEscape(char ch); +GTEST_API_ bool AtomMatchesChar(bool escaped, char pattern, char ch); +GTEST_API_ bool ValidateRegex(const char* regex); +GTEST_API_ bool MatchRegexAtHead(const char* regex, const char* str); +GTEST_API_ bool MatchRepetitionAndRegexAtHead( + bool escaped, char ch, char repeat, const char* regex, const char* str); +GTEST_API_ bool MatchRegexAnywhere(const char* regex, const char* str); + +#endif // GTEST_USES_SIMPLE_RE + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, char** argv); +GTEST_API_ void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv); + +#if GTEST_HAS_DEATH_TEST + +// Returns the message describing the last system error, regardless of the +// platform. +GTEST_API_ std::string GetLastErrnoDescription(); + +# if GTEST_OS_WINDOWS +// Provides leak-safe Windows kernel handle ownership. +class AutoHandle { + public: + AutoHandle() : handle_(INVALID_HANDLE_VALUE) {} + explicit AutoHandle(HANDLE handle) : handle_(handle) {} + + ~AutoHandle() { Reset(); } + + HANDLE Get() const { return handle_; } + void Reset() { Reset(INVALID_HANDLE_VALUE); } + void Reset(HANDLE handle) { + if (handle != handle_) { + if (handle_ != INVALID_HANDLE_VALUE) + ::CloseHandle(handle_); + handle_ = handle; + } + } + + private: + HANDLE handle_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AutoHandle); +}; +# endif // GTEST_OS_WINDOWS + +// Attempts to parse a string into a positive integer pointed to by the +// number parameter. Returns true if that is possible. +// GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we can use +// it here. +template +bool ParseNaturalNumber(const ::std::string& str, Integer* number) { + // Fail fast if the given string does not begin with a digit; + // this bypasses strtoXXX's "optional leading whitespace and plus + // or minus sign" semantics, which are undesirable here. + if (str.empty() || !IsDigit(str[0])) { + return false; + } + errno = 0; + + char* end; + // BiggestConvertible is the largest integer type that system-provided + // string-to-number conversion routines can return. + +# if GTEST_OS_WINDOWS && !defined(__GNUC__) + + // MSVC and C++ Builder define __int64 instead of the standard long long. + typedef unsigned __int64 BiggestConvertible; + const BiggestConvertible parsed = _strtoui64(str.c_str(), &end, 10); + +# else + + typedef unsigned long long BiggestConvertible; // NOLINT + const BiggestConvertible parsed = strtoull(str.c_str(), &end, 10); + +# endif // GTEST_OS_WINDOWS && !defined(__GNUC__) + + const bool parse_success = *end == '\0' && errno == 0; + + // TODO(vladl@google.com): Convert this to compile time assertion when it is + // available. + GTEST_CHECK_(sizeof(Integer) <= sizeof(parsed)); + + const Integer result = static_cast(parsed); + if (parse_success && static_cast(result) == parsed) { + *number = result; + return true; + } + return false; +} +#endif // GTEST_HAS_DEATH_TEST + +// TestResult contains some private methods that should be hidden from +// Google Test user but are required for testing. This class allow our tests +// to access them. +// +// This class is supplied only for the purpose of testing Google Test's own +// constructs. Do not use it in user tests, either directly or indirectly. +class TestResultAccessor { + public: + static void RecordProperty(TestResult* test_result, + const std::string& xml_element, + const TestProperty& property) { + test_result->RecordProperty(xml_element, property); + } + + static void ClearTestPartResults(TestResult* test_result) { + test_result->ClearTestPartResults(); + } + + static const std::vector& test_part_results( + const TestResult& test_result) { + return test_result.test_part_results(); + } +}; + +#if GTEST_CAN_STREAM_RESULTS_ + +// Streams test results to the given port on the given host machine. +class StreamingListener : public EmptyTestEventListener { + public: + // Abstract base class for writing strings to a socket. + class AbstractSocketWriter { + public: + virtual ~AbstractSocketWriter() {} + + // Sends a string to the socket. + virtual void Send(const string& message) = 0; + + // Closes the socket. + virtual void CloseConnection() {} + + // Sends a string and a newline to the socket. + void SendLn(const string& message) { + Send(message + "\n"); + } + }; + + // Concrete class for actually writing strings to a socket. + class SocketWriter : public AbstractSocketWriter { + public: + SocketWriter(const string& host, const string& port) + : sockfd_(-1), host_name_(host), port_num_(port) { + MakeConnection(); + } + + virtual ~SocketWriter() { + if (sockfd_ != -1) + CloseConnection(); + } + + // Sends a string to the socket. + virtual void Send(const string& message) { + GTEST_CHECK_(sockfd_ != -1) + << "Send() can be called only when there is a connection."; + + const int len = static_cast(message.length()); + if (write(sockfd_, message.c_str(), len) != len) { + GTEST_LOG_(WARNING) + << "stream_result_to: failed to stream to " + << host_name_ << ":" << port_num_; + } + } + + private: + // Creates a client socket and connects to the server. + void MakeConnection(); + + // Closes the socket. + void CloseConnection() { + GTEST_CHECK_(sockfd_ != -1) + << "CloseConnection() can be called only when there is a connection."; + + close(sockfd_); + sockfd_ = -1; + } + + int sockfd_; // socket file descriptor + const string host_name_; + const string port_num_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(SocketWriter); + }; // class SocketWriter + + // Escapes '=', '&', '%', and '\n' characters in str as "%xx". + static string UrlEncode(const char* str); + + StreamingListener(const string& host, const string& port) + : socket_writer_(new SocketWriter(host, port)) { Start(); } + + explicit StreamingListener(AbstractSocketWriter* socket_writer) + : socket_writer_(socket_writer) { Start(); } + + void OnTestProgramStart(const UnitTest& /* unit_test */) { + SendLn("event=TestProgramStart"); + } + + void OnTestProgramEnd(const UnitTest& unit_test) { + // Note that Google Test current only report elapsed time for each + // test iteration, not for the entire test program. + SendLn("event=TestProgramEnd&passed=" + FormatBool(unit_test.Passed())); + + // Notify the streaming server to stop. + socket_writer_->CloseConnection(); + } + + void OnTestIterationStart(const UnitTest& /* unit_test */, int iteration) { + SendLn("event=TestIterationStart&iteration=" + + StreamableToString(iteration)); + } + + void OnTestIterationEnd(const UnitTest& unit_test, int /* iteration */) { + SendLn("event=TestIterationEnd&passed=" + + FormatBool(unit_test.Passed()) + "&elapsed_time=" + + StreamableToString(unit_test.elapsed_time()) + "ms"); + } + + void OnTestCaseStart(const TestCase& test_case) { + SendLn(std::string("event=TestCaseStart&name=") + test_case.name()); + } + + void OnTestCaseEnd(const TestCase& test_case) { + SendLn("event=TestCaseEnd&passed=" + FormatBool(test_case.Passed()) + + "&elapsed_time=" + StreamableToString(test_case.elapsed_time()) + + "ms"); + } + + void OnTestStart(const TestInfo& test_info) { + SendLn(std::string("event=TestStart&name=") + test_info.name()); + } + + void OnTestEnd(const TestInfo& test_info) { + SendLn("event=TestEnd&passed=" + + FormatBool((test_info.result())->Passed()) + + "&elapsed_time=" + + StreamableToString((test_info.result())->elapsed_time()) + "ms"); + } + + void OnTestPartResult(const TestPartResult& test_part_result) { + const char* file_name = test_part_result.file_name(); + if (file_name == NULL) + file_name = ""; + SendLn("event=TestPartResult&file=" + UrlEncode(file_name) + + "&line=" + StreamableToString(test_part_result.line_number()) + + "&message=" + UrlEncode(test_part_result.message())); + } + + private: + // Sends the given message and a newline to the socket. + void SendLn(const string& message) { socket_writer_->SendLn(message); } + + // Called at the start of streaming to notify the receiver what + // protocol we are using. + void Start() { SendLn("gtest_streaming_protocol_version=1.0"); } + + string FormatBool(bool value) { return value ? "1" : "0"; } + + const scoped_ptr socket_writer_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(StreamingListener); +}; // class StreamingListener + +#endif // GTEST_CAN_STREAM_RESULTS_ + +} // namespace internal +} // namespace testing + +#endif // GTEST_SRC_GTEST_INTERNAL_INL_H_ +#undef GTEST_IMPLEMENTATION_ + +#if GTEST_OS_WINDOWS +# define vsnprintf _vsnprintf +#endif // GTEST_OS_WINDOWS + +namespace testing { + +using internal::CountIf; +using internal::ForEach; +using internal::GetElementOr; +using internal::Shuffle; + +// Constants. + +// A test whose test case name or test name matches this filter is +// disabled and not run. +static const char kDisableTestFilter[] = "DISABLED_*:*/DISABLED_*"; + +// A test case whose name matches this filter is considered a death +// test case and will be run before test cases whose name doesn't +// match this filter. +static const char kDeathTestCaseFilter[] = "*DeathTest:*DeathTest/*"; + +// A test filter that matches everything. +static const char kUniversalFilter[] = "*"; + +// The default output file for XML output. +static const char kDefaultOutputFile[] = "test_detail.xml"; + +// The environment variable name for the test shard index. +static const char kTestShardIndex[] = "GTEST_SHARD_INDEX"; +// The environment variable name for the total number of test shards. +static const char kTestTotalShards[] = "GTEST_TOTAL_SHARDS"; +// The environment variable name for the test shard status file. +static const char kTestShardStatusFile[] = "GTEST_SHARD_STATUS_FILE"; + +namespace internal { + +// The text used in failure messages to indicate the start of the +// stack trace. +const char kStackTraceMarker[] = "\nStack trace:\n"; + +// g_help_flag is true iff the --help flag or an equivalent form is +// specified on the command line. +bool g_help_flag = false; + +} // namespace internal + +static const char* GetDefaultFilter() { + return kUniversalFilter; +} + +GTEST_DEFINE_bool_( + also_run_disabled_tests, + internal::BoolFromGTestEnv("also_run_disabled_tests", false), + "Run disabled tests too, in addition to the tests normally being run."); + +GTEST_DEFINE_bool_( + break_on_failure, + internal::BoolFromGTestEnv("break_on_failure", false), + "True iff a failed assertion should be a debugger break-point."); + +GTEST_DEFINE_bool_( + catch_exceptions, + internal::BoolFromGTestEnv("catch_exceptions", true), + "True iff " GTEST_NAME_ + " should catch exceptions and treat them as test failures."); + +GTEST_DEFINE_string_( + color, + internal::StringFromGTestEnv("color", "auto"), + "Whether to use colors in the output. Valid values: yes, no, " + "and auto. 'auto' means to use colors if the output is " + "being sent to a terminal and the TERM environment variable " + "is set to a terminal type that supports colors."); + +GTEST_DEFINE_string_( + filter, + internal::StringFromGTestEnv("filter", GetDefaultFilter()), + "A colon-separated list of glob (not regex) patterns " + "for filtering the tests to run, optionally followed by a " + "'-' and a : separated list of negative patterns (tests to " + "exclude). A test is run if it matches one of the positive " + "patterns and does not match any of the negative patterns."); + +GTEST_DEFINE_bool_(list_tests, false, + "List all tests without running them."); + +GTEST_DEFINE_string_( + output, + internal::StringFromGTestEnv("output", ""), + "A format (currently must be \"xml\"), optionally followed " + "by a colon and an output file name or directory. A directory " + "is indicated by a trailing pathname separator. " + "Examples: \"xml:filename.xml\", \"xml::directoryname/\". " + "If a directory is specified, output files will be created " + "within that directory, with file-names based on the test " + "executable's name and, if necessary, made unique by adding " + "digits."); + +GTEST_DEFINE_bool_( + print_time, + internal::BoolFromGTestEnv("print_time", true), + "True iff " GTEST_NAME_ + " should display elapsed time in text output."); + +GTEST_DEFINE_int32_( + random_seed, + internal::Int32FromGTestEnv("random_seed", 0), + "Random number seed to use when shuffling test orders. Must be in range " + "[1, 99999], or 0 to use a seed based on the current time."); + +GTEST_DEFINE_int32_( + repeat, + internal::Int32FromGTestEnv("repeat", 1), + "How many times to repeat each test. Specify a negative number " + "for repeating forever. Useful for shaking out flaky tests."); + +GTEST_DEFINE_bool_( + show_internal_stack_frames, false, + "True iff " GTEST_NAME_ " should include internal stack frames when " + "printing test failure stack traces."); + +GTEST_DEFINE_bool_( + shuffle, + internal::BoolFromGTestEnv("shuffle", false), + "True iff " GTEST_NAME_ + " should randomize tests' order on every run."); + +GTEST_DEFINE_int32_( + stack_trace_depth, + internal::Int32FromGTestEnv("stack_trace_depth", kMaxStackTraceDepth), + "The maximum number of stack frames to print when an " + "assertion fails. The valid range is 0 through 100, inclusive."); + +GTEST_DEFINE_string_( + stream_result_to, + internal::StringFromGTestEnv("stream_result_to", ""), + "This flag specifies the host name and the port number on which to stream " + "test results. Example: \"localhost:555\". The flag is effective only on " + "Linux."); + +GTEST_DEFINE_bool_( + throw_on_failure, + internal::BoolFromGTestEnv("throw_on_failure", false), + "When this flag is specified, a failed assertion will throw an exception " + "if exceptions are enabled or exit the program with a non-zero code " + "otherwise."); + +namespace internal { + +// Generates a random number from [0, range), using a Linear +// Congruential Generator (LCG). Crashes if 'range' is 0 or greater +// than kMaxRange. +UInt32 Random::Generate(UInt32 range) { + // These constants are the same as are used in glibc's rand(3). + state_ = (1103515245U*state_ + 12345U) % kMaxRange; + + GTEST_CHECK_(range > 0) + << "Cannot generate a number in the range [0, 0)."; + GTEST_CHECK_(range <= kMaxRange) + << "Generation of a number in [0, " << range << ") was requested, " + << "but this can only generate numbers in [0, " << kMaxRange << ")."; + + // Converting via modulus introduces a bit of downward bias, but + // it's simple, and a linear congruential generator isn't too good + // to begin with. + return state_ % range; +} + +// GTestIsInitialized() returns true iff the user has initialized +// Google Test. Useful for catching the user mistake of not initializing +// Google Test before calling RUN_ALL_TESTS(). +// +// A user must call testing::InitGoogleTest() to initialize Google +// Test. g_init_gtest_count is set to the number of times +// InitGoogleTest() has been called. We don't protect this variable +// under a mutex as it is only accessed in the main thread. +GTEST_API_ int g_init_gtest_count = 0; +static bool GTestIsInitialized() { return g_init_gtest_count != 0; } + +// Iterates over a vector of TestCases, keeping a running sum of the +// results of calling a given int-returning method on each. +// Returns the sum. +static int SumOverTestCaseList(const std::vector& case_list, + int (TestCase::*method)() const) { + int sum = 0; + for (size_t i = 0; i < case_list.size(); i++) { + sum += (case_list[i]->*method)(); + } + return sum; +} + +// Returns true iff the test case passed. +static bool TestCasePassed(const TestCase* test_case) { + return test_case->should_run() && test_case->Passed(); +} + +// Returns true iff the test case failed. +static bool TestCaseFailed(const TestCase* test_case) { + return test_case->should_run() && test_case->Failed(); +} + +// Returns true iff test_case contains at least one test that should +// run. +static bool ShouldRunTestCase(const TestCase* test_case) { + return test_case->should_run(); +} + +// AssertHelper constructor. +AssertHelper::AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message) + : data_(new AssertHelperData(type, file, line, message)) { +} + +AssertHelper::~AssertHelper() { + delete data_; +} + +// Message assignment, for assertion streaming support. +void AssertHelper::operator=(const Message& message) const { + UnitTest::GetInstance()-> + AddTestPartResult(data_->type, data_->file, data_->line, + AppendUserMessage(data_->message, message), + UnitTest::GetInstance()->impl() + ->CurrentOsStackTraceExceptTop(1) + // Skips the stack frame for this function itself. + ); // NOLINT +} + +// Mutex for linked pointers. +GTEST_API_ GTEST_DEFINE_STATIC_MUTEX_(g_linked_ptr_mutex); + +// Application pathname gotten in InitGoogleTest. +std::string g_executable_path; + +// Returns the current application's name, removing directory path if that +// is present. +FilePath GetCurrentExecutableName() { + FilePath result; + +#if GTEST_OS_WINDOWS + result.Set(FilePath(g_executable_path).RemoveExtension("exe")); +#else + result.Set(FilePath(g_executable_path)); +#endif // GTEST_OS_WINDOWS + + return result.RemoveDirectoryName(); +} + +// Functions for processing the gtest_output flag. + +// Returns the output format, or "" for normal printed output. +std::string UnitTestOptions::GetOutputFormat() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) return std::string(""); + + const char* const colon = strchr(gtest_output_flag, ':'); + return (colon == NULL) ? + std::string(gtest_output_flag) : + std::string(gtest_output_flag, colon - gtest_output_flag); +} + +// Returns the name of the requested output file, or the default if none +// was explicitly specified. +std::string UnitTestOptions::GetAbsolutePathToOutputFile() { + const char* const gtest_output_flag = GTEST_FLAG(output).c_str(); + if (gtest_output_flag == NULL) + return ""; + + const char* const colon = strchr(gtest_output_flag, ':'); + if (colon == NULL) + return internal::FilePath::ConcatPaths( + internal::FilePath( + UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(kDefaultOutputFile)).string(); + + internal::FilePath output_name(colon + 1); + if (!output_name.IsAbsolutePath()) + // TODO(wan@google.com): on Windows \some\path is not an absolute + // path (as its meaning depends on the current drive), yet the + // following logic for turning it into an absolute path is wrong. + // Fix it. + output_name = internal::FilePath::ConcatPaths( + internal::FilePath(UnitTest::GetInstance()->original_working_dir()), + internal::FilePath(colon + 1)); + + if (!output_name.IsDirectory()) + return output_name.string(); + + internal::FilePath result(internal::FilePath::GenerateUniqueFileName( + output_name, internal::GetCurrentExecutableName(), + GetOutputFormat().c_str())); + return result.string(); +} + +// Returns true iff the wildcard pattern matches the string. The +// first ':' or '\0' character in pattern marks the end of it. +// +// This recursive algorithm isn't very efficient, but is clear and +// works well enough for matching test names, which are short. +bool UnitTestOptions::PatternMatchesString(const char *pattern, + const char *str) { + switch (*pattern) { + case '\0': + case ':': // Either ':' or '\0' marks the end of the pattern. + return *str == '\0'; + case '?': // Matches any single character. + return *str != '\0' && PatternMatchesString(pattern + 1, str + 1); + case '*': // Matches any string (possibly empty) of characters. + return (*str != '\0' && PatternMatchesString(pattern, str + 1)) || + PatternMatchesString(pattern + 1, str); + default: // Non-special character. Matches itself. + return *pattern == *str && + PatternMatchesString(pattern + 1, str + 1); + } +} + +bool UnitTestOptions::MatchesFilter( + const std::string& name, const char* filter) { + const char *cur_pattern = filter; + for (;;) { + if (PatternMatchesString(cur_pattern, name.c_str())) { + return true; + } + + // Finds the next pattern in the filter. + cur_pattern = strchr(cur_pattern, ':'); + + // Returns if no more pattern can be found. + if (cur_pattern == NULL) { + return false; + } + + // Skips the pattern separater (the ':' character). + cur_pattern++; + } +} + +// Returns true iff the user-specified filter matches the test case +// name and the test name. +bool UnitTestOptions::FilterMatchesTest(const std::string &test_case_name, + const std::string &test_name) { + const std::string& full_name = test_case_name + "." + test_name.c_str(); + + // Split --gtest_filter at '-', if there is one, to separate into + // positive filter and negative filter portions + const char* const p = GTEST_FLAG(filter).c_str(); + const char* const dash = strchr(p, '-'); + std::string positive; + std::string negative; + if (dash == NULL) { + positive = GTEST_FLAG(filter).c_str(); // Whole string is a positive filter + negative = ""; + } else { + positive = std::string(p, dash); // Everything up to the dash + negative = std::string(dash + 1); // Everything after the dash + if (positive.empty()) { + // Treat '-test1' as the same as '*-test1' + positive = kUniversalFilter; + } + } + + // A filter is a colon-separated list of patterns. It matches a + // test if any pattern in it matches the test. + return (MatchesFilter(full_name, positive.c_str()) && + !MatchesFilter(full_name, negative.c_str())); +} + +#if GTEST_HAS_SEH +// Returns EXCEPTION_EXECUTE_HANDLER if Google Test should handle the +// given SEH exception, or EXCEPTION_CONTINUE_SEARCH otherwise. +// This function is useful as an __except condition. +int UnitTestOptions::GTestShouldProcessSEH(DWORD exception_code) { + // Google Test should handle a SEH exception if: + // 1. the user wants it to, AND + // 2. this is not a breakpoint exception, AND + // 3. this is not a C++ exception (VC++ implements them via SEH, + // apparently). + // + // SEH exception code for C++ exceptions. + // (see http://support.microsoft.com/kb/185294 for more information). + const DWORD kCxxExceptionCode = 0xe06d7363; + + bool should_handle = true; + + if (!GTEST_FLAG(catch_exceptions)) + should_handle = false; + else if (exception_code == EXCEPTION_BREAKPOINT) + should_handle = false; + else if (exception_code == kCxxExceptionCode) + should_handle = false; + + return should_handle ? EXCEPTION_EXECUTE_HANDLER : EXCEPTION_CONTINUE_SEARCH; +} +#endif // GTEST_HAS_SEH + +} // namespace internal + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. Intercepts only failures from the current thread. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + TestPartResultArray* result) + : intercept_mode_(INTERCEPT_ONLY_CURRENT_THREAD), + result_(result) { + Init(); +} + +// The c'tor sets this object as the test part result reporter used by +// Google Test. The 'result' parameter specifies where to report the +// results. +ScopedFakeTestPartResultReporter::ScopedFakeTestPartResultReporter( + InterceptMode intercept_mode, TestPartResultArray* result) + : intercept_mode_(intercept_mode), + result_(result) { + Init(); +} + +void ScopedFakeTestPartResultReporter::Init() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + old_reporter_ = impl->GetGlobalTestPartResultReporter(); + impl->SetGlobalTestPartResultReporter(this); + } else { + old_reporter_ = impl->GetTestPartResultReporterForCurrentThread(); + impl->SetTestPartResultReporterForCurrentThread(this); + } +} + +// The d'tor restores the test part result reporter used by Google Test +// before. +ScopedFakeTestPartResultReporter::~ScopedFakeTestPartResultReporter() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + if (intercept_mode_ == INTERCEPT_ALL_THREADS) { + impl->SetGlobalTestPartResultReporter(old_reporter_); + } else { + impl->SetTestPartResultReporterForCurrentThread(old_reporter_); + } +} + +// Increments the test part result count and remembers the result. +// This method is from the TestPartResultReporterInterface interface. +void ScopedFakeTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + result_->Append(result); +} + +namespace internal { + +// Returns the type ID of ::testing::Test. We should always call this +// instead of GetTypeId< ::testing::Test>() to get the type ID of +// testing::Test. This is to work around a suspected linker bug when +// using Google Test as a framework on Mac OS X. The bug causes +// GetTypeId< ::testing::Test>() to return different values depending +// on whether the call is from the Google Test framework itself or +// from user test code. GetTestTypeId() is guaranteed to always +// return the same value, as it always calls GetTypeId<>() from the +// gtest.cc, which is within the Google Test framework. +TypeId GetTestTypeId() { + return GetTypeId(); +} + +// The value of GetTestTypeId() as seen from within the Google Test +// library. This is solely for testing GetTestTypeId(). +extern const TypeId kTestTypeIdInGoogleTest = GetTestTypeId(); + +// This predicate-formatter checks that 'results' contains a test part +// failure of the given type and that the failure message contains the +// given substring. +AssertionResult HasOneFailure(const char* /* results_expr */, + const char* /* type_expr */, + const char* /* substr_expr */, + const TestPartResultArray& results, + TestPartResult::Type type, + const string& substr) { + const std::string expected(type == TestPartResult::kFatalFailure ? + "1 fatal failure" : + "1 non-fatal failure"); + Message msg; + if (results.size() != 1) { + msg << "Expected: " << expected << "\n" + << " Actual: " << results.size() << " failures"; + for (int i = 0; i < results.size(); i++) { + msg << "\n" << results.GetTestPartResult(i); + } + return AssertionFailure() << msg; + } + + const TestPartResult& r = results.GetTestPartResult(0); + if (r.type() != type) { + return AssertionFailure() << "Expected: " << expected << "\n" + << " Actual:\n" + << r; + } + + if (strstr(r.message(), substr.c_str()) == NULL) { + return AssertionFailure() << "Expected: " << expected << " containing \"" + << substr << "\"\n" + << " Actual:\n" + << r; + } + + return AssertionSuccess(); +} + +// The constructor of SingleFailureChecker remembers where to look up +// test part results, what type of failure we expect, and what +// substring the failure message should contain. +SingleFailureChecker:: SingleFailureChecker( + const TestPartResultArray* results, + TestPartResult::Type type, + const string& substr) + : results_(results), + type_(type), + substr_(substr) {} + +// The destructor of SingleFailureChecker verifies that the given +// TestPartResultArray contains exactly one failure that has the given +// type and contains the given substring. If that's not the case, a +// non-fatal failure will be generated. +SingleFailureChecker::~SingleFailureChecker() { + EXPECT_PRED_FORMAT3(HasOneFailure, *results_, type_, substr_); +} + +DefaultGlobalTestPartResultReporter::DefaultGlobalTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultGlobalTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->current_test_result()->AddTestPartResult(result); + unit_test_->listeners()->repeater()->OnTestPartResult(result); +} + +DefaultPerThreadTestPartResultReporter::DefaultPerThreadTestPartResultReporter( + UnitTestImpl* unit_test) : unit_test_(unit_test) {} + +void DefaultPerThreadTestPartResultReporter::ReportTestPartResult( + const TestPartResult& result) { + unit_test_->GetGlobalTestPartResultReporter()->ReportTestPartResult(result); +} + +// Returns the global test part result reporter. +TestPartResultReporterInterface* +UnitTestImpl::GetGlobalTestPartResultReporter() { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + return global_test_part_result_repoter_; +} + +// Sets the global test part result reporter. +void UnitTestImpl::SetGlobalTestPartResultReporter( + TestPartResultReporterInterface* reporter) { + internal::MutexLock lock(&global_test_part_result_reporter_mutex_); + global_test_part_result_repoter_ = reporter; +} + +// Returns the test part result reporter for the current thread. +TestPartResultReporterInterface* +UnitTestImpl::GetTestPartResultReporterForCurrentThread() { + return per_thread_test_part_result_reporter_.get(); +} + +// Sets the test part result reporter for the current thread. +void UnitTestImpl::SetTestPartResultReporterForCurrentThread( + TestPartResultReporterInterface* reporter) { + per_thread_test_part_result_reporter_.set(reporter); +} + +// Gets the number of successful test cases. +int UnitTestImpl::successful_test_case_count() const { + return CountIf(test_cases_, TestCasePassed); +} + +// Gets the number of failed test cases. +int UnitTestImpl::failed_test_case_count() const { + return CountIf(test_cases_, TestCaseFailed); +} + +// Gets the number of all test cases. +int UnitTestImpl::total_test_case_count() const { + return static_cast(test_cases_.size()); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTestImpl::test_case_to_run_count() const { + return CountIf(test_cases_, ShouldRunTestCase); +} + +// Gets the number of successful tests. +int UnitTestImpl::successful_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::successful_test_count); +} + +// Gets the number of failed tests. +int UnitTestImpl::failed_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::failed_test_count); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTestImpl::reportable_disabled_test_count() const { + return SumOverTestCaseList(test_cases_, + &TestCase::reportable_disabled_test_count); +} + +// Gets the number of disabled tests. +int UnitTestImpl::disabled_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::disabled_test_count); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTestImpl::reportable_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::reportable_test_count); +} + +// Gets the number of all tests. +int UnitTestImpl::total_test_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::total_test_count); +} + +// Gets the number of tests that should run. +int UnitTestImpl::test_to_run_count() const { + return SumOverTestCaseList(test_cases_, &TestCase::test_to_run_count); +} + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// CurrentOsStackTraceExceptTop(1), Foo() will be included in the +// trace but Bar() and CurrentOsStackTraceExceptTop() won't. +std::string UnitTestImpl::CurrentOsStackTraceExceptTop(int skip_count) { + (void)skip_count; + return ""; +} + +// Returns the current time in milliseconds. +TimeInMillis GetTimeInMillis() { +#if GTEST_OS_WINDOWS_MOBILE || defined(__BORLANDC__) + // Difference between 1970-01-01 and 1601-01-01 in milliseconds. + // http://analogous.blogspot.com/2005/04/epoch.html + const TimeInMillis kJavaEpochToWinFileTimeDelta = + static_cast(116444736UL) * 100000UL; + const DWORD kTenthMicrosInMilliSecond = 10000; + + SYSTEMTIME now_systime; + FILETIME now_filetime; + ULARGE_INTEGER now_int64; + // TODO(kenton@google.com): Shouldn't this just use + // GetSystemTimeAsFileTime()? + GetSystemTime(&now_systime); + if (SystemTimeToFileTime(&now_systime, &now_filetime)) { + now_int64.LowPart = now_filetime.dwLowDateTime; + now_int64.HighPart = now_filetime.dwHighDateTime; + now_int64.QuadPart = (now_int64.QuadPart / kTenthMicrosInMilliSecond) - + kJavaEpochToWinFileTimeDelta; + return now_int64.QuadPart; + } + return 0; +#elif GTEST_OS_WINDOWS && !GTEST_HAS_GETTIMEOFDAY_ + __timeb64 now; + +# ifdef _MSC_VER + + // MSVC 8 deprecates _ftime64(), so we want to suppress warning 4996 + // (deprecated function) there. + // TODO(kenton@google.com): Use GetTickCount()? Or use + // SystemTimeToFileTime() +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996. + _ftime64(&now); +# pragma warning(pop) // Restores the warning state. +# else + + _ftime64(&now); + +# endif // _MSC_VER + + return static_cast(now.time) * 1000 + now.millitm; +#elif GTEST_HAS_GETTIMEOFDAY_ + struct timeval now; + gettimeofday(&now, NULL); + return static_cast(now.tv_sec) * 1000 + now.tv_usec / 1000; +#else +# error "Don't know how to get the current time on your system." +#endif +} + +// Utilities + +// class String. + +#if GTEST_OS_WINDOWS_MOBILE +// Creates a UTF-16 wide string from the given ANSI string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the wide string, or NULL if the +// input is NULL. +LPCWSTR String::AnsiToUtf16(const char* ansi) { + if (!ansi) return NULL; + const int length = strlen(ansi); + const int unicode_length = + MultiByteToWideChar(CP_ACP, 0, ansi, length, + NULL, 0); + WCHAR* unicode = new WCHAR[unicode_length + 1]; + MultiByteToWideChar(CP_ACP, 0, ansi, length, + unicode, unicode_length); + unicode[unicode_length] = 0; + return unicode; +} + +// Creates an ANSI string from the given wide string, allocating +// memory using new. The caller is responsible for deleting the return +// value using delete[]. Returns the ANSI string, or NULL if the +// input is NULL. +const char* String::Utf16ToAnsi(LPCWSTR utf16_str) { + if (!utf16_str) return NULL; + const int ansi_length = + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, + NULL, 0, NULL, NULL); + char* ansi = new char[ansi_length + 1]; + WideCharToMultiByte(CP_ACP, 0, utf16_str, -1, + ansi, ansi_length, NULL, NULL); + ansi[ansi_length] = 0; + return ansi; +} + +#endif // GTEST_OS_WINDOWS_MOBILE + +// Compares two C strings. Returns true iff they have the same content. +// +// Unlike strcmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CStringEquals(const char * lhs, const char * rhs) { + if ( lhs == NULL ) return rhs == NULL; + + if ( rhs == NULL ) return false; + + return strcmp(lhs, rhs) == 0; +} + +#if GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +// Converts an array of wide chars to a narrow string using the UTF-8 +// encoding, and streams the result to the given Message object. +static void StreamWideCharsToMessage(const wchar_t* wstr, size_t length, + Message* msg) { + for (size_t i = 0; i != length; ) { // NOLINT + if (wstr[i] != L'\0') { + *msg << WideStringToUtf8(wstr + i, static_cast(length - i)); + while (i != length && wstr[i] != L'\0') + i++; + } else { + *msg << '\0'; + i++; + } + } +} + +#endif // GTEST_HAS_STD_WSTRING || GTEST_HAS_GLOBAL_WSTRING + +} // namespace internal + +// Constructs an empty Message. +// We allocate the stringstream separately because otherwise each use of +// ASSERT/EXPECT in a procedure adds over 200 bytes to the procedure's +// stack frame leading to huge stack frames in some cases; gcc does not reuse +// the stack space. +Message::Message() : ss_(new ::std::stringstream) { + // By default, we want there to be enough precision when printing + // a double to a Message. + *ss_ << std::setprecision(std::numeric_limits::digits10 + 2); +} + +// These two overloads allow streaming a wide C string to a Message +// using the UTF-8 encoding. +Message& Message::operator <<(const wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} +Message& Message::operator <<(wchar_t* wide_c_str) { + return *this << internal::String::ShowWideCString(wide_c_str); +} + +#if GTEST_HAS_STD_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::std::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING +// Converts the given wide string to a narrow string using the UTF-8 +// encoding, and streams the result to this Message object. +Message& Message::operator <<(const ::wstring& wstr) { + internal::StreamWideCharsToMessage(wstr.c_str(), wstr.length(), this); + return *this; +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +// Gets the text streamed to this object so far as an std::string. +// Each '\0' character in the buffer is replaced with "\\0". +std::string Message::GetString() const { + return internal::StringStreamToString(ss_.get()); +} + +// AssertionResult constructors. +// Used in EXPECT_TRUE/FALSE(assertion_result). +AssertionResult::AssertionResult(const AssertionResult& other) + : success_(other.success_), + message_(other.message_.get() != NULL ? + new ::std::string(*other.message_) : + static_cast< ::std::string*>(NULL)) { +} + +// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. +AssertionResult AssertionResult::operator!() const { + AssertionResult negation(!success_); + if (message_.get() != NULL) + negation << *message_; + return negation; +} + +// Makes a successful assertion result. +AssertionResult AssertionSuccess() { + return AssertionResult(true); +} + +// Makes a failed assertion result. +AssertionResult AssertionFailure() { + return AssertionResult(false); +} + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << message. +AssertionResult AssertionFailure(const Message& message) { + return AssertionFailure() << message; +} + +namespace internal { + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case) { + Message msg; + msg << "Value of: " << actual_expression; + if (actual_value != actual_expression) { + msg << "\n Actual: " << actual_value; + } + + msg << "\nExpected: " << expected_expression; + if (ignoring_case) { + msg << " (ignoring case)"; + } + if (expected_value != expected_expression) { + msg << "\nWhich is: " << expected_value; + } + + return AssertionFailure() << msg; +} + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value) { + const char* actual_message = assertion_result.message(); + Message msg; + msg << "Value of: " << expression_text + << "\n Actual: " << actual_predicate_value; + if (actual_message[0] != '\0') + msg << " (" << actual_message << ")"; + msg << "\nExpected: " << expected_predicate_value; + return msg.GetString(); +} + +// Helper function for implementing ASSERT_NEAR. +AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error) { + const double diff = fabs(val1 - val2); + if (diff <= abs_error) return AssertionSuccess(); + + // TODO(wan): do not print the value of an expression if it's + // already a literal. + return AssertionFailure() + << "The difference between " << expr1 << " and " << expr2 + << " is " << diff << ", which exceeds " << abs_error_expr << ", where\n" + << expr1 << " evaluates to " << val1 << ",\n" + << expr2 << " evaluates to " << val2 << ", and\n" + << abs_error_expr << " evaluates to " << abs_error << "."; +} + + +// Helper template for implementing FloatLE() and DoubleLE(). +template +AssertionResult FloatingPointLE(const char* expr1, + const char* expr2, + RawType val1, + RawType val2) { + // Returns success if val1 is less than val2, + if (val1 < val2) { + return AssertionSuccess(); + } + + // or if val1 is almost equal to val2. + const FloatingPoint lhs(val1), rhs(val2); + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + // Note that the above two checks will both fail if either val1 or + // val2 is NaN, as the IEEE floating-point standard requires that + // any predicate involving a NaN must return false. + + ::std::stringstream val1_ss; + val1_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val1; + + ::std::stringstream val2_ss; + val2_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << val2; + + return AssertionFailure() + << "Expected: (" << expr1 << ") <= (" << expr2 << ")\n" + << " Actual: " << StringStreamToString(&val1_ss) << " vs " + << StringStreamToString(&val2_ss); +} + +} // namespace internal + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2) { + return internal::FloatingPointLE(expr1, expr2, val1, val2); +} + +namespace internal { + +// The helper function for {ASSERT|EXPECT}_EQ with int or enum +// arguments. +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + if (expected == actual) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_?? with integer or enum arguments. It is here +// just to avoid copy-and-paste of similar code. +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + BiggestInt val1, BiggestInt val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return AssertionFailure() \ + << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + }\ +} + +// Implements the helper function for {ASSERT|EXPECT}_NE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(NE, !=) +// Implements the helper function for {ASSERT|EXPECT}_LE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(LE, <=) +// Implements the helper function for {ASSERT|EXPECT}_LT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(LT, < ) +// Implements the helper function for {ASSERT|EXPECT}_GE with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(GE, >=) +// Implements the helper function for {ASSERT|EXPECT}_GT with int or +// enum arguments. +GTEST_IMPL_CMP_HELPER_(GT, > ) + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + false); +} + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual) { + if (String::CaseInsensitiveCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + true); +} + +// The helper function for {ASSERT|EXPECT}_STRNE. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2) { + if (!String::CaseInsensitiveCStringEquals(s1, s2)) { + return AssertionSuccess(); + } else { + return AssertionFailure() + << "Expected: (" << s1_expression << ") != (" + << s2_expression << ") (ignoring case), actual: \"" + << s1 << "\" vs \"" << s2 << "\""; + } +} + +} // namespace internal + +namespace { + +// Helper functions for implementing IsSubString() and IsNotSubstring(). + +// This group of overloaded functions return true iff needle is a +// substring of haystack. NULL is considered a substring of itself +// only. + +bool IsSubstringPred(const char* needle, const char* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return strstr(haystack, needle) != NULL; +} + +bool IsSubstringPred(const wchar_t* needle, const wchar_t* haystack) { + if (needle == NULL || haystack == NULL) + return needle == haystack; + + return wcsstr(haystack, needle) != NULL; +} + +// StringType here can be either ::std::string or ::std::wstring. +template +bool IsSubstringPred(const StringType& needle, + const StringType& haystack) { + return haystack.find(needle) != StringType::npos; +} + +// This function implements either IsSubstring() or IsNotSubstring(), +// depending on the value of the expected_to_be_substring parameter. +// StringType here can be const char*, const wchar_t*, ::std::string, +// or ::std::wstring. +template +AssertionResult IsSubstringImpl( + bool expected_to_be_substring, + const char* needle_expr, const char* haystack_expr, + const StringType& needle, const StringType& haystack) { + if (IsSubstringPred(needle, haystack) == expected_to_be_substring) + return AssertionSuccess(); + + const bool is_wide_string = sizeof(needle[0]) > 1; + const char* const begin_string_quote = is_wide_string ? "L\"" : "\""; + return AssertionFailure() + << "Value of: " << needle_expr << "\n" + << " Actual: " << begin_string_quote << needle << "\"\n" + << "Expected: " << (expected_to_be_substring ? "" : "not ") + << "a substring of " << haystack_expr << "\n" + << "Which is: " << begin_string_quote << haystack << "\""; +} + +} // namespace + +// IsSubstring() and IsNotSubstring() check whether needle is a +// substring of haystack (NULL is considered a substring of itself +// only), and return an appropriate error message when they fail. + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} + +#if GTEST_HAS_STD_WSTRING +AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(true, needle_expr, haystack_expr, needle, haystack); +} + +AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack) { + return IsSubstringImpl(false, needle_expr, haystack_expr, needle, haystack); +} +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +#if GTEST_OS_WINDOWS + +namespace { + +// Helper function for IsHRESULT{SuccessFailure} predicates +AssertionResult HRESULTFailureHelper(const char* expr, + const char* expected, + long hr) { // NOLINT +# if GTEST_OS_WINDOWS_MOBILE + + // Windows CE doesn't support FormatMessage. + const char error_text[] = ""; + +# else + + // Looks up the human-readable system message for the HRESULT code + // and since we're not passing any params to FormatMessage, we don't + // want inserts expanded. + const DWORD kFlags = FORMAT_MESSAGE_FROM_SYSTEM | + FORMAT_MESSAGE_IGNORE_INSERTS; + const DWORD kBufSize = 4096; + // Gets the system's human readable message string for this HRESULT. + char error_text[kBufSize] = { '\0' }; + DWORD message_length = ::FormatMessageA(kFlags, + 0, // no source, we're asking system + hr, // the error + 0, // no line width restrictions + error_text, // output buffer + kBufSize, // buf size + NULL); // no arguments for inserts + // Trims tailing white space (FormatMessage leaves a trailing CR-LF) + for (; message_length && IsSpace(error_text[message_length - 1]); + --message_length) { + error_text[message_length - 1] = '\0'; + } + +# endif // GTEST_OS_WINDOWS_MOBILE + + const std::string error_hex("0x" + String::FormatHexInt(hr)); + return ::testing::AssertionFailure() + << "Expected: " << expr << " " << expected << ".\n" + << " Actual: " << error_hex << " " << error_text << "\n"; +} + +} // namespace + +AssertionResult IsHRESULTSuccess(const char* expr, long hr) { // NOLINT + if (SUCCEEDED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "succeeds", hr); +} + +AssertionResult IsHRESULTFailure(const char* expr, long hr) { // NOLINT + if (FAILED(hr)) { + return AssertionSuccess(); + } + return HRESULTFailureHelper(expr, "fails", hr); +} + +#endif // GTEST_OS_WINDOWS + +// Utility functions for encoding Unicode text (wide strings) in +// UTF-8. + +// A Unicode code-point can have upto 21 bits, and is encoded in UTF-8 +// like this: +// +// Code-point length Encoding +// 0 - 7 bits 0xxxxxxx +// 8 - 11 bits 110xxxxx 10xxxxxx +// 12 - 16 bits 1110xxxx 10xxxxxx 10xxxxxx +// 17 - 21 bits 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + +// The maximum code-point a one-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint1 = (static_cast(1) << 7) - 1; + +// The maximum code-point a two-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint2 = (static_cast(1) << (5 + 6)) - 1; + +// The maximum code-point a three-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint3 = (static_cast(1) << (4 + 2*6)) - 1; + +// The maximum code-point a four-byte UTF-8 sequence can represent. +const UInt32 kMaxCodePoint4 = (static_cast(1) << (3 + 3*6)) - 1; + +// Chops off the n lowest bits from a bit pattern. Returns the n +// lowest bits. As a side effect, the original bit pattern will be +// shifted to the right by n bits. +inline UInt32 ChopLowBits(UInt32* bits, int n) { + const UInt32 low_bits = *bits & ((static_cast(1) << n) - 1); + *bits >>= n; + return low_bits; +} + +// Converts a Unicode code point to a narrow string in UTF-8 encoding. +// code_point parameter is of type UInt32 because wchar_t may not be +// wide enough to contain a code point. +// If the code_point is not a valid Unicode code point +// (i.e. outside of Unicode range U+0 to U+10FFFF) it will be converted +// to "(Invalid Unicode 0xXXXXXXXX)". +std::string CodePointToUtf8(UInt32 code_point) { + if (code_point > kMaxCodePoint4) { + return "(Invalid Unicode 0x" + String::FormatHexInt(code_point) + ")"; + } + + char str[5]; // Big enough for the largest valid code point. + if (code_point <= kMaxCodePoint1) { + str[1] = '\0'; + str[0] = static_cast(code_point); // 0xxxxxxx + } else if (code_point <= kMaxCodePoint2) { + str[2] = '\0'; + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xC0 | code_point); // 110xxxxx + } else if (code_point <= kMaxCodePoint3) { + str[3] = '\0'; + str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xE0 | code_point); // 1110xxxx + } else { // code_point <= kMaxCodePoint4 + str[4] = '\0'; + str[3] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[2] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[1] = static_cast(0x80 | ChopLowBits(&code_point, 6)); // 10xxxxxx + str[0] = static_cast(0xF0 | code_point); // 11110xxx + } + return str; +} + +// The following two functions only make sense if the the system +// uses UTF-16 for wide string encoding. All supported systems +// with 16 bit wchar_t (Windows, Cygwin, Symbian OS) do use UTF-16. + +// Determines if the arguments constitute UTF-16 surrogate pair +// and thus should be combined into a single Unicode code point +// using CreateCodePointFromUtf16SurrogatePair. +inline bool IsUtf16SurrogatePair(wchar_t first, wchar_t second) { + return sizeof(wchar_t) == 2 && + (first & 0xFC00) == 0xD800 && (second & 0xFC00) == 0xDC00; +} + +// Creates a Unicode code point from UTF16 surrogate pair. +inline UInt32 CreateCodePointFromUtf16SurrogatePair(wchar_t first, + wchar_t second) { + const UInt32 mask = (1 << 10) - 1; + return (sizeof(wchar_t) == 2) ? + (((first & mask) << 10) | (second & mask)) + 0x10000 : + // This function should not be called when the condition is + // false, but we provide a sensible default in case it is. + static_cast(first); +} + +// Converts a wide string to a narrow string in UTF-8 encoding. +// The wide string is assumed to have the following encoding: +// UTF-16 if sizeof(wchar_t) == 2 (on Windows, Cygwin, Symbian OS) +// UTF-32 if sizeof(wchar_t) == 4 (on Linux) +// Parameter str points to a null-terminated wide string. +// Parameter num_chars may additionally limit the number +// of wchar_t characters processed. -1 is used when the entire string +// should be processed. +// If the string contains code points that are not valid Unicode code points +// (i.e. outside of Unicode range U+0 to U+10FFFF) they will be output +// as '(Invalid Unicode 0xXXXXXXXX)'. If the string is in UTF16 encoding +// and contains invalid UTF-16 surrogate pairs, values in those pairs +// will be encoded as individual Unicode characters from Basic Normal Plane. +std::string WideStringToUtf8(const wchar_t* str, int num_chars) { + if (num_chars == -1) + num_chars = static_cast(wcslen(str)); + + ::std::stringstream stream; + for (int i = 0; i < num_chars; ++i) { + UInt32 unicode_code_point; + + if (str[i] == L'\0') { + break; + } else if (i + 1 < num_chars && IsUtf16SurrogatePair(str[i], str[i + 1])) { + unicode_code_point = CreateCodePointFromUtf16SurrogatePair(str[i], + str[i + 1]); + i++; + } else { + unicode_code_point = static_cast(str[i]); + } + + stream << CodePointToUtf8(unicode_code_point); + } + return StringStreamToString(&stream); +} + +// Converts a wide C string to an std::string using the UTF-8 encoding. +// NULL will be converted to "(null)". +std::string String::ShowWideCString(const wchar_t * wide_c_str) { + if (wide_c_str == NULL) return "(null)"; + + return internal::WideStringToUtf8(wide_c_str, -1); +} + +// Compares two wide C strings. Returns true iff they have the same +// content. +// +// Unlike wcscmp(), this function can handle NULL argument(s). A NULL +// C string is considered different to any non-NULL C string, +// including the empty string. +bool String::WideCStringEquals(const wchar_t * lhs, const wchar_t * rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + + return wcscmp(lhs, rhs) == 0; +} + +// Helper function for *_STREQ on wide strings. +AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual) { + if (String::WideCStringEquals(expected, actual)) { + return AssertionSuccess(); + } + + return EqFailure(expected_expression, + actual_expression, + PrintToString(expected), + PrintToString(actual), + false); +} + +// Helper function for *_STRNE on wide strings. +AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2) { + if (!String::WideCStringEquals(s1, s2)) { + return AssertionSuccess(); + } + + return AssertionFailure() << "Expected: (" << s1_expression << ") != (" + << s2_expression << "), actual: " + << PrintToString(s1) + << " vs " << PrintToString(s2); +} + +// Compares two C strings, ignoring case. Returns true iff they have +// the same content. +// +// Unlike strcasecmp(), this function can handle NULL argument(s). A +// NULL C string is considered different to any non-NULL C string, +// including the empty string. +bool String::CaseInsensitiveCStringEquals(const char * lhs, const char * rhs) { + if (lhs == NULL) + return rhs == NULL; + if (rhs == NULL) + return false; + return posix::StrCaseCmp(lhs, rhs) == 0; +} + + // Compares two wide C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. +bool String::CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs) { + if (lhs == NULL) return rhs == NULL; + + if (rhs == NULL) return false; + +#if GTEST_OS_WINDOWS + return _wcsicmp(lhs, rhs) == 0; +#elif GTEST_OS_LINUX && !GTEST_OS_LINUX_ANDROID + return wcscasecmp(lhs, rhs) == 0; +#else + // Android, Mac OS X and Cygwin don't define wcscasecmp. + // Other unknown OSes may not define it either. + wint_t left, right; + do { + left = towlower(*lhs++); + right = towlower(*rhs++); + } while (left && left == right); + return left == right; +#endif // OS selector +} + +// Returns true iff str ends with the given suffix, ignoring case. +// Any string is considered to end with an empty suffix. +bool String::EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix) { + const size_t str_len = str.length(); + const size_t suffix_len = suffix.length(); + return (str_len >= suffix_len) && + CaseInsensitiveCStringEquals(str.c_str() + str_len - suffix_len, + suffix.c_str()); +} + +// Formats an int value as "%02d". +std::string String::FormatIntWidth2(int value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << value; + return ss.str(); +} + +// Formats an int value as "%X". +std::string String::FormatHexInt(int value) { + std::stringstream ss; + ss << std::hex << std::uppercase << value; + return ss.str(); +} + +// Formats a byte as "%02X". +std::string String::FormatByte(unsigned char value) { + std::stringstream ss; + ss << std::setfill('0') << std::setw(2) << std::hex << std::uppercase + << static_cast(value); + return ss.str(); +} + +// Converts the buffer in a stringstream to an std::string, converting NUL +// bytes to "\\0" along the way. +std::string StringStreamToString(::std::stringstream* ss) { + const ::std::string& str = ss->str(); + const char* const start = str.c_str(); + const char* const end = start + str.length(); + + std::string result; + result.reserve(2 * (end - start)); + for (const char* ch = start; ch != end; ++ch) { + if (*ch == '\0') { + result += "\\0"; // Replaces NUL with "\\0"; + } else { + result += *ch; + } + } + + return result; +} + +// Appends the user-supplied message to the Google-Test-generated message. +std::string AppendUserMessage(const std::string& gtest_msg, + const Message& user_msg) { + // Appends the user message if it's non-empty. + const std::string user_msg_string = user_msg.GetString(); + if (user_msg_string.empty()) { + return gtest_msg; + } + + return gtest_msg + "\n" + user_msg_string; +} + +} // namespace internal + +// class TestResult + +// Creates an empty TestResult. +TestResult::TestResult() + : death_test_count_(0), + elapsed_time_(0) { +} + +// D'tor. +TestResult::~TestResult() { +} + +// Returns the i-th test part result among all the results. i can +// range from 0 to total_part_count() - 1. If i is not in that range, +// aborts the program. +const TestPartResult& TestResult::GetTestPartResult(int i) const { + if (i < 0 || i >= total_part_count()) + internal::posix::Abort(); + return test_part_results_.at(i); +} + +// Returns the i-th test property. i can range from 0 to +// test_property_count() - 1. If i is not in that range, aborts the +// program. +const TestProperty& TestResult::GetTestProperty(int i) const { + if (i < 0 || i >= test_property_count()) + internal::posix::Abort(); + return test_properties_.at(i); +} + +// Clears the test part results. +void TestResult::ClearTestPartResults() { + test_part_results_.clear(); +} + +// Adds a test part result to the list. +void TestResult::AddTestPartResult(const TestPartResult& test_part_result) { + test_part_results_.push_back(test_part_result); +} + +// Adds a test property to the list. If a property with the same key as the +// supplied property is already represented, the value of this test_property +// replaces the old value for that key. +void TestResult::RecordProperty(const std::string& xml_element, + const TestProperty& test_property) { + if (!ValidateTestProperty(xml_element, test_property)) { + return; + } + internal::MutexLock lock(&test_properites_mutex_); + const std::vector::iterator property_with_matching_key = + std::find_if(test_properties_.begin(), test_properties_.end(), + internal::TestPropertyKeyIs(test_property.key())); + if (property_with_matching_key == test_properties_.end()) { + test_properties_.push_back(test_property); + return; + } + property_with_matching_key->SetValue(test_property.value()); +} + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuitesAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "random_seed", + "tests", + "time", + "timestamp" +}; + +// The list of reserved attributes used in the element of XML +// output. +static const char* const kReservedTestSuiteAttributes[] = { + "disabled", + "errors", + "failures", + "name", + "tests", + "time" +}; + +// The list of reserved attributes used in the element of XML output. +static const char* const kReservedTestCaseAttributes[] = { + "classname", + "name", + "status", + "time", + "type_param", + "value_param" +}; + +template +std::vector ArrayAsVector(const char* const (&array)[kSize]) { + return std::vector(array, array + kSize); +} + +static std::vector GetReservedAttributesForElement( + const std::string& xml_element) { + if (xml_element == "testsuites") { + return ArrayAsVector(kReservedTestSuitesAttributes); + } else if (xml_element == "testsuite") { + return ArrayAsVector(kReservedTestSuiteAttributes); + } else if (xml_element == "testcase") { + return ArrayAsVector(kReservedTestCaseAttributes); + } else { + GTEST_CHECK_(false) << "Unrecognized xml_element provided: " << xml_element; + } + // This code is unreachable but some compilers may not realizes that. + return std::vector(); +} + +static std::string FormatWordList(const std::vector& words) { + Message word_list; + for (size_t i = 0; i < words.size(); ++i) { + if (i > 0 && words.size() > 2) { + word_list << ", "; + } + if (i == words.size() - 1) { + word_list << "and "; + } + word_list << "'" << words[i] << "'"; + } + return word_list.GetString(); +} + +bool ValidateTestPropertyName(const std::string& property_name, + const std::vector& reserved_names) { + if (std::find(reserved_names.begin(), reserved_names.end(), property_name) != + reserved_names.end()) { + ADD_FAILURE() << "Reserved key used in RecordProperty(): " << property_name + << " (" << FormatWordList(reserved_names) + << " are reserved by " << GTEST_NAME_ << ")"; + return false; + } + return true; +} + +// Adds a failure if the key is a reserved attribute of the element named +// xml_element. Returns true if the property is valid. +bool TestResult::ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property) { + return ValidateTestPropertyName(test_property.key(), + GetReservedAttributesForElement(xml_element)); +} + +// Clears the object. +void TestResult::Clear() { + test_part_results_.clear(); + test_properties_.clear(); + death_test_count_ = 0; + elapsed_time_ = 0; +} + +// Returns true iff the test failed. +bool TestResult::Failed() const { + for (int i = 0; i < total_part_count(); ++i) { + if (GetTestPartResult(i).failed()) + return true; + } + return false; +} + +// Returns true iff the test part fatally failed. +static bool TestPartFatallyFailed(const TestPartResult& result) { + return result.fatally_failed(); +} + +// Returns true iff the test fatally failed. +bool TestResult::HasFatalFailure() const { + return CountIf(test_part_results_, TestPartFatallyFailed) > 0; +} + +// Returns true iff the test part non-fatally failed. +static bool TestPartNonfatallyFailed(const TestPartResult& result) { + return result.nonfatally_failed(); +} + +// Returns true iff the test has a non-fatal failure. +bool TestResult::HasNonfatalFailure() const { + return CountIf(test_part_results_, TestPartNonfatallyFailed) > 0; +} + +// Gets the number of all test parts. This is the sum of the number +// of successful test parts and the number of failed test parts. +int TestResult::total_part_count() const { + return static_cast(test_part_results_.size()); +} + +// Returns the number of the test properties. +int TestResult::test_property_count() const { + return static_cast(test_properties_.size()); +} + +// class Test + +// Creates a Test object. + +// The c'tor saves the values of all Google Test flags. +Test::Test() + : gtest_flag_saver_(new internal::GTestFlagSaver) { +} + +// The d'tor restores the values of all Google Test flags. +Test::~Test() { + delete gtest_flag_saver_; +} + +// Sets up the test fixture. +// +// A sub-class may override this. +void Test::SetUp() { +} + +// Tears down the test fixture. +// +// A sub-class may override this. +void Test::TearDown() { +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, const std::string& value) { + UnitTest::GetInstance()->RecordProperty(key, value); +} + +// Allows user supplied key value pairs to be recorded for later output. +void Test::RecordProperty(const std::string& key, int value) { + Message value_message; + value_message << value; + RecordProperty(key, value_message.GetString().c_str()); +} + +namespace internal { + +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message) { + // This function is a friend of UnitTest and as such has access to + // AddTestPartResult. + UnitTest::GetInstance()->AddTestPartResult( + result_type, + NULL, // No info about the source file where the exception occurred. + -1, // We have no info on which line caused the exception. + message, + ""); // No stack trace, either. +} + +} // namespace internal + +// Google Test requires all tests in the same test case to use the same test +// fixture class. This function checks if the current test has the +// same fixture class as the first test in the current test case. If +// yes, it returns true; otherwise it generates a Google Test failure and +// returns false. +bool Test::HasSameFixtureClass() { + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + const TestCase* const test_case = impl->current_test_case(); + + // Info about the first test in the current test case. + const TestInfo* const first_test_info = test_case->test_info_list()[0]; + const internal::TypeId first_fixture_id = first_test_info->fixture_class_id_; + const char* const first_test_name = first_test_info->name(); + + // Info about the current test. + const TestInfo* const this_test_info = impl->current_test_info(); + const internal::TypeId this_fixture_id = this_test_info->fixture_class_id_; + const char* const this_test_name = this_test_info->name(); + + if (this_fixture_id != first_fixture_id) { + // Is the first test defined using TEST? + const bool first_is_TEST = first_fixture_id == internal::GetTestTypeId(); + // Is this test defined using TEST? + const bool this_is_TEST = this_fixture_id == internal::GetTestTypeId(); + + if (first_is_TEST || this_is_TEST) { + // The user mixed TEST and TEST_F in this test case - we'll tell + // him/her how to fix it. + + // Gets the name of the TEST and the name of the TEST_F. Note + // that first_is_TEST and this_is_TEST cannot both be true, as + // the fixture IDs are different for the two tests. + const char* const TEST_name = + first_is_TEST ? first_test_name : this_test_name; + const char* const TEST_F_name = + first_is_TEST ? this_test_name : first_test_name; + + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class, so mixing TEST_F and TEST in the same test case is\n" + << "illegal. In test case " << this_test_info->test_case_name() + << ",\n" + << "test " << TEST_F_name << " is defined using TEST_F but\n" + << "test " << TEST_name << " is defined using TEST. You probably\n" + << "want to change the TEST to TEST_F or move it to another test\n" + << "case."; + } else { + // The user defined two fixture classes with the same name in + // two namespaces - we'll tell him/her how to fix it. + ADD_FAILURE() + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " + << this_test_info->test_case_name() << ",\n" + << "you defined test " << first_test_name + << " and test " << this_test_name << "\n" + << "using two different test fixture classes. This can happen if\n" + << "the two classes are from different namespaces or translation\n" + << "units and have the same name. You should probably rename one\n" + << "of the classes to put the tests into different test cases."; + } + return false; + } + + return true; +} + +#if GTEST_HAS_SEH + +// Adds an "exception thrown" fatal failure to the current test. This +// function returns its result via an output parameter pointer because VC++ +// prohibits creation of objects with destructors on stack in functions +// using __try (see error C2712). +static std::string* FormatSehExceptionMessage(DWORD exception_code, + const char* location) { + Message message; + message << "SEH exception with code 0x" << std::setbase(16) << + exception_code << std::setbase(10) << " thrown in " << location << "."; + + return new std::string(message.GetString()); +} + +#endif // GTEST_HAS_SEH + +namespace internal { + +#if GTEST_HAS_EXCEPTIONS + +// Adds an "exception thrown" fatal failure to the current test. +static std::string FormatCxxExceptionMessage(const char* description, + const char* location) { + Message message; + if (description != NULL) { + message << "C++ exception with description \"" << description << "\""; + } else { + message << "Unknown C++ exception"; + } + message << " thrown in " << location << "."; + + return message.GetString(); +} + +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result); + +GoogleTestFailureException::GoogleTestFailureException( + const TestPartResult& failure) + : ::std::runtime_error(PrintTestPartResultToString(failure).c_str()) {} + +#endif // GTEST_HAS_EXCEPTIONS + +// We put these helper functions in the internal namespace as IBM's xlC +// compiler rejects the code if they were declared static. + +// Runs the given method and handles SEH exceptions it throws, when +// SEH is supported; returns the 0-value for type Result in case of an +// SEH exception. (Microsoft compilers cannot handle SEH and C++ +// exceptions in the same function. Therefore, we provide a separate +// wrapper function for handling SEH exceptions.) +template +Result HandleSehExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { +#if GTEST_HAS_SEH + __try { + return (object->*method)(); + } __except (internal::UnitTestOptions::GTestShouldProcessSEH( // NOLINT + GetExceptionCode())) { + // We create the exception message on the heap because VC++ prohibits + // creation of objects with destructors on stack in functions using __try + // (see error C2712). + std::string* exception_message = FormatSehExceptionMessage( + GetExceptionCode(), location); + internal::ReportFailureInUnknownLocation(TestPartResult::kFatalFailure, + *exception_message); + delete exception_message; + return static_cast(0); + } +#else + (void)location; + return (object->*method)(); +#endif // GTEST_HAS_SEH +} + +// Runs the given method and catches and reports C++ and/or SEH-style +// exceptions, if they are supported; returns the 0-value for type +// Result in case of an SEH exception. +template +Result HandleExceptionsInMethodIfSupported( + T* object, Result (T::*method)(), const char* location) { + // NOTE: The user code can affect the way in which Google Test handles + // exceptions by setting GTEST_FLAG(catch_exceptions), but only before + // RUN_ALL_TESTS() starts. It is technically possible to check the flag + // after the exception is caught and either report or re-throw the + // exception based on the flag's value: + // + // try { + // // Perform the test method. + // } catch (...) { + // if (GTEST_FLAG(catch_exceptions)) + // // Report the exception as failure. + // else + // throw; // Re-throws the original exception. + // } + // + // However, the purpose of this flag is to allow the program to drop into + // the debugger when the exception is thrown. On most platforms, once the + // control enters the catch block, the exception origin information is + // lost and the debugger will stop the program at the point of the + // re-throw in this function -- instead of at the point of the original + // throw statement in the code under test. For this reason, we perform + // the check early, sacrificing the ability to affect Google Test's + // exception handling in the method where the exception is thrown. + if (internal::GetUnitTestImpl()->catch_exceptions()) { +#if GTEST_HAS_EXCEPTIONS + try { + return HandleSehExceptionsInMethodIfSupported(object, method, location); + } catch (const internal::GoogleTestFailureException&) { // NOLINT + // This exception type can only be thrown by a failed Google + // Test assertion with the intention of letting another testing + // framework catch it. Therefore we just re-throw it. + throw; + } catch (const std::exception& e) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(e.what(), location)); + } catch (...) { // NOLINT + internal::ReportFailureInUnknownLocation( + TestPartResult::kFatalFailure, + FormatCxxExceptionMessage(NULL, location)); + } + return static_cast(0); +#else + return HandleSehExceptionsInMethodIfSupported(object, method, location); +#endif // GTEST_HAS_EXCEPTIONS + } else { + return (object->*method)(); + } +} + +} // namespace internal + +// Runs the test and updates the test result. +void Test::Run() { + if (!HasSameFixtureClass()) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported(this, &Test::SetUp, "SetUp()"); + // We will run the test only if SetUp() was successful. + if (!HasFatalFailure()) { + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TestBody, "the test body"); + } + + // However, we want to clean up as much as possible. Hence we will + // always call TearDown(), even if SetUp() or the test body has + // failed. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &Test::TearDown, "TearDown()"); +} + +// Returns true iff the current test has a fatal failure. +bool Test::HasFatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()->HasFatalFailure(); +} + +// Returns true iff the current test has a non-fatal failure. +bool Test::HasNonfatalFailure() { + return internal::GetUnitTestImpl()->current_test_result()-> + HasNonfatalFailure(); +} + +// class TestInfo + +// Constructs a TestInfo object. It assumes ownership of the test factory +// object. +TestInfo::TestInfo(const std::string& a_test_case_name, + const std::string& a_name, + const char* a_type_param, + const char* a_value_param, + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory) + : test_case_name_(a_test_case_name), + name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + value_param_(a_value_param ? new std::string(a_value_param) : NULL), + fixture_class_id_(fixture_class_id), + should_run_(false), + is_disabled_(false), + matches_filter_(false), + factory_(factory), + result_() {} + +// Destructs a TestInfo object. +TestInfo::~TestInfo() { delete factory_; } + +namespace internal { + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// name: name of the test +// type_param: the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param: text representation of the test's value parameter, +// or NULL if this is not a value-parameterized test. +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +TestInfo* MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + TypeId fixture_class_id, + SetUpTestCaseFunc set_up_tc, + TearDownTestCaseFunc tear_down_tc, + TestFactoryBase* factory) { + TestInfo* const test_info = + new TestInfo(test_case_name, name, type_param, value_param, + fixture_class_id, factory); + GetUnitTestImpl()->AddTestInfo(set_up_tc, tear_down_tc, test_info); + return test_info; +} + +#if GTEST_HAS_PARAM_TEST +void ReportInvalidTestCaseType(const char* test_case_name, + const char* file, int line) { + Message errors; + errors + << "Attempted redefinition of test case " << test_case_name << ".\n" + << "All tests in the same test case must use the same test fixture\n" + << "class. However, in test case " << test_case_name << ", you tried\n" + << "to define a test using a fixture class different from the one\n" + << "used earlier. This can happen if the two fixture classes are\n" + << "from different namespaces and have the same name. You should\n" + << "probably rename one of the classes to put the tests into different\n" + << "test cases."; + + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors.GetString().c_str()); +} +#endif // GTEST_HAS_PARAM_TEST + +} // namespace internal + +namespace { + +// A predicate that checks the test name of a TestInfo against a known +// value. +// +// This is used for implementation of the TestCase class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestNameIs is copyable. +class TestNameIs { + public: + // Constructor. + // + // TestNameIs has NO default constructor. + explicit TestNameIs(const char* name) + : name_(name) {} + + // Returns true iff the test name of test_info matches name_. + bool operator()(const TestInfo * test_info) const { + return test_info && test_info->name() == name_; + } + + private: + std::string name_; +}; + +} // namespace + +namespace internal { + +// This method expands all parameterized tests registered with macros TEST_P +// and INSTANTIATE_TEST_CASE_P into regular tests and registers those. +// This will be done just once during the program runtime. +void UnitTestImpl::RegisterParameterizedTests() { +#if GTEST_HAS_PARAM_TEST + if (!parameterized_tests_registered_) { + parameterized_test_registry_.RegisterTests(); + parameterized_tests_registered_ = true; + } +#endif +} + +} // namespace internal + +// Creates the test object, runs it, records its result, and then +// deletes it. +void TestInfo::Run() { + if (!should_run_) return; + + // Tells UnitTest where to store test result. + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_info(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + // Notifies the unit test event listeners that a test is about to start. + repeater->OnTestStart(*this); + + const TimeInMillis start = internal::GetTimeInMillis(); + + impl->os_stack_trace_getter()->UponLeavingGTest(); + + // Creates the test object. + Test* const test = internal::HandleExceptionsInMethodIfSupported( + factory_, &internal::TestFactoryBase::CreateTest, + "the test fixture's constructor"); + + // Runs the test only if the test object was created and its + // constructor didn't generate a fatal failure. + if ((test != NULL) && !Test::HasFatalFailure()) { + // This doesn't throw as all user code that can throw are wrapped into + // exception handling code. + test->Run(); + } + + // Deletes the test object. + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + test, &Test::DeleteSelf_, "the test fixture's destructor"); + + result_.set_elapsed_time(internal::GetTimeInMillis() - start); + + // Notifies the unit test event listener that a test has just finished. + repeater->OnTestEnd(*this); + + // Tells UnitTest to stop associating assertion results to this + // test. + impl->set_current_test_info(NULL); +} + +// class TestCase + +// Gets the number of successful tests in this test case. +int TestCase::successful_test_count() const { + return CountIf(test_info_list_, TestPassed); +} + +// Gets the number of failed tests in this test case. +int TestCase::failed_test_count() const { + return CountIf(test_info_list_, TestFailed); +} + +// Gets the number of disabled tests that will be reported in the XML report. +int TestCase::reportable_disabled_test_count() const { + return CountIf(test_info_list_, TestReportableDisabled); +} + +// Gets the number of disabled tests in this test case. +int TestCase::disabled_test_count() const { + return CountIf(test_info_list_, TestDisabled); +} + +// Gets the number of tests to be printed in the XML report. +int TestCase::reportable_test_count() const { + return CountIf(test_info_list_, TestReportable); +} + +// Get the number of tests in this test case that should run. +int TestCase::test_to_run_count() const { + return CountIf(test_info_list_, ShouldRunTest); +} + +// Gets the number of all tests. +int TestCase::total_test_count() const { + return static_cast(test_info_list_.size()); +} + +// Creates a TestCase with the given name. +// +// Arguments: +// +// name: name of the test case +// a_type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase::TestCase(const char* a_name, const char* a_type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) + : name_(a_name), + type_param_(a_type_param ? new std::string(a_type_param) : NULL), + set_up_tc_(set_up_tc), + tear_down_tc_(tear_down_tc), + should_run_(false), + elapsed_time_(0) { +} + +// Destructor of TestCase. +TestCase::~TestCase() { + // Deletes every Test in the collection. + ForEach(test_info_list_, internal::Delete); +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +const TestInfo* TestCase::GetTestInfo(int i) const { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? NULL : test_info_list_[index]; +} + +// Returns the i-th test among all the tests. i can range from 0 to +// total_test_count() - 1. If i is not in that range, returns NULL. +TestInfo* TestCase::GetMutableTestInfo(int i) { + const int index = GetElementOr(test_indices_, i, -1); + return index < 0 ? NULL : test_info_list_[index]; +} + +// Adds a test to this test case. Will delete the test upon +// destruction of the TestCase object. +void TestCase::AddTestInfo(TestInfo * test_info) { + test_info_list_.push_back(test_info); + test_indices_.push_back(static_cast(test_indices_.size())); +} + +// Runs every test in this TestCase. +void TestCase::Run() { + if (!should_run_) return; + + internal::UnitTestImpl* const impl = internal::GetUnitTestImpl(); + impl->set_current_test_case(this); + + TestEventListener* repeater = UnitTest::GetInstance()->listeners().repeater(); + + repeater->OnTestCaseStart(*this); + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunSetUpTestCase, "SetUpTestCase()"); + + const internal::TimeInMillis start = internal::GetTimeInMillis(); + for (int i = 0; i < total_test_count(); i++) { + GetMutableTestInfo(i)->Run(); + } + elapsed_time_ = internal::GetTimeInMillis() - start; + + impl->os_stack_trace_getter()->UponLeavingGTest(); + internal::HandleExceptionsInMethodIfSupported( + this, &TestCase::RunTearDownTestCase, "TearDownTestCase()"); + + repeater->OnTestCaseEnd(*this); + impl->set_current_test_case(NULL); +} + +// Clears the results of all tests in this test case. +void TestCase::ClearResult() { + ad_hoc_test_result_.Clear(); + ForEach(test_info_list_, TestInfo::ClearTestResult); +} + +// Shuffles the tests in this test case. +void TestCase::ShuffleTests(internal::Random* random) { + Shuffle(random, &test_indices_); +} + +// Restores the test order to before the first shuffle. +void TestCase::UnshuffleTests() { + for (size_t i = 0; i < test_indices_.size(); i++) { + test_indices_[i] = static_cast(i); + } +} + +// Formats a countable noun. Depending on its quantity, either the +// singular form or the plural form is used. e.g. +// +// FormatCountableNoun(1, "formula", "formuli") returns "1 formula". +// FormatCountableNoun(5, "book", "books") returns "5 books". +static std::string FormatCountableNoun(int count, + const char * singular_form, + const char * plural_form) { + return internal::StreamableToString(count) + " " + + (count == 1 ? singular_form : plural_form); +} + +// Formats the count of tests. +static std::string FormatTestCount(int test_count) { + return FormatCountableNoun(test_count, "test", "tests"); +} + +// Formats the count of test cases. +static std::string FormatTestCaseCount(int test_case_count) { + return FormatCountableNoun(test_case_count, "test case", "test cases"); +} + +// Converts a TestPartResult::Type enum to human-friendly string +// representation. Both kNonFatalFailure and kFatalFailure are translated +// to "Failure", as the user usually doesn't care about the difference +// between the two when viewing the test result. +static const char * TestPartResultTypeToString(TestPartResult::Type type) { + switch (type) { + case TestPartResult::kSuccess: + return "Success"; + + case TestPartResult::kNonFatalFailure: + case TestPartResult::kFatalFailure: +#ifdef _MSC_VER + return "error: "; +#else + return "Failure\n"; +#endif + default: + return "Unknown result type"; + } +} + +namespace internal { + +// Prints a TestPartResult to an std::string. +static std::string PrintTestPartResultToString( + const TestPartResult& test_part_result) { + return (Message() + << internal::FormatFileLocation(test_part_result.file_name(), + test_part_result.line_number()) + << " " << TestPartResultTypeToString(test_part_result.type()) + << test_part_result.message()).GetString(); +} + +// Prints a TestPartResult. +static void PrintTestPartResult(const TestPartResult& test_part_result) { + const std::string& result = + PrintTestPartResultToString(test_part_result); + printf("%s\n", result.c_str()); + fflush(stdout); + // If the test program runs in Visual Studio or a debugger, the + // following statements add the test part result message to the Output + // window such that the user can double-click on it to jump to the + // corresponding source code location; otherwise they do nothing. +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + // We don't call OutputDebugString*() on Windows Mobile, as printing + // to stdout is done by OutputDebugString() there already - we don't + // want the same message printed twice. + ::OutputDebugStringA(result.c_str()); + ::OutputDebugStringA("\n"); +#endif +} + +// class PrettyUnitTestResultPrinter + +enum GTestColor { + COLOR_DEFAULT, + COLOR_RED, + COLOR_GREEN, + COLOR_YELLOW +}; + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns the character attribute for the given color. +WORD GetColorAttribute(GTestColor color) { + switch (color) { + case COLOR_RED: return FOREGROUND_RED; + case COLOR_GREEN: return FOREGROUND_GREEN; + case COLOR_YELLOW: return FOREGROUND_RED | FOREGROUND_GREEN; + default: return 0; + } +} + +#else + +// Returns the ANSI color code for the given color. COLOR_DEFAULT is +// an invalid input. +const char* GetAnsiColorCode(GTestColor color) { + switch (color) { + case COLOR_RED: return "1"; + case COLOR_GREEN: return "2"; + case COLOR_YELLOW: return "3"; + default: return NULL; + }; +} + +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + +// Returns true iff Google Test should use colors in the output. +bool ShouldUseColor(bool stdout_is_tty) { + const char* const gtest_color = GTEST_FLAG(color).c_str(); + + if (String::CaseInsensitiveCStringEquals(gtest_color, "auto")) { +#if GTEST_OS_WINDOWS + // On Windows the TERM variable is usually not set, but the + // console there does support colors. + return stdout_is_tty; +#else + // On non-Windows platforms, we rely on the TERM variable. + const char* const term = posix::GetEnv("TERM"); + const bool term_supports_color = + String::CStringEquals(term, "xterm") || + String::CStringEquals(term, "xterm-color") || + String::CStringEquals(term, "xterm-256color") || + String::CStringEquals(term, "screen") || + String::CStringEquals(term, "screen-256color") || + String::CStringEquals(term, "linux") || + String::CStringEquals(term, "cygwin"); + return stdout_is_tty && term_supports_color; +#endif // GTEST_OS_WINDOWS + } + + return String::CaseInsensitiveCStringEquals(gtest_color, "yes") || + String::CaseInsensitiveCStringEquals(gtest_color, "true") || + String::CaseInsensitiveCStringEquals(gtest_color, "t") || + String::CStringEquals(gtest_color, "1"); + // We take "yes", "true", "t", and "1" as meaning "yes". If the + // value is neither one of these nor "auto", we treat it as "no" to + // be conservative. +} + +// Helpers for printing colored strings to stdout. Note that on Windows, we +// cannot simply emit special characters and have the terminal change colors. +// This routine must actually emit the characters rather than return a string +// that would be colored when printed, as can be done on Linux. +void ColoredPrintf(GTestColor color, const char* fmt, ...) { + va_list args; + va_start(args, fmt); + +#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS || GTEST_OS_IOS + const bool use_color = false; +#else + static const bool in_color_mode = + ShouldUseColor(posix::IsATTY(posix::FileNo(stdout)) != 0); + const bool use_color = in_color_mode && (color != COLOR_DEFAULT); +#endif // GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN || GTEST_OS_ZOS + // The '!= 0' comparison is necessary to satisfy MSVC 7.1. + + if (!use_color) { + vprintf(fmt, args); + va_end(args); + return; + } + +#if GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + const HANDLE stdout_handle = GetStdHandle(STD_OUTPUT_HANDLE); + + // Gets the current text color. + CONSOLE_SCREEN_BUFFER_INFO buffer_info; + GetConsoleScreenBufferInfo(stdout_handle, &buffer_info); + const WORD old_color_attrs = buffer_info.wAttributes; + + // We need to flush the stream buffers into the console before each + // SetConsoleTextAttribute call lest it affect the text that is already + // printed but has not yet reached the console. + fflush(stdout); + SetConsoleTextAttribute(stdout_handle, + GetColorAttribute(color) | FOREGROUND_INTENSITY); + vprintf(fmt, args); + + fflush(stdout); + // Restores the text color. + SetConsoleTextAttribute(stdout_handle, old_color_attrs); +#else + printf("\033[0;3%sm", GetAnsiColorCode(color)); + vprintf(fmt, args); + printf("\033[m"); // Resets the terminal to default. +#endif // GTEST_OS_WINDOWS && !GTEST_OS_WINDOWS_MOBILE + va_end(args); +} + +// Text printed in Google Test's text output and --gunit_list_tests +// output to label the type parameter and value parameter for a test. +static const char kTypeParamLabel[] = "TypeParam"; +static const char kValueParamLabel[] = "GetParam()"; + +void PrintFullTestCommentIfPresent(const TestInfo& test_info) { + const char* const type_param = test_info.type_param(); + const char* const value_param = test_info.value_param(); + + if (type_param != NULL || value_param != NULL) { + printf(", where "); + if (type_param != NULL) { + printf("%s = %s", kTypeParamLabel, type_param); + if (value_param != NULL) + printf(" and "); + } + if (value_param != NULL) { + printf("%s = %s", kValueParamLabel, value_param); + } + } +} + +// This class implements the TestEventListener interface. +// +// Class PrettyUnitTestResultPrinter is copyable. +class PrettyUnitTestResultPrinter : public TestEventListener { + public: + PrettyUnitTestResultPrinter() {} + static void PrintTestName(const char * test_case, const char * test) { + printf("%s.%s", test_case, test); + } + + // The following methods override what's in the TestEventListener class. + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestCaseStart(const TestCase& test_case); + virtual void OnTestStart(const TestInfo& test_info); + virtual void OnTestPartResult(const TestPartResult& result); + virtual void OnTestEnd(const TestInfo& test_info); + virtual void OnTestCaseEnd(const TestCase& test_case); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} + + private: + static void PrintFailedTests(const UnitTest& unit_test); +}; + + // Fired before each iteration of tests starts. +void PrettyUnitTestResultPrinter::OnTestIterationStart( + const UnitTest& unit_test, int iteration) { + if (GTEST_FLAG(repeat) != 1) + printf("\nRepeating all tests (iteration %d) . . .\n\n", iteration + 1); + + const char* const filter = GTEST_FLAG(filter).c_str(); + + // Prints the filter if it's not *. This reminds the user that some + // tests may be skipped. + if (!String::CStringEquals(filter, kUniversalFilter)) { + ColoredPrintf(COLOR_YELLOW, + "Note: %s filter = %s\n", GTEST_NAME_, filter); + } + + if (internal::ShouldShard(kTestTotalShards, kTestShardIndex, false)) { + const Int32 shard_index = Int32FromEnvOrDie(kTestShardIndex, -1); + ColoredPrintf(COLOR_YELLOW, + "Note: This is test shard %d of %s.\n", + static_cast(shard_index) + 1, + internal::posix::GetEnv(kTestTotalShards)); + } + + if (GTEST_FLAG(shuffle)) { + ColoredPrintf(COLOR_YELLOW, + "Note: Randomizing tests' orders with a seed of %d .\n", + unit_test.random_seed()); + } + + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("Running %s from %s.\n", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsSetUpStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment set-up.\n"); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseStart(const TestCase& test_case) { + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s", counts.c_str(), test_case.name()); + if (test_case.type_param() == NULL) { + printf("\n"); + } else { + printf(", where %s = %s\n", kTypeParamLabel, test_case.type_param()); + } + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestStart(const TestInfo& test_info) { + ColoredPrintf(COLOR_GREEN, "[ RUN ] "); + PrintTestName(test_info.test_case_name(), test_info.name()); + printf("\n"); + fflush(stdout); +} + +// Called after an assertion failure. +void PrettyUnitTestResultPrinter::OnTestPartResult( + const TestPartResult& result) { + // If the test part succeeded, we don't need to do anything. + if (result.type() == TestPartResult::kSuccess) + return; + + // Print failure message from the assertion (e.g. expected this and got that). + PrintTestPartResult(result); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestEnd(const TestInfo& test_info) { + if (test_info.result()->Passed()) { + ColoredPrintf(COLOR_GREEN, "[ OK ] "); + } else { + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + } + PrintTestName(test_info.test_case_name(), test_info.name()); + if (test_info.result()->Failed()) + PrintFullTestCommentIfPresent(test_info); + + if (GTEST_FLAG(print_time)) { + printf(" (%s ms)\n", internal::StreamableToString( + test_info.result()->elapsed_time()).c_str()); + } else { + printf("\n"); + } + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnTestCaseEnd(const TestCase& test_case) { + if (!GTEST_FLAG(print_time)) return; + + const std::string counts = + FormatCountableNoun(test_case.test_to_run_count(), "test", "tests"); + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("%s from %s (%s ms total)\n\n", + counts.c_str(), test_case.name(), + internal::StreamableToString(test_case.elapsed_time()).c_str()); + fflush(stdout); +} + +void PrettyUnitTestResultPrinter::OnEnvironmentsTearDownStart( + const UnitTest& /*unit_test*/) { + ColoredPrintf(COLOR_GREEN, "[----------] "); + printf("Global test environment tear-down\n"); + fflush(stdout); +} + +// Internal helper for printing the list of failed tests. +void PrettyUnitTestResultPrinter::PrintFailedTests(const UnitTest& unit_test) { + const int failed_test_count = unit_test.failed_test_count(); + if (failed_test_count == 0) { + return; + } + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + const TestCase& test_case = *unit_test.GetTestCase(i); + if (!test_case.should_run() || (test_case.failed_test_count() == 0)) { + continue; + } + for (int j = 0; j < test_case.total_test_count(); ++j) { + const TestInfo& test_info = *test_case.GetTestInfo(j); + if (!test_info.should_run() || test_info.result()->Passed()) { + continue; + } + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s.%s", test_case.name(), test_info.name()); + PrintFullTestCommentIfPresent(test_info); + printf("\n"); + } + } +} + +void PrettyUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + ColoredPrintf(COLOR_GREEN, "[==========] "); + printf("%s from %s ran.", + FormatTestCount(unit_test.test_to_run_count()).c_str(), + FormatTestCaseCount(unit_test.test_case_to_run_count()).c_str()); + if (GTEST_FLAG(print_time)) { + printf(" (%s ms total)", + internal::StreamableToString(unit_test.elapsed_time()).c_str()); + } + printf("\n"); + ColoredPrintf(COLOR_GREEN, "[ PASSED ] "); + printf("%s.\n", FormatTestCount(unit_test.successful_test_count()).c_str()); + + int num_failures = unit_test.failed_test_count(); + if (!unit_test.Passed()) { + const int failed_test_count = unit_test.failed_test_count(); + ColoredPrintf(COLOR_RED, "[ FAILED ] "); + printf("%s, listed below:\n", FormatTestCount(failed_test_count).c_str()); + PrintFailedTests(unit_test); + printf("\n%2d FAILED %s\n", num_failures, + num_failures == 1 ? "TEST" : "TESTS"); + } + + int num_disabled = unit_test.reportable_disabled_test_count(); + if (num_disabled && !GTEST_FLAG(also_run_disabled_tests)) { + if (!num_failures) { + printf("\n"); // Add a spacer if no FAILURE banner is displayed. + } + ColoredPrintf(COLOR_YELLOW, + " YOU HAVE %d DISABLED %s\n\n", + num_disabled, + num_disabled == 1 ? "TEST" : "TESTS"); + } + // Ensure that Google Test output is printed before, e.g., heapchecker output. + fflush(stdout); +} + +// End PrettyUnitTestResultPrinter + +// class TestEventRepeater +// +// This class forwards events to other event listeners. +class TestEventRepeater : public TestEventListener { + public: + TestEventRepeater() : forwarding_enabled_(true) {} + virtual ~TestEventRepeater(); + void Append(TestEventListener *listener); + TestEventListener* Release(TestEventListener* listener); + + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled() const { return forwarding_enabled_; } + void set_forwarding_enabled(bool enable) { forwarding_enabled_ = enable; } + + virtual void OnTestProgramStart(const UnitTest& unit_test); + virtual void OnTestIterationStart(const UnitTest& unit_test, int iteration); + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test); + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test); + virtual void OnTestCaseStart(const TestCase& test_case); + virtual void OnTestStart(const TestInfo& test_info); + virtual void OnTestPartResult(const TestPartResult& result); + virtual void OnTestEnd(const TestInfo& test_info); + virtual void OnTestCaseEnd(const TestCase& test_case); + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test); + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test); + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + virtual void OnTestProgramEnd(const UnitTest& unit_test); + + private: + // Controls whether events will be forwarded to listeners_. Set to false + // in death test child processes. + bool forwarding_enabled_; + // The list of listeners that receive events. + std::vector listeners_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventRepeater); +}; + +TestEventRepeater::~TestEventRepeater() { + ForEach(listeners_, Delete); +} + +void TestEventRepeater::Append(TestEventListener *listener) { + listeners_.push_back(listener); +} + +// TODO(vladl@google.com): Factor the search functionality into Vector::Find. +TestEventListener* TestEventRepeater::Release(TestEventListener *listener) { + for (size_t i = 0; i < listeners_.size(); ++i) { + if (listeners_[i] == listener) { + listeners_.erase(listeners_.begin() + i); + return listener; + } + } + + return NULL; +} + +// Since most methods are very similar, use macros to reduce boilerplate. +// This defines a member that forwards the call to all listeners. +#define GTEST_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (size_t i = 0; i < listeners_.size(); i++) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} +// This defines a member that forwards the call to all listeners in reverse +// order. +#define GTEST_REVERSE_REPEATER_METHOD_(Name, Type) \ +void TestEventRepeater::Name(const Type& parameter) { \ + if (forwarding_enabled_) { \ + for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { \ + listeners_[i]->Name(parameter); \ + } \ + } \ +} + +GTEST_REPEATER_METHOD_(OnTestProgramStart, UnitTest) +GTEST_REPEATER_METHOD_(OnEnvironmentsSetUpStart, UnitTest) +GTEST_REPEATER_METHOD_(OnTestCaseStart, TestCase) +GTEST_REPEATER_METHOD_(OnTestStart, TestInfo) +GTEST_REPEATER_METHOD_(OnTestPartResult, TestPartResult) +GTEST_REPEATER_METHOD_(OnEnvironmentsTearDownStart, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsSetUpEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnEnvironmentsTearDownEnd, UnitTest) +GTEST_REVERSE_REPEATER_METHOD_(OnTestEnd, TestInfo) +GTEST_REVERSE_REPEATER_METHOD_(OnTestCaseEnd, TestCase) +GTEST_REVERSE_REPEATER_METHOD_(OnTestProgramEnd, UnitTest) + +#undef GTEST_REPEATER_METHOD_ +#undef GTEST_REVERSE_REPEATER_METHOD_ + +void TestEventRepeater::OnTestIterationStart(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (size_t i = 0; i < listeners_.size(); i++) { + listeners_[i]->OnTestIterationStart(unit_test, iteration); + } + } +} + +void TestEventRepeater::OnTestIterationEnd(const UnitTest& unit_test, + int iteration) { + if (forwarding_enabled_) { + for (int i = static_cast(listeners_.size()) - 1; i >= 0; i--) { + listeners_[i]->OnTestIterationEnd(unit_test, iteration); + } + } +} + +// End TestEventRepeater + +// This class generates an XML output file. +class XmlUnitTestResultPrinter : public EmptyTestEventListener { + public: + explicit XmlUnitTestResultPrinter(const char* output_file); + + virtual void OnTestIterationEnd(const UnitTest& unit_test, int iteration); + + private: + // Is c a whitespace character that is normalized to a space character + // when it appears in an XML attribute value? + static bool IsNormalizableWhitespace(char c) { + return c == 0x9 || c == 0xA || c == 0xD; + } + + // May c appear in a well-formed XML document? + static bool IsValidXmlCharacter(char c) { + return IsNormalizableWhitespace(c) || c >= 0x20; + } + + // Returns an XML-escaped copy of the input string str. If + // is_attribute is true, the text is meant to appear as an attribute + // value, and normalizable whitespace is preserved by replacing it + // with character references. + static std::string EscapeXml(const std::string& str, bool is_attribute); + + // Returns the given string with all characters invalid in XML removed. + static std::string RemoveInvalidXmlCharacters(const std::string& str); + + // Convenience wrapper around EscapeXml when str is an attribute value. + static std::string EscapeXmlAttribute(const std::string& str) { + return EscapeXml(str, true); + } + + // Convenience wrapper around EscapeXml when str is not an attribute value. + static std::string EscapeXmlText(const char* str) { + return EscapeXml(str, false); + } + + // Verifies that the given attribute belongs to the given element and + // streams the attribute as XML. + static void OutputXmlAttribute(std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value); + + // Streams an XML CDATA section, escaping invalid CDATA sequences as needed. + static void OutputXmlCDataSection(::std::ostream* stream, const char* data); + + // Streams an XML representation of a TestInfo object. + static void OutputXmlTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info); + + // Prints an XML representation of a TestCase object + static void PrintXmlTestCase(::std::ostream* stream, + const TestCase& test_case); + + // Prints an XML summary of unit_test to output stream out. + static void PrintXmlUnitTest(::std::ostream* stream, + const UnitTest& unit_test); + + // Produces a string representing the test properties in a result as space + // delimited XML attributes based on the property key="value" pairs. + // When the std::string is not empty, it includes a space at the beginning, + // to delimit this attribute from prior attributes. + static std::string TestPropertiesAsXmlAttributes(const TestResult& result); + + // The output file. + const std::string output_file_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(XmlUnitTestResultPrinter); +}; + +// Creates a new XmlUnitTestResultPrinter. +XmlUnitTestResultPrinter::XmlUnitTestResultPrinter(const char* output_file) + : output_file_(output_file) { + if (output_file_.c_str() == NULL || output_file_.empty()) { + fprintf(stderr, "XML output file may not be null\n"); + fflush(stderr); + exit(EXIT_FAILURE); + } +} + +// Called after the unit test ends. +void XmlUnitTestResultPrinter::OnTestIterationEnd(const UnitTest& unit_test, + int /*iteration*/) { + FILE* xmlout = NULL; + FilePath output_file(output_file_); + FilePath output_dir(output_file.RemoveFileName()); + + if (output_dir.CreateDirectoriesRecursively()) { + xmlout = posix::FOpen(output_file_.c_str(), "w"); + } + if (xmlout == NULL) { + // TODO(wan): report the reason of the failure. + // + // We don't do it for now as: + // + // 1. There is no urgent need for it. + // 2. It's a bit involved to make the errno variable thread-safe on + // all three operating systems (Linux, Windows, and Mac OS). + // 3. To interpret the meaning of errno in a thread-safe way, + // we need the strerror_r() function, which is not available on + // Windows. + fprintf(stderr, + "Unable to open file \"%s\"\n", + output_file_.c_str()); + fflush(stderr); + exit(EXIT_FAILURE); + } + std::stringstream stream; + PrintXmlUnitTest(&stream, unit_test); + fprintf(xmlout, "%s", StringStreamToString(&stream).c_str()); + fclose(xmlout); +} + +// Returns an XML-escaped copy of the input string str. If is_attribute +// is true, the text is meant to appear as an attribute value, and +// normalizable whitespace is preserved by replacing it with character +// references. +// +// Invalid XML characters in str, if any, are stripped from the output. +// It is expected that most, if not all, of the text processed by this +// module will consist of ordinary English text. +// If this module is ever modified to produce version 1.1 XML output, +// most invalid characters can be retained using character references. +// TODO(wan): It might be nice to have a minimally invasive, human-readable +// escaping scheme for invalid characters, rather than dropping them. +std::string XmlUnitTestResultPrinter::EscapeXml( + const std::string& str, bool is_attribute) { + Message m; + + for (size_t i = 0; i < str.size(); ++i) { + const char ch = str[i]; + switch (ch) { + case '<': + m << "<"; + break; + case '>': + m << ">"; + break; + case '&': + m << "&"; + break; + case '\'': + if (is_attribute) + m << "'"; + else + m << '\''; + break; + case '"': + if (is_attribute) + m << """; + else + m << '"'; + break; + default: + if (IsValidXmlCharacter(ch)) { + if (is_attribute && IsNormalizableWhitespace(ch)) + m << "&#x" << String::FormatByte(static_cast(ch)) + << ";"; + else + m << ch; + } + break; + } + } + + return m.GetString(); +} + +// Returns the given string with all characters invalid in XML removed. +// Currently invalid characters are dropped from the string. An +// alternative is to replace them with certain characters such as . or ?. +std::string XmlUnitTestResultPrinter::RemoveInvalidXmlCharacters( + const std::string& str) { + std::string output; + output.reserve(str.size()); + for (std::string::const_iterator it = str.begin(); it != str.end(); ++it) + if (IsValidXmlCharacter(*it)) + output.push_back(*it); + + return output; +} + +// The following routines generate an XML representation of a UnitTest +// object. +// +// This is how Google Test concepts map to the DTD: +// +// <-- corresponds to a UnitTest object +// <-- corresponds to a TestCase object +// <-- corresponds to a TestInfo object +// ... +// ... +// ... +// <-- individual assertion failures +// +// +// + +// Formats the given time in milliseconds as seconds. +std::string FormatTimeInMillisAsSeconds(TimeInMillis ms) { + ::std::stringstream ss; + ss << ms/1000.0; + return ss.str(); +} + +// Converts the given epoch time in milliseconds to a date string in the ISO +// 8601 format, without the timezone information. +std::string FormatEpochTimeInMillisAsIso8601(TimeInMillis ms) { + // Using non-reentrant version as localtime_r is not portable. + time_t seconds = static_cast(ms / 1000); +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4996) // Temporarily disables warning 4996 + // (function or variable may be unsafe). + const struct tm* const time_struct = localtime(&seconds); // NOLINT +# pragma warning(pop) // Restores the warning state again. +#else + const struct tm* const time_struct = localtime(&seconds); // NOLINT +#endif + if (time_struct == NULL) + return ""; // Invalid ms value + + // YYYY-MM-DDThh:mm:ss + return StreamableToString(time_struct->tm_year + 1900) + "-" + + String::FormatIntWidth2(time_struct->tm_mon + 1) + "-" + + String::FormatIntWidth2(time_struct->tm_mday) + "T" + + String::FormatIntWidth2(time_struct->tm_hour) + ":" + + String::FormatIntWidth2(time_struct->tm_min) + ":" + + String::FormatIntWidth2(time_struct->tm_sec); +} + +// Streams an XML CDATA section, escaping invalid CDATA sequences as needed. +void XmlUnitTestResultPrinter::OutputXmlCDataSection(::std::ostream* stream, + const char* data) { + const char* segment = data; + *stream << ""); + if (next_segment != NULL) { + stream->write( + segment, static_cast(next_segment - segment)); + *stream << "]]>]]>"); + } else { + *stream << segment; + break; + } + } + *stream << "]]>"; +} + +void XmlUnitTestResultPrinter::OutputXmlAttribute( + std::ostream* stream, + const std::string& element_name, + const std::string& name, + const std::string& value) { + const std::vector& allowed_names = + GetReservedAttributesForElement(element_name); + + GTEST_CHECK_(std::find(allowed_names.begin(), allowed_names.end(), name) != + allowed_names.end()) + << "Attribute " << name << " is not allowed for element <" << element_name + << ">."; + + *stream << " " << name << "=\"" << EscapeXmlAttribute(value) << "\""; +} + +// Prints an XML representation of a TestInfo object. +// TODO(wan): There is also value in printing properties with the plain printer. +void XmlUnitTestResultPrinter::OutputXmlTestInfo(::std::ostream* stream, + const char* test_case_name, + const TestInfo& test_info) { + const TestResult& result = *test_info.result(); + const std::string kTestcase = "testcase"; + + *stream << " \n"; + } + const string location = internal::FormatCompilerIndependentFileLocation( + part.file_name(), part.line_number()); + const string summary = location + "\n" + part.summary(); + *stream << " "; + const string detail = location + "\n" + part.message(); + OutputXmlCDataSection(stream, RemoveInvalidXmlCharacters(detail).c_str()); + *stream << "\n"; + } + } + + if (failures == 0) + *stream << " />\n"; + else + *stream << " \n"; +} + +// Prints an XML representation of a TestCase object +void XmlUnitTestResultPrinter::PrintXmlTestCase(std::ostream* stream, + const TestCase& test_case) { + const std::string kTestsuite = "testsuite"; + *stream << " <" << kTestsuite; + OutputXmlAttribute(stream, kTestsuite, "name", test_case.name()); + OutputXmlAttribute(stream, kTestsuite, "tests", + StreamableToString(test_case.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuite, "failures", + StreamableToString(test_case.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuite, "disabled", + StreamableToString(test_case.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuite, "errors", "0"); + OutputXmlAttribute(stream, kTestsuite, "time", + FormatTimeInMillisAsSeconds(test_case.elapsed_time())); + *stream << TestPropertiesAsXmlAttributes(test_case.ad_hoc_test_result()) + << ">\n"; + + for (int i = 0; i < test_case.total_test_count(); ++i) { + if (test_case.GetTestInfo(i)->is_reportable()) + OutputXmlTestInfo(stream, test_case.name(), *test_case.GetTestInfo(i)); + } + *stream << " \n"; +} + +// Prints an XML summary of unit_test to output stream out. +void XmlUnitTestResultPrinter::PrintXmlUnitTest(std::ostream* stream, + const UnitTest& unit_test) { + const std::string kTestsuites = "testsuites"; + + *stream << "\n"; + *stream << "<" << kTestsuites; + + OutputXmlAttribute(stream, kTestsuites, "tests", + StreamableToString(unit_test.reportable_test_count())); + OutputXmlAttribute(stream, kTestsuites, "failures", + StreamableToString(unit_test.failed_test_count())); + OutputXmlAttribute( + stream, kTestsuites, "disabled", + StreamableToString(unit_test.reportable_disabled_test_count())); + OutputXmlAttribute(stream, kTestsuites, "errors", "0"); + OutputXmlAttribute( + stream, kTestsuites, "timestamp", + FormatEpochTimeInMillisAsIso8601(unit_test.start_timestamp())); + OutputXmlAttribute(stream, kTestsuites, "time", + FormatTimeInMillisAsSeconds(unit_test.elapsed_time())); + + if (GTEST_FLAG(shuffle)) { + OutputXmlAttribute(stream, kTestsuites, "random_seed", + StreamableToString(unit_test.random_seed())); + } + + *stream << TestPropertiesAsXmlAttributes(unit_test.ad_hoc_test_result()); + + OutputXmlAttribute(stream, kTestsuites, "name", "AllTests"); + *stream << ">\n"; + + for (int i = 0; i < unit_test.total_test_case_count(); ++i) { + if (unit_test.GetTestCase(i)->reportable_test_count() > 0) + PrintXmlTestCase(stream, *unit_test.GetTestCase(i)); + } + *stream << "\n"; +} + +// Produces a string representing the test properties in a result as space +// delimited XML attributes based on the property key="value" pairs. +std::string XmlUnitTestResultPrinter::TestPropertiesAsXmlAttributes( + const TestResult& result) { + Message attributes; + for (int i = 0; i < result.test_property_count(); ++i) { + const TestProperty& property = result.GetTestProperty(i); + attributes << " " << property.key() << "=" + << "\"" << EscapeXmlAttribute(property.value()) << "\""; + } + return attributes.GetString(); +} + +// End XmlUnitTestResultPrinter + +#if GTEST_CAN_STREAM_RESULTS_ + +// Checks if str contains '=', '&', '%' or '\n' characters. If yes, +// replaces them by "%xx" where xx is their hexadecimal value. For +// example, replaces "=" with "%3D". This algorithm is O(strlen(str)) +// in both time and space -- important as the input str may contain an +// arbitrarily long test failure message and stack trace. +string StreamingListener::UrlEncode(const char* str) { + string result; + result.reserve(strlen(str) + 1); + for (char ch = *str; ch != '\0'; ch = *++str) { + switch (ch) { + case '%': + case '=': + case '&': + case '\n': + result.append("%" + String::FormatByte(static_cast(ch))); + break; + default: + result.push_back(ch); + break; + } + } + return result; +} + +void StreamingListener::SocketWriter::MakeConnection() { + GTEST_CHECK_(sockfd_ == -1) + << "MakeConnection() can't be called when there is already a connection."; + + addrinfo hints; + memset(&hints, 0, sizeof(hints)); + hints.ai_family = AF_UNSPEC; // To allow both IPv4 and IPv6 addresses. + hints.ai_socktype = SOCK_STREAM; + addrinfo* servinfo = NULL; + + // Use the getaddrinfo() to get a linked list of IP addresses for + // the given host name. + const int error_num = getaddrinfo( + host_name_.c_str(), port_num_.c_str(), &hints, &servinfo); + if (error_num != 0) { + GTEST_LOG_(WARNING) << "stream_result_to: getaddrinfo() failed: " + << gai_strerror(error_num); + } + + // Loop through all the results and connect to the first we can. + for (addrinfo* cur_addr = servinfo; sockfd_ == -1 && cur_addr != NULL; + cur_addr = cur_addr->ai_next) { + sockfd_ = socket( + cur_addr->ai_family, cur_addr->ai_socktype, cur_addr->ai_protocol); + if (sockfd_ != -1) { + // Connect the client socket to the server socket. + if (connect(sockfd_, cur_addr->ai_addr, cur_addr->ai_addrlen) == -1) { + close(sockfd_); + sockfd_ = -1; + } + } + } + + freeaddrinfo(servinfo); // all done with this structure + + if (sockfd_ == -1) { + GTEST_LOG_(WARNING) << "stream_result_to: failed to connect to " + << host_name_ << ":" << port_num_; + } +} + +// End of class Streaming Listener +#endif // GTEST_CAN_STREAM_RESULTS__ + +// Class ScopedTrace + +// Pushes the given source file location and message onto a per-thread +// trace stack maintained by Google Test. +ScopedTrace::ScopedTrace(const char* file, int line, const Message& message) + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { + TraceInfo trace; + trace.file = file; + trace.line = line; + trace.message = message.GetString(); + + UnitTest::GetInstance()->PushGTestTrace(trace); +} + +// Pops the info pushed by the c'tor. +ScopedTrace::~ScopedTrace() + GTEST_LOCK_EXCLUDED_(&UnitTest::mutex_) { + UnitTest::GetInstance()->PopGTestTrace(); +} + + +// class OsStackTraceGetter + +// Returns the current OS stack trace as an std::string. Parameters: +// +// max_depth - the maximum number of stack frames to be included +// in the trace. +// skip_count - the number of top frames to be skipped; doesn't count +// against max_depth. +// +string OsStackTraceGetter::CurrentStackTrace(int /* max_depth */, + int /* skip_count */) + GTEST_LOCK_EXCLUDED_(mutex_) { + return ""; +} + +void OsStackTraceGetter::UponLeavingGTest() + GTEST_LOCK_EXCLUDED_(mutex_) { +} + +const char* const +OsStackTraceGetter::kElidedFramesMarker = + "... " GTEST_NAME_ " internal frames ..."; + +// A helper class that creates the premature-exit file in its +// constructor and deletes the file in its destructor. +class ScopedPrematureExitFile { + public: + explicit ScopedPrematureExitFile(const char* premature_exit_filepath) + : premature_exit_filepath_(premature_exit_filepath) { + // If a path to the premature-exit file is specified... + if (premature_exit_filepath != NULL && *premature_exit_filepath != '\0') { + // create the file with a single "0" character in it. I/O + // errors are ignored as there's nothing better we can do and we + // don't want to fail the test because of this. + FILE* pfile = posix::FOpen(premature_exit_filepath, "w"); + fwrite("0", 1, 1, pfile); + fclose(pfile); + } + } + + ~ScopedPrematureExitFile() { + if (premature_exit_filepath_ != NULL && *premature_exit_filepath_ != '\0') { + remove(premature_exit_filepath_); + } + } + + private: + const char* const premature_exit_filepath_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedPrematureExitFile); +}; + +} // namespace internal + +// class TestEventListeners + +TestEventListeners::TestEventListeners() + : repeater_(new internal::TestEventRepeater()), + default_result_printer_(NULL), + default_xml_generator_(NULL) { +} + +TestEventListeners::~TestEventListeners() { delete repeater_; } + +// Returns the standard listener responsible for the default console +// output. Can be removed from the listeners list to shut down default +// console output. Note that removing this object from the listener list +// with Release transfers its ownership to the user. +void TestEventListeners::Append(TestEventListener* listener) { + repeater_->Append(listener); +} + +// Removes the given event listener from the list and returns it. It then +// becomes the caller's responsibility to delete the listener. Returns +// NULL if the listener is not found in the list. +TestEventListener* TestEventListeners::Release(TestEventListener* listener) { + if (listener == default_result_printer_) + default_result_printer_ = NULL; + else if (listener == default_xml_generator_) + default_xml_generator_ = NULL; + return repeater_->Release(listener); +} + +// Returns repeater that broadcasts the TestEventListener events to all +// subscribers. +TestEventListener* TestEventListeners::repeater() { return repeater_; } + +// Sets the default_result_printer attribute to the provided listener. +// The listener is also added to the listener list and previous +// default_result_printer is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultResultPrinter(TestEventListener* listener) { + if (default_result_printer_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_result_printer_); + default_result_printer_ = listener; + if (listener != NULL) + Append(listener); + } +} + +// Sets the default_xml_generator attribute to the provided listener. The +// listener is also added to the listener list and previous +// default_xml_generator is removed from it and deleted. The listener can +// also be NULL in which case it will not be added to the list. Does +// nothing if the previous and the current listener objects are the same. +void TestEventListeners::SetDefaultXmlGenerator(TestEventListener* listener) { + if (default_xml_generator_ != listener) { + // It is an error to pass this method a listener that is already in the + // list. + delete Release(default_xml_generator_); + default_xml_generator_ = listener; + if (listener != NULL) + Append(listener); + } +} + +// Controls whether events will be forwarded by the repeater to the +// listeners in the list. +bool TestEventListeners::EventForwardingEnabled() const { + return repeater_->forwarding_enabled(); +} + +void TestEventListeners::SuppressEventForwarding() { + repeater_->set_forwarding_enabled(false); +} + +// class UnitTest + +// Gets the singleton UnitTest object. The first time this method is +// called, a UnitTest object is constructed and returned. Consecutive +// calls will return the same object. +// +// We don't protect this under mutex_ as a user is not supposed to +// call this before main() starts, from which point on the return +// value will never change. +UnitTest* UnitTest::GetInstance() { + // When compiled with MSVC 7.1 in optimized mode, destroying the + // UnitTest object upon exiting the program messes up the exit code, + // causing successful tests to appear failed. We have to use a + // different implementation in this case to bypass the compiler bug. + // This implementation makes the compiler happy, at the cost of + // leaking the UnitTest object. + + // CodeGear C++Builder insists on a public destructor for the + // default implementation. Use this implementation to keep good OO + // design with private destructor. + +#if (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) + static UnitTest* const instance = new UnitTest; + return instance; +#else + static UnitTest instance; + return &instance; +#endif // (_MSC_VER == 1310 && !defined(_DEBUG)) || defined(__BORLANDC__) +} + +// Gets the number of successful test cases. +int UnitTest::successful_test_case_count() const { + return impl()->successful_test_case_count(); +} + +// Gets the number of failed test cases. +int UnitTest::failed_test_case_count() const { + return impl()->failed_test_case_count(); +} + +// Gets the number of all test cases. +int UnitTest::total_test_case_count() const { + return impl()->total_test_case_count(); +} + +// Gets the number of all test cases that contain at least one test +// that should run. +int UnitTest::test_case_to_run_count() const { + return impl()->test_case_to_run_count(); +} + +// Gets the number of successful tests. +int UnitTest::successful_test_count() const { + return impl()->successful_test_count(); +} + +// Gets the number of failed tests. +int UnitTest::failed_test_count() const { return impl()->failed_test_count(); } + +// Gets the number of disabled tests that will be reported in the XML report. +int UnitTest::reportable_disabled_test_count() const { + return impl()->reportable_disabled_test_count(); +} + +// Gets the number of disabled tests. +int UnitTest::disabled_test_count() const { + return impl()->disabled_test_count(); +} + +// Gets the number of tests to be printed in the XML report. +int UnitTest::reportable_test_count() const { + return impl()->reportable_test_count(); +} + +// Gets the number of all tests. +int UnitTest::total_test_count() const { return impl()->total_test_count(); } + +// Gets the number of tests that should run. +int UnitTest::test_to_run_count() const { return impl()->test_to_run_count(); } + +// Gets the time of the test program start, in ms from the start of the +// UNIX epoch. +internal::TimeInMillis UnitTest::start_timestamp() const { + return impl()->start_timestamp(); +} + +// Gets the elapsed time, in milliseconds. +internal::TimeInMillis UnitTest::elapsed_time() const { + return impl()->elapsed_time(); +} + +// Returns true iff the unit test passed (i.e. all test cases passed). +bool UnitTest::Passed() const { return impl()->Passed(); } + +// Returns true iff the unit test failed (i.e. some test case failed +// or something outside of all tests failed). +bool UnitTest::Failed() const { return impl()->Failed(); } + +// Gets the i-th test case among all the test cases. i can range from 0 to +// total_test_case_count() - 1. If i is not in that range, returns NULL. +const TestCase* UnitTest::GetTestCase(int i) const { + return impl()->GetTestCase(i); +} + +// Returns the TestResult containing information on test failures and +// properties logged outside of individual test cases. +const TestResult& UnitTest::ad_hoc_test_result() const { + return *impl()->ad_hoc_test_result(); +} + +// Gets the i-th test case among all the test cases. i can range from 0 to +// total_test_case_count() - 1. If i is not in that range, returns NULL. +TestCase* UnitTest::GetMutableTestCase(int i) { + return impl()->GetMutableTestCase(i); +} + +// Returns the list of event listeners that can be used to track events +// inside Google Test. +TestEventListeners& UnitTest::listeners() { + return *impl()->listeners(); +} + +// Registers and returns a global test environment. When a test +// program is run, all global test environments will be set-up in the +// order they were registered. After all tests in the program have +// finished, all global test environments will be torn-down in the +// *reverse* order they were registered. +// +// The UnitTest object takes ownership of the given environment. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +Environment* UnitTest::AddEnvironment(Environment* env) { + if (env == NULL) { + return NULL; + } + + impl_->environments().push_back(env); + return env; +} + +// Adds a TestPartResult to the current TestResult object. All Google Test +// assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) eventually call +// this to report their results. The user code should use the +// assertion macros instead of calling this directly. +void UnitTest::AddTestPartResult( + TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) GTEST_LOCK_EXCLUDED_(mutex_) { + Message msg; + msg << message; + + internal::MutexLock lock(&mutex_); + if (impl_->gtest_trace_stack().size() > 0) { + msg << "\n" << GTEST_NAME_ << " trace:"; + + for (int i = static_cast(impl_->gtest_trace_stack().size()); + i > 0; --i) { + const internal::TraceInfo& trace = impl_->gtest_trace_stack()[i - 1]; + msg << "\n" << internal::FormatFileLocation(trace.file, trace.line) + << " " << trace.message; + } + } + + if (os_stack_trace.c_str() != NULL && !os_stack_trace.empty()) { + msg << internal::kStackTraceMarker << os_stack_trace; + } + + const TestPartResult result = + TestPartResult(result_type, file_name, line_number, + msg.GetString().c_str()); + impl_->GetTestPartResultReporterForCurrentThread()-> + ReportTestPartResult(result); + + if (result_type != TestPartResult::kSuccess) { + // gtest_break_on_failure takes precedence over + // gtest_throw_on_failure. This allows a user to set the latter + // in the code (perhaps in order to use Google Test assertions + // with another testing framework) and specify the former on the + // command line for debugging. + if (GTEST_FLAG(break_on_failure)) { +#if GTEST_OS_WINDOWS + // Using DebugBreak on Windows allows gtest to still break into a debugger + // when a failure happens and both the --gtest_break_on_failure and + // the --gtest_catch_exceptions flags are specified. + DebugBreak(); +#else + // Dereference NULL through a volatile pointer to prevent the compiler + // from removing. We use this rather than abort() or __builtin_trap() for + // portability: Symbian doesn't implement abort() well, and some debuggers + // don't correctly trap abort(). + *static_cast(NULL) = 1; +#endif // GTEST_OS_WINDOWS + } else if (GTEST_FLAG(throw_on_failure)) { +#if GTEST_HAS_EXCEPTIONS + throw internal::GoogleTestFailureException(result); +#else + // We cannot call abort() as it generates a pop-up in debug mode + // that cannot be suppressed in VC 7.1 or below. + exit(1); +#endif + } + } +} + +// Adds a TestProperty to the current TestResult object when invoked from +// inside a test, to current TestCase's ad_hoc_test_result_ when invoked +// from SetUpTestCase or TearDownTestCase, or to the global property set +// when invoked elsewhere. If the result already contains a property with +// the same key, the value will be updated. +void UnitTest::RecordProperty(const std::string& key, + const std::string& value) { + impl_->RecordProperty(TestProperty(key, value)); +} + +// Runs all tests in this UnitTest object and prints the result. +// Returns 0 if successful, or 1 otherwise. +// +// We don't protect this under mutex_, as we only support calling it +// from the main thread. +int UnitTest::Run() { + const bool in_death_test_child_process = + internal::GTEST_FLAG(internal_run_death_test).length() > 0; + + // Google Test implements this protocol for catching that a test + // program exits before returning control to Google Test: + // + // 1. Upon start, Google Test creates a file whose absolute path + // is specified by the environment variable + // TEST_PREMATURE_EXIT_FILE. + // 2. When Google Test has finished its work, it deletes the file. + // + // This allows a test runner to set TEST_PREMATURE_EXIT_FILE before + // running a Google-Test-based test program and check the existence + // of the file at the end of the test execution to see if it has + // exited prematurely. + + // If we are in the child process of a death test, don't + // create/delete the premature exit file, as doing so is unnecessary + // and will confuse the parent process. Otherwise, create/delete + // the file upon entering/leaving this function. If the program + // somehow exits before this function has a chance to return, the + // premature-exit file will be left undeleted, causing a test runner + // that understands the premature-exit-file protocol to report the + // test as having failed. + const internal::ScopedPrematureExitFile premature_exit_file( + in_death_test_child_process ? + NULL : internal::posix::GetEnv("TEST_PREMATURE_EXIT_FILE")); + + // Captures the value of GTEST_FLAG(catch_exceptions). This value will be + // used for the duration of the program. + impl()->set_catch_exceptions(GTEST_FLAG(catch_exceptions)); + +#if GTEST_HAS_SEH + // Either the user wants Google Test to catch exceptions thrown by the + // tests or this is executing in the context of death test child + // process. In either case the user does not want to see pop-up dialogs + // about crashes - they are expected. + if (impl()->catch_exceptions() || in_death_test_child_process) { +# if !GTEST_OS_WINDOWS_MOBILE + // SetErrorMode doesn't exist on CE. + SetErrorMode(SEM_FAILCRITICALERRORS | SEM_NOALIGNMENTFAULTEXCEPT | + SEM_NOGPFAULTERRORBOX | SEM_NOOPENFILEERRORBOX); +# endif // !GTEST_OS_WINDOWS_MOBILE + +# if (defined(_MSC_VER) || GTEST_OS_WINDOWS_MINGW) && !GTEST_OS_WINDOWS_MOBILE + // Death test children can be terminated with _abort(). On Windows, + // _abort() can show a dialog with a warning message. This forces the + // abort message to go to stderr instead. + _set_error_mode(_OUT_TO_STDERR); +# endif + +# if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE + // In the debug version, Visual Studio pops up a separate dialog + // offering a choice to debug the aborted program. We need to suppress + // this dialog or it will pop up for every EXPECT/ASSERT_DEATH statement + // executed. Google Test will notify the user of any unexpected + // failure via stderr. + // + // VC++ doesn't define _set_abort_behavior() prior to the version 8.0. + // Users of prior VC versions shall suffer the agony and pain of + // clicking through the countless debug dialogs. + // TODO(vladl@google.com): find a way to suppress the abort dialog() in the + // debug mode when compiled with VC 7.1 or lower. + if (!GTEST_FLAG(break_on_failure)) + _set_abort_behavior( + 0x0, // Clear the following flags: + _WRITE_ABORT_MSG | _CALL_REPORTFAULT); // pop-up window, core dump. +# endif + } +#endif // GTEST_HAS_SEH + + return internal::HandleExceptionsInMethodIfSupported( + impl(), + &internal::UnitTestImpl::RunAllTests, + "auxiliary test code (environments or event listeners)") ? 0 : 1; +} + +// Returns the working directory when the first TEST() or TEST_F() was +// executed. +const char* UnitTest::original_working_dir() const { + return impl_->original_working_dir_.c_str(); +} + +// Returns the TestCase object for the test that's currently running, +// or NULL if no test is running. +const TestCase* UnitTest::current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_case(); +} + +// Returns the TestInfo object for the test that's currently running, +// or NULL if no test is running. +const TestInfo* UnitTest::current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + return impl_->current_test_info(); +} + +// Returns the random seed used at the start of the current test run. +int UnitTest::random_seed() const { return impl_->random_seed(); } + +#if GTEST_HAS_PARAM_TEST +// Returns ParameterizedTestCaseRegistry object used to keep track of +// value-parameterized tests and instantiate and register them. +internal::ParameterizedTestCaseRegistry& + UnitTest::parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_) { + return impl_->parameterized_test_registry(); +} +#endif // GTEST_HAS_PARAM_TEST + +// Creates an empty UnitTest. +UnitTest::UnitTest() { + impl_ = new internal::UnitTestImpl(this); +} + +// Destructor of UnitTest. +UnitTest::~UnitTest() { + delete impl_; +} + +// Pushes a trace defined by SCOPED_TRACE() on to the per-thread +// Google Test trace stack. +void UnitTest::PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().push_back(trace); +} + +// Pops a trace from the per-thread Google Test trace stack. +void UnitTest::PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_) { + internal::MutexLock lock(&mutex_); + impl_->gtest_trace_stack().pop_back(); +} + +namespace internal { + +UnitTestImpl::UnitTestImpl(UnitTest* parent) + : parent_(parent), +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4355) // Temporarily disables warning 4355 + // (using this in initializer). + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), +# pragma warning(pop) // Restores the warning state again. +#else + default_global_test_part_result_reporter_(this), + default_per_thread_test_part_result_reporter_(this), +#endif // _MSC_VER + global_test_part_result_repoter_( + &default_global_test_part_result_reporter_), + per_thread_test_part_result_reporter_( + &default_per_thread_test_part_result_reporter_), +#if GTEST_HAS_PARAM_TEST + parameterized_test_registry_(), + parameterized_tests_registered_(false), +#endif // GTEST_HAS_PARAM_TEST + last_death_test_case_(-1), + current_test_case_(NULL), + current_test_info_(NULL), + ad_hoc_test_result_(), + os_stack_trace_getter_(NULL), + post_flag_parse_init_performed_(false), + random_seed_(0), // Will be overridden by the flag before first use. + random_(0), // Will be reseeded before first use. + start_timestamp_(0), + elapsed_time_(0), +#if GTEST_HAS_DEATH_TEST + death_test_factory_(new DefaultDeathTestFactory), +#endif + // Will be overridden by the flag before first use. + catch_exceptions_(false) { + listeners()->SetDefaultResultPrinter(new PrettyUnitTestResultPrinter); +} + +UnitTestImpl::~UnitTestImpl() { + // Deletes every TestCase. + ForEach(test_cases_, internal::Delete); + + // Deletes every Environment. + ForEach(environments_, internal::Delete); + + delete os_stack_trace_getter_; +} + +// Adds a TestProperty to the current TestResult object when invoked in a +// context of a test, to current test case's ad_hoc_test_result when invoke +// from SetUpTestCase/TearDownTestCase, or to the global property set +// otherwise. If the result already contains a property with the same key, +// the value will be updated. +void UnitTestImpl::RecordProperty(const TestProperty& test_property) { + std::string xml_element; + TestResult* test_result; // TestResult appropriate for property recording. + + if (current_test_info_ != NULL) { + xml_element = "testcase"; + test_result = &(current_test_info_->result_); + } else if (current_test_case_ != NULL) { + xml_element = "testsuite"; + test_result = &(current_test_case_->ad_hoc_test_result_); + } else { + xml_element = "testsuites"; + test_result = &ad_hoc_test_result_; + } + test_result->RecordProperty(xml_element, test_property); +} + +#if GTEST_HAS_DEATH_TEST +// Disables event forwarding if the control is currently in a death test +// subprocess. Must not be called before InitGoogleTest. +void UnitTestImpl::SuppressTestEventsIfInSubprocess() { + if (internal_run_death_test_flag_.get() != NULL) + listeners()->SuppressEventForwarding(); +} +#endif // GTEST_HAS_DEATH_TEST + +// Initializes event listeners performing XML output as specified by +// UnitTestOptions. Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureXmlOutput() { + const std::string& output_format = UnitTestOptions::GetOutputFormat(); + if (output_format == "xml") { + listeners()->SetDefaultXmlGenerator(new XmlUnitTestResultPrinter( + UnitTestOptions::GetAbsolutePathToOutputFile().c_str())); + } else if (output_format != "") { + printf("WARNING: unrecognized output format \"%s\" ignored.\n", + output_format.c_str()); + fflush(stdout); + } +} + +#if GTEST_CAN_STREAM_RESULTS_ +// Initializes event listeners for streaming test results in string form. +// Must not be called before InitGoogleTest. +void UnitTestImpl::ConfigureStreamingOutput() { + const std::string& target = GTEST_FLAG(stream_result_to); + if (!target.empty()) { + const size_t pos = target.find(':'); + if (pos != std::string::npos) { + listeners()->Append(new StreamingListener(target.substr(0, pos), + target.substr(pos+1))); + } else { + printf("WARNING: unrecognized streaming target \"%s\" ignored.\n", + target.c_str()); + fflush(stdout); + } + } +} +#endif // GTEST_CAN_STREAM_RESULTS_ + +// Performs initialization dependent upon flag values obtained in +// ParseGoogleTestFlagsOnly. Is called from InitGoogleTest after the call to +// ParseGoogleTestFlagsOnly. In case a user neglects to call InitGoogleTest +// this function is also called from RunAllTests. Since this function can be +// called more than once, it has to be idempotent. +void UnitTestImpl::PostFlagParsingInit() { + // Ensures that this function does not execute more than once. + if (!post_flag_parse_init_performed_) { + post_flag_parse_init_performed_ = true; + +#if GTEST_HAS_DEATH_TEST + InitDeathTestSubprocessControlInfo(); + SuppressTestEventsIfInSubprocess(); +#endif // GTEST_HAS_DEATH_TEST + + // Registers parameterized tests. This makes parameterized tests + // available to the UnitTest reflection API without running + // RUN_ALL_TESTS. + RegisterParameterizedTests(); + + // Configures listeners for XML output. This makes it possible for users + // to shut down the default XML output before invoking RUN_ALL_TESTS. + ConfigureXmlOutput(); + +#if GTEST_CAN_STREAM_RESULTS_ + // Configures listeners for streaming test results to the specified server. + ConfigureStreamingOutput(); +#endif // GTEST_CAN_STREAM_RESULTS_ + } +} + +// A predicate that checks the name of a TestCase against a known +// value. +// +// This is used for implementation of the UnitTest class only. We put +// it in the anonymous namespace to prevent polluting the outer +// namespace. +// +// TestCaseNameIs is copyable. +class TestCaseNameIs { + public: + // Constructor. + explicit TestCaseNameIs(const std::string& name) + : name_(name) {} + + // Returns true iff the name of test_case matches name_. + bool operator()(const TestCase* test_case) const { + return test_case != NULL && strcmp(test_case->name(), name_.c_str()) == 0; + } + + private: + std::string name_; +}; + +// Finds and returns a TestCase with the given name. If one doesn't +// exist, creates one and returns it. It's the CALLER'S +// RESPONSIBILITY to ensure that this function is only called WHEN THE +// TESTS ARE NOT SHUFFLED. +// +// Arguments: +// +// test_case_name: name of the test case +// type_param: the name of the test case's type parameter, or NULL if +// this is not a typed or a type-parameterized test case. +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +TestCase* UnitTestImpl::GetTestCase(const char* test_case_name, + const char* type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc) { + // Can we find a TestCase with the given name? + const std::vector::const_iterator test_case = + std::find_if(test_cases_.begin(), test_cases_.end(), + TestCaseNameIs(test_case_name)); + + if (test_case != test_cases_.end()) + return *test_case; + + // No. Let's create one. + TestCase* const new_test_case = + new TestCase(test_case_name, type_param, set_up_tc, tear_down_tc); + + // Is this a death test case? + if (internal::UnitTestOptions::MatchesFilter(test_case_name, + kDeathTestCaseFilter)) { + // Yes. Inserts the test case after the last death test case + // defined so far. This only works when the test cases haven't + // been shuffled. Otherwise we may end up running a death test + // after a non-death test. + ++last_death_test_case_; + test_cases_.insert(test_cases_.begin() + last_death_test_case_, + new_test_case); + } else { + // No. Appends to the end of the list. + test_cases_.push_back(new_test_case); + } + + test_case_indices_.push_back(static_cast(test_case_indices_.size())); + return new_test_case; +} + +// Helpers for setting up / tearing down the given environment. They +// are for use in the ForEach() function. +static void SetUpEnvironment(Environment* env) { env->SetUp(); } +static void TearDownEnvironment(Environment* env) { env->TearDown(); } + +// Runs all tests in this UnitTest object, prints the result, and +// returns true if all tests are successful. If any exception is +// thrown during a test, the test is considered to be failed, but the +// rest of the tests will still be run. +// +// When parameterized tests are enabled, it expands and registers +// parameterized tests first in RegisterParameterizedTests(). +// All other functions called from RunAllTests() may safely assume that +// parameterized tests are ready to be counted and run. +bool UnitTestImpl::RunAllTests() { + // Makes sure InitGoogleTest() was called. + if (!GTestIsInitialized()) { + printf("%s", + "\nThis test program did NOT call ::testing::InitGoogleTest " + "before calling RUN_ALL_TESTS(). Please fix it.\n"); + return false; + } + + // Do not run any test if the --help flag was specified. + if (g_help_flag) + return true; + + // Repeats the call to the post-flag parsing initialization in case the + // user didn't call InitGoogleTest. + PostFlagParsingInit(); + + // Even if sharding is not on, test runners may want to use the + // GTEST_SHARD_STATUS_FILE to query whether the test supports the sharding + // protocol. + internal::WriteToShardStatusFileIfNeeded(); + + // True iff we are in a subprocess for running a thread-safe-style + // death test. + bool in_subprocess_for_death_test = false; + +#if GTEST_HAS_DEATH_TEST + in_subprocess_for_death_test = (internal_run_death_test_flag_.get() != NULL); +#endif // GTEST_HAS_DEATH_TEST + + const bool should_shard = ShouldShard(kTestTotalShards, kTestShardIndex, + in_subprocess_for_death_test); + + // Compares the full test names with the filter to decide which + // tests to run. + const bool has_tests_to_run = FilterTests(should_shard + ? HONOR_SHARDING_PROTOCOL + : IGNORE_SHARDING_PROTOCOL) > 0; + + // Lists the tests and exits if the --gtest_list_tests flag was specified. + if (GTEST_FLAG(list_tests)) { + // This must be called *after* FilterTests() has been called. + ListTestsMatchingFilter(); + return true; + } + + random_seed_ = GTEST_FLAG(shuffle) ? + GetRandomSeedFromFlag(GTEST_FLAG(random_seed)) : 0; + + // True iff at least one test has failed. + bool failed = false; + + TestEventListener* repeater = listeners()->repeater(); + + start_timestamp_ = GetTimeInMillis(); + repeater->OnTestProgramStart(*parent_); + + // How many times to repeat the tests? We don't want to repeat them + // when we are inside the subprocess of a death test. + const int repeat = in_subprocess_for_death_test ? 1 : GTEST_FLAG(repeat); + // Repeats forever if the repeat count is negative. + const bool forever = repeat < 0; + for (int i = 0; forever || i != repeat; i++) { + // We want to preserve failures generated by ad-hoc test + // assertions executed before RUN_ALL_TESTS(). + ClearNonAdHocTestResult(); + + const TimeInMillis start = GetTimeInMillis(); + + // Shuffles test cases and tests if requested. + if (has_tests_to_run && GTEST_FLAG(shuffle)) { + random()->Reseed(random_seed_); + // This should be done before calling OnTestIterationStart(), + // such that a test event listener can see the actual test order + // in the event. + ShuffleTests(); + } + + // Tells the unit test event listeners that the tests are about to start. + repeater->OnTestIterationStart(*parent_, i); + + // Runs each test case if there is at least one test to run. + if (has_tests_to_run) { + // Sets up all environments beforehand. + repeater->OnEnvironmentsSetUpStart(*parent_); + ForEach(environments_, SetUpEnvironment); + repeater->OnEnvironmentsSetUpEnd(*parent_); + + // Runs the tests only if there was no fatal failure during global + // set-up. + if (!Test::HasFatalFailure()) { + for (int test_index = 0; test_index < total_test_case_count(); + test_index++) { + GetMutableTestCase(test_index)->Run(); + } + } + + // Tears down all environments in reverse order afterwards. + repeater->OnEnvironmentsTearDownStart(*parent_); + std::for_each(environments_.rbegin(), environments_.rend(), + TearDownEnvironment); + repeater->OnEnvironmentsTearDownEnd(*parent_); + } + + elapsed_time_ = GetTimeInMillis() - start; + + // Tells the unit test event listener that the tests have just finished. + repeater->OnTestIterationEnd(*parent_, i); + + // Gets the result and clears it. + if (!Passed()) { + failed = true; + } + + // Restores the original test order after the iteration. This + // allows the user to quickly repro a failure that happens in the + // N-th iteration without repeating the first (N - 1) iterations. + // This is not enclosed in "if (GTEST_FLAG(shuffle)) { ... }", in + // case the user somehow changes the value of the flag somewhere + // (it's always safe to unshuffle the tests). + UnshuffleTests(); + + if (GTEST_FLAG(shuffle)) { + // Picks a new random seed for each iteration. + random_seed_ = GetNextRandomSeed(random_seed_); + } + } + + repeater->OnTestProgramEnd(*parent_); + + return !failed; +} + +// Reads the GTEST_SHARD_STATUS_FILE environment variable, and creates the file +// if the variable is present. If a file already exists at this location, this +// function will write over it. If the variable is present, but the file cannot +// be created, prints an error and exits. +void WriteToShardStatusFileIfNeeded() { + const char* const test_shard_file = posix::GetEnv(kTestShardStatusFile); + if (test_shard_file != NULL) { + FILE* const file = posix::FOpen(test_shard_file, "w"); + if (file == NULL) { + ColoredPrintf(COLOR_RED, + "Could not write to the test shard status file \"%s\" " + "specified by the %s environment variable.\n", + test_shard_file, kTestShardStatusFile); + fflush(stdout); + exit(EXIT_FAILURE); + } + fclose(file); + } +} + +// Checks whether sharding is enabled by examining the relevant +// environment variable values. If the variables are present, +// but inconsistent (i.e., shard_index >= total_shards), prints +// an error and exits. If in_subprocess_for_death_test, sharding is +// disabled because it must only be applied to the original test +// process. Otherwise, we could filter out death tests we intended to execute. +bool ShouldShard(const char* total_shards_env, + const char* shard_index_env, + bool in_subprocess_for_death_test) { + if (in_subprocess_for_death_test) { + return false; + } + + const Int32 total_shards = Int32FromEnvOrDie(total_shards_env, -1); + const Int32 shard_index = Int32FromEnvOrDie(shard_index_env, -1); + + if (total_shards == -1 && shard_index == -1) { + return false; + } else if (total_shards == -1 && shard_index != -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestShardIndex << " = " << shard_index + << ", but have left " << kTestTotalShards << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (total_shards != -1 && shard_index == -1) { + const Message msg = Message() + << "Invalid environment variables: you have " + << kTestTotalShards << " = " << total_shards + << ", but have left " << kTestShardIndex << " unset.\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } else if (shard_index < 0 || shard_index >= total_shards) { + const Message msg = Message() + << "Invalid environment variables: we require 0 <= " + << kTestShardIndex << " < " << kTestTotalShards + << ", but you have " << kTestShardIndex << "=" << shard_index + << ", " << kTestTotalShards << "=" << total_shards << ".\n"; + ColoredPrintf(COLOR_RED, msg.GetString().c_str()); + fflush(stdout); + exit(EXIT_FAILURE); + } + + return total_shards > 1; +} + +// Parses the environment variable var as an Int32. If it is unset, +// returns default_val. If it is not an Int32, prints an error +// and aborts. +Int32 Int32FromEnvOrDie(const char* var, Int32 default_val) { + const char* str_val = posix::GetEnv(var); + if (str_val == NULL) { + return default_val; + } + + Int32 result; + if (!ParseInt32(Message() << "The value of environment variable " << var, + str_val, &result)) { + exit(EXIT_FAILURE); + } + return result; +} + +// Given the total number of shards, the shard index, and the test id, +// returns true iff the test should be run on this shard. The test id is +// some arbitrary but unique non-negative integer assigned to each test +// method. Assumes that 0 <= shard_index < total_shards. +bool ShouldRunTestOnShard(int total_shards, int shard_index, int test_id) { + return (test_id % total_shards) == shard_index; +} + +// Compares the name of each test with the user-specified filter to +// decide whether the test should be run, then records the result in +// each TestCase and TestInfo object. +// If shard_tests == true, further filters tests based on sharding +// variables in the environment - see +// http://code.google.com/p/googletest/wiki/GoogleTestAdvancedGuide. +// Returns the number of tests that should run. +int UnitTestImpl::FilterTests(ReactionToSharding shard_tests) { + const Int32 total_shards = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestTotalShards, -1) : -1; + const Int32 shard_index = shard_tests == HONOR_SHARDING_PROTOCOL ? + Int32FromEnvOrDie(kTestShardIndex, -1) : -1; + + // num_runnable_tests are the number of tests that will + // run across all shards (i.e., match filter and are not disabled). + // num_selected_tests are the number of tests to be run on + // this shard. + int num_runnable_tests = 0; + int num_selected_tests = 0; + for (size_t i = 0; i < test_cases_.size(); i++) { + TestCase* const test_case = test_cases_[i]; + const std::string &test_case_name = test_case->name(); + test_case->set_should_run(false); + + for (size_t j = 0; j < test_case->test_info_list().size(); j++) { + TestInfo* const test_info = test_case->test_info_list()[j]; + const std::string test_name(test_info->name()); + // A test is disabled if test case name or test name matches + // kDisableTestFilter. + const bool is_disabled = + internal::UnitTestOptions::MatchesFilter(test_case_name, + kDisableTestFilter) || + internal::UnitTestOptions::MatchesFilter(test_name, + kDisableTestFilter); + test_info->is_disabled_ = is_disabled; + + const bool matches_filter = + internal::UnitTestOptions::FilterMatchesTest(test_case_name, + test_name); + test_info->matches_filter_ = matches_filter; + + const bool is_runnable = + (GTEST_FLAG(also_run_disabled_tests) || !is_disabled) && + matches_filter; + + const bool is_selected = is_runnable && + (shard_tests == IGNORE_SHARDING_PROTOCOL || + ShouldRunTestOnShard(total_shards, shard_index, + num_runnable_tests)); + + num_runnable_tests += is_runnable; + num_selected_tests += is_selected; + + test_info->should_run_ = is_selected; + test_case->set_should_run(test_case->should_run() || is_selected); + } + } + return num_selected_tests; +} + +// Prints the given C-string on a single line by replacing all '\n' +// characters with string "\\n". If the output takes more than +// max_length characters, only prints the first max_length characters +// and "...". +static void PrintOnOneLine(const char* str, int max_length) { + if (str != NULL) { + for (int i = 0; *str != '\0'; ++str) { + if (i >= max_length) { + printf("..."); + break; + } + if (*str == '\n') { + printf("\\n"); + i += 2; + } else { + printf("%c", *str); + ++i; + } + } + } +} + +// Prints the names of the tests matching the user-specified filter flag. +void UnitTestImpl::ListTestsMatchingFilter() { + // Print at most this many characters for each type/value parameter. + const int kMaxParamLength = 250; + + for (size_t i = 0; i < test_cases_.size(); i++) { + const TestCase* const test_case = test_cases_[i]; + bool printed_test_case_name = false; + + for (size_t j = 0; j < test_case->test_info_list().size(); j++) { + const TestInfo* const test_info = + test_case->test_info_list()[j]; + if (test_info->matches_filter_) { + if (!printed_test_case_name) { + printed_test_case_name = true; + printf("%s.", test_case->name()); + if (test_case->type_param() != NULL) { + printf(" # %s = ", kTypeParamLabel); + // We print the type parameter on a single line to make + // the output easy to parse by a program. + PrintOnOneLine(test_case->type_param(), kMaxParamLength); + } + printf("\n"); + } + printf(" %s", test_info->name()); + if (test_info->value_param() != NULL) { + printf(" # %s = ", kValueParamLabel); + // We print the value parameter on a single line to make the + // output easy to parse by a program. + PrintOnOneLine(test_info->value_param(), kMaxParamLength); + } + printf("\n"); + } + } + } + fflush(stdout); +} + +// Sets the OS stack trace getter. +// +// Does nothing if the input and the current OS stack trace getter are +// the same; otherwise, deletes the old getter and makes the input the +// current getter. +void UnitTestImpl::set_os_stack_trace_getter( + OsStackTraceGetterInterface* getter) { + if (os_stack_trace_getter_ != getter) { + delete os_stack_trace_getter_; + os_stack_trace_getter_ = getter; + } +} + +// Returns the current OS stack trace getter if it is not NULL; +// otherwise, creates an OsStackTraceGetter, makes it the current +// getter, and returns it. +OsStackTraceGetterInterface* UnitTestImpl::os_stack_trace_getter() { + if (os_stack_trace_getter_ == NULL) { + os_stack_trace_getter_ = new OsStackTraceGetter; + } + + return os_stack_trace_getter_; +} + +// Returns the TestResult for the test that's currently running, or +// the TestResult for the ad hoc test if no test is running. +TestResult* UnitTestImpl::current_test_result() { + return current_test_info_ ? + &(current_test_info_->result_) : &ad_hoc_test_result_; +} + +// Shuffles all test cases, and the tests within each test case, +// making sure that death tests are still run first. +void UnitTestImpl::ShuffleTests() { + // Shuffles the death test cases. + ShuffleRange(random(), 0, last_death_test_case_ + 1, &test_case_indices_); + + // Shuffles the non-death test cases. + ShuffleRange(random(), last_death_test_case_ + 1, + static_cast(test_cases_.size()), &test_case_indices_); + + // Shuffles the tests inside each test case. + for (size_t i = 0; i < test_cases_.size(); i++) { + test_cases_[i]->ShuffleTests(random()); + } +} + +// Restores the test cases and tests to their order before the first shuffle. +void UnitTestImpl::UnshuffleTests() { + for (size_t i = 0; i < test_cases_.size(); i++) { + // Unshuffles the tests in each test case. + test_cases_[i]->UnshuffleTests(); + // Resets the index of each test case. + test_case_indices_[i] = static_cast(i); + } +} + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +std::string GetCurrentOsStackTraceExceptTop(UnitTest* /*unit_test*/, + int skip_count) { + // We pass skip_count + 1 to skip this wrapper function in addition + // to what the user really wants to skip. + return GetUnitTestImpl()->CurrentOsStackTraceExceptTop(skip_count + 1); +} + +// Used by the GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_ macro to +// suppress unreachable code warnings. +namespace { +class ClassUniqueToAlwaysTrue {}; +} + +bool IsTrue(bool condition) { return condition; } + +bool AlwaysTrue() { +#if GTEST_HAS_EXCEPTIONS + // This condition is always false so AlwaysTrue() never actually throws, + // but it makes the compiler think that it may throw. + if (IsTrue(false)) + throw ClassUniqueToAlwaysTrue(); +#endif // GTEST_HAS_EXCEPTIONS + return true; +} + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +bool SkipPrefix(const char* prefix, const char** pstr) { + const size_t prefix_len = strlen(prefix); + if (strncmp(*pstr, prefix, prefix_len) == 0) { + *pstr += prefix_len; + return true; + } + return false; +} + +// Parses a string as a command line flag. The string should have +// the format "--flag=value". When def_optional is true, the "=value" +// part can be omitted. +// +// Returns the value of the flag, or NULL if the parsing failed. +const char* ParseFlagValue(const char* str, + const char* flag, + bool def_optional) { + // str and flag must not be NULL. + if (str == NULL || flag == NULL) return NULL; + + // The flag must start with "--" followed by GTEST_FLAG_PREFIX_. + const std::string flag_str = std::string("--") + GTEST_FLAG_PREFIX_ + flag; + const size_t flag_len = flag_str.length(); + if (strncmp(str, flag_str.c_str(), flag_len) != 0) return NULL; + + // Skips the flag name. + const char* flag_end = str + flag_len; + + // When def_optional is true, it's OK to not have a "=value" part. + if (def_optional && (flag_end[0] == '\0')) { + return flag_end; + } + + // If def_optional is true and there are more characters after the + // flag name, or if def_optional is false, there must be a '=' after + // the flag name. + if (flag_end[0] != '=') return NULL; + + // Returns the string after "=". + return flag_end + 1; +} + +// Parses a string for a bool flag, in the form of either +// "--flag=value" or "--flag". +// +// In the former case, the value is taken as true as long as it does +// not start with '0', 'f', or 'F'. +// +// In the latter case, the value is taken as true. +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseBoolFlag(const char* str, const char* flag, bool* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, true); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Converts the string value to a bool. + *value = !(*value_str == '0' || *value_str == 'f' || *value_str == 'F'); + return true; +} + +// Parses a string for an Int32 flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseInt32Flag(const char* str, const char* flag, Int32* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + return ParseInt32(Message() << "The value of flag --" << flag, + value_str, value); +} + +// Parses a string for a string flag, in the form of +// "--flag=value". +// +// On success, stores the value of the flag in *value, and returns +// true. On failure, returns false without changing *value. +bool ParseStringFlag(const char* str, const char* flag, std::string* value) { + // Gets the value of the flag as a string. + const char* const value_str = ParseFlagValue(str, flag, false); + + // Aborts if the parsing failed. + if (value_str == NULL) return false; + + // Sets *value to the value of the flag. + *value = value_str; + return true; +} + +// Determines whether a string has a prefix that Google Test uses for its +// flags, i.e., starts with GTEST_FLAG_PREFIX_ or GTEST_FLAG_PREFIX_DASH_. +// If Google Test detects that a command line flag has its prefix but is not +// recognized, it will print its help message. Flags starting with +// GTEST_INTERNAL_PREFIX_ followed by "internal_" are considered Google Test +// internal flags and do not trigger the help message. +static bool HasGoogleTestFlagPrefix(const char* str) { + return (SkipPrefix("--", &str) || + SkipPrefix("-", &str) || + SkipPrefix("/", &str)) && + !SkipPrefix(GTEST_FLAG_PREFIX_ "internal_", &str) && + (SkipPrefix(GTEST_FLAG_PREFIX_, &str) || + SkipPrefix(GTEST_FLAG_PREFIX_DASH_, &str)); +} + +// Prints a string containing code-encoded text. The following escape +// sequences can be used in the string to control the text color: +// +// @@ prints a single '@' character. +// @R changes the color to red. +// @G changes the color to green. +// @Y changes the color to yellow. +// @D changes to the default terminal text color. +// +// TODO(wan@google.com): Write tests for this once we add stdout +// capturing to Google Test. +static void PrintColorEncoded(const char* str) { + GTestColor color = COLOR_DEFAULT; // The current color. + + // Conceptually, we split the string into segments divided by escape + // sequences. Then we print one segment at a time. At the end of + // each iteration, the str pointer advances to the beginning of the + // next segment. + for (;;) { + const char* p = strchr(str, '@'); + if (p == NULL) { + ColoredPrintf(color, "%s", str); + return; + } + + ColoredPrintf(color, "%s", std::string(str, p).c_str()); + + const char ch = p[1]; + str = p + 2; + if (ch == '@') { + ColoredPrintf(color, "@"); + } else if (ch == 'D') { + color = COLOR_DEFAULT; + } else if (ch == 'R') { + color = COLOR_RED; + } else if (ch == 'G') { + color = COLOR_GREEN; + } else if (ch == 'Y') { + color = COLOR_YELLOW; + } else { + --str; + } + } +} + +static const char kColorEncodedHelpMessage[] = +"This program contains tests written using " GTEST_NAME_ ". You can use the\n" +"following command line flags to control its behavior:\n" +"\n" +"Test Selection:\n" +" @G--" GTEST_FLAG_PREFIX_ "list_tests@D\n" +" List the names of all tests instead of running them. The name of\n" +" TEST(Foo, Bar) is \"Foo.Bar\".\n" +" @G--" GTEST_FLAG_PREFIX_ "filter=@YPOSTIVE_PATTERNS" + "[@G-@YNEGATIVE_PATTERNS]@D\n" +" Run only the tests whose name matches one of the positive patterns but\n" +" none of the negative patterns. '?' matches any single character; '*'\n" +" matches any substring; ':' separates two patterns.\n" +" @G--" GTEST_FLAG_PREFIX_ "also_run_disabled_tests@D\n" +" Run all disabled tests too.\n" +"\n" +"Test Execution:\n" +" @G--" GTEST_FLAG_PREFIX_ "repeat=@Y[COUNT]@D\n" +" Run the tests repeatedly; use a negative count to repeat forever.\n" +" @G--" GTEST_FLAG_PREFIX_ "shuffle@D\n" +" Randomize tests' orders on every iteration.\n" +" @G--" GTEST_FLAG_PREFIX_ "random_seed=@Y[NUMBER]@D\n" +" Random number seed to use for shuffling test orders (between 1 and\n" +" 99999, or 0 to use a seed based on the current time).\n" +"\n" +"Test Output:\n" +" @G--" GTEST_FLAG_PREFIX_ "color=@Y(@Gyes@Y|@Gno@Y|@Gauto@Y)@D\n" +" Enable/disable colored output. The default is @Gauto@D.\n" +" -@G-" GTEST_FLAG_PREFIX_ "print_time=0@D\n" +" Don't print the elapsed time of each test.\n" +" @G--" GTEST_FLAG_PREFIX_ "output=xml@Y[@G:@YDIRECTORY_PATH@G" + GTEST_PATH_SEP_ "@Y|@G:@YFILE_PATH]@D\n" +" Generate an XML report in the given directory or with the given file\n" +" name. @YFILE_PATH@D defaults to @Gtest_details.xml@D.\n" +#if GTEST_CAN_STREAM_RESULTS_ +" @G--" GTEST_FLAG_PREFIX_ "stream_result_to=@YHOST@G:@YPORT@D\n" +" Stream test results to the given server.\n" +#endif // GTEST_CAN_STREAM_RESULTS_ +"\n" +"Assertion Behavior:\n" +#if GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "death_test_style=@Y(@Gfast@Y|@Gthreadsafe@Y)@D\n" +" Set the default death test style.\n" +#endif // GTEST_HAS_DEATH_TEST && !GTEST_OS_WINDOWS +" @G--" GTEST_FLAG_PREFIX_ "break_on_failure@D\n" +" Turn assertion failures into debugger break-points.\n" +" @G--" GTEST_FLAG_PREFIX_ "throw_on_failure@D\n" +" Turn assertion failures into C++ exceptions.\n" +" @G--" GTEST_FLAG_PREFIX_ "catch_exceptions=0@D\n" +" Do not report exceptions as test failures. Instead, allow them\n" +" to crash the program or throw a pop-up (on Windows).\n" +"\n" +"Except for @G--" GTEST_FLAG_PREFIX_ "list_tests@D, you can alternatively set " + "the corresponding\n" +"environment variable of a flag (all letters in upper-case). For example, to\n" +"disable colored text output, you can either specify @G--" GTEST_FLAG_PREFIX_ + "color=no@D or set\n" +"the @G" GTEST_FLAG_PREFIX_UPPER_ "COLOR@D environment variable to @Gno@D.\n" +"\n" +"For more information, please read the " GTEST_NAME_ " documentation at\n" +"@G" GTEST_PROJECT_URL_ "@D. If you find a bug in " GTEST_NAME_ "\n" +"(not one in your own code or tests), please report it to\n" +"@G<" GTEST_DEV_EMAIL_ ">@D.\n"; + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. The type parameter CharType can be +// instantiated to either char or wchar_t. +template +void ParseGoogleTestFlagsOnlyImpl(int* argc, CharType** argv) { + for (int i = 1; i < *argc; i++) { + const std::string arg_string = StreamableToString(argv[i]); + const char* const arg = arg_string.c_str(); + + using internal::ParseBoolFlag; + using internal::ParseInt32Flag; + using internal::ParseStringFlag; + + // Do we see a Google Test flag? + if (ParseBoolFlag(arg, kAlsoRunDisabledTestsFlag, + >EST_FLAG(also_run_disabled_tests)) || + ParseBoolFlag(arg, kBreakOnFailureFlag, + >EST_FLAG(break_on_failure)) || + ParseBoolFlag(arg, kCatchExceptionsFlag, + >EST_FLAG(catch_exceptions)) || + ParseStringFlag(arg, kColorFlag, >EST_FLAG(color)) || + ParseStringFlag(arg, kDeathTestStyleFlag, + >EST_FLAG(death_test_style)) || + ParseBoolFlag(arg, kDeathTestUseFork, + >EST_FLAG(death_test_use_fork)) || + ParseStringFlag(arg, kFilterFlag, >EST_FLAG(filter)) || + ParseStringFlag(arg, kInternalRunDeathTestFlag, + >EST_FLAG(internal_run_death_test)) || + ParseBoolFlag(arg, kListTestsFlag, >EST_FLAG(list_tests)) || + ParseStringFlag(arg, kOutputFlag, >EST_FLAG(output)) || + ParseBoolFlag(arg, kPrintTimeFlag, >EST_FLAG(print_time)) || + ParseInt32Flag(arg, kRandomSeedFlag, >EST_FLAG(random_seed)) || + ParseInt32Flag(arg, kRepeatFlag, >EST_FLAG(repeat)) || + ParseBoolFlag(arg, kShuffleFlag, >EST_FLAG(shuffle)) || + ParseInt32Flag(arg, kStackTraceDepthFlag, + >EST_FLAG(stack_trace_depth)) || + ParseStringFlag(arg, kStreamResultToFlag, + >EST_FLAG(stream_result_to)) || + ParseBoolFlag(arg, kThrowOnFailureFlag, + >EST_FLAG(throw_on_failure)) + ) { + // Yes. Shift the remainder of the argv list left by one. Note + // that argv has (*argc + 1) elements, the last one always being + // NULL. The following loop moves the trailing NULL element as + // well. + for (int j = i; j != *argc; j++) { + argv[j] = argv[j + 1]; + } + + // Decrements the argument count. + (*argc)--; + + // We also need to decrement the iterator as we just removed + // an element. + i--; + } else if (arg_string == "--help" || arg_string == "-h" || + arg_string == "-?" || arg_string == "/?" || + HasGoogleTestFlagPrefix(arg)) { + // Both help flag and unrecognized Google Test flags (excluding + // internal ones) trigger help display. + g_help_flag = true; + } + } + + if (g_help_flag) { + // We print the help here instead of in RUN_ALL_TESTS(), as the + // latter may not be called at all if the user is using Google + // Test with another testing framework. + PrintColorEncoded(kColorEncodedHelpMessage); + } +} + +// Parses the command line for Google Test flags, without initializing +// other parts of Google Test. +void ParseGoogleTestFlagsOnly(int* argc, char** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} +void ParseGoogleTestFlagsOnly(int* argc, wchar_t** argv) { + ParseGoogleTestFlagsOnlyImpl(argc, argv); +} + +// The internal implementation of InitGoogleTest(). +// +// The type parameter CharType can be instantiated to either char or +// wchar_t. +template +void InitGoogleTestImpl(int* argc, CharType** argv) { + g_init_gtest_count++; + + // We don't want to run the initialization code twice. + if (g_init_gtest_count != 1) return; + + if (*argc <= 0) return; + + internal::g_executable_path = internal::StreamableToString(argv[0]); + +#if GTEST_HAS_DEATH_TEST + + g_argvs.clear(); + for (int i = 0; i != *argc; i++) { + g_argvs.push_back(StreamableToString(argv[i])); + } + +#endif // GTEST_HAS_DEATH_TEST + + ParseGoogleTestFlagsOnly(argc, argv); + GetUnitTestImpl()->PostFlagParsingInit(); +} + +} // namespace internal + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +void InitGoogleTest(int* argc, char** argv) { + internal::InitGoogleTestImpl(argc, argv); +} + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +void InitGoogleTest(int* argc, wchar_t** argv) { + internal::InitGoogleTestImpl(argc, argv); +} + +} // namespace testing +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan), vladl@google.com (Vlad Losev) +// +// This file implements death tests. + + +#if GTEST_HAS_DEATH_TEST + +# if GTEST_OS_MAC +# include +# endif // GTEST_OS_MAC + +# include +# include +# include + +# if GTEST_OS_LINUX +# include +# endif // GTEST_OS_LINUX + +# include + +# if GTEST_OS_WINDOWS +# include +# else +# include +# include +# endif // GTEST_OS_WINDOWS + +# if GTEST_OS_QNX +# include +# endif // GTEST_OS_QNX + +#endif // GTEST_HAS_DEATH_TEST + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { + +// Constants. + +// The default death test style. +static const char kDefaultDeathTestStyle[] = "fast"; + +GTEST_DEFINE_string_( + death_test_style, + internal::StringFromGTestEnv("death_test_style", kDefaultDeathTestStyle), + "Indicates how to run a death test in a forked child process: " + "\"threadsafe\" (child process re-executes the test binary " + "from the beginning, running only the specific death test) or " + "\"fast\" (child process runs the death test immediately " + "after forking)."); + +GTEST_DEFINE_bool_( + death_test_use_fork, + internal::BoolFromGTestEnv("death_test_use_fork", false), + "Instructs to use fork()/_exit() instead of clone() in death tests. " + "Ignored and always uses fork() on POSIX systems where clone() is not " + "implemented. Useful when running under valgrind or similar tools if " + "those do not support clone(). Valgrind 3.3.1 will just fail if " + "it sees an unsupported combination of clone() flags. " + "It is not recommended to use this flag w/o valgrind though it will " + "work in 99% of the cases. Once valgrind is fixed, this flag will " + "most likely be removed."); + +namespace internal { +GTEST_DEFINE_string_( + internal_run_death_test, "", + "Indicates the file, line number, temporal index of " + "the single death test to run, and a file descriptor to " + "which a success code may be sent, all separated by " + "the '|' characters. This flag is specified if and only if the current " + "process is a sub-process launched for running a thread-safe " + "death test. FOR INTERNAL USE ONLY."); +} // namespace internal + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Valid only for fast death tests. Indicates the code is running in the +// child process of a fast style death test. +static bool g_in_fast_death_test_child = false; + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +bool InDeathTestChild() { +# if GTEST_OS_WINDOWS + + // On Windows, death tests are thread-safe regardless of the value of the + // death_test_style flag. + return !GTEST_FLAG(internal_run_death_test).empty(); + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") + return !GTEST_FLAG(internal_run_death_test).empty(); + else + return g_in_fast_death_test_child; +#endif +} + +} // namespace internal + +// ExitedWithCode constructor. +ExitedWithCode::ExitedWithCode(int exit_code) : exit_code_(exit_code) { +} + +// ExitedWithCode function-call operator. +bool ExitedWithCode::operator()(int exit_status) const { +# if GTEST_OS_WINDOWS + + return exit_status == exit_code_; + +# else + + return WIFEXITED(exit_status) && WEXITSTATUS(exit_status) == exit_code_; + +# endif // GTEST_OS_WINDOWS +} + +# if !GTEST_OS_WINDOWS +// KilledBySignal constructor. +KilledBySignal::KilledBySignal(int signum) : signum_(signum) { +} + +// KilledBySignal function-call operator. +bool KilledBySignal::operator()(int exit_status) const { + return WIFSIGNALED(exit_status) && WTERMSIG(exit_status) == signum_; +} +# endif // !GTEST_OS_WINDOWS + +namespace internal { + +// Utilities needed for death tests. + +// Generates a textual description of a given exit code, in the format +// specified by wait(2). +static std::string ExitSummary(int exit_code) { + Message m; + +# if GTEST_OS_WINDOWS + + m << "Exited with exit status " << exit_code; + +# else + + if (WIFEXITED(exit_code)) { + m << "Exited with exit status " << WEXITSTATUS(exit_code); + } else if (WIFSIGNALED(exit_code)) { + m << "Terminated by signal " << WTERMSIG(exit_code); + } +# ifdef WCOREDUMP + if (WCOREDUMP(exit_code)) { + m << " (core dumped)"; + } +# endif +# endif // GTEST_OS_WINDOWS + + return m.GetString(); +} + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +bool ExitedUnsuccessfully(int exit_status) { + return !ExitedWithCode(0)(exit_status); +} + +# if !GTEST_OS_WINDOWS +// Generates a textual failure message when a death test finds more than +// one thread running, or cannot determine the number of threads, prior +// to executing the given statement. It is the responsibility of the +// caller not to pass a thread_count of 1. +static std::string DeathTestThreadWarning(size_t thread_count) { + Message msg; + msg << "Death tests use fork(), which is unsafe particularly" + << " in a threaded context. For this test, " << GTEST_NAME_ << " "; + if (thread_count == 0) + msg << "couldn't detect the number of threads."; + else + msg << "detected " << thread_count << " threads."; + return msg.GetString(); +} +# endif // !GTEST_OS_WINDOWS + +// Flag characters for reporting a death test that did not die. +static const char kDeathTestLived = 'L'; +static const char kDeathTestReturned = 'R'; +static const char kDeathTestThrew = 'T'; +static const char kDeathTestInternalError = 'I'; + +// An enumeration describing all of the possible ways that a death test can +// conclude. DIED means that the process died while executing the test +// code; LIVED means that process lived beyond the end of the test code; +// RETURNED means that the test statement attempted to execute a return +// statement, which is not allowed; THREW means that the test statement +// returned control by throwing an exception. IN_PROGRESS means the test +// has not yet concluded. +// TODO(vladl@google.com): Unify names and possibly values for +// AbortReason, DeathTestOutcome, and flag characters above. +enum DeathTestOutcome { IN_PROGRESS, DIED, LIVED, RETURNED, THREW }; + +// Routine for aborting the program which is safe to call from an +// exec-style death test child process, in which case the error +// message is propagated back to the parent process. Otherwise, the +// message is simply printed to stderr. In either case, the program +// then exits with status 1. +void DeathTestAbort(const std::string& message) { + // On a POSIX system, this function may be called from a threadsafe-style + // death test child process, which operates on a very small stack. Use + // the heap for any additional non-minuscule memory requirements. + const InternalRunDeathTestFlag* const flag = + GetUnitTestImpl()->internal_run_death_test_flag(); + if (flag != NULL) { + FILE* parent = posix::FDOpen(flag->write_fd(), "w"); + fputc(kDeathTestInternalError, parent); + fprintf(parent, "%s", message.c_str()); + fflush(parent); + _exit(1); + } else { + fprintf(stderr, "%s", message.c_str()); + fflush(stderr); + posix::Abort(); + } +} + +// A replacement for CHECK that calls DeathTestAbort if the assertion +// fails. +# define GTEST_DEATH_TEST_CHECK_(expression) \ + do { \ + if (!::testing::internal::IsTrue(expression)) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// This macro is similar to GTEST_DEATH_TEST_CHECK_, but it is meant for +// evaluating any system call that fulfills two conditions: it must return +// -1 on failure, and set errno to EINTR when it is interrupted and +// should be tried again. The macro expands to a loop that repeatedly +// evaluates the expression as long as it evaluates to -1 and sets +// errno to EINTR. If the expression evaluates to -1 but errno is +// something other than EINTR, DeathTestAbort is called. +# define GTEST_DEATH_TEST_CHECK_SYSCALL_(expression) \ + do { \ + int gtest_retval; \ + do { \ + gtest_retval = (expression); \ + } while (gtest_retval == -1 && errno == EINTR); \ + if (gtest_retval == -1) { \ + DeathTestAbort( \ + ::std::string("CHECK failed: File ") + __FILE__ + ", line " \ + + ::testing::internal::StreamableToString(__LINE__) + ": " \ + + #expression + " != -1"); \ + } \ + } while (::testing::internal::AlwaysFalse()) + +// Returns the message describing the last system error in errno. +std::string GetLastErrnoDescription() { + return errno == 0 ? "" : posix::StrError(errno); +} + +// This is called from a death test parent process to read a failure +// message from the death test child process and log it with the FATAL +// severity. On Windows, the message is read from a pipe handle. On other +// platforms, it is read from a file descriptor. +static void FailFromInternalError(int fd) { + Message error; + char buffer[256]; + int num_read; + + do { + while ((num_read = posix::Read(fd, buffer, 255)) > 0) { + buffer[num_read] = '\0'; + error << buffer; + } + } while (num_read == -1 && errno == EINTR); + + if (num_read == 0) { + GTEST_LOG_(FATAL) << error.GetString(); + } else { + const int last_error = errno; + GTEST_LOG_(FATAL) << "Error while reading death test internal: " + << GetLastErrnoDescription() << " [" << last_error << "]"; + } +} + +// Death test constructor. Increments the running death test count +// for the current test. +DeathTest::DeathTest() { + TestInfo* const info = GetUnitTestImpl()->current_test_info(); + if (info == NULL) { + DeathTestAbort("Cannot run a death test outside of a TEST or " + "TEST_F construct"); + } +} + +// Creates and returns a death test by dispatching to the current +// death test factory. +bool DeathTest::Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) { + return GetUnitTestImpl()->death_test_factory()->Create( + statement, regex, file, line, test); +} + +const char* DeathTest::LastMessage() { + return last_death_test_message_.c_str(); +} + +void DeathTest::set_last_death_test_message(const std::string& message) { + last_death_test_message_ = message; +} + +std::string DeathTest::last_death_test_message_; + +// Provides cross platform implementation for some death functionality. +class DeathTestImpl : public DeathTest { + protected: + DeathTestImpl(const char* a_statement, const RE* a_regex) + : statement_(a_statement), + regex_(a_regex), + spawned_(false), + status_(-1), + outcome_(IN_PROGRESS), + read_fd_(-1), + write_fd_(-1) {} + + // read_fd_ is expected to be closed and cleared by a derived class. + ~DeathTestImpl() { GTEST_DEATH_TEST_CHECK_(read_fd_ == -1); } + + void Abort(AbortReason reason); + virtual bool Passed(bool status_ok); + + const char* statement() const { return statement_; } + const RE* regex() const { return regex_; } + bool spawned() const { return spawned_; } + void set_spawned(bool is_spawned) { spawned_ = is_spawned; } + int status() const { return status_; } + void set_status(int a_status) { status_ = a_status; } + DeathTestOutcome outcome() const { return outcome_; } + void set_outcome(DeathTestOutcome an_outcome) { outcome_ = an_outcome; } + int read_fd() const { return read_fd_; } + void set_read_fd(int fd) { read_fd_ = fd; } + int write_fd() const { return write_fd_; } + void set_write_fd(int fd) { write_fd_ = fd; } + + // Called in the parent process only. Reads the result code of the death + // test child process via a pipe, interprets it to set the outcome_ + // member, and closes read_fd_. Outputs diagnostics and terminates in + // case of unexpected codes. + void ReadAndInterpretStatusByte(); + + private: + // The textual content of the code this object is testing. This class + // doesn't own this string and should not attempt to delete it. + const char* const statement_; + // The regular expression which test output must match. DeathTestImpl + // doesn't own this object and should not attempt to delete it. + const RE* const regex_; + // True if the death test child process has been successfully spawned. + bool spawned_; + // The exit status of the child process. + int status_; + // How the death test concluded. + DeathTestOutcome outcome_; + // Descriptor to the read end of the pipe to the child process. It is + // always -1 in the child process. The child keeps its write end of the + // pipe in write_fd_. + int read_fd_; + // Descriptor to the child's write end of the pipe to the parent process. + // It is always -1 in the parent process. The parent keeps its end of the + // pipe in read_fd_. + int write_fd_; +}; + +// Called in the parent process only. Reads the result code of the death +// test child process via a pipe, interprets it to set the outcome_ +// member, and closes read_fd_. Outputs diagnostics and terminates in +// case of unexpected codes. +void DeathTestImpl::ReadAndInterpretStatusByte() { + char flag; + int bytes_read; + + // The read() here blocks until data is available (signifying the + // failure of the death test) or until the pipe is closed (signifying + // its success), so it's okay to call this in the parent before + // the child process has exited. + do { + bytes_read = posix::Read(read_fd(), &flag, 1); + } while (bytes_read == -1 && errno == EINTR); + + if (bytes_read == 0) { + set_outcome(DIED); + } else if (bytes_read == 1) { + switch (flag) { + case kDeathTestReturned: + set_outcome(RETURNED); + break; + case kDeathTestThrew: + set_outcome(THREW); + break; + case kDeathTestLived: + set_outcome(LIVED); + break; + case kDeathTestInternalError: + FailFromInternalError(read_fd()); // Does not return. + break; + default: + GTEST_LOG_(FATAL) << "Death test child process reported " + << "unexpected status byte (" + << static_cast(flag) << ")"; + } + } else { + GTEST_LOG_(FATAL) << "Read from death test child process failed: " + << GetLastErrnoDescription(); + } + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Close(read_fd())); + set_read_fd(-1); +} + +// Signals that the death test code which should have exited, didn't. +// Should be called only in a death test child process. +// Writes a status byte to the child's status file descriptor, then +// calls _exit(1). +void DeathTestImpl::Abort(AbortReason reason) { + // The parent process considers the death test to be a failure if + // it finds any data in our pipe. So, here we write a single flag byte + // to the pipe, then exit. + const char status_ch = + reason == TEST_DID_NOT_DIE ? kDeathTestLived : + reason == TEST_THREW_EXCEPTION ? kDeathTestThrew : kDeathTestReturned; + + GTEST_DEATH_TEST_CHECK_SYSCALL_(posix::Write(write_fd(), &status_ch, 1)); + // We are leaking the descriptor here because on some platforms (i.e., + // when built as Windows DLL), destructors of global objects will still + // run after calling _exit(). On such systems, write_fd_ will be + // indirectly closed from the destructor of UnitTestImpl, causing double + // close if it is also closed here. On debug configurations, double close + // may assert. As there are no in-process buffers to flush here, we are + // relying on the OS to close the descriptor after the process terminates + // when the destructors are not run. + _exit(1); // Exits w/o any normal exit hooks (we were supposed to crash) +} + +// Returns an indented copy of stderr output for a death test. +// This makes distinguishing death test output lines from regular log lines +// much easier. +static ::std::string FormatDeathTestOutput(const ::std::string& output) { + ::std::string ret; + for (size_t at = 0; ; ) { + const size_t line_end = output.find('\n', at); + ret += "[ DEATH ] "; + if (line_end == ::std::string::npos) { + ret += output.substr(at); + break; + } + ret += output.substr(at, line_end + 1 - at); + at = line_end + 1; + } + return ret; +} + +// Assesses the success or failure of a death test, using both private +// members which have previously been set, and one argument: +// +// Private data members: +// outcome: An enumeration describing how the death test +// concluded: DIED, LIVED, THREW, or RETURNED. The death test +// fails in the latter three cases. +// status: The exit status of the child process. On *nix, it is in the +// in the format specified by wait(2). On Windows, this is the +// value supplied to the ExitProcess() API or a numeric code +// of the exception that terminated the program. +// regex: A regular expression object to be applied to +// the test's captured standard error output; the death test +// fails if it does not match. +// +// Argument: +// status_ok: true if exit_status is acceptable in the context of +// this particular death test, which fails if it is false +// +// Returns true iff all of the above conditions are met. Otherwise, the +// first failing condition, in the order given above, is the one that is +// reported. Also sets the last death test message string. +bool DeathTestImpl::Passed(bool status_ok) { + if (!spawned()) + return false; + + const std::string error_message = GetCapturedStderr(); + + bool success = false; + Message buffer; + + buffer << "Death test: " << statement() << "\n"; + switch (outcome()) { + case LIVED: + buffer << " Result: failed to die.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case THREW: + buffer << " Result: threw an exception.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case RETURNED: + buffer << " Result: illegal return in test statement.\n" + << " Error msg:\n" << FormatDeathTestOutput(error_message); + break; + case DIED: + if (status_ok) { + const bool matched = RE::PartialMatch(error_message.c_str(), *regex()); + if (matched) { + success = true; + } else { + buffer << " Result: died but not with expected error.\n" + << " Expected: " << regex()->pattern() << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + } else { + buffer << " Result: died but not with expected exit code:\n" + << " " << ExitSummary(status()) << "\n" + << "Actual msg:\n" << FormatDeathTestOutput(error_message); + } + break; + case IN_PROGRESS: + default: + GTEST_LOG_(FATAL) + << "DeathTest::Passed somehow called before conclusion of test"; + } + + DeathTest::set_last_death_test_message(buffer.GetString()); + return success; +} + +# if GTEST_OS_WINDOWS +// WindowsDeathTest implements death tests on Windows. Due to the +// specifics of starting new processes on Windows, death tests there are +// always threadsafe, and Google Test considers the +// --gtest_death_test_style=fast setting to be equivalent to +// --gtest_death_test_style=threadsafe there. +// +// A few implementation notes: Like the Linux version, the Windows +// implementation uses pipes for child-to-parent communication. But due to +// the specifics of pipes on Windows, some extra steps are required: +// +// 1. The parent creates a communication pipe and stores handles to both +// ends of it. +// 2. The parent starts the child and provides it with the information +// necessary to acquire the handle to the write end of the pipe. +// 3. The child acquires the write end of the pipe and signals the parent +// using a Windows event. +// 4. Now the parent can release the write end of the pipe on its side. If +// this is done before step 3, the object's reference count goes down to +// 0 and it is destroyed, preventing the child from acquiring it. The +// parent now has to release it, or read operations on the read end of +// the pipe will not return when the child terminates. +// 5. The parent reads child's output through the pipe (outcome code and +// any possible error messages) from the pipe, and its stderr and then +// determines whether to fail the test. +// +// Note: to distinguish Win32 API calls from the local method and function +// calls, the former are explicitly resolved in the global namespace. +// +class WindowsDeathTest : public DeathTestImpl { + public: + WindowsDeathTest(const char* a_statement, + const RE* a_regex, + const char* file, + int line) + : DeathTestImpl(a_statement, a_regex), file_(file), line_(line) {} + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + virtual TestRole AssumeRole(); + + private: + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; + // Handle to the write end of the pipe to the child process. + AutoHandle write_handle_; + // Child process handle. + AutoHandle child_handle_; + // Event the child process uses to signal the parent that it has + // acquired the handle to the write end of the pipe. After seeing this + // event the parent can release its own handles to make sure its + // ReadFile() calls return when the child terminates. + AutoHandle event_handle_; +}; + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int WindowsDeathTest::Wait() { + if (!spawned()) + return 0; + + // Wait until the child either signals that it has acquired the write end + // of the pipe or it dies. + const HANDLE wait_handles[2] = { child_handle_.Get(), event_handle_.Get() }; + switch (::WaitForMultipleObjects(2, + wait_handles, + FALSE, // Waits for any of the handles. + INFINITE)) { + case WAIT_OBJECT_0: + case WAIT_OBJECT_0 + 1: + break; + default: + GTEST_DEATH_TEST_CHECK_(false); // Should not get here. + } + + // The child has acquired the write end of the pipe or exited. + // We release the handle on our side and continue. + write_handle_.Reset(); + event_handle_.Reset(); + + ReadAndInterpretStatusByte(); + + // Waits for the child process to exit if it haven't already. This + // returns immediately if the child has already exited, regardless of + // whether previous calls to WaitForMultipleObjects synchronized on this + // handle or not. + GTEST_DEATH_TEST_CHECK_( + WAIT_OBJECT_0 == ::WaitForSingleObject(child_handle_.Get(), + INFINITE)); + DWORD status_code; + GTEST_DEATH_TEST_CHECK_( + ::GetExitCodeProcess(child_handle_.Get(), &status_code) != FALSE); + child_handle_.Reset(); + set_status(static_cast(status_code)); + return status(); +} + +// The AssumeRole process for a Windows death test. It creates a child +// process with the same executable as the current process to run the +// death test. The child process is given the --gtest_filter and +// --gtest_internal_run_death_test flags such that it knows to run the +// current death test only. +DeathTest::TestRole WindowsDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + // ParseInternalRunDeathTestFlag() has performed all the necessary + // processing. + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + // WindowsDeathTest uses an anonymous pipe to communicate results of + // a death test. + SECURITY_ATTRIBUTES handles_are_inheritable = { + sizeof(SECURITY_ATTRIBUTES), NULL, TRUE }; + HANDLE read_handle, write_handle; + GTEST_DEATH_TEST_CHECK_( + ::CreatePipe(&read_handle, &write_handle, &handles_are_inheritable, + 0) // Default buffer size. + != FALSE); + set_read_fd(::_open_osfhandle(reinterpret_cast(read_handle), + O_RDONLY)); + write_handle_.Reset(write_handle); + event_handle_.Reset(::CreateEvent( + &handles_are_inheritable, + TRUE, // The event will automatically reset to non-signaled state. + FALSE, // The initial state is non-signalled. + NULL)); // The even is unnamed. + GTEST_DEATH_TEST_CHECK_(event_handle_.Get() != NULL); + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + + "=" + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(static_cast(::GetCurrentProcessId())) + + // size_t has the same width as pointers on both 32-bit and 64-bit + // Windows platforms. + // See http://msdn.microsoft.com/en-us/library/tcxf1dw6.aspx. + "|" + StreamableToString(reinterpret_cast(write_handle)) + + "|" + StreamableToString(reinterpret_cast(event_handle_.Get())); + + char executable_path[_MAX_PATH + 1]; // NOLINT + GTEST_DEATH_TEST_CHECK_( + _MAX_PATH + 1 != ::GetModuleFileNameA(NULL, + executable_path, + _MAX_PATH)); + + std::string command_line = + std::string(::GetCommandLineA()) + " " + filter_flag + " \"" + + internal_flag + "\""; + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // Flush the log buffers since the log streams are shared with the child. + FlushInfoLog(); + + // The child process will share the standard handles with the parent. + STARTUPINFOA startup_info; + memset(&startup_info, 0, sizeof(STARTUPINFO)); + startup_info.dwFlags = STARTF_USESTDHANDLES; + startup_info.hStdInput = ::GetStdHandle(STD_INPUT_HANDLE); + startup_info.hStdOutput = ::GetStdHandle(STD_OUTPUT_HANDLE); + startup_info.hStdError = ::GetStdHandle(STD_ERROR_HANDLE); + + PROCESS_INFORMATION process_info; + GTEST_DEATH_TEST_CHECK_(::CreateProcessA( + executable_path, + const_cast(command_line.c_str()), + NULL, // Retuned process handle is not inheritable. + NULL, // Retuned thread handle is not inheritable. + TRUE, // Child inherits all inheritable handles (for write_handle_). + 0x0, // Default creation flags. + NULL, // Inherit the parent's environment. + UnitTest::GetInstance()->original_working_dir(), + &startup_info, + &process_info) != FALSE); + child_handle_.Reset(process_info.hProcess); + ::CloseHandle(process_info.hThread); + set_spawned(true); + return OVERSEE_TEST; +} +# else // We are not on Windows. + +// ForkingDeathTest provides implementations for most of the abstract +// methods of the DeathTest interface. Only the AssumeRole method is +// left undefined. +class ForkingDeathTest : public DeathTestImpl { + public: + ForkingDeathTest(const char* statement, const RE* regex); + + // All of these virtual functions are inherited from DeathTest. + virtual int Wait(); + + protected: + void set_child_pid(pid_t child_pid) { child_pid_ = child_pid; } + + private: + // PID of child process during death test; 0 in the child process itself. + pid_t child_pid_; +}; + +// Constructs a ForkingDeathTest. +ForkingDeathTest::ForkingDeathTest(const char* a_statement, const RE* a_regex) + : DeathTestImpl(a_statement, a_regex), + child_pid_(-1) {} + +// Waits for the child in a death test to exit, returning its exit +// status, or 0 if no child process exists. As a side effect, sets the +// outcome data member. +int ForkingDeathTest::Wait() { + if (!spawned()) + return 0; + + ReadAndInterpretStatusByte(); + + int status_value; + GTEST_DEATH_TEST_CHECK_SYSCALL_(waitpid(child_pid_, &status_value, 0)); + set_status(status_value); + return status_value; +} + +// A concrete death test class that forks, then immediately runs the test +// in the child process. +class NoExecDeathTest : public ForkingDeathTest { + public: + NoExecDeathTest(const char* a_statement, const RE* a_regex) : + ForkingDeathTest(a_statement, a_regex) { } + virtual TestRole AssumeRole(); +}; + +// The AssumeRole process for a fork-and-run death test. It implements a +// straightforward fork, with a simple pipe to transmit the status byte. +DeathTest::TestRole NoExecDeathTest::AssumeRole() { + const size_t thread_count = GetThreadCount(); + if (thread_count != 1) { + GTEST_LOG_(WARNING) << DeathTestThreadWarning(thread_count); + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + + DeathTest::set_last_death_test_message(""); + CaptureStderr(); + // When we fork the process below, the log file buffers are copied, but the + // file descriptors are shared. We flush all log files here so that closing + // the file descriptors in the child process doesn't throw off the + // synchronization between descriptors and buffers in the parent process. + // This is as close to the fork as possible to avoid a race condition in case + // there are multiple threads running before the death test, and another + // thread writes to the log file. + FlushInfoLog(); + + const pid_t child_pid = fork(); + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + set_child_pid(child_pid); + if (child_pid == 0) { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[0])); + set_write_fd(pipe_fd[1]); + // Redirects all logging to stderr in the child process to prevent + // concurrent writes to the log files. We capture stderr in the parent + // process and append the child process' output to a log. + LogToStderr(); + // Event forwarding to the listeners of event listener API mush be shut + // down in death test subprocesses. + GetUnitTestImpl()->listeners()->SuppressEventForwarding(); + g_in_fast_death_test_child = true; + return EXECUTE_TEST; + } else { + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; + } +} + +// A concrete death test class that forks and re-executes the main +// program from the beginning, with command-line flags set that cause +// only this specific death test to be run. +class ExecDeathTest : public ForkingDeathTest { + public: + ExecDeathTest(const char* a_statement, const RE* a_regex, + const char* file, int line) : + ForkingDeathTest(a_statement, a_regex), file_(file), line_(line) { } + virtual TestRole AssumeRole(); + private: + static ::std::vector + GetArgvsForDeathTestChildProcess() { + ::std::vector args = GetInjectableArgvs(); + return args; + } + // The name of the file in which the death test is located. + const char* const file_; + // The line number on which the death test is located. + const int line_; +}; + +// Utility class for accumulating command-line arguments. +class Arguments { + public: + Arguments() { + args_.push_back(NULL); + } + + ~Arguments() { + for (std::vector::iterator i = args_.begin(); i != args_.end(); + ++i) { + free(*i); + } + } + void AddArgument(const char* argument) { + args_.insert(args_.end() - 1, posix::StrDup(argument)); + } + + template + void AddArguments(const ::std::vector& arguments) { + for (typename ::std::vector::const_iterator i = arguments.begin(); + i != arguments.end(); + ++i) { + args_.insert(args_.end() - 1, posix::StrDup(i->c_str())); + } + } + char* const* Argv() { + return &args_[0]; + } + + private: + std::vector args_; +}; + +// A struct that encompasses the arguments to the child process of a +// threadsafe-style death test process. +struct ExecDeathTestArgs { + char* const* argv; // Command-line arguments for the child's call to exec + int close_fd; // File descriptor to close; the read end of a pipe +}; + +# if GTEST_OS_MAC +inline char** GetEnviron() { + // When Google Test is built as a framework on MacOS X, the environ variable + // is unavailable. Apple's documentation (man environ) recommends using + // _NSGetEnviron() instead. + return *_NSGetEnviron(); +} +# else +// Some POSIX platforms expect you to declare environ. extern "C" makes +// it reside in the global namespace. +extern "C" char** environ; +inline char** GetEnviron() { return environ; } +# endif // GTEST_OS_MAC + +# if !GTEST_OS_QNX +// The main function for a threadsafe-style death test child process. +// This function is called in a clone()-ed process and thus must avoid +// any potentially unsafe operations like malloc or libc functions. +static int ExecDeathTestChildMain(void* child_arg) { + ExecDeathTestArgs* const args = static_cast(child_arg); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(args->close_fd)); + + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + // We can safely call execve() as it's a direct system call. We + // cannot use execvp() as it's a libc function and thus potentially + // unsafe. Since execve() doesn't search the PATH, the user must + // invoke the test program via a valid path that contains at least + // one path separator. + execve(args->argv[0], args->argv, GetEnviron()); + DeathTestAbort(std::string("execve(") + args->argv[0] + ", ...) in " + + original_dir + " failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; +} +# endif // !GTEST_OS_QNX + +// Two utility routines that together determine the direction the stack +// grows. +// This could be accomplished more elegantly by a single recursive +// function, but we want to guard against the unlikely possibility of +// a smart compiler optimizing the recursion away. +// +// GTEST_NO_INLINE_ is required to prevent GCC 4.6 from inlining +// StackLowerThanAddress into StackGrowsDown, which then doesn't give +// correct answer. +void StackLowerThanAddress(const void* ptr, bool* result) GTEST_NO_INLINE_; +void StackLowerThanAddress(const void* ptr, bool* result) { + int dummy; + *result = (&dummy < ptr); +} + +bool StackGrowsDown() { + int dummy; + bool result; + StackLowerThanAddress(&dummy, &result); + return result; +} + +// Spawns a child process with the same executable as the current process in +// a thread-safe manner and instructs it to run the death test. The +// implementation uses fork(2) + exec. On systems where clone(2) is +// available, it is used instead, being slightly more thread-safe. On QNX, +// fork supports only single-threaded environments, so this function uses +// spawn(2) there instead. The function dies with an error message if +// anything goes wrong. +static pid_t ExecDeathTestSpawnChild(char* const* argv, int close_fd) { + ExecDeathTestArgs args = { argv, close_fd }; + pid_t child_pid = -1; + +# if GTEST_OS_QNX + // Obtains the current directory and sets it to be closed in the child + // process. + const int cwd_fd = open(".", O_RDONLY); + GTEST_DEATH_TEST_CHECK_(cwd_fd != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(cwd_fd, F_SETFD, FD_CLOEXEC)); + // We need to execute the test program in the same environment where + // it was originally invoked. Therefore we change to the original + // working directory first. + const char* const original_dir = + UnitTest::GetInstance()->original_working_dir(); + // We can safely call chdir() as it's a direct system call. + if (chdir(original_dir) != 0) { + DeathTestAbort(std::string("chdir(\"") + original_dir + "\") failed: " + + GetLastErrnoDescription()); + return EXIT_FAILURE; + } + + int fd_flags; + // Set close_fd to be closed after spawn. + GTEST_DEATH_TEST_CHECK_SYSCALL_(fd_flags = fcntl(close_fd, F_GETFD)); + GTEST_DEATH_TEST_CHECK_SYSCALL_(fcntl(close_fd, F_SETFD, + fd_flags | FD_CLOEXEC)); + struct inheritance inherit = {0}; + // spawn is a system call. + child_pid = spawn(args.argv[0], 0, NULL, &inherit, args.argv, GetEnviron()); + // Restores the current working directory. + GTEST_DEATH_TEST_CHECK_(fchdir(cwd_fd) != -1); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(cwd_fd)); + +# else // GTEST_OS_QNX +# if GTEST_OS_LINUX + // When a SIGPROF signal is received while fork() or clone() are executing, + // the process may hang. To avoid this, we ignore SIGPROF here and re-enable + // it after the call to fork()/clone() is complete. + struct sigaction saved_sigprof_action; + struct sigaction ignore_sigprof_action; + memset(&ignore_sigprof_action, 0, sizeof(ignore_sigprof_action)); + sigemptyset(&ignore_sigprof_action.sa_mask); + ignore_sigprof_action.sa_handler = SIG_IGN; + GTEST_DEATH_TEST_CHECK_SYSCALL_(sigaction( + SIGPROF, &ignore_sigprof_action, &saved_sigprof_action)); +# endif // GTEST_OS_LINUX + +# if GTEST_HAS_CLONE + const bool use_fork = GTEST_FLAG(death_test_use_fork); + + if (!use_fork) { + static const bool stack_grows_down = StackGrowsDown(); + const size_t stack_size = getpagesize(); + // MMAP_ANONYMOUS is not defined on Mac, so we use MAP_ANON instead. + void* const stack = mmap(NULL, stack_size, PROT_READ | PROT_WRITE, + MAP_ANON | MAP_PRIVATE, -1, 0); + GTEST_DEATH_TEST_CHECK_(stack != MAP_FAILED); + + // Maximum stack alignment in bytes: For a downward-growing stack, this + // amount is subtracted from size of the stack space to get an address + // that is within the stack space and is aligned on all systems we care + // about. As far as I know there is no ABI with stack alignment greater + // than 64. We assume stack and stack_size already have alignment of + // kMaxStackAlignment. + const size_t kMaxStackAlignment = 64; + void* const stack_top = + static_cast(stack) + + (stack_grows_down ? stack_size - kMaxStackAlignment : 0); + GTEST_DEATH_TEST_CHECK_(stack_size > kMaxStackAlignment && + reinterpret_cast(stack_top) % kMaxStackAlignment == 0); + + child_pid = clone(&ExecDeathTestChildMain, stack_top, SIGCHLD, &args); + + GTEST_DEATH_TEST_CHECK_(munmap(stack, stack_size) != -1); + } +# else + const bool use_fork = true; +# endif // GTEST_HAS_CLONE + + if (use_fork && (child_pid = fork()) == 0) { + ExecDeathTestChildMain(&args); + _exit(0); + } +# endif // GTEST_OS_QNX +# if GTEST_OS_LINUX + GTEST_DEATH_TEST_CHECK_SYSCALL_( + sigaction(SIGPROF, &saved_sigprof_action, NULL)); +# endif // GTEST_OS_LINUX + + GTEST_DEATH_TEST_CHECK_(child_pid != -1); + return child_pid; +} + +// The AssumeRole process for a fork-and-exec death test. It re-executes the +// main program from the beginning, setting the --gtest_filter +// and --gtest_internal_run_death_test flags to cause only the current +// death test to be re-run. +DeathTest::TestRole ExecDeathTest::AssumeRole() { + const UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const TestInfo* const info = impl->current_test_info(); + const int death_test_index = info->result()->death_test_count(); + + if (flag != NULL) { + set_write_fd(flag->write_fd()); + return EXECUTE_TEST; + } + + int pipe_fd[2]; + GTEST_DEATH_TEST_CHECK_(pipe(pipe_fd) != -1); + // Clear the close-on-exec flag on the write end of the pipe, lest + // it be closed when the child process does an exec: + GTEST_DEATH_TEST_CHECK_(fcntl(pipe_fd[1], F_SETFD, 0) != -1); + + const std::string filter_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kFilterFlag + "=" + + info->test_case_name() + "." + info->name(); + const std::string internal_flag = + std::string("--") + GTEST_FLAG_PREFIX_ + kInternalRunDeathTestFlag + "=" + + file_ + "|" + StreamableToString(line_) + "|" + + StreamableToString(death_test_index) + "|" + + StreamableToString(pipe_fd[1]); + Arguments args; + args.AddArguments(GetArgvsForDeathTestChildProcess()); + args.AddArgument(filter_flag.c_str()); + args.AddArgument(internal_flag.c_str()); + + DeathTest::set_last_death_test_message(""); + + CaptureStderr(); + // See the comment in NoExecDeathTest::AssumeRole for why the next line + // is necessary. + FlushInfoLog(); + + const pid_t child_pid = ExecDeathTestSpawnChild(args.Argv(), pipe_fd[0]); + GTEST_DEATH_TEST_CHECK_SYSCALL_(close(pipe_fd[1])); + set_child_pid(child_pid); + set_read_fd(pipe_fd[0]); + set_spawned(true); + return OVERSEE_TEST; +} + +# endif // !GTEST_OS_WINDOWS + +// Creates a concrete DeathTest-derived class that depends on the +// --gtest_death_test_style flag, and sets the pointer pointed to +// by the "test" argument to its address. If the test should be +// skipped, sets that pointer to NULL. Returns true, unless the +// flag is set to an invalid value. +bool DefaultDeathTestFactory::Create(const char* statement, const RE* regex, + const char* file, int line, + DeathTest** test) { + UnitTestImpl* const impl = GetUnitTestImpl(); + const InternalRunDeathTestFlag* const flag = + impl->internal_run_death_test_flag(); + const int death_test_index = impl->current_test_info() + ->increment_death_test_count(); + + if (flag != NULL) { + if (death_test_index > flag->index()) { + DeathTest::set_last_death_test_message( + "Death test count (" + StreamableToString(death_test_index) + + ") somehow exceeded expected maximum (" + + StreamableToString(flag->index()) + ")"); + return false; + } + + if (!(flag->file() == file && flag->line() == line && + flag->index() == death_test_index)) { + *test = NULL; + return true; + } + } + +# if GTEST_OS_WINDOWS + + if (GTEST_FLAG(death_test_style) == "threadsafe" || + GTEST_FLAG(death_test_style) == "fast") { + *test = new WindowsDeathTest(statement, regex, file, line); + } + +# else + + if (GTEST_FLAG(death_test_style) == "threadsafe") { + *test = new ExecDeathTest(statement, regex, file, line); + } else if (GTEST_FLAG(death_test_style) == "fast") { + *test = new NoExecDeathTest(statement, regex); + } + +# endif // GTEST_OS_WINDOWS + + else { // NOLINT - this is more readable than unbalanced brackets inside #if. + DeathTest::set_last_death_test_message( + "Unknown death test style \"" + GTEST_FLAG(death_test_style) + + "\" encountered"); + return false; + } + + return true; +} + +// Splits a given string on a given delimiter, populating a given +// vector with the fields. GTEST_HAS_DEATH_TEST implies that we have +// ::std::string, so we can use it here. +static void SplitString(const ::std::string& str, char delimiter, + ::std::vector< ::std::string>* dest) { + ::std::vector< ::std::string> parsed; + ::std::string::size_type pos = 0; + while (::testing::internal::AlwaysTrue()) { + const ::std::string::size_type colon = str.find(delimiter, pos); + if (colon == ::std::string::npos) { + parsed.push_back(str.substr(pos)); + break; + } else { + parsed.push_back(str.substr(pos, colon - pos)); + pos = colon + 1; + } + } + dest->swap(parsed); +} + +# if GTEST_OS_WINDOWS +// Recreates the pipe and event handles from the provided parameters, +// signals the event, and returns a file descriptor wrapped around the pipe +// handle. This function is called in the child process only. +int GetStatusFileDescriptor(unsigned int parent_process_id, + size_t write_handle_as_size_t, + size_t event_handle_as_size_t) { + AutoHandle parent_process_handle(::OpenProcess(PROCESS_DUP_HANDLE, + FALSE, // Non-inheritable. + parent_process_id)); + if (parent_process_handle.Get() == INVALID_HANDLE_VALUE) { + DeathTestAbort("Unable to open parent process " + + StreamableToString(parent_process_id)); + } + + // TODO(vladl@google.com): Replace the following check with a + // compile-time assertion when available. + GTEST_CHECK_(sizeof(HANDLE) <= sizeof(size_t)); + + const HANDLE write_handle = + reinterpret_cast(write_handle_as_size_t); + HANDLE dup_write_handle; + + // The newly initialized handle is accessible only in in the parent + // process. To obtain one accessible within the child, we need to use + // DuplicateHandle. + if (!::DuplicateHandle(parent_process_handle.Get(), write_handle, + ::GetCurrentProcess(), &dup_write_handle, + 0x0, // Requested privileges ignored since + // DUPLICATE_SAME_ACCESS is used. + FALSE, // Request non-inheritable handler. + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the pipe handle " + + StreamableToString(write_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const HANDLE event_handle = reinterpret_cast(event_handle_as_size_t); + HANDLE dup_event_handle; + + if (!::DuplicateHandle(parent_process_handle.Get(), event_handle, + ::GetCurrentProcess(), &dup_event_handle, + 0x0, + FALSE, + DUPLICATE_SAME_ACCESS)) { + DeathTestAbort("Unable to duplicate the event handle " + + StreamableToString(event_handle_as_size_t) + + " from the parent process " + + StreamableToString(parent_process_id)); + } + + const int write_fd = + ::_open_osfhandle(reinterpret_cast(dup_write_handle), O_APPEND); + if (write_fd == -1) { + DeathTestAbort("Unable to convert pipe handle " + + StreamableToString(write_handle_as_size_t) + + " to a file descriptor"); + } + + // Signals the parent that the write end of the pipe has been acquired + // so the parent can release its own write end. + ::SetEvent(dup_event_handle); + + return write_fd; +} +# endif // GTEST_OS_WINDOWS + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag() { + if (GTEST_FLAG(internal_run_death_test) == "") return NULL; + + // GTEST_HAS_DEATH_TEST implies that we have ::std::string, so we + // can use it here. + int line = -1; + int index = -1; + ::std::vector< ::std::string> fields; + SplitString(GTEST_FLAG(internal_run_death_test).c_str(), '|', &fields); + int write_fd = -1; + +# if GTEST_OS_WINDOWS + + unsigned int parent_process_id = 0; + size_t write_handle_as_size_t = 0; + size_t event_handle_as_size_t = 0; + + if (fields.size() != 6 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &parent_process_id) + || !ParseNaturalNumber(fields[4], &write_handle_as_size_t) + || !ParseNaturalNumber(fields[5], &event_handle_as_size_t)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + write_fd = GetStatusFileDescriptor(parent_process_id, + write_handle_as_size_t, + event_handle_as_size_t); +# else + + if (fields.size() != 4 + || !ParseNaturalNumber(fields[1], &line) + || !ParseNaturalNumber(fields[2], &index) + || !ParseNaturalNumber(fields[3], &write_fd)) { + DeathTestAbort("Bad --gtest_internal_run_death_test flag: " + + GTEST_FLAG(internal_run_death_test)); + } + +# endif // GTEST_OS_WINDOWS + + return new InternalRunDeathTestFlag(fields[0], line, index, write_fd); +} + +} // namespace internal + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: keith.ray@gmail.com (Keith Ray) + + +#include + +#if GTEST_OS_WINDOWS_MOBILE +# include +#elif GTEST_OS_WINDOWS +# include +# include +#elif GTEST_OS_SYMBIAN +// Symbian OpenC has PATH_MAX in sys/syslimits.h +# include +#else +# include +# include // Some Linux distributions define PATH_MAX here. +#endif // GTEST_OS_WINDOWS_MOBILE + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_MAX_ _MAX_PATH +#elif defined(PATH_MAX) +# define GTEST_PATH_MAX_ PATH_MAX +#elif defined(_XOPEN_PATH_MAX) +# define GTEST_PATH_MAX_ _XOPEN_PATH_MAX +#else +# define GTEST_PATH_MAX_ _POSIX_PATH_MAX +#endif // GTEST_OS_WINDOWS + + +namespace testing { +namespace internal { + +#if GTEST_OS_WINDOWS +// On Windows, '\\' is the standard path separator, but many tools and the +// Windows API also accept '/' as an alternate path separator. Unless otherwise +// noted, a file path can contain either kind of path separators, or a mixture +// of them. +const char kPathSeparator = '\\'; +const char kAlternatePathSeparator = '/'; +const char kPathSeparatorString[] = "\\"; +const char kAlternatePathSeparatorString[] = "/"; +# if GTEST_OS_WINDOWS_MOBILE +// Windows CE doesn't have a current directory. You should not use +// the current directory in tests on Windows CE, but this at least +// provides a reasonable fallback. +const char kCurrentDirectoryString[] = "\\"; +// Windows CE doesn't define INVALID_FILE_ATTRIBUTES +const DWORD kInvalidFileAttributes = 0xffffffff; +# else +const char kCurrentDirectoryString[] = ".\\"; +# endif // GTEST_OS_WINDOWS_MOBILE +#else +const char kPathSeparator = '/'; +const char kPathSeparatorString[] = "/"; +const char kCurrentDirectoryString[] = "./"; +#endif // GTEST_OS_WINDOWS + +// Returns whether the given character is a valid path separator. +static bool IsPathSeparator(char c) { +#if GTEST_HAS_ALT_PATH_SEP_ + return (c == kPathSeparator) || (c == kAlternatePathSeparator); +#else + return c == kPathSeparator; +#endif +} + +// Returns the current working directory, or "" if unsuccessful. +FilePath FilePath::GetCurrentDir() { +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE doesn't have a current directory, so we just return + // something reasonable. + return FilePath(kCurrentDirectoryString); +#elif GTEST_OS_WINDOWS + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(_getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); +#else + char cwd[GTEST_PATH_MAX_ + 1] = { '\0' }; + return FilePath(getcwd(cwd, sizeof(cwd)) == NULL ? "" : cwd); +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns a copy of the FilePath with the case-insensitive extension removed. +// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns +// FilePath("dir/file"). If a case-insensitive extension is not +// found, returns a copy of the original FilePath. +FilePath FilePath::RemoveExtension(const char* extension) const { + const std::string dot_extension = std::string(".") + extension; + if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) { + return FilePath(pathname_.substr( + 0, pathname_.length() - dot_extension.length())); + } + return *this; +} + +// Returns a pointer to the last occurence of a valid path separator in +// the FilePath. On Windows, for example, both '/' and '\' are valid path +// separators. Returns NULL if no path separator was found. +const char* FilePath::FindLastPathSeparator() const { + const char* const last_sep = strrchr(c_str(), kPathSeparator); +#if GTEST_HAS_ALT_PATH_SEP_ + const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator); + // Comparing two pointers of which only one is NULL is undefined. + if (last_alt_sep != NULL && + (last_sep == NULL || last_alt_sep > last_sep)) { + return last_alt_sep; + } +#endif + return last_sep; +} + +// Returns a copy of the FilePath with the directory part removed. +// Example: FilePath("path/to/file").RemoveDirectoryName() returns +// FilePath("file"). If there is no directory part ("just_a_file"), it returns +// the FilePath unmodified. If there is no file part ("just_a_dir/") it +// returns an empty FilePath (""). +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveDirectoryName() const { + const char* const last_sep = FindLastPathSeparator(); + return last_sep ? FilePath(last_sep + 1) : *this; +} + +// RemoveFileName returns the directory path with the filename removed. +// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". +// If the FilePath is "a_file" or "/a_file", RemoveFileName returns +// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does +// not have a file, like "just/a/dir/", it returns the FilePath unmodified. +// On Windows platform, '\' is the path separator, otherwise it is '/'. +FilePath FilePath::RemoveFileName() const { + const char* const last_sep = FindLastPathSeparator(); + std::string dir; + if (last_sep) { + dir = std::string(c_str(), last_sep + 1 - c_str()); + } else { + dir = kCurrentDirectoryString; + } + return FilePath(dir); +} + +// Helper functions for naming files in a directory for xml output. + +// Given directory = "dir", base_name = "test", number = 0, +// extension = "xml", returns "dir/test.xml". If number is greater +// than zero (e.g., 12), returns "dir/test_12.xml". +// On Windows platform, uses \ as the separator rather than /. +FilePath FilePath::MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension) { + std::string file; + if (number == 0) { + file = base_name.string() + "." + extension; + } else { + file = base_name.string() + "_" + StreamableToString(number) + + "." + extension; + } + return ConcatPaths(directory, FilePath(file)); +} + +// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml". +// On Windows, uses \ as the separator rather than /. +FilePath FilePath::ConcatPaths(const FilePath& directory, + const FilePath& relative_path) { + if (directory.IsEmpty()) + return relative_path; + const FilePath dir(directory.RemoveTrailingPathSeparator()); + return FilePath(dir.string() + kPathSeparator + relative_path.string()); +} + +// Returns true if pathname describes something findable in the file-system, +// either a file, directory, or whatever. +bool FilePath::FileOrDirectoryExists() const { +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + return attributes != kInvalidFileAttributes; +#else + posix::StatStruct file_stat; + return posix::Stat(pathname_.c_str(), &file_stat) == 0; +#endif // GTEST_OS_WINDOWS_MOBILE +} + +// Returns true if pathname describes a directory in the file-system +// that exists. +bool FilePath::DirectoryExists() const { + bool result = false; +#if GTEST_OS_WINDOWS + // Don't strip off trailing separator if path is a root directory on + // Windows (like "C:\\"). + const FilePath& path(IsRootDirectory() ? *this : + RemoveTrailingPathSeparator()); +#else + const FilePath& path(*this); +#endif + +#if GTEST_OS_WINDOWS_MOBILE + LPCWSTR unicode = String::AnsiToUtf16(path.c_str()); + const DWORD attributes = GetFileAttributes(unicode); + delete [] unicode; + if ((attributes != kInvalidFileAttributes) && + (attributes & FILE_ATTRIBUTE_DIRECTORY)) { + result = true; + } +#else + posix::StatStruct file_stat; + result = posix::Stat(path.c_str(), &file_stat) == 0 && + posix::IsDir(file_stat); +#endif // GTEST_OS_WINDOWS_MOBILE + + return result; +} + +// Returns true if pathname describes a root directory. (Windows has one +// root directory per disk drive.) +bool FilePath::IsRootDirectory() const { +#if GTEST_OS_WINDOWS + // TODO(wan@google.com): on Windows a network share like + // \\server\share can be a root directory, although it cannot be the + // current directory. Handle this properly. + return pathname_.length() == 3 && IsAbsolutePath(); +#else + return pathname_.length() == 1 && IsPathSeparator(pathname_.c_str()[0]); +#endif +} + +// Returns true if pathname describes an absolute path. +bool FilePath::IsAbsolutePath() const { + const char* const name = pathname_.c_str(); +#if GTEST_OS_WINDOWS + return pathname_.length() >= 3 && + ((name[0] >= 'a' && name[0] <= 'z') || + (name[0] >= 'A' && name[0] <= 'Z')) && + name[1] == ':' && + IsPathSeparator(name[2]); +#else + return IsPathSeparator(name[0]); +#endif +} + +// Returns a pathname for a file that does not currently exist. The pathname +// will be directory/base_name.extension or +// directory/base_name_.extension if directory/base_name.extension +// already exists. The number will be incremented until a pathname is found +// that does not already exist. +// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. +// There could be a race condition if two or more processes are calling this +// function at the same time -- they could both pick the same filename. +FilePath FilePath::GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension) { + FilePath full_pathname; + int number = 0; + do { + full_pathname.Set(MakeFileName(directory, base_name, number++, extension)); + } while (full_pathname.FileOrDirectoryExists()); + return full_pathname; +} + +// Returns true if FilePath ends with a path separator, which indicates that +// it is intended to represent a directory. Returns false otherwise. +// This does NOT check that a directory (or file) actually exists. +bool FilePath::IsDirectory() const { + return !pathname_.empty() && + IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]); +} + +// Create directories so that path exists. Returns true if successful or if +// the directories already exist; returns false if unable to create directories +// for any reason. +bool FilePath::CreateDirectoriesRecursively() const { + if (!this->IsDirectory()) { + return false; + } + + if (pathname_.length() == 0 || this->DirectoryExists()) { + return true; + } + + const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName()); + return parent.CreateDirectoriesRecursively() && this->CreateFolder(); +} + +// Create the directory so that path exists. Returns true if successful or +// if the directory already exists; returns false if unable to create the +// directory for any reason, including if the parent directory does not +// exist. Not named "CreateDirectory" because that's a macro on Windows. +bool FilePath::CreateFolder() const { +#if GTEST_OS_WINDOWS_MOBILE + FilePath removed_sep(this->RemoveTrailingPathSeparator()); + LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str()); + int result = CreateDirectory(unicode, NULL) ? 0 : -1; + delete [] unicode; +#elif GTEST_OS_WINDOWS + int result = _mkdir(pathname_.c_str()); +#else + int result = mkdir(pathname_.c_str(), 0777); +#endif // GTEST_OS_WINDOWS_MOBILE + + if (result == -1) { + return this->DirectoryExists(); // An error is OK if the directory exists. + } + return true; // No error. +} + +// If input name has a trailing separator character, remove it and return the +// name, otherwise return the name string unmodified. +// On Windows platform, uses \ as the separator, other platforms use /. +FilePath FilePath::RemoveTrailingPathSeparator() const { + return IsDirectory() + ? FilePath(pathname_.substr(0, pathname_.length() - 1)) + : *this; +} + +// Removes any redundant separators that might be in the pathname. +// For example, "bar///foo" becomes "bar/foo". Does not eliminate other +// redundancies that might be in a pathname involving "." or "..". +// TODO(wan@google.com): handle Windows network shares (e.g. \\server\share). +void FilePath::Normalize() { + if (pathname_.c_str() == NULL) { + pathname_ = ""; + return; + } + const char* src = pathname_.c_str(); + char* const dest = new char[pathname_.length() + 1]; + char* dest_ptr = dest; + memset(dest_ptr, 0, pathname_.length() + 1); + + while (*src != '\0') { + *dest_ptr = *src; + if (!IsPathSeparator(*src)) { + src++; + } else { +#if GTEST_HAS_ALT_PATH_SEP_ + if (*dest_ptr == kAlternatePathSeparator) { + *dest_ptr = kPathSeparator; + } +#endif + while (IsPathSeparator(*src)) + src++; + } + dest_ptr++; + } + *dest_ptr = '\0'; + pathname_ = dest; + delete[] dest; +} + +} // namespace internal +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + + +#include +#include +#include +#include + +#if GTEST_OS_WINDOWS_MOBILE +# include // For TerminateProcess() +#elif GTEST_OS_WINDOWS +# include +# include +#else +# include +#endif // GTEST_OS_WINDOWS_MOBILE + +#if GTEST_OS_MAC +# include +# include +# include +#endif // GTEST_OS_MAC + +#if GTEST_OS_QNX +# include +# include +#endif // GTEST_OS_QNX + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { +namespace internal { + +#if defined(_MSC_VER) || defined(__BORLANDC__) +// MSVC and C++Builder do not provide a definition of STDERR_FILENO. +const int kStdOutFileno = 1; +const int kStdErrFileno = 2; +#else +const int kStdOutFileno = STDOUT_FILENO; +const int kStdErrFileno = STDERR_FILENO; +#endif // _MSC_VER + +#if GTEST_OS_MAC + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const task_t task = mach_task_self(); + mach_msg_type_number_t thread_count; + thread_act_array_t thread_list; + const kern_return_t status = task_threads(task, &thread_list, &thread_count); + if (status == KERN_SUCCESS) { + // task_threads allocates resources in thread_list and we need to free them + // to avoid leaks. + vm_deallocate(task, + reinterpret_cast(thread_list), + sizeof(thread_t) * thread_count); + return static_cast(thread_count); + } else { + return 0; + } +} + +#elif GTEST_OS_QNX + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +size_t GetThreadCount() { + const int fd = open("/proc/self/as", O_RDONLY); + if (fd < 0) { + return 0; + } + procfs_info process_info; + const int status = + devctl(fd, DCMD_PROC_INFO, &process_info, sizeof(process_info), NULL); + close(fd); + if (status == EOK) { + return static_cast(process_info.num_threads); + } else { + return 0; + } +} + +#else + +size_t GetThreadCount() { + // There's no portable way to detect the number of threads, so we just + // return 0 to indicate that we cannot detect it. + return 0; +} + +#endif // GTEST_OS_MAC + +#if GTEST_USES_POSIX_RE + +// Implements RE. Currently only needed for death tests. + +RE::~RE() { + if (is_valid_) { + // regfree'ing an invalid regex might crash because the content + // of the regex is undefined. Since the regex's are essentially + // the same, one cannot be valid (or invalid) without the other + // being so too. + regfree(&partial_regex_); + regfree(&full_regex_); + } + free(const_cast(pattern_)); +} + +// Returns true iff regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.full_regex_, str, 1, &match, 0) == 0; +} + +// Returns true iff regular expression re matches a substring of str +// (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + if (!re.is_valid_) return false; + + regmatch_t match; + return regexec(&re.partial_regex_, str, 1, &match, 0) == 0; +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = posix::StrDup(regex); + + // Reserves enough bytes to hold the regular expression used for a + // full match. + const size_t full_regex_len = strlen(regex) + 10; + char* const full_pattern = new char[full_regex_len]; + + snprintf(full_pattern, full_regex_len, "^(%s)$", regex); + is_valid_ = regcomp(&full_regex_, full_pattern, REG_EXTENDED) == 0; + // We want to call regcomp(&partial_regex_, ...) even if the + // previous expression returns false. Otherwise partial_regex_ may + // not be properly initialized can may cause trouble when it's + // freed. + // + // Some implementation of POSIX regex (e.g. on at least some + // versions of Cygwin) doesn't accept the empty string as a valid + // regex. We change it to an equivalent form "()" to be safe. + if (is_valid_) { + const char* const partial_regex = (*regex == '\0') ? "()" : regex; + is_valid_ = regcomp(&partial_regex_, partial_regex, REG_EXTENDED) == 0; + } + EXPECT_TRUE(is_valid_) + << "Regular expression \"" << regex + << "\" is not a valid POSIX Extended regular expression."; + + delete[] full_pattern; +} + +#elif GTEST_USES_SIMPLE_RE + +// Returns true iff ch appears anywhere in str (excluding the +// terminating '\0' character). +bool IsInSet(char ch, const char* str) { + return ch != '\0' && strchr(str, ch) != NULL; +} + +// Returns true iff ch belongs to the given classification. Unlike +// similar functions in , these aren't affected by the +// current locale. +bool IsAsciiDigit(char ch) { return '0' <= ch && ch <= '9'; } +bool IsAsciiPunct(char ch) { + return IsInSet(ch, "^-!\"#$%&'()*+,./:;<=>?@[\\]_`{|}~"); +} +bool IsRepeat(char ch) { return IsInSet(ch, "?*+"); } +bool IsAsciiWhiteSpace(char ch) { return IsInSet(ch, " \f\n\r\t\v"); } +bool IsAsciiWordChar(char ch) { + return ('a' <= ch && ch <= 'z') || ('A' <= ch && ch <= 'Z') || + ('0' <= ch && ch <= '9') || ch == '_'; +} + +// Returns true iff "\\c" is a supported escape sequence. +bool IsValidEscape(char c) { + return (IsAsciiPunct(c) || IsInSet(c, "dDfnrsStvwW")); +} + +// Returns true iff the given atom (specified by escaped and pattern) +// matches ch. The result is undefined if the atom is invalid. +bool AtomMatchesChar(bool escaped, char pattern_char, char ch) { + if (escaped) { // "\\p" where p is pattern_char. + switch (pattern_char) { + case 'd': return IsAsciiDigit(ch); + case 'D': return !IsAsciiDigit(ch); + case 'f': return ch == '\f'; + case 'n': return ch == '\n'; + case 'r': return ch == '\r'; + case 's': return IsAsciiWhiteSpace(ch); + case 'S': return !IsAsciiWhiteSpace(ch); + case 't': return ch == '\t'; + case 'v': return ch == '\v'; + case 'w': return IsAsciiWordChar(ch); + case 'W': return !IsAsciiWordChar(ch); + } + return IsAsciiPunct(pattern_char) && pattern_char == ch; + } + + return (pattern_char == '.' && ch != '\n') || pattern_char == ch; +} + +// Helper function used by ValidateRegex() to format error messages. +std::string FormatRegexSyntaxError(const char* regex, int index) { + return (Message() << "Syntax error at index " << index + << " in simple regular expression \"" << regex << "\": ").GetString(); +} + +// Generates non-fatal failures and returns false if regex is invalid; +// otherwise returns true. +bool ValidateRegex(const char* regex) { + if (regex == NULL) { + // TODO(wan@google.com): fix the source file location in the + // assertion failures to match where the regex is used in user + // code. + ADD_FAILURE() << "NULL is not a valid simple regular expression."; + return false; + } + + bool is_valid = true; + + // True iff ?, *, or + can follow the previous atom. + bool prev_repeatable = false; + for (int i = 0; regex[i]; i++) { + if (regex[i] == '\\') { // An escape sequence + i++; + if (regex[i] == '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "'\\' cannot appear at the end."; + return false; + } + + if (!IsValidEscape(regex[i])) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i - 1) + << "invalid escape sequence \"\\" << regex[i] << "\"."; + is_valid = false; + } + prev_repeatable = true; + } else { // Not an escape sequence. + const char ch = regex[i]; + + if (ch == '^' && i > 0) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'^' can only appear at the beginning."; + is_valid = false; + } else if (ch == '$' && regex[i + 1] != '\0') { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'$' can only appear at the end."; + is_valid = false; + } else if (IsInSet(ch, "()[]{}|")) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' is unsupported."; + is_valid = false; + } else if (IsRepeat(ch) && !prev_repeatable) { + ADD_FAILURE() << FormatRegexSyntaxError(regex, i) + << "'" << ch << "' can only follow a repeatable token."; + is_valid = false; + } + + prev_repeatable = !IsInSet(ch, "^$?*+"); + } + } + + return is_valid; +} + +// Matches a repeated regex atom followed by a valid simple regular +// expression. The regex atom is defined as c if escaped is false, +// or \c otherwise. repeat is the repetition meta character (?, *, +// or +). The behavior is undefined if str contains too many +// characters to be indexable by size_t, in which case the test will +// probably time out anyway. We are fine with this limitation as +// std::string has it too. +bool MatchRepetitionAndRegexAtHead( + bool escaped, char c, char repeat, const char* regex, + const char* str) { + const size_t min_count = (repeat == '+') ? 1 : 0; + const size_t max_count = (repeat == '?') ? 1 : + static_cast(-1) - 1; + // We cannot call numeric_limits::max() as it conflicts with the + // max() macro on Windows. + + for (size_t i = 0; i <= max_count; ++i) { + // We know that the atom matches each of the first i characters in str. + if (i >= min_count && MatchRegexAtHead(regex, str + i)) { + // We have enough matches at the head, and the tail matches too. + // Since we only care about *whether* the pattern matches str + // (as opposed to *how* it matches), there is no need to find a + // greedy match. + return true; + } + if (str[i] == '\0' || !AtomMatchesChar(escaped, c, str[i])) + return false; + } + return false; +} + +// Returns true iff regex matches a prefix of str. regex must be a +// valid simple regular expression and not start with "^", or the +// result is undefined. +bool MatchRegexAtHead(const char* regex, const char* str) { + if (*regex == '\0') // An empty regex matches a prefix of anything. + return true; + + // "$" only matches the end of a string. Note that regex being + // valid guarantees that there's nothing after "$" in it. + if (*regex == '$') + return *str == '\0'; + + // Is the first thing in regex an escape sequence? + const bool escaped = *regex == '\\'; + if (escaped) + ++regex; + if (IsRepeat(regex[1])) { + // MatchRepetitionAndRegexAtHead() calls MatchRegexAtHead(), so + // here's an indirect recursion. It terminates as the regex gets + // shorter in each recursion. + return MatchRepetitionAndRegexAtHead( + escaped, regex[0], regex[1], regex + 2, str); + } else { + // regex isn't empty, isn't "$", and doesn't start with a + // repetition. We match the first atom of regex with the first + // character of str and recurse. + return (*str != '\0') && AtomMatchesChar(escaped, *regex, *str) && + MatchRegexAtHead(regex + 1, str + 1); + } +} + +// Returns true iff regex matches any substring of str. regex must be +// a valid simple regular expression, or the result is undefined. +// +// The algorithm is recursive, but the recursion depth doesn't exceed +// the regex length, so we won't need to worry about running out of +// stack space normally. In rare cases the time complexity can be +// exponential with respect to the regex length + the string length, +// but usually it's must faster (often close to linear). +bool MatchRegexAnywhere(const char* regex, const char* str) { + if (regex == NULL || str == NULL) + return false; + + if (*regex == '^') + return MatchRegexAtHead(regex + 1, str); + + // A successful match can be anywhere in str. + do { + if (MatchRegexAtHead(regex, str)) + return true; + } while (*str++ != '\0'); + return false; +} + +// Implements the RE class. + +RE::~RE() { + free(const_cast(pattern_)); + free(const_cast(full_pattern_)); +} + +// Returns true iff regular expression re matches the entire str. +bool RE::FullMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.full_pattern_, str); +} + +// Returns true iff regular expression re matches a substring of str +// (including str itself). +bool RE::PartialMatch(const char* str, const RE& re) { + return re.is_valid_ && MatchRegexAnywhere(re.pattern_, str); +} + +// Initializes an RE from its string representation. +void RE::Init(const char* regex) { + pattern_ = full_pattern_ = NULL; + if (regex != NULL) { + pattern_ = posix::StrDup(regex); + } + + is_valid_ = ValidateRegex(regex); + if (!is_valid_) { + // No need to calculate the full pattern when the regex is invalid. + return; + } + + const size_t len = strlen(regex); + // Reserves enough bytes to hold the regular expression used for a + // full match: we need space to prepend a '^', append a '$', and + // terminate the string with '\0'. + char* buffer = static_cast(malloc(len + 3)); + full_pattern_ = buffer; + + if (*regex != '^') + *buffer++ = '^'; // Makes sure full_pattern_ starts with '^'. + + // We don't use snprintf or strncpy, as they trigger a warning when + // compiled with VC++ 8.0. + memcpy(buffer, regex, len); + buffer += len; + + if (len == 0 || regex[len - 1] != '$') + *buffer++ = '$'; // Makes sure full_pattern_ ends with '$'. + + *buffer = '\0'; +} + +#endif // GTEST_USES_POSIX_RE + +const char kUnknownFile[] = "unknown file"; + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line) { + const std::string file_name(file == NULL ? kUnknownFile : file); + + if (line < 0) { + return file_name + ":"; + } +#ifdef _MSC_VER + return file_name + "(" + StreamableToString(line) + "):"; +#else + return file_name + ":" + StreamableToString(line) + ":"; +#endif // _MSC_VER +} + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +// Note that FormatCompilerIndependentFileLocation() does NOT append colon +// to the file location it produces, unlike FormatFileLocation(). +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation( + const char* file, int line) { + const std::string file_name(file == NULL ? kUnknownFile : file); + + if (line < 0) + return file_name; + else + return file_name + ":" + StreamableToString(line); +} + + +GTestLog::GTestLog(GTestLogSeverity severity, const char* file, int line) + : severity_(severity) { + const char* const marker = + severity == GTEST_INFO ? "[ INFO ]" : + severity == GTEST_WARNING ? "[WARNING]" : + severity == GTEST_ERROR ? "[ ERROR ]" : "[ FATAL ]"; + GetStream() << ::std::endl << marker << " " + << FormatFileLocation(file, line).c_str() << ": "; +} + +// Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. +GTestLog::~GTestLog() { + GetStream() << ::std::endl; + if (severity_ == GTEST_FATAL) { + fflush(stderr); + posix::Abort(); + } +} +// Disable Microsoft deprecation warnings for POSIX functions called from +// this class (creat, dup, dup2, and close) +#ifdef _MSC_VER +# pragma warning(push) +# pragma warning(disable: 4996) +#endif // _MSC_VER + +#if GTEST_HAS_STREAM_REDIRECTION + +// Object that captures an output stream (stdout/stderr). +class CapturedStream { + public: + // The ctor redirects the stream to a temporary file. + explicit CapturedStream(int fd) : fd_(fd), uncaptured_fd_(dup(fd)) { +# if GTEST_OS_WINDOWS + char temp_dir_path[MAX_PATH + 1] = { '\0' }; // NOLINT + char temp_file_path[MAX_PATH + 1] = { '\0' }; // NOLINT + + ::GetTempPathA(sizeof(temp_dir_path), temp_dir_path); + const UINT success = ::GetTempFileNameA(temp_dir_path, + "gtest_redir", + 0, // Generate unique file name. + temp_file_path); + GTEST_CHECK_(success != 0) + << "Unable to create a temporary file in " << temp_dir_path; + const int captured_fd = creat(temp_file_path, _S_IREAD | _S_IWRITE); + GTEST_CHECK_(captured_fd != -1) << "Unable to open temporary file " + << temp_file_path; + filename_ = temp_file_path; +# else + // There's no guarantee that a test has write access to the current + // directory, so we create the temporary file in the /tmp directory + // instead. We use /tmp on most systems, and /sdcard on Android. + // That's because Android doesn't have /tmp. +# if GTEST_OS_LINUX_ANDROID + // Note: Android applications are expected to call the framework's + // Context.getExternalStorageDirectory() method through JNI to get + // the location of the world-writable SD Card directory. However, + // this requires a Context handle, which cannot be retrieved + // globally from native code. Doing so also precludes running the + // code as part of a regular standalone executable, which doesn't + // run in a Dalvik process (e.g. when running it through 'adb shell'). + // + // The location /sdcard is directly accessible from native code + // and is the only location (unofficially) supported by the Android + // team. It's generally a symlink to the real SD Card mount point + // which can be /mnt/sdcard, /mnt/sdcard0, /system/media/sdcard, or + // other OEM-customized locations. Never rely on these, and always + // use /sdcard. + char name_template[] = "/sdcard/gtest_captured_stream.XXXXXX"; +# else + char name_template[] = "/tmp/captured_stream.XXXXXX"; +# endif // GTEST_OS_LINUX_ANDROID + const int captured_fd = mkstemp(name_template); + filename_ = name_template; +# endif // GTEST_OS_WINDOWS + fflush(NULL); + dup2(captured_fd, fd_); + close(captured_fd); + } + + ~CapturedStream() { + remove(filename_.c_str()); + } + + std::string GetCapturedString() { + if (uncaptured_fd_ != -1) { + // Restores the original stream. + fflush(NULL); + dup2(uncaptured_fd_, fd_); + close(uncaptured_fd_); + uncaptured_fd_ = -1; + } + + FILE* const file = posix::FOpen(filename_.c_str(), "r"); + const std::string content = ReadEntireFile(file); + posix::FClose(file); + return content; + } + + private: + // Reads the entire content of a file as an std::string. + static std::string ReadEntireFile(FILE* file); + + // Returns the size (in bytes) of a file. + static size_t GetFileSize(FILE* file); + + const int fd_; // A stream to capture. + int uncaptured_fd_; + // Name of the temporary file holding the stderr output. + ::std::string filename_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(CapturedStream); +}; + +// Returns the size (in bytes) of a file. +size_t CapturedStream::GetFileSize(FILE* file) { + fseek(file, 0, SEEK_END); + return static_cast(ftell(file)); +} + +// Reads the entire content of a file as a string. +std::string CapturedStream::ReadEntireFile(FILE* file) { + const size_t file_size = GetFileSize(file); + char* const buffer = new char[file_size]; + + size_t bytes_last_read = 0; // # of bytes read in the last fread() + size_t bytes_read = 0; // # of bytes read so far + + fseek(file, 0, SEEK_SET); + + // Keeps reading the file until we cannot read further or the + // pre-determined file size is reached. + do { + bytes_last_read = fread(buffer+bytes_read, 1, file_size-bytes_read, file); + bytes_read += bytes_last_read; + } while (bytes_last_read > 0 && bytes_read < file_size); + + const std::string content(buffer, bytes_read); + delete[] buffer; + + return content; +} + +# ifdef _MSC_VER +# pragma warning(pop) +# endif // _MSC_VER + +static CapturedStream* g_captured_stderr = NULL; +static CapturedStream* g_captured_stdout = NULL; + +// Starts capturing an output stream (stdout/stderr). +void CaptureStream(int fd, const char* stream_name, CapturedStream** stream) { + if (*stream != NULL) { + GTEST_LOG_(FATAL) << "Only one " << stream_name + << " capturer can exist at a time."; + } + *stream = new CapturedStream(fd); +} + +// Stops capturing the output stream and returns the captured string. +std::string GetCapturedStream(CapturedStream** captured_stream) { + const std::string content = (*captured_stream)->GetCapturedString(); + + delete *captured_stream; + *captured_stream = NULL; + + return content; +} + +// Starts capturing stdout. +void CaptureStdout() { + CaptureStream(kStdOutFileno, "stdout", &g_captured_stdout); +} + +// Starts capturing stderr. +void CaptureStderr() { + CaptureStream(kStdErrFileno, "stderr", &g_captured_stderr); +} + +// Stops capturing stdout and returns the captured string. +std::string GetCapturedStdout() { + return GetCapturedStream(&g_captured_stdout); +} + +// Stops capturing stderr and returns the captured string. +std::string GetCapturedStderr() { + return GetCapturedStream(&g_captured_stderr); +} + +#endif // GTEST_HAS_STREAM_REDIRECTION + +#if GTEST_HAS_DEATH_TEST + +// A copy of all command line arguments. Set by InitGoogleTest(). +::std::vector g_argvs; + +static const ::std::vector* g_injected_test_argvs = + NULL; // Owned. + +void SetInjectableArgvs(const ::std::vector* argvs) { + if (g_injected_test_argvs != argvs) + delete g_injected_test_argvs; + g_injected_test_argvs = argvs; +} + +const ::std::vector& GetInjectableArgvs() { + if (g_injected_test_argvs != NULL) { + return *g_injected_test_argvs; + } + return g_argvs; +} +#endif // GTEST_HAS_DEATH_TEST + +#if GTEST_OS_WINDOWS_MOBILE +namespace posix { +void Abort() { + DebugBreak(); + TerminateProcess(GetCurrentProcess(), 1); +} +} // namespace posix +#endif // GTEST_OS_WINDOWS_MOBILE + +// Returns the name of the environment variable corresponding to the +// given flag. For example, FlagToEnvVar("foo") will return +// "GTEST_FOO" in the open-source version. +static std::string FlagToEnvVar(const char* flag) { + const std::string full_flag = + (Message() << GTEST_FLAG_PREFIX_ << flag).GetString(); + + Message env_var; + for (size_t i = 0; i != full_flag.length(); i++) { + env_var << ToUpper(full_flag.c_str()[i]); + } + + return env_var.GetString(); +} + +// Parses 'str' for a 32-bit signed integer. If successful, writes +// the result to *value and returns true; otherwise leaves *value +// unchanged and returns false. +bool ParseInt32(const Message& src_text, const char* str, Int32* value) { + // Parses the environment variable as a decimal integer. + char* end = NULL; + const long long_value = strtol(str, &end, 10); // NOLINT + + // Has strtol() consumed all characters in the string? + if (*end != '\0') { + // No - an invalid character was encountered. + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value \"" << str << "\".\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + // Is the parsed value in the range of an Int32? + const Int32 result = static_cast(long_value); + if (long_value == LONG_MAX || long_value == LONG_MIN || + // The parsed value overflows as a long. (strtol() returns + // LONG_MAX or LONG_MIN when the input overflows.) + result != long_value + // The parsed value overflows as an Int32. + ) { + Message msg; + msg << "WARNING: " << src_text + << " is expected to be a 32-bit integer, but actually" + << " has value " << str << ", which overflows.\n"; + printf("%s", msg.GetString().c_str()); + fflush(stdout); + return false; + } + + *value = result; + return true; +} + +// Reads and returns the Boolean environment variable corresponding to +// the given flag; if it's not set, returns default_value. +// +// The value is considered true iff it's not "0". +bool BoolFromGTestEnv(const char* flag, bool default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + return string_value == NULL ? + default_value : strcmp(string_value, "0") != 0; +} + +// Reads and returns a 32-bit integer stored in the environment +// variable corresponding to the given flag; if it isn't set or +// doesn't represent a valid 32-bit integer, returns default_value. +Int32 Int32FromGTestEnv(const char* flag, Int32 default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const string_value = posix::GetEnv(env_var.c_str()); + if (string_value == NULL) { + // The environment variable is not set. + return default_value; + } + + Int32 result = default_value; + if (!ParseInt32(Message() << "Environment variable " << env_var, + string_value, &result)) { + printf("The default value %s is used.\n", + (Message() << default_value).GetString().c_str()); + fflush(stdout); + return default_value; + } + + return result; +} + +// Reads and returns the string environment variable corresponding to +// the given flag; if it's not set, returns default_value. +const char* StringFromGTestEnv(const char* flag, const char* default_value) { + const std::string env_var = FlagToEnvVar(flag); + const char* const value = posix::GetEnv(env_var.c_str()); + return value == NULL ? default_value : value; +} + +} // namespace internal +} // namespace testing +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// It uses the << operator when possible, and prints the bytes in the +// object otherwise. A user can override its behavior for a class +// type Foo by defining either operator<<(::std::ostream&, const Foo&) +// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that +// defines Foo. + +#include +#include +#include // NOLINT +#include + +namespace testing { + +namespace { + +using ::std::ostream; + +// Prints a segment of bytes in the given object. +void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start, + size_t count, ostream* os) { + char text[5] = ""; + for (size_t i = 0; i != count; i++) { + const size_t j = start + i; + if (i != 0) { + // Organizes the bytes into groups of 2 for easy parsing by + // human. + if ((j % 2) == 0) + *os << ' '; + else + *os << '-'; + } + GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]); + *os << text; + } +} + +// Prints the bytes in the given value to the given ostream. +void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count, + ostream* os) { + // Tells the user how big the object is. + *os << count << "-byte object <"; + + const size_t kThreshold = 132; + const size_t kChunkSize = 64; + // If the object size is bigger than kThreshold, we'll have to omit + // some details by printing only the first and the last kChunkSize + // bytes. + // TODO(wan): let the user control the threshold using a flag. + if (count < kThreshold) { + PrintByteSegmentInObjectTo(obj_bytes, 0, count, os); + } else { + PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os); + *os << " ... "; + // Rounds up to 2-byte boundary. + const size_t resume_pos = (count - kChunkSize + 1)/2*2; + PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os); + } + *os << ">"; +} + +} // namespace + +namespace internal2 { + +// Delegates to PrintBytesInObjectToImpl() to print the bytes in the +// given object. The delegation simplifies the implementation, which +// uses the << operator and thus is easier done outside of the +// ::testing::internal namespace, which contains a << operator that +// sometimes conflicts with the one in STL. +void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count, + ostream* os) { + PrintBytesInObjectToImpl(obj_bytes, count, os); +} + +} // namespace internal2 + +namespace internal { + +// Depending on the value of a char (or wchar_t), we print it in one +// of three formats: +// - as is if it's a printable ASCII (e.g. 'a', '2', ' '), +// - as a hexidecimal escape sequence (e.g. '\x7F'), or +// - as a special escape sequence (e.g. '\r', '\n'). +enum CharFormat { + kAsIs, + kHexEscape, + kSpecialEscape +}; + +// Returns true if c is a printable ASCII character. We test the +// value of c directly instead of calling isprint(), which is buggy on +// Windows Mobile. +inline bool IsPrintableAscii(wchar_t c) { + return 0x20 <= c && c <= 0x7E; +} + +// Prints a wide or narrow char c as a character literal without the +// quotes, escaping it when necessary; returns how c was formatted. +// The template argument UnsignedChar is the unsigned version of Char, +// which is the type of c. +template +static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) { + switch (static_cast(c)) { + case L'\0': + *os << "\\0"; + break; + case L'\'': + *os << "\\'"; + break; + case L'\\': + *os << "\\\\"; + break; + case L'\a': + *os << "\\a"; + break; + case L'\b': + *os << "\\b"; + break; + case L'\f': + *os << "\\f"; + break; + case L'\n': + *os << "\\n"; + break; + case L'\r': + *os << "\\r"; + break; + case L'\t': + *os << "\\t"; + break; + case L'\v': + *os << "\\v"; + break; + default: + if (IsPrintableAscii(c)) { + *os << static_cast(c); + return kAsIs; + } else { + *os << "\\x" + String::FormatHexInt(static_cast(c)); + return kHexEscape; + } + } + return kSpecialEscape; +} + +// Prints a wchar_t c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) { + switch (c) { + case L'\'': + *os << "'"; + return kAsIs; + case L'"': + *os << "\\\""; + return kSpecialEscape; + default: + return PrintAsCharLiteralTo(c, os); + } +} + +// Prints a char c as if it's part of a string literal, escaping it when +// necessary; returns how c was formatted. +static CharFormat PrintAsStringLiteralTo(char c, ostream* os) { + return PrintAsStringLiteralTo( + static_cast(static_cast(c)), os); +} + +// Prints a wide or narrow character c and its code. '\0' is printed +// as "'\\0'", other unprintable characters are also properly escaped +// using the standard C++ escape sequence. The template argument +// UnsignedChar is the unsigned version of Char, which is the type of c. +template +void PrintCharAndCodeTo(Char c, ostream* os) { + // First, print c as a literal in the most readable form we can find. + *os << ((sizeof(c) > 1) ? "L'" : "'"); + const CharFormat format = PrintAsCharLiteralTo(c, os); + *os << "'"; + + // To aid user debugging, we also print c's code in decimal, unless + // it's 0 (in which case c was printed as '\\0', making the code + // obvious). + if (c == 0) + return; + *os << " (" << static_cast(c); + + // For more convenience, we print c's code again in hexidecimal, + // unless c was already printed in the form '\x##' or the code is in + // [1, 9]. + if (format == kHexEscape || (1 <= c && c <= 9)) { + // Do nothing. + } else { + *os << ", 0x" << String::FormatHexInt(static_cast(c)); + } + *os << ")"; +} + +void PrintTo(unsigned char c, ::std::ostream* os) { + PrintCharAndCodeTo(c, os); +} +void PrintTo(signed char c, ::std::ostream* os) { + PrintCharAndCodeTo(c, os); +} + +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its code. L'\0' is printed as "L'\\0'". +void PrintTo(wchar_t wc, ostream* os) { + PrintCharAndCodeTo(wc, os); +} + +// Prints the given array of characters to the ostream. CharType must be either +// char or wchar_t. +// The array starts at begin, the length is len, it may include '\0' characters +// and may not be NUL-terminated. +template +static void PrintCharsAsStringTo( + const CharType* begin, size_t len, ostream* os) { + const char* const kQuoteBegin = sizeof(CharType) == 1 ? "\"" : "L\""; + *os << kQuoteBegin; + bool is_previous_hex = false; + for (size_t index = 0; index < len; ++index) { + const CharType cur = begin[index]; + if (is_previous_hex && IsXDigit(cur)) { + // Previous character is of '\x..' form and this character can be + // interpreted as another hexadecimal digit in its number. Break string to + // disambiguate. + *os << "\" " << kQuoteBegin; + } + is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape; + } + *os << "\""; +} + +// Prints a (const) char/wchar_t array of 'len' elements, starting at address +// 'begin'. CharType must be either char or wchar_t. +template +static void UniversalPrintCharArray( + const CharType* begin, size_t len, ostream* os) { + // The code + // const char kFoo[] = "foo"; + // generates an array of 4, not 3, elements, with the last one being '\0'. + // + // Therefore when printing a char array, we don't print the last element if + // it's '\0', such that the output matches the string literal as it's + // written in the source code. + if (len > 0 && begin[len - 1] == '\0') { + PrintCharsAsStringTo(begin, len - 1, os); + return; + } + + // If, however, the last element in the array is not '\0', e.g. + // const char kFoo[] = { 'f', 'o', 'o' }; + // we must print the entire array. We also print a message to indicate + // that the array is not NUL-terminated. + PrintCharsAsStringTo(begin, len, os); + *os << " (no terminating NUL)"; +} + +// Prints a (const) char array of 'len' elements, starting at address 'begin'. +void UniversalPrintArray(const char* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints a (const) wchar_t array of 'len' elements, starting at address +// 'begin'. +void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) { + UniversalPrintCharArray(begin, len, os); +} + +// Prints the given C string to the ostream. +void PrintTo(const char* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_(s) << " pointing to "; + PrintCharsAsStringTo(s, strlen(s), os); + } +} + +// MSVC compiler can be configured to define whar_t as a typedef +// of unsigned short. Defining an overload for const wchar_t* in that case +// would cause pointers to unsigned shorts be printed as wide strings, +// possibly accessing more memory than intended and causing invalid +// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when +// wchar_t is implemented as a native type. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Prints the given wide C string to the ostream. +void PrintTo(const wchar_t* s, ostream* os) { + if (s == NULL) { + *os << "NULL"; + } else { + *os << ImplicitCast_(s) << " pointing to "; + PrintCharsAsStringTo(s, wcslen(s), os); + } +} +#endif // wchar_t is native + +// Prints a ::string object. +#if GTEST_HAS_GLOBAL_STRING +void PrintStringTo(const ::string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_STRING + +void PrintStringTo(const ::std::string& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} + +// Prints a ::wstring object. +#if GTEST_HAS_GLOBAL_WSTRING +void PrintWideStringTo(const ::wstring& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +void PrintWideStringTo(const ::std::wstring& s, ostream* os) { + PrintCharsAsStringTo(s.data(), s.size(), os); +} +#endif // GTEST_HAS_STD_WSTRING + +} // namespace internal + +} // namespace testing +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// +// The Google C++ Testing Framework (Google Test) + + +// Indicates that this translation unit is part of Google Test's +// implementation. It must come before gtest-internal-inl.h is +// included, or there will be a compiler error. This trick is to +// prevent a user from accidentally including gtest-internal-inl.h in +// his code. +#define GTEST_IMPLEMENTATION_ 1 +#undef GTEST_IMPLEMENTATION_ + +namespace testing { + +using internal::GetUnitTestImpl; + +// Gets the summary of the failure message by omitting the stack trace +// in it. +std::string TestPartResult::ExtractSummary(const char* message) { + const char* const stack_trace = strstr(message, internal::kStackTraceMarker); + return stack_trace == NULL ? message : + std::string(message, stack_trace); +} + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result) { + return os + << result.file_name() << ":" << result.line_number() << ": " + << (result.type() == TestPartResult::kSuccess ? "Success" : + result.type() == TestPartResult::kFatalFailure ? "Fatal failure" : + "Non-fatal failure") << ":\n" + << result.message() << std::endl; +} + +// Appends a TestPartResult to the array. +void TestPartResultArray::Append(const TestPartResult& result) { + array_.push_back(result); +} + +// Returns the TestPartResult at the given index (0-based). +const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const { + if (index < 0 || index >= size()) { + printf("\nInvalid index (%d) into TestPartResultArray.\n", index); + internal::posix::Abort(); + } + + return array_[index]; +} + +// Returns the number of TestPartResult objects in the array. +int TestPartResultArray::size() const { + return static_cast(array_.size()); +} + +namespace internal { + +HasNewFatalFailureHelper::HasNewFatalFailureHelper() + : has_new_fatal_failure_(false), + original_reporter_(GetUnitTestImpl()-> + GetTestPartResultReporterForCurrentThread()) { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this); +} + +HasNewFatalFailureHelper::~HasNewFatalFailureHelper() { + GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread( + original_reporter_); +} + +void HasNewFatalFailureHelper::ReportTestPartResult( + const TestPartResult& result) { + if (result.fatally_failed()) + has_new_fatal_failure_ = true; + original_reporter_->ReportTestPartResult(result); +} + +} // namespace internal + +} // namespace testing +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + + +namespace testing { +namespace internal { + +#if GTEST_HAS_TYPED_TEST_P + +// Skips to the first non-space char in str. Returns an empty string if str +// contains only whitespace characters. +static const char* SkipSpaces(const char* str) { + while (IsSpace(*str)) + str++; + return str; +} + +// Verifies that registered_tests match the test names in +// defined_test_names_; returns registered_tests if successful, or +// aborts the program otherwise. +const char* TypedTestCasePState::VerifyRegisteredTestNames( + const char* file, int line, const char* registered_tests) { + typedef ::std::set::const_iterator DefinedTestIter; + registered_ = true; + + // Skip initial whitespace in registered_tests since some + // preprocessors prefix stringizied literals with whitespace. + registered_tests = SkipSpaces(registered_tests); + + Message errors; + ::std::set tests; + for (const char* names = registered_tests; names != NULL; + names = SkipComma(names)) { + const std::string name = GetPrefixUntilComma(names); + if (tests.count(name) != 0) { + errors << "Test " << name << " is listed more than once.\n"; + continue; + } + + bool found = false; + for (DefinedTestIter it = defined_test_names_.begin(); + it != defined_test_names_.end(); + ++it) { + if (name == *it) { + found = true; + break; + } + } + + if (found) { + tests.insert(name); + } else { + errors << "No test named " << name + << " can be found in this test case.\n"; + } + } + + for (DefinedTestIter it = defined_test_names_.begin(); + it != defined_test_names_.end(); + ++it) { + if (tests.count(*it) == 0) { + errors << "You forgot to list test " << *it << ".\n"; + } + } + + const std::string& errors_str = errors.GetString(); + if (errors_str != "") { + fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(), + errors_str.c_str()); + fflush(stderr); + posix::Abort(); + } + + return registered_tests; +} + +#endif // GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing diff --git a/Engine/lib/gtest/fused-src/gtest/gtest.h b/Engine/lib/gtest/fused-src/gtest/gtest.h new file mode 100644 index 000000000..4f3804f70 --- /dev/null +++ b/Engine/lib/gtest/fused-src/gtest/gtest.h @@ -0,0 +1,20061 @@ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the public API for Google Test. It should be +// included by any test program that uses Google Test. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! +// +// Acknowledgment: Google Test borrowed the idea of automatic test +// registration from Barthelemy Dagenais' (barthelemy@prologique.com) +// easyUnit framework. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_H_ + +#include +#include +#include + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file declares functions and macros used internally by +// Google Test. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan) +// +// Low-level types and utilities for porting Google Test to various +// platforms. They are subject to change without notice. DO NOT USE +// THEM IN USER CODE. +// +// This file is fundamental to Google Test. All other Google Test source +// files are expected to #include this. Therefore, it cannot #include +// any other Google Test header. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +// The user can define the following macros in the build script to +// control Google Test's behavior. If the user doesn't define a macro +// in this list, Google Test will define it. +// +// GTEST_HAS_CLONE - Define it to 1/0 to indicate that clone(2) +// is/isn't available. +// GTEST_HAS_EXCEPTIONS - Define it to 1/0 to indicate that exceptions +// are enabled. +// GTEST_HAS_GLOBAL_STRING - Define it to 1/0 to indicate that ::string +// is/isn't available (some systems define +// ::string, which is different to std::string). +// GTEST_HAS_GLOBAL_WSTRING - Define it to 1/0 to indicate that ::string +// is/isn't available (some systems define +// ::wstring, which is different to std::wstring). +// GTEST_HAS_POSIX_RE - Define it to 1/0 to indicate that POSIX regular +// expressions are/aren't available. +// GTEST_HAS_PTHREAD - Define it to 1/0 to indicate that +// is/isn't available. +// GTEST_HAS_RTTI - Define it to 1/0 to indicate that RTTI is/isn't +// enabled. +// GTEST_HAS_STD_WSTRING - Define it to 1/0 to indicate that +// std::wstring does/doesn't work (Google Test can +// be used where std::wstring is unavailable). +// GTEST_HAS_TR1_TUPLE - Define it to 1/0 to indicate tr1::tuple +// is/isn't available. +// GTEST_HAS_SEH - Define it to 1/0 to indicate whether the +// compiler supports Microsoft's "Structured +// Exception Handling". +// GTEST_HAS_STREAM_REDIRECTION +// - Define it to 1/0 to indicate whether the +// platform supports I/O stream redirection using +// dup() and dup2(). +// GTEST_USE_OWN_TR1_TUPLE - Define it to 1/0 to indicate whether Google +// Test's own tr1 tuple implementation should be +// used. Unused when the user sets +// GTEST_HAS_TR1_TUPLE to 0. +// GTEST_LANG_CXX11 - Define it to 1/0 to indicate that Google Test +// is building in C++11/C++98 mode. +// GTEST_LINKED_AS_SHARED_LIBRARY +// - Define to 1 when compiling tests that use +// Google Test as a shared library (known as +// DLL on Windows). +// GTEST_CREATE_SHARED_LIBRARY +// - Define to 1 when compiling Google Test itself +// as a shared library. + +// This header defines the following utilities: +// +// Macros indicating the current platform (defined to 1 if compiled on +// the given platform; otherwise undefined): +// GTEST_OS_AIX - IBM AIX +// GTEST_OS_CYGWIN - Cygwin +// GTEST_OS_HPUX - HP-UX +// GTEST_OS_LINUX - Linux +// GTEST_OS_LINUX_ANDROID - Google Android +// GTEST_OS_MAC - Mac OS X +// GTEST_OS_IOS - iOS +// GTEST_OS_IOS_SIMULATOR - iOS simulator +// GTEST_OS_NACL - Google Native Client (NaCl) +// GTEST_OS_OPENBSD - OpenBSD +// GTEST_OS_QNX - QNX +// GTEST_OS_SOLARIS - Sun Solaris +// GTEST_OS_SYMBIAN - Symbian +// GTEST_OS_WINDOWS - Windows (Desktop, MinGW, or Mobile) +// GTEST_OS_WINDOWS_DESKTOP - Windows Desktop +// GTEST_OS_WINDOWS_MINGW - MinGW +// GTEST_OS_WINDOWS_MOBILE - Windows Mobile +// GTEST_OS_ZOS - z/OS +// +// Among the platforms, Cygwin, Linux, Max OS X, and Windows have the +// most stable support. Since core members of the Google Test project +// don't have access to other platforms, support for them may be less +// stable. If you notice any problems on your platform, please notify +// googletestframework@googlegroups.com (patches for fixing them are +// even more welcome!). +// +// Note that it is possible that none of the GTEST_OS_* macros are defined. +// +// Macros indicating available Google Test features (defined to 1 if +// the corresponding feature is supported; otherwise undefined): +// GTEST_HAS_COMBINE - the Combine() function (for value-parameterized +// tests) +// GTEST_HAS_DEATH_TEST - death tests +// GTEST_HAS_PARAM_TEST - value-parameterized tests +// GTEST_HAS_TYPED_TEST - typed tests +// GTEST_HAS_TYPED_TEST_P - type-parameterized tests +// GTEST_USES_POSIX_RE - enhanced POSIX regex is used. Do not confuse with +// GTEST_HAS_POSIX_RE (see above) which users can +// define themselves. +// GTEST_USES_SIMPLE_RE - our own simple regex is used; +// the above two are mutually exclusive. +// GTEST_CAN_COMPARE_NULL - accepts untyped NULL in EXPECT_EQ(). +// +// Macros for basic C++ coding: +// GTEST_AMBIGUOUS_ELSE_BLOCKER_ - for disabling a gcc warning. +// GTEST_ATTRIBUTE_UNUSED_ - declares that a class' instances or a +// variable don't have to be used. +// GTEST_DISALLOW_ASSIGN_ - disables operator=. +// GTEST_DISALLOW_COPY_AND_ASSIGN_ - disables copy ctor and operator=. +// GTEST_MUST_USE_RESULT_ - declares that a function's result must be used. +// +// Synchronization: +// Mutex, MutexLock, ThreadLocal, GetThreadCount() +// - synchronization primitives. +// GTEST_IS_THREADSAFE - defined to 1 to indicate that the above +// synchronization primitives have real implementations +// and Google Test is thread-safe; or 0 otherwise. +// +// Template meta programming: +// is_pointer - as in TR1; needed on Symbian and IBM XL C/C++ only. +// IteratorTraits - partial implementation of std::iterator_traits, which +// is not available in libCstd when compiled with Sun C++. +// +// Smart pointers: +// scoped_ptr - as in TR2. +// +// Regular expressions: +// RE - a simple regular expression class using the POSIX +// Extended Regular Expression syntax on UNIX-like +// platforms, or a reduced regular exception syntax on +// other platforms, including Windows. +// +// Logging: +// GTEST_LOG_() - logs messages at the specified severity level. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. +// +// Stdout and stderr capturing: +// CaptureStdout() - starts capturing stdout. +// GetCapturedStdout() - stops capturing stdout and returns the captured +// string. +// CaptureStderr() - starts capturing stderr. +// GetCapturedStderr() - stops capturing stderr and returns the captured +// string. +// +// Integer types: +// TypeWithSize - maps an integer to a int type. +// Int32, UInt32, Int64, UInt64, TimeInMillis +// - integers of known sizes. +// BiggestInt - the biggest signed integer type. +// +// Command-line utilities: +// GTEST_FLAG() - references a flag. +// GTEST_DECLARE_*() - declares a flag. +// GTEST_DEFINE_*() - defines a flag. +// GetInjectableArgvs() - returns the command line as a vector of strings. +// +// Environment variable utilities: +// GetEnv() - gets the value of an environment variable. +// BoolFromGTestEnv() - parses a bool environment variable. +// Int32FromGTestEnv() - parses an Int32 environment variable. +// StringFromGTestEnv() - parses a string environment variable. + +#include // for isspace, etc +#include // for ptrdiff_t +#include +#include +#include +#ifndef _WIN32_WCE +# include +# include +#endif // !_WIN32_WCE + +#if defined __APPLE__ +# include +# include +#endif + +#include // NOLINT +#include // NOLINT +#include // NOLINT + +#define GTEST_DEV_EMAIL_ "googletestframework@@googlegroups.com" +#define GTEST_FLAG_PREFIX_ "gtest_" +#define GTEST_FLAG_PREFIX_DASH_ "gtest-" +#define GTEST_FLAG_PREFIX_UPPER_ "GTEST_" +#define GTEST_NAME_ "Google Test" +#define GTEST_PROJECT_URL_ "http://code.google.com/p/googletest/" + +// Determines the version of gcc that is used to compile this. +#ifdef __GNUC__ +// 40302 means version 4.3.2. +# define GTEST_GCC_VER_ \ + (__GNUC__*10000 + __GNUC_MINOR__*100 + __GNUC_PATCHLEVEL__) +#endif // __GNUC__ + +// Determines the platform on which Google Test is compiled. +#ifdef __CYGWIN__ +# define GTEST_OS_CYGWIN 1 +#elif defined __SYMBIAN32__ +# define GTEST_OS_SYMBIAN 1 +#elif defined _WIN32 +# define GTEST_OS_WINDOWS 1 +# ifdef _WIN32_WCE +# define GTEST_OS_WINDOWS_MOBILE 1 +# elif defined(__MINGW__) || defined(__MINGW32__) +# define GTEST_OS_WINDOWS_MINGW 1 +# else +# define GTEST_OS_WINDOWS_DESKTOP 1 +# endif // _WIN32_WCE +#elif defined __APPLE__ +# define GTEST_OS_MAC 1 +# if TARGET_OS_IPHONE +# define GTEST_OS_IOS 1 +# if TARGET_IPHONE_SIMULATOR +# define GTEST_OS_IOS_SIMULATOR 1 +# endif +# endif +#elif defined __linux__ +# define GTEST_OS_LINUX 1 +# if defined __ANDROID__ +# define GTEST_OS_LINUX_ANDROID 1 +# endif +#elif defined __MVS__ +# define GTEST_OS_ZOS 1 +#elif defined(__sun) && defined(__SVR4) +# define GTEST_OS_SOLARIS 1 +#elif defined(_AIX) +# define GTEST_OS_AIX 1 +#elif defined(__hpux) +# define GTEST_OS_HPUX 1 +#elif defined __native_client__ +# define GTEST_OS_NACL 1 +#elif defined __OpenBSD__ +# define GTEST_OS_OPENBSD 1 +#elif defined __QNX__ +# define GTEST_OS_QNX 1 +#endif // __CYGWIN__ + +#ifndef GTEST_LANG_CXX11 +// gcc and clang define __GXX_EXPERIMENTAL_CXX0X__ when +// -std={c,gnu}++{0x,11} is passed. The C++11 standard specifies a +// value for __cplusplus, and recent versions of clang, gcc, and +// probably other compilers set that too in C++11 mode. +# if __GXX_EXPERIMENTAL_CXX0X__ || __cplusplus >= 201103L +// Compiling in at least C++11 mode. +# define GTEST_LANG_CXX11 1 +# else +# define GTEST_LANG_CXX11 0 +# endif +#endif + +// Brings in definitions for functions used in the testing::internal::posix +// namespace (read, write, close, chdir, isatty, stat). We do not currently +// use them on Windows Mobile. +#if !GTEST_OS_WINDOWS +// This assumes that non-Windows OSes provide unistd.h. For OSes where this +// is not the case, we need to include headers that provide the functions +// mentioned above. +# include +# include +#elif !GTEST_OS_WINDOWS_MOBILE +# include +# include +#endif + +#if GTEST_OS_LINUX_ANDROID +// Used to define __ANDROID_API__ matching the target NDK API level. +# include // NOLINT +#endif + +// Defines this to true iff Google Test can use POSIX regular expressions. +#ifndef GTEST_HAS_POSIX_RE +# if GTEST_OS_LINUX_ANDROID +// On Android, is only available starting with Gingerbread. +# define GTEST_HAS_POSIX_RE (__ANDROID_API__ >= 9) +# else +# define GTEST_HAS_POSIX_RE (!GTEST_OS_WINDOWS) +# endif +#endif + +#if GTEST_HAS_POSIX_RE + +// On some platforms, needs someone to define size_t, and +// won't compile otherwise. We can #include it here as we already +// included , which is guaranteed to define size_t through +// . +# include // NOLINT + +# define GTEST_USES_POSIX_RE 1 + +#elif GTEST_OS_WINDOWS + +// is not available on Windows. Use our own simple regex +// implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#else + +// may not be available on this platform. Use our own +// simple regex implementation instead. +# define GTEST_USES_SIMPLE_RE 1 + +#endif // GTEST_HAS_POSIX_RE + +#ifndef GTEST_HAS_EXCEPTIONS +// The user didn't tell us whether exceptions are enabled, so we need +// to figure it out. +# if defined(_MSC_VER) || defined(__BORLANDC__) +// MSVC's and C++Builder's implementations of the STL use the _HAS_EXCEPTIONS +// macro to enable exceptions, so we'll do the same. +// Assumes that exceptions are enabled by default. +# ifndef _HAS_EXCEPTIONS +# define _HAS_EXCEPTIONS 1 +# endif // _HAS_EXCEPTIONS +# define GTEST_HAS_EXCEPTIONS _HAS_EXCEPTIONS +# elif defined(__GNUC__) && __EXCEPTIONS +// gcc defines __EXCEPTIONS to 1 iff exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__SUNPRO_CC) +// Sun Pro CC supports exceptions. However, there is no compile-time way of +// detecting whether they are enabled or not. Therefore, we assume that +// they are enabled unless the user tells us otherwise. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__IBMCPP__) && __EXCEPTIONS +// xlC defines __EXCEPTIONS to 1 iff exceptions are enabled. +# define GTEST_HAS_EXCEPTIONS 1 +# elif defined(__HP_aCC) +// Exception handling is in effect by default in HP aCC compiler. It has to +// be turned of by +noeh compiler option if desired. +# define GTEST_HAS_EXCEPTIONS 1 +# else +// For other compilers, we assume exceptions are disabled to be +// conservative. +# define GTEST_HAS_EXCEPTIONS 0 +# endif // defined(_MSC_VER) || defined(__BORLANDC__) +#endif // GTEST_HAS_EXCEPTIONS + +#if !defined(GTEST_HAS_STD_STRING) +// Even though we don't use this macro any longer, we keep it in case +// some clients still depend on it. +# define GTEST_HAS_STD_STRING 1 +#elif !GTEST_HAS_STD_STRING +// The user told us that ::std::string isn't available. +# error "Google Test cannot be used where ::std::string isn't available." +#endif // !defined(GTEST_HAS_STD_STRING) + +#ifndef GTEST_HAS_GLOBAL_STRING +// The user didn't tell us whether ::string is available, so we need +// to figure it out. + +# define GTEST_HAS_GLOBAL_STRING 0 + +#endif // GTEST_HAS_GLOBAL_STRING + +#ifndef GTEST_HAS_STD_WSTRING +// The user didn't tell us whether ::std::wstring is available, so we need +// to figure it out. +// TODO(wan@google.com): uses autoconf to detect whether ::std::wstring +// is available. + +// Cygwin 1.7 and below doesn't support ::std::wstring. +// Solaris' libc++ doesn't support it either. Android has +// no support for it at least as recent as Froyo (2.2). +# define GTEST_HAS_STD_WSTRING \ + (!(GTEST_OS_LINUX_ANDROID || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS)) + +#endif // GTEST_HAS_STD_WSTRING + +#ifndef GTEST_HAS_GLOBAL_WSTRING +// The user didn't tell us whether ::wstring is available, so we need +// to figure it out. +# define GTEST_HAS_GLOBAL_WSTRING \ + (GTEST_HAS_STD_WSTRING && GTEST_HAS_GLOBAL_STRING) +#endif // GTEST_HAS_GLOBAL_WSTRING + +// Determines whether RTTI is available. +#ifndef GTEST_HAS_RTTI +// The user didn't tell us whether RTTI is enabled, so we need to +// figure it out. + +# ifdef _MSC_VER + +# ifdef _CPPRTTI // MSVC defines this macro iff RTTI is enabled. +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +// Starting with version 4.3.2, gcc defines __GXX_RTTI iff RTTI is enabled. +# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40302) + +# ifdef __GXX_RTTI +// When building against STLport with the Android NDK and with +// -frtti -fno-exceptions, the build fails at link time with undefined +// references to __cxa_bad_typeid. Note sure if STL or toolchain bug, +// so disable RTTI when detected. +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) && \ + !defined(__EXCEPTIONS) +# define GTEST_HAS_RTTI 0 +# else +# define GTEST_HAS_RTTI 1 +# endif // GTEST_OS_LINUX_ANDROID && __STLPORT_MAJOR && !__EXCEPTIONS +# else +# define GTEST_HAS_RTTI 0 +# endif // __GXX_RTTI + +// Clang defines __GXX_RTTI starting with version 3.0, but its manual recommends +// using has_feature instead. has_feature(cxx_rtti) is supported since 2.7, the +// first version with C++ support. +# elif defined(__clang__) + +# define GTEST_HAS_RTTI __has_feature(cxx_rtti) + +// Starting with version 9.0 IBM Visual Age defines __RTTI_ALL__ to 1 if +// both the typeid and dynamic_cast features are present. +# elif defined(__IBMCPP__) && (__IBMCPP__ >= 900) + +# ifdef __RTTI_ALL__ +# define GTEST_HAS_RTTI 1 +# else +# define GTEST_HAS_RTTI 0 +# endif + +# else + +// For all other compilers, we assume RTTI is enabled. +# define GTEST_HAS_RTTI 1 + +# endif // _MSC_VER + +#endif // GTEST_HAS_RTTI + +// It's this header's responsibility to #include when RTTI +// is enabled. +#if GTEST_HAS_RTTI +# include +#endif + +// Determines whether Google Test can use the pthreads library. +#ifndef GTEST_HAS_PTHREAD +// The user didn't tell us explicitly, so we assume pthreads support is +// available on Linux and Mac. +// +// To disable threading support in Google Test, add -DGTEST_HAS_PTHREAD=0 +// to your compiler flags. +# define GTEST_HAS_PTHREAD (GTEST_OS_LINUX || GTEST_OS_MAC || GTEST_OS_HPUX \ + || GTEST_OS_QNX) +#endif // GTEST_HAS_PTHREAD + +#if GTEST_HAS_PTHREAD +// gtest-port.h guarantees to #include when GTEST_HAS_PTHREAD is +// true. +# include // NOLINT + +// For timespec and nanosleep, used below. +# include // NOLINT +#endif + +// Determines whether Google Test can use tr1/tuple. You can define +// this macro to 0 to prevent Google Test from using tuple (any +// feature depending on tuple with be disabled in this mode). +#ifndef GTEST_HAS_TR1_TUPLE +# if GTEST_OS_LINUX_ANDROID && defined(_STLPORT_MAJOR) +// STLport, provided with the Android NDK, has neither or . +# define GTEST_HAS_TR1_TUPLE 0 +# else +// The user didn't tell us not to do it, so we assume it's OK. +# define GTEST_HAS_TR1_TUPLE 1 +# endif +#endif // GTEST_HAS_TR1_TUPLE + +// Determines whether Google Test's own tr1 tuple implementation +// should be used. +#ifndef GTEST_USE_OWN_TR1_TUPLE +// The user didn't tell us, so we need to figure it out. + +// We use our own TR1 tuple if we aren't sure the user has an +// implementation of it already. At this time, libstdc++ 4.0.0+ and +// MSVC 2010 are the only mainstream standard libraries that come +// with a TR1 tuple implementation. NVIDIA's CUDA NVCC compiler +// pretends to be GCC by defining __GNUC__ and friends, but cannot +// compile GCC's tuple implementation. MSVC 2008 (9.0) provides TR1 +// tuple in a 323 MB Feature Pack download, which we cannot assume the +// user has. QNX's QCC compiler is a modified GCC but it doesn't +// support TR1 tuple. libc++ only provides std::tuple, in C++11 mode, +// and it can be used with some compilers that define __GNUC__. +# if (defined(__GNUC__) && !defined(__CUDACC__) && (GTEST_GCC_VER_ >= 40000) \ + && !GTEST_OS_QNX && !defined(_LIBCPP_VERSION)) || _MSC_VER >= 1600 +# define GTEST_ENV_HAS_TR1_TUPLE_ 1 +# endif + +// C++11 specifies that provides std::tuple. Use that if gtest is used +// in C++11 mode and libstdc++ isn't very old (binaries targeting OS X 10.6 +// can build with clang but need to use gcc4.2's libstdc++). +# if GTEST_LANG_CXX11 && (!defined(__GLIBCXX__) || __GLIBCXX__ > 20110325) +# define GTEST_ENV_HAS_STD_TUPLE_ 1 +# endif + +# if GTEST_ENV_HAS_TR1_TUPLE_ || GTEST_ENV_HAS_STD_TUPLE_ +# define GTEST_USE_OWN_TR1_TUPLE 0 +# else +# define GTEST_USE_OWN_TR1_TUPLE 1 +# endif + +#endif // GTEST_USE_OWN_TR1_TUPLE + +// To avoid conditional compilation everywhere, we make it +// gtest-port.h's responsibility to #include the header implementing +// tr1/tuple. +#if GTEST_HAS_TR1_TUPLE + +# if GTEST_USE_OWN_TR1_TUPLE +// This file was GENERATED by command: +// pump.py gtest-tuple.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2009 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Implements a subset of TR1 tuple needed by Google Test and Google Mock. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ + +#include // For ::std::pair. + +// The compiler used in Symbian has a bug that prevents us from declaring the +// tuple template as a friend (it complains that tuple is redefined). This +// hack bypasses the bug by declaring the members that should otherwise be +// private as public. +// Sun Studio versions < 12 also have the above bug. +#if defined(__SYMBIAN32__) || (defined(__SUNPRO_CC) && __SUNPRO_CC < 0x590) +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ public: +#else +# define GTEST_DECLARE_TUPLE_AS_FRIEND_ \ + template friend class tuple; \ + private: +#endif + +// GTEST_n_TUPLE_(T) is the type of an n-tuple. +#define GTEST_0_TUPLE_(T) tuple<> +#define GTEST_1_TUPLE_(T) tuple +#define GTEST_2_TUPLE_(T) tuple +#define GTEST_3_TUPLE_(T) tuple +#define GTEST_4_TUPLE_(T) tuple +#define GTEST_5_TUPLE_(T) tuple +#define GTEST_6_TUPLE_(T) tuple +#define GTEST_7_TUPLE_(T) tuple +#define GTEST_8_TUPLE_(T) tuple +#define GTEST_9_TUPLE_(T) tuple +#define GTEST_10_TUPLE_(T) tuple + +// GTEST_n_TYPENAMES_(T) declares a list of n typenames. +#define GTEST_0_TYPENAMES_(T) +#define GTEST_1_TYPENAMES_(T) typename T##0 +#define GTEST_2_TYPENAMES_(T) typename T##0, typename T##1 +#define GTEST_3_TYPENAMES_(T) typename T##0, typename T##1, typename T##2 +#define GTEST_4_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3 +#define GTEST_5_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4 +#define GTEST_6_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5 +#define GTEST_7_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6 +#define GTEST_8_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, typename T##7 +#define GTEST_9_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, \ + typename T##7, typename T##8 +#define GTEST_10_TYPENAMES_(T) typename T##0, typename T##1, typename T##2, \ + typename T##3, typename T##4, typename T##5, typename T##6, \ + typename T##7, typename T##8, typename T##9 + +// In theory, defining stuff in the ::std namespace is undefined +// behavior. We can do this as we are playing the role of a standard +// library vendor. +namespace std { +namespace tr1 { + +template +class tuple; + +// Anything in namespace gtest_internal is Google Test's INTERNAL +// IMPLEMENTATION DETAIL and MUST NOT BE USED DIRECTLY in user code. +namespace gtest_internal { + +// ByRef::type is T if T is a reference; otherwise it's const T&. +template +struct ByRef { typedef const T& type; }; // NOLINT +template +struct ByRef { typedef T& type; }; // NOLINT + +// A handy wrapper for ByRef. +#define GTEST_BY_REF_(T) typename ::std::tr1::gtest_internal::ByRef::type + +// AddRef::type is T if T is a reference; otherwise it's T&. This +// is the same as tr1::add_reference::type. +template +struct AddRef { typedef T& type; }; // NOLINT +template +struct AddRef { typedef T& type; }; // NOLINT + +// A handy wrapper for AddRef. +#define GTEST_ADD_REF_(T) typename ::std::tr1::gtest_internal::AddRef::type + +// A helper for implementing get(). +template class Get; + +// A helper for implementing tuple_element. kIndexValid is true +// iff k < the number of fields in tuple type T. +template +struct TupleElement; + +template +struct TupleElement { + typedef T0 type; +}; + +template +struct TupleElement { + typedef T1 type; +}; + +template +struct TupleElement { + typedef T2 type; +}; + +template +struct TupleElement { + typedef T3 type; +}; + +template +struct TupleElement { + typedef T4 type; +}; + +template +struct TupleElement { + typedef T5 type; +}; + +template +struct TupleElement { + typedef T6 type; +}; + +template +struct TupleElement { + typedef T7 type; +}; + +template +struct TupleElement { + typedef T8 type; +}; + +template +struct TupleElement { + typedef T9 type; +}; + +} // namespace gtest_internal + +template <> +class tuple<> { + public: + tuple() {} + tuple(const tuple& /* t */) {} + tuple& operator=(const tuple& /* t */) { return *this; } +}; + +template +class GTEST_1_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0) : f0_(f0) {} + + tuple(const tuple& t) : f0_(t.f0_) {} + + template + tuple(const GTEST_1_TUPLE_(U)& t) : f0_(t.f0_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_1_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_1_TUPLE_(U)& t) { + f0_ = t.f0_; + return *this; + } + + T0 f0_; +}; + +template +class GTEST_2_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1) : f0_(f0), + f1_(f1) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_) {} + + template + tuple(const GTEST_2_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_) {} + template + tuple(const ::std::pair& p) : f0_(p.first), f1_(p.second) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_2_TUPLE_(U)& t) { + return CopyFrom(t); + } + template + tuple& operator=(const ::std::pair& p) { + f0_ = p.first; + f1_ = p.second; + return *this; + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_2_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + return *this; + } + + T0 f0_; + T1 f1_; +}; + +template +class GTEST_3_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2) : f0_(f0), f1_(f1), f2_(f2) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} + + template + tuple(const GTEST_3_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_3_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_3_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; +}; + +template +class GTEST_4_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_) {} + + template + tuple(const GTEST_4_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_4_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_4_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; +}; + +template +class GTEST_5_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, + GTEST_BY_REF_(T4) f4) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_) {} + + template + tuple(const GTEST_5_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_5_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_5_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; +}; + +template +class GTEST_6_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_) {} + + template + tuple(const GTEST_6_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_6_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_6_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; +}; + +template +class GTEST_7_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3), f4_(f4), f5_(f5), f6_(f6) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} + + template + tuple(const GTEST_7_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_7_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_7_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; +}; + +template +class GTEST_8_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, + GTEST_BY_REF_(T7) f7) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5), f6_(f6), f7_(f7) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} + + template + tuple(const GTEST_8_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_8_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_8_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; +}; + +template +class GTEST_9_TUPLE_(T) { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, + GTEST_BY_REF_(T8) f8) : f0_(f0), f1_(f1), f2_(f2), f3_(f3), f4_(f4), + f5_(f5), f6_(f6), f7_(f7), f8_(f8) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} + + template + tuple(const GTEST_9_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_9_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_9_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + f8_ = t.f8_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; + T8 f8_; +}; + +template +class tuple { + public: + template friend class gtest_internal::Get; + + tuple() : f0_(), f1_(), f2_(), f3_(), f4_(), f5_(), f6_(), f7_(), f8_(), + f9_() {} + + explicit tuple(GTEST_BY_REF_(T0) f0, GTEST_BY_REF_(T1) f1, + GTEST_BY_REF_(T2) f2, GTEST_BY_REF_(T3) f3, GTEST_BY_REF_(T4) f4, + GTEST_BY_REF_(T5) f5, GTEST_BY_REF_(T6) f6, GTEST_BY_REF_(T7) f7, + GTEST_BY_REF_(T8) f8, GTEST_BY_REF_(T9) f9) : f0_(f0), f1_(f1), f2_(f2), + f3_(f3), f4_(f4), f5_(f5), f6_(f6), f7_(f7), f8_(f8), f9_(f9) {} + + tuple(const tuple& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), f3_(t.f3_), + f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), f9_(t.f9_) {} + + template + tuple(const GTEST_10_TUPLE_(U)& t) : f0_(t.f0_), f1_(t.f1_), f2_(t.f2_), + f3_(t.f3_), f4_(t.f4_), f5_(t.f5_), f6_(t.f6_), f7_(t.f7_), f8_(t.f8_), + f9_(t.f9_) {} + + tuple& operator=(const tuple& t) { return CopyFrom(t); } + + template + tuple& operator=(const GTEST_10_TUPLE_(U)& t) { + return CopyFrom(t); + } + + GTEST_DECLARE_TUPLE_AS_FRIEND_ + + template + tuple& CopyFrom(const GTEST_10_TUPLE_(U)& t) { + f0_ = t.f0_; + f1_ = t.f1_; + f2_ = t.f2_; + f3_ = t.f3_; + f4_ = t.f4_; + f5_ = t.f5_; + f6_ = t.f6_; + f7_ = t.f7_; + f8_ = t.f8_; + f9_ = t.f9_; + return *this; + } + + T0 f0_; + T1 f1_; + T2 f2_; + T3 f3_; + T4 f4_; + T5 f5_; + T6 f6_; + T7 f7_; + T8 f8_; + T9 f9_; +}; + +// 6.1.3.2 Tuple creation functions. + +// Known limitations: we don't support passing an +// std::tr1::reference_wrapper to make_tuple(). And we don't +// implement tie(). + +inline tuple<> make_tuple() { return tuple<>(); } + +template +inline GTEST_1_TUPLE_(T) make_tuple(const T0& f0) { + return GTEST_1_TUPLE_(T)(f0); +} + +template +inline GTEST_2_TUPLE_(T) make_tuple(const T0& f0, const T1& f1) { + return GTEST_2_TUPLE_(T)(f0, f1); +} + +template +inline GTEST_3_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2) { + return GTEST_3_TUPLE_(T)(f0, f1, f2); +} + +template +inline GTEST_4_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3) { + return GTEST_4_TUPLE_(T)(f0, f1, f2, f3); +} + +template +inline GTEST_5_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4) { + return GTEST_5_TUPLE_(T)(f0, f1, f2, f3, f4); +} + +template +inline GTEST_6_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5) { + return GTEST_6_TUPLE_(T)(f0, f1, f2, f3, f4, f5); +} + +template +inline GTEST_7_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6) { + return GTEST_7_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6); +} + +template +inline GTEST_8_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7) { + return GTEST_8_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7); +} + +template +inline GTEST_9_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, + const T8& f8) { + return GTEST_9_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8); +} + +template +inline GTEST_10_TUPLE_(T) make_tuple(const T0& f0, const T1& f1, const T2& f2, + const T3& f3, const T4& f4, const T5& f5, const T6& f6, const T7& f7, + const T8& f8, const T9& f9) { + return GTEST_10_TUPLE_(T)(f0, f1, f2, f3, f4, f5, f6, f7, f8, f9); +} + +// 6.1.3.3 Tuple helper classes. + +template struct tuple_size; + +template +struct tuple_size { + static const int value = 0; +}; + +template +struct tuple_size { + static const int value = 1; +}; + +template +struct tuple_size { + static const int value = 2; +}; + +template +struct tuple_size { + static const int value = 3; +}; + +template +struct tuple_size { + static const int value = 4; +}; + +template +struct tuple_size { + static const int value = 5; +}; + +template +struct tuple_size { + static const int value = 6; +}; + +template +struct tuple_size { + static const int value = 7; +}; + +template +struct tuple_size { + static const int value = 8; +}; + +template +struct tuple_size { + static const int value = 9; +}; + +template +struct tuple_size { + static const int value = 10; +}; + +template +struct tuple_element { + typedef typename gtest_internal::TupleElement< + k < (tuple_size::value), k, Tuple>::type type; +}; + +#define GTEST_TUPLE_ELEMENT_(k, Tuple) typename tuple_element::type + +// 6.1.3.4 Element access. + +namespace gtest_internal { + +template <> +class Get<0> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) + Field(Tuple& t) { return t.f0_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(0, Tuple)) + ConstField(const Tuple& t) { return t.f0_; } +}; + +template <> +class Get<1> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) + Field(Tuple& t) { return t.f1_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(1, Tuple)) + ConstField(const Tuple& t) { return t.f1_; } +}; + +template <> +class Get<2> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) + Field(Tuple& t) { return t.f2_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(2, Tuple)) + ConstField(const Tuple& t) { return t.f2_; } +}; + +template <> +class Get<3> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) + Field(Tuple& t) { return t.f3_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(3, Tuple)) + ConstField(const Tuple& t) { return t.f3_; } +}; + +template <> +class Get<4> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) + Field(Tuple& t) { return t.f4_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(4, Tuple)) + ConstField(const Tuple& t) { return t.f4_; } +}; + +template <> +class Get<5> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) + Field(Tuple& t) { return t.f5_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(5, Tuple)) + ConstField(const Tuple& t) { return t.f5_; } +}; + +template <> +class Get<6> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) + Field(Tuple& t) { return t.f6_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(6, Tuple)) + ConstField(const Tuple& t) { return t.f6_; } +}; + +template <> +class Get<7> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) + Field(Tuple& t) { return t.f7_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(7, Tuple)) + ConstField(const Tuple& t) { return t.f7_; } +}; + +template <> +class Get<8> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) + Field(Tuple& t) { return t.f8_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(8, Tuple)) + ConstField(const Tuple& t) { return t.f8_; } +}; + +template <> +class Get<9> { + public: + template + static GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) + Field(Tuple& t) { return t.f9_; } // NOLINT + + template + static GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(9, Tuple)) + ConstField(const Tuple& t) { return t.f9_; } +}; + +} // namespace gtest_internal + +template +GTEST_ADD_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) +get(GTEST_10_TUPLE_(T)& t) { + return gtest_internal::Get::Field(t); +} + +template +GTEST_BY_REF_(GTEST_TUPLE_ELEMENT_(k, GTEST_10_TUPLE_(T))) +get(const GTEST_10_TUPLE_(T)& t) { + return gtest_internal::Get::ConstField(t); +} + +// 6.1.3.5 Relational operators + +// We only implement == and !=, as we don't have a need for the rest yet. + +namespace gtest_internal { + +// SameSizeTuplePrefixComparator::Eq(t1, t2) returns true if the +// first k fields of t1 equals the first k fields of t2. +// SameSizeTuplePrefixComparator(k1, k2) would be a compiler error if +// k1 != k2. +template +struct SameSizeTuplePrefixComparator; + +template <> +struct SameSizeTuplePrefixComparator<0, 0> { + template + static bool Eq(const Tuple1& /* t1 */, const Tuple2& /* t2 */) { + return true; + } +}; + +template +struct SameSizeTuplePrefixComparator { + template + static bool Eq(const Tuple1& t1, const Tuple2& t2) { + return SameSizeTuplePrefixComparator::Eq(t1, t2) && + ::std::tr1::get(t1) == ::std::tr1::get(t2); + } +}; + +} // namespace gtest_internal + +template +inline bool operator==(const GTEST_10_TUPLE_(T)& t, + const GTEST_10_TUPLE_(U)& u) { + return gtest_internal::SameSizeTuplePrefixComparator< + tuple_size::value, + tuple_size::value>::Eq(t, u); +} + +template +inline bool operator!=(const GTEST_10_TUPLE_(T)& t, + const GTEST_10_TUPLE_(U)& u) { return !(t == u); } + +// 6.1.4 Pairs. +// Unimplemented. + +} // namespace tr1 +} // namespace std + +#undef GTEST_0_TUPLE_ +#undef GTEST_1_TUPLE_ +#undef GTEST_2_TUPLE_ +#undef GTEST_3_TUPLE_ +#undef GTEST_4_TUPLE_ +#undef GTEST_5_TUPLE_ +#undef GTEST_6_TUPLE_ +#undef GTEST_7_TUPLE_ +#undef GTEST_8_TUPLE_ +#undef GTEST_9_TUPLE_ +#undef GTEST_10_TUPLE_ + +#undef GTEST_0_TYPENAMES_ +#undef GTEST_1_TYPENAMES_ +#undef GTEST_2_TYPENAMES_ +#undef GTEST_3_TYPENAMES_ +#undef GTEST_4_TYPENAMES_ +#undef GTEST_5_TYPENAMES_ +#undef GTEST_6_TYPENAMES_ +#undef GTEST_7_TYPENAMES_ +#undef GTEST_8_TYPENAMES_ +#undef GTEST_9_TYPENAMES_ +#undef GTEST_10_TYPENAMES_ + +#undef GTEST_DECLARE_TUPLE_AS_FRIEND_ +#undef GTEST_BY_REF_ +#undef GTEST_ADD_REF_ +#undef GTEST_TUPLE_ELEMENT_ + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TUPLE_H_ +# elif GTEST_ENV_HAS_STD_TUPLE_ +# include +// C++11 puts its tuple into the ::std namespace rather than +// ::std::tr1. gtest expects tuple to live in ::std::tr1, so put it there. +// This causes undefined behavior, but supported compilers react in +// the way we intend. +namespace std { +namespace tr1 { +using ::std::get; +using ::std::make_tuple; +using ::std::tuple; +using ::std::tuple_element; +using ::std::tuple_size; +} +} + +# elif GTEST_OS_SYMBIAN + +// On Symbian, BOOST_HAS_TR1_TUPLE causes Boost's TR1 tuple library to +// use STLport's tuple implementation, which unfortunately doesn't +// work as the copy of STLport distributed with Symbian is incomplete. +// By making sure BOOST_HAS_TR1_TUPLE is undefined, we force Boost to +// use its own tuple implementation. +# ifdef BOOST_HAS_TR1_TUPLE +# undef BOOST_HAS_TR1_TUPLE +# endif // BOOST_HAS_TR1_TUPLE + +// This prevents , which defines +// BOOST_HAS_TR1_TUPLE, from being #included by Boost's . +# define BOOST_TR1_DETAIL_CONFIG_HPP_INCLUDED +# include + +# elif defined(__GNUC__) && (GTEST_GCC_VER_ >= 40000) +// GCC 4.0+ implements tr1/tuple in the header. This does +// not conform to the TR1 spec, which requires the header to be . + +# if !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 +// Until version 4.3.2, gcc has a bug that causes , +// which is #included by , to not compile when RTTI is +// disabled. _TR1_FUNCTIONAL is the header guard for +// . Hence the following #define is a hack to prevent +// from being included. +# define _TR1_FUNCTIONAL 1 +# include +# undef _TR1_FUNCTIONAL // Allows the user to #include + // if he chooses to. +# else +# include // NOLINT +# endif // !GTEST_HAS_RTTI && GTEST_GCC_VER_ < 40302 + +# else +// If the compiler is not GCC 4.0+, we assume the user is using a +// spec-conforming TR1 implementation. +# include // NOLINT +# endif // GTEST_USE_OWN_TR1_TUPLE + +#endif // GTEST_HAS_TR1_TUPLE + +// Determines whether clone(2) is supported. +// Usually it will only be available on Linux, excluding +// Linux on the Itanium architecture. +// Also see http://linux.die.net/man/2/clone. +#ifndef GTEST_HAS_CLONE +// The user didn't tell us, so we need to figure it out. + +# if GTEST_OS_LINUX && !defined(__ia64__) +# if GTEST_OS_LINUX_ANDROID +// On Android, clone() is only available on ARM starting with Gingerbread. +# if defined(__arm__) && __ANDROID_API__ >= 9 +# define GTEST_HAS_CLONE 1 +# else +# define GTEST_HAS_CLONE 0 +# endif +# else +# define GTEST_HAS_CLONE 1 +# endif +# else +# define GTEST_HAS_CLONE 0 +# endif // GTEST_OS_LINUX && !defined(__ia64__) + +#endif // GTEST_HAS_CLONE + +// Determines whether to support stream redirection. This is used to test +// output correctness and to implement death tests. +#ifndef GTEST_HAS_STREAM_REDIRECTION +// By default, we assume that stream redirection is supported on all +// platforms except known mobile ones. +# if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_SYMBIAN +# define GTEST_HAS_STREAM_REDIRECTION 0 +# else +# define GTEST_HAS_STREAM_REDIRECTION 1 +# endif // !GTEST_OS_WINDOWS_MOBILE && !GTEST_OS_SYMBIAN +#endif // GTEST_HAS_STREAM_REDIRECTION + +// Determines whether to support death tests. +// Google Test does not support death tests for VC 7.1 and earlier as +// abort() in a VC 7.1 application compiled as GUI in debug config +// pops up a dialog window that cannot be suppressed programmatically. +#if (GTEST_OS_LINUX || GTEST_OS_CYGWIN || GTEST_OS_SOLARIS || \ + (GTEST_OS_MAC && !GTEST_OS_IOS) || GTEST_OS_IOS_SIMULATOR || \ + (GTEST_OS_WINDOWS_DESKTOP && _MSC_VER >= 1400) || \ + GTEST_OS_WINDOWS_MINGW || GTEST_OS_AIX || GTEST_OS_HPUX || \ + GTEST_OS_OPENBSD || GTEST_OS_QNX) +# define GTEST_HAS_DEATH_TEST 1 +# include // NOLINT +#endif + +// We don't support MSVC 7.1 with exceptions disabled now. Therefore +// all the compilers we care about are adequate for supporting +// value-parameterized tests. +#define GTEST_HAS_PARAM_TEST 1 + +// Determines whether to support type-driven tests. + +// Typed tests need and variadic macros, which GCC, VC++ 8.0, +// Sun Pro CC, IBM Visual Age, and HP aCC support. +#if defined(__GNUC__) || (_MSC_VER >= 1400) || defined(__SUNPRO_CC) || \ + defined(__IBMCPP__) || defined(__HP_aCC) +# define GTEST_HAS_TYPED_TEST 1 +# define GTEST_HAS_TYPED_TEST_P 1 +#endif + +// Determines whether to support Combine(). This only makes sense when +// value-parameterized tests are enabled. The implementation doesn't +// work on Sun Studio since it doesn't understand templated conversion +// operators. +#if GTEST_HAS_PARAM_TEST && GTEST_HAS_TR1_TUPLE && !defined(__SUNPRO_CC) +# define GTEST_HAS_COMBINE 1 +#endif + +// Determines whether the system compiler uses UTF-16 for encoding wide strings. +#define GTEST_WIDE_STRING_USES_UTF16_ \ + (GTEST_OS_WINDOWS || GTEST_OS_CYGWIN || GTEST_OS_SYMBIAN || GTEST_OS_AIX) + +// Determines whether test results can be streamed to a socket. +#if GTEST_OS_LINUX +# define GTEST_CAN_STREAM_RESULTS_ 1 +#endif + +// Defines some utility macros. + +// The GNU compiler emits a warning if nested "if" statements are followed by +// an "else" statement and braces are not used to explicitly disambiguate the +// "else" binding. This leads to problems with code like: +// +// if (gate) +// ASSERT_*(condition) << "Some message"; +// +// The "switch (0) case 0:" idiom is used to suppress this. +#ifdef __INTEL_COMPILER +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ +#else +# define GTEST_AMBIGUOUS_ELSE_BLOCKER_ switch (0) case 0: default: // NOLINT +#endif + +// Use this annotation at the end of a struct/class definition to +// prevent the compiler from optimizing away instances that are never +// used. This is useful when all interesting logic happens inside the +// c'tor and / or d'tor. Example: +// +// struct Foo { +// Foo() { ... } +// } GTEST_ATTRIBUTE_UNUSED_; +// +// Also use it after a variable or parameter declaration to tell the +// compiler the variable/parameter does not have to be used. +#if defined(__GNUC__) && !defined(COMPILER_ICC) +# define GTEST_ATTRIBUTE_UNUSED_ __attribute__ ((unused)) +#else +# define GTEST_ATTRIBUTE_UNUSED_ +#endif + +// A macro to disallow operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_ASSIGN_(type)\ + void operator=(type const &) + +// A macro to disallow copy constructor and operator= +// This should be used in the private: declarations for a class. +#define GTEST_DISALLOW_COPY_AND_ASSIGN_(type)\ + type(type const &);\ + GTEST_DISALLOW_ASSIGN_(type) + +// Tell the compiler to warn about unused return values for functions declared +// with this macro. The macro should be used on function declarations +// following the argument list: +// +// Sprocket* AllocateSprocket() GTEST_MUST_USE_RESULT_; +#if defined(__GNUC__) && (GTEST_GCC_VER_ >= 30400) && !defined(COMPILER_ICC) +# define GTEST_MUST_USE_RESULT_ __attribute__ ((warn_unused_result)) +#else +# define GTEST_MUST_USE_RESULT_ +#endif // __GNUC__ && (GTEST_GCC_VER_ >= 30400) && !COMPILER_ICC + +// Determine whether the compiler supports Microsoft's Structured Exception +// Handling. This is supported by several Windows compilers but generally +// does not exist on any other system. +#ifndef GTEST_HAS_SEH +// The user didn't tell us, so we need to figure it out. + +# if defined(_MSC_VER) || defined(__BORLANDC__) +// These two compilers are known to support SEH. +# define GTEST_HAS_SEH 1 +# else +// Assume no SEH. +# define GTEST_HAS_SEH 0 +# endif + +#endif // GTEST_HAS_SEH + +#ifdef _MSC_VER + +# if GTEST_LINKED_AS_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllimport) +# elif GTEST_CREATE_SHARED_LIBRARY +# define GTEST_API_ __declspec(dllexport) +# endif + +#endif // _MSC_VER + +#ifndef GTEST_API_ +# define GTEST_API_ +#endif + +#ifdef __GNUC__ +// Ask the compiler to never inline a given function. +# define GTEST_NO_INLINE_ __attribute__((noinline)) +#else +# define GTEST_NO_INLINE_ +#endif + +// _LIBCPP_VERSION is defined by the libc++ library from the LLVM project. +#if defined(__GLIBCXX__) || defined(_LIBCPP_VERSION) +# define GTEST_HAS_CXXABI_H_ 1 +#else +# define GTEST_HAS_CXXABI_H_ 0 +#endif + +namespace testing { + +class Message; + +namespace internal { + +// A secret type that Google Test users don't know about. It has no +// definition on purpose. Therefore it's impossible to create a +// Secret object, which is what we want. +class Secret; + +// The GTEST_COMPILE_ASSERT_ macro can be used to verify that a compile time +// expression is true. For example, you could use it to verify the +// size of a static array: +// +// GTEST_COMPILE_ASSERT_(ARRAYSIZE(content_type_names) == CONTENT_NUM_TYPES, +// content_type_names_incorrect_size); +// +// or to make sure a struct is smaller than a certain size: +// +// GTEST_COMPILE_ASSERT_(sizeof(foo) < 128, foo_too_large); +// +// The second argument to the macro is the name of the variable. If +// the expression is false, most compilers will issue a warning/error +// containing the name of the variable. + +template +struct CompileAssert { +}; + +#define GTEST_COMPILE_ASSERT_(expr, msg) \ + typedef ::testing::internal::CompileAssert<(static_cast(expr))> \ + msg[static_cast(expr) ? 1 : -1] GTEST_ATTRIBUTE_UNUSED_ + +// Implementation details of GTEST_COMPILE_ASSERT_: +// +// - GTEST_COMPILE_ASSERT_ works by defining an array type that has -1 +// elements (and thus is invalid) when the expression is false. +// +// - The simpler definition +// +// #define GTEST_COMPILE_ASSERT_(expr, msg) typedef char msg[(expr) ? 1 : -1] +// +// does not work, as gcc supports variable-length arrays whose sizes +// are determined at run-time (this is gcc's extension and not part +// of the C++ standard). As a result, gcc fails to reject the +// following code with the simple definition: +// +// int foo; +// GTEST_COMPILE_ASSERT_(foo, msg); // not supposed to compile as foo is +// // not a compile-time constant. +// +// - By using the type CompileAssert<(bool(expr))>, we ensures that +// expr is a compile-time constant. (Template arguments must be +// determined at compile-time.) +// +// - The outter parentheses in CompileAssert<(bool(expr))> are necessary +// to work around a bug in gcc 3.4.4 and 4.0.1. If we had written +// +// CompileAssert +// +// instead, these compilers will refuse to compile +// +// GTEST_COMPILE_ASSERT_(5 > 0, some_message); +// +// (They seem to think the ">" in "5 > 0" marks the end of the +// template argument list.) +// +// - The array size is (bool(expr) ? 1 : -1), instead of simply +// +// ((expr) ? 1 : -1). +// +// This is to avoid running into a bug in MS VC 7.1, which +// causes ((0.0) ? 1 : -1) to incorrectly evaluate to 1. + +// StaticAssertTypeEqHelper is used by StaticAssertTypeEq defined in gtest.h. +// +// This template is declared, but intentionally undefined. +template +struct StaticAssertTypeEqHelper; + +template +struct StaticAssertTypeEqHelper {}; + +#if GTEST_HAS_GLOBAL_STRING +typedef ::string string; +#else +typedef ::std::string string; +#endif // GTEST_HAS_GLOBAL_STRING + +#if GTEST_HAS_GLOBAL_WSTRING +typedef ::wstring wstring; +#elif GTEST_HAS_STD_WSTRING +typedef ::std::wstring wstring; +#endif // GTEST_HAS_GLOBAL_WSTRING + +// A helper for suppressing warnings on constant condition. It just +// returns 'condition'. +GTEST_API_ bool IsTrue(bool condition); + +// Defines scoped_ptr. + +// This implementation of scoped_ptr is PARTIAL - it only contains +// enough stuff to satisfy Google Test's need. +template +class scoped_ptr { + public: + typedef T element_type; + + explicit scoped_ptr(T* p = NULL) : ptr_(p) {} + ~scoped_ptr() { reset(); } + + T& operator*() const { return *ptr_; } + T* operator->() const { return ptr_; } + T* get() const { return ptr_; } + + T* release() { + T* const ptr = ptr_; + ptr_ = NULL; + return ptr; + } + + void reset(T* p = NULL) { + if (p != ptr_) { + if (IsTrue(sizeof(T) > 0)) { // Makes sure T is a complete type. + delete ptr_; + } + ptr_ = p; + } + } + + private: + T* ptr_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(scoped_ptr); +}; + +// Defines RE. + +// A simple C++ wrapper for . It uses the POSIX Extended +// Regular Expression syntax. +class GTEST_API_ RE { + public: + // A copy constructor is required by the Standard to initialize object + // references from r-values. + RE(const RE& other) { Init(other.pattern()); } + + // Constructs an RE from a string. + RE(const ::std::string& regex) { Init(regex.c_str()); } // NOLINT + +#if GTEST_HAS_GLOBAL_STRING + + RE(const ::string& regex) { Init(regex.c_str()); } // NOLINT + +#endif // GTEST_HAS_GLOBAL_STRING + + RE(const char* regex) { Init(regex); } // NOLINT + ~RE(); + + // Returns the string representation of the regex. + const char* pattern() const { return pattern_; } + + // FullMatch(str, re) returns true iff regular expression re matches + // the entire str. + // PartialMatch(str, re) returns true iff regular expression re + // matches a substring of str (including str itself). + // + // TODO(wan@google.com): make FullMatch() and PartialMatch() work + // when str contains NUL characters. + static bool FullMatch(const ::std::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::std::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + +#if GTEST_HAS_GLOBAL_STRING + + static bool FullMatch(const ::string& str, const RE& re) { + return FullMatch(str.c_str(), re); + } + static bool PartialMatch(const ::string& str, const RE& re) { + return PartialMatch(str.c_str(), re); + } + +#endif // GTEST_HAS_GLOBAL_STRING + + static bool FullMatch(const char* str, const RE& re); + static bool PartialMatch(const char* str, const RE& re); + + private: + void Init(const char* regex); + + // We use a const char* instead of an std::string, as Google Test used to be + // used where std::string is not available. TODO(wan@google.com): change to + // std::string. + const char* pattern_; + bool is_valid_; + +#if GTEST_USES_POSIX_RE + + regex_t full_regex_; // For FullMatch(). + regex_t partial_regex_; // For PartialMatch(). + +#else // GTEST_USES_SIMPLE_RE + + const char* full_pattern_; // For FullMatch(); + +#endif + + GTEST_DISALLOW_ASSIGN_(RE); +}; + +// Formats a source file path and a line number as they would appear +// in an error message from the compiler used to compile this code. +GTEST_API_ ::std::string FormatFileLocation(const char* file, int line); + +// Formats a file location for compiler-independent XML output. +// Although this function is not platform dependent, we put it next to +// FormatFileLocation in order to contrast the two functions. +GTEST_API_ ::std::string FormatCompilerIndependentFileLocation(const char* file, + int line); + +// Defines logging utilities: +// GTEST_LOG_(severity) - logs messages at the specified severity level. The +// message itself is streamed into the macro. +// LogToStderr() - directs all log messages to stderr. +// FlushInfoLog() - flushes informational log messages. + +enum GTestLogSeverity { + GTEST_INFO, + GTEST_WARNING, + GTEST_ERROR, + GTEST_FATAL +}; + +// Formats log entry severity, provides a stream object for streaming the +// log message, and terminates the message with a newline when going out of +// scope. +class GTEST_API_ GTestLog { + public: + GTestLog(GTestLogSeverity severity, const char* file, int line); + + // Flushes the buffers and, if severity is GTEST_FATAL, aborts the program. + ~GTestLog(); + + ::std::ostream& GetStream() { return ::std::cerr; } + + private: + const GTestLogSeverity severity_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestLog); +}; + +#define GTEST_LOG_(severity) \ + ::testing::internal::GTestLog(::testing::internal::GTEST_##severity, \ + __FILE__, __LINE__).GetStream() + +inline void LogToStderr() {} +inline void FlushInfoLog() { fflush(NULL); } + +// INTERNAL IMPLEMENTATION - DO NOT USE. +// +// GTEST_CHECK_ is an all-mode assert. It aborts the program if the condition +// is not satisfied. +// Synopsys: +// GTEST_CHECK_(boolean_condition); +// or +// GTEST_CHECK_(boolean_condition) << "Additional message"; +// +// This checks the condition and if the condition is not satisfied +// it prints message about the condition violation, including the +// condition itself, plus additional message streamed into it, if any, +// and then it aborts the program. It aborts the program irrespective of +// whether it is built in the debug mode or not. +#define GTEST_CHECK_(condition) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::IsTrue(condition)) \ + ; \ + else \ + GTEST_LOG_(FATAL) << "Condition " #condition " failed. " + +// An all-mode assert to verify that the given POSIX-style function +// call returns 0 (indicating success). Known limitation: this +// doesn't expand to a balanced 'if' statement, so enclose the macro +// in {} if you need to use it as the only statement in an 'if' +// branch. +#define GTEST_CHECK_POSIX_SUCCESS_(posix_call) \ + if (const int gtest_error = (posix_call)) \ + GTEST_LOG_(FATAL) << #posix_call << "failed with error " \ + << gtest_error + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Use ImplicitCast_ as a safe version of static_cast for upcasting in +// the type hierarchy (e.g. casting a Foo* to a SuperclassOfFoo* or a +// const Foo*). When you use ImplicitCast_, the compiler checks that +// the cast is safe. Such explicit ImplicitCast_s are necessary in +// surprisingly many situations where C++ demands an exact type match +// instead of an argument type convertable to a target type. +// +// The syntax for using ImplicitCast_ is the same as for static_cast: +// +// ImplicitCast_(expr) +// +// ImplicitCast_ would have been part of the C++ standard library, +// but the proposal was submitted too late. It will probably make +// its way into the language in the future. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., implicit_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template +inline To ImplicitCast_(To x) { return x; } + +// When you upcast (that is, cast a pointer from type Foo to type +// SuperclassOfFoo), it's fine to use ImplicitCast_<>, since upcasts +// always succeed. When you downcast (that is, cast a pointer from +// type Foo to type SubclassOfFoo), static_cast<> isn't safe, because +// how do you know the pointer is really of type SubclassOfFoo? It +// could be a bare Foo, or of type DifferentSubclassOfFoo. Thus, +// when you downcast, you should use this macro. In debug mode, we +// use dynamic_cast<> to double-check the downcast is legal (we die +// if it's not). In normal mode, we do the efficient static_cast<> +// instead. Thus, it's important to test in debug mode to make sure +// the cast is legal! +// This is the only place in the code we should use dynamic_cast<>. +// In particular, you SHOULDN'T be using dynamic_cast<> in order to +// do RTTI (eg code like this: +// if (dynamic_cast(foo)) HandleASubclass1Object(foo); +// if (dynamic_cast(foo)) HandleASubclass2Object(foo); +// You should design the code some other way not to need this. +// +// This relatively ugly name is intentional. It prevents clashes with +// similar functions users may have (e.g., down_cast). The internal +// namespace alone is not enough because the function can be found by ADL. +template // use like this: DownCast_(foo); +inline To DownCast_(From* f) { // so we only accept pointers + // Ensures that To is a sub-type of From *. This test is here only + // for compile-time type checking, and has no overhead in an + // optimized build at run-time, as it will be optimized away + // completely. + if (false) { + const To to = NULL; + ::testing::internal::ImplicitCast_(to); + } + +#if GTEST_HAS_RTTI + // RTTI: debug mode only! + GTEST_CHECK_(f == NULL || dynamic_cast(f) != NULL); +#endif + return static_cast(f); +} + +// Downcasts the pointer of type Base to Derived. +// Derived must be a subclass of Base. The parameter MUST +// point to a class of type Derived, not any subclass of it. +// When RTTI is available, the function performs a runtime +// check to enforce this. +template +Derived* CheckedDowncastToActualType(Base* base) { +#if GTEST_HAS_RTTI + GTEST_CHECK_(typeid(*base) == typeid(Derived)); + return dynamic_cast(base); // NOLINT +#else + return static_cast(base); // Poor man's downcast. +#endif +} + +#if GTEST_HAS_STREAM_REDIRECTION + +// Defines the stderr capturer: +// CaptureStdout - starts capturing stdout. +// GetCapturedStdout - stops capturing stdout and returns the captured string. +// CaptureStderr - starts capturing stderr. +// GetCapturedStderr - stops capturing stderr and returns the captured string. +// +GTEST_API_ void CaptureStdout(); +GTEST_API_ std::string GetCapturedStdout(); +GTEST_API_ void CaptureStderr(); +GTEST_API_ std::string GetCapturedStderr(); + +#endif // GTEST_HAS_STREAM_REDIRECTION + + +#if GTEST_HAS_DEATH_TEST + +const ::std::vector& GetInjectableArgvs(); +void SetInjectableArgvs(const ::std::vector* + new_argvs); + +// A copy of all command line arguments. Set by InitGoogleTest(). +extern ::std::vector g_argvs; + +#endif // GTEST_HAS_DEATH_TEST + +// Defines synchronization primitives. + +#if GTEST_HAS_PTHREAD + +// Sleeps for (roughly) n milli-seconds. This function is only for +// testing Google Test's own constructs. Don't use it in user tests, +// either directly or indirectly. +inline void SleepMilliseconds(int n) { + const timespec time = { + 0, // 0 seconds. + n * 1000L * 1000L, // And n ms. + }; + nanosleep(&time, NULL); +} + +// Allows a controller thread to pause execution of newly created +// threads until notified. Instances of this class must be created +// and destroyed in the controller thread. +// +// This class is only for testing Google Test's own constructs. Do not +// use it in user tests, either directly or indirectly. +class Notification { + public: + Notification() : notified_(false) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); + } + ~Notification() { + pthread_mutex_destroy(&mutex_); + } + + // Notifies all threads created with this notification to start. Must + // be called from the controller thread. + void Notify() { + pthread_mutex_lock(&mutex_); + notified_ = true; + pthread_mutex_unlock(&mutex_); + } + + // Blocks until the controller thread notifies. Must be called from a test + // thread. + void WaitForNotification() { + for (;;) { + pthread_mutex_lock(&mutex_); + const bool notified = notified_; + pthread_mutex_unlock(&mutex_); + if (notified) + break; + SleepMilliseconds(10); + } + } + + private: + pthread_mutex_t mutex_; + bool notified_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(Notification); +}; + +// As a C-function, ThreadFuncWithCLinkage cannot be templated itself. +// Consequently, it cannot select a correct instantiation of ThreadWithParam +// in order to call its Run(). Introducing ThreadWithParamBase as a +// non-templated base class for ThreadWithParam allows us to bypass this +// problem. +class ThreadWithParamBase { + public: + virtual ~ThreadWithParamBase() {} + virtual void Run() = 0; +}; + +// pthread_create() accepts a pointer to a function type with the C linkage. +// According to the Standard (7.5/1), function types with different linkages +// are different even if they are otherwise identical. Some compilers (for +// example, SunStudio) treat them as different types. Since class methods +// cannot be defined with C-linkage we need to define a free C-function to +// pass into pthread_create(). +extern "C" inline void* ThreadFuncWithCLinkage(void* thread) { + static_cast(thread)->Run(); + return NULL; +} + +// Helper class for testing Google Test's multi-threading constructs. +// To use it, write: +// +// void ThreadFunc(int param) { /* Do things with param */ } +// Notification thread_can_start; +// ... +// // The thread_can_start parameter is optional; you can supply NULL. +// ThreadWithParam thread(&ThreadFunc, 5, &thread_can_start); +// thread_can_start.Notify(); +// +// These classes are only for testing Google Test's own constructs. Do +// not use them in user tests, either directly or indirectly. +template +class ThreadWithParam : public ThreadWithParamBase { + public: + typedef void (*UserThreadFunc)(T); + + ThreadWithParam( + UserThreadFunc func, T param, Notification* thread_can_start) + : func_(func), + param_(param), + thread_can_start_(thread_can_start), + finished_(false) { + ThreadWithParamBase* const base = this; + // The thread can be created only after all fields except thread_ + // have been initialized. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_create(&thread_, 0, &ThreadFuncWithCLinkage, base)); + } + ~ThreadWithParam() { Join(); } + + void Join() { + if (!finished_) { + GTEST_CHECK_POSIX_SUCCESS_(pthread_join(thread_, 0)); + finished_ = true; + } + } + + virtual void Run() { + if (thread_can_start_ != NULL) + thread_can_start_->WaitForNotification(); + func_(param_); + } + + private: + const UserThreadFunc func_; // User-supplied thread function. + const T param_; // User-supplied parameter to the thread function. + // When non-NULL, used to block execution until the controller thread + // notifies. + Notification* const thread_can_start_; + bool finished_; // true iff we know that the thread function has finished. + pthread_t thread_; // The native thread object. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadWithParam); +}; + +// MutexBase and Mutex implement mutex on pthreads-based platforms. They +// are used in conjunction with class MutexLock: +// +// Mutex mutex; +// ... +// MutexLock lock(&mutex); // Acquires the mutex and releases it at the end +// // of the current scope. +// +// MutexBase implements behavior for both statically and dynamically +// allocated mutexes. Do not use MutexBase directly. Instead, write +// the following to define a static mutex: +// +// GTEST_DEFINE_STATIC_MUTEX_(g_some_mutex); +// +// You can forward declare a static mutex like this: +// +// GTEST_DECLARE_STATIC_MUTEX_(g_some_mutex); +// +// To create a dynamic mutex, just define an object of type Mutex. +class MutexBase { + public: + // Acquires this mutex. + void Lock() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_lock(&mutex_)); + owner_ = pthread_self(); + has_owner_ = true; + } + + // Releases this mutex. + void Unlock() { + // Since the lock is being released the owner_ field should no longer be + // considered valid. We don't protect writing to has_owner_ here, as it's + // the caller's responsibility to ensure that the current thread holds the + // mutex when this is called. + has_owner_ = false; + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_unlock(&mutex_)); + } + + // Does nothing if the current thread holds the mutex. Otherwise, crashes + // with high probability. + void AssertHeld() const { + GTEST_CHECK_(has_owner_ && pthread_equal(owner_, pthread_self())) + << "The current thread is not holding the mutex @" << this; + } + + // A static mutex may be used before main() is entered. It may even + // be used before the dynamic initialization stage. Therefore we + // must be able to initialize a static mutex object at link time. + // This means MutexBase has to be a POD and its member variables + // have to be public. + public: + pthread_mutex_t mutex_; // The underlying pthread mutex. + // has_owner_ indicates whether the owner_ field below contains a valid thread + // ID and is therefore safe to inspect (e.g., to use in pthread_equal()). All + // accesses to the owner_ field should be protected by a check of this field. + // An alternative might be to memset() owner_ to all zeros, but there's no + // guarantee that a zero'd pthread_t is necessarily invalid or even different + // from pthread_self(). + bool has_owner_; + pthread_t owner_; // The thread holding the mutex. +}; + +// Forward-declares a static mutex. +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::MutexBase mutex + +// Defines and statically (i.e. at link time) initializes a static mutex. +// The initialization list here does not explicitly initialize each field, +// instead relying on default initialization for the unspecified fields. In +// particular, the owner_ field (a pthread_t) is not explicitly initialized. +// This allows initialization to work whether pthread_t is a scalar or struct. +// The flag -Wmissing-field-initializers must not be specified for this to work. +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) \ + ::testing::internal::MutexBase mutex = { PTHREAD_MUTEX_INITIALIZER, false } + +// The Mutex class can only be used for mutexes created at runtime. It +// shares its API with MutexBase otherwise. +class Mutex : public MutexBase { + public: + Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_init(&mutex_, NULL)); + has_owner_ = false; + } + ~Mutex() { + GTEST_CHECK_POSIX_SUCCESS_(pthread_mutex_destroy(&mutex_)); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(Mutex); +}; + +// We cannot name this class MutexLock as the ctor declaration would +// conflict with a macro named MutexLock, which is defined on some +// platforms. Hence the typedef trick below. +class GTestMutexLock { + public: + explicit GTestMutexLock(MutexBase* mutex) + : mutex_(mutex) { mutex_->Lock(); } + + ~GTestMutexLock() { mutex_->Unlock(); } + + private: + MutexBase* const mutex_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(GTestMutexLock); +}; + +typedef GTestMutexLock MutexLock; + +// Helpers for ThreadLocal. + +// pthread_key_create() requires DeleteThreadLocalValue() to have +// C-linkage. Therefore it cannot be templatized to access +// ThreadLocal. Hence the need for class +// ThreadLocalValueHolderBase. +class ThreadLocalValueHolderBase { + public: + virtual ~ThreadLocalValueHolderBase() {} +}; + +// Called by pthread to delete thread-local data stored by +// pthread_setspecific(). +extern "C" inline void DeleteThreadLocalValue(void* value_holder) { + delete static_cast(value_holder); +} + +// Implements thread-local storage on pthreads-based systems. +// +// // Thread 1 +// ThreadLocal tl(100); // 100 is the default value for each thread. +// +// // Thread 2 +// tl.set(150); // Changes the value for thread 2 only. +// EXPECT_EQ(150, tl.get()); +// +// // Thread 1 +// EXPECT_EQ(100, tl.get()); // In thread 1, tl has the original value. +// tl.set(200); +// EXPECT_EQ(200, tl.get()); +// +// The template type argument T must have a public copy constructor. +// In addition, the default ThreadLocal constructor requires T to have +// a public default constructor. +// +// An object managed for a thread by a ThreadLocal instance is deleted +// when the thread exits. Or, if the ThreadLocal instance dies in +// that thread, when the ThreadLocal dies. It's the user's +// responsibility to ensure that all other threads using a ThreadLocal +// have exited when it dies, or the per-thread objects for those +// threads will not be deleted. +// +// Google Test only uses global ThreadLocal objects. That means they +// will die after main() has returned. Therefore, no per-thread +// object managed by Google Test will be leaked as long as all threads +// using Google Test have exited when main() returns. +template +class ThreadLocal { + public: + ThreadLocal() : key_(CreateKey()), + default_() {} + explicit ThreadLocal(const T& value) : key_(CreateKey()), + default_(value) {} + + ~ThreadLocal() { + // Destroys the managed object for the current thread, if any. + DeleteThreadLocalValue(pthread_getspecific(key_)); + + // Releases resources associated with the key. This will *not* + // delete managed objects for other threads. + GTEST_CHECK_POSIX_SUCCESS_(pthread_key_delete(key_)); + } + + T* pointer() { return GetOrCreateValue(); } + const T* pointer() const { return GetOrCreateValue(); } + const T& get() const { return *pointer(); } + void set(const T& value) { *pointer() = value; } + + private: + // Holds a value of type T. + class ValueHolder : public ThreadLocalValueHolderBase { + public: + explicit ValueHolder(const T& value) : value_(value) {} + + T* pointer() { return &value_; } + + private: + T value_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ValueHolder); + }; + + static pthread_key_t CreateKey() { + pthread_key_t key; + // When a thread exits, DeleteThreadLocalValue() will be called on + // the object managed for that thread. + GTEST_CHECK_POSIX_SUCCESS_( + pthread_key_create(&key, &DeleteThreadLocalValue)); + return key; + } + + T* GetOrCreateValue() const { + ThreadLocalValueHolderBase* const holder = + static_cast(pthread_getspecific(key_)); + if (holder != NULL) { + return CheckedDowncastToActualType(holder)->pointer(); + } + + ValueHolder* const new_holder = new ValueHolder(default_); + ThreadLocalValueHolderBase* const holder_base = new_holder; + GTEST_CHECK_POSIX_SUCCESS_(pthread_setspecific(key_, holder_base)); + return new_holder->pointer(); + } + + // A key pthreads uses for looking up per-thread values. + const pthread_key_t key_; + const T default_; // The default value for each thread. + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ThreadLocal); +}; + +# define GTEST_IS_THREADSAFE 1 + +#else // GTEST_HAS_PTHREAD + +// A dummy implementation of synchronization primitives (mutex, lock, +// and thread-local variable). Necessary for compiling Google Test where +// mutex is not supported - using Google Test in multiple threads is not +// supported on such platforms. + +class Mutex { + public: + Mutex() {} + void Lock() {} + void Unlock() {} + void AssertHeld() const {} +}; + +# define GTEST_DECLARE_STATIC_MUTEX_(mutex) \ + extern ::testing::internal::Mutex mutex + +# define GTEST_DEFINE_STATIC_MUTEX_(mutex) ::testing::internal::Mutex mutex + +class GTestMutexLock { + public: + explicit GTestMutexLock(Mutex*) {} // NOLINT +}; + +typedef GTestMutexLock MutexLock; + +template +class ThreadLocal { + public: + ThreadLocal() : value_() {} + explicit ThreadLocal(const T& value) : value_(value) {} + T* pointer() { return &value_; } + const T* pointer() const { return &value_; } + const T& get() const { return value_; } + void set(const T& value) { value_ = value; } + private: + T value_; +}; + +// The above synchronization primitives have dummy implementations. +// Therefore Google Test is not thread-safe. +# define GTEST_IS_THREADSAFE 0 + +#endif // GTEST_HAS_PTHREAD + +// Returns the number of threads running in the process, or 0 to indicate that +// we cannot detect it. +GTEST_API_ size_t GetThreadCount(); + +// Passing non-POD classes through ellipsis (...) crashes the ARM +// compiler and generates a warning in Sun Studio. The Nokia Symbian +// and the IBM XL C/C++ compiler try to instantiate a copy constructor +// for objects passed through ellipsis (...), failing for uncopyable +// objects. We define this to ensure that only POD is passed through +// ellipsis on these systems. +#if defined(__SYMBIAN32__) || defined(__IBMCPP__) || defined(__SUNPRO_CC) +// We lose support for NULL detection where the compiler doesn't like +// passing non-POD classes through ellipsis (...). +# define GTEST_ELLIPSIS_NEEDS_POD_ 1 +#else +# define GTEST_CAN_COMPARE_NULL 1 +#endif + +// The Nokia Symbian and IBM XL C/C++ compilers cannot decide between +// const T& and const T* in a function template. These compilers +// _can_ decide between class template specializations for T and T*, +// so a tr1::type_traits-like is_pointer works. +#if defined(__SYMBIAN32__) || defined(__IBMCPP__) +# define GTEST_NEEDS_IS_POINTER_ 1 +#endif + +template +struct bool_constant { + typedef bool_constant type; + static const bool value = bool_value; +}; +template const bool bool_constant::value; + +typedef bool_constant false_type; +typedef bool_constant true_type; + +template +struct is_pointer : public false_type {}; + +template +struct is_pointer : public true_type {}; + +template +struct IteratorTraits { + typedef typename Iterator::value_type value_type; +}; + +template +struct IteratorTraits { + typedef T value_type; +}; + +template +struct IteratorTraits { + typedef T value_type; +}; + +#if GTEST_OS_WINDOWS +# define GTEST_PATH_SEP_ "\\" +# define GTEST_HAS_ALT_PATH_SEP_ 1 +// The biggest signed integer type the compiler supports. +typedef __int64 BiggestInt; +#else +# define GTEST_PATH_SEP_ "/" +# define GTEST_HAS_ALT_PATH_SEP_ 0 +typedef long long BiggestInt; // NOLINT +#endif // GTEST_OS_WINDOWS + +// Utilities for char. + +// isspace(int ch) and friends accept an unsigned char or EOF. char +// may be signed, depending on the compiler (or compiler flags). +// Therefore we need to cast a char to unsigned char before calling +// isspace(), etc. + +inline bool IsAlpha(char ch) { + return isalpha(static_cast(ch)) != 0; +} +inline bool IsAlNum(char ch) { + return isalnum(static_cast(ch)) != 0; +} +inline bool IsDigit(char ch) { + return isdigit(static_cast(ch)) != 0; +} +inline bool IsLower(char ch) { + return islower(static_cast(ch)) != 0; +} +inline bool IsSpace(char ch) { + return isspace(static_cast(ch)) != 0; +} +inline bool IsUpper(char ch) { + return isupper(static_cast(ch)) != 0; +} +inline bool IsXDigit(char ch) { + return isxdigit(static_cast(ch)) != 0; +} +inline bool IsXDigit(wchar_t ch) { + const unsigned char low_byte = static_cast(ch); + return ch == low_byte && isxdigit(low_byte) != 0; +} + +inline char ToLower(char ch) { + return static_cast(tolower(static_cast(ch))); +} +inline char ToUpper(char ch) { + return static_cast(toupper(static_cast(ch))); +} + +// The testing::internal::posix namespace holds wrappers for common +// POSIX functions. These wrappers hide the differences between +// Windows/MSVC and POSIX systems. Since some compilers define these +// standard functions as macros, the wrapper cannot have the same name +// as the wrapped function. + +namespace posix { + +// Functions with a different name on Windows. + +#if GTEST_OS_WINDOWS + +typedef struct _stat StatStruct; + +# ifdef __BORLANDC__ +inline int IsATTY(int fd) { return isatty(fd); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +# else // !__BORLANDC__ +# if GTEST_OS_WINDOWS_MOBILE +inline int IsATTY(int /* fd */) { return 0; } +# else +inline int IsATTY(int fd) { return _isatty(fd); } +# endif // GTEST_OS_WINDOWS_MOBILE +inline int StrCaseCmp(const char* s1, const char* s2) { + return _stricmp(s1, s2); +} +inline char* StrDup(const char* src) { return _strdup(src); } +# endif // __BORLANDC__ + +# if GTEST_OS_WINDOWS_MOBILE +inline int FileNo(FILE* file) { return reinterpret_cast(_fileno(file)); } +// Stat(), RmDir(), and IsDir() are not needed on Windows CE at this +// time and thus not defined there. +# else +inline int FileNo(FILE* file) { return _fileno(file); } +inline int Stat(const char* path, StatStruct* buf) { return _stat(path, buf); } +inline int RmDir(const char* dir) { return _rmdir(dir); } +inline bool IsDir(const StatStruct& st) { + return (_S_IFDIR & st.st_mode) != 0; +} +# endif // GTEST_OS_WINDOWS_MOBILE + +#else + +typedef struct stat StatStruct; + +inline int FileNo(FILE* file) { return fileno(file); } +inline int IsATTY(int fd) { return isatty(fd); } +inline int Stat(const char* path, StatStruct* buf) { return stat(path, buf); } +inline int StrCaseCmp(const char* s1, const char* s2) { + return strcasecmp(s1, s2); +} +inline char* StrDup(const char* src) { return strdup(src); } +inline int RmDir(const char* dir) { return rmdir(dir); } +inline bool IsDir(const StatStruct& st) { return S_ISDIR(st.st_mode); } + +#endif // GTEST_OS_WINDOWS + +// Functions deprecated by MSVC 8.0. + +#ifdef _MSC_VER +// Temporarily disable warning 4996 (deprecated function). +# pragma warning(push) +# pragma warning(disable:4996) +#endif + +inline const char* StrNCpy(char* dest, const char* src, size_t n) { + return strncpy(dest, src, n); +} + +// ChDir(), FReopen(), FDOpen(), Read(), Write(), Close(), and +// StrError() aren't needed on Windows CE at this time and thus not +// defined there. + +#if !GTEST_OS_WINDOWS_MOBILE +inline int ChDir(const char* dir) { return chdir(dir); } +#endif +inline FILE* FOpen(const char* path, const char* mode) { + return fopen(path, mode); +} +#if !GTEST_OS_WINDOWS_MOBILE +inline FILE *FReopen(const char* path, const char* mode, FILE* stream) { + return freopen(path, mode, stream); +} +inline FILE* FDOpen(int fd, const char* mode) { return fdopen(fd, mode); } +#endif +inline int FClose(FILE* fp) { return fclose(fp); } +#if !GTEST_OS_WINDOWS_MOBILE +inline int Read(int fd, void* buf, unsigned int count) { + return static_cast(read(fd, buf, count)); +} +inline int Write(int fd, const void* buf, unsigned int count) { + return static_cast(write(fd, buf, count)); +} +inline int Close(int fd) { return close(fd); } +inline const char* StrError(int errnum) { return strerror(errnum); } +#endif +inline const char* GetEnv(const char* name) { +#if GTEST_OS_WINDOWS_MOBILE + // We are on Windows CE, which has no environment variables. + return NULL; +#elif defined(__BORLANDC__) || defined(__SunOS_5_8) || defined(__SunOS_5_9) + // Environment variables which we programmatically clear will be set to the + // empty string rather than unset (NULL). Handle that case. + const char* const env = getenv(name); + return (env != NULL && env[0] != '\0') ? env : NULL; +#else + return getenv(name); +#endif +} + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif + +#if GTEST_OS_WINDOWS_MOBILE +// Windows CE has no C library. The abort() function is used in +// several places in Google Test. This implementation provides a reasonable +// imitation of standard behaviour. +void Abort(); +#else +inline void Abort() { abort(); } +#endif // GTEST_OS_WINDOWS_MOBILE + +} // namespace posix + +// MSVC "deprecates" snprintf and issues warnings wherever it is used. In +// order to avoid these warnings, we need to use _snprintf or _snprintf_s on +// MSVC-based platforms. We map the GTEST_SNPRINTF_ macro to the appropriate +// function in order to achieve that. We use macro definition here because +// snprintf is a variadic function. +#if _MSC_VER >= 1400 && !GTEST_OS_WINDOWS_MOBILE +// MSVC 2005 and above support variadic macros. +# define GTEST_SNPRINTF_(buffer, size, format, ...) \ + _snprintf_s(buffer, size, size, format, __VA_ARGS__) +#elif defined(_MSC_VER) +// Windows CE does not define _snprintf_s and MSVC prior to 2005 doesn't +// complain about _snprintf. +# define GTEST_SNPRINTF_ _snprintf +#else +# define GTEST_SNPRINTF_ snprintf +#endif + +// The maximum number a BiggestInt can represent. This definition +// works no matter BiggestInt is represented in one's complement or +// two's complement. +// +// We cannot rely on numeric_limits in STL, as __int64 and long long +// are not part of standard C++ and numeric_limits doesn't need to be +// defined for them. +const BiggestInt kMaxBiggestInt = + ~(static_cast(1) << (8*sizeof(BiggestInt) - 1)); + +// This template class serves as a compile-time function from size to +// type. It maps a size in bytes to a primitive type with that +// size. e.g. +// +// TypeWithSize<4>::UInt +// +// is typedef-ed to be unsigned int (unsigned integer made up of 4 +// bytes). +// +// Such functionality should belong to STL, but I cannot find it +// there. +// +// Google Test uses this class in the implementation of floating-point +// comparison. +// +// For now it only handles UInt (unsigned int) as that's all Google Test +// needs. Other types can be easily added in the future if need +// arises. +template +class TypeWithSize { + public: + // This prevents the user from using TypeWithSize with incorrect + // values of N. + typedef void UInt; +}; + +// The specialization for size 4. +template <> +class TypeWithSize<4> { + public: + // unsigned int has size 4 in both gcc and MSVC. + // + // As base/basictypes.h doesn't compile on Windows, we cannot use + // uint32, uint64, and etc here. + typedef int Int; + typedef unsigned int UInt; +}; + +// The specialization for size 8. +template <> +class TypeWithSize<8> { + public: +#if GTEST_OS_WINDOWS + typedef __int64 Int; + typedef unsigned __int64 UInt; +#else + typedef long long Int; // NOLINT + typedef unsigned long long UInt; // NOLINT +#endif // GTEST_OS_WINDOWS +}; + +// Integer types of known sizes. +typedef TypeWithSize<4>::Int Int32; +typedef TypeWithSize<4>::UInt UInt32; +typedef TypeWithSize<8>::Int Int64; +typedef TypeWithSize<8>::UInt UInt64; +typedef TypeWithSize<8>::Int TimeInMillis; // Represents time in milliseconds. + +// Utilities for command line flags and environment variables. + +// Macro for referencing flags. +#define GTEST_FLAG(name) FLAGS_gtest_##name + +// Macros for declaring flags. +#define GTEST_DECLARE_bool_(name) GTEST_API_ extern bool GTEST_FLAG(name) +#define GTEST_DECLARE_int32_(name) \ + GTEST_API_ extern ::testing::internal::Int32 GTEST_FLAG(name) +#define GTEST_DECLARE_string_(name) \ + GTEST_API_ extern ::std::string GTEST_FLAG(name) + +// Macros for defining flags. +#define GTEST_DEFINE_bool_(name, default_val, doc) \ + GTEST_API_ bool GTEST_FLAG(name) = (default_val) +#define GTEST_DEFINE_int32_(name, default_val, doc) \ + GTEST_API_ ::testing::internal::Int32 GTEST_FLAG(name) = (default_val) +#define GTEST_DEFINE_string_(name, default_val, doc) \ + GTEST_API_ ::std::string GTEST_FLAG(name) = (default_val) + +// Thread annotations +#define GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks) +#define GTEST_LOCK_EXCLUDED_(locks) + +// Parses 'str' for a 32-bit signed integer. If successful, writes the result +// to *value and returns true; otherwise leaves *value unchanged and returns +// false. +// TODO(chandlerc): Find a better way to refactor flag and environment parsing +// out of both gtest-port.cc and gtest.cc to avoid exporting this utility +// function. +bool ParseInt32(const Message& src_text, const char* str, Int32* value); + +// Parses a bool/Int32/string from the environment variable +// corresponding to the given Google Test flag. +bool BoolFromGTestEnv(const char* flag, bool default_val); +GTEST_API_ Int32 Int32FromGTestEnv(const char* flag, Int32 default_val); +const char* StringFromGTestEnv(const char* flag, const char* default_val); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_H_ + +#if GTEST_OS_LINUX +# include +# include +# include +# include +#endif // GTEST_OS_LINUX + +#if GTEST_HAS_EXCEPTIONS +# include +#endif + +#include +#include +#include +#include +#include +#include + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the Message class. +// +// IMPORTANT NOTE: Due to limitation of the C++ language, we have to +// leave some internal implementation details in this header file. +// They are clearly marked by comments like this: +// +// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +// +// Such code is NOT meant to be used by a user directly, and is subject +// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user +// program! + +#ifndef GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +#define GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ + +#include + + +// Ensures that there is at least one operator<< in the global namespace. +// See Message& operator<<(...) below for why. +void operator<<(const testing::internal::Secret&, int); + +namespace testing { + +// The Message class works like an ostream repeater. +// +// Typical usage: +// +// 1. You stream a bunch of values to a Message object. +// It will remember the text in a stringstream. +// 2. Then you stream the Message object to an ostream. +// This causes the text in the Message to be streamed +// to the ostream. +// +// For example; +// +// testing::Message foo; +// foo << 1 << " != " << 2; +// std::cout << foo; +// +// will print "1 != 2". +// +// Message is not intended to be inherited from. In particular, its +// destructor is not virtual. +// +// Note that stringstream behaves differently in gcc and in MSVC. You +// can stream a NULL char pointer to it in the former, but not in the +// latter (it causes an access violation if you do). The Message +// class hides this difference by treating a NULL char pointer as +// "(null)". +class GTEST_API_ Message { + private: + // The type of basic IO manipulators (endl, ends, and flush) for + // narrow streams. + typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&); + + public: + // Constructs an empty Message. + Message(); + + // Copy constructor. + Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT + *ss_ << msg.GetString(); + } + + // Constructs a Message from a C-string. + explicit Message(const char* str) : ss_(new ::std::stringstream) { + *ss_ << str; + } + +#if GTEST_OS_SYMBIAN + // Streams a value (either a pointer or not) to this object. + template + inline Message& operator <<(const T& value) { + StreamHelper(typename internal::is_pointer::type(), value); + return *this; + } +#else + // Streams a non-pointer value to this object. + template + inline Message& operator <<(const T& val) { + // Some libraries overload << for STL containers. These + // overloads are defined in the global namespace instead of ::std. + // + // C++'s symbol lookup rule (i.e. Koenig lookup) says that these + // overloads are visible in either the std namespace or the global + // namespace, but not other namespaces, including the testing + // namespace which Google Test's Message class is in. + // + // To allow STL containers (and other types that has a << operator + // defined in the global namespace) to be used in Google Test + // assertions, testing::Message must access the custom << operator + // from the global namespace. With this using declaration, + // overloads of << defined in the global namespace and those + // visible via Koenig lookup are both exposed in this function. + using ::operator <<; + *ss_ << val; + return *this; + } + + // Streams a pointer value to this object. + // + // This function is an overload of the previous one. When you + // stream a pointer to a Message, this definition will be used as it + // is more specialized. (The C++ Standard, section + // [temp.func.order].) If you stream a non-pointer, then the + // previous definition will be used. + // + // The reason for this overload is that streaming a NULL pointer to + // ostream is undefined behavior. Depending on the compiler, you + // may get "0", "(nil)", "(null)", or an access violation. To + // ensure consistent result across compilers, we always treat NULL + // as "(null)". + template + inline Message& operator <<(T* const& pointer) { // NOLINT + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + return *this; + } +#endif // GTEST_OS_SYMBIAN + + // Since the basic IO manipulators are overloaded for both narrow + // and wide streams, we have to provide this specialized definition + // of operator <<, even though its body is the same as the + // templatized version above. Without this definition, streaming + // endl or other basic IO manipulators to Message will confuse the + // compiler. + Message& operator <<(BasicNarrowIoManip val) { + *ss_ << val; + return *this; + } + + // Instead of 1/0, we want to see true/false for bool values. + Message& operator <<(bool b) { + return *this << (b ? "true" : "false"); + } + + // These two overloads allow streaming a wide C string to a Message + // using the UTF-8 encoding. + Message& operator <<(const wchar_t* wide_c_str); + Message& operator <<(wchar_t* wide_c_str); + +#if GTEST_HAS_STD_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::std::wstring& wstr); +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_GLOBAL_WSTRING + // Converts the given wide string to a narrow string using the UTF-8 + // encoding, and streams the result to this Message object. + Message& operator <<(const ::wstring& wstr); +#endif // GTEST_HAS_GLOBAL_WSTRING + + // Gets the text streamed to this object so far as an std::string. + // Each '\0' character in the buffer is replaced with "\\0". + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + std::string GetString() const; + + private: + +#if GTEST_OS_SYMBIAN + // These are needed as the Nokia Symbian Compiler cannot decide between + // const T& and const T* in a function template. The Nokia compiler _can_ + // decide between class template specializations for T and T*, so a + // tr1::type_traits-like is_pointer works, and we can overload on that. + template + inline void StreamHelper(internal::true_type /*is_pointer*/, T* pointer) { + if (pointer == NULL) { + *ss_ << "(null)"; + } else { + *ss_ << pointer; + } + } + template + inline void StreamHelper(internal::false_type /*is_pointer*/, + const T& value) { + // See the comments in Message& operator <<(const T&) above for why + // we need this using statement. + using ::operator <<; + *ss_ << value; + } +#endif // GTEST_OS_SYMBIAN + + // We'll hold the text streamed to this object here. + const internal::scoped_ptr< ::std::stringstream> ss_; + + // We declare (but don't implement) this to prevent the compiler + // from implementing the assignment operator. + void operator=(const Message&); +}; + +// Streams a Message to an ostream. +inline std::ostream& operator <<(std::ostream& os, const Message& sb) { + return os << sb.GetString(); +} + +namespace internal { + +// Converts a streamable value to an std::string. A NULL pointer is +// converted to "(null)". When the input value is a ::string, +// ::std::string, ::wstring, or ::std::wstring object, each NUL +// character in it is replaced with "\\0". +template +std::string StreamableToString(const T& streamable) { + return (Message() << streamable).GetString(); +} + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_MESSAGE_H_ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file declares the String class and functions used internally by +// Google Test. They are subject to change without notice. They should not used +// by code external to Google Test. +// +// This header file is #included by . +// It should not be #included by other files. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ + +#ifdef __BORLANDC__ +// string.h is not guaranteed to provide strcpy on C++ Builder. +# include +#endif + +#include +#include + + +namespace testing { +namespace internal { + +// String - an abstract class holding static string utilities. +class GTEST_API_ String { + public: + // Static utility methods + + // Clones a 0-terminated C string, allocating memory using new. The + // caller is responsible for deleting the return value using + // delete[]. Returns the cloned string, or NULL if the input is + // NULL. + // + // This is different from strdup() in string.h, which allocates + // memory using malloc(). + static const char* CloneCString(const char* c_str); + +#if GTEST_OS_WINDOWS_MOBILE + // Windows CE does not have the 'ANSI' versions of Win32 APIs. To be + // able to pass strings to Win32 APIs on CE we need to convert them + // to 'Unicode', UTF-16. + + // Creates a UTF-16 wide string from the given ANSI string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the wide string, or NULL if the + // input is NULL. + // + // The wide string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static LPCWSTR AnsiToUtf16(const char* c_str); + + // Creates an ANSI string from the given wide string, allocating + // memory using new. The caller is responsible for deleting the return + // value using delete[]. Returns the ANSI string, or NULL if the + // input is NULL. + // + // The returned string is created using the ANSI codepage (CP_ACP) to + // match the behaviour of the ANSI versions of Win32 calls and the + // C runtime. + static const char* Utf16ToAnsi(LPCWSTR utf16_str); +#endif + + // Compares two C strings. Returns true iff they have the same content. + // + // Unlike strcmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CStringEquals(const char* lhs, const char* rhs); + + // Converts a wide C string to a String using the UTF-8 encoding. + // NULL will be converted to "(null)". If an error occurred during + // the conversion, "(failed to convert from wide string)" is + // returned. + static std::string ShowWideCString(const wchar_t* wide_c_str); + + // Compares two wide C strings. Returns true iff they have the same + // content. + // + // Unlike wcscmp(), this function can handle NULL argument(s). A + // NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs); + + // Compares two C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike strcasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL C string, + // including the empty string. + static bool CaseInsensitiveCStringEquals(const char* lhs, + const char* rhs); + + // Compares two wide C strings, ignoring case. Returns true iff they + // have the same content. + // + // Unlike wcscasecmp(), this function can handle NULL argument(s). + // A NULL C string is considered different to any non-NULL wide C string, + // including the empty string. + // NB: The implementations on different platforms slightly differ. + // On windows, this method uses _wcsicmp which compares according to LC_CTYPE + // environment variable. On GNU platform this method uses wcscasecmp + // which compares according to LC_CTYPE category of the current locale. + // On MacOS X, it uses towlower, which also uses LC_CTYPE category of the + // current locale. + static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs, + const wchar_t* rhs); + + // Returns true iff the given string ends with the given suffix, ignoring + // case. Any string is considered to end with an empty suffix. + static bool EndsWithCaseInsensitive( + const std::string& str, const std::string& suffix); + + // Formats an int value as "%02d". + static std::string FormatIntWidth2(int value); // "%02d" for width == 2 + + // Formats an int value as "%X". + static std::string FormatHexInt(int value); + + // Formats a byte as "%02X". + static std::string FormatByte(unsigned char value); + + private: + String(); // Not meant to be instantiated. +}; // class String + +// Gets the content of the stringstream's buffer as an std::string. Each '\0' +// character in the buffer is replaced with "\\0". +GTEST_API_ std::string StringStreamToString(::std::stringstream* stream); + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: keith.ray@gmail.com (Keith Ray) +// +// Google Test filepath utilities +// +// This header file declares classes and functions used internally by +// Google Test. They are subject to change without notice. +// +// This file is #included in . +// Do not include this header file separately! + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ + + +namespace testing { +namespace internal { + +// FilePath - a class for file and directory pathname manipulation which +// handles platform-specific conventions (like the pathname separator). +// Used for helper functions for naming files in a directory for xml output. +// Except for Set methods, all methods are const or static, which provides an +// "immutable value object" -- useful for peace of mind. +// A FilePath with a value ending in a path separator ("like/this/") represents +// a directory, otherwise it is assumed to represent a file. In either case, +// it may or may not represent an actual file or directory in the file system. +// Names are NOT checked for syntax correctness -- no checking for illegal +// characters, malformed paths, etc. + +class GTEST_API_ FilePath { + public: + FilePath() : pathname_("") { } + FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) { } + + explicit FilePath(const std::string& pathname) : pathname_(pathname) { + Normalize(); + } + + FilePath& operator=(const FilePath& rhs) { + Set(rhs); + return *this; + } + + void Set(const FilePath& rhs) { + pathname_ = rhs.pathname_; + } + + const std::string& string() const { return pathname_; } + const char* c_str() const { return pathname_.c_str(); } + + // Returns the current working directory, or "" if unsuccessful. + static FilePath GetCurrentDir(); + + // Given directory = "dir", base_name = "test", number = 0, + // extension = "xml", returns "dir/test.xml". If number is greater + // than zero (e.g., 12), returns "dir/test_12.xml". + // On Windows platform, uses \ as the separator rather than /. + static FilePath MakeFileName(const FilePath& directory, + const FilePath& base_name, + int number, + const char* extension); + + // Given directory = "dir", relative_path = "test.xml", + // returns "dir/test.xml". + // On Windows, uses \ as the separator rather than /. + static FilePath ConcatPaths(const FilePath& directory, + const FilePath& relative_path); + + // Returns a pathname for a file that does not currently exist. The pathname + // will be directory/base_name.extension or + // directory/base_name_.extension if directory/base_name.extension + // already exists. The number will be incremented until a pathname is found + // that does not already exist. + // Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'. + // There could be a race condition if two or more processes are calling this + // function at the same time -- they could both pick the same filename. + static FilePath GenerateUniqueFileName(const FilePath& directory, + const FilePath& base_name, + const char* extension); + + // Returns true iff the path is "". + bool IsEmpty() const { return pathname_.empty(); } + + // If input name has a trailing separator character, removes it and returns + // the name, otherwise return the name string unmodified. + // On Windows platform, uses \ as the separator, other platforms use /. + FilePath RemoveTrailingPathSeparator() const; + + // Returns a copy of the FilePath with the directory part removed. + // Example: FilePath("path/to/file").RemoveDirectoryName() returns + // FilePath("file"). If there is no directory part ("just_a_file"), it returns + // the FilePath unmodified. If there is no file part ("just_a_dir/") it + // returns an empty FilePath (""). + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveDirectoryName() const; + + // RemoveFileName returns the directory path with the filename removed. + // Example: FilePath("path/to/file").RemoveFileName() returns "path/to/". + // If the FilePath is "a_file" or "/a_file", RemoveFileName returns + // FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does + // not have a file, like "just/a/dir/", it returns the FilePath unmodified. + // On Windows platform, '\' is the path separator, otherwise it is '/'. + FilePath RemoveFileName() const; + + // Returns a copy of the FilePath with the case-insensitive extension removed. + // Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns + // FilePath("dir/file"). If a case-insensitive extension is not + // found, returns a copy of the original FilePath. + FilePath RemoveExtension(const char* extension) const; + + // Creates directories so that path exists. Returns true if successful or if + // the directories already exist; returns false if unable to create + // directories for any reason. Will also return false if the FilePath does + // not represent a directory (that is, it doesn't end with a path separator). + bool CreateDirectoriesRecursively() const; + + // Create the directory so that path exists. Returns true if successful or + // if the directory already exists; returns false if unable to create the + // directory for any reason, including if the parent directory does not + // exist. Not named "CreateDirectory" because that's a macro on Windows. + bool CreateFolder() const; + + // Returns true if FilePath describes something in the file-system, + // either a file, directory, or whatever, and that something exists. + bool FileOrDirectoryExists() const; + + // Returns true if pathname describes a directory in the file-system + // that exists. + bool DirectoryExists() const; + + // Returns true if FilePath ends with a path separator, which indicates that + // it is intended to represent a directory. Returns false otherwise. + // This does NOT check that a directory (or file) actually exists. + bool IsDirectory() const; + + // Returns true if pathname describes a root directory. (Windows has one + // root directory per disk drive.) + bool IsRootDirectory() const; + + // Returns true if pathname describes an absolute path. + bool IsAbsolutePath() const; + + private: + // Replaces multiple consecutive separators with a single separator. + // For example, "bar///foo" becomes "bar/foo". Does not eliminate other + // redundancies that might be in a pathname involving "." or "..". + // + // A pathname with multiple consecutive separators may occur either through + // user error or as a result of some scripts or APIs that generate a pathname + // with a trailing separator. On other platforms the same API or script + // may NOT generate a pathname with a trailing "/". Then elsewhere that + // pathname may have another "/" and pathname components added to it, + // without checking for the separator already being there. + // The script language and operating system may allow paths like "foo//bar" + // but some of the functions in FilePath will not handle that correctly. In + // particular, RemoveTrailingPathSeparator() only removes one separator, and + // it is called in CreateDirectoriesRecursively() assuming that it will change + // a pathname from directory syntax (trailing separator) to filename syntax. + // + // On Windows this method also replaces the alternate path separator '/' with + // the primary path separator '\\', so that for example "bar\\/\\foo" becomes + // "bar\\foo". + + void Normalize(); + + // Returns a pointer to the last occurence of a valid path separator in + // the FilePath. On Windows, for example, both '/' and '\' are valid path + // separators. Returns NULL if no path separator was found. + const char* FindLastPathSeparator() const; + + std::string pathname_; +}; // class FilePath + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_ +// This file was GENERATED by command: +// pump.py gtest-type-util.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Type utilities needed for implementing typed and type-parameterized +// tests. This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently we support at most 50 types in a list, and at most 50 +// type-parameterized tests in one type-parameterized test case. +// Please contact googletestframework@googlegroups.com if you need +// more. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + + +// #ifdef __GNUC__ is too general here. It is possible to use gcc without using +// libstdc++ (which is where cxxabi.h comes from). +# if GTEST_HAS_CXXABI_H_ +# include +# elif defined(__HP_aCC) +# include +# endif // GTEST_HASH_CXXABI_H_ + +namespace testing { +namespace internal { + +// GetTypeName() returns a human-readable name of type T. +// NB: This function is also used in Google Mock, so don't move it inside of +// the typed-test-only section below. +template +std::string GetTypeName() { +# if GTEST_HAS_RTTI + + const char* const name = typeid(T).name(); +# if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC) + int status = 0; + // gcc's implementation of typeid(T).name() mangles the type name, + // so we have to demangle it. +# if GTEST_HAS_CXXABI_H_ + using abi::__cxa_demangle; +# endif // GTEST_HAS_CXXABI_H_ + char* const readable_name = __cxa_demangle(name, 0, 0, &status); + const std::string name_str(status == 0 ? readable_name : name); + free(readable_name); + return name_str; +# else + return name; +# endif // GTEST_HAS_CXXABI_H_ || __HP_aCC + +# else + + return ""; + +# endif // GTEST_HAS_RTTI +} + +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// AssertyTypeEq::type is defined iff T1 and T2 are the same +// type. This can be used as a compile-time assertion to ensure that +// two types are equal. + +template +struct AssertTypeEq; + +template +struct AssertTypeEq { + typedef bool type; +}; + +// A unique type used as the default value for the arguments of class +// template Types. This allows us to simulate variadic templates +// (e.g. Types, Type, and etc), which C++ doesn't +// support directly. +struct None {}; + +// The following family of struct and struct templates are used to +// represent type lists. In particular, TypesN +// represents a type list with N types (T1, T2, ..., and TN) in it. +// Except for Types0, every struct in the family has two member types: +// Head for the first type in the list, and Tail for the rest of the +// list. + +// The empty type list. +struct Types0 {}; + +// Type lists of length 1, 2, 3, and so on. + +template +struct Types1 { + typedef T1 Head; + typedef Types0 Tail; +}; +template +struct Types2 { + typedef T1 Head; + typedef Types1 Tail; +}; + +template +struct Types3 { + typedef T1 Head; + typedef Types2 Tail; +}; + +template +struct Types4 { + typedef T1 Head; + typedef Types3 Tail; +}; + +template +struct Types5 { + typedef T1 Head; + typedef Types4 Tail; +}; + +template +struct Types6 { + typedef T1 Head; + typedef Types5 Tail; +}; + +template +struct Types7 { + typedef T1 Head; + typedef Types6 Tail; +}; + +template +struct Types8 { + typedef T1 Head; + typedef Types7 Tail; +}; + +template +struct Types9 { + typedef T1 Head; + typedef Types8 Tail; +}; + +template +struct Types10 { + typedef T1 Head; + typedef Types9 Tail; +}; + +template +struct Types11 { + typedef T1 Head; + typedef Types10 Tail; +}; + +template +struct Types12 { + typedef T1 Head; + typedef Types11 Tail; +}; + +template +struct Types13 { + typedef T1 Head; + typedef Types12 Tail; +}; + +template +struct Types14 { + typedef T1 Head; + typedef Types13 Tail; +}; + +template +struct Types15 { + typedef T1 Head; + typedef Types14 Tail; +}; + +template +struct Types16 { + typedef T1 Head; + typedef Types15 Tail; +}; + +template +struct Types17 { + typedef T1 Head; + typedef Types16 Tail; +}; + +template +struct Types18 { + typedef T1 Head; + typedef Types17 Tail; +}; + +template +struct Types19 { + typedef T1 Head; + typedef Types18 Tail; +}; + +template +struct Types20 { + typedef T1 Head; + typedef Types19 Tail; +}; + +template +struct Types21 { + typedef T1 Head; + typedef Types20 Tail; +}; + +template +struct Types22 { + typedef T1 Head; + typedef Types21 Tail; +}; + +template +struct Types23 { + typedef T1 Head; + typedef Types22 Tail; +}; + +template +struct Types24 { + typedef T1 Head; + typedef Types23 Tail; +}; + +template +struct Types25 { + typedef T1 Head; + typedef Types24 Tail; +}; + +template +struct Types26 { + typedef T1 Head; + typedef Types25 Tail; +}; + +template +struct Types27 { + typedef T1 Head; + typedef Types26 Tail; +}; + +template +struct Types28 { + typedef T1 Head; + typedef Types27 Tail; +}; + +template +struct Types29 { + typedef T1 Head; + typedef Types28 Tail; +}; + +template +struct Types30 { + typedef T1 Head; + typedef Types29 Tail; +}; + +template +struct Types31 { + typedef T1 Head; + typedef Types30 Tail; +}; + +template +struct Types32 { + typedef T1 Head; + typedef Types31 Tail; +}; + +template +struct Types33 { + typedef T1 Head; + typedef Types32 Tail; +}; + +template +struct Types34 { + typedef T1 Head; + typedef Types33 Tail; +}; + +template +struct Types35 { + typedef T1 Head; + typedef Types34 Tail; +}; + +template +struct Types36 { + typedef T1 Head; + typedef Types35 Tail; +}; + +template +struct Types37 { + typedef T1 Head; + typedef Types36 Tail; +}; + +template +struct Types38 { + typedef T1 Head; + typedef Types37 Tail; +}; + +template +struct Types39 { + typedef T1 Head; + typedef Types38 Tail; +}; + +template +struct Types40 { + typedef T1 Head; + typedef Types39 Tail; +}; + +template +struct Types41 { + typedef T1 Head; + typedef Types40 Tail; +}; + +template +struct Types42 { + typedef T1 Head; + typedef Types41 Tail; +}; + +template +struct Types43 { + typedef T1 Head; + typedef Types42 Tail; +}; + +template +struct Types44 { + typedef T1 Head; + typedef Types43 Tail; +}; + +template +struct Types45 { + typedef T1 Head; + typedef Types44 Tail; +}; + +template +struct Types46 { + typedef T1 Head; + typedef Types45 Tail; +}; + +template +struct Types47 { + typedef T1 Head; + typedef Types46 Tail; +}; + +template +struct Types48 { + typedef T1 Head; + typedef Types47 Tail; +}; + +template +struct Types49 { + typedef T1 Head; + typedef Types48 Tail; +}; + +template +struct Types50 { + typedef T1 Head; + typedef Types49 Tail; +}; + + +} // namespace internal + +// We don't want to require the users to write TypesN<...> directly, +// as that would require them to count the length. Types<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Types +// will appear as Types in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Types, and Google Test will translate +// that to TypesN internally to make error messages +// readable. The translation is done by the 'type' member of the +// Types template. +template +struct Types { + typedef internal::Types50 type; +}; + +template <> +struct Types { + typedef internal::Types0 type; +}; +template +struct Types { + typedef internal::Types1 type; +}; +template +struct Types { + typedef internal::Types2 type; +}; +template +struct Types { + typedef internal::Types3 type; +}; +template +struct Types { + typedef internal::Types4 type; +}; +template +struct Types { + typedef internal::Types5 type; +}; +template +struct Types { + typedef internal::Types6 type; +}; +template +struct Types { + typedef internal::Types7 type; +}; +template +struct Types { + typedef internal::Types8 type; +}; +template +struct Types { + typedef internal::Types9 type; +}; +template +struct Types { + typedef internal::Types10 type; +}; +template +struct Types { + typedef internal::Types11 type; +}; +template +struct Types { + typedef internal::Types12 type; +}; +template +struct Types { + typedef internal::Types13 type; +}; +template +struct Types { + typedef internal::Types14 type; +}; +template +struct Types { + typedef internal::Types15 type; +}; +template +struct Types { + typedef internal::Types16 type; +}; +template +struct Types { + typedef internal::Types17 type; +}; +template +struct Types { + typedef internal::Types18 type; +}; +template +struct Types { + typedef internal::Types19 type; +}; +template +struct Types { + typedef internal::Types20 type; +}; +template +struct Types { + typedef internal::Types21 type; +}; +template +struct Types { + typedef internal::Types22 type; +}; +template +struct Types { + typedef internal::Types23 type; +}; +template +struct Types { + typedef internal::Types24 type; +}; +template +struct Types { + typedef internal::Types25 type; +}; +template +struct Types { + typedef internal::Types26 type; +}; +template +struct Types { + typedef internal::Types27 type; +}; +template +struct Types { + typedef internal::Types28 type; +}; +template +struct Types { + typedef internal::Types29 type; +}; +template +struct Types { + typedef internal::Types30 type; +}; +template +struct Types { + typedef internal::Types31 type; +}; +template +struct Types { + typedef internal::Types32 type; +}; +template +struct Types { + typedef internal::Types33 type; +}; +template +struct Types { + typedef internal::Types34 type; +}; +template +struct Types { + typedef internal::Types35 type; +}; +template +struct Types { + typedef internal::Types36 type; +}; +template +struct Types { + typedef internal::Types37 type; +}; +template +struct Types { + typedef internal::Types38 type; +}; +template +struct Types { + typedef internal::Types39 type; +}; +template +struct Types { + typedef internal::Types40 type; +}; +template +struct Types { + typedef internal::Types41 type; +}; +template +struct Types { + typedef internal::Types42 type; +}; +template +struct Types { + typedef internal::Types43 type; +}; +template +struct Types { + typedef internal::Types44 type; +}; +template +struct Types { + typedef internal::Types45 type; +}; +template +struct Types { + typedef internal::Types46 type; +}; +template +struct Types { + typedef internal::Types47 type; +}; +template +struct Types { + typedef internal::Types48 type; +}; +template +struct Types { + typedef internal::Types49 type; +}; + +namespace internal { + +# define GTEST_TEMPLATE_ template class + +// The template "selector" struct TemplateSel is used to +// represent Tmpl, which must be a class template with one type +// parameter, as a type. TemplateSel::Bind::type is defined +// as the type Tmpl. This allows us to actually instantiate the +// template "selected" by TemplateSel. +// +// This trick is necessary for simulating typedef for class templates, +// which C++ doesn't support directly. +template +struct TemplateSel { + template + struct Bind { + typedef Tmpl type; + }; +}; + +# define GTEST_BIND_(TmplSel, T) \ + TmplSel::template Bind::type + +// A unique struct template used as the default value for the +// arguments of class template Templates. This allows us to simulate +// variadic templates (e.g. Templates, Templates, +// and etc), which C++ doesn't support directly. +template +struct NoneT {}; + +// The following family of struct and struct templates are used to +// represent template lists. In particular, TemplatesN represents a list of N templates (T1, T2, ..., and TN). Except +// for Templates0, every struct in the family has two member types: +// Head for the selector of the first template in the list, and Tail +// for the rest of the list. + +// The empty template list. +struct Templates0 {}; + +// Template lists of length 1, 2, 3, and so on. + +template +struct Templates1 { + typedef TemplateSel Head; + typedef Templates0 Tail; +}; +template +struct Templates2 { + typedef TemplateSel Head; + typedef Templates1 Tail; +}; + +template +struct Templates3 { + typedef TemplateSel Head; + typedef Templates2 Tail; +}; + +template +struct Templates4 { + typedef TemplateSel Head; + typedef Templates3 Tail; +}; + +template +struct Templates5 { + typedef TemplateSel Head; + typedef Templates4 Tail; +}; + +template +struct Templates6 { + typedef TemplateSel Head; + typedef Templates5 Tail; +}; + +template +struct Templates7 { + typedef TemplateSel Head; + typedef Templates6 Tail; +}; + +template +struct Templates8 { + typedef TemplateSel Head; + typedef Templates7 Tail; +}; + +template +struct Templates9 { + typedef TemplateSel Head; + typedef Templates8 Tail; +}; + +template +struct Templates10 { + typedef TemplateSel Head; + typedef Templates9 Tail; +}; + +template +struct Templates11 { + typedef TemplateSel Head; + typedef Templates10 Tail; +}; + +template +struct Templates12 { + typedef TemplateSel Head; + typedef Templates11 Tail; +}; + +template +struct Templates13 { + typedef TemplateSel Head; + typedef Templates12 Tail; +}; + +template +struct Templates14 { + typedef TemplateSel Head; + typedef Templates13 Tail; +}; + +template +struct Templates15 { + typedef TemplateSel Head; + typedef Templates14 Tail; +}; + +template +struct Templates16 { + typedef TemplateSel Head; + typedef Templates15 Tail; +}; + +template +struct Templates17 { + typedef TemplateSel Head; + typedef Templates16 Tail; +}; + +template +struct Templates18 { + typedef TemplateSel Head; + typedef Templates17 Tail; +}; + +template +struct Templates19 { + typedef TemplateSel Head; + typedef Templates18 Tail; +}; + +template +struct Templates20 { + typedef TemplateSel Head; + typedef Templates19 Tail; +}; + +template +struct Templates21 { + typedef TemplateSel Head; + typedef Templates20 Tail; +}; + +template +struct Templates22 { + typedef TemplateSel Head; + typedef Templates21 Tail; +}; + +template +struct Templates23 { + typedef TemplateSel Head; + typedef Templates22 Tail; +}; + +template +struct Templates24 { + typedef TemplateSel Head; + typedef Templates23 Tail; +}; + +template +struct Templates25 { + typedef TemplateSel Head; + typedef Templates24 Tail; +}; + +template +struct Templates26 { + typedef TemplateSel Head; + typedef Templates25 Tail; +}; + +template +struct Templates27 { + typedef TemplateSel Head; + typedef Templates26 Tail; +}; + +template +struct Templates28 { + typedef TemplateSel Head; + typedef Templates27 Tail; +}; + +template +struct Templates29 { + typedef TemplateSel Head; + typedef Templates28 Tail; +}; + +template +struct Templates30 { + typedef TemplateSel Head; + typedef Templates29 Tail; +}; + +template +struct Templates31 { + typedef TemplateSel Head; + typedef Templates30 Tail; +}; + +template +struct Templates32 { + typedef TemplateSel Head; + typedef Templates31 Tail; +}; + +template +struct Templates33 { + typedef TemplateSel Head; + typedef Templates32 Tail; +}; + +template +struct Templates34 { + typedef TemplateSel Head; + typedef Templates33 Tail; +}; + +template +struct Templates35 { + typedef TemplateSel Head; + typedef Templates34 Tail; +}; + +template +struct Templates36 { + typedef TemplateSel Head; + typedef Templates35 Tail; +}; + +template +struct Templates37 { + typedef TemplateSel Head; + typedef Templates36 Tail; +}; + +template +struct Templates38 { + typedef TemplateSel Head; + typedef Templates37 Tail; +}; + +template +struct Templates39 { + typedef TemplateSel Head; + typedef Templates38 Tail; +}; + +template +struct Templates40 { + typedef TemplateSel Head; + typedef Templates39 Tail; +}; + +template +struct Templates41 { + typedef TemplateSel Head; + typedef Templates40 Tail; +}; + +template +struct Templates42 { + typedef TemplateSel Head; + typedef Templates41 Tail; +}; + +template +struct Templates43 { + typedef TemplateSel Head; + typedef Templates42 Tail; +}; + +template +struct Templates44 { + typedef TemplateSel Head; + typedef Templates43 Tail; +}; + +template +struct Templates45 { + typedef TemplateSel Head; + typedef Templates44 Tail; +}; + +template +struct Templates46 { + typedef TemplateSel Head; + typedef Templates45 Tail; +}; + +template +struct Templates47 { + typedef TemplateSel Head; + typedef Templates46 Tail; +}; + +template +struct Templates48 { + typedef TemplateSel Head; + typedef Templates47 Tail; +}; + +template +struct Templates49 { + typedef TemplateSel Head; + typedef Templates48 Tail; +}; + +template +struct Templates50 { + typedef TemplateSel Head; + typedef Templates49 Tail; +}; + + +// We don't want to require the users to write TemplatesN<...> directly, +// as that would require them to count the length. Templates<...> is much +// easier to write, but generates horrible messages when there is a +// compiler error, as gcc insists on printing out each template +// argument, even if it has the default value (this means Templates +// will appear as Templates in the compiler +// errors). +// +// Our solution is to combine the best part of the two approaches: a +// user would write Templates, and Google Test will translate +// that to TemplatesN internally to make error messages +// readable. The translation is done by the 'type' member of the +// Templates template. +template +struct Templates { + typedef Templates50 type; +}; + +template <> +struct Templates { + typedef Templates0 type; +}; +template +struct Templates { + typedef Templates1 type; +}; +template +struct Templates { + typedef Templates2 type; +}; +template +struct Templates { + typedef Templates3 type; +}; +template +struct Templates { + typedef Templates4 type; +}; +template +struct Templates { + typedef Templates5 type; +}; +template +struct Templates { + typedef Templates6 type; +}; +template +struct Templates { + typedef Templates7 type; +}; +template +struct Templates { + typedef Templates8 type; +}; +template +struct Templates { + typedef Templates9 type; +}; +template +struct Templates { + typedef Templates10 type; +}; +template +struct Templates { + typedef Templates11 type; +}; +template +struct Templates { + typedef Templates12 type; +}; +template +struct Templates { + typedef Templates13 type; +}; +template +struct Templates { + typedef Templates14 type; +}; +template +struct Templates { + typedef Templates15 type; +}; +template +struct Templates { + typedef Templates16 type; +}; +template +struct Templates { + typedef Templates17 type; +}; +template +struct Templates { + typedef Templates18 type; +}; +template +struct Templates { + typedef Templates19 type; +}; +template +struct Templates { + typedef Templates20 type; +}; +template +struct Templates { + typedef Templates21 type; +}; +template +struct Templates { + typedef Templates22 type; +}; +template +struct Templates { + typedef Templates23 type; +}; +template +struct Templates { + typedef Templates24 type; +}; +template +struct Templates { + typedef Templates25 type; +}; +template +struct Templates { + typedef Templates26 type; +}; +template +struct Templates { + typedef Templates27 type; +}; +template +struct Templates { + typedef Templates28 type; +}; +template +struct Templates { + typedef Templates29 type; +}; +template +struct Templates { + typedef Templates30 type; +}; +template +struct Templates { + typedef Templates31 type; +}; +template +struct Templates { + typedef Templates32 type; +}; +template +struct Templates { + typedef Templates33 type; +}; +template +struct Templates { + typedef Templates34 type; +}; +template +struct Templates { + typedef Templates35 type; +}; +template +struct Templates { + typedef Templates36 type; +}; +template +struct Templates { + typedef Templates37 type; +}; +template +struct Templates { + typedef Templates38 type; +}; +template +struct Templates { + typedef Templates39 type; +}; +template +struct Templates { + typedef Templates40 type; +}; +template +struct Templates { + typedef Templates41 type; +}; +template +struct Templates { + typedef Templates42 type; +}; +template +struct Templates { + typedef Templates43 type; +}; +template +struct Templates { + typedef Templates44 type; +}; +template +struct Templates { + typedef Templates45 type; +}; +template +struct Templates { + typedef Templates46 type; +}; +template +struct Templates { + typedef Templates47 type; +}; +template +struct Templates { + typedef Templates48 type; +}; +template +struct Templates { + typedef Templates49 type; +}; + +// The TypeList template makes it possible to use either a single type +// or a Types<...> list in TYPED_TEST_CASE() and +// INSTANTIATE_TYPED_TEST_CASE_P(). + +template +struct TypeList { + typedef Types1 type; +}; + +template +struct TypeList > { + typedef typename Types::type type; +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_ + +// Due to C++ preprocessor weirdness, we need double indirection to +// concatenate two tokens when one of them is __LINE__. Writing +// +// foo ## __LINE__ +// +// will result in the token foo__LINE__, instead of foo followed by +// the current line number. For more details, see +// http://www.parashift.com/c++-faq-lite/misc-technical-issues.html#faq-39.6 +#define GTEST_CONCAT_TOKEN_(foo, bar) GTEST_CONCAT_TOKEN_IMPL_(foo, bar) +#define GTEST_CONCAT_TOKEN_IMPL_(foo, bar) foo ## bar + +class ProtocolMessage; +namespace proto2 { class Message; } + +namespace testing { + +// Forward declarations. + +class AssertionResult; // Result of an assertion. +class Message; // Represents a failure message. +class Test; // Represents a test. +class TestInfo; // Information about a test. +class TestPartResult; // Result of a test part. +class UnitTest; // A collection of test cases. + +template +::std::string PrintToString(const T& value); + +namespace internal { + +struct TraceInfo; // Information about a trace point. +class ScopedTrace; // Implements scoped trace. +class TestInfoImpl; // Opaque implementation of TestInfo +class UnitTestImpl; // Opaque implementation of UnitTest + +// How many times InitGoogleTest() has been called. +GTEST_API_ extern int g_init_gtest_count; + +// The text used in failure messages to indicate the start of the +// stack trace. +GTEST_API_ extern const char kStackTraceMarker[]; + +// Two overloaded helpers for checking at compile time whether an +// expression is a null pointer literal (i.e. NULL or any 0-valued +// compile-time integral constant). Their return values have +// different sizes, so we can use sizeof() to test which version is +// picked by the compiler. These helpers have no implementations, as +// we only need their signatures. +// +// Given IsNullLiteralHelper(x), the compiler will pick the first +// version if x can be implicitly converted to Secret*, and pick the +// second version otherwise. Since Secret is a secret and incomplete +// type, the only expression a user can write that has type Secret* is +// a null pointer literal. Therefore, we know that x is a null +// pointer literal if and only if the first version is picked by the +// compiler. +char IsNullLiteralHelper(Secret* p); +char (&IsNullLiteralHelper(...))[2]; // NOLINT + +// A compile-time bool constant that is true if and only if x is a +// null pointer literal (i.e. NULL or any 0-valued compile-time +// integral constant). +#ifdef GTEST_ELLIPSIS_NEEDS_POD_ +// We lose support for NULL detection where the compiler doesn't like +// passing non-POD classes through ellipsis (...). +# define GTEST_IS_NULL_LITERAL_(x) false +#else +# define GTEST_IS_NULL_LITERAL_(x) \ + (sizeof(::testing::internal::IsNullLiteralHelper(x)) == 1) +#endif // GTEST_ELLIPSIS_NEEDS_POD_ + +// Appends the user-supplied message to the Google-Test-generated message. +GTEST_API_ std::string AppendUserMessage( + const std::string& gtest_msg, const Message& user_msg); + +#if GTEST_HAS_EXCEPTIONS + +// This exception is thrown by (and only by) a failed Google Test +// assertion when GTEST_FLAG(throw_on_failure) is true (if exceptions +// are enabled). We derive it from std::runtime_error, which is for +// errors presumably detectable only at run time. Since +// std::runtime_error inherits from std::exception, many testing +// frameworks know how to extract and print the message inside it. +class GTEST_API_ GoogleTestFailureException : public ::std::runtime_error { + public: + explicit GoogleTestFailureException(const TestPartResult& failure); +}; + +#endif // GTEST_HAS_EXCEPTIONS + +// A helper class for creating scoped traces in user programs. +class GTEST_API_ ScopedTrace { + public: + // The c'tor pushes the given source file location and message onto + // a trace stack maintained by Google Test. + ScopedTrace(const char* file, int line, const Message& message); + + // The d'tor pops the info pushed by the c'tor. + // + // Note that the d'tor is not virtual in order to be efficient. + // Don't inherit from ScopedTrace! + ~ScopedTrace(); + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ScopedTrace); +} GTEST_ATTRIBUTE_UNUSED_; // A ScopedTrace object does its job in its + // c'tor and d'tor. Therefore it doesn't + // need to be used otherwise. + +// Constructs and returns the message for an equality assertion +// (e.g. ASSERT_EQ, EXPECT_STREQ, etc) failure. +// +// The first four parameters are the expressions used in the assertion +// and their values, as strings. For example, for ASSERT_EQ(foo, bar) +// where foo is 5 and bar is 6, we have: +// +// expected_expression: "foo" +// actual_expression: "bar" +// expected_value: "5" +// actual_value: "6" +// +// The ignoring_case parameter is true iff the assertion is a +// *_STRCASEEQ*. When it's true, the string " (ignoring case)" will +// be inserted into the message. +GTEST_API_ AssertionResult EqFailure(const char* expected_expression, + const char* actual_expression, + const std::string& expected_value, + const std::string& actual_value, + bool ignoring_case); + +// Constructs a failure message for Boolean assertions such as EXPECT_TRUE. +GTEST_API_ std::string GetBoolAssertionFailureMessage( + const AssertionResult& assertion_result, + const char* expression_text, + const char* actual_predicate_value, + const char* expected_predicate_value); + +// This template class represents an IEEE floating-point number +// (either single-precision or double-precision, depending on the +// template parameters). +// +// The purpose of this class is to do more sophisticated number +// comparison. (Due to round-off error, etc, it's very unlikely that +// two floating-points will be equal exactly. Hence a naive +// comparison by the == operation often doesn't work.) +// +// Format of IEEE floating-point: +// +// The most-significant bit being the leftmost, an IEEE +// floating-point looks like +// +// sign_bit exponent_bits fraction_bits +// +// Here, sign_bit is a single bit that designates the sign of the +// number. +// +// For float, there are 8 exponent bits and 23 fraction bits. +// +// For double, there are 11 exponent bits and 52 fraction bits. +// +// More details can be found at +// http://en.wikipedia.org/wiki/IEEE_floating-point_standard. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +template +class FloatingPoint { + public: + // Defines the unsigned integer type that has the same size as the + // floating point number. + typedef typename TypeWithSize::UInt Bits; + + // Constants. + + // # of bits in a number. + static const size_t kBitCount = 8*sizeof(RawType); + + // # of fraction bits in a number. + static const size_t kFractionBitCount = + std::numeric_limits::digits - 1; + + // # of exponent bits in a number. + static const size_t kExponentBitCount = kBitCount - 1 - kFractionBitCount; + + // The mask for the sign bit. + static const Bits kSignBitMask = static_cast(1) << (kBitCount - 1); + + // The mask for the fraction bits. + static const Bits kFractionBitMask = + ~static_cast(0) >> (kExponentBitCount + 1); + + // The mask for the exponent bits. + static const Bits kExponentBitMask = ~(kSignBitMask | kFractionBitMask); + + // How many ULP's (Units in the Last Place) we want to tolerate when + // comparing two numbers. The larger the value, the more error we + // allow. A 0 value means that two numbers must be exactly the same + // to be considered equal. + // + // The maximum error of a single floating-point operation is 0.5 + // units in the last place. On Intel CPU's, all floating-point + // calculations are done with 80-bit precision, while double has 64 + // bits. Therefore, 4 should be enough for ordinary use. + // + // See the following article for more details on ULP: + // http://randomascii.wordpress.com/2012/02/25/comparing-floating-point-numbers-2012-edition/ + static const size_t kMaxUlps = 4; + + // Constructs a FloatingPoint from a raw floating-point number. + // + // On an Intel CPU, passing a non-normalized NAN (Not a Number) + // around may change its bits, although the new value is guaranteed + // to be also a NAN. Therefore, don't expect this constructor to + // preserve the bits in x when x is a NAN. + explicit FloatingPoint(const RawType& x) { u_.value_ = x; } + + // Static methods + + // Reinterprets a bit pattern as a floating-point number. + // + // This function is needed to test the AlmostEquals() method. + static RawType ReinterpretBits(const Bits bits) { + FloatingPoint fp(0); + fp.u_.bits_ = bits; + return fp.u_.value_; + } + + // Returns the floating-point number that represent positive infinity. + static RawType Infinity() { + return ReinterpretBits(kExponentBitMask); + } + + // Returns the maximum representable finite floating-point number. + static RawType Max(); + + // Non-static methods + + // Returns the bits that represents this number. + const Bits &bits() const { return u_.bits_; } + + // Returns the exponent bits of this number. + Bits exponent_bits() const { return kExponentBitMask & u_.bits_; } + + // Returns the fraction bits of this number. + Bits fraction_bits() const { return kFractionBitMask & u_.bits_; } + + // Returns the sign bit of this number. + Bits sign_bit() const { return kSignBitMask & u_.bits_; } + + // Returns true iff this is NAN (not a number). + bool is_nan() const { + // It's a NAN if the exponent bits are all ones and the fraction + // bits are not entirely zeros. + return (exponent_bits() == kExponentBitMask) && (fraction_bits() != 0); + } + + // Returns true iff this number is at most kMaxUlps ULP's away from + // rhs. In particular, this function: + // + // - returns false if either number is (or both are) NAN. + // - treats really large numbers as almost equal to infinity. + // - thinks +0.0 and -0.0 are 0 DLP's apart. + bool AlmostEquals(const FloatingPoint& rhs) const { + // The IEEE standard says that any comparison operation involving + // a NAN must return false. + if (is_nan() || rhs.is_nan()) return false; + + return DistanceBetweenSignAndMagnitudeNumbers(u_.bits_, rhs.u_.bits_) + <= kMaxUlps; + } + + private: + // The data type used to store the actual floating-point number. + union FloatingPointUnion { + RawType value_; // The raw floating-point number. + Bits bits_; // The bits that represent the number. + }; + + // Converts an integer from the sign-and-magnitude representation to + // the biased representation. More precisely, let N be 2 to the + // power of (kBitCount - 1), an integer x is represented by the + // unsigned number x + N. + // + // For instance, + // + // -N + 1 (the most negative number representable using + // sign-and-magnitude) is represented by 1; + // 0 is represented by N; and + // N - 1 (the biggest number representable using + // sign-and-magnitude) is represented by 2N - 1. + // + // Read http://en.wikipedia.org/wiki/Signed_number_representations + // for more details on signed number representations. + static Bits SignAndMagnitudeToBiased(const Bits &sam) { + if (kSignBitMask & sam) { + // sam represents a negative number. + return ~sam + 1; + } else { + // sam represents a positive number. + return kSignBitMask | sam; + } + } + + // Given two numbers in the sign-and-magnitude representation, + // returns the distance between them as an unsigned number. + static Bits DistanceBetweenSignAndMagnitudeNumbers(const Bits &sam1, + const Bits &sam2) { + const Bits biased1 = SignAndMagnitudeToBiased(sam1); + const Bits biased2 = SignAndMagnitudeToBiased(sam2); + return (biased1 >= biased2) ? (biased1 - biased2) : (biased2 - biased1); + } + + FloatingPointUnion u_; +}; + +// We cannot use std::numeric_limits::max() as it clashes with the max() +// macro defined by . +template <> +inline float FloatingPoint::Max() { return FLT_MAX; } +template <> +inline double FloatingPoint::Max() { return DBL_MAX; } + +// Typedefs the instances of the FloatingPoint template class that we +// care to use. +typedef FloatingPoint Float; +typedef FloatingPoint Double; + +// In order to catch the mistake of putting tests that use different +// test fixture classes in the same test case, we need to assign +// unique IDs to fixture classes and compare them. The TypeId type is +// used to hold such IDs. The user should treat TypeId as an opaque +// type: the only operation allowed on TypeId values is to compare +// them for equality using the == operator. +typedef const void* TypeId; + +template +class TypeIdHelper { + public: + // dummy_ must not have a const type. Otherwise an overly eager + // compiler (e.g. MSVC 7.1 & 8.0) may try to merge + // TypeIdHelper::dummy_ for different Ts as an "optimization". + static bool dummy_; +}; + +template +bool TypeIdHelper::dummy_ = false; + +// GetTypeId() returns the ID of type T. Different values will be +// returned for different types. Calling the function twice with the +// same type argument is guaranteed to return the same ID. +template +TypeId GetTypeId() { + // The compiler is required to allocate a different + // TypeIdHelper::dummy_ variable for each T used to instantiate + // the template. Therefore, the address of dummy_ is guaranteed to + // be unique. + return &(TypeIdHelper::dummy_); +} + +// Returns the type ID of ::testing::Test. Always call this instead +// of GetTypeId< ::testing::Test>() to get the type ID of +// ::testing::Test, as the latter may give the wrong result due to a +// suspected linker bug when compiling Google Test as a Mac OS X +// framework. +GTEST_API_ TypeId GetTestTypeId(); + +// Defines the abstract factory interface that creates instances +// of a Test object. +class TestFactoryBase { + public: + virtual ~TestFactoryBase() {} + + // Creates a test instance to run. The instance is both created and destroyed + // within TestInfoImpl::Run() + virtual Test* CreateTest() = 0; + + protected: + TestFactoryBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestFactoryBase); +}; + +// This class provides implementation of TeastFactoryBase interface. +// It is used in TEST and TEST_F macros. +template +class TestFactoryImpl : public TestFactoryBase { + public: + virtual Test* CreateTest() { return new TestClass; } +}; + +#if GTEST_OS_WINDOWS + +// Predicate-formatters for implementing the HRESULT checking macros +// {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED} +// We pass a long instead of HRESULT to avoid causing an +// include dependency for the HRESULT type. +GTEST_API_ AssertionResult IsHRESULTSuccess(const char* expr, + long hr); // NOLINT +GTEST_API_ AssertionResult IsHRESULTFailure(const char* expr, + long hr); // NOLINT + +#endif // GTEST_OS_WINDOWS + +// Types of SetUpTestCase() and TearDownTestCase() functions. +typedef void (*SetUpTestCaseFunc)(); +typedef void (*TearDownTestCaseFunc)(); + +// Creates a new TestInfo object and registers it with Google Test; +// returns the created object. +// +// Arguments: +// +// test_case_name: name of the test case +// name: name of the test +// type_param the name of the test's type parameter, or NULL if +// this is not a typed or a type-parameterized test. +// value_param text representation of the test's value parameter, +// or NULL if this is not a type-parameterized test. +// fixture_class_id: ID of the test fixture class +// set_up_tc: pointer to the function that sets up the test case +// tear_down_tc: pointer to the function that tears down the test case +// factory: pointer to the factory that creates a test object. +// The newly created TestInfo instance will assume +// ownership of the factory object. +GTEST_API_ TestInfo* MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + TypeId fixture_class_id, + SetUpTestCaseFunc set_up_tc, + TearDownTestCaseFunc tear_down_tc, + TestFactoryBase* factory); + +// If *pstr starts with the given prefix, modifies *pstr to be right +// past the prefix and returns true; otherwise leaves *pstr unchanged +// and returns false. None of pstr, *pstr, and prefix can be NULL. +GTEST_API_ bool SkipPrefix(const char* prefix, const char** pstr); + +#if GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// State of the definition of a type-parameterized test case. +class GTEST_API_ TypedTestCasePState { + public: + TypedTestCasePState() : registered_(false) {} + + // Adds the given test name to defined_test_names_ and return true + // if the test case hasn't been registered; otherwise aborts the + // program. + bool AddTestName(const char* file, int line, const char* case_name, + const char* test_name) { + if (registered_) { + fprintf(stderr, "%s Test %s must be defined before " + "REGISTER_TYPED_TEST_CASE_P(%s, ...).\n", + FormatFileLocation(file, line).c_str(), test_name, case_name); + fflush(stderr); + posix::Abort(); + } + defined_test_names_.insert(test_name); + return true; + } + + // Verifies that registered_tests match the test names in + // defined_test_names_; returns registered_tests if successful, or + // aborts the program otherwise. + const char* VerifyRegisteredTestNames( + const char* file, int line, const char* registered_tests); + + private: + bool registered_; + ::std::set defined_test_names_; +}; + +// Skips to the first non-space char after the first comma in 'str'; +// returns NULL if no comma is found in 'str'. +inline const char* SkipComma(const char* str) { + const char* comma = strchr(str, ','); + if (comma == NULL) { + return NULL; + } + while (IsSpace(*(++comma))) {} + return comma; +} + +// Returns the prefix of 'str' before the first comma in it; returns +// the entire string if it contains no comma. +inline std::string GetPrefixUntilComma(const char* str) { + const char* comma = strchr(str, ','); + return comma == NULL ? str : std::string(str, comma); +} + +// TypeParameterizedTest::Register() +// registers a list of type-parameterized tests with Google Test. The +// return value is insignificant - we just need to return something +// such that we can call this function in a namespace scope. +// +// Implementation note: The GTEST_TEMPLATE_ macro declares a template +// template parameter. It's defined in gtest-type-util.h. +template +class TypeParameterizedTest { + public: + // 'index' is the index of the test in the type list 'Types' + // specified in INSTANTIATE_TYPED_TEST_CASE_P(Prefix, TestCase, + // Types). Valid values for 'index' are [0, N - 1] where N is the + // length of Types. + static bool Register(const char* prefix, const char* case_name, + const char* test_names, int index) { + typedef typename Types::Head Type; + typedef Fixture FixtureClass; + typedef typename GTEST_BIND_(TestSel, Type) TestClass; + + // First, registers the first type-parameterized test in the type + // list. + MakeAndRegisterTestInfo( + (std::string(prefix) + (prefix[0] == '\0' ? "" : "/") + case_name + "/" + + StreamableToString(index)).c_str(), + GetPrefixUntilComma(test_names).c_str(), + GetTypeName().c_str(), + NULL, // No value parameter. + GetTypeId(), + TestClass::SetUpTestCase, + TestClass::TearDownTestCase, + new TestFactoryImpl); + + // Next, recurses (at compile time) with the tail of the type list. + return TypeParameterizedTest + ::Register(prefix, case_name, test_names, index + 1); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTest { + public: + static bool Register(const char* /*prefix*/, const char* /*case_name*/, + const char* /*test_names*/, int /*index*/) { + return true; + } +}; + +// TypeParameterizedTestCase::Register() +// registers *all combinations* of 'Tests' and 'Types' with Google +// Test. The return value is insignificant - we just need to return +// something such that we can call this function in a namespace scope. +template +class TypeParameterizedTestCase { + public: + static bool Register(const char* prefix, const char* case_name, + const char* test_names) { + typedef typename Tests::Head Head; + + // First, register the first test in 'Test' for each type in 'Types'. + TypeParameterizedTest::Register( + prefix, case_name, test_names, 0); + + // Next, recurses (at compile time) with the tail of the test list. + return TypeParameterizedTestCase + ::Register(prefix, case_name, SkipComma(test_names)); + } +}; + +// The base case for the compile time recursion. +template +class TypeParameterizedTestCase { + public: + static bool Register(const char* /*prefix*/, const char* /*case_name*/, + const char* /*test_names*/) { + return true; + } +}; + +#endif // GTEST_HAS_TYPED_TEST || GTEST_HAS_TYPED_TEST_P + +// Returns the current OS stack trace as an std::string. +// +// The maximum number of stack frames to be included is specified by +// the gtest_stack_trace_depth flag. The skip_count parameter +// specifies the number of top frames to be skipped, which doesn't +// count against the number of frames to be included. +// +// For example, if Foo() calls Bar(), which in turn calls +// GetCurrentOsStackTraceExceptTop(..., 1), Foo() will be included in +// the trace but Bar() and GetCurrentOsStackTraceExceptTop() won't. +GTEST_API_ std::string GetCurrentOsStackTraceExceptTop( + UnitTest* unit_test, int skip_count); + +// Helpers for suppressing warnings on unreachable code or constant +// condition. + +// Always returns true. +GTEST_API_ bool AlwaysTrue(); + +// Always returns false. +inline bool AlwaysFalse() { return !AlwaysTrue(); } + +// Helper for suppressing false warning from Clang on a const char* +// variable declared in a conditional expression always being NULL in +// the else branch. +struct GTEST_API_ ConstCharPtr { + ConstCharPtr(const char* str) : value(str) {} + operator bool() const { return true; } + const char* value; +}; + +// A simple Linear Congruential Generator for generating random +// numbers with a uniform distribution. Unlike rand() and srand(), it +// doesn't use global state (and therefore can't interfere with user +// code). Unlike rand_r(), it's portable. An LCG isn't very random, +// but it's good enough for our purposes. +class GTEST_API_ Random { + public: + static const UInt32 kMaxRange = 1u << 31; + + explicit Random(UInt32 seed) : state_(seed) {} + + void Reseed(UInt32 seed) { state_ = seed; } + + // Generates a random number from [0, range). Crashes if 'range' is + // 0 or greater than kMaxRange. + UInt32 Generate(UInt32 range); + + private: + UInt32 state_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(Random); +}; + +// Defining a variable of type CompileAssertTypesEqual will cause a +// compiler error iff T1 and T2 are different types. +template +struct CompileAssertTypesEqual; + +template +struct CompileAssertTypesEqual { +}; + +// Removes the reference from a type if it is a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::remove_reference, which is not widely available yet. +template +struct RemoveReference { typedef T type; }; // NOLINT +template +struct RemoveReference { typedef T type; }; // NOLINT + +// A handy wrapper around RemoveReference that works when the argument +// T depends on template parameters. +#define GTEST_REMOVE_REFERENCE_(T) \ + typename ::testing::internal::RemoveReference::type + +// Removes const from a type if it is a const type, otherwise leaves +// it unchanged. This is the same as tr1::remove_const, which is not +// widely available yet. +template +struct RemoveConst { typedef T type; }; // NOLINT +template +struct RemoveConst { typedef T type; }; // NOLINT + +// MSVC 8.0, Sun C++, and IBM XL C++ have a bug which causes the above +// definition to fail to remove the const in 'const int[3]' and 'const +// char[3][4]'. The following specialization works around the bug. +template +struct RemoveConst { + typedef typename RemoveConst::type type[N]; +}; + +#if defined(_MSC_VER) && _MSC_VER < 1400 +// This is the only specialization that allows VC++ 7.1 to remove const in +// 'const int[3] and 'const int[3][4]'. However, it causes trouble with GCC +// and thus needs to be conditionally compiled. +template +struct RemoveConst { + typedef typename RemoveConst::type type[N]; +}; +#endif + +// A handy wrapper around RemoveConst that works when the argument +// T depends on template parameters. +#define GTEST_REMOVE_CONST_(T) \ + typename ::testing::internal::RemoveConst::type + +// Turns const U&, U&, const U, and U all into U. +#define GTEST_REMOVE_REFERENCE_AND_CONST_(T) \ + GTEST_REMOVE_CONST_(GTEST_REMOVE_REFERENCE_(T)) + +// Adds reference to a type if it is not a reference type, +// otherwise leaves it unchanged. This is the same as +// tr1::add_reference, which is not widely available yet. +template +struct AddReference { typedef T& type; }; // NOLINT +template +struct AddReference { typedef T& type; }; // NOLINT + +// A handy wrapper around AddReference that works when the argument T +// depends on template parameters. +#define GTEST_ADD_REFERENCE_(T) \ + typename ::testing::internal::AddReference::type + +// Adds a reference to const on top of T as necessary. For example, +// it transforms +// +// char ==> const char& +// const char ==> const char& +// char& ==> const char& +// const char& ==> const char& +// +// The argument T must depend on some template parameters. +#define GTEST_REFERENCE_TO_CONST_(T) \ + GTEST_ADD_REFERENCE_(const GTEST_REMOVE_REFERENCE_(T)) + +// ImplicitlyConvertible::value is a compile-time bool +// constant that's true iff type From can be implicitly converted to +// type To. +template +class ImplicitlyConvertible { + private: + // We need the following helper functions only for their types. + // They have no implementations. + + // MakeFrom() is an expression whose type is From. We cannot simply + // use From(), as the type From may not have a public default + // constructor. + static From MakeFrom(); + + // These two functions are overloaded. Given an expression + // Helper(x), the compiler will pick the first version if x can be + // implicitly converted to type To; otherwise it will pick the + // second version. + // + // The first version returns a value of size 1, and the second + // version returns a value of size 2. Therefore, by checking the + // size of Helper(x), which can be done at compile time, we can tell + // which version of Helper() is used, and hence whether x can be + // implicitly converted to type To. + static char Helper(To); + static char (&Helper(...))[2]; // NOLINT + + // We have to put the 'public' section after the 'private' section, + // or MSVC refuses to compile the code. + public: + // MSVC warns about implicitly converting from double to int for + // possible loss of data, so we need to temporarily disable the + // warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4244) // Temporarily disables warning 4244. + + static const bool value = + sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; +# pragma warning(pop) // Restores the warning state. +#elif defined(__BORLANDC__) + // C++Builder cannot use member overload resolution during template + // instantiation. The simplest workaround is to use its C++0x type traits + // functions (C++Builder 2009 and above only). + static const bool value = __is_convertible(From, To); +#else + static const bool value = + sizeof(Helper(ImplicitlyConvertible::MakeFrom())) == 1; +#endif // _MSV_VER +}; +template +const bool ImplicitlyConvertible::value; + +// IsAProtocolMessage::value is a compile-time bool constant that's +// true iff T is type ProtocolMessage, proto2::Message, or a subclass +// of those. +template +struct IsAProtocolMessage + : public bool_constant< + ImplicitlyConvertible::value || + ImplicitlyConvertible::value> { +}; + +// When the compiler sees expression IsContainerTest(0), if C is an +// STL-style container class, the first overload of IsContainerTest +// will be viable (since both C::iterator* and C::const_iterator* are +// valid types and NULL can be implicitly converted to them). It will +// be picked over the second overload as 'int' is a perfect match for +// the type of argument 0. If C::iterator or C::const_iterator is not +// a valid type, the first overload is not viable, and the second +// overload will be picked. Therefore, we can determine whether C is +// a container class by checking the type of IsContainerTest(0). +// The value of the expression is insignificant. +// +// Note that we look for both C::iterator and C::const_iterator. The +// reason is that C++ injects the name of a class as a member of the +// class itself (e.g. you can refer to class iterator as either +// 'iterator' or 'iterator::iterator'). If we look for C::iterator +// only, for example, we would mistakenly think that a class named +// iterator is an STL container. +// +// Also note that the simpler approach of overloading +// IsContainerTest(typename C::const_iterator*) and +// IsContainerTest(...) doesn't work with Visual Age C++ and Sun C++. +typedef int IsContainer; +template +IsContainer IsContainerTest(int /* dummy */, + typename C::iterator* /* it */ = NULL, + typename C::const_iterator* /* const_it */ = NULL) { + return 0; +} + +typedef char IsNotContainer; +template +IsNotContainer IsContainerTest(long /* dummy */) { return '\0'; } + +// EnableIf::type is void when 'Cond' is true, and +// undefined when 'Cond' is false. To use SFINAE to make a function +// overload only apply when a particular expression is true, add +// "typename EnableIf::type* = 0" as the last parameter. +template struct EnableIf; +template<> struct EnableIf { typedef void type; }; // NOLINT + +// Utilities for native arrays. + +// ArrayEq() compares two k-dimensional native arrays using the +// elements' operator==, where k can be any integer >= 0. When k is +// 0, ArrayEq() degenerates into comparing a single pair of values. + +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs); + +// This generic version is used when k is 0. +template +inline bool ArrayEq(const T& lhs, const U& rhs) { return lhs == rhs; } + +// This overload is used when k >= 1. +template +inline bool ArrayEq(const T(&lhs)[N], const U(&rhs)[N]) { + return internal::ArrayEq(lhs, N, rhs); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous ArrayEq() function, arrays with different sizes would +// lead to different copies of the template code. +template +bool ArrayEq(const T* lhs, size_t size, const U* rhs) { + for (size_t i = 0; i != size; i++) { + if (!internal::ArrayEq(lhs[i], rhs[i])) + return false; + } + return true; +} + +// Finds the first element in the iterator range [begin, end) that +// equals elem. Element may be a native array type itself. +template +Iter ArrayAwareFind(Iter begin, Iter end, const Element& elem) { + for (Iter it = begin; it != end; ++it) { + if (internal::ArrayEq(*it, elem)) + return it; + } + return end; +} + +// CopyArray() copies a k-dimensional native array using the elements' +// operator=, where k can be any integer >= 0. When k is 0, +// CopyArray() degenerates into copying a single value. + +template +void CopyArray(const T* from, size_t size, U* to); + +// This generic version is used when k is 0. +template +inline void CopyArray(const T& from, U* to) { *to = from; } + +// This overload is used when k >= 1. +template +inline void CopyArray(const T(&from)[N], U(*to)[N]) { + internal::CopyArray(from, N, *to); +} + +// This helper reduces code bloat. If we instead put its logic inside +// the previous CopyArray() function, arrays with different sizes +// would lead to different copies of the template code. +template +void CopyArray(const T* from, size_t size, U* to) { + for (size_t i = 0; i != size; i++) { + internal::CopyArray(from[i], to + i); + } +} + +// The relation between an NativeArray object (see below) and the +// native array it represents. +enum RelationToSource { + kReference, // The NativeArray references the native array. + kCopy // The NativeArray makes a copy of the native array and + // owns the copy. +}; + +// Adapts a native array to a read-only STL-style container. Instead +// of the complete STL container concept, this adaptor only implements +// members useful for Google Mock's container matchers. New members +// should be added as needed. To simplify the implementation, we only +// support Element being a raw type (i.e. having no top-level const or +// reference modifier). It's the client's responsibility to satisfy +// this requirement. Element can be an array type itself (hence +// multi-dimensional arrays are supported). +template +class NativeArray { + public: + // STL-style container typedefs. + typedef Element value_type; + typedef Element* iterator; + typedef const Element* const_iterator; + + // Constructs from a native array. + NativeArray(const Element* array, size_t count, RelationToSource relation) { + Init(array, count, relation); + } + + // Copy constructor. + NativeArray(const NativeArray& rhs) { + Init(rhs.array_, rhs.size_, rhs.relation_to_source_); + } + + ~NativeArray() { + // Ensures that the user doesn't instantiate NativeArray with a + // const or reference type. + static_cast(StaticAssertTypeEqHelper()); + if (relation_to_source_ == kCopy) + delete[] array_; + } + + // STL-style container methods. + size_t size() const { return size_; } + const_iterator begin() const { return array_; } + const_iterator end() const { return array_ + size_; } + bool operator==(const NativeArray& rhs) const { + return size() == rhs.size() && + ArrayEq(begin(), size(), rhs.begin()); + } + + private: + // Initializes this object; makes a copy of the input array if + // 'relation' is kCopy. + void Init(const Element* array, size_t a_size, RelationToSource relation) { + if (relation == kReference) { + array_ = array; + } else { + Element* const copy = new Element[a_size]; + CopyArray(array, a_size, copy); + array_ = copy; + } + size_ = a_size; + relation_to_source_ = relation; + } + + const Element* array_; + size_t size_; + RelationToSource relation_to_source_; + + GTEST_DISALLOW_ASSIGN_(NativeArray); +}; + +} // namespace internal +} // namespace testing + +#define GTEST_MESSAGE_AT_(file, line, message, result_type) \ + ::testing::internal::AssertHelper(result_type, file, line, message) \ + = ::testing::Message() + +#define GTEST_MESSAGE_(message, result_type) \ + GTEST_MESSAGE_AT_(__FILE__, __LINE__, message, result_type) + +#define GTEST_FATAL_FAILURE_(message) \ + return GTEST_MESSAGE_(message, ::testing::TestPartResult::kFatalFailure) + +#define GTEST_NONFATAL_FAILURE_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kNonFatalFailure) + +#define GTEST_SUCCESS_(message) \ + GTEST_MESSAGE_(message, ::testing::TestPartResult::kSuccess) + +// Suppresses MSVC warnings 4072 (unreachable code) for the code following +// statement if it returns or throws (or doesn't return or throw in some +// situations). +#define GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) \ + if (::testing::internal::AlwaysTrue()) { statement; } + +#define GTEST_TEST_THROW_(statement, expected_exception, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::ConstCharPtr gtest_msg = "") { \ + bool gtest_caught_expected = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (expected_exception const&) { \ + gtest_caught_expected = true; \ + } \ + catch (...) { \ + gtest_msg.value = \ + "Expected: " #statement " throws an exception of type " \ + #expected_exception ".\n Actual: it throws a different type."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + if (!gtest_caught_expected) { \ + gtest_msg.value = \ + "Expected: " #statement " throws an exception of type " \ + #expected_exception ".\n Actual: it throws nothing."; \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testthrow_, __LINE__): \ + fail(gtest_msg.value) + +#define GTEST_TEST_NO_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (...) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnothrow_, __LINE__): \ + fail("Expected: " #statement " doesn't throw an exception.\n" \ + " Actual: it throws.") + +#define GTEST_TEST_ANY_THROW_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + bool gtest_caught_any = false; \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } \ + catch (...) { \ + gtest_caught_any = true; \ + } \ + if (!gtest_caught_any) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testanythrow_, __LINE__): \ + fail("Expected: " #statement " throws an exception.\n" \ + " Actual: it doesn't.") + + +// Implements Boolean test assertions such as EXPECT_TRUE. expression can be +// either a boolean expression or an AssertionResult. text is a textual +// represenation of expression as it was passed into the EXPECT_TRUE. +#define GTEST_TEST_BOOLEAN_(expression, text, actual, expected, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar_ = \ + ::testing::AssertionResult(expression)) \ + ; \ + else \ + fail(::testing::internal::GetBoolAssertionFailureMessage(\ + gtest_ar_, text, #actual, #expected).c_str()) + +#define GTEST_TEST_NO_FATAL_FAILURE_(statement, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + ::testing::internal::HasNewFatalFailureHelper gtest_fatal_failure_checker; \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + if (gtest_fatal_failure_checker.has_new_fatal_failure()) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__); \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_testnofatal_, __LINE__): \ + fail("Expected: " #statement " doesn't generate new fatal " \ + "failures in the current thread.\n" \ + " Actual: it does.") + +// Expands to the name of the class that implements the given test. +#define GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + test_case_name##_##test_name##_Test + +// Helper macro for defining tests. +#define GTEST_TEST_(test_case_name, test_name, parent_class, parent_id)\ +class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) : public parent_class {\ + public:\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {}\ + private:\ + virtual void TestBody();\ + static ::testing::TestInfo* const test_info_ GTEST_ATTRIBUTE_UNUSED_;\ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name));\ +};\ +\ +::testing::TestInfo* const GTEST_TEST_CLASS_NAME_(test_case_name, test_name)\ + ::test_info_ =\ + ::testing::internal::MakeAndRegisterTestInfo(\ + #test_case_name, #test_name, NULL, NULL, \ + (parent_id), \ + parent_class::SetUpTestCase, \ + parent_class::TearDownTestCase, \ + new ::testing::internal::TestFactoryImpl<\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>);\ +void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_INTERNAL_H_ +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines the public API for death tests. It is +// #included by gtest.h so a user doesn't need to include this +// directly. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ + +// Copyright 2005, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: wan@google.com (Zhanyong Wan), eefacm@gmail.com (Sean Mcafee) +// +// The Google C++ Testing Framework (Google Test) +// +// This header file defines internal utilities needed for implementing +// death tests. They are subject to change without notice. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + + +#include + +namespace testing { +namespace internal { + +GTEST_DECLARE_string_(internal_run_death_test); + +// Names of the flags (needed for parsing Google Test flags). +const char kDeathTestStyleFlag[] = "death_test_style"; +const char kDeathTestUseFork[] = "death_test_use_fork"; +const char kInternalRunDeathTestFlag[] = "internal_run_death_test"; + +#if GTEST_HAS_DEATH_TEST + +// DeathTest is a class that hides much of the complexity of the +// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method +// returns a concrete class that depends on the prevailing death test +// style, as defined by the --gtest_death_test_style and/or +// --gtest_internal_run_death_test flags. + +// In describing the results of death tests, these terms are used with +// the corresponding definitions: +// +// exit status: The integer exit information in the format specified +// by wait(2) +// exit code: The integer code passed to exit(3), _exit(2), or +// returned from main() +class GTEST_API_ DeathTest { + public: + // Create returns false if there was an error determining the + // appropriate action to take for the current death test; for example, + // if the gtest_death_test_style flag is set to an invalid value. + // The LastMessage method will return a more detailed message in that + // case. Otherwise, the DeathTest pointer pointed to by the "test" + // argument is set. If the death test should be skipped, the pointer + // is set to NULL; otherwise, it is set to the address of a new concrete + // DeathTest object that controls the execution of the current test. + static bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); + DeathTest(); + virtual ~DeathTest() { } + + // A helper class that aborts a death test when it's deleted. + class ReturnSentinel { + public: + explicit ReturnSentinel(DeathTest* test) : test_(test) { } + ~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); } + private: + DeathTest* const test_; + GTEST_DISALLOW_COPY_AND_ASSIGN_(ReturnSentinel); + } GTEST_ATTRIBUTE_UNUSED_; + + // An enumeration of possible roles that may be taken when a death + // test is encountered. EXECUTE means that the death test logic should + // be executed immediately. OVERSEE means that the program should prepare + // the appropriate environment for a child process to execute the death + // test, then wait for it to complete. + enum TestRole { OVERSEE_TEST, EXECUTE_TEST }; + + // An enumeration of the three reasons that a test might be aborted. + enum AbortReason { + TEST_ENCOUNTERED_RETURN_STATEMENT, + TEST_THREW_EXCEPTION, + TEST_DID_NOT_DIE + }; + + // Assumes one of the above roles. + virtual TestRole AssumeRole() = 0; + + // Waits for the death test to finish and returns its status. + virtual int Wait() = 0; + + // Returns true if the death test passed; that is, the test process + // exited during the test, its exit status matches a user-supplied + // predicate, and its stderr output matches a user-supplied regular + // expression. + // The user-supplied predicate may be a macro expression rather + // than a function pointer or functor, or else Wait and Passed could + // be combined. + virtual bool Passed(bool exit_status_ok) = 0; + + // Signals that the death test did not die as expected. + virtual void Abort(AbortReason reason) = 0; + + // Returns a human-readable outcome message regarding the outcome of + // the last death test. + static const char* LastMessage(); + + static void set_last_death_test_message(const std::string& message); + + private: + // A string containing a description of the outcome of the last death test. + static std::string last_death_test_message_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(DeathTest); +}; + +// Factory interface for death tests. May be mocked out for testing. +class DeathTestFactory { + public: + virtual ~DeathTestFactory() { } + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test) = 0; +}; + +// A concrete DeathTestFactory implementation for normal use. +class DefaultDeathTestFactory : public DeathTestFactory { + public: + virtual bool Create(const char* statement, const RE* regex, + const char* file, int line, DeathTest** test); +}; + +// Returns true if exit_status describes a process that was terminated +// by a signal, or exited normally with a nonzero exit code. +GTEST_API_ bool ExitedUnsuccessfully(int exit_status); + +// Traps C++ exceptions escaping statement and reports them as test +// failures. Note that trapping SEH exceptions is not implemented here. +# if GTEST_HAS_EXCEPTIONS +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + try { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } catch (const ::std::exception& gtest_exception) { \ + fprintf(\ + stderr, \ + "\n%s: Caught std::exception-derived exception escaping the " \ + "death test statement. Exception message: %s\n", \ + ::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \ + gtest_exception.what()); \ + fflush(stderr); \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } catch (...) { \ + death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \ + } + +# else +# define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement) + +# endif + +// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*, +// ASSERT_EXIT*, and EXPECT_EXIT*. +# define GTEST_DEATH_TEST_(statement, predicate, regex, fail) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + const ::testing::internal::RE& gtest_regex = (regex); \ + ::testing::internal::DeathTest* gtest_dt; \ + if (!::testing::internal::DeathTest::Create(#statement, >est_regex, \ + __FILE__, __LINE__, >est_dt)) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + if (gtest_dt != NULL) { \ + ::testing::internal::scoped_ptr< ::testing::internal::DeathTest> \ + gtest_dt_ptr(gtest_dt); \ + switch (gtest_dt->AssumeRole()) { \ + case ::testing::internal::DeathTest::OVERSEE_TEST: \ + if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \ + goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \ + } \ + break; \ + case ::testing::internal::DeathTest::EXECUTE_TEST: { \ + ::testing::internal::DeathTest::ReturnSentinel \ + gtest_sentinel(gtest_dt); \ + GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \ + gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \ + break; \ + } \ + default: \ + break; \ + } \ + } \ + } else \ + GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__): \ + fail(::testing::internal::DeathTest::LastMessage()) +// The symbol "fail" here expands to something into which a message +// can be streamed. + +// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in +// NDEBUG mode. In this case we need the statements to be executed, the regex is +// ignored, and the macro must accept a streamed message even though the message +// is never printed. +# define GTEST_EXECUTE_STATEMENT_(statement, regex) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + } else \ + ::testing::Message() + +// A class representing the parsed contents of the +// --gtest_internal_run_death_test flag, as it existed when +// RUN_ALL_TESTS was called. +class InternalRunDeathTestFlag { + public: + InternalRunDeathTestFlag(const std::string& a_file, + int a_line, + int an_index, + int a_write_fd) + : file_(a_file), line_(a_line), index_(an_index), + write_fd_(a_write_fd) {} + + ~InternalRunDeathTestFlag() { + if (write_fd_ >= 0) + posix::Close(write_fd_); + } + + const std::string& file() const { return file_; } + int line() const { return line_; } + int index() const { return index_; } + int write_fd() const { return write_fd_; } + + private: + std::string file_; + int line_; + int index_; + int write_fd_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(InternalRunDeathTestFlag); +}; + +// Returns a newly created InternalRunDeathTestFlag object with fields +// initialized from the GTEST_FLAG(internal_run_death_test) flag if +// the flag is specified; otherwise returns NULL. +InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag(); + +#else // GTEST_HAS_DEATH_TEST + +// This macro is used for implementing macros such as +// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where +// death tests are not supported. Those macros must compile on such systems +// iff EXPECT_DEATH and ASSERT_DEATH compile with the same parameters on +// systems that support death tests. This allows one to write such a macro +// on a system that does not support death tests and be sure that it will +// compile on a death-test supporting system. +// +// Parameters: +// statement - A statement that a macro such as EXPECT_DEATH would test +// for program termination. This macro has to make sure this +// statement is compiled but not executed, to ensure that +// EXPECT_DEATH_IF_SUPPORTED compiles with a certain +// parameter iff EXPECT_DEATH compiles with it. +// regex - A regex that a macro such as EXPECT_DEATH would use to test +// the output of statement. This parameter has to be +// compiled but not evaluated by this macro, to ensure that +// this macro only accepts expressions that a macro such as +// EXPECT_DEATH would accept. +// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED +// and a return statement for ASSERT_DEATH_IF_SUPPORTED. +// This ensures that ASSERT_DEATH_IF_SUPPORTED will not +// compile inside functions where ASSERT_DEATH doesn't +// compile. +// +// The branch that has an always false condition is used to ensure that +// statement and regex are compiled (and thus syntactically correct) but +// never executed. The unreachable code macro protects the terminator +// statement from generating an 'unreachable code' warning in case +// statement unconditionally returns or throws. The Message constructor at +// the end allows the syntax of streaming additional messages into the +// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH. +# define GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, terminator) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (::testing::internal::AlwaysTrue()) { \ + GTEST_LOG_(WARNING) \ + << "Death tests are not supported on this platform.\n" \ + << "Statement '" #statement "' cannot be verified."; \ + } else if (::testing::internal::AlwaysFalse()) { \ + ::testing::internal::RE::PartialMatch(".*", (regex)); \ + GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \ + terminator; \ + } else \ + ::testing::Message() + +#endif // GTEST_HAS_DEATH_TEST + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_ + +namespace testing { + +// This flag controls the style of death tests. Valid values are "threadsafe", +// meaning that the death test child process will re-execute the test binary +// from the start, running only a single death test, or "fast", +// meaning that the child process will execute the test logic immediately +// after forking. +GTEST_DECLARE_string_(death_test_style); + +#if GTEST_HAS_DEATH_TEST + +namespace internal { + +// Returns a Boolean value indicating whether the caller is currently +// executing in the context of the death test child process. Tools such as +// Valgrind heap checkers may need this to modify their behavior in death +// tests. IMPORTANT: This is an internal utility. Using it may break the +// implementation of death tests. User code MUST NOT use it. +GTEST_API_ bool InDeathTestChild(); + +} // namespace internal + +// The following macros are useful for writing death tests. + +// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is +// executed: +// +// 1. It generates a warning if there is more than one active +// thread. This is because it's safe to fork() or clone() only +// when there is a single thread. +// +// 2. The parent process clone()s a sub-process and runs the death +// test in it; the sub-process exits with code 0 at the end of the +// death test, if it hasn't exited already. +// +// 3. The parent process waits for the sub-process to terminate. +// +// 4. The parent process checks the exit code and error message of +// the sub-process. +// +// Examples: +// +// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number"); +// for (int i = 0; i < 5; i++) { +// EXPECT_DEATH(server.ProcessRequest(i), +// "Invalid request .* in ProcessRequest()") +// << "Failed to die on request " << i; +// } +// +// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting"); +// +// bool KilledBySIGHUP(int exit_code) { +// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP; +// } +// +// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!"); +// +// On the regular expressions used in death tests: +// +// On POSIX-compliant systems (*nix), we use the library, +// which uses the POSIX extended regex syntax. +// +// On other platforms (e.g. Windows), we only support a simple regex +// syntax implemented as part of Google Test. This limited +// implementation should be enough most of the time when writing +// death tests; though it lacks many features you can find in PCRE +// or POSIX extended regex syntax. For example, we don't support +// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and +// repetition count ("x{5,7}"), among others. +// +// Below is the syntax that we do support. We chose it to be a +// subset of both PCRE and POSIX extended regex, so it's easy to +// learn wherever you come from. In the following: 'A' denotes a +// literal character, period (.), or a single \\ escape sequence; +// 'x' and 'y' denote regular expressions; 'm' and 'n' are for +// natural numbers. +// +// c matches any literal character c +// \\d matches any decimal digit +// \\D matches any character that's not a decimal digit +// \\f matches \f +// \\n matches \n +// \\r matches \r +// \\s matches any ASCII whitespace, including \n +// \\S matches any character that's not a whitespace +// \\t matches \t +// \\v matches \v +// \\w matches any letter, _, or decimal digit +// \\W matches any character that \\w doesn't match +// \\c matches any literal character c, which must be a punctuation +// . matches any single character except \n +// A? matches 0 or 1 occurrences of A +// A* matches 0 or many occurrences of A +// A+ matches 1 or many occurrences of A +// ^ matches the beginning of a string (not that of each line) +// $ matches the end of a string (not that of each line) +// xy matches x followed by y +// +// If you accidentally use PCRE or POSIX extended regex features +// not implemented by us, you will get a run-time failure. In that +// case, please try to rewrite your regular expression within the +// above syntax. +// +// This implementation is *not* meant to be as highly tuned or robust +// as a compiled regex library, but should perform well enough for a +// death test, which already incurs significant overhead by launching +// a child process. +// +// Known caveats: +// +// A "threadsafe" style death test obtains the path to the test +// program from argv[0] and re-executes it in the sub-process. For +// simplicity, the current implementation doesn't search the PATH +// when launching the sub-process. This means that the user must +// invoke the test program via a path that contains at least one +// path separator (e.g. path/to/foo_test and +// /absolute/path/to/bar_test are fine, but foo_test is not). This +// is rarely a problem as people usually don't put the test binary +// directory in PATH. +// +// TODO(wan@google.com): make thread-safe death tests search the PATH. + +// Asserts that a given statement causes the program to exit, with an +// integer exit status that satisfies predicate, and emitting error output +// that matches regex. +# define ASSERT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_FATAL_FAILURE_) + +// Like ASSERT_EXIT, but continues on to successive tests in the +// test case, if any: +# define EXPECT_EXIT(statement, predicate, regex) \ + GTEST_DEATH_TEST_(statement, predicate, regex, GTEST_NONFATAL_FAILURE_) + +// Asserts that a given statement causes the program to exit, either by +// explicitly exiting with a nonzero exit code or being killed by a +// signal, and emitting error output that matches regex. +# define ASSERT_DEATH(statement, regex) \ + ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Like ASSERT_DEATH, but continues on to successive tests in the +// test case, if any: +# define EXPECT_DEATH(statement, regex) \ + EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, regex) + +// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*: + +// Tests that an exit code describes a normal exit with a given exit code. +class GTEST_API_ ExitedWithCode { + public: + explicit ExitedWithCode(int exit_code); + bool operator()(int exit_status) const; + private: + // No implementation - assignment is unsupported. + void operator=(const ExitedWithCode& other); + + const int exit_code_; +}; + +# if !GTEST_OS_WINDOWS +// Tests that an exit code describes an exit due to termination by a +// given signal. +class GTEST_API_ KilledBySignal { + public: + explicit KilledBySignal(int signum); + bool operator()(int exit_status) const; + private: + const int signum_; +}; +# endif // !GTEST_OS_WINDOWS + +// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode. +// The death testing framework causes this to have interesting semantics, +// since the sideeffects of the call are only visible in opt mode, and not +// in debug mode. +// +// In practice, this can be used to test functions that utilize the +// LOG(DFATAL) macro using the following style: +// +// int DieInDebugOr12(int* sideeffect) { +// if (sideeffect) { +// *sideeffect = 12; +// } +// LOG(DFATAL) << "death"; +// return 12; +// } +// +// TEST(TestCase, TestDieOr12WorksInDgbAndOpt) { +// int sideeffect = 0; +// // Only asserts in dbg. +// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death"); +// +// #ifdef NDEBUG +// // opt-mode has sideeffect visible. +// EXPECT_EQ(12, sideeffect); +// #else +// // dbg-mode no visible sideeffect. +// EXPECT_EQ(0, sideeffect); +// #endif +// } +// +// This will assert that DieInDebugReturn12InOpt() crashes in debug +// mode, usually due to a DCHECK or LOG(DFATAL), but returns the +// appropriate fallback value (12 in this case) in opt mode. If you +// need to test that a function has appropriate side-effects in opt +// mode, include assertions against the side-effects. A general +// pattern for this is: +// +// EXPECT_DEBUG_DEATH({ +// // Side-effects here will have an effect after this statement in +// // opt mode, but none in debug mode. +// EXPECT_EQ(12, DieInDebugOr12(&sideeffect)); +// }, "death"); +// +# ifdef NDEBUG + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + GTEST_EXECUTE_STATEMENT_(statement, regex) + +# else + +# define EXPECT_DEBUG_DEATH(statement, regex) \ + EXPECT_DEATH(statement, regex) + +# define ASSERT_DEBUG_DEATH(statement, regex) \ + ASSERT_DEATH(statement, regex) + +# endif // NDEBUG for EXPECT_DEBUG_DEATH +#endif // GTEST_HAS_DEATH_TEST + +// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and +// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if +// death tests are supported; otherwise they just issue a warning. This is +// useful when you are combining death test assertions with normal test +// assertions in one test. +#if GTEST_HAS_DEATH_TEST +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + EXPECT_DEATH(statement, regex) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + ASSERT_DEATH(statement, regex) +#else +# define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, ) +# define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \ + GTEST_UNSUPPORTED_DEATH_TEST_(statement, regex, return) +#endif + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_ +// This file was GENERATED by command: +// pump.py gtest-param-test.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: vladl@google.com (Vlad Losev) +// +// Macros and functions for implementing parameterized tests +// in Google C++ Testing Framework (Google Test) +// +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +#ifndef GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ + + +// Value-parameterized tests allow you to test your code with different +// parameters without writing multiple copies of the same test. +// +// Here is how you use value-parameterized tests: + +#if 0 + +// To write value-parameterized tests, first you should define a fixture +// class. It is usually derived from testing::TestWithParam (see below for +// another inheritance scheme that's sometimes useful in more complicated +// class hierarchies), where the type of your parameter values. +// TestWithParam is itself derived from testing::Test. T can be any +// copyable type. If it's a raw pointer, you are responsible for managing the +// lifespan of the pointed values. + +class FooTest : public ::testing::TestWithParam { + // You can implement all the usual class fixture members here. +}; + +// Then, use the TEST_P macro to define as many parameterized tests +// for this fixture as you want. The _P suffix is for "parameterized" +// or "pattern", whichever you prefer to think. + +TEST_P(FooTest, DoesBlah) { + // Inside a test, access the test parameter with the GetParam() method + // of the TestWithParam class: + EXPECT_TRUE(foo.Blah(GetParam())); + ... +} + +TEST_P(FooTest, HasBlahBlah) { + ... +} + +// Finally, you can use INSTANTIATE_TEST_CASE_P to instantiate the test +// case with any set of parameters you want. Google Test defines a number +// of functions for generating test parameters. They return what we call +// (surprise!) parameter generators. Here is a summary of them, which +// are all in the testing namespace: +// +// +// Range(begin, end [, step]) - Yields values {begin, begin+step, +// begin+step+step, ...}. The values do not +// include end. step defaults to 1. +// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}. +// ValuesIn(container) - Yields values from a C-style array, an STL +// ValuesIn(begin,end) container, or an iterator range [begin, end). +// Bool() - Yields sequence {false, true}. +// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product +// for the math savvy) of the values generated +// by the N generators. +// +// For more details, see comments at the definitions of these functions below +// in this file. +// +// The following statement will instantiate tests from the FooTest test case +// each with parameter values "meeny", "miny", and "moe". + +INSTANTIATE_TEST_CASE_P(InstantiationName, + FooTest, + Values("meeny", "miny", "moe")); + +// To distinguish different instances of the pattern, (yes, you +// can instantiate it more then once) the first argument to the +// INSTANTIATE_TEST_CASE_P macro is a prefix that will be added to the +// actual test case name. Remember to pick unique prefixes for different +// instantiations. The tests from the instantiation above will have +// these names: +// +// * InstantiationName/FooTest.DoesBlah/0 for "meeny" +// * InstantiationName/FooTest.DoesBlah/1 for "miny" +// * InstantiationName/FooTest.DoesBlah/2 for "moe" +// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny" +// * InstantiationName/FooTest.HasBlahBlah/1 for "miny" +// * InstantiationName/FooTest.HasBlahBlah/2 for "moe" +// +// You can use these names in --gtest_filter. +// +// This statement will instantiate all tests from FooTest again, each +// with parameter values "cat" and "dog": + +const char* pets[] = {"cat", "dog"}; +INSTANTIATE_TEST_CASE_P(AnotherInstantiationName, FooTest, ValuesIn(pets)); + +// The tests from the instantiation above will have these names: +// +// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog" +// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat" +// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog" +// +// Please note that INSTANTIATE_TEST_CASE_P will instantiate all tests +// in the given test case, whether their definitions come before or +// AFTER the INSTANTIATE_TEST_CASE_P statement. +// +// Please also note that generator expressions (including parameters to the +// generators) are evaluated in InitGoogleTest(), after main() has started. +// This allows the user on one hand, to adjust generator parameters in order +// to dynamically determine a set of tests to run and on the other hand, +// give the user a chance to inspect the generated tests with Google Test +// reflection API before RUN_ALL_TESTS() is executed. +// +// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc +// for more examples. +// +// In the future, we plan to publish the API for defining new parameter +// generators. But for now this interface remains part of the internal +// implementation and is subject to change. +// +// +// A parameterized test fixture must be derived from testing::Test and from +// testing::WithParamInterface, where T is the type of the parameter +// values. Inheriting from TestWithParam satisfies that requirement because +// TestWithParam inherits from both Test and WithParamInterface. In more +// complicated hierarchies, however, it is occasionally useful to inherit +// separately from Test and WithParamInterface. For example: + +class BaseTest : public ::testing::Test { + // You can inherit all the usual members for a non-parameterized test + // fixture here. +}; + +class DerivedTest : public BaseTest, public ::testing::WithParamInterface { + // The usual test fixture members go here too. +}; + +TEST_F(BaseTest, HasFoo) { + // This is an ordinary non-parameterized test. +} + +TEST_P(DerivedTest, DoesBlah) { + // GetParam works just the same here as if you inherit from TestWithParam. + EXPECT_TRUE(foo.Blah(GetParam())); +} + +#endif // 0 + + +#if !GTEST_OS_SYMBIAN +# include +#endif + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// Type and function utilities for implementing parameterized tests. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ + +#include +#include +#include + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. +// Copyright 2003 Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Authors: Dan Egnor (egnor@google.com) +// +// A "smart" pointer type with reference tracking. Every pointer to a +// particular object is kept on a circular linked list. When the last pointer +// to an object is destroyed or reassigned, the object is deleted. +// +// Used properly, this deletes the object when the last reference goes away. +// There are several caveats: +// - Like all reference counting schemes, cycles lead to leaks. +// - Each smart pointer is actually two pointers (8 bytes instead of 4). +// - Every time a pointer is assigned, the entire list of pointers to that +// object is traversed. This class is therefore NOT SUITABLE when there +// will often be more than two or three pointers to a particular object. +// - References are only tracked as long as linked_ptr<> objects are copied. +// If a linked_ptr<> is converted to a raw pointer and back, BAD THINGS +// will happen (double deletion). +// +// A good use of this class is storing object references in STL containers. +// You can safely put linked_ptr<> in a vector<>. +// Other uses may not be as good. +// +// Note: If you use an incomplete type with linked_ptr<>, the class +// *containing* linked_ptr<> must have a constructor and destructor (even +// if they do nothing!). +// +// Bill Gibbons suggested we use something like this. +// +// Thread Safety: +// Unlike other linked_ptr implementations, in this implementation +// a linked_ptr object is thread-safe in the sense that: +// - it's safe to copy linked_ptr objects concurrently, +// - it's safe to copy *from* a linked_ptr and read its underlying +// raw pointer (e.g. via get()) concurrently, and +// - it's safe to write to two linked_ptrs that point to the same +// shared object concurrently. +// TODO(wan@google.com): rename this to safe_linked_ptr to avoid +// confusion with normal linked_ptr. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ + +#include +#include + + +namespace testing { +namespace internal { + +// Protects copying of all linked_ptr objects. +GTEST_API_ GTEST_DECLARE_STATIC_MUTEX_(g_linked_ptr_mutex); + +// This is used internally by all instances of linked_ptr<>. It needs to be +// a non-template class because different types of linked_ptr<> can refer to +// the same object (linked_ptr(obj) vs linked_ptr(obj)). +// So, it needs to be possible for different types of linked_ptr to participate +// in the same circular linked list, so we need a single class type here. +// +// DO NOT USE THIS CLASS DIRECTLY YOURSELF. Use linked_ptr. +class linked_ptr_internal { + public: + // Create a new circle that includes only this instance. + void join_new() { + next_ = this; + } + + // Many linked_ptr operations may change p.link_ for some linked_ptr + // variable p in the same circle as this object. Therefore we need + // to prevent two such operations from occurring concurrently. + // + // Note that different types of linked_ptr objects can coexist in a + // circle (e.g. linked_ptr, linked_ptr, and + // linked_ptr). Therefore we must use a single mutex to + // protect all linked_ptr objects. This can create serious + // contention in production code, but is acceptable in a testing + // framework. + + // Join an existing circle. + void join(linked_ptr_internal const* ptr) + GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) { + MutexLock lock(&g_linked_ptr_mutex); + + linked_ptr_internal const* p = ptr; + while (p->next_ != ptr) p = p->next_; + p->next_ = this; + next_ = ptr; + } + + // Leave whatever circle we're part of. Returns true if we were the + // last member of the circle. Once this is done, you can join() another. + bool depart() + GTEST_LOCK_EXCLUDED_(g_linked_ptr_mutex) { + MutexLock lock(&g_linked_ptr_mutex); + + if (next_ == this) return true; + linked_ptr_internal const* p = next_; + while (p->next_ != this) p = p->next_; + p->next_ = next_; + return false; + } + + private: + mutable linked_ptr_internal const* next_; +}; + +template +class linked_ptr { + public: + typedef T element_type; + + // Take over ownership of a raw pointer. This should happen as soon as + // possible after the object is created. + explicit linked_ptr(T* ptr = NULL) { capture(ptr); } + ~linked_ptr() { depart(); } + + // Copy an existing linked_ptr<>, adding ourselves to the list of references. + template linked_ptr(linked_ptr const& ptr) { copy(&ptr); } + linked_ptr(linked_ptr const& ptr) { // NOLINT + assert(&ptr != this); + copy(&ptr); + } + + // Assignment releases the old value and acquires the new. + template linked_ptr& operator=(linked_ptr const& ptr) { + depart(); + copy(&ptr); + return *this; + } + + linked_ptr& operator=(linked_ptr const& ptr) { + if (&ptr != this) { + depart(); + copy(&ptr); + } + return *this; + } + + // Smart pointer members. + void reset(T* ptr = NULL) { + depart(); + capture(ptr); + } + T* get() const { return value_; } + T* operator->() const { return value_; } + T& operator*() const { return *value_; } + + bool operator==(T* p) const { return value_ == p; } + bool operator!=(T* p) const { return value_ != p; } + template + bool operator==(linked_ptr const& ptr) const { + return value_ == ptr.get(); + } + template + bool operator!=(linked_ptr const& ptr) const { + return value_ != ptr.get(); + } + + private: + template + friend class linked_ptr; + + T* value_; + linked_ptr_internal link_; + + void depart() { + if (link_.depart()) delete value_; + } + + void capture(T* ptr) { + value_ = ptr; + link_.join_new(); + } + + template void copy(linked_ptr const* ptr) { + value_ = ptr->get(); + if (value_) + link_.join(&ptr->link_); + else + link_.join_new(); + } +}; + +template inline +bool operator==(T* ptr, const linked_ptr& x) { + return ptr == x.get(); +} + +template inline +bool operator!=(T* ptr, const linked_ptr& x) { + return ptr != x.get(); +} + +// A function to convert T* into linked_ptr +// Doing e.g. make_linked_ptr(new FooBarBaz(arg)) is a shorter notation +// for linked_ptr >(new FooBarBaz(arg)) +template +linked_ptr make_linked_ptr(T* ptr) { + return linked_ptr(ptr); +} + +} // namespace internal +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_LINKED_PTR_H_ +// Copyright 2007, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +// Google Test - The Google C++ Testing Framework +// +// This file implements a universal value printer that can print a +// value of any type T: +// +// void ::testing::internal::UniversalPrinter::Print(value, ostream_ptr); +// +// A user can teach this function how to print a class type T by +// defining either operator<<() or PrintTo() in the namespace that +// defines T. More specifically, the FIRST defined function in the +// following list will be used (assuming T is defined in namespace +// foo): +// +// 1. foo::PrintTo(const T&, ostream*) +// 2. operator<<(ostream&, const T&) defined in either foo or the +// global namespace. +// +// If none of the above is defined, it will print the debug string of +// the value if it is a protocol buffer, or print the raw bytes in the +// value otherwise. +// +// To aid debugging: when T is a reference type, the address of the +// value is also printed; when T is a (const) char pointer, both the +// pointer value and the NUL-terminated string it points to are +// printed. +// +// We also provide some convenient wrappers: +// +// // Prints a value to a string. For a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// std::string ::testing::PrintToString(const T& value); +// +// // Prints a value tersely: for a reference type, the referenced +// // value (but not the address) is printed; for a (const or not) char +// // pointer, the NUL-terminated string (but not the pointer) is +// // printed. +// void ::testing::internal::UniversalTersePrint(const T& value, ostream*); +// +// // Prints value using the type inferred by the compiler. The difference +// // from UniversalTersePrint() is that this function prints both the +// // pointer and the NUL-terminated string for a (const or not) char pointer. +// void ::testing::internal::UniversalPrint(const T& value, ostream*); +// +// // Prints the fields of a tuple tersely to a string vector, one +// // element for each field. Tuple support must be enabled in +// // gtest-port.h. +// std::vector UniversalTersePrintTupleFieldsToStrings( +// const Tuple& value); +// +// Known limitation: +// +// The print primitives print the elements of an STL-style container +// using the compiler-inferred type of *iter where iter is a +// const_iterator of the container. When const_iterator is an input +// iterator but not a forward iterator, this inferred type may not +// match value_type, and the print output may be incorrect. In +// practice, this is rarely a problem as for most containers +// const_iterator is a forward iterator. We'll fix this if there's an +// actual need for it. Note that this fix cannot rely on value_type +// being defined as many user-defined container types don't have +// value_type. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#include // NOLINT +#include +#include +#include +#include + +namespace testing { + +// Definitions in the 'internal' and 'internal2' name spaces are +// subject to change without notice. DO NOT USE THEM IN USER CODE! +namespace internal2 { + +// Prints the given number of bytes in the given object to the given +// ostream. +GTEST_API_ void PrintBytesInObjectTo(const unsigned char* obj_bytes, + size_t count, + ::std::ostream* os); + +// For selecting which printer to use when a given type has neither << +// nor PrintTo(). +enum TypeKind { + kProtobuf, // a protobuf type + kConvertibleToInteger, // a type implicitly convertible to BiggestInt + // (e.g. a named or unnamed enum type) + kOtherType // anything else +}; + +// TypeWithoutFormatter::PrintValue(value, os) is called +// by the universal printer to print a value of type T when neither +// operator<< nor PrintTo() is defined for T, where kTypeKind is the +// "kind" of T as defined by enum TypeKind. +template +class TypeWithoutFormatter { + public: + // This default version is called when kTypeKind is kOtherType. + static void PrintValue(const T& value, ::std::ostream* os) { + PrintBytesInObjectTo(reinterpret_cast(&value), + sizeof(value), os); + } +}; + +// We print a protobuf using its ShortDebugString() when the string +// doesn't exceed this many characters; otherwise we print it using +// DebugString() for better readability. +const size_t kProtobufOneLinerMaxLength = 50; + +template +class TypeWithoutFormatter { + public: + static void PrintValue(const T& value, ::std::ostream* os) { + const ::testing::internal::string short_str = value.ShortDebugString(); + const ::testing::internal::string pretty_str = + short_str.length() <= kProtobufOneLinerMaxLength ? + short_str : ("\n" + value.DebugString()); + *os << ("<" + pretty_str + ">"); + } +}; + +template +class TypeWithoutFormatter { + public: + // Since T has no << operator or PrintTo() but can be implicitly + // converted to BiggestInt, we print it as a BiggestInt. + // + // Most likely T is an enum type (either named or unnamed), in which + // case printing it as an integer is the desired behavior. In case + // T is not an enum, printing it as an integer is the best we can do + // given that it has no user-defined printer. + static void PrintValue(const T& value, ::std::ostream* os) { + const internal::BiggestInt kBigInt = value; + *os << kBigInt; + } +}; + +// Prints the given value to the given ostream. If the value is a +// protocol message, its debug string is printed; if it's an enum or +// of a type implicitly convertible to BiggestInt, it's printed as an +// integer; otherwise the bytes in the value are printed. This is +// what UniversalPrinter::Print() does when it knows nothing about +// type T and T has neither << operator nor PrintTo(). +// +// A user can override this behavior for a class type Foo by defining +// a << operator in the namespace where Foo is defined. +// +// We put this operator in namespace 'internal2' instead of 'internal' +// to simplify the implementation, as much code in 'internal' needs to +// use << in STL, which would conflict with our own << were it defined +// in 'internal'. +// +// Note that this operator<< takes a generic std::basic_ostream type instead of the more restricted std::ostream. If +// we define it to take an std::ostream instead, we'll get an +// "ambiguous overloads" compiler error when trying to print a type +// Foo that supports streaming to std::basic_ostream, as the compiler cannot tell whether +// operator<<(std::ostream&, const T&) or +// operator<<(std::basic_stream, const Foo&) is more +// specific. +template +::std::basic_ostream& operator<<( + ::std::basic_ostream& os, const T& x) { + TypeWithoutFormatter::value ? kProtobuf : + internal::ImplicitlyConvertible::value ? + kConvertibleToInteger : kOtherType)>::PrintValue(x, &os); + return os; +} + +} // namespace internal2 +} // namespace testing + +// This namespace MUST NOT BE NESTED IN ::testing, or the name look-up +// magic needed for implementing UniversalPrinter won't work. +namespace testing_internal { + +// Used to print a value that is not an STL-style container when the +// user doesn't define PrintTo() for it. +template +void DefaultPrintNonContainerTo(const T& value, ::std::ostream* os) { + // With the following statement, during unqualified name lookup, + // testing::internal2::operator<< appears as if it was declared in + // the nearest enclosing namespace that contains both + // ::testing_internal and ::testing::internal2, i.e. the global + // namespace. For more details, refer to the C++ Standard section + // 7.3.4-1 [namespace.udir]. This allows us to fall back onto + // testing::internal2::operator<< in case T doesn't come with a << + // operator. + // + // We cannot write 'using ::testing::internal2::operator<<;', which + // gcc 3.3 fails to compile due to a compiler bug. + using namespace ::testing::internal2; // NOLINT + + // Assuming T is defined in namespace foo, in the next statement, + // the compiler will consider all of: + // + // 1. foo::operator<< (thanks to Koenig look-up), + // 2. ::operator<< (as the current namespace is enclosed in ::), + // 3. testing::internal2::operator<< (thanks to the using statement above). + // + // The operator<< whose type matches T best will be picked. + // + // We deliberately allow #2 to be a candidate, as sometimes it's + // impossible to define #1 (e.g. when foo is ::std, defining + // anything in it is undefined behavior unless you are a compiler + // vendor.). + *os << value; +} + +} // namespace testing_internal + +namespace testing { +namespace internal { + +// UniversalPrinter::Print(value, ostream_ptr) prints the given +// value to the given ostream. The caller must ensure that +// 'ostream_ptr' is not NULL, or the behavior is undefined. +// +// We define UniversalPrinter as a class template (as opposed to a +// function template), as we need to partially specialize it for +// reference types, which cannot be done with function templates. +template +class UniversalPrinter; + +template +void UniversalPrint(const T& value, ::std::ostream* os); + +// Used to print an STL-style container when the user doesn't define +// a PrintTo() for it. +template +void DefaultPrintTo(IsContainer /* dummy */, + false_type /* is not a pointer */, + const C& container, ::std::ostream* os) { + const size_t kMaxCount = 32; // The maximum number of elements to print. + *os << '{'; + size_t count = 0; + for (typename C::const_iterator it = container.begin(); + it != container.end(); ++it, ++count) { + if (count > 0) { + *os << ','; + if (count == kMaxCount) { // Enough has been printed. + *os << " ..."; + break; + } + } + *os << ' '; + // We cannot call PrintTo(*it, os) here as PrintTo() doesn't + // handle *it being a native array. + internal::UniversalPrint(*it, os); + } + + if (count > 0) { + *os << ' '; + } + *os << '}'; +} + +// Used to print a pointer that is neither a char pointer nor a member +// pointer, when the user doesn't define PrintTo() for it. (A member +// variable pointer or member function pointer doesn't really point to +// a location in the address space. Their representation is +// implementation-defined. Therefore they will be printed as raw +// bytes.) +template +void DefaultPrintTo(IsNotContainer /* dummy */, + true_type /* is a pointer */, + T* p, ::std::ostream* os) { + if (p == NULL) { + *os << "NULL"; + } else { + // C++ doesn't allow casting from a function pointer to any object + // pointer. + // + // IsTrue() silences warnings: "Condition is always true", + // "unreachable code". + if (IsTrue(ImplicitlyConvertible::value)) { + // T is not a function type. We just call << to print p, + // relying on ADL to pick up user-defined << for their pointer + // types, if any. + *os << p; + } else { + // T is a function type, so '*os << p' doesn't do what we want + // (it just prints p as bool). We want to print p as a const + // void*. However, we cannot cast it to const void* directly, + // even using reinterpret_cast, as earlier versions of gcc + // (e.g. 3.4.5) cannot compile the cast when p is a function + // pointer. Casting to UInt64 first solves the problem. + *os << reinterpret_cast( + reinterpret_cast(p)); + } + } +} + +// Used to print a non-container, non-pointer value when the user +// doesn't define PrintTo() for it. +template +void DefaultPrintTo(IsNotContainer /* dummy */, + false_type /* is not a pointer */, + const T& value, ::std::ostream* os) { + ::testing_internal::DefaultPrintNonContainerTo(value, os); +} + +// Prints the given value using the << operator if it has one; +// otherwise prints the bytes in it. This is what +// UniversalPrinter::Print() does when PrintTo() is not specialized +// or overloaded for type T. +// +// A user can override this behavior for a class type Foo by defining +// an overload of PrintTo() in the namespace where Foo is defined. We +// give the user this option as sometimes defining a << operator for +// Foo is not desirable (e.g. the coding style may prevent doing it, +// or there is already a << operator but it doesn't do what the user +// wants). +template +void PrintTo(const T& value, ::std::ostream* os) { + // DefaultPrintTo() is overloaded. The type of its first two + // arguments determine which version will be picked. If T is an + // STL-style container, the version for container will be called; if + // T is a pointer, the pointer version will be called; otherwise the + // generic version will be called. + // + // Note that we check for container types here, prior to we check + // for protocol message types in our operator<<. The rationale is: + // + // For protocol messages, we want to give people a chance to + // override Google Mock's format by defining a PrintTo() or + // operator<<. For STL containers, other formats can be + // incompatible with Google Mock's format for the container + // elements; therefore we check for container types here to ensure + // that our format is used. + // + // The second argument of DefaultPrintTo() is needed to bypass a bug + // in Symbian's C++ compiler that prevents it from picking the right + // overload between: + // + // PrintTo(const T& x, ...); + // PrintTo(T* x, ...); + DefaultPrintTo(IsContainerTest(0), is_pointer(), value, os); +} + +// The following list of PrintTo() overloads tells +// UniversalPrinter::Print() how to print standard types (built-in +// types, strings, plain arrays, and pointers). + +// Overloads for various char types. +GTEST_API_ void PrintTo(unsigned char c, ::std::ostream* os); +GTEST_API_ void PrintTo(signed char c, ::std::ostream* os); +inline void PrintTo(char c, ::std::ostream* os) { + // When printing a plain char, we always treat it as unsigned. This + // way, the output won't be affected by whether the compiler thinks + // char is signed or not. + PrintTo(static_cast(c), os); +} + +// Overloads for other simple built-in types. +inline void PrintTo(bool x, ::std::ostream* os) { + *os << (x ? "true" : "false"); +} + +// Overload for wchar_t type. +// Prints a wchar_t as a symbol if it is printable or as its internal +// code otherwise and also as its decimal code (except for L'\0'). +// The L'\0' char is printed as "L'\\0'". The decimal code is printed +// as signed integer when wchar_t is implemented by the compiler +// as a signed type and is printed as an unsigned integer when wchar_t +// is implemented as an unsigned type. +GTEST_API_ void PrintTo(wchar_t wc, ::std::ostream* os); + +// Overloads for C strings. +GTEST_API_ void PrintTo(const char* s, ::std::ostream* os); +inline void PrintTo(char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// signed/unsigned char is often used for representing binary data, so +// we print pointers to it as void* to be safe. +inline void PrintTo(const signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(signed char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(const unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +inline void PrintTo(unsigned char* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} + +// MSVC can be configured to define wchar_t as a typedef of unsigned +// short. It defines _NATIVE_WCHAR_T_DEFINED when wchar_t is a native +// type. When wchar_t is a typedef, defining an overload for const +// wchar_t* would cause unsigned short* be printed as a wide string, +// possibly causing invalid memory accesses. +#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED) +// Overloads for wide C strings +GTEST_API_ void PrintTo(const wchar_t* s, ::std::ostream* os); +inline void PrintTo(wchar_t* s, ::std::ostream* os) { + PrintTo(ImplicitCast_(s), os); +} +#endif + +// Overload for C arrays. Multi-dimensional arrays are printed +// properly. + +// Prints the given number of elements in an array, without printing +// the curly braces. +template +void PrintRawArrayTo(const T a[], size_t count, ::std::ostream* os) { + UniversalPrint(a[0], os); + for (size_t i = 1; i != count; i++) { + *os << ", "; + UniversalPrint(a[i], os); + } +} + +// Overloads for ::string and ::std::string. +#if GTEST_HAS_GLOBAL_STRING +GTEST_API_ void PrintStringTo(const ::string&s, ::std::ostream* os); +inline void PrintTo(const ::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} +#endif // GTEST_HAS_GLOBAL_STRING + +GTEST_API_ void PrintStringTo(const ::std::string&s, ::std::ostream* os); +inline void PrintTo(const ::std::string& s, ::std::ostream* os) { + PrintStringTo(s, os); +} + +// Overloads for ::wstring and ::std::wstring. +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_API_ void PrintWideStringTo(const ::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_GLOBAL_WSTRING + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ void PrintWideStringTo(const ::std::wstring&s, ::std::ostream* os); +inline void PrintTo(const ::std::wstring& s, ::std::ostream* os) { + PrintWideStringTo(s, os); +} +#endif // GTEST_HAS_STD_WSTRING + +#if GTEST_HAS_TR1_TUPLE +// Overload for ::std::tr1::tuple. Needed for printing function arguments, +// which are packed as tuples. + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template +void PrintTupleTo(const T& t, ::std::ostream* os); + +// Overloaded PrintTo() for tuples of various arities. We support +// tuples of up-to 10 fields. The following implementation works +// regardless of whether tr1::tuple is implemented using the +// non-standard variadic template feature or not. + +inline void PrintTo(const ::std::tr1::tuple<>& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo(const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} + +template +void PrintTo( + const ::std::tr1::tuple& t, + ::std::ostream* os) { + PrintTupleTo(t, os); +} +#endif // GTEST_HAS_TR1_TUPLE + +// Overload for std::pair. +template +void PrintTo(const ::std::pair& value, ::std::ostream* os) { + *os << '('; + // We cannot use UniversalPrint(value.first, os) here, as T1 may be + // a reference type. The same for printing value.second. + UniversalPrinter::Print(value.first, os); + *os << ", "; + UniversalPrinter::Print(value.second, os); + *os << ')'; +} + +// Implements printing a non-reference type T by letting the compiler +// pick the right overload of PrintTo() for T. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4180) // Temporarily disables warning 4180. +#endif // _MSC_VER + + // Note: we deliberately don't call this PrintTo(), as that name + // conflicts with ::testing::internal::PrintTo in the body of the + // function. + static void Print(const T& value, ::std::ostream* os) { + // By default, ::testing::internal::PrintTo() is used for printing + // the value. + // + // Thanks to Koenig look-up, if T is a class and has its own + // PrintTo() function defined in its namespace, that function will + // be visible here. Since it is more specific than the generic ones + // in ::testing::internal, it will be picked by the compiler in the + // following statement - exactly what we want. + PrintTo(value, os); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif // _MSC_VER +}; + +// UniversalPrintArray(begin, len, os) prints an array of 'len' +// elements, starting at address 'begin'. +template +void UniversalPrintArray(const T* begin, size_t len, ::std::ostream* os) { + if (len == 0) { + *os << "{}"; + } else { + *os << "{ "; + const size_t kThreshold = 18; + const size_t kChunkSize = 8; + // If the array has more than kThreshold elements, we'll have to + // omit some details by printing only the first and the last + // kChunkSize elements. + // TODO(wan@google.com): let the user control the threshold using a flag. + if (len <= kThreshold) { + PrintRawArrayTo(begin, len, os); + } else { + PrintRawArrayTo(begin, kChunkSize, os); + *os << ", ..., "; + PrintRawArrayTo(begin + len - kChunkSize, kChunkSize, os); + } + *os << " }"; + } +} +// This overload prints a (const) char array compactly. +GTEST_API_ void UniversalPrintArray( + const char* begin, size_t len, ::std::ostream* os); + +// This overload prints a (const) wchar_t array compactly. +GTEST_API_ void UniversalPrintArray( + const wchar_t* begin, size_t len, ::std::ostream* os); + +// Implements printing an array type T[N]. +template +class UniversalPrinter { + public: + // Prints the given array, omitting some elements when there are too + // many. + static void Print(const T (&a)[N], ::std::ostream* os) { + UniversalPrintArray(a, N, os); + } +}; + +// Implements printing a reference type T&. +template +class UniversalPrinter { + public: + // MSVC warns about adding const to a function type, so we want to + // disable the warning. +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4180) // Temporarily disables warning 4180. +#endif // _MSC_VER + + static void Print(const T& value, ::std::ostream* os) { + // Prints the address of the value. We use reinterpret_cast here + // as static_cast doesn't compile when T is a function type. + *os << "@" << reinterpret_cast(&value) << " "; + + // Then prints the value itself. + UniversalPrint(value, os); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif // _MSC_VER +}; + +// Prints a value tersely: for a reference type, the referenced value +// (but not the address) is printed; for a (const) char pointer, the +// NUL-terminated string (but not the pointer) is printed. + +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T& value, ::std::ostream* os) { + UniversalPrint(value, os); + } +}; +template +class UniversalTersePrinter { + public: + static void Print(const T (&value)[N], ::std::ostream* os) { + UniversalPrinter::Print(value, os); + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(const char* str, ::std::ostream* os) { + if (str == NULL) { + *os << "NULL"; + } else { + UniversalPrint(string(str), os); + } + } +}; +template <> +class UniversalTersePrinter { + public: + static void Print(char* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +#if GTEST_HAS_STD_WSTRING +template <> +class UniversalTersePrinter { + public: + static void Print(const wchar_t* str, ::std::ostream* os) { + if (str == NULL) { + *os << "NULL"; + } else { + UniversalPrint(::std::wstring(str), os); + } + } +}; +#endif + +template <> +class UniversalTersePrinter { + public: + static void Print(wchar_t* str, ::std::ostream* os) { + UniversalTersePrinter::Print(str, os); + } +}; + +template +void UniversalTersePrint(const T& value, ::std::ostream* os) { + UniversalTersePrinter::Print(value, os); +} + +// Prints a value using the type inferred by the compiler. The +// difference between this and UniversalTersePrint() is that for a +// (const) char pointer, this prints both the pointer and the +// NUL-terminated string. +template +void UniversalPrint(const T& value, ::std::ostream* os) { + // A workarond for the bug in VC++ 7.1 that prevents us from instantiating + // UniversalPrinter with T directly. + typedef T T1; + UniversalPrinter::Print(value, os); +} + +#if GTEST_HAS_TR1_TUPLE +typedef ::std::vector Strings; + +// This helper template allows PrintTo() for tuples and +// UniversalTersePrintTupleFieldsToStrings() to be defined by +// induction on the number of tuple fields. The idea is that +// TuplePrefixPrinter::PrintPrefixTo(t, os) prints the first N +// fields in tuple t, and can be defined in terms of +// TuplePrefixPrinter. + +// The inductive case. +template +struct TuplePrefixPrinter { + // Prints the first N fields of a tuple. + template + static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { + TuplePrefixPrinter::PrintPrefixTo(t, os); + *os << ", "; + UniversalPrinter::type> + ::Print(::std::tr1::get(t), os); + } + + // Tersely prints the first N fields of a tuple to a string vector, + // one element for each field. + template + static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { + TuplePrefixPrinter::TersePrintPrefixToStrings(t, strings); + ::std::stringstream ss; + UniversalTersePrint(::std::tr1::get(t), &ss); + strings->push_back(ss.str()); + } +}; + +// Base cases. +template <> +struct TuplePrefixPrinter<0> { + template + static void PrintPrefixTo(const Tuple&, ::std::ostream*) {} + + template + static void TersePrintPrefixToStrings(const Tuple&, Strings*) {} +}; +// We have to specialize the entire TuplePrefixPrinter<> class +// template here, even though the definition of +// TersePrintPrefixToStrings() is the same as the generic version, as +// Embarcadero (formerly CodeGear, formerly Borland) C++ doesn't +// support specializing a method template of a class template. +template <> +struct TuplePrefixPrinter<1> { + template + static void PrintPrefixTo(const Tuple& t, ::std::ostream* os) { + UniversalPrinter::type>:: + Print(::std::tr1::get<0>(t), os); + } + + template + static void TersePrintPrefixToStrings(const Tuple& t, Strings* strings) { + ::std::stringstream ss; + UniversalTersePrint(::std::tr1::get<0>(t), &ss); + strings->push_back(ss.str()); + } +}; + +// Helper function for printing a tuple. T must be instantiated with +// a tuple type. +template +void PrintTupleTo(const T& t, ::std::ostream* os) { + *os << "("; + TuplePrefixPrinter< ::std::tr1::tuple_size::value>:: + PrintPrefixTo(t, os); + *os << ")"; +} + +// Prints the fields of a tuple tersely to a string vector, one +// element for each field. See the comment before +// UniversalTersePrint() for how we define "tersely". +template +Strings UniversalTersePrintTupleFieldsToStrings(const Tuple& value) { + Strings result; + TuplePrefixPrinter< ::std::tr1::tuple_size::value>:: + TersePrintPrefixToStrings(value, &result); + return result; +} +#endif // GTEST_HAS_TR1_TUPLE + +} // namespace internal + +template +::std::string PrintToString(const T& value) { + ::std::stringstream ss; + internal::UniversalTersePrinter::Print(value, &ss); + return ss.str(); +} + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRINTERS_H_ + +#if GTEST_HAS_PARAM_TEST + +namespace testing { +namespace internal { + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Outputs a message explaining invalid registration of different +// fixture class for the same test case. This may happen when +// TEST_P macro is used to define two tests with the same name +// but in different namespaces. +GTEST_API_ void ReportInvalidTestCaseType(const char* test_case_name, + const char* file, int line); + +template class ParamGeneratorInterface; +template class ParamGenerator; + +// Interface for iterating over elements provided by an implementation +// of ParamGeneratorInterface. +template +class ParamIteratorInterface { + public: + virtual ~ParamIteratorInterface() {} + // A pointer to the base generator instance. + // Used only for the purposes of iterator comparison + // to make sure that two iterators belong to the same generator. + virtual const ParamGeneratorInterface* BaseGenerator() const = 0; + // Advances iterator to point to the next element + // provided by the generator. The caller is responsible + // for not calling Advance() on an iterator equal to + // BaseGenerator()->End(). + virtual void Advance() = 0; + // Clones the iterator object. Used for implementing copy semantics + // of ParamIterator. + virtual ParamIteratorInterface* Clone() const = 0; + // Dereferences the current iterator and provides (read-only) access + // to the pointed value. It is the caller's responsibility not to call + // Current() on an iterator equal to BaseGenerator()->End(). + // Used for implementing ParamGenerator::operator*(). + virtual const T* Current() const = 0; + // Determines whether the given iterator and other point to the same + // element in the sequence generated by the generator. + // Used for implementing ParamGenerator::operator==(). + virtual bool Equals(const ParamIteratorInterface& other) const = 0; +}; + +// Class iterating over elements provided by an implementation of +// ParamGeneratorInterface. It wraps ParamIteratorInterface +// and implements the const forward iterator concept. +template +class ParamIterator { + public: + typedef T value_type; + typedef const T& reference; + typedef ptrdiff_t difference_type; + + // ParamIterator assumes ownership of the impl_ pointer. + ParamIterator(const ParamIterator& other) : impl_(other.impl_->Clone()) {} + ParamIterator& operator=(const ParamIterator& other) { + if (this != &other) + impl_.reset(other.impl_->Clone()); + return *this; + } + + const T& operator*() const { return *impl_->Current(); } + const T* operator->() const { return impl_->Current(); } + // Prefix version of operator++. + ParamIterator& operator++() { + impl_->Advance(); + return *this; + } + // Postfix version of operator++. + ParamIterator operator++(int /*unused*/) { + ParamIteratorInterface* clone = impl_->Clone(); + impl_->Advance(); + return ParamIterator(clone); + } + bool operator==(const ParamIterator& other) const { + return impl_.get() == other.impl_.get() || impl_->Equals(*other.impl_); + } + bool operator!=(const ParamIterator& other) const { + return !(*this == other); + } + + private: + friend class ParamGenerator; + explicit ParamIterator(ParamIteratorInterface* impl) : impl_(impl) {} + scoped_ptr > impl_; +}; + +// ParamGeneratorInterface is the binary interface to access generators +// defined in other translation units. +template +class ParamGeneratorInterface { + public: + typedef T ParamType; + + virtual ~ParamGeneratorInterface() {} + + // Generator interface definition + virtual ParamIteratorInterface* Begin() const = 0; + virtual ParamIteratorInterface* End() const = 0; +}; + +// Wraps ParamGeneratorInterface and provides general generator syntax +// compatible with the STL Container concept. +// This class implements copy initialization semantics and the contained +// ParamGeneratorInterface instance is shared among all copies +// of the original object. This is possible because that instance is immutable. +template +class ParamGenerator { + public: + typedef ParamIterator iterator; + + explicit ParamGenerator(ParamGeneratorInterface* impl) : impl_(impl) {} + ParamGenerator(const ParamGenerator& other) : impl_(other.impl_) {} + + ParamGenerator& operator=(const ParamGenerator& other) { + impl_ = other.impl_; + return *this; + } + + iterator begin() const { return iterator(impl_->Begin()); } + iterator end() const { return iterator(impl_->End()); } + + private: + linked_ptr > impl_; +}; + +// Generates values from a range of two comparable values. Can be used to +// generate sequences of user-defined types that implement operator+() and +// operator<(). +// This class is used in the Range() function. +template +class RangeGenerator : public ParamGeneratorInterface { + public: + RangeGenerator(T begin, T end, IncrementT step) + : begin_(begin), end_(end), + step_(step), end_index_(CalculateEndIndex(begin, end, step)) {} + virtual ~RangeGenerator() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, begin_, 0, step_); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, end_, end_index_, step_); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, T value, int index, + IncrementT step) + : base_(base), value_(value), index_(index), step_(step) {} + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + virtual void Advance() { + value_ = value_ + step_; + index_++; + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const T* Current() const { return &value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const int other_index = + CheckedDowncastToActualType(&other)->index_; + return index_ == other_index; + } + + private: + Iterator(const Iterator& other) + : ParamIteratorInterface(), + base_(other.base_), value_(other.value_), index_(other.index_), + step_(other.step_) {} + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + T value_; + int index_; + const IncrementT step_; + }; // class RangeGenerator::Iterator + + static int CalculateEndIndex(const T& begin, + const T& end, + const IncrementT& step) { + int end_index = 0; + for (T i = begin; i < end; i = i + step) + end_index++; + return end_index; + } + + // No implementation - assignment is unsupported. + void operator=(const RangeGenerator& other); + + const T begin_; + const T end_; + const IncrementT step_; + // The index for the end() iterator. All the elements in the generated + // sequence are indexed (0-based) to aid iterator comparison. + const int end_index_; +}; // class RangeGenerator + + +// Generates values from a pair of STL-style iterators. Used in the +// ValuesIn() function. The elements are copied from the source range +// since the source can be located on the stack, and the generator +// is likely to persist beyond that stack frame. +template +class ValuesInIteratorRangeGenerator : public ParamGeneratorInterface { + public: + template + ValuesInIteratorRangeGenerator(ForwardIterator begin, ForwardIterator end) + : container_(begin, end) {} + virtual ~ValuesInIteratorRangeGenerator() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, container_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, container_.end()); + } + + private: + typedef typename ::std::vector ContainerType; + + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + typename ContainerType::const_iterator iterator) + : base_(base), iterator_(iterator) {} + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + virtual void Advance() { + ++iterator_; + value_.reset(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + // We need to use cached value referenced by iterator_ because *iterator_ + // can return a temporary object (and of type other then T), so just + // having "return &*iterator_;" doesn't work. + // value_ is updated here and not in Advance() because Advance() + // can advance iterator_ beyond the end of the range, and we cannot + // detect that fact. The client code, on the other hand, is + // responsible for not calling Current() on an out-of-range iterator. + virtual const T* Current() const { + if (value_.get() == NULL) + value_.reset(new T(*iterator_)); + return value_.get(); + } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + return iterator_ == + CheckedDowncastToActualType(&other)->iterator_; + } + + private: + Iterator(const Iterator& other) + // The explicit constructor call suppresses a false warning + // emitted by gcc when supplied with the -Wextra option. + : ParamIteratorInterface(), + base_(other.base_), + iterator_(other.iterator_) {} + + const ParamGeneratorInterface* const base_; + typename ContainerType::const_iterator iterator_; + // A cached value of *iterator_. We keep it here to allow access by + // pointer in the wrapping iterator's operator->(). + // value_ needs to be mutable to be accessed in Current(). + // Use of scoped_ptr helps manage cached value's lifetime, + // which is bound by the lifespan of the iterator itself. + mutable scoped_ptr value_; + }; // class ValuesInIteratorRangeGenerator::Iterator + + // No implementation - assignment is unsupported. + void operator=(const ValuesInIteratorRangeGenerator& other); + + const ContainerType container_; +}; // class ValuesInIteratorRangeGenerator + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Stores a parameter value and later creates tests parameterized with that +// value. +template +class ParameterizedTestFactory : public TestFactoryBase { + public: + typedef typename TestClass::ParamType ParamType; + explicit ParameterizedTestFactory(ParamType parameter) : + parameter_(parameter) {} + virtual Test* CreateTest() { + TestClass::SetParam(¶meter_); + return new TestClass(); + } + + private: + const ParamType parameter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactoryBase is a base class for meta-factories that create +// test factories for passing into MakeAndRegisterTestInfo function. +template +class TestMetaFactoryBase { + public: + virtual ~TestMetaFactoryBase() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) = 0; +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// TestMetaFactory creates test factories for passing into +// MakeAndRegisterTestInfo function. Since MakeAndRegisterTestInfo receives +// ownership of test factory pointer, same factory object cannot be passed +// into that method twice. But ParameterizedTestCaseInfo is going to call +// it for each Test/Parameter value combination. Thus it needs meta factory +// creator class. +template +class TestMetaFactory + : public TestMetaFactoryBase { + public: + typedef typename TestCase::ParamType ParamType; + + TestMetaFactory() {} + + virtual TestFactoryBase* CreateTestFactory(ParamType parameter) { + return new ParameterizedTestFactory(parameter); + } + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestMetaFactory); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseInfoBase is a generic interface +// to ParameterizedTestCaseInfo classes. ParameterizedTestCaseInfoBase +// accumulates test information provided by TEST_P macro invocations +// and generators provided by INSTANTIATE_TEST_CASE_P macro invocations +// and uses that information to register all resulting test instances +// in RegisterTests method. The ParameterizeTestCaseRegistry class holds +// a collection of pointers to the ParameterizedTestCaseInfo objects +// and calls RegisterTests() on each of them when asked. +class ParameterizedTestCaseInfoBase { + public: + virtual ~ParameterizedTestCaseInfoBase() {} + + // Base part of test case name for display purposes. + virtual const string& GetTestCaseName() const = 0; + // Test case id to verify identity. + virtual TypeId GetTestCaseTypeId() const = 0; + // UnitTest class invokes this method to register tests in this + // test case right before running them in RUN_ALL_TESTS macro. + // This method should not be called more then once on any single + // instance of a ParameterizedTestCaseInfoBase derived class. + virtual void RegisterTests() = 0; + + protected: + ParameterizedTestCaseInfoBase() {} + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfoBase); +}; + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseInfo accumulates tests obtained from TEST_P +// macro invocations for a particular test case and generators +// obtained from INSTANTIATE_TEST_CASE_P macro invocations for that +// test case. It registers tests with all values generated by all +// generators when asked. +template +class ParameterizedTestCaseInfo : public ParameterizedTestCaseInfoBase { + public: + // ParamType and GeneratorCreationFunc are private types but are required + // for declarations of public methods AddTestPattern() and + // AddTestCaseInstantiation(). + typedef typename TestCase::ParamType ParamType; + // A function that returns an instance of appropriate generator type. + typedef ParamGenerator(GeneratorCreationFunc)(); + + explicit ParameterizedTestCaseInfo(const char* name) + : test_case_name_(name) {} + + // Test case base name for display purposes. + virtual const string& GetTestCaseName() const { return test_case_name_; } + // Test case id to verify identity. + virtual TypeId GetTestCaseTypeId() const { return GetTypeId(); } + // TEST_P macro uses AddTestPattern() to record information + // about a single test in a LocalTestInfo structure. + // test_case_name is the base name of the test case (without invocation + // prefix). test_base_name is the name of an individual test without + // parameter index. For the test SequenceA/FooTest.DoBar/1 FooTest is + // test case base name and DoBar is test base name. + void AddTestPattern(const char* test_case_name, + const char* test_base_name, + TestMetaFactoryBase* meta_factory) { + tests_.push_back(linked_ptr(new TestInfo(test_case_name, + test_base_name, + meta_factory))); + } + // INSTANTIATE_TEST_CASE_P macro uses AddGenerator() to record information + // about a generator. + int AddTestCaseInstantiation(const string& instantiation_name, + GeneratorCreationFunc* func, + const char* /* file */, + int /* line */) { + instantiations_.push_back(::std::make_pair(instantiation_name, func)); + return 0; // Return value used only to run this method in namespace scope. + } + // UnitTest class invokes this method to register tests in this test case + // test cases right before running tests in RUN_ALL_TESTS macro. + // This method should not be called more then once on any single + // instance of a ParameterizedTestCaseInfoBase derived class. + // UnitTest has a guard to prevent from calling this method more then once. + virtual void RegisterTests() { + for (typename TestInfoContainer::iterator test_it = tests_.begin(); + test_it != tests_.end(); ++test_it) { + linked_ptr test_info = *test_it; + for (typename InstantiationContainer::iterator gen_it = + instantiations_.begin(); gen_it != instantiations_.end(); + ++gen_it) { + const string& instantiation_name = gen_it->first; + ParamGenerator generator((*gen_it->second)()); + + string test_case_name; + if ( !instantiation_name.empty() ) + test_case_name = instantiation_name + "/"; + test_case_name += test_info->test_case_base_name; + + int i = 0; + for (typename ParamGenerator::iterator param_it = + generator.begin(); + param_it != generator.end(); ++param_it, ++i) { + Message test_name_stream; + test_name_stream << test_info->test_base_name << "/" << i; + MakeAndRegisterTestInfo( + test_case_name.c_str(), + test_name_stream.GetString().c_str(), + NULL, // No type parameter. + PrintToString(*param_it).c_str(), + GetTestCaseTypeId(), + TestCase::SetUpTestCase, + TestCase::TearDownTestCase, + test_info->test_meta_factory->CreateTestFactory(*param_it)); + } // for param_it + } // for gen_it + } // for test_it + } // RegisterTests + + private: + // LocalTestInfo structure keeps information about a single test registered + // with TEST_P macro. + struct TestInfo { + TestInfo(const char* a_test_case_base_name, + const char* a_test_base_name, + TestMetaFactoryBase* a_test_meta_factory) : + test_case_base_name(a_test_case_base_name), + test_base_name(a_test_base_name), + test_meta_factory(a_test_meta_factory) {} + + const string test_case_base_name; + const string test_base_name; + const scoped_ptr > test_meta_factory; + }; + typedef ::std::vector > TestInfoContainer; + // Keeps pairs of + // received from INSTANTIATE_TEST_CASE_P macros. + typedef ::std::vector > + InstantiationContainer; + + const string test_case_name_; + TestInfoContainer tests_; + InstantiationContainer instantiations_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseInfo); +}; // class ParameterizedTestCaseInfo + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// ParameterizedTestCaseRegistry contains a map of ParameterizedTestCaseInfoBase +// classes accessed by test case names. TEST_P and INSTANTIATE_TEST_CASE_P +// macros use it to locate their corresponding ParameterizedTestCaseInfo +// descriptors. +class ParameterizedTestCaseRegistry { + public: + ParameterizedTestCaseRegistry() {} + ~ParameterizedTestCaseRegistry() { + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + delete *it; + } + } + + // Looks up or creates and returns a structure containing information about + // tests and instantiations of a particular test case. + template + ParameterizedTestCaseInfo* GetTestCasePatternHolder( + const char* test_case_name, + const char* file, + int line) { + ParameterizedTestCaseInfo* typed_test_info = NULL; + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + if ((*it)->GetTestCaseName() == test_case_name) { + if ((*it)->GetTestCaseTypeId() != GetTypeId()) { + // Complain about incorrect usage of Google Test facilities + // and terminate the program since we cannot guaranty correct + // test case setup and tear-down in this case. + ReportInvalidTestCaseType(test_case_name, file, line); + posix::Abort(); + } else { + // At this point we are sure that the object we found is of the same + // type we are looking for, so we downcast it to that type + // without further checks. + typed_test_info = CheckedDowncastToActualType< + ParameterizedTestCaseInfo >(*it); + } + break; + } + } + if (typed_test_info == NULL) { + typed_test_info = new ParameterizedTestCaseInfo(test_case_name); + test_case_infos_.push_back(typed_test_info); + } + return typed_test_info; + } + void RegisterTests() { + for (TestCaseInfoContainer::iterator it = test_case_infos_.begin(); + it != test_case_infos_.end(); ++it) { + (*it)->RegisterTests(); + } + } + + private: + typedef ::std::vector TestCaseInfoContainer; + + TestCaseInfoContainer test_case_infos_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(ParameterizedTestCaseRegistry); +}; + +} // namespace internal +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_H_ +// This file was GENERATED by command: +// pump.py gtest-param-util-generated.h.pump +// DO NOT EDIT BY HAND!!! + +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: vladl@google.com (Vlad Losev) + +// Type and function utilities for implementing parameterized tests. +// This file is generated by a SCRIPT. DO NOT EDIT BY HAND! +// +// Currently Google Test supports at most 50 arguments in Values, +// and at most 10 arguments in Combine. Please contact +// googletestframework@googlegroups.com if you need more. +// Please note that the number of arguments to Combine is limited +// by the maximum arity of the implementation of tr1::tuple which is +// currently set at 10. + +#ifndef GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ +#define GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ + +// scripts/fuse_gtest.py depends on gtest's own header being #included +// *unconditionally*. Therefore these #includes cannot be moved +// inside #if GTEST_HAS_PARAM_TEST. + +#if GTEST_HAS_PARAM_TEST + +namespace testing { + +// Forward declarations of ValuesIn(), which is implemented in +// include/gtest/gtest-param-test.h. +template +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end); + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]); + +template +internal::ParamGenerator ValuesIn( + const Container& container); + +namespace internal { + +// Used in the Values() function to provide polymorphic capabilities. +template +class ValueArray1 { + public: + explicit ValueArray1(T1 v1) : v1_(v1) {} + + template + operator ParamGenerator() const { return ValuesIn(&v1_, &v1_ + 1); } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray1& other); + + const T1 v1_; +}; + +template +class ValueArray2 { + public: + ValueArray2(T1 v1, T2 v2) : v1_(v1), v2_(v2) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray2& other); + + const T1 v1_; + const T2 v2_; +}; + +template +class ValueArray3 { + public: + ValueArray3(T1 v1, T2 v2, T3 v3) : v1_(v1), v2_(v2), v3_(v3) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray3& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; +}; + +template +class ValueArray4 { + public: + ValueArray4(T1 v1, T2 v2, T3 v3, T4 v4) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray4& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; +}; + +template +class ValueArray5 { + public: + ValueArray5(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray5& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; +}; + +template +class ValueArray6 { + public: + ValueArray6(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray6& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; +}; + +template +class ValueArray7 { + public: + ValueArray7(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray7& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; +}; + +template +class ValueArray8 { + public: + ValueArray8(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray8& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; +}; + +template +class ValueArray9 { + public: + ValueArray9(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray9& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; +}; + +template +class ValueArray10 { + public: + ValueArray10(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray10& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; +}; + +template +class ValueArray11 { + public: + ValueArray11(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray11& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; +}; + +template +class ValueArray12 { + public: + ValueArray12(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray12& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; +}; + +template +class ValueArray13 { + public: + ValueArray13(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray13& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; +}; + +template +class ValueArray14 { + public: + ValueArray14(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray14& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; +}; + +template +class ValueArray15 { + public: + ValueArray15(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray15& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; +}; + +template +class ValueArray16 { + public: + ValueArray16(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray16& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; +}; + +template +class ValueArray17 { + public: + ValueArray17(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray17& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; +}; + +template +class ValueArray18 { + public: + ValueArray18(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray18& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; +}; + +template +class ValueArray19 { + public: + ValueArray19(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray19& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; +}; + +template +class ValueArray20 { + public: + ValueArray20(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray20& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; +}; + +template +class ValueArray21 { + public: + ValueArray21(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray21& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; +}; + +template +class ValueArray22 { + public: + ValueArray22(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray22& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; +}; + +template +class ValueArray23 { + public: + ValueArray23(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray23& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; +}; + +template +class ValueArray24 { + public: + ValueArray24(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray24& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; +}; + +template +class ValueArray25 { + public: + ValueArray25(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray25& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; +}; + +template +class ValueArray26 { + public: + ValueArray26(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray26& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; +}; + +template +class ValueArray27 { + public: + ValueArray27(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray27& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; +}; + +template +class ValueArray28 { + public: + ValueArray28(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray28& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; +}; + +template +class ValueArray29 { + public: + ValueArray29(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray29& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; +}; + +template +class ValueArray30 { + public: + ValueArray30(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray30& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; +}; + +template +class ValueArray31 { + public: + ValueArray31(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray31& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; +}; + +template +class ValueArray32 { + public: + ValueArray32(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray32& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; +}; + +template +class ValueArray33 { + public: + ValueArray33(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, + T33 v33) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray33& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; +}; + +template +class ValueArray34 { + public: + ValueArray34(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray34& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; +}; + +template +class ValueArray35 { + public: + ValueArray35(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), + v32_(v32), v33_(v33), v34_(v34), v35_(v35) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray35& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; +}; + +template +class ValueArray36 { + public: + ValueArray36(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), + v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray36& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; +}; + +template +class ValueArray37 { + public: + ValueArray37(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), + v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), + v36_(v36), v37_(v37) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray37& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; +}; + +template +class ValueArray38 { + public: + ValueArray38(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray38& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; +}; + +template +class ValueArray39 { + public: + ValueArray39(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray39& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; +}; + +template +class ValueArray40 { + public: + ValueArray40(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), + v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), + v40_(v40) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray40& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; +}; + +template +class ValueArray41 { + public: + ValueArray41(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, + T41 v41) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray41& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; +}; + +template +class ValueArray42 { + public: + ValueArray42(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray42& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; +}; + +template +class ValueArray43 { + public: + ValueArray43(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), + v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), + v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), + v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), + v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), + v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), + v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray43& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; +}; + +template +class ValueArray44 { + public: + ValueArray44(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), + v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), + v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), v18_(v18), + v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), v24_(v24), + v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), v30_(v30), + v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), v36_(v36), + v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), v42_(v42), + v43_(v43), v44_(v44) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray44& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; +}; + +template +class ValueArray45 { + public: + ValueArray45(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), + v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), v11_(v11), + v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), v17_(v17), + v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), v23_(v23), + v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), v29_(v29), + v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), v35_(v35), + v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), v41_(v41), + v42_(v42), v43_(v43), v44_(v44), v45_(v45) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray45& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; +}; + +template +class ValueArray46 { + public: + ValueArray46(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) : v1_(v1), v2_(v2), v3_(v3), + v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), + v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray46& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; +}; + +template +class ValueArray47 { + public: + ValueArray47(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) : v1_(v1), v2_(v2), + v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), v10_(v10), + v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), v16_(v16), + v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), v22_(v22), + v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), v28_(v28), + v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), v34_(v34), + v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), v40_(v40), + v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), v46_(v46), + v47_(v47) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray47& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; +}; + +template +class ValueArray48 { + public: + ValueArray48(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48) : v1_(v1), + v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), v8_(v8), v9_(v9), + v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), v15_(v15), + v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), v21_(v21), + v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), v27_(v27), + v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), v33_(v33), + v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), v39_(v39), + v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), v45_(v45), + v46_(v46), v47_(v47), v48_(v48) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray48& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; +}; + +template +class ValueArray49 { + public: + ValueArray49(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, + T49 v49) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), + v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_), static_cast(v49_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray49& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; + const T49 v49_; +}; + +template +class ValueArray50 { + public: + ValueArray50(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, T48 v48, T49 v49, + T50 v50) : v1_(v1), v2_(v2), v3_(v3), v4_(v4), v5_(v5), v6_(v6), v7_(v7), + v8_(v8), v9_(v9), v10_(v10), v11_(v11), v12_(v12), v13_(v13), v14_(v14), + v15_(v15), v16_(v16), v17_(v17), v18_(v18), v19_(v19), v20_(v20), + v21_(v21), v22_(v22), v23_(v23), v24_(v24), v25_(v25), v26_(v26), + v27_(v27), v28_(v28), v29_(v29), v30_(v30), v31_(v31), v32_(v32), + v33_(v33), v34_(v34), v35_(v35), v36_(v36), v37_(v37), v38_(v38), + v39_(v39), v40_(v40), v41_(v41), v42_(v42), v43_(v43), v44_(v44), + v45_(v45), v46_(v46), v47_(v47), v48_(v48), v49_(v49), v50_(v50) {} + + template + operator ParamGenerator() const { + const T array[] = {static_cast(v1_), static_cast(v2_), + static_cast(v3_), static_cast(v4_), static_cast(v5_), + static_cast(v6_), static_cast(v7_), static_cast(v8_), + static_cast(v9_), static_cast(v10_), static_cast(v11_), + static_cast(v12_), static_cast(v13_), static_cast(v14_), + static_cast(v15_), static_cast(v16_), static_cast(v17_), + static_cast(v18_), static_cast(v19_), static_cast(v20_), + static_cast(v21_), static_cast(v22_), static_cast(v23_), + static_cast(v24_), static_cast(v25_), static_cast(v26_), + static_cast(v27_), static_cast(v28_), static_cast(v29_), + static_cast(v30_), static_cast(v31_), static_cast(v32_), + static_cast(v33_), static_cast(v34_), static_cast(v35_), + static_cast(v36_), static_cast(v37_), static_cast(v38_), + static_cast(v39_), static_cast(v40_), static_cast(v41_), + static_cast(v42_), static_cast(v43_), static_cast(v44_), + static_cast(v45_), static_cast(v46_), static_cast(v47_), + static_cast(v48_), static_cast(v49_), static_cast(v50_)}; + return ValuesIn(array); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const ValueArray50& other); + + const T1 v1_; + const T2 v2_; + const T3 v3_; + const T4 v4_; + const T5 v5_; + const T6 v6_; + const T7 v7_; + const T8 v8_; + const T9 v9_; + const T10 v10_; + const T11 v11_; + const T12 v12_; + const T13 v13_; + const T14 v14_; + const T15 v15_; + const T16 v16_; + const T17 v17_; + const T18 v18_; + const T19 v19_; + const T20 v20_; + const T21 v21_; + const T22 v22_; + const T23 v23_; + const T24 v24_; + const T25 v25_; + const T26 v26_; + const T27 v27_; + const T28 v28_; + const T29 v29_; + const T30 v30_; + const T31 v31_; + const T32 v32_; + const T33 v33_; + const T34 v34_; + const T35 v35_; + const T36 v36_; + const T37 v37_; + const T38 v38_; + const T39 v39_; + const T40 v40_; + const T41 v41_; + const T42 v42_; + const T43 v43_; + const T44 v44_; + const T45 v45_; + const T46 v46_; + const T47 v47_; + const T48 v48_; + const T49 v49_; + const T50 v50_; +}; + +# if GTEST_HAS_COMBINE +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Generates values from the Cartesian product of values produced +// by the argument generators. +// +template +class CartesianProductGenerator2 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator2(const ParamGenerator& g1, + const ParamGenerator& g2) + : g1_(g1), g2_(g2) {} + virtual ~CartesianProductGenerator2() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current2_; + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + ParamType current_value_; + }; // class CartesianProductGenerator2::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator2& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; +}; // class CartesianProductGenerator2 + + +template +class CartesianProductGenerator3 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator3(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3) + : g1_(g1), g2_(g2), g3_(g3) {} + virtual ~CartesianProductGenerator3() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current3_; + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + ParamType current_value_; + }; // class CartesianProductGenerator3::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator3& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; +}; // class CartesianProductGenerator3 + + +template +class CartesianProductGenerator4 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator4(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} + virtual ~CartesianProductGenerator4() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current4_; + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + ParamType current_value_; + }; // class CartesianProductGenerator4::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator4& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; +}; // class CartesianProductGenerator4 + + +template +class CartesianProductGenerator5 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator5(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} + virtual ~CartesianProductGenerator5() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current5_; + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + ParamType current_value_; + }; // class CartesianProductGenerator5::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator5& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; +}; // class CartesianProductGenerator5 + + +template +class CartesianProductGenerator6 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator6(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} + virtual ~CartesianProductGenerator6() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current6_; + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + ParamType current_value_; + }; // class CartesianProductGenerator6::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator6& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; +}; // class CartesianProductGenerator6 + + +template +class CartesianProductGenerator7 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator7(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} + virtual ~CartesianProductGenerator7() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current7_; + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + ParamType current_value_; + }; // class CartesianProductGenerator7::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator7& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; +}; // class CartesianProductGenerator7 + + +template +class CartesianProductGenerator8 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator8(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), + g8_(g8) {} + virtual ~CartesianProductGenerator8() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current8_; + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + ParamType current_value_; + }; // class CartesianProductGenerator8::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator8& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; +}; // class CartesianProductGenerator8 + + +template +class CartesianProductGenerator9 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator9(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8, const ParamGenerator& g9) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9) {} + virtual ~CartesianProductGenerator9() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end(), g9_, g9_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8, + const ParamGenerator& g9, + const typename ParamGenerator::iterator& current9) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8), + begin9_(g9.begin()), end9_(g9.end()), current9_(current9) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current9_; + if (current9_ == end9_) { + current9_ = begin9_; + ++current8_; + } + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_ && + current9_ == typed_other->current9_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_), + begin9_(other.begin9_), + end9_(other.end9_), + current9_(other.current9_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_, + *current9_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_ || + current9_ == end9_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + const typename ParamGenerator::iterator begin9_; + const typename ParamGenerator::iterator end9_; + typename ParamGenerator::iterator current9_; + ParamType current_value_; + }; // class CartesianProductGenerator9::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator9& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; + const ParamGenerator g9_; +}; // class CartesianProductGenerator9 + + +template +class CartesianProductGenerator10 + : public ParamGeneratorInterface< ::std::tr1::tuple > { + public: + typedef ::std::tr1::tuple ParamType; + + CartesianProductGenerator10(const ParamGenerator& g1, + const ParamGenerator& g2, const ParamGenerator& g3, + const ParamGenerator& g4, const ParamGenerator& g5, + const ParamGenerator& g6, const ParamGenerator& g7, + const ParamGenerator& g8, const ParamGenerator& g9, + const ParamGenerator& g10) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9), g10_(g10) {} + virtual ~CartesianProductGenerator10() {} + + virtual ParamIteratorInterface* Begin() const { + return new Iterator(this, g1_, g1_.begin(), g2_, g2_.begin(), g3_, + g3_.begin(), g4_, g4_.begin(), g5_, g5_.begin(), g6_, g6_.begin(), g7_, + g7_.begin(), g8_, g8_.begin(), g9_, g9_.begin(), g10_, g10_.begin()); + } + virtual ParamIteratorInterface* End() const { + return new Iterator(this, g1_, g1_.end(), g2_, g2_.end(), g3_, g3_.end(), + g4_, g4_.end(), g5_, g5_.end(), g6_, g6_.end(), g7_, g7_.end(), g8_, + g8_.end(), g9_, g9_.end(), g10_, g10_.end()); + } + + private: + class Iterator : public ParamIteratorInterface { + public: + Iterator(const ParamGeneratorInterface* base, + const ParamGenerator& g1, + const typename ParamGenerator::iterator& current1, + const ParamGenerator& g2, + const typename ParamGenerator::iterator& current2, + const ParamGenerator& g3, + const typename ParamGenerator::iterator& current3, + const ParamGenerator& g4, + const typename ParamGenerator::iterator& current4, + const ParamGenerator& g5, + const typename ParamGenerator::iterator& current5, + const ParamGenerator& g6, + const typename ParamGenerator::iterator& current6, + const ParamGenerator& g7, + const typename ParamGenerator::iterator& current7, + const ParamGenerator& g8, + const typename ParamGenerator::iterator& current8, + const ParamGenerator& g9, + const typename ParamGenerator::iterator& current9, + const ParamGenerator& g10, + const typename ParamGenerator::iterator& current10) + : base_(base), + begin1_(g1.begin()), end1_(g1.end()), current1_(current1), + begin2_(g2.begin()), end2_(g2.end()), current2_(current2), + begin3_(g3.begin()), end3_(g3.end()), current3_(current3), + begin4_(g4.begin()), end4_(g4.end()), current4_(current4), + begin5_(g5.begin()), end5_(g5.end()), current5_(current5), + begin6_(g6.begin()), end6_(g6.end()), current6_(current6), + begin7_(g7.begin()), end7_(g7.end()), current7_(current7), + begin8_(g8.begin()), end8_(g8.end()), current8_(current8), + begin9_(g9.begin()), end9_(g9.end()), current9_(current9), + begin10_(g10.begin()), end10_(g10.end()), current10_(current10) { + ComputeCurrentValue(); + } + virtual ~Iterator() {} + + virtual const ParamGeneratorInterface* BaseGenerator() const { + return base_; + } + // Advance should not be called on beyond-of-range iterators + // so no component iterators must be beyond end of range, either. + virtual void Advance() { + assert(!AtEnd()); + ++current10_; + if (current10_ == end10_) { + current10_ = begin10_; + ++current9_; + } + if (current9_ == end9_) { + current9_ = begin9_; + ++current8_; + } + if (current8_ == end8_) { + current8_ = begin8_; + ++current7_; + } + if (current7_ == end7_) { + current7_ = begin7_; + ++current6_; + } + if (current6_ == end6_) { + current6_ = begin6_; + ++current5_; + } + if (current5_ == end5_) { + current5_ = begin5_; + ++current4_; + } + if (current4_ == end4_) { + current4_ = begin4_; + ++current3_; + } + if (current3_ == end3_) { + current3_ = begin3_; + ++current2_; + } + if (current2_ == end2_) { + current2_ = begin2_; + ++current1_; + } + ComputeCurrentValue(); + } + virtual ParamIteratorInterface* Clone() const { + return new Iterator(*this); + } + virtual const ParamType* Current() const { return ¤t_value_; } + virtual bool Equals(const ParamIteratorInterface& other) const { + // Having the same base generator guarantees that the other + // iterator is of the same type and we can downcast. + GTEST_CHECK_(BaseGenerator() == other.BaseGenerator()) + << "The program attempted to compare iterators " + << "from different generators." << std::endl; + const Iterator* typed_other = + CheckedDowncastToActualType(&other); + // We must report iterators equal if they both point beyond their + // respective ranges. That can happen in a variety of fashions, + // so we have to consult AtEnd(). + return (AtEnd() && typed_other->AtEnd()) || + ( + current1_ == typed_other->current1_ && + current2_ == typed_other->current2_ && + current3_ == typed_other->current3_ && + current4_ == typed_other->current4_ && + current5_ == typed_other->current5_ && + current6_ == typed_other->current6_ && + current7_ == typed_other->current7_ && + current8_ == typed_other->current8_ && + current9_ == typed_other->current9_ && + current10_ == typed_other->current10_); + } + + private: + Iterator(const Iterator& other) + : base_(other.base_), + begin1_(other.begin1_), + end1_(other.end1_), + current1_(other.current1_), + begin2_(other.begin2_), + end2_(other.end2_), + current2_(other.current2_), + begin3_(other.begin3_), + end3_(other.end3_), + current3_(other.current3_), + begin4_(other.begin4_), + end4_(other.end4_), + current4_(other.current4_), + begin5_(other.begin5_), + end5_(other.end5_), + current5_(other.current5_), + begin6_(other.begin6_), + end6_(other.end6_), + current6_(other.current6_), + begin7_(other.begin7_), + end7_(other.end7_), + current7_(other.current7_), + begin8_(other.begin8_), + end8_(other.end8_), + current8_(other.current8_), + begin9_(other.begin9_), + end9_(other.end9_), + current9_(other.current9_), + begin10_(other.begin10_), + end10_(other.end10_), + current10_(other.current10_) { + ComputeCurrentValue(); + } + + void ComputeCurrentValue() { + if (!AtEnd()) + current_value_ = ParamType(*current1_, *current2_, *current3_, + *current4_, *current5_, *current6_, *current7_, *current8_, + *current9_, *current10_); + } + bool AtEnd() const { + // We must report iterator past the end of the range when either of the + // component iterators has reached the end of its range. + return + current1_ == end1_ || + current2_ == end2_ || + current3_ == end3_ || + current4_ == end4_ || + current5_ == end5_ || + current6_ == end6_ || + current7_ == end7_ || + current8_ == end8_ || + current9_ == end9_ || + current10_ == end10_; + } + + // No implementation - assignment is unsupported. + void operator=(const Iterator& other); + + const ParamGeneratorInterface* const base_; + // begin[i]_ and end[i]_ define the i-th range that Iterator traverses. + // current[i]_ is the actual traversing iterator. + const typename ParamGenerator::iterator begin1_; + const typename ParamGenerator::iterator end1_; + typename ParamGenerator::iterator current1_; + const typename ParamGenerator::iterator begin2_; + const typename ParamGenerator::iterator end2_; + typename ParamGenerator::iterator current2_; + const typename ParamGenerator::iterator begin3_; + const typename ParamGenerator::iterator end3_; + typename ParamGenerator::iterator current3_; + const typename ParamGenerator::iterator begin4_; + const typename ParamGenerator::iterator end4_; + typename ParamGenerator::iterator current4_; + const typename ParamGenerator::iterator begin5_; + const typename ParamGenerator::iterator end5_; + typename ParamGenerator::iterator current5_; + const typename ParamGenerator::iterator begin6_; + const typename ParamGenerator::iterator end6_; + typename ParamGenerator::iterator current6_; + const typename ParamGenerator::iterator begin7_; + const typename ParamGenerator::iterator end7_; + typename ParamGenerator::iterator current7_; + const typename ParamGenerator::iterator begin8_; + const typename ParamGenerator::iterator end8_; + typename ParamGenerator::iterator current8_; + const typename ParamGenerator::iterator begin9_; + const typename ParamGenerator::iterator end9_; + typename ParamGenerator::iterator current9_; + const typename ParamGenerator::iterator begin10_; + const typename ParamGenerator::iterator end10_; + typename ParamGenerator::iterator current10_; + ParamType current_value_; + }; // class CartesianProductGenerator10::Iterator + + // No implementation - assignment is unsupported. + void operator=(const CartesianProductGenerator10& other); + + const ParamGenerator g1_; + const ParamGenerator g2_; + const ParamGenerator g3_; + const ParamGenerator g4_; + const ParamGenerator g5_; + const ParamGenerator g6_; + const ParamGenerator g7_; + const ParamGenerator g8_; + const ParamGenerator g9_; + const ParamGenerator g10_; +}; // class CartesianProductGenerator10 + + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Helper classes providing Combine() with polymorphic features. They allow +// casting CartesianProductGeneratorN to ParamGenerator if T is +// convertible to U. +// +template +class CartesianProductHolder2 { + public: +CartesianProductHolder2(const Generator1& g1, const Generator2& g2) + : g1_(g1), g2_(g2) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator2( + static_cast >(g1_), + static_cast >(g2_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder2& other); + + const Generator1 g1_; + const Generator2 g2_; +}; // class CartesianProductHolder2 + +template +class CartesianProductHolder3 { + public: +CartesianProductHolder3(const Generator1& g1, const Generator2& g2, + const Generator3& g3) + : g1_(g1), g2_(g2), g3_(g3) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator3( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder3& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; +}; // class CartesianProductHolder3 + +template +class CartesianProductHolder4 { + public: +CartesianProductHolder4(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator4( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder4& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; +}; // class CartesianProductHolder4 + +template +class CartesianProductHolder5 { + public: +CartesianProductHolder5(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator5( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder5& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; +}; // class CartesianProductHolder5 + +template +class CartesianProductHolder6 { + public: +CartesianProductHolder6(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator6( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder6& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; +}; // class CartesianProductHolder6 + +template +class CartesianProductHolder7 { + public: +CartesianProductHolder7(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator7( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder7& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; +}; // class CartesianProductHolder7 + +template +class CartesianProductHolder8 { + public: +CartesianProductHolder8(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), + g8_(g8) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator8( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder8& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; +}; // class CartesianProductHolder8 + +template +class CartesianProductHolder9 { + public: +CartesianProductHolder9(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8, + const Generator9& g9) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator9( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_), + static_cast >(g9_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder9& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; + const Generator9 g9_; +}; // class CartesianProductHolder9 + +template +class CartesianProductHolder10 { + public: +CartesianProductHolder10(const Generator1& g1, const Generator2& g2, + const Generator3& g3, const Generator4& g4, const Generator5& g5, + const Generator6& g6, const Generator7& g7, const Generator8& g8, + const Generator9& g9, const Generator10& g10) + : g1_(g1), g2_(g2), g3_(g3), g4_(g4), g5_(g5), g6_(g6), g7_(g7), g8_(g8), + g9_(g9), g10_(g10) {} + template + operator ParamGenerator< ::std::tr1::tuple >() const { + return ParamGenerator< ::std::tr1::tuple >( + new CartesianProductGenerator10( + static_cast >(g1_), + static_cast >(g2_), + static_cast >(g3_), + static_cast >(g4_), + static_cast >(g5_), + static_cast >(g6_), + static_cast >(g7_), + static_cast >(g8_), + static_cast >(g9_), + static_cast >(g10_))); + } + + private: + // No implementation - assignment is unsupported. + void operator=(const CartesianProductHolder10& other); + + const Generator1 g1_; + const Generator2 g2_; + const Generator3 g3_; + const Generator4 g4_; + const Generator5 g5_; + const Generator6 g6_; + const Generator7 g7_; + const Generator8 g8_; + const Generator9 g9_; + const Generator10 g10_; +}; // class CartesianProductHolder10 + +# endif // GTEST_HAS_COMBINE + +} // namespace internal +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_INTERNAL_GTEST_PARAM_UTIL_GENERATED_H_ + +#if GTEST_HAS_PARAM_TEST + +namespace testing { + +// Functions producing parameter generators. +// +// Google Test uses these generators to produce parameters for value- +// parameterized tests. When a parameterized test case is instantiated +// with a particular generator, Google Test creates and runs tests +// for each element in the sequence produced by the generator. +// +// In the following sample, tests from test case FooTest are instantiated +// each three times with parameter values 3, 5, and 8: +// +// class FooTest : public TestWithParam { ... }; +// +// TEST_P(FooTest, TestThis) { +// } +// TEST_P(FooTest, TestThat) { +// } +// INSTANTIATE_TEST_CASE_P(TestSequence, FooTest, Values(3, 5, 8)); +// + +// Range() returns generators providing sequences of values in a range. +// +// Synopsis: +// Range(start, end) +// - returns a generator producing a sequence of values {start, start+1, +// start+2, ..., }. +// Range(start, end, step) +// - returns a generator producing a sequence of values {start, start+step, +// start+step+step, ..., }. +// Notes: +// * The generated sequences never include end. For example, Range(1, 5) +// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2) +// returns a generator producing {1, 3, 5, 7}. +// * start and end must have the same type. That type may be any integral or +// floating-point type or a user defined type satisfying these conditions: +// * It must be assignable (have operator=() defined). +// * It must have operator+() (operator+(int-compatible type) for +// two-operand version). +// * It must have operator<() defined. +// Elements in the resulting sequences will also have that type. +// * Condition start < end must be satisfied in order for resulting sequences +// to contain any elements. +// +template +internal::ParamGenerator Range(T start, T end, IncrementT step) { + return internal::ParamGenerator( + new internal::RangeGenerator(start, end, step)); +} + +template +internal::ParamGenerator Range(T start, T end) { + return Range(start, end, 1); +} + +// ValuesIn() function allows generation of tests with parameters coming from +// a container. +// +// Synopsis: +// ValuesIn(const T (&array)[N]) +// - returns a generator producing sequences with elements from +// a C-style array. +// ValuesIn(const Container& container) +// - returns a generator producing sequences with elements from +// an STL-style container. +// ValuesIn(Iterator begin, Iterator end) +// - returns a generator producing sequences with elements from +// a range [begin, end) defined by a pair of STL-style iterators. These +// iterators can also be plain C pointers. +// +// Please note that ValuesIn copies the values from the containers +// passed in and keeps them to generate tests in RUN_ALL_TESTS(). +// +// Examples: +// +// This instantiates tests from test case StringTest +// each with C-string values of "foo", "bar", and "baz": +// +// const char* strings[] = {"foo", "bar", "baz"}; +// INSTANTIATE_TEST_CASE_P(StringSequence, SrtingTest, ValuesIn(strings)); +// +// This instantiates tests from test case StlStringTest +// each with STL strings with values "a" and "b": +// +// ::std::vector< ::std::string> GetParameterStrings() { +// ::std::vector< ::std::string> v; +// v.push_back("a"); +// v.push_back("b"); +// return v; +// } +// +// INSTANTIATE_TEST_CASE_P(CharSequence, +// StlStringTest, +// ValuesIn(GetParameterStrings())); +// +// +// This will also instantiate tests from CharTest +// each with parameter values 'a' and 'b': +// +// ::std::list GetParameterChars() { +// ::std::list list; +// list.push_back('a'); +// list.push_back('b'); +// return list; +// } +// ::std::list l = GetParameterChars(); +// INSTANTIATE_TEST_CASE_P(CharSequence2, +// CharTest, +// ValuesIn(l.begin(), l.end())); +// +template +internal::ParamGenerator< + typename ::testing::internal::IteratorTraits::value_type> +ValuesIn(ForwardIterator begin, ForwardIterator end) { + typedef typename ::testing::internal::IteratorTraits + ::value_type ParamType; + return internal::ParamGenerator( + new internal::ValuesInIteratorRangeGenerator(begin, end)); +} + +template +internal::ParamGenerator ValuesIn(const T (&array)[N]) { + return ValuesIn(array, array + N); +} + +template +internal::ParamGenerator ValuesIn( + const Container& container) { + return ValuesIn(container.begin(), container.end()); +} + +// Values() allows generating tests from explicitly specified list of +// parameters. +// +// Synopsis: +// Values(T v1, T v2, ..., T vN) +// - returns a generator producing sequences with elements v1, v2, ..., vN. +// +// For example, this instantiates tests from test case BarTest each +// with values "one", "two", and "three": +// +// INSTANTIATE_TEST_CASE_P(NumSequence, BarTest, Values("one", "two", "three")); +// +// This instantiates tests from test case BazTest each with values 1, 2, 3.5. +// The exact type of values will depend on the type of parameter in BazTest. +// +// INSTANTIATE_TEST_CASE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5)); +// +// Currently, Values() supports from 1 to 50 parameters. +// +template +internal::ValueArray1 Values(T1 v1) { + return internal::ValueArray1(v1); +} + +template +internal::ValueArray2 Values(T1 v1, T2 v2) { + return internal::ValueArray2(v1, v2); +} + +template +internal::ValueArray3 Values(T1 v1, T2 v2, T3 v3) { + return internal::ValueArray3(v1, v2, v3); +} + +template +internal::ValueArray4 Values(T1 v1, T2 v2, T3 v3, T4 v4) { + return internal::ValueArray4(v1, v2, v3, v4); +} + +template +internal::ValueArray5 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5) { + return internal::ValueArray5(v1, v2, v3, v4, v5); +} + +template +internal::ValueArray6 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6) { + return internal::ValueArray6(v1, v2, v3, v4, v5, v6); +} + +template +internal::ValueArray7 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7) { + return internal::ValueArray7(v1, v2, v3, v4, v5, + v6, v7); +} + +template +internal::ValueArray8 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8) { + return internal::ValueArray8(v1, v2, v3, v4, + v5, v6, v7, v8); +} + +template +internal::ValueArray9 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9) { + return internal::ValueArray9(v1, v2, v3, + v4, v5, v6, v7, v8, v9); +} + +template +internal::ValueArray10 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10) { + return internal::ValueArray10(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10); +} + +template +internal::ValueArray11 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11) { + return internal::ValueArray11(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11); +} + +template +internal::ValueArray12 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12) { + return internal::ValueArray12(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12); +} + +template +internal::ValueArray13 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13) { + return internal::ValueArray13(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13); +} + +template +internal::ValueArray14 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14) { + return internal::ValueArray14(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14); +} + +template +internal::ValueArray15 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15) { + return internal::ValueArray15(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15); +} + +template +internal::ValueArray16 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16) { + return internal::ValueArray16(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16); +} + +template +internal::ValueArray17 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17) { + return internal::ValueArray17(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17); +} + +template +internal::ValueArray18 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18) { + return internal::ValueArray18(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18); +} + +template +internal::ValueArray19 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19) { + return internal::ValueArray19(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19); +} + +template +internal::ValueArray20 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20) { + return internal::ValueArray20(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20); +} + +template +internal::ValueArray21 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21) { + return internal::ValueArray21(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21); +} + +template +internal::ValueArray22 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22) { + return internal::ValueArray22(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22); +} + +template +internal::ValueArray23 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23) { + return internal::ValueArray23(v1, v2, v3, + v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23); +} + +template +internal::ValueArray24 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24) { + return internal::ValueArray24(v1, v2, + v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, + v19, v20, v21, v22, v23, v24); +} + +template +internal::ValueArray25 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, + T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, + T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25) { + return internal::ValueArray25(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, + v18, v19, v20, v21, v22, v23, v24, v25); +} + +template +internal::ValueArray26 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26) { + return internal::ValueArray26(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26); +} + +template +internal::ValueArray27 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27) { + return internal::ValueArray27(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, + v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27); +} + +template +internal::ValueArray28 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28) { + return internal::ValueArray28(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, + v28); +} + +template +internal::ValueArray29 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29) { + return internal::ValueArray29(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, + v27, v28, v29); +} + +template +internal::ValueArray30 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30) { + return internal::ValueArray30(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, + v26, v27, v28, v29, v30); +} + +template +internal::ValueArray31 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31) { + return internal::ValueArray31(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, + v25, v26, v27, v28, v29, v30, v31); +} + +template +internal::ValueArray32 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32) { + return internal::ValueArray32(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32); +} + +template +internal::ValueArray33 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33) { + return internal::ValueArray33(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33); +} + +template +internal::ValueArray34 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, + T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, + T31 v31, T32 v32, T33 v33, T34 v34) { + return internal::ValueArray34(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, + v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34); +} + +template +internal::ValueArray35 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35) { + return internal::ValueArray35(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, + v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35); +} + +template +internal::ValueArray36 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36) { + return internal::ValueArray36(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36); +} + +template +internal::ValueArray37 Values(T1 v1, T2 v2, T3 v3, + T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37) { + return internal::ValueArray37(v1, v2, v3, + v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36, v37); +} + +template +internal::ValueArray38 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37, T38 v38) { + return internal::ValueArray38(v1, v2, + v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, + v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, + v33, v34, v35, v36, v37, v38); +} + +template +internal::ValueArray39 Values(T1 v1, T2 v2, + T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, + T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, + T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, + T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, + T37 v37, T38 v38, T39 v39) { + return internal::ValueArray39(v1, + v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, + v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, + v32, v33, v34, v35, v36, v37, v38, v39); +} + +template +internal::ValueArray40 Values(T1 v1, + T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, + T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, + T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, + T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, + T36 v36, T37 v37, T38 v38, T39 v39, T40 v40) { + return internal::ValueArray40(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, + v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, + v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40); +} + +template +internal::ValueArray41 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41) { + return internal::ValueArray41(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, + v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, v28, + v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41); +} + +template +internal::ValueArray42 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42) { + return internal::ValueArray42(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, v13, + v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, v27, + v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, v41, + v42); +} + +template +internal::ValueArray43 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43) { + return internal::ValueArray43(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, v12, + v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, v26, + v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, v40, + v41, v42, v43); +} + +template +internal::ValueArray44 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, + T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, T17 v17, + T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, T25 v25, + T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, T33 v33, + T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, T41 v41, + T42 v42, T43 v43, T44 v44) { + return internal::ValueArray44(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, v11, + v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, v25, + v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, v39, + v40, v41, v42, v43, v44); +} + +template +internal::ValueArray45 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, T8 v8, + T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, T16 v16, + T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, T24 v24, + T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, T32 v32, + T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, T40 v40, + T41 v41, T42 v42, T43 v43, T44 v44, T45 v45) { + return internal::ValueArray45(v1, v2, v3, v4, v5, v6, v7, v8, v9, v10, + v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, v24, + v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, v38, + v39, v40, v41, v42, v43, v44, v45); +} + +template +internal::ValueArray46 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46) { + return internal::ValueArray46(v1, v2, v3, v4, v5, v6, v7, v8, v9, + v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, + v38, v39, v40, v41, v42, v43, v44, v45, v46); +} + +template +internal::ValueArray47 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, T7 v7, + T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47) { + return internal::ValueArray47(v1, v2, v3, v4, v5, v6, v7, v8, + v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, v23, + v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, v37, + v38, v39, v40, v41, v42, v43, v44, v45, v46, v47); +} + +template +internal::ValueArray48 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, T6 v6, + T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, T15 v15, + T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, T23 v23, + T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, T31 v31, + T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, T39 v39, + T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, T47 v47, + T48 v48) { + return internal::ValueArray48(v1, v2, v3, v4, v5, v6, v7, + v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, v22, + v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, v36, + v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48); +} + +template +internal::ValueArray49 Values(T1 v1, T2 v2, T3 v3, T4 v4, T5 v5, + T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, T14 v14, + T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, T22 v22, + T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, T30 v30, + T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, T38 v38, + T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, T46 v46, + T47 v47, T48 v48, T49 v49) { + return internal::ValueArray49(v1, v2, v3, v4, v5, v6, + v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, v20, v21, + v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, v34, v35, + v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, v48, v49); +} + +template +internal::ValueArray50 Values(T1 v1, T2 v2, T3 v3, T4 v4, + T5 v5, T6 v6, T7 v7, T8 v8, T9 v9, T10 v10, T11 v11, T12 v12, T13 v13, + T14 v14, T15 v15, T16 v16, T17 v17, T18 v18, T19 v19, T20 v20, T21 v21, + T22 v22, T23 v23, T24 v24, T25 v25, T26 v26, T27 v27, T28 v28, T29 v29, + T30 v30, T31 v31, T32 v32, T33 v33, T34 v34, T35 v35, T36 v36, T37 v37, + T38 v38, T39 v39, T40 v40, T41 v41, T42 v42, T43 v43, T44 v44, T45 v45, + T46 v46, T47 v47, T48 v48, T49 v49, T50 v50) { + return internal::ValueArray50(v1, v2, v3, v4, + v5, v6, v7, v8, v9, v10, v11, v12, v13, v14, v15, v16, v17, v18, v19, + v20, v21, v22, v23, v24, v25, v26, v27, v28, v29, v30, v31, v32, v33, + v34, v35, v36, v37, v38, v39, v40, v41, v42, v43, v44, v45, v46, v47, + v48, v49, v50); +} + +// Bool() allows generating tests with parameters in a set of (false, true). +// +// Synopsis: +// Bool() +// - returns a generator producing sequences with elements {false, true}. +// +// It is useful when testing code that depends on Boolean flags. Combinations +// of multiple flags can be tested when several Bool()'s are combined using +// Combine() function. +// +// In the following example all tests in the test case FlagDependentTest +// will be instantiated twice with parameters false and true. +// +// class FlagDependentTest : public testing::TestWithParam { +// virtual void SetUp() { +// external_flag = GetParam(); +// } +// } +// INSTANTIATE_TEST_CASE_P(BoolSequence, FlagDependentTest, Bool()); +// +inline internal::ParamGenerator Bool() { + return Values(false, true); +} + +# if GTEST_HAS_COMBINE +// Combine() allows the user to combine two or more sequences to produce +// values of a Cartesian product of those sequences' elements. +// +// Synopsis: +// Combine(gen1, gen2, ..., genN) +// - returns a generator producing sequences with elements coming from +// the Cartesian product of elements from the sequences generated by +// gen1, gen2, ..., genN. The sequence elements will have a type of +// tuple where T1, T2, ..., TN are the types +// of elements from sequences produces by gen1, gen2, ..., genN. +// +// Combine can have up to 10 arguments. This number is currently limited +// by the maximum number of elements in the tuple implementation used by Google +// Test. +// +// Example: +// +// This will instantiate tests in test case AnimalTest each one with +// the parameter values tuple("cat", BLACK), tuple("cat", WHITE), +// tuple("dog", BLACK), and tuple("dog", WHITE): +// +// enum Color { BLACK, GRAY, WHITE }; +// class AnimalTest +// : public testing::TestWithParam > {...}; +// +// TEST_P(AnimalTest, AnimalLooksNice) {...} +// +// INSTANTIATE_TEST_CASE_P(AnimalVariations, AnimalTest, +// Combine(Values("cat", "dog"), +// Values(BLACK, WHITE))); +// +// This will instantiate tests in FlagDependentTest with all variations of two +// Boolean flags: +// +// class FlagDependentTest +// : public testing::TestWithParam > { +// virtual void SetUp() { +// // Assigns external_flag_1 and external_flag_2 values from the tuple. +// tie(external_flag_1, external_flag_2) = GetParam(); +// } +// }; +// +// TEST_P(FlagDependentTest, TestFeature1) { +// // Test your code using external_flag_1 and external_flag_2 here. +// } +// INSTANTIATE_TEST_CASE_P(TwoBoolSequence, FlagDependentTest, +// Combine(Bool(), Bool())); +// +template +internal::CartesianProductHolder2 Combine( + const Generator1& g1, const Generator2& g2) { + return internal::CartesianProductHolder2( + g1, g2); +} + +template +internal::CartesianProductHolder3 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3) { + return internal::CartesianProductHolder3( + g1, g2, g3); +} + +template +internal::CartesianProductHolder4 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4) { + return internal::CartesianProductHolder4( + g1, g2, g3, g4); +} + +template +internal::CartesianProductHolder5 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5) { + return internal::CartesianProductHolder5( + g1, g2, g3, g4, g5); +} + +template +internal::CartesianProductHolder6 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6) { + return internal::CartesianProductHolder6( + g1, g2, g3, g4, g5, g6); +} + +template +internal::CartesianProductHolder7 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7) { + return internal::CartesianProductHolder7( + g1, g2, g3, g4, g5, g6, g7); +} + +template +internal::CartesianProductHolder8 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8) { + return internal::CartesianProductHolder8( + g1, g2, g3, g4, g5, g6, g7, g8); +} + +template +internal::CartesianProductHolder9 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8, const Generator9& g9) { + return internal::CartesianProductHolder9( + g1, g2, g3, g4, g5, g6, g7, g8, g9); +} + +template +internal::CartesianProductHolder10 Combine( + const Generator1& g1, const Generator2& g2, const Generator3& g3, + const Generator4& g4, const Generator5& g5, const Generator6& g6, + const Generator7& g7, const Generator8& g8, const Generator9& g9, + const Generator10& g10) { + return internal::CartesianProductHolder10( + g1, g2, g3, g4, g5, g6, g7, g8, g9, g10); +} +# endif // GTEST_HAS_COMBINE + + + +# define TEST_P(test_case_name, test_name) \ + class GTEST_TEST_CLASS_NAME_(test_case_name, test_name) \ + : public test_case_name { \ + public: \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)() {} \ + virtual void TestBody(); \ + private: \ + static int AddToRegistry() { \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder(\ + #test_case_name, __FILE__, __LINE__)->AddTestPattern(\ + #test_case_name, \ + #test_name, \ + new ::testing::internal::TestMetaFactory< \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)>()); \ + return 0; \ + } \ + static int gtest_registering_dummy_; \ + GTEST_DISALLOW_COPY_AND_ASSIGN_(\ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)); \ + }; \ + int GTEST_TEST_CLASS_NAME_(test_case_name, \ + test_name)::gtest_registering_dummy_ = \ + GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::AddToRegistry(); \ + void GTEST_TEST_CLASS_NAME_(test_case_name, test_name)::TestBody() + +# define INSTANTIATE_TEST_CASE_P(prefix, test_case_name, generator) \ + ::testing::internal::ParamGenerator \ + gtest_##prefix##test_case_name##_EvalGenerator_() { return generator; } \ + int gtest_##prefix##test_case_name##_dummy_ = \ + ::testing::UnitTest::GetInstance()->parameterized_test_registry(). \ + GetTestCasePatternHolder(\ + #test_case_name, __FILE__, __LINE__)->AddTestCaseInstantiation(\ + #prefix, \ + >est_##prefix##test_case_name##_EvalGenerator_, \ + __FILE__, __LINE__) + +} // namespace testing + +#endif // GTEST_HAS_PARAM_TEST + +#endif // GTEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) +// +// Google C++ Testing Framework definitions useful in production code. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PROD_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PROD_H_ + +// When you need to test the private or protected members of a class, +// use the FRIEND_TEST macro to declare your tests as friends of the +// class. For example: +// +// class MyClass { +// private: +// void MyMethod(); +// FRIEND_TEST(MyClassTest, MyMethod); +// }; +// +// class MyClassTest : public testing::Test { +// // ... +// }; +// +// TEST_F(MyClassTest, MyMethod) { +// // Can call MyClass::MyMethod() here. +// } + +#define FRIEND_TEST(test_case_name, test_name)\ +friend class test_case_name##_##test_name##_Test + +#endif // GTEST_INCLUDE_GTEST_GTEST_PROD_H_ +// Copyright 2008, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: mheule@google.com (Markus Heule) +// + +#ifndef GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +#define GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ + +#include +#include + +namespace testing { + +// A copyable object representing the result of a test part (i.e. an +// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()). +// +// Don't inherit from TestPartResult as its destructor is not virtual. +class GTEST_API_ TestPartResult { + public: + // The possible outcomes of a test part (i.e. an assertion or an + // explicit SUCCEED(), FAIL(), or ADD_FAILURE()). + enum Type { + kSuccess, // Succeeded. + kNonFatalFailure, // Failed but the test can continue. + kFatalFailure // Failed and the test should be terminated. + }; + + // C'tor. TestPartResult does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestPartResult object. + TestPartResult(Type a_type, + const char* a_file_name, + int a_line_number, + const char* a_message) + : type_(a_type), + file_name_(a_file_name == NULL ? "" : a_file_name), + line_number_(a_line_number), + summary_(ExtractSummary(a_message)), + message_(a_message) { + } + + // Gets the outcome of the test part. + Type type() const { return type_; } + + // Gets the name of the source file where the test part took place, or + // NULL if it's unknown. + const char* file_name() const { + return file_name_.empty() ? NULL : file_name_.c_str(); + } + + // Gets the line in the source file where the test part took place, + // or -1 if it's unknown. + int line_number() const { return line_number_; } + + // Gets the summary of the failure message. + const char* summary() const { return summary_.c_str(); } + + // Gets the message associated with the test part. + const char* message() const { return message_.c_str(); } + + // Returns true iff the test part passed. + bool passed() const { return type_ == kSuccess; } + + // Returns true iff the test part failed. + bool failed() const { return type_ != kSuccess; } + + // Returns true iff the test part non-fatally failed. + bool nonfatally_failed() const { return type_ == kNonFatalFailure; } + + // Returns true iff the test part fatally failed. + bool fatally_failed() const { return type_ == kFatalFailure; } + + private: + Type type_; + + // Gets the summary of the failure message by omitting the stack + // trace in it. + static std::string ExtractSummary(const char* message); + + // The name of the source file where the test part took place, or + // "" if the source file is unknown. + std::string file_name_; + // The line in the source file where the test part took place, or -1 + // if the line number is unknown. + int line_number_; + std::string summary_; // The test failure summary. + std::string message_; // The test failure message. +}; + +// Prints a TestPartResult object. +std::ostream& operator<<(std::ostream& os, const TestPartResult& result); + +// An array of TestPartResult objects. +// +// Don't inherit from TestPartResultArray as its destructor is not +// virtual. +class GTEST_API_ TestPartResultArray { + public: + TestPartResultArray() {} + + // Appends the given TestPartResult to the array. + void Append(const TestPartResult& result); + + // Returns the TestPartResult at the given index (0-based). + const TestPartResult& GetTestPartResult(int index) const; + + // Returns the number of TestPartResult objects in the array. + int size() const; + + private: + std::vector array_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestPartResultArray); +}; + +// This interface knows how to report a test part result. +class TestPartResultReporterInterface { + public: + virtual ~TestPartResultReporterInterface() {} + + virtual void ReportTestPartResult(const TestPartResult& result) = 0; +}; + +namespace internal { + +// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a +// statement generates new fatal failures. To do so it registers itself as the +// current test part result reporter. Besides checking if fatal failures were +// reported, it only delegates the reporting to the former result reporter. +// The original result reporter is restored in the destructor. +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +class GTEST_API_ HasNewFatalFailureHelper + : public TestPartResultReporterInterface { + public: + HasNewFatalFailureHelper(); + virtual ~HasNewFatalFailureHelper(); + virtual void ReportTestPartResult(const TestPartResult& result); + bool has_new_fatal_failure() const { return has_new_fatal_failure_; } + private: + bool has_new_fatal_failure_; + TestPartResultReporterInterface* original_reporter_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(HasNewFatalFailureHelper); +}; + +} // namespace internal + +} // namespace testing + +#endif // GTEST_INCLUDE_GTEST_GTEST_TEST_PART_H_ +// Copyright 2008 Google Inc. +// All Rights Reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +// +// Author: wan@google.com (Zhanyong Wan) + +#ifndef GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ +#define GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// This header implements typed tests and type-parameterized tests. + +// Typed (aka type-driven) tests repeat the same test for types in a +// list. You must know which types you want to test with when writing +// typed tests. Here's how you do it: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + public: + ... + typedef std::list List; + static T shared_; + T value_; +}; + +// Next, associate a list of types with the test case, which will be +// repeated for each type in the list. The typedef is necessary for +// the macro to parse correctly. +typedef testing::Types MyTypes; +TYPED_TEST_CASE(FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// TYPED_TEST_CASE(FooTest, int); + +// Then, use TYPED_TEST() instead of TEST_F() to define as many typed +// tests for this test case as you want. +TYPED_TEST(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + // Since we are inside a derived class template, C++ requires use to + // visit the members of FooTest via 'this'. + TypeParam n = this->value_; + + // To visit static members of the fixture, add the TestFixture:: + // prefix. + n += TestFixture::shared_; + + // To refer to typedefs in the fixture, add the "typename + // TestFixture::" prefix. + typename TestFixture::List values; + values.push_back(n); + ... +} + +TYPED_TEST(FooTest, HasPropertyA) { ... } + +#endif // 0 + +// Type-parameterized tests are abstract test patterns parameterized +// by a type. Compared with typed tests, type-parameterized tests +// allow you to define the test pattern without knowing what the type +// parameters are. The defined pattern can be instantiated with +// different types any number of times, in any number of translation +// units. +// +// If you are designing an interface or concept, you can define a +// suite of type-parameterized tests to verify properties that any +// valid implementation of the interface/concept should have. Then, +// each implementation can easily instantiate the test suite to verify +// that it conforms to the requirements, without having to write +// similar tests repeatedly. Here's an example: + +#if 0 + +// First, define a fixture class template. It should be parameterized +// by a type. Remember to derive it from testing::Test. +template +class FooTest : public testing::Test { + ... +}; + +// Next, declare that you will define a type-parameterized test case +// (the _P suffix is for "parameterized" or "pattern", whichever you +// prefer): +TYPED_TEST_CASE_P(FooTest); + +// Then, use TYPED_TEST_P() to define as many type-parameterized tests +// for this type-parameterized test case as you want. +TYPED_TEST_P(FooTest, DoesBlah) { + // Inside a test, refer to TypeParam to get the type parameter. + TypeParam n = 0; + ... +} + +TYPED_TEST_P(FooTest, HasPropertyA) { ... } + +// Now the tricky part: you need to register all test patterns before +// you can instantiate them. The first argument of the macro is the +// test case name; the rest are the names of the tests in this test +// case. +REGISTER_TYPED_TEST_CASE_P(FooTest, + DoesBlah, HasPropertyA); + +// Finally, you are free to instantiate the pattern with the types you +// want. If you put the above code in a header file, you can #include +// it in multiple C++ source files and instantiate it multiple times. +// +// To distinguish different instances of the pattern, the first +// argument to the INSTANTIATE_* macro is a prefix that will be added +// to the actual test case name. Remember to pick unique prefixes for +// different instances. +typedef testing::Types MyTypes; +INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, MyTypes); + +// If the type list contains only one type, you can write that type +// directly without Types<...>: +// INSTANTIATE_TYPED_TEST_CASE_P(My, FooTest, int); + +#endif // 0 + + +// Implements typed tests. + +#if GTEST_HAS_TYPED_TEST + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the typedef for the type parameters of the +// given test case. +# define GTEST_TYPE_PARAMS_(TestCaseName) gtest_type_params_##TestCaseName##_ + +// The 'Types' template argument below must have spaces around it +// since some compilers may choke on '>>' when passing a template +// instance (e.g. Types) +# define TYPED_TEST_CASE(CaseName, Types) \ + typedef ::testing::internal::TypeList< Types >::type \ + GTEST_TYPE_PARAMS_(CaseName) + +# define TYPED_TEST(CaseName, TestName) \ + template \ + class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \ + : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + virtual void TestBody(); \ + }; \ + bool gtest_##CaseName##_##TestName##_registered_ GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTest< \ + CaseName, \ + ::testing::internal::TemplateSel< \ + GTEST_TEST_CLASS_NAME_(CaseName, TestName)>, \ + GTEST_TYPE_PARAMS_(CaseName)>::Register(\ + "", #CaseName, #TestName, 0); \ + template \ + void GTEST_TEST_CLASS_NAME_(CaseName, TestName)::TestBody() + +#endif // GTEST_HAS_TYPED_TEST + +// Implements type-parameterized tests. + +#if GTEST_HAS_TYPED_TEST_P + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the namespace name that the type-parameterized tests for +// the given type-parameterized test case are defined in. The exact +// name of the namespace is subject to change without notice. +# define GTEST_CASE_NAMESPACE_(TestCaseName) \ + gtest_case_##TestCaseName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// +// Expands to the name of the variable used to remember the names of +// the defined tests in the given test case. +# define GTEST_TYPED_TEST_CASE_P_STATE_(TestCaseName) \ + gtest_typed_test_case_p_state_##TestCaseName##_ + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY. +// +// Expands to the name of the variable used to remember the names of +// the registered tests in the given test case. +# define GTEST_REGISTERED_TEST_NAMES_(TestCaseName) \ + gtest_registered_test_names_##TestCaseName##_ + +// The variables defined in the type-parameterized test macros are +// static as typically these macros are used in a .h file that can be +// #included in multiple translation units linked together. +# define TYPED_TEST_CASE_P(CaseName) \ + static ::testing::internal::TypedTestCasePState \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName) + +# define TYPED_TEST_P(CaseName, TestName) \ + namespace GTEST_CASE_NAMESPACE_(CaseName) { \ + template \ + class TestName : public CaseName { \ + private: \ + typedef CaseName TestFixture; \ + typedef gtest_TypeParam_ TypeParam; \ + virtual void TestBody(); \ + }; \ + static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).AddTestName(\ + __FILE__, __LINE__, #CaseName, #TestName); \ + } \ + template \ + void GTEST_CASE_NAMESPACE_(CaseName)::TestName::TestBody() + +# define REGISTER_TYPED_TEST_CASE_P(CaseName, ...) \ + namespace GTEST_CASE_NAMESPACE_(CaseName) { \ + typedef ::testing::internal::Templates<__VA_ARGS__>::type gtest_AllTests_; \ + } \ + static const char* const GTEST_REGISTERED_TEST_NAMES_(CaseName) = \ + GTEST_TYPED_TEST_CASE_P_STATE_(CaseName).VerifyRegisteredTestNames(\ + __FILE__, __LINE__, #__VA_ARGS__) + +// The 'Types' template argument below must have spaces around it +// since some compilers may choke on '>>' when passing a template +// instance (e.g. Types) +# define INSTANTIATE_TYPED_TEST_CASE_P(Prefix, CaseName, Types) \ + bool gtest_##Prefix##_##CaseName GTEST_ATTRIBUTE_UNUSED_ = \ + ::testing::internal::TypeParameterizedTestCase::type>::Register(\ + #Prefix, #CaseName, GTEST_REGISTERED_TEST_NAMES_(CaseName)) + +#endif // GTEST_HAS_TYPED_TEST_P + +#endif // GTEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_ + +// Depending on the platform, different string classes are available. +// On Linux, in addition to ::std::string, Google also makes use of +// class ::string, which has the same interface as ::std::string, but +// has a different implementation. +// +// The user can define GTEST_HAS_GLOBAL_STRING to 1 to indicate that +// ::string is available AND is a distinct type to ::std::string, or +// define it to 0 to indicate otherwise. +// +// If the user's ::std::string and ::string are the same class due to +// aliasing, he should define GTEST_HAS_GLOBAL_STRING to 0. +// +// If the user doesn't define GTEST_HAS_GLOBAL_STRING, it is defined +// heuristically. + +namespace testing { + +// Declares the flags. + +// This flag temporary enables the disabled tests. +GTEST_DECLARE_bool_(also_run_disabled_tests); + +// This flag brings the debugger on an assertion failure. +GTEST_DECLARE_bool_(break_on_failure); + +// This flag controls whether Google Test catches all test-thrown exceptions +// and logs them as failures. +GTEST_DECLARE_bool_(catch_exceptions); + +// This flag enables using colors in terminal output. Available values are +// "yes" to enable colors, "no" (disable colors), or "auto" (the default) +// to let Google Test decide. +GTEST_DECLARE_string_(color); + +// This flag sets up the filter to select by name using a glob pattern +// the tests to run. If the filter is not given all tests are executed. +GTEST_DECLARE_string_(filter); + +// This flag causes the Google Test to list tests. None of the tests listed +// are actually run if the flag is provided. +GTEST_DECLARE_bool_(list_tests); + +// This flag controls whether Google Test emits a detailed XML report to a file +// in addition to its normal textual output. +GTEST_DECLARE_string_(output); + +// This flags control whether Google Test prints the elapsed time for each +// test. +GTEST_DECLARE_bool_(print_time); + +// This flag specifies the random number seed. +GTEST_DECLARE_int32_(random_seed); + +// This flag sets how many times the tests are repeated. The default value +// is 1. If the value is -1 the tests are repeating forever. +GTEST_DECLARE_int32_(repeat); + +// This flag controls whether Google Test includes Google Test internal +// stack frames in failure stack traces. +GTEST_DECLARE_bool_(show_internal_stack_frames); + +// When this flag is specified, tests' order is randomized on every iteration. +GTEST_DECLARE_bool_(shuffle); + +// This flag specifies the maximum number of stack frames to be +// printed in a failure message. +GTEST_DECLARE_int32_(stack_trace_depth); + +// When this flag is specified, a failed assertion will throw an +// exception if exceptions are enabled, or exit the program with a +// non-zero code otherwise. +GTEST_DECLARE_bool_(throw_on_failure); + +// When this flag is set with a "host:port" string, on supported +// platforms test results are streamed to the specified port on +// the specified host machine. +GTEST_DECLARE_string_(stream_result_to); + +// The upper limit for valid stack trace depths. +const int kMaxStackTraceDepth = 100; + +namespace internal { + +class AssertHelper; +class DefaultGlobalTestPartResultReporter; +class ExecDeathTest; +class NoExecDeathTest; +class FinalSuccessChecker; +class GTestFlagSaver; +class StreamingListenerTest; +class TestResultAccessor; +class TestEventListenersAccessor; +class TestEventRepeater; +class UnitTestRecordPropertyTestHelper; +class WindowsDeathTest; +class UnitTestImpl* GetUnitTestImpl(); +void ReportFailureInUnknownLocation(TestPartResult::Type result_type, + const std::string& message); + +} // namespace internal + +// The friend relationship of some of these classes is cyclic. +// If we don't forward declare them the compiler might confuse the classes +// in friendship clauses with same named classes on the scope. +class Test; +class TestCase; +class TestInfo; +class UnitTest; + +// A class for indicating whether an assertion was successful. When +// the assertion wasn't successful, the AssertionResult object +// remembers a non-empty message that describes how it failed. +// +// To create an instance of this class, use one of the factory functions +// (AssertionSuccess() and AssertionFailure()). +// +// This class is useful for two purposes: +// 1. Defining predicate functions to be used with Boolean test assertions +// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts +// 2. Defining predicate-format functions to be +// used with predicate assertions (ASSERT_PRED_FORMAT*, etc). +// +// For example, if you define IsEven predicate: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5))) +// will print the message +// +// Value of: IsEven(Fib(5)) +// Actual: false (5 is odd) +// Expected: true +// +// instead of a more opaque +// +// Value of: IsEven(Fib(5)) +// Actual: false +// Expected: true +// +// in case IsEven is a simple Boolean predicate. +// +// If you expect your predicate to be reused and want to support informative +// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up +// about half as often as positive ones in our tests), supply messages for +// both success and failure cases: +// +// testing::AssertionResult IsEven(int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess() << n << " is even"; +// else +// return testing::AssertionFailure() << n << " is odd"; +// } +// +// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print +// +// Value of: IsEven(Fib(6)) +// Actual: true (8 is even) +// Expected: false +// +// NB: Predicates that support negative Boolean assertions have reduced +// performance in positive ones so be careful not to use them in tests +// that have lots (tens of thousands) of positive Boolean assertions. +// +// To use this class with EXPECT_PRED_FORMAT assertions such as: +// +// // Verifies that Foo() returns an even number. +// EXPECT_PRED_FORMAT1(IsEven, Foo()); +// +// you need to define: +// +// testing::AssertionResult IsEven(const char* expr, int n) { +// if ((n % 2) == 0) +// return testing::AssertionSuccess(); +// else +// return testing::AssertionFailure() +// << "Expected: " << expr << " is even\n Actual: it's " << n; +// } +// +// If Foo() returns 5, you will see the following message: +// +// Expected: Foo() is even +// Actual: it's 5 +// +class GTEST_API_ AssertionResult { + public: + // Copy constructor. + // Used in EXPECT_TRUE/FALSE(assertion_result). + AssertionResult(const AssertionResult& other); + // Used in the EXPECT_TRUE/FALSE(bool_expression). + explicit AssertionResult(bool success) : success_(success) {} + + // Returns true iff the assertion succeeded. + operator bool() const { return success_; } // NOLINT + + // Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE. + AssertionResult operator!() const; + + // Returns the text streamed into this AssertionResult. Test assertions + // use it when they fail (i.e., the predicate's outcome doesn't match the + // assertion's expectation). When nothing has been streamed into the + // object, returns an empty string. + const char* message() const { + return message_.get() != NULL ? message_->c_str() : ""; + } + // TODO(vladl@google.com): Remove this after making sure no clients use it. + // Deprecated; please use message() instead. + const char* failure_message() const { return message(); } + + // Streams a custom failure message into this object. + template AssertionResult& operator<<(const T& value) { + AppendMessage(Message() << value); + return *this; + } + + // Allows streaming basic output manipulators such as endl or flush into + // this object. + AssertionResult& operator<<( + ::std::ostream& (*basic_manipulator)(::std::ostream& stream)) { + AppendMessage(Message() << basic_manipulator); + return *this; + } + + private: + // Appends the contents of message to message_. + void AppendMessage(const Message& a_message) { + if (message_.get() == NULL) + message_.reset(new ::std::string); + message_->append(a_message.GetString().c_str()); + } + + // Stores result of the assertion predicate. + bool success_; + // Stores the message describing the condition in case the expectation + // construct is not satisfied with the predicate's outcome. + // Referenced via a pointer to avoid taking too much stack frame space + // with test assertions. + internal::scoped_ptr< ::std::string> message_; + + GTEST_DISALLOW_ASSIGN_(AssertionResult); +}; + +// Makes a successful assertion result. +GTEST_API_ AssertionResult AssertionSuccess(); + +// Makes a failed assertion result. +GTEST_API_ AssertionResult AssertionFailure(); + +// Makes a failed assertion result with the given failure message. +// Deprecated; use AssertionFailure() << msg. +GTEST_API_ AssertionResult AssertionFailure(const Message& msg); + +// The abstract class that all tests inherit from. +// +// In Google Test, a unit test program contains one or many TestCases, and +// each TestCase contains one or many Tests. +// +// When you define a test using the TEST macro, you don't need to +// explicitly derive from Test - the TEST macro automatically does +// this for you. +// +// The only time you derive from Test is when defining a test fixture +// to be used a TEST_F. For example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { ... } +// virtual void TearDown() { ... } +// ... +// }; +// +// TEST_F(FooTest, Bar) { ... } +// TEST_F(FooTest, Baz) { ... } +// +// Test is not copyable. +class GTEST_API_ Test { + public: + friend class TestInfo; + + // Defines types for pointers to functions that set up and tear down + // a test case. + typedef internal::SetUpTestCaseFunc SetUpTestCaseFunc; + typedef internal::TearDownTestCaseFunc TearDownTestCaseFunc; + + // The d'tor is virtual as we intend to inherit from Test. + virtual ~Test(); + + // Sets up the stuff shared by all tests in this test case. + // + // Google Test will call Foo::SetUpTestCase() before running the first + // test in test case Foo. Hence a sub-class can define its own + // SetUpTestCase() method to shadow the one defined in the super + // class. + static void SetUpTestCase() {} + + // Tears down the stuff shared by all tests in this test case. + // + // Google Test will call Foo::TearDownTestCase() after running the last + // test in test case Foo. Hence a sub-class can define its own + // TearDownTestCase() method to shadow the one defined in the super + // class. + static void TearDownTestCase() {} + + // Returns true iff the current test has a fatal failure. + static bool HasFatalFailure(); + + // Returns true iff the current test has a non-fatal failure. + static bool HasNonfatalFailure(); + + // Returns true iff the current test has a (either fatal or + // non-fatal) failure. + static bool HasFailure() { return HasFatalFailure() || HasNonfatalFailure(); } + + // Logs a property for the current test, test case, or for the entire + // invocation of the test program when used outside of the context of a + // test case. Only the last value for a given key is remembered. These + // are public static so they can be called from utility functions that are + // not members of the test fixture. Calls to RecordProperty made during + // lifespan of the test (from the moment its constructor starts to the + // moment its destructor finishes) will be output in XML as attributes of + // the element. Properties recorded from fixture's + // SetUpTestCase or TearDownTestCase are logged as attributes of the + // corresponding element. Calls to RecordProperty made in the + // global context (before or after invocation of RUN_ALL_TESTS and from + // SetUp/TearDown method of Environment objects registered with Google + // Test) will be output as attributes of the element. + static void RecordProperty(const std::string& key, const std::string& value); + static void RecordProperty(const std::string& key, int value); + + protected: + // Creates a Test object. + Test(); + + // Sets up the test fixture. + virtual void SetUp(); + + // Tears down the test fixture. + virtual void TearDown(); + + private: + // Returns true iff the current test has the same fixture class as + // the first test in the current test case. + static bool HasSameFixtureClass(); + + // Runs the test after the test fixture has been set up. + // + // A sub-class must implement this to define the test logic. + // + // DO NOT OVERRIDE THIS FUNCTION DIRECTLY IN A USER PROGRAM. + // Instead, use the TEST or TEST_F macro. + virtual void TestBody() = 0; + + // Sets up, executes, and tears down the test. + void Run(); + + // Deletes self. We deliberately pick an unusual name for this + // internal method to avoid clashing with names used in user TESTs. + void DeleteSelf_() { delete this; } + + // Uses a GTestFlagSaver to save and restore all Google Test flags. + const internal::GTestFlagSaver* const gtest_flag_saver_; + + // Often a user mis-spells SetUp() as Setup() and spends a long time + // wondering why it is never called by Google Test. The declaration of + // the following method is solely for catching such an error at + // compile time: + // + // - The return type is deliberately chosen to be not void, so it + // will be a conflict if a user declares void Setup() in his test + // fixture. + // + // - This method is private, so it will be another compiler error + // if a user calls it from his test fixture. + // + // DO NOT OVERRIDE THIS FUNCTION. + // + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } + + // We disallow copying Tests. + GTEST_DISALLOW_COPY_AND_ASSIGN_(Test); +}; + +typedef internal::TimeInMillis TimeInMillis; + +// A copyable object representing a user specified test property which can be +// output as a key/value string pair. +// +// Don't inherit from TestProperty as its destructor is not virtual. +class TestProperty { + public: + // C'tor. TestProperty does NOT have a default constructor. + // Always use this constructor (with parameters) to create a + // TestProperty object. + TestProperty(const std::string& a_key, const std::string& a_value) : + key_(a_key), value_(a_value) { + } + + // Gets the user supplied key. + const char* key() const { + return key_.c_str(); + } + + // Gets the user supplied value. + const char* value() const { + return value_.c_str(); + } + + // Sets a new value, overriding the one supplied in the constructor. + void SetValue(const std::string& new_value) { + value_ = new_value; + } + + private: + // The key supplied by the user. + std::string key_; + // The value supplied by the user. + std::string value_; +}; + +// The result of a single Test. This includes a list of +// TestPartResults, a list of TestProperties, a count of how many +// death tests there are in the Test, and how much time it took to run +// the Test. +// +// TestResult is not copyable. +class GTEST_API_ TestResult { + public: + // Creates an empty TestResult. + TestResult(); + + // D'tor. Do not inherit from TestResult. + ~TestResult(); + + // Gets the number of all test parts. This is the sum of the number + // of successful test parts and the number of failed test parts. + int total_part_count() const; + + // Returns the number of the test properties. + int test_property_count() const; + + // Returns true iff the test passed (i.e. no test part failed). + bool Passed() const { return !Failed(); } + + // Returns true iff the test failed. + bool Failed() const; + + // Returns true iff the test fatally failed. + bool HasFatalFailure() const; + + // Returns true iff the test has a non-fatal failure. + bool HasNonfatalFailure() const; + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns the i-th test part result among all the results. i can range + // from 0 to test_property_count() - 1. If i is not in that range, aborts + // the program. + const TestPartResult& GetTestPartResult(int i) const; + + // Returns the i-th test property. i can range from 0 to + // test_property_count() - 1. If i is not in that range, aborts the + // program. + const TestProperty& GetTestProperty(int i) const; + + private: + friend class TestInfo; + friend class TestCase; + friend class UnitTest; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::ExecDeathTest; + friend class internal::TestResultAccessor; + friend class internal::UnitTestImpl; + friend class internal::WindowsDeathTest; + + // Gets the vector of TestPartResults. + const std::vector& test_part_results() const { + return test_part_results_; + } + + // Gets the vector of TestProperties. + const std::vector& test_properties() const { + return test_properties_; + } + + // Sets the elapsed time. + void set_elapsed_time(TimeInMillis elapsed) { elapsed_time_ = elapsed; } + + // Adds a test property to the list. The property is validated and may add + // a non-fatal failure if invalid (e.g., if it conflicts with reserved + // key names). If a property is already recorded for the same key, the + // value will be updated, rather than storing multiple values for the same + // key. xml_element specifies the element for which the property is being + // recorded and is used for validation. + void RecordProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a failure if the key is a reserved attribute of Google Test + // testcase tags. Returns true if the property is valid. + // TODO(russr): Validate attribute names are legal and human readable. + static bool ValidateTestProperty(const std::string& xml_element, + const TestProperty& test_property); + + // Adds a test part result to the list. + void AddTestPartResult(const TestPartResult& test_part_result); + + // Returns the death test count. + int death_test_count() const { return death_test_count_; } + + // Increments the death test count, returning the new count. + int increment_death_test_count() { return ++death_test_count_; } + + // Clears the test part results. + void ClearTestPartResults(); + + // Clears the object. + void Clear(); + + // Protects mutable state of the property vector and of owned + // properties, whose values may be updated. + internal::Mutex test_properites_mutex_; + + // The vector of TestPartResults + std::vector test_part_results_; + // The vector of TestProperties + std::vector test_properties_; + // Running count of death tests. + int death_test_count_; + // The elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + + // We disallow copying TestResult. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestResult); +}; // class TestResult + +// A TestInfo object stores the following information about a test: +// +// Test case name +// Test name +// Whether the test should be run +// A function pointer that creates the test object when invoked +// Test result +// +// The constructor of TestInfo registers itself with the UnitTest +// singleton such that the RUN_ALL_TESTS() macro knows which tests to +// run. +class GTEST_API_ TestInfo { + public: + // Destructs a TestInfo object. This function is not virtual, so + // don't inherit from TestInfo. + ~TestInfo(); + + // Returns the test case name. + const char* test_case_name() const { return test_case_name_.c_str(); } + + // Returns the test name. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a typed + // or a type-parameterized test. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; + } + + // Returns the text representation of the value parameter, or NULL if this + // is not a value-parameterized test. + const char* value_param() const { + if (value_param_.get() != NULL) + return value_param_->c_str(); + return NULL; + } + + // Returns true if this test should run, that is if the test is not + // disabled (or it is disabled but the also_run_disabled_tests flag has + // been specified) and its full name matches the user-specified filter. + // + // Google Test allows the user to filter the tests by their full names. + // The full name of a test Bar in test case Foo is defined as + // "Foo.Bar". Only the tests that match the filter will run. + // + // A filter is a colon-separated list of glob (not regex) patterns, + // optionally followed by a '-' and a colon-separated list of + // negative patterns (tests to exclude). A test is run if it + // matches one of the positive patterns and does not match any of + // the negative patterns. + // + // For example, *A*:Foo.* is a filter that matches any string that + // contains the character 'A' or starts with "Foo.". + bool should_run() const { return should_run_; } + + // Returns true iff this test will appear in the XML report. + bool is_reportable() const { + // For now, the XML report includes all tests matching the filter. + // In the future, we may trim tests that are excluded because of + // sharding. + return matches_filter_; + } + + // Returns the result of the test. + const TestResult* result() const { return &result_; } + + private: +#if GTEST_HAS_DEATH_TEST + friend class internal::DefaultDeathTestFactory; +#endif // GTEST_HAS_DEATH_TEST + friend class Test; + friend class TestCase; + friend class internal::UnitTestImpl; + friend class internal::StreamingListenerTest; + friend TestInfo* internal::MakeAndRegisterTestInfo( + const char* test_case_name, + const char* name, + const char* type_param, + const char* value_param, + internal::TypeId fixture_class_id, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc, + internal::TestFactoryBase* factory); + + // Constructs a TestInfo object. The newly constructed instance assumes + // ownership of the factory object. + TestInfo(const std::string& test_case_name, + const std::string& name, + const char* a_type_param, // NULL if not a type-parameterized test + const char* a_value_param, // NULL if not a value-parameterized test + internal::TypeId fixture_class_id, + internal::TestFactoryBase* factory); + + // Increments the number of death tests encountered in this test so + // far. + int increment_death_test_count() { + return result_.increment_death_test_count(); + } + + // Creates the test object, runs it, records its result, and then + // deletes it. + void Run(); + + static void ClearTestResult(TestInfo* test_info) { + test_info->result_.Clear(); + } + + // These fields are immutable properties of the test. + const std::string test_case_name_; // Test case name + const std::string name_; // Test name + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const internal::scoped_ptr type_param_; + // Text representation of the value parameter, or NULL if this is not a + // value-parameterized test. + const internal::scoped_ptr value_param_; + const internal::TypeId fixture_class_id_; // ID of the test fixture class + bool should_run_; // True iff this test should run + bool is_disabled_; // True iff this test is disabled + bool matches_filter_; // True if this test matches the + // user-specified filter. + internal::TestFactoryBase* const factory_; // The factory that creates + // the test object + + // This field is mutable and needs to be reset before running the + // test for the second time. + TestResult result_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestInfo); +}; + +// A test case, which consists of a vector of TestInfos. +// +// TestCase is not copyable. +class GTEST_API_ TestCase { + public: + // Creates a TestCase with the given name. + // + // TestCase does NOT have a default constructor. Always use this + // constructor to create a TestCase object. + // + // Arguments: + // + // name: name of the test case + // a_type_param: the name of the test's type parameter, or NULL if + // this is not a type-parameterized test. + // set_up_tc: pointer to the function that sets up the test case + // tear_down_tc: pointer to the function that tears down the test case + TestCase(const char* name, const char* a_type_param, + Test::SetUpTestCaseFunc set_up_tc, + Test::TearDownTestCaseFunc tear_down_tc); + + // Destructor of TestCase. + virtual ~TestCase(); + + // Gets the name of the TestCase. + const char* name() const { return name_.c_str(); } + + // Returns the name of the parameter type, or NULL if this is not a + // type-parameterized test case. + const char* type_param() const { + if (type_param_.get() != NULL) + return type_param_->c_str(); + return NULL; + } + + // Returns true if any test in this test case should run. + bool should_run() const { return should_run_; } + + // Gets the number of successful tests in this test case. + int successful_test_count() const; + + // Gets the number of failed tests in this test case. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests in this test case. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Get the number of tests in this test case that should run. + int test_to_run_count() const; + + // Gets the number of all tests in this test case. + int total_test_count() const; + + // Returns true iff the test case passed. + bool Passed() const { return !Failed(); } + + // Returns true iff the test case failed. + bool Failed() const { return failed_test_count() > 0; } + + // Returns the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const { return elapsed_time_; } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + const TestInfo* GetTestInfo(int i) const; + + // Returns the TestResult that holds test properties recorded during + // execution of SetUpTestCase and TearDownTestCase. + const TestResult& ad_hoc_test_result() const { return ad_hoc_test_result_; } + + private: + friend class Test; + friend class internal::UnitTestImpl; + + // Gets the (mutable) vector of TestInfos in this TestCase. + std::vector& test_info_list() { return test_info_list_; } + + // Gets the (immutable) vector of TestInfos in this TestCase. + const std::vector& test_info_list() const { + return test_info_list_; + } + + // Returns the i-th test among all the tests. i can range from 0 to + // total_test_count() - 1. If i is not in that range, returns NULL. + TestInfo* GetMutableTestInfo(int i); + + // Sets the should_run member. + void set_should_run(bool should) { should_run_ = should; } + + // Adds a TestInfo to this test case. Will delete the TestInfo upon + // destruction of the TestCase object. + void AddTestInfo(TestInfo * test_info); + + // Clears the results of all tests in this test case. + void ClearResult(); + + // Clears the results of all tests in the given test case. + static void ClearTestCaseResult(TestCase* test_case) { + test_case->ClearResult(); + } + + // Runs every test in this TestCase. + void Run(); + + // Runs SetUpTestCase() for this TestCase. This wrapper is needed + // for catching exceptions thrown from SetUpTestCase(). + void RunSetUpTestCase() { (*set_up_tc_)(); } + + // Runs TearDownTestCase() for this TestCase. This wrapper is + // needed for catching exceptions thrown from TearDownTestCase(). + void RunTearDownTestCase() { (*tear_down_tc_)(); } + + // Returns true iff test passed. + static bool TestPassed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Passed(); + } + + // Returns true iff test failed. + static bool TestFailed(const TestInfo* test_info) { + return test_info->should_run() && test_info->result()->Failed(); + } + + // Returns true iff the test is disabled and will be reported in the XML + // report. + static bool TestReportableDisabled(const TestInfo* test_info) { + return test_info->is_reportable() && test_info->is_disabled_; + } + + // Returns true iff test is disabled. + static bool TestDisabled(const TestInfo* test_info) { + return test_info->is_disabled_; + } + + // Returns true iff this test will appear in the XML report. + static bool TestReportable(const TestInfo* test_info) { + return test_info->is_reportable(); + } + + // Returns true if the given test should run. + static bool ShouldRunTest(const TestInfo* test_info) { + return test_info->should_run(); + } + + // Shuffles the tests in this test case. + void ShuffleTests(internal::Random* random); + + // Restores the test order to before the first shuffle. + void UnshuffleTests(); + + // Name of the test case. + std::string name_; + // Name of the parameter type, or NULL if this is not a typed or a + // type-parameterized test. + const internal::scoped_ptr type_param_; + // The vector of TestInfos in their original order. It owns the + // elements in the vector. + std::vector test_info_list_; + // Provides a level of indirection for the test list to allow easy + // shuffling and restoring the test order. The i-th element in this + // vector is the index of the i-th test in the shuffled test list. + std::vector test_indices_; + // Pointer to the function that sets up the test case. + Test::SetUpTestCaseFunc set_up_tc_; + // Pointer to the function that tears down the test case. + Test::TearDownTestCaseFunc tear_down_tc_; + // True iff any test in this test case should run. + bool should_run_; + // Elapsed time, in milliseconds. + TimeInMillis elapsed_time_; + // Holds test properties recorded during execution of SetUpTestCase and + // TearDownTestCase. + TestResult ad_hoc_test_result_; + + // We disallow copying TestCases. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestCase); +}; + +// An Environment object is capable of setting up and tearing down an +// environment. The user should subclass this to define his own +// environment(s). +// +// An Environment object does the set-up and tear-down in virtual +// methods SetUp() and TearDown() instead of the constructor and the +// destructor, as: +// +// 1. You cannot safely throw from a destructor. This is a problem +// as in some cases Google Test is used where exceptions are enabled, and +// we may want to implement ASSERT_* using exceptions where they are +// available. +// 2. You cannot use ASSERT_* directly in a constructor or +// destructor. +class Environment { + public: + // The d'tor is virtual as we need to subclass Environment. + virtual ~Environment() {} + + // Override this to define how to set up the environment. + virtual void SetUp() {} + + // Override this to define how to tear down the environment. + virtual void TearDown() {} + private: + // If you see an error about overriding the following function or + // about it being private, you have mis-spelled SetUp() as Setup(). + struct Setup_should_be_spelled_SetUp {}; + virtual Setup_should_be_spelled_SetUp* Setup() { return NULL; } +}; + +// The interface for tracing execution of tests. The methods are organized in +// the order the corresponding events are fired. +class TestEventListener { + public: + virtual ~TestEventListener() {} + + // Fired before any test activity starts. + virtual void OnTestProgramStart(const UnitTest& unit_test) = 0; + + // Fired before each iteration of tests starts. There may be more than + // one iteration if GTEST_FLAG(repeat) is set. iteration is the iteration + // index, starting from 0. + virtual void OnTestIterationStart(const UnitTest& unit_test, + int iteration) = 0; + + // Fired before environment set-up for each iteration of tests starts. + virtual void OnEnvironmentsSetUpStart(const UnitTest& unit_test) = 0; + + // Fired after environment set-up for each iteration of tests ends. + virtual void OnEnvironmentsSetUpEnd(const UnitTest& unit_test) = 0; + + // Fired before the test case starts. + virtual void OnTestCaseStart(const TestCase& test_case) = 0; + + // Fired before the test starts. + virtual void OnTestStart(const TestInfo& test_info) = 0; + + // Fired after a failed assertion or a SUCCEED() invocation. + virtual void OnTestPartResult(const TestPartResult& test_part_result) = 0; + + // Fired after the test ends. + virtual void OnTestEnd(const TestInfo& test_info) = 0; + + // Fired after the test case ends. + virtual void OnTestCaseEnd(const TestCase& test_case) = 0; + + // Fired before environment tear-down for each iteration of tests starts. + virtual void OnEnvironmentsTearDownStart(const UnitTest& unit_test) = 0; + + // Fired after environment tear-down for each iteration of tests ends. + virtual void OnEnvironmentsTearDownEnd(const UnitTest& unit_test) = 0; + + // Fired after each iteration of tests finishes. + virtual void OnTestIterationEnd(const UnitTest& unit_test, + int iteration) = 0; + + // Fired after all test activities have ended. + virtual void OnTestProgramEnd(const UnitTest& unit_test) = 0; +}; + +// The convenience class for users who need to override just one or two +// methods and are not concerned that a possible change to a signature of +// the methods they override will not be caught during the build. For +// comments about each method please see the definition of TestEventListener +// above. +class EmptyTestEventListener : public TestEventListener { + public: + virtual void OnTestProgramStart(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationStart(const UnitTest& /*unit_test*/, + int /*iteration*/) {} + virtual void OnEnvironmentsSetUpStart(const UnitTest& /*unit_test*/) {} + virtual void OnEnvironmentsSetUpEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestCaseStart(const TestCase& /*test_case*/) {} + virtual void OnTestStart(const TestInfo& /*test_info*/) {} + virtual void OnTestPartResult(const TestPartResult& /*test_part_result*/) {} + virtual void OnTestEnd(const TestInfo& /*test_info*/) {} + virtual void OnTestCaseEnd(const TestCase& /*test_case*/) {} + virtual void OnEnvironmentsTearDownStart(const UnitTest& /*unit_test*/) {} + virtual void OnEnvironmentsTearDownEnd(const UnitTest& /*unit_test*/) {} + virtual void OnTestIterationEnd(const UnitTest& /*unit_test*/, + int /*iteration*/) {} + virtual void OnTestProgramEnd(const UnitTest& /*unit_test*/) {} +}; + +// TestEventListeners lets users add listeners to track events in Google Test. +class GTEST_API_ TestEventListeners { + public: + TestEventListeners(); + ~TestEventListeners(); + + // Appends an event listener to the end of the list. Google Test assumes + // the ownership of the listener (i.e. it will delete the listener when + // the test program finishes). + void Append(TestEventListener* listener); + + // Removes the given event listener from the list and returns it. It then + // becomes the caller's responsibility to delete the listener. Returns + // NULL if the listener is not found in the list. + TestEventListener* Release(TestEventListener* listener); + + // Returns the standard listener responsible for the default console + // output. Can be removed from the listeners list to shut down default + // console output. Note that removing this object from the listener list + // with Release transfers its ownership to the caller and makes this + // function return NULL the next time. + TestEventListener* default_result_printer() const { + return default_result_printer_; + } + + // Returns the standard listener responsible for the default XML output + // controlled by the --gtest_output=xml flag. Can be removed from the + // listeners list by users who want to shut down the default XML output + // controlled by this flag and substitute it with custom one. Note that + // removing this object from the listener list with Release transfers its + // ownership to the caller and makes this function return NULL the next + // time. + TestEventListener* default_xml_generator() const { + return default_xml_generator_; + } + + private: + friend class TestCase; + friend class TestInfo; + friend class internal::DefaultGlobalTestPartResultReporter; + friend class internal::NoExecDeathTest; + friend class internal::TestEventListenersAccessor; + friend class internal::UnitTestImpl; + + // Returns repeater that broadcasts the TestEventListener events to all + // subscribers. + TestEventListener* repeater(); + + // Sets the default_result_printer attribute to the provided listener. + // The listener is also added to the listener list and previous + // default_result_printer is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultResultPrinter(TestEventListener* listener); + + // Sets the default_xml_generator attribute to the provided listener. The + // listener is also added to the listener list and previous + // default_xml_generator is removed from it and deleted. The listener can + // also be NULL in which case it will not be added to the list. Does + // nothing if the previous and the current listener objects are the same. + void SetDefaultXmlGenerator(TestEventListener* listener); + + // Controls whether events will be forwarded by the repeater to the + // listeners in the list. + bool EventForwardingEnabled() const; + void SuppressEventForwarding(); + + // The actual list of listeners. + internal::TestEventRepeater* repeater_; + // Listener responsible for the standard result output. + TestEventListener* default_result_printer_; + // Listener responsible for the creation of the XML output file. + TestEventListener* default_xml_generator_; + + // We disallow copying TestEventListeners. + GTEST_DISALLOW_COPY_AND_ASSIGN_(TestEventListeners); +}; + +// A UnitTest consists of a vector of TestCases. +// +// This is a singleton class. The only instance of UnitTest is +// created when UnitTest::GetInstance() is first called. This +// instance is never deleted. +// +// UnitTest is not copyable. +// +// This class is thread-safe as long as the methods are called +// according to their specification. +class GTEST_API_ UnitTest { + public: + // Gets the singleton UnitTest object. The first time this method + // is called, a UnitTest object is constructed and returned. + // Consecutive calls will return the same object. + static UnitTest* GetInstance(); + + // Runs all tests in this UnitTest object and prints the result. + // Returns 0 if successful, or 1 otherwise. + // + // This method can only be called from the main thread. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + int Run() GTEST_MUST_USE_RESULT_; + + // Returns the working directory when the first TEST() or TEST_F() + // was executed. The UnitTest object owns the string. + const char* original_working_dir() const; + + // Returns the TestCase object for the test that's currently running, + // or NULL if no test is running. + const TestCase* current_test_case() const + GTEST_LOCK_EXCLUDED_(mutex_); + + // Returns the TestInfo object for the test that's currently running, + // or NULL if no test is running. + const TestInfo* current_test_info() const + GTEST_LOCK_EXCLUDED_(mutex_); + + // Returns the random seed used at the start of the current test run. + int random_seed() const; + +#if GTEST_HAS_PARAM_TEST + // Returns the ParameterizedTestCaseRegistry object used to keep track of + // value-parameterized tests and instantiate and register them. + // + // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + internal::ParameterizedTestCaseRegistry& parameterized_test_registry() + GTEST_LOCK_EXCLUDED_(mutex_); +#endif // GTEST_HAS_PARAM_TEST + + // Gets the number of successful test cases. + int successful_test_case_count() const; + + // Gets the number of failed test cases. + int failed_test_case_count() const; + + // Gets the number of all test cases. + int total_test_case_count() const; + + // Gets the number of all test cases that contain at least one test + // that should run. + int test_case_to_run_count() const; + + // Gets the number of successful tests. + int successful_test_count() const; + + // Gets the number of failed tests. + int failed_test_count() const; + + // Gets the number of disabled tests that will be reported in the XML report. + int reportable_disabled_test_count() const; + + // Gets the number of disabled tests. + int disabled_test_count() const; + + // Gets the number of tests to be printed in the XML report. + int reportable_test_count() const; + + // Gets the number of all tests. + int total_test_count() const; + + // Gets the number of tests that should run. + int test_to_run_count() const; + + // Gets the time of the test program start, in ms from the start of the + // UNIX epoch. + TimeInMillis start_timestamp() const; + + // Gets the elapsed time, in milliseconds. + TimeInMillis elapsed_time() const; + + // Returns true iff the unit test passed (i.e. all test cases passed). + bool Passed() const; + + // Returns true iff the unit test failed (i.e. some test case failed + // or something outside of all tests failed). + bool Failed() const; + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + const TestCase* GetTestCase(int i) const; + + // Returns the TestResult containing information on test failures and + // properties logged outside of individual test cases. + const TestResult& ad_hoc_test_result() const; + + // Returns the list of event listeners that can be used to track events + // inside Google Test. + TestEventListeners& listeners(); + + private: + // Registers and returns a global test environment. When a test + // program is run, all global test environments will be set-up in + // the order they were registered. After all tests in the program + // have finished, all global test environments will be torn-down in + // the *reverse* order they were registered. + // + // The UnitTest object takes ownership of the given environment. + // + // This method can only be called from the main thread. + Environment* AddEnvironment(Environment* env); + + // Adds a TestPartResult to the current TestResult object. All + // Google Test assertion macros (e.g. ASSERT_TRUE, EXPECT_EQ, etc) + // eventually call this to report their results. The user code + // should use the assertion macros instead of calling this directly. + void AddTestPartResult(TestPartResult::Type result_type, + const char* file_name, + int line_number, + const std::string& message, + const std::string& os_stack_trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Adds a TestProperty to the current TestResult object when invoked from + // inside a test, to current TestCase's ad_hoc_test_result_ when invoked + // from SetUpTestCase or TearDownTestCase, or to the global property set + // when invoked elsewhere. If the result already contains a property with + // the same key, the value will be updated. + void RecordProperty(const std::string& key, const std::string& value); + + // Gets the i-th test case among all the test cases. i can range from 0 to + // total_test_case_count() - 1. If i is not in that range, returns NULL. + TestCase* GetMutableTestCase(int i); + + // Accessors for the implementation object. + internal::UnitTestImpl* impl() { return impl_; } + const internal::UnitTestImpl* impl() const { return impl_; } + + // These classes and funcions are friends as they need to access private + // members of UnitTest. + friend class Test; + friend class internal::AssertHelper; + friend class internal::ScopedTrace; + friend class internal::StreamingListenerTest; + friend class internal::UnitTestRecordPropertyTestHelper; + friend Environment* AddGlobalTestEnvironment(Environment* env); + friend internal::UnitTestImpl* internal::GetUnitTestImpl(); + friend void internal::ReportFailureInUnknownLocation( + TestPartResult::Type result_type, + const std::string& message); + + // Creates an empty UnitTest. + UnitTest(); + + // D'tor + virtual ~UnitTest(); + + // Pushes a trace defined by SCOPED_TRACE() on to the per-thread + // Google Test trace stack. + void PushGTestTrace(const internal::TraceInfo& trace) + GTEST_LOCK_EXCLUDED_(mutex_); + + // Pops a trace from the per-thread Google Test trace stack. + void PopGTestTrace() + GTEST_LOCK_EXCLUDED_(mutex_); + + // Protects mutable state in *impl_. This is mutable as some const + // methods need to lock it too. + mutable internal::Mutex mutex_; + + // Opaque implementation object. This field is never changed once + // the object is constructed. We don't mark it as const here, as + // doing so will cause a warning in the constructor of UnitTest. + // Mutable state in *impl_ is protected by mutex_. + internal::UnitTestImpl* impl_; + + // We disallow copying UnitTest. + GTEST_DISALLOW_COPY_AND_ASSIGN_(UnitTest); +}; + +// A convenient wrapper for adding an environment for the test +// program. +// +// You should call this before RUN_ALL_TESTS() is called, probably in +// main(). If you use gtest_main, you need to call this before main() +// starts for it to take effect. For example, you can define a global +// variable like this: +// +// testing::Environment* const foo_env = +// testing::AddGlobalTestEnvironment(new FooEnvironment); +// +// However, we strongly recommend you to write your own main() and +// call AddGlobalTestEnvironment() there, as relying on initialization +// of global variables makes the code harder to read and may cause +// problems when you register multiple environments from different +// translation units and the environments have dependencies among them +// (remember that the compiler doesn't guarantee the order in which +// global variables from different translation units are initialized). +inline Environment* AddGlobalTestEnvironment(Environment* env) { + return UnitTest::GetInstance()->AddEnvironment(env); +} + +// Initializes Google Test. This must be called before calling +// RUN_ALL_TESTS(). In particular, it parses a command line for the +// flags that Google Test recognizes. Whenever a Google Test flag is +// seen, it is removed from argv, and *argc is decremented. +// +// No value is returned. Instead, the Google Test flag variables are +// updated. +// +// Calling the function for the second time has no user-visible effect. +GTEST_API_ void InitGoogleTest(int* argc, char** argv); + +// This overloaded version can be used in Windows programs compiled in +// UNICODE mode. +GTEST_API_ void InitGoogleTest(int* argc, wchar_t** argv); + +namespace internal { + +// FormatForComparison::Format(value) formats a +// value of type ToPrint that is an operand of a comparison assertion +// (e.g. ASSERT_EQ). OtherOperand is the type of the other operand in +// the comparison, and is used to help determine the best way to +// format the value. In particular, when the value is a C string +// (char pointer) and the other operand is an STL string object, we +// want to format the C string as a string, since we know it is +// compared by value with the string object. If the value is a char +// pointer but the other operand is not an STL string object, we don't +// know whether the pointer is supposed to point to a NUL-terminated +// string, and thus want to print it as a pointer to be safe. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// The default case. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint& value) { + return ::testing::PrintToString(value); + } +}; + +// Array. +template +class FormatForComparison { + public: + static ::std::string Format(const ToPrint* value) { + return FormatForComparison::Format(value); + } +}; + +// By default, print C string as pointers to be safe, as we don't know +// whether they actually point to a NUL-terminated string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(CharType) \ + template \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(static_cast(value)); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const char); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(wchar_t); +GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_(const wchar_t); + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_POINTER_ + +// If a C string is compared with an STL string object, we know it's meant +// to point to a NUL-terminated string, and thus can print it as a string. + +#define GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(CharType, OtherStringType) \ + template <> \ + class FormatForComparison { \ + public: \ + static ::std::string Format(CharType* value) { \ + return ::testing::PrintToString(value); \ + } \ + } + +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::std::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::std::string); + +#if GTEST_HAS_GLOBAL_STRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(char, ::string); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const char, ::string); +#endif + +#if GTEST_HAS_GLOBAL_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::wstring); +#endif + +#if GTEST_HAS_STD_WSTRING +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(wchar_t, ::std::wstring); +GTEST_IMPL_FORMAT_C_STRING_AS_STRING_(const wchar_t, ::std::wstring); +#endif + +#undef GTEST_IMPL_FORMAT_C_STRING_AS_STRING_ + +// Formats a comparison assertion (e.g. ASSERT_EQ, EXPECT_LT, and etc) +// operand to be used in a failure message. The type (but not value) +// of the other operand may affect the format. This allows us to +// print a char* as a raw pointer when it is compared against another +// char* or void*, and print it as a C string when it is compared +// against an std::string object, for example. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +std::string FormatForComparisonFailureMessage( + const T1& value, const T2& /* other_operand */) { + return FormatForComparison::Format(value); +} + +// The helper function for {ASSERT|EXPECT}_EQ. +template +AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { +#ifdef _MSC_VER +# pragma warning(push) // Saves the current warning state. +# pragma warning(disable:4389) // Temporarily disables warning on + // signed/unsigned mismatch. +#endif + + if (expected == actual) { + return AssertionSuccess(); + } + +#ifdef _MSC_VER +# pragma warning(pop) // Restores the warning state. +#endif + + return EqFailure(expected_expression, + actual_expression, + FormatForComparisonFailureMessage(expected, actual), + FormatForComparisonFailureMessage(actual, expected), + false); +} + +// With this overloaded version, we allow anonymous enums to be used +// in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous enums +// can be implicitly cast to BiggestInt. +GTEST_API_ AssertionResult CmpHelperEQ(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual); + +// The helper class for {ASSERT|EXPECT}_EQ. The template argument +// lhs_is_null_literal is true iff the first argument to ASSERT_EQ() +// is a null pointer literal. The following default implementation is +// for lhs_is_null_literal being false. +template +class EqHelper { + public: + // This templatized version is for the general case. + template + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } + + // With this overloaded version, we allow anonymous enums to be used + // in {ASSERT|EXPECT}_EQ when compiled with gcc 4, as anonymous + // enums can be implicitly cast to BiggestInt. + // + // Even though its body looks the same as the above version, we + // cannot merge the two, as it will make anonymous enums unhappy. + static AssertionResult Compare(const char* expected_expression, + const char* actual_expression, + BiggestInt expected, + BiggestInt actual) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } +}; + +// This specialization is used when the first argument to ASSERT_EQ() +// is a null pointer literal, like NULL, false, or 0. +template <> +class EqHelper { + public: + // We define two overloaded versions of Compare(). The first + // version will be picked when the second argument to ASSERT_EQ() is + // NOT a pointer, e.g. ASSERT_EQ(0, AnIntFunction()) or + // EXPECT_EQ(false, a_bool). + template + static AssertionResult Compare( + const char* expected_expression, + const char* actual_expression, + const T1& expected, + const T2& actual, + // The following line prevents this overload from being considered if T2 + // is not a pointer type. We need this because ASSERT_EQ(NULL, my_ptr) + // expands to Compare("", "", NULL, my_ptr), which requires a conversion + // to match the Secret* in the other overload, which would otherwise make + // this template match better. + typename EnableIf::value>::type* = 0) { + return CmpHelperEQ(expected_expression, actual_expression, expected, + actual); + } + + // This version will be picked when the second argument to ASSERT_EQ() is a + // pointer, e.g. ASSERT_EQ(NULL, a_pointer). + template + static AssertionResult Compare( + const char* expected_expression, + const char* actual_expression, + // We used to have a second template parameter instead of Secret*. That + // template parameter would deduce to 'long', making this a better match + // than the first overload even without the first overload's EnableIf. + // Unfortunately, gcc with -Wconversion-null warns when "passing NULL to + // non-pointer argument" (even a deduced integral argument), so the old + // implementation caused warnings in user code. + Secret* /* expected (NULL) */, + T* actual) { + // We already know that 'expected' is a null pointer. + return CmpHelperEQ(expected_expression, actual_expression, + static_cast(NULL), actual); + } +}; + +// A macro for implementing the helper functions needed to implement +// ASSERT_?? and EXPECT_??. It is here just to avoid copy-and-paste +// of similar code. +// +// For each templatized helper function, we also define an overloaded +// version for BiggestInt in order to reduce code bloat and allow +// anonymous enums to be used with {ASSERT|EXPECT}_?? when compiled +// with gcc 4. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +#define GTEST_IMPL_CMP_HELPER_(op_name, op)\ +template \ +AssertionResult CmpHelper##op_name(const char* expr1, const char* expr2, \ + const T1& val1, const T2& val2) {\ + if (val1 op val2) {\ + return AssertionSuccess();\ + } else {\ + return AssertionFailure() \ + << "Expected: (" << expr1 << ") " #op " (" << expr2\ + << "), actual: " << FormatForComparisonFailureMessage(val1, val2)\ + << " vs " << FormatForComparisonFailureMessage(val2, val1);\ + }\ +}\ +GTEST_API_ AssertionResult CmpHelper##op_name(\ + const char* expr1, const char* expr2, BiggestInt val1, BiggestInt val2) + +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. + +// Implements the helper function for {ASSERT|EXPECT}_NE +GTEST_IMPL_CMP_HELPER_(NE, !=); +// Implements the helper function for {ASSERT|EXPECT}_LE +GTEST_IMPL_CMP_HELPER_(LE, <=); +// Implements the helper function for {ASSERT|EXPECT}_LT +GTEST_IMPL_CMP_HELPER_(LT, <); +// Implements the helper function for {ASSERT|EXPECT}_GE +GTEST_IMPL_CMP_HELPER_(GE, >=); +// Implements the helper function for {ASSERT|EXPECT}_GT +GTEST_IMPL_CMP_HELPER_(GT, >); + +#undef GTEST_IMPL_CMP_HELPER_ + +// The helper function for {ASSERT|EXPECT}_STREQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual); + +// The helper function for {ASSERT|EXPECT}_STRCASEEQ. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASEEQ(const char* expected_expression, + const char* actual_expression, + const char* expected, + const char* actual); + +// The helper function for {ASSERT|EXPECT}_STRNE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + +// The helper function for {ASSERT|EXPECT}_STRCASENE. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRCASENE(const char* s1_expression, + const char* s2_expression, + const char* s1, + const char* s2); + + +// Helper function for *_STREQ on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTREQ(const char* expected_expression, + const char* actual_expression, + const wchar_t* expected, + const wchar_t* actual); + +// Helper function for *_STRNE on wide strings. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult CmpHelperSTRNE(const char* s1_expression, + const char* s2_expression, + const wchar_t* s1, + const wchar_t* s2); + +} // namespace internal + +// IsSubstring() and IsNotSubstring() are intended to be used as the +// first argument to {EXPECT,ASSERT}_PRED_FORMAT2(), not by +// themselves. They check whether needle is a substring of haystack +// (NULL is considered a substring of itself only), and return an +// appropriate error message when they fail. +// +// The {needle,haystack}_expr arguments are the stringified +// expressions that generated the two real arguments. +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const char* needle, const char* haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const wchar_t* needle, const wchar_t* haystack); +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::string& needle, const ::std::string& haystack); + +#if GTEST_HAS_STD_WSTRING +GTEST_API_ AssertionResult IsSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +GTEST_API_ AssertionResult IsNotSubstring( + const char* needle_expr, const char* haystack_expr, + const ::std::wstring& needle, const ::std::wstring& haystack); +#endif // GTEST_HAS_STD_WSTRING + +namespace internal { + +// Helper template function for comparing floating-points. +// +// Template parameter: +// +// RawType: the raw floating-point type (either float or double) +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +template +AssertionResult CmpHelperFloatingPointEQ(const char* expected_expression, + const char* actual_expression, + RawType expected, + RawType actual) { + const FloatingPoint lhs(expected), rhs(actual); + + if (lhs.AlmostEquals(rhs)) { + return AssertionSuccess(); + } + + ::std::stringstream expected_ss; + expected_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << expected; + + ::std::stringstream actual_ss; + actual_ss << std::setprecision(std::numeric_limits::digits10 + 2) + << actual; + + return EqFailure(expected_expression, + actual_expression, + StringStreamToString(&expected_ss), + StringStreamToString(&actual_ss), + false); +} + +// Helper function for implementing ASSERT_NEAR. +// +// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM. +GTEST_API_ AssertionResult DoubleNearPredFormat(const char* expr1, + const char* expr2, + const char* abs_error_expr, + double val1, + double val2, + double abs_error); + +// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE. +// A class that enables one to stream messages to assertion macros +class GTEST_API_ AssertHelper { + public: + // Constructor. + AssertHelper(TestPartResult::Type type, + const char* file, + int line, + const char* message); + ~AssertHelper(); + + // Message assignment is a semantic trick to enable assertion + // streaming; see the GTEST_MESSAGE_ macro below. + void operator=(const Message& message) const; + + private: + // We put our data in a struct so that the size of the AssertHelper class can + // be as small as possible. This is important because gcc is incapable of + // re-using stack space even for temporary variables, so every EXPECT_EQ + // reserves stack space for another AssertHelper. + struct AssertHelperData { + AssertHelperData(TestPartResult::Type t, + const char* srcfile, + int line_num, + const char* msg) + : type(t), file(srcfile), line(line_num), message(msg) { } + + TestPartResult::Type const type; + const char* const file; + int const line; + std::string const message; + + private: + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelperData); + }; + + AssertHelperData* const data_; + + GTEST_DISALLOW_COPY_AND_ASSIGN_(AssertHelper); +}; + +} // namespace internal + +#if GTEST_HAS_PARAM_TEST +// The pure interface class that all value-parameterized tests inherit from. +// A value-parameterized class must inherit from both ::testing::Test and +// ::testing::WithParamInterface. In most cases that just means inheriting +// from ::testing::TestWithParam, but more complicated test hierarchies +// may need to inherit from Test and WithParamInterface at different levels. +// +// This interface has support for accessing the test parameter value via +// the GetParam() method. +// +// Use it with one of the parameter generator defining functions, like Range(), +// Values(), ValuesIn(), Bool(), and Combine(). +// +// class FooTest : public ::testing::TestWithParam { +// protected: +// FooTest() { +// // Can use GetParam() here. +// } +// virtual ~FooTest() { +// // Can use GetParam() here. +// } +// virtual void SetUp() { +// // Can use GetParam() here. +// } +// virtual void TearDown { +// // Can use GetParam() here. +// } +// }; +// TEST_P(FooTest, DoesBar) { +// // Can use GetParam() method here. +// Foo foo; +// ASSERT_TRUE(foo.DoesBar(GetParam())); +// } +// INSTANTIATE_TEST_CASE_P(OneToTenRange, FooTest, ::testing::Range(1, 10)); + +template +class WithParamInterface { + public: + typedef T ParamType; + virtual ~WithParamInterface() {} + + // The current parameter value. Is also available in the test fixture's + // constructor. This member function is non-static, even though it only + // references static data, to reduce the opportunity for incorrect uses + // like writing 'WithParamInterface::GetParam()' for a test that + // uses a fixture whose parameter type is int. + const ParamType& GetParam() const { + GTEST_CHECK_(parameter_ != NULL) + << "GetParam() can only be called inside a value-parameterized test " + << "-- did you intend to write TEST_P instead of TEST_F?"; + return *parameter_; + } + + private: + // Sets parameter value. The caller is responsible for making sure the value + // remains alive and unchanged throughout the current test. + static void SetParam(const ParamType* parameter) { + parameter_ = parameter; + } + + // Static value used for accessing parameter during a test lifetime. + static const ParamType* parameter_; + + // TestClass must be a subclass of WithParamInterface and Test. + template friend class internal::ParameterizedTestFactory; +}; + +template +const T* WithParamInterface::parameter_ = NULL; + +// Most value-parameterized classes can ignore the existence of +// WithParamInterface, and can just inherit from ::testing::TestWithParam. + +template +class TestWithParam : public Test, public WithParamInterface { +}; + +#endif // GTEST_HAS_PARAM_TEST + +// Macros for indicating success/failure in test code. + +// ADD_FAILURE unconditionally adds a failure to the current test. +// SUCCEED generates a success - it doesn't automatically make the +// current test successful, as a test is only successful when it has +// no failure. +// +// EXPECT_* verifies that a certain condition is satisfied. If not, +// it behaves like ADD_FAILURE. In particular: +// +// EXPECT_TRUE verifies that a Boolean condition is true. +// EXPECT_FALSE verifies that a Boolean condition is false. +// +// FAIL and ASSERT_* are similar to ADD_FAILURE and EXPECT_*, except +// that they will also abort the current function on failure. People +// usually want the fail-fast behavior of FAIL and ASSERT_*, but those +// writing data-driven tests often find themselves using ADD_FAILURE +// and EXPECT_* more. + +// Generates a nonfatal failure with a generic message. +#define ADD_FAILURE() GTEST_NONFATAL_FAILURE_("Failed") + +// Generates a nonfatal failure at the given source file location with +// a generic message. +#define ADD_FAILURE_AT(file, line) \ + GTEST_MESSAGE_AT_(file, line, "Failed", \ + ::testing::TestPartResult::kNonFatalFailure) + +// Generates a fatal failure with a generic message. +#define GTEST_FAIL() GTEST_FATAL_FAILURE_("Failed") + +// Define this macro to 1 to omit the definition of FAIL(), which is a +// generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_FAIL +# define FAIL() GTEST_FAIL() +#endif + +// Generates a success with a generic message. +#define GTEST_SUCCEED() GTEST_SUCCESS_("Succeeded") + +// Define this macro to 1 to omit the definition of SUCCEED(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_SUCCEED +# define SUCCEED() GTEST_SUCCEED() +#endif + +// Macros for testing exceptions. +// +// * {ASSERT|EXPECT}_THROW(statement, expected_exception): +// Tests that the statement throws the expected exception. +// * {ASSERT|EXPECT}_NO_THROW(statement): +// Tests that the statement doesn't throw any exception. +// * {ASSERT|EXPECT}_ANY_THROW(statement): +// Tests that the statement throws an exception. + +#define EXPECT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_NONFATAL_FAILURE_) +#define EXPECT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define EXPECT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_NONFATAL_FAILURE_) +#define ASSERT_THROW(statement, expected_exception) \ + GTEST_TEST_THROW_(statement, expected_exception, GTEST_FATAL_FAILURE_) +#define ASSERT_NO_THROW(statement) \ + GTEST_TEST_NO_THROW_(statement, GTEST_FATAL_FAILURE_) +#define ASSERT_ANY_THROW(statement) \ + GTEST_TEST_ANY_THROW_(statement, GTEST_FATAL_FAILURE_) + +// Boolean assertions. Condition can be either a Boolean expression or an +// AssertionResult. For more information on how to use AssertionResult with +// these macros see comments on that class. +#define EXPECT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_NONFATAL_FAILURE_) +#define EXPECT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_NONFATAL_FAILURE_) +#define ASSERT_TRUE(condition) \ + GTEST_TEST_BOOLEAN_(condition, #condition, false, true, \ + GTEST_FATAL_FAILURE_) +#define ASSERT_FALSE(condition) \ + GTEST_TEST_BOOLEAN_(!(condition), #condition, true, false, \ + GTEST_FATAL_FAILURE_) + +// Includes the auto-generated header that implements a family of +// generic predicate assertion macros. +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +// This file is AUTOMATICALLY GENERATED on 10/31/2011 by command +// 'gen_gtest_pred_impl.py 5'. DO NOT EDIT BY HAND! +// +// Implements a family of generic predicate assertion macros. + +#ifndef GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ +#define GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +// Makes sure this header is not included before gtest.h. +#ifndef GTEST_INCLUDE_GTEST_GTEST_H_ +# error Do not include gtest_pred_impl.h directly. Include gtest.h instead. +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ + +// This header implements a family of generic predicate assertion +// macros: +// +// ASSERT_PRED_FORMAT1(pred_format, v1) +// ASSERT_PRED_FORMAT2(pred_format, v1, v2) +// ... +// +// where pred_format is a function or functor that takes n (in the +// case of ASSERT_PRED_FORMATn) values and their source expression +// text, and returns a testing::AssertionResult. See the definition +// of ASSERT_EQ in gtest.h for an example. +// +// If you don't care about formatting, you can use the more +// restrictive version: +// +// ASSERT_PRED1(pred, v1) +// ASSERT_PRED2(pred, v1, v2) +// ... +// +// where pred is an n-ary function or functor that returns bool, +// and the values v1, v2, ..., must support the << operator for +// streaming to std::ostream. +// +// We also define the EXPECT_* variations. +// +// For now we only support predicates whose arity is at most 5. +// Please email googletestframework@googlegroups.com if you need +// support for higher arities. + +// GTEST_ASSERT_ is the basic statement to which all of the assertions +// in this file reduce. Don't use this in your code. + +#define GTEST_ASSERT_(expression, on_failure) \ + GTEST_AMBIGUOUS_ELSE_BLOCKER_ \ + if (const ::testing::AssertionResult gtest_ar = (expression)) \ + ; \ + else \ + on_failure(gtest_ar.failure_message()) + + +// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +template +AssertionResult AssertPred1Helper(const char* pred_text, + const char* e1, + Pred pred, + const T1& v1) { + if (pred(v1)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1. +// Don't use this in your code. +#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, v1), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use +// this in your code. +#define GTEST_PRED1_(pred, v1, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, \ + #v1, \ + pred, \ + v1), on_failure) + +// Unary predicate assertion macros. +#define EXPECT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT1(pred_format, v1) \ + GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED1(pred, v1) \ + GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +template +AssertionResult AssertPred2Helper(const char* pred_text, + const char* e1, + const char* e2, + Pred pred, + const T1& v1, + const T2& v2) { + if (pred(v1, v2)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2. +// Don't use this in your code. +#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use +// this in your code. +#define GTEST_PRED2_(pred, v1, v2, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, \ + #v1, \ + #v2, \ + pred, \ + v1, \ + v2), on_failure) + +// Binary predicate assertion macros. +#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \ + GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED2(pred, v1, v2) \ + GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +template +AssertionResult AssertPred3Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3) { + if (pred(v1, v2, v3)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3. +// Don't use this in your code. +#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use +// this in your code. +#define GTEST_PRED3_(pred, v1, v2, v3, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred3Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + pred, \ + v1, \ + v2, \ + v3), on_failure) + +// Ternary predicate assertion macros. +#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \ + GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED3(pred, v1, v2, v3) \ + GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +template +AssertionResult AssertPred4Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4) { + if (pred(v1, v2, v3, v4)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4. +// Don't use this in your code. +#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use +// this in your code. +#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4), on_failure) + +// 4-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \ + GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED4(pred, v1, v2, v3, v4) \ + GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_) + + + +// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +template +AssertionResult AssertPred5Helper(const char* pred_text, + const char* e1, + const char* e2, + const char* e3, + const char* e4, + const char* e5, + Pred pred, + const T1& v1, + const T2& v2, + const T3& v3, + const T4& v4, + const T5& v5) { + if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess(); + + return AssertionFailure() << pred_text << "(" + << e1 << ", " + << e2 << ", " + << e3 << ", " + << e4 << ", " + << e5 << ") evaluates to false, where" + << "\n" << e1 << " evaluates to " << v1 + << "\n" << e2 << " evaluates to " << v2 + << "\n" << e3 << " evaluates to " << v3 + << "\n" << e4 << " evaluates to " << v4 + << "\n" << e5 << " evaluates to " << v5; +} + +// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5. +// Don't use this in your code. +#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \ + on_failure) + +// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use +// this in your code. +#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure)\ + GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, \ + #v1, \ + #v2, \ + #v3, \ + #v4, \ + #v5, \ + pred, \ + v1, \ + v2, \ + v3, \ + v4, \ + v5), on_failure) + +// 5-ary predicate assertion macros. +#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_) +#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \ + GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) +#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \ + GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_) + + + +#endif // GTEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_ + +// Macros for testing equalities and inequalities. +// +// * {ASSERT|EXPECT}_EQ(expected, actual): Tests that expected == actual +// * {ASSERT|EXPECT}_NE(v1, v2): Tests that v1 != v2 +// * {ASSERT|EXPECT}_LT(v1, v2): Tests that v1 < v2 +// * {ASSERT|EXPECT}_LE(v1, v2): Tests that v1 <= v2 +// * {ASSERT|EXPECT}_GT(v1, v2): Tests that v1 > v2 +// * {ASSERT|EXPECT}_GE(v1, v2): Tests that v1 >= v2 +// +// When they are not, Google Test prints both the tested expressions and +// their actual values. The values must be compatible built-in types, +// or you will get a compiler error. By "compatible" we mean that the +// values can be compared by the respective operator. +// +// Note: +// +// 1. It is possible to make a user-defined type work with +// {ASSERT|EXPECT}_??(), but that requires overloading the +// comparison operators and is thus discouraged by the Google C++ +// Usage Guide. Therefore, you are advised to use the +// {ASSERT|EXPECT}_TRUE() macro to assert that two objects are +// equal. +// +// 2. The {ASSERT|EXPECT}_??() macros do pointer comparisons on +// pointers (in particular, C strings). Therefore, if you use it +// with two C strings, you are testing how their locations in memory +// are related, not how their content is related. To compare two C +// strings by content, use {ASSERT|EXPECT}_STR*(). +// +// 3. {ASSERT|EXPECT}_EQ(expected, actual) is preferred to +// {ASSERT|EXPECT}_TRUE(expected == actual), as the former tells you +// what the actual value is when it fails, and similarly for the +// other comparisons. +// +// 4. Do not depend on the order in which {ASSERT|EXPECT}_??() +// evaluate their arguments, which is undefined. +// +// 5. These macros evaluate their arguments exactly once. +// +// Examples: +// +// EXPECT_NE(5, Foo()); +// EXPECT_EQ(NULL, a_pointer); +// ASSERT_LT(i, array_size); +// ASSERT_GT(records.size(), 0) << "There is no record left."; + +#define EXPECT_EQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal:: \ + EqHelper::Compare, \ + expected, actual) +#define EXPECT_NE(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperNE, expected, actual) +#define EXPECT_LE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define EXPECT_LT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define EXPECT_GE(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define EXPECT_GT(val1, val2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +#define GTEST_ASSERT_EQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal:: \ + EqHelper::Compare, \ + expected, actual) +#define GTEST_ASSERT_NE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperNE, val1, val2) +#define GTEST_ASSERT_LE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLE, val1, val2) +#define GTEST_ASSERT_LT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperLT, val1, val2) +#define GTEST_ASSERT_GE(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGE, val1, val2) +#define GTEST_ASSERT_GT(val1, val2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperGT, val1, val2) + +// Define macro GTEST_DONT_DEFINE_ASSERT_XY to 1 to omit the definition of +// ASSERT_XY(), which clashes with some users' own code. + +#if !GTEST_DONT_DEFINE_ASSERT_EQ +# define ASSERT_EQ(val1, val2) GTEST_ASSERT_EQ(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_NE +# define ASSERT_NE(val1, val2) GTEST_ASSERT_NE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LE +# define ASSERT_LE(val1, val2) GTEST_ASSERT_LE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_LT +# define ASSERT_LT(val1, val2) GTEST_ASSERT_LT(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GE +# define ASSERT_GE(val1, val2) GTEST_ASSERT_GE(val1, val2) +#endif + +#if !GTEST_DONT_DEFINE_ASSERT_GT +# define ASSERT_GT(val1, val2) GTEST_ASSERT_GT(val1, val2) +#endif + +// C-string Comparisons. All tests treat NULL and any non-NULL string +// as different. Two NULLs are equal. +// +// * {ASSERT|EXPECT}_STREQ(s1, s2): Tests that s1 == s2 +// * {ASSERT|EXPECT}_STRNE(s1, s2): Tests that s1 != s2 +// * {ASSERT|EXPECT}_STRCASEEQ(s1, s2): Tests that s1 == s2, ignoring case +// * {ASSERT|EXPECT}_STRCASENE(s1, s2): Tests that s1 != s2, ignoring case +// +// For wide or narrow string objects, you can use the +// {ASSERT|EXPECT}_??() macros. +// +// Don't depend on the order in which the arguments are evaluated, +// which is undefined. +// +// These macros evaluate their arguments exactly once. + +#define EXPECT_STREQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) +#define EXPECT_STRNE(s1, s2) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define EXPECT_STRCASEEQ(expected, actual) \ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) +#define EXPECT_STRCASENE(s1, s2)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +#define ASSERT_STREQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTREQ, expected, actual) +#define ASSERT_STRNE(s1, s2) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRNE, s1, s2) +#define ASSERT_STRCASEEQ(expected, actual) \ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASEEQ, expected, actual) +#define ASSERT_STRCASENE(s1, s2)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperSTRCASENE, s1, s2) + +// Macros for comparing floating-point numbers. +// +// * {ASSERT|EXPECT}_FLOAT_EQ(expected, actual): +// Tests that two float values are almost equal. +// * {ASSERT|EXPECT}_DOUBLE_EQ(expected, actual): +// Tests that two double values are almost equal. +// * {ASSERT|EXPECT}_NEAR(v1, v2, abs_error): +// Tests that v1 and v2 are within the given distance to each other. +// +// Google Test uses ULP-based comparison to automatically pick a default +// error bound that is appropriate for the operands. See the +// FloatingPoint template class in gtest-internal.h if you are +// interested in the implementation details. + +#define EXPECT_FLOAT_EQ(expected, actual)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define EXPECT_DOUBLE_EQ(expected, actual)\ + EXPECT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define ASSERT_FLOAT_EQ(expected, actual)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define ASSERT_DOUBLE_EQ(expected, actual)\ + ASSERT_PRED_FORMAT2(::testing::internal::CmpHelperFloatingPointEQ, \ + expected, actual) + +#define EXPECT_NEAR(val1, val2, abs_error)\ + EXPECT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +#define ASSERT_NEAR(val1, val2, abs_error)\ + ASSERT_PRED_FORMAT3(::testing::internal::DoubleNearPredFormat, \ + val1, val2, abs_error) + +// These predicate format functions work on floating-point values, and +// can be used in {ASSERT|EXPECT}_PRED_FORMAT2*(), e.g. +// +// EXPECT_PRED_FORMAT2(testing::DoubleLE, Foo(), 5.0); + +// Asserts that val1 is less than, or almost equal to, val2. Fails +// otherwise. In particular, it fails if either val1 or val2 is NaN. +GTEST_API_ AssertionResult FloatLE(const char* expr1, const char* expr2, + float val1, float val2); +GTEST_API_ AssertionResult DoubleLE(const char* expr1, const char* expr2, + double val1, double val2); + + +#if GTEST_OS_WINDOWS + +// Macros that test for HRESULT failure and success, these are only useful +// on Windows, and rely on Windows SDK macros and APIs to compile. +// +// * {ASSERT|EXPECT}_HRESULT_{SUCCEEDED|FAILED}(expr) +// +// When expr unexpectedly fails or succeeds, Google Test prints the +// expected result and the actual result with both a human-readable +// string representation of the error, if available, as well as the +// hex result code. +# define EXPECT_HRESULT_SUCCEEDED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define ASSERT_HRESULT_SUCCEEDED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTSuccess, (expr)) + +# define EXPECT_HRESULT_FAILED(expr) \ + EXPECT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +# define ASSERT_HRESULT_FAILED(expr) \ + ASSERT_PRED_FORMAT1(::testing::internal::IsHRESULTFailure, (expr)) + +#endif // GTEST_OS_WINDOWS + +// Macros that execute statement and check that it doesn't generate new fatal +// failures in the current thread. +// +// * {ASSERT|EXPECT}_NO_FATAL_FAILURE(statement); +// +// Examples: +// +// EXPECT_NO_FATAL_FAILURE(Process()); +// ASSERT_NO_FATAL_FAILURE(Process()) << "Process() failed"; +// +#define ASSERT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_FATAL_FAILURE_) +#define EXPECT_NO_FATAL_FAILURE(statement) \ + GTEST_TEST_NO_FATAL_FAILURE_(statement, GTEST_NONFATAL_FAILURE_) + +// Causes a trace (including the source file path, the current line +// number, and the given message) to be included in every test failure +// message generated by code in the current scope. The effect is +// undone when the control leaves the current scope. +// +// The message argument can be anything streamable to std::ostream. +// +// In the implementation, we include the current line number as part +// of the dummy variable name, thus allowing multiple SCOPED_TRACE()s +// to appear in the same block - as long as they are on different +// lines. +#define SCOPED_TRACE(message) \ + ::testing::internal::ScopedTrace GTEST_CONCAT_TOKEN_(gtest_trace_, __LINE__)(\ + __FILE__, __LINE__, ::testing::Message() << (message)) + +// Compile-time assertion for type equality. +// StaticAssertTypeEq() compiles iff type1 and type2 are +// the same type. The value it returns is not interesting. +// +// Instead of making StaticAssertTypeEq a class template, we make it a +// function template that invokes a helper class template. This +// prevents a user from misusing StaticAssertTypeEq by +// defining objects of that type. +// +// CAVEAT: +// +// When used inside a method of a class template, +// StaticAssertTypeEq() is effective ONLY IF the method is +// instantiated. For example, given: +// +// template class Foo { +// public: +// void Bar() { testing::StaticAssertTypeEq(); } +// }; +// +// the code: +// +// void Test1() { Foo foo; } +// +// will NOT generate a compiler error, as Foo::Bar() is never +// actually instantiated. Instead, you need: +// +// void Test2() { Foo foo; foo.Bar(); } +// +// to cause a compiler error. +template +bool StaticAssertTypeEq() { + (void)internal::StaticAssertTypeEqHelper(); + return true; +} + +// Defines a test. +// +// The first parameter is the name of the test case, and the second +// parameter is the name of the test within the test case. +// +// The convention is to end the test case name with "Test". For +// example, a test case for the Foo class can be named FooTest. +// +// The user should put his test code between braces after using this +// macro. Example: +// +// TEST(FooTest, InitializesCorrectly) { +// Foo foo; +// EXPECT_TRUE(foo.StatusIsOK()); +// } + +// Note that we call GetTestTypeId() instead of GetTypeId< +// ::testing::Test>() here to get the type ID of testing::Test. This +// is to work around a suspected linker bug when using Google Test as +// a framework on Mac OS X. The bug causes GetTypeId< +// ::testing::Test>() to return different values depending on whether +// the call is from the Google Test framework itself or from user test +// code. GetTestTypeId() is guaranteed to always return the same +// value, as it always calls GetTypeId<>() from the Google Test +// framework. +#define GTEST_TEST(test_case_name, test_name)\ + GTEST_TEST_(test_case_name, test_name, \ + ::testing::Test, ::testing::internal::GetTestTypeId()) + +// Define this macro to 1 to omit the definition of TEST(), which +// is a generic name and clashes with some other libraries. +#if !GTEST_DONT_DEFINE_TEST +# define TEST(test_case_name, test_name) GTEST_TEST(test_case_name, test_name) +#endif + +// Defines a test that uses a test fixture. +// +// The first parameter is the name of the test fixture class, which +// also doubles as the test case name. The second parameter is the +// name of the test within the test case. +// +// A test fixture class must be declared earlier. The user should put +// his test code between braces after using this macro. Example: +// +// class FooTest : public testing::Test { +// protected: +// virtual void SetUp() { b_.AddElement(3); } +// +// Foo a_; +// Foo b_; +// }; +// +// TEST_F(FooTest, InitializesCorrectly) { +// EXPECT_TRUE(a_.StatusIsOK()); +// } +// +// TEST_F(FooTest, ReturnsElementCountCorrectly) { +// EXPECT_EQ(0, a_.size()); +// EXPECT_EQ(1, b_.size()); +// } + +#define TEST_F(test_fixture, test_name)\ + GTEST_TEST_(test_fixture, test_name, test_fixture, \ + ::testing::internal::GetTypeId()) + +} // namespace testing + +// Use this function in main() to run all tests. It returns 0 if all +// tests are successful, or 1 otherwise. +// +// RUN_ALL_TESTS() should be invoked after the command line has been +// parsed by InitGoogleTest(). +// +// This function was formerly a macro; thus, it is in the global +// namespace and has an all-caps name. +int RUN_ALL_TESTS() GTEST_MUST_USE_RESULT_; + +inline int RUN_ALL_TESTS() { + return ::testing::UnitTest::GetInstance()->Run(); +} + +#endif // GTEST_INCLUDE_GTEST_GTEST_H_ diff --git a/Engine/lib/gtest/fused-src/gtest/gtest_main.cc b/Engine/lib/gtest/fused-src/gtest/gtest_main.cc new file mode 100644 index 000000000..f30282255 --- /dev/null +++ b/Engine/lib/gtest/fused-src/gtest/gtest_main.cc @@ -0,0 +1,38 @@ +// Copyright 2006, Google Inc. +// All rights reserved. +// +// Redistribution and use in source and binary forms, with or without +// modification, are permitted provided that the following conditions are +// met: +// +// * Redistributions of source code must retain the above copyright +// notice, this list of conditions and the following disclaimer. +// * Redistributions in binary form must reproduce the above +// copyright notice, this list of conditions and the following disclaimer +// in the documentation and/or other materials provided with the +// distribution. +// * Neither the name of Google Inc. nor the names of its +// contributors may be used to endorse or promote products derived from +// this software without specific prior written permission. +// +// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + +#include + +#include "gtest/gtest.h" + +GTEST_API_ int main(int argc, char **argv) { + printf("Running main() from gtest_main.cc\n"); + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} diff --git a/Engine/source/testing/unitTesting.cpp b/Engine/source/testing/unitTesting.cpp new file mode 100644 index 000000000..a05b93270 --- /dev/null +++ b/Engine/source/testing/unitTesting.cpp @@ -0,0 +1,102 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED + +#include "console/engineAPI.h" +#include "console/consoleInternal.h" +#include "unitTesting.h" + +#include + +//----------------------------------------------------------------------------- + +class TorqueUnitTestListener : public ::testing::EmptyTestEventListener +{ + // Called before a test starts. + virtual void OnTestStart( const ::testing::TestInfo& testInfo ) + { + Con::printf("> Starting Test '%s.%s'", + testInfo.test_case_name(), testInfo.name()); + } + + // Called after a failed assertion or a SUCCEED() invocation. + virtual void OnTestPartResult( const ::testing::TestPartResult& testPartResult ) + { + if ( testPartResult.failed() ) + { + Con::warnf(">> Failed with '%s' in '%s' at (line:%d)", + testPartResult.summary(), + testPartResult.file_name(), + testPartResult.line_number() + ); + } + else + { + Con::printf(">> Passed with '%s' in '%s' at (line:%d)", + testPartResult.summary(), + testPartResult.file_name(), + testPartResult.line_number() + ); + } + } + + // Called after a test ends. + virtual void OnTestEnd( const ::testing::TestInfo& testInfo ) + { + Con::printf("> Ending Test '%s.%s'\n", + testInfo.test_case_name(), testInfo.name()); + } +}; + +DefineConsoleFunction( runAllUnitTests, int, (),, + "" ) +{ + // Set-up some empty arguments. + S32 testArgc = 0; + char** testArgv = NULL; + + // Initialize Google Test. + testing::InitGoogleTest( &testArgc, testArgv ); + + // Fetch the unit test instance. + testing::UnitTest& unitTest = *testing::UnitTest::GetInstance(); + + // Fetch the unit test event listeners. + testing::TestEventListeners& listeners = unitTest.listeners(); + + // Release the default listener. + delete listeners.Release( listeners.default_result_printer() ); + + // Add the Torque unit test listener. + listeners.Append( new TorqueUnitTestListener ); + + Con::printf( "\nUnit Tests Starting...\n" ); + + const S32 result = RUN_ALL_TESTS(); + + Con::printf( "\n... Unit Tests Ended.\n" ); + + return result; +} + +#endif // TORQUE_TESTS_ENABLED diff --git a/Engine/source/testing/unitTesting.h b/Engine/source/testing/unitTesting.h new file mode 100644 index 000000000..27cca588b --- /dev/null +++ b/Engine/source/testing/unitTesting.h @@ -0,0 +1,32 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _UNIT_TESTING_H_ +#define _UNIT_TESTING_H_ + +#ifdef TORQUE_TESTS_ENABLED + +#include + +#endif // TORQUE_TESTS_ENABLED + +#endif // _UNIT_TESTING_H_ diff --git a/Tools/projectGenerator/libs/libgtest.conf b/Tools/projectGenerator/libs/libgtest.conf new file mode 100644 index 000000000..3d66b9f4a --- /dev/null +++ b/Tools/projectGenerator/libs/libgtest.conf @@ -0,0 +1,34 @@ + diff --git a/Tools/projectGenerator/modules/testing.inc b/Tools/projectGenerator/modules/testing.inc new file mode 100644 index 000000000..cf9f21c26 --- /dev/null +++ b/Tools/projectGenerator/modules/testing.inc @@ -0,0 +1,35 @@ + From 3fbaa60e404ca91f32c2c8073b454b594221c60b Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 24 Jun 2014 00:02:10 +1000 Subject: [PATCH 091/317] Added memory leak testing for MSVC. --- Engine/source/testing/memoryTester.h | 66 +++++++++++++++++++++++++++ Engine/source/testing/unitTesting.cpp | 4 ++ 2 files changed, 70 insertions(+) create mode 100644 Engine/source/testing/memoryTester.h diff --git a/Engine/source/testing/memoryTester.h b/Engine/source/testing/memoryTester.h new file mode 100644 index 000000000..aba45f275 --- /dev/null +++ b/Engine/source/testing/memoryTester.h @@ -0,0 +1,66 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include +#include + +#if defined(TORQUE_OS_WIN) +#define _CRTDBG_MAP_ALLOC +#include +#endif + +namespace testing +{ + // Original author: Stephan Brenner + // https://github.com/ymx/gtest_mem + class MemoryLeakDetector : public EmptyTestEventListener + { + public: + virtual void OnTestStart(const TestInfo&) + { +#if defined(TORQUE_OS_WIN) + _CrtMemCheckpoint(&memState_); +#endif + } + + virtual void OnTestEnd(const TestInfo& test_info) + { + if(test_info.result()->Passed()) + { +#if defined(TORQUE_OS_WIN) + _CrtMemState stateNow, stateDiff; + _CrtMemCheckpoint(&stateNow); + int diffResult = _CrtMemDifference(&stateDiff, &memState_, &stateNow); + if (diffResult) + { + FAIL() << "Memory leak of " << stateDiff.lSizes[1] << " byte(s) detected."; + } +#endif + } + } + + private: +#if defined(TORQUE_OS_WIN) + _CrtMemState memState_; +#endif + }; +} \ No newline at end of file diff --git a/Engine/source/testing/unitTesting.cpp b/Engine/source/testing/unitTesting.cpp index a05b93270..70d4f2b58 100644 --- a/Engine/source/testing/unitTesting.cpp +++ b/Engine/source/testing/unitTesting.cpp @@ -25,6 +25,7 @@ #include "console/engineAPI.h" #include "console/consoleInternal.h" #include "unitTesting.h" +#include "memoryTester.h" #include @@ -87,6 +88,9 @@ DefineConsoleFunction( runAllUnitTests, int, (),, // Release the default listener. delete listeners.Release( listeners.default_result_printer() ); + // Add the memory leak tester. + listeners.Append( new testing::MemoryLeakDetector ); + // Add the Torque unit test listener. listeners.Append( new TorqueUnitTestListener ); From ef1a1326d3347ddfe7a52b855d67c673979f87fd Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 24 Jun 2014 08:31:23 +1000 Subject: [PATCH 092/317] Added macros for convenience. --- Engine/source/testing/unitTesting.h | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/Engine/source/testing/unitTesting.h b/Engine/source/testing/unitTesting.h index 27cca588b..7002de344 100644 --- a/Engine/source/testing/unitTesting.h +++ b/Engine/source/testing/unitTesting.h @@ -27,6 +27,17 @@ #include +/// Allow test fixtures named with a Fixture suffix, so that we can name tests +/// after a class name rather than having to call them XXTest. +#define TEST_FIX(test_fixture, test_name)\ + GTEST_TEST_(test_fixture, test_name, test_fixture##Fixture, \ + ::testing::internal::GetTypeId()) + +/// Convenience to define a test fixture with a Fixture suffix for use with +/// TEST_FIX. +#define FIXTURE(test_fixture)\ + class test_fixture##Fixture : public ::testing::Test + #endif // TORQUE_TESTS_ENABLED #endif // _UNIT_TESTING_H_ From caa915d0ec3544396186f6cec79895864ee43ab0 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 24 Jun 2014 09:12:50 +1000 Subject: [PATCH 093/317] Fixed copyight in memory tester. --- Engine/source/testing/memoryTester.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Engine/source/testing/memoryTester.h b/Engine/source/testing/memoryTester.h index aba45f275..72d19e8d8 100644 --- a/Engine/source/testing/memoryTester.h +++ b/Engine/source/testing/memoryTester.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2014 GarageGames, LLC +// Copyright (c) 2013 Stephan Brenner https://github.com/ymx/gtest_mem // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to @@ -30,8 +30,6 @@ namespace testing { - // Original author: Stephan Brenner - // https://github.com/ymx/gtest_mem class MemoryLeakDetector : public EmptyTestEventListener { public: From 3f687d8f43c0c435b3c8b46fcb36b3f6358e85c8 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 1 Jul 2014 18:14:08 +0200 Subject: [PATCH 094/317] Make use of PlayerData::swimForce --- Engine/source/T3D/player.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 6ce9f9020..593d48c7b 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -2955,7 +2955,7 @@ void Player::updateMove(const Move* move) // Clamp acceleration. F32 maxAcc = (mDataBlock->swimForce / getMass()) * TickSec; - if ( false && swimSpeed > maxAcc ) + if ( swimSpeed > maxAcc ) swimAcc *= maxAcc / swimSpeed; acc += swimAcc; From 2ae879ec703143c6106cbf00f61c02e79ebb8c09 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 8 Jul 2014 14:42:59 +0200 Subject: [PATCH 095/317] NavPath::alwaysRender works the same as NavMesh::alwaysRender. --- Engine/source/navigation/navPath.cpp | 55 +++++++++++++++++++++------- Engine/source/navigation/navPath.h | 2 + 2 files changed, 44 insertions(+), 13 deletions(-) diff --git a/Engine/source/navigation/navPath.cpp b/Engine/source/navigation/navPath.cpp index 5e4978afc..93c1a7a28 100644 --- a/Engine/source/navigation/navPath.cpp +++ b/Engine/source/navigation/navPath.cpp @@ -44,7 +44,8 @@ NavPath::NavPath() : mFrom(0.0f, 0.0f, 0.0f), mTo(0.0f, 0.0f, 0.0f) { - mTypeMask |= MarkerObjectType; + mTypeMask |= StaticShapeObjectType | MarkerObjectType; + mNetFlags.clear(Ghostable); mMesh = NULL; mWaypoints = NULL; @@ -162,6 +163,25 @@ const char *NavPath::getProtectedTo(void *obj, const char *data) return ""; } +bool NavPath::setProtectedAlwaysRender(void *obj, const char *index, const char *data) +{ + NavPath *path = static_cast(obj); + bool always = dAtob(data); + if(always) + { + if(!gEditingMission) + path->mNetFlags.set(Ghostable); + } + else + { + if(!gEditingMission) + path->mNetFlags.clear(Ghostable); + } + path->mAlwaysRender = always; + path->setMaskBits(PathMask); + return true; +} + static IRangeValidator NaturalNumber(1, S32_MAX); void NavPath::initPersistFields() @@ -188,9 +208,10 @@ void NavPath::initPersistFields() endGroup("NavPath"); addGroup("NavPath Render"); - - addField("alwaysRender", TypeBool, Offset(mAlwaysRender, NavPath), - "Render this NavPath even when not selected."); + + addProtectedField("alwaysRender", TypeBool, Offset(mAlwaysRender, NavMesh), + &setProtectedAlwaysRender, &defaultProtectedGetFn, + "Display this NavPath even outside the editor."); addField("xray", TypeBool, Offset(mXray, NavPath), "Render this NavPath through other objects."); @@ -204,8 +225,10 @@ bool NavPath::onAdd() if(!Parent::onAdd()) return false; + addToScene(); + // Ghost immediately if the editor's already open. - if(gEditingMission) + if(gEditingMission || mAlwaysRender) mNetFlags.set(Ghostable); // Automatically find a path if we can. @@ -215,18 +238,15 @@ bool NavPath::onAdd() // Set initial world bounds and stuff. resize(); - // Finally, add us to the simulation. - addToScene(); - return true; } void NavPath::onRemove() { - Parent::onRemove(); - // Remove from simulation. removeFromScene(); + + Parent::onRemove(); } bool NavPath::init() @@ -324,7 +344,9 @@ bool NavPath::plan() if(!init()) return false; - visitNext(); + if(!visitNext()) + return false; + while(update()); if(!finalise()) @@ -476,11 +498,18 @@ S32 NavPath::getCount() void NavPath::onEditorEnable() { mNetFlags.set(Ghostable); + if(isClientObject() && !mAlwaysRender) + addToScene(); } void NavPath::onEditorDisable() { - mNetFlags.clear(Ghostable); + if(!mAlwaysRender) + { + mNetFlags.clear(Ghostable); + if(isClientObject()) + removeFromScene(); + } } void NavPath::inspectPostApply() @@ -501,7 +530,7 @@ void NavPath::prepRenderImage(SceneRenderState *state) { ObjectRenderInst *ri = state->getRenderPass()->allocInst(); ri->renderDelegate.bind(this, &NavPath::renderSimple); - ri->type = RenderPassManager::RIT_Editor; + ri->type = RenderPassManager::RIT_Object; ri->translucentSort = true; ri->defaultKey = 1; state->getRenderPass()->addInst(ri); diff --git a/Engine/source/navigation/navPath.h b/Engine/source/navigation/navPath.h index 123a625d3..d7ba3d917 100644 --- a/Engine/source/navigation/navPath.h +++ b/Engine/source/navigation/navPath.h @@ -155,6 +155,8 @@ private: static const char *getProtectedMesh(void *obj, const char *data); static bool setProtectedWaypoints(void *obj, const char *index, const char *data); + static bool setProtectedAlwaysRender(void *obj, const char *index, const char *data); + static bool setProtectedFrom(void *obj, const char *index, const char *data); static const char *getProtectedFrom(void *obj, const char *data); From b48050209d87f673eb8d34f8a54a875fcd05d7c5 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 24 Jun 2014 12:57:43 +1000 Subject: [PATCH 096/317] Replaced existing Vector tests. --- Engine/source/core/util/test/testVector2.cpp | 138 ------------------- Engine/source/core/util/test/vectorTest.cpp | 118 ++++++++++++++++ Engine/source/unit/tests/testVector.cpp | 53 ------- 3 files changed, 118 insertions(+), 191 deletions(-) delete mode 100644 Engine/source/core/util/test/testVector2.cpp create mode 100644 Engine/source/core/util/test/vectorTest.cpp delete mode 100644 Engine/source/unit/tests/testVector.cpp diff --git a/Engine/source/core/util/test/testVector2.cpp b/Engine/source/core/util/test/testVector2.cpp deleted file mode 100644 index cbdf45487..000000000 --- a/Engine/source/core/util/test/testVector2.cpp +++ /dev/null @@ -1,138 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "unit/test.h" -#include "console/console.h" -#include "core/util/tVector.h" - - -#ifndef TORQUE_SHIPPING - -using namespace UnitTesting; - -#define TEST( x ) test( ( x ), "FAIL: " #x ) -#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x ) - -CreateUnitTest( TestVector, "Util/Vector" ) -{ - bool dtorVals[ 10 ]; - struct Dtor - { - bool* ptr; - Dtor() {} - Dtor( bool* ptr ) - : ptr( ptr ) { *ptr = false; } - ~Dtor() - { - *ptr = true; - } - }; - void testDestruction() - { - Vector< Dtor > v; - - for( U32 i = 0; i < 9; ++ i ) - v.push_back( Dtor( &dtorVals[ i ] ) ); - - v.decrement(); - v.decrement( 2 ); - v.pop_back(); - v.increment(); - v.last() = Dtor( &dtorVals[ 9 ] ); - v.clear(); - - TEST( dtorVals[ 0 ] ); - TEST( dtorVals[ 1 ] ); - TEST( dtorVals[ 2 ] ); - TEST( dtorVals[ 3 ] ); - TEST( dtorVals[ 4 ] ); - TEST( dtorVals[ 5 ] ); - TEST( dtorVals[ 6 ] ); - TEST( dtorVals[ 7 ] ); - TEST( dtorVals[ 8 ] ); - TEST( dtorVals[ 9 ] ); - } - - static S32 QSORT_CALLBACK sortInts( const S32* a, const S32* b ) - { - S32 av = *a; - S32 bv = *b; - - if( av < bv ) - return -1; - else if( av > bv ) - return 1; - else - return 0; - } - - void testSort() - { - Vector< S32 > v; - - v.push_back( 0 ); - v.push_back( 10 ); - v.push_back( 2 ); - v.push_back( 3 ); - v.push_back( 14 ); - v.push_back( 4 ); - v.push_back( 12 ); - v.push_back( 6 ); - v.push_back( 16 ); - v.push_back( 7 ); - v.push_back( 8 ); - v.push_back( 1 ); - v.push_back( 11 ); - v.push_back( 5 ); - v.push_back( 13 ); - v.push_back( 9 ); - v.push_back( 15 ); - - v.sort( sortInts ); - - TEST( v[ 0 ] == 0 ); - TEST( v[ 1 ] == 1 ); - TEST( v[ 2 ] == 2 ); - TEST( v[ 3 ] == 3 ); - TEST( v[ 4 ] == 4 ); - TEST( v[ 5 ] == 5 ); - TEST( v[ 6 ] == 6 ); - TEST( v[ 7 ] == 7 ); - TEST( v[ 8 ] == 8 ); - TEST( v[ 9 ] == 9 ); - TEST( v[ 10 ] == 10 ); - TEST( v[ 11 ] == 11 ); - TEST( v[ 12 ] == 12 ); - TEST( v[ 13 ] == 13 ); - TEST( v[ 14 ] == 14 ); - TEST( v[ 15 ] == 15 ); - TEST( v[ 16 ] == 16 ); - } - - void run() - { - testSort(); - testDestruction(); - } -}; - -#endif diff --git a/Engine/source/core/util/test/vectorTest.cpp b/Engine/source/core/util/test/vectorTest.cpp new file mode 100644 index 000000000..0aaf2f29e --- /dev/null +++ b/Engine/source/core/util/test/vectorTest.cpp @@ -0,0 +1,118 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "core/util/tVector.h" + +// Define some test data used below. +static const S32 ints[] = {0, 10, 2, 3, 14, 4, 12, 6, 16, 7, 8, 1, 11, 5, 13, 9, 15}; +static const U32 length = sizeof(ints) / sizeof(S32); +static S32 QSORT_CALLBACK sortInts(const S32* a, const S32* b) +{ + S32 av = *a; + S32 bv = *b; + + if (av < bv) + return -1; + else if (av > bv) + return 1; + else + return 0; +} + +TEST(Vector, Allocation) +{ + Vector *vector = new Vector; + for (S32 i = 0; i < 1000; i++) + vector->push_back(10000 + i); + + // Erase the first element, 500 times. + for (S32 i = 0; i < 500; i++) + vector->erase(U32(0)); + + vector->compact(); + + EXPECT_EQ(vector->size(), 500) << "Vector was unexpectedly short!"; + + delete vector; +} + +TEST(Vector, Deallocation) +{ + struct Dtor + { + bool* ptr; + Dtor() {} // Needed for vector increment. + Dtor(bool* ptr): ptr(ptr) {} + ~Dtor() + { + *ptr = true; + } + }; + + bool dtorVals[10]; + Vector v; + + // Only add the first 9 entries; the last is populated below. + for (U32 i = 0; i < 9; i++) + v.push_back(Dtor(&dtorVals[i])); + + // Fill the values array with false so we can test for destruction. + for (U32 i = 0; i < 10; i++) + dtorVals[i] = false; + + v.decrement(); + EXPECT_TRUE(dtorVals[8]) << "Vector::decrement failed to call destructor"; + + v.decrement(2); + EXPECT_TRUE(dtorVals[7]) << "Vector::decrement failed to call destructor"; + EXPECT_TRUE(dtorVals[6]) << "Vector::decrement failed to call destructor"; + + v.pop_back(); + EXPECT_TRUE(dtorVals[5]) << "Vector::pop_back failed to call destructor"; + + v.increment(); + v.last() = Dtor(&dtorVals[9]); + v.clear(); + + // All elements should have been destructed. + for (U32 i = 0; i < 10; i++) + EXPECT_TRUE(dtorVals[i]) + << "Element " << i << "'s destructor was not called"; +} + +TEST(Vector, Sorting) +{ + Vector v; + + for(U32 i = 0; i < length; i++) + v.push_back(ints[i]); + + v.sort(sortInts); + + for(U32 i = 0; i < length - 1; i++) + EXPECT_TRUE(v[i] <= v[i + 1]) + << "Element " << i << " was not in sorted order"; +} + +#endif \ No newline at end of file diff --git a/Engine/source/unit/tests/testVector.cpp b/Engine/source/unit/tests/testVector.cpp deleted file mode 100644 index c7e38d250..000000000 --- a/Engine/source/unit/tests/testVector.cpp +++ /dev/null @@ -1,53 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "platform/platform.h" -#include "unit/test.h" -#include "unit/memoryTester.h" -#include "core/util/tVector.h" - -using namespace UnitTesting; - -CreateUnitTest(TestVectorAllocate, "Types/Vector") -{ - void run() - { - MemoryTester m; - m.mark(); - - Vector *vector = new Vector; - for(S32 i=0; i<1000; i++) - vector->push_back(10000 + i); - - // Erase the first element, 500 times. - for(S32 i=0; i<500; i++) - vector->erase(U32(0)); - - vector->compact(); - - test(vector->size() == 500, "Vector was unexpectedly short!"); - - delete vector; - - test(m.check(), "Vector allocation test leaked memory!"); - } -}; \ No newline at end of file From ec34d9928c5361d5cb664ae78bf1927ee348a4fb Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 8 Jul 2014 17:39:22 +0200 Subject: [PATCH 097/317] Only check memory if a flag is set. --- Engine/source/testing/unitTesting.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/Engine/source/testing/unitTesting.cpp b/Engine/source/testing/unitTesting.cpp index 70d4f2b58..0c42577ac 100644 --- a/Engine/source/testing/unitTesting.cpp +++ b/Engine/source/testing/unitTesting.cpp @@ -88,8 +88,10 @@ DefineConsoleFunction( runAllUnitTests, int, (),, // Release the default listener. delete listeners.Release( listeners.default_result_printer() ); - // Add the memory leak tester. - listeners.Append( new testing::MemoryLeakDetector ); + if ( Con::getBoolVariable( "$testing::checkMemoryLeaks", false ) ) { + // Add the memory leak tester. + listeners.Append( new testing::MemoryLeakDetector ); + } // Add the Torque unit test listener. listeners.Append( new TorqueUnitTestListener ); From 8b1ff267f09381e51f851af0373588a004e8a376 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 8 Jul 2014 17:41:25 +0200 Subject: [PATCH 098/317] Fixed quaternion set from euler. --- Engine/source/math/mQuat.cpp | 22 +++++----- Engine/source/math/test/mQuatTest.cpp | 63 +++++++++++++++++++++++++++ 2 files changed, 74 insertions(+), 11 deletions(-) create mode 100644 Engine/source/math/test/mQuatTest.cpp diff --git a/Engine/source/math/mQuat.cpp b/Engine/source/math/mQuat.cpp index c78252bc2..7ffc4eaa3 100644 --- a/Engine/source/math/mQuat.cpp +++ b/Engine/source/math/mQuat.cpp @@ -35,27 +35,27 @@ QuatF& QuatF::set( const EulerF & e ) F32 cx, sx; F32 cy, sy; F32 cz, sz; - mSinCos( -e.x * 0.5f, sx, cx ); - mSinCos( -e.y * 0.5f, sy, cy ); - mSinCos( -e.z * 0.5f, sz, cz ); + mSinCos( e.x * 0.5f, sx, cx ); + mSinCos( e.y * 0.5f, sy, cy ); + mSinCos( e.z * 0.5f, sz, cz ); - // Qyaw(z) = [ (0, 0, sin z/2), cos z/2 ] + // Qyaw(z) = [ (0, 0, sin z/2), cos z/2 ] // Qpitch(x) = [ (sin x/2, 0, 0), cos x/2 ] - // Qroll(y) = [ (0, sin y/2, 0), cos y/2 ] - // this = Qresult = Qyaw*Qpitch*Qroll ZXY + // Qroll(y) = [ (0, sin y/2, 0), cos y/2 ] + // this = Qresult = Qyaw*Qpitch*Qroll ZXY // // The code that folows is a simplification of: - // roll*=pitch; - // roll*=yaw; - // *this = roll; + // roll*=pitch; + // roll*=yaw; + // *this = roll; F32 cycz, sysz, sycz, cysz; cycz = cy*cz; sysz = sy*sz; sycz = sy*cz; cysz = cy*sz; - w = cycz*cx + sysz*sx; + w = cycz*cx - sysz*sx; x = cycz*sx + sysz*cx; - y = sycz*cx - cysz*sx; + y = sycz*cx + cysz*sx; z = cysz*cx - sycz*sx; return *this; diff --git a/Engine/source/math/test/mQuatTest.cpp b/Engine/source/math/test/mQuatTest.cpp new file mode 100644 index 000000000..4b4027e4d --- /dev/null +++ b/Engine/source/math/test/mQuatTest.cpp @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "math/mQuat.h" +#include "math/mAngAxis.h" +#include "math/mMatrix.h" + +/// For testing things that should be close to 0, but accounting for floating- +/// point inaccuracy. +static const F32 epsilon = 1e-3f; + +/// Test quaternions for equality by expecting the angle between them to be +/// close to 0. +#define EXPECT_QUAT_EQ(q1, q2) EXPECT_LT(q1.angleBetween(q2), epsilon) + +TEST(QuatF, AngleBetween) +{ + QuatF p(QuatF::Identity), q(QuatF::Identity); + EXPECT_LT(p.angleBetween(q), epsilon) + << "Angle between identity quaternions should be ~0."; + + p.set(EulerF(0.1, 0.15, -0.2)); + q = p; + EXPECT_LT(p.angleBetween(q), epsilon) + << "Angle between identical quaternions should be ~0."; +} + +/// Test conversion from EulerF. +TEST(QuatF, Construction) +{ + EulerF eId(0, 0, 0); + EXPECT_QUAT_EQ(QuatF(eId), QuatF::Identity) + << "Quaternions constructed from identity EulerF and QuatF::Identity not equal."; + + EulerF eRot(0.0f, -0.0f, 1.5707963267948966f); + MatrixF mat(eRot); + AngAxisF aaRot(mat); + EXPECT_QUAT_EQ(QuatF(eRot), QuatF(aaRot)) + << "Quaternions constructed from EulerF and AngAxisF not equal."; +} + +#endif \ No newline at end of file From 2248c90d52d24e48241decf8b17ea9a8b663a186 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 8 Jul 2014 18:55:37 +0200 Subject: [PATCH 099/317] Default mainfiles to run unit tests. --- Templates/Empty/game/runTests.cs | 13 +++++++++++++ Templates/Full/game/runTests.cs | 13 +++++++++++++ 2 files changed, 26 insertions(+) create mode 100644 Templates/Empty/game/runTests.cs create mode 100644 Templates/Full/game/runTests.cs diff --git a/Templates/Empty/game/runTests.cs b/Templates/Empty/game/runTests.cs new file mode 100644 index 000000000..9099a6e68 --- /dev/null +++ b/Templates/Empty/game/runTests.cs @@ -0,0 +1,13 @@ +new GuiControlProfile(GuiDefaultProfile); +new GuiControlProfile(GuiToolTipProfile); +new GuiCanvas(Canvas); +function onLightManagerActivate() {} +function onLightManagerDeactivate() {} +Canvas.setWindowTitle("Torque 3D Unit Tests"); +new RenderPassManager(DiffuseRenderPassManager); +setLightManager("Basic Lighting"); +setLogMode(2); +$Con::LogBufferEnabled = false; +$Testing::checkMemoryLeaks = false; +runAllUnitTests(); +quit(); diff --git a/Templates/Full/game/runTests.cs b/Templates/Full/game/runTests.cs new file mode 100644 index 000000000..9099a6e68 --- /dev/null +++ b/Templates/Full/game/runTests.cs @@ -0,0 +1,13 @@ +new GuiControlProfile(GuiDefaultProfile); +new GuiControlProfile(GuiToolTipProfile); +new GuiCanvas(Canvas); +function onLightManagerActivate() {} +function onLightManagerDeactivate() {} +Canvas.setWindowTitle("Torque 3D Unit Tests"); +new RenderPassManager(DiffuseRenderPassManager); +setLightManager("Basic Lighting"); +setLogMode(2); +$Con::LogBufferEnabled = false; +$Testing::checkMemoryLeaks = false; +runAllUnitTests(); +quit(); From dbc0dff52bc373179f0cc8d0d0d152db826b080a Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 8 Jul 2014 19:09:26 +0200 Subject: [PATCH 100/317] Align test file name with actual file name. --- Engine/source/core/util/test/{vectorTest.cpp => tVectorTest.cpp} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename Engine/source/core/util/test/{vectorTest.cpp => tVectorTest.cpp} (100%) diff --git a/Engine/source/core/util/test/vectorTest.cpp b/Engine/source/core/util/test/tVectorTest.cpp similarity index 100% rename from Engine/source/core/util/test/vectorTest.cpp rename to Engine/source/core/util/test/tVectorTest.cpp From 1a5664c580ef33f0fc2306cf9e09a29c42ced8f1 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 8 Jul 2014 22:06:57 +0200 Subject: [PATCH 101/317] Removed existing test framework. Now we just fix compiler errors! --- Engine/source/unit/consoleTest.cpp | 39 --- Engine/source/unit/memoryTester.cpp | 37 --- Engine/source/unit/memoryTester.h | 37 --- Engine/source/unit/test.cpp | 288 ------------------ Engine/source/unit/test.h | 165 ---------- .../unit/unitTestComponentInterface.cpp | 24 -- .../source/unit/unitTestComponentInterface.h | 91 ------ 7 files changed, 681 deletions(-) delete mode 100644 Engine/source/unit/consoleTest.cpp delete mode 100644 Engine/source/unit/memoryTester.cpp delete mode 100644 Engine/source/unit/memoryTester.h delete mode 100644 Engine/source/unit/test.cpp delete mode 100644 Engine/source/unit/test.h delete mode 100644 Engine/source/unit/unitTestComponentInterface.cpp delete mode 100644 Engine/source/unit/unitTestComponentInterface.h diff --git a/Engine/source/unit/consoleTest.cpp b/Engine/source/unit/consoleTest.cpp deleted file mode 100644 index 2a159ca0c..000000000 --- a/Engine/source/unit/consoleTest.cpp +++ /dev/null @@ -1,39 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "core/strings/stringFunctions.h" - -#include "unit/test.h" -#include "console/console.h" - -using namespace UnitTesting; - -ConsoleFunction(unitTest_runTests, void, 1, 3, "([searchString[, bool skipInteractive]])" - "@brief Run unit tests, or just the tests that prefix match against the searchString.\n\n" - "@ingroup Console") -{ - const char *searchString = (argc > 1 ? argv[1] : ""); - bool skip = (argc > 2 ? dAtob(argv[2]) : false); - - TestRun tr; - tr.test(searchString, skip); -} \ No newline at end of file diff --git a/Engine/source/unit/memoryTester.cpp b/Engine/source/unit/memoryTester.cpp deleted file mode 100644 index 15b646bbb..000000000 --- a/Engine/source/unit/memoryTester.cpp +++ /dev/null @@ -1,37 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "platform/platform.h" -#include "unit/memoryTester.h" - -using namespace UnitTesting; - -void MemoryTester::mark() -{ - -} - -bool MemoryTester::check() -{ - //UnitTesting::UnitPrint("MemoryTester::check - unavailable w/o TORQUE_DEBUG_GUARD defined!"); - return true; -} diff --git a/Engine/source/unit/memoryTester.h b/Engine/source/unit/memoryTester.h deleted file mode 100644 index bd9dfd71c..000000000 --- a/Engine/source/unit/memoryTester.h +++ /dev/null @@ -1,37 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#ifndef _UNIT_MEMORYTESTER_H_ -#define _UNIT_MEMORYTESTER_H_ - -namespace UnitTesting -{ - class MemoryTester - { - public: - void mark(); - bool check(); - }; -} - - -#endif diff --git a/Engine/source/unit/test.cpp b/Engine/source/unit/test.cpp deleted file mode 100644 index f2d8aced0..000000000 --- a/Engine/source/unit/test.cpp +++ /dev/null @@ -1,288 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include -#include - -#include "core/strings/stringFunctions.h" - -#include "console/console.h" - -#include "unit/test.h" - -#include "core/util/journal/process.h" - - -namespace UnitTesting -{ - -//----------------------------------------------------------------------------- - -TestRegistry *TestRegistry::_list = 0; - - -//----------------------------------------------------------------------------- - -static const S32 MaxMarginCount = 32; -static const S32 MaxMarginValue = 128; -static S32 _Margin[MaxMarginCount] = { 3 }; -static S32* _MarginPtr = _Margin; -static char _MarginString[MaxMarginValue]; - -static void _printMargin() -{ - if (*_MarginPtr) - ::fwrite(_MarginString,1,*_MarginPtr,stdout); -} - -void UnitMargin::Push(S32 margin) -{ - if (_MarginPtr < _Margin + MaxMarginCount) { - *++_MarginPtr = (margin < MaxMarginValue)? margin: MaxMarginValue; - memset(_MarginString,' ',*_MarginPtr); - } -} - -void UnitMargin::Pop() -{ - if (_MarginPtr > _Margin) { - _MarginPtr--; - memset(_MarginString,' ',*_MarginPtr); - } -} - -S32 UnitMargin::Current() -{ - return *_MarginPtr; -} - -void UnitPrint(const char* str) -{ - static bool lineStart = true; - Platform::outputDebugString(str); - - // Need to scan for '\n' in order to support margins - const char* ptr = str, *itr = ptr; - for (; *itr != 0; itr++) - if (*itr == '\n') - { - if (lineStart) - _printMargin(); - ::fwrite(ptr,1,itr - ptr + 1,stdout); - ptr = itr + 1; - lineStart = true; - } - - // End the line with a carriage return unless the - // line ends with a line continuation char. - if (ptr != itr) { - if (lineStart) - _printMargin(); - if (itr[-1] == '\\') { - ::fwrite(ptr,1,itr - ptr - 1,stdout); - lineStart = false; - } - else { - ::fwrite(ptr,1,itr - ptr,stdout); - ::fwrite("\n",1,1,stdout); - lineStart = true; - } - } - else { - ::fwrite("\n",1,1,stdout); - lineStart = true; - } - ::fflush(stdout); -} - - -//----------------------------------------------------------------------------- - -UnitTest::UnitTest() { - _testCount = 0; - _failureCount = 0; - _warningCount = 0; - _lastTestResult = true; -} - -void UnitTest::fail(const char* msg) -{ - Con::warnf("** Failed: %s",msg); - dFetchAndAdd( _failureCount, 1 ); -} - -void UnitTest::warn(const char* msg) -{ - Con::warnf("** Warning: %s",msg); - dFetchAndAdd( _warningCount, 1 ); -} - - -//----------------------------------------------------------------------------- - -TestRegistry::TestRegistry(const char* name, bool interactive, const char *className) -{ - // Check that no existing test uses the same class-name; this is guaranteed - // to lead to funkiness. - TestRegistry *walk = _list; - while(walk) - { - if(walk->_className) - { - AssertFatal(dStricmp(className, walk->_className), "TestRegistry::TestRegistry - got two unit tests with identical class names; they must have unique class names!"); - } - - walk = walk->_next; - } - - // Add us to the list. - _next = _list; - _list = this; - - // And fill in our fields. - _name = name; - _className = className; - _isInteractive = interactive; -} - -DynamicTestRegistration::DynamicTestRegistration( const char *name, UnitTest *test ) : TestRegistry( name, false, NULL ), mUnitTest( test ) -{ - -} - -DynamicTestRegistration::~DynamicTestRegistration() -{ - // Un-link ourselves from the test registry - TestRegistry *walk = _list; - - // Easy case! - if( walk == this ) - _list = _next; - else - { - // Search for us and remove - while( ( walk != 0 ) && ( walk->_next != 0 ) && ( walk->_next != this ) ) - walk = walk->_next; - - // When this loop is broken, walk will be the unit test in the list previous to this one - if( walk != 0 && walk->_next != 0 ) - walk->_next = walk->_next->_next; - } -} - - -//----------------------------------------------------------------------------- - -TestRun::TestRun() -{ - _subCount = 0; - _testCount = 0; - _failureCount = 0; - _warningCount = 0; -} - -void TestRun::printStats() -{ - Con::printf("-- %d test%s run (with %d sub-test%s)", - _testCount,(_testCount != 1)? "s": "", - _subCount,(_subCount != 1)? "s": ""); - - if (_testCount) - { - if (_failureCount) - Con::printf("** %d reported failure%s", - _failureCount,(_failureCount != 1)? "s": ""); - else if (_warningCount) - Con::printf("** %d reported warning%s", - _warningCount,(_warningCount != 1)? "s": ""); - else - Con::printf("-- No reported failures"); - } -} - -void TestRun::test(TestRegistry* reg) -{ - Con::printf("-- Testing: %s %s",reg->getName(), reg->isInteractive() ? "(interactive)" : "" ); - - UnitMargin::Push(_Margin[0]); - - // Run the test. - UnitTest* test = reg->newTest(); - test->run(); - - UnitMargin::Pop(); - - // Update stats. - _failureCount += test->getFailureCount(); - _subCount += test->getTestCount(); - _warningCount += test->getWarningCount(); - _testCount++; - - // Don't forget to delete the test! - delete test; -} - -// [tom, 2/5/2007] To provide a predictable environment for the tests, this -// now changes the current directory to the executable's directory before -// running the tests. The previous current directory is restored on exit. - -bool TestRun::test(const char* module, bool skipInteractive) -{ - StringTableEntry cwdSave = Platform::getCurrentDirectory(); - - S32 len = strlen(module); - - const char *skipMsg = skipInteractive ? "(skipping interactive tests)" : ""; - - // Indicate to the user what we're up to. - if (!len) - Con::printf("-- Running all unit tests %s", skipMsg); - else - Con::printf("-- Running %s tests %s",module, skipMsg); - - for (TestRegistry* itr = TestRegistry::getFirst(); itr; itr = itr->getNext()) - { - if (!len || !dStrnicmp(module,itr->getName(),len)) - { - // Skip the test if it's interactive and we're in skipinteractive mode. - if(skipInteractive && itr->isInteractive()) - continue; - - // Otherwise, run the test! - Platform::setCurrentDirectory(Platform::getMainDotCsDir()); - test(itr); - } - } - // Print out a nice report on how we did. - printStats(); - - Platform::setCurrentDirectory(cwdSave); - - // sanity check for avoid Process::requestShutdown() called on some tests - Process::processEvents(); - - // And indicate our failure situation in the return value. - return !_failureCount; -} - -} // Namespace - diff --git a/Engine/source/unit/test.h b/Engine/source/unit/test.h deleted file mode 100644 index df737d655..000000000 --- a/Engine/source/unit/test.h +++ /dev/null @@ -1,165 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#ifndef UNIT_UNITTESTING_H -#define UNIT_UNITTESTING_H - -#ifndef _PLATFORMINTRINSICS_H_ -# include "platform/platformIntrinsics.h" -#endif - - -namespace UnitTesting { - -//----------------------------------------------------------------------------- - -struct UnitMargin -{ -static void Push(S32 margin); -static void Pop(); -static S32 Current(); -}; - -void UnitPrint(const char* msg); - - -//----------------------------------------------------------------------------- - -class UnitTest { - S32 _testCount; - S32 _failureCount; - S32 _warningCount; - - bool _lastTestResult; - -public: - UnitTest(); - virtual ~UnitTest() {}; - - /// Test an assertion and note if it has failed. - bool test(bool a,const char* msg) { - dFetchAndAdd( _testCount, 1 ); - if (!a) - fail(msg); - _lastTestResult = a; - return a; - } - - /// Report a failture condition. - void fail(const char* msg); - - /// Report a warning - void warn(const char* msg); - - S32 getTestCount() const { return _testCount; } - S32 getFailureCount() const { return _failureCount; } - S32 getWarningCount() const { return _warningCount; } - bool lastTestPassed() const { return _lastTestResult; } - - /// Implement this with the specific test. - virtual void run() = 0; -}; - - -//----------------------------------------------------------------------------- - -class TestRegistry -{ - friend class DynamicTestRegistration; // Bless me, Father, for I have sinned, but this is damn cool - - static TestRegistry *_list; - TestRegistry *_next; - - const char *_name; - const char *_className; - bool _isInteractive; - -public: - TestRegistry(const char* name, bool interactive, const char *className); - virtual ~TestRegistry() {} - static TestRegistry* getFirst() { return _list; } - TestRegistry* getNext() { return _next; } - const char* getName() { return _name; } - const bool isInteractive() { return _isInteractive; } - virtual UnitTest* newTest() = 0; -}; - -template -class TestRegistration: public TestRegistry -{ -public: - virtual ~TestRegistration() - { - } - - TestRegistration(const char* name, bool interactive, const char *className) - : TestRegistry(name, interactive, className) - { - } - - virtual UnitTest* newTest() - { - return new T; - } -}; - -class DynamicTestRegistration : public TestRegistry -{ - - UnitTest *mUnitTest; - -public: - DynamicTestRegistration( const char *name, UnitTest *test ); - - virtual ~DynamicTestRegistration(); - - virtual UnitTest *newTest() { return mUnitTest; } -}; - - -//----------------------------------------------------------------------------- - -class TestRun { - S32 _testCount; - S32 _subCount; - S32 _failureCount; - S32 _warningCount; - void test(TestRegistry* reg); -public: - TestRun(); - void printStats(); - bool test(const char* module, bool skipInteractive = false); -}; - -#define CreateUnitTest(Class,Name) \ - class Class; \ - static UnitTesting::TestRegistration _UnitTester##Class (Name, false, #Class); \ - class Class : public UnitTesting::UnitTest - -#define CreateInteractiveTest(Class,Name) \ - class Class; \ - static UnitTesting::TestRegistration _UnitTester##Class (Name, true, #Class); \ - class Class : public UnitTesting::UnitTest - -} // Namespace - -#endif diff --git a/Engine/source/unit/unitTestComponentInterface.cpp b/Engine/source/unit/unitTestComponentInterface.cpp deleted file mode 100644 index 14d5653c7..000000000 --- a/Engine/source/unit/unitTestComponentInterface.cpp +++ /dev/null @@ -1,24 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "unit/unitTestComponentInterface.h" - diff --git a/Engine/source/unit/unitTestComponentInterface.h b/Engine/source/unit/unitTestComponentInterface.h deleted file mode 100644 index d829311b8..000000000 --- a/Engine/source/unit/unitTestComponentInterface.h +++ /dev/null @@ -1,91 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#ifndef _UNITTESTCOMPONENTINTERFACE_H_ -#define _UNITTESTCOMPONENTINTERFACE_H_ - -#include "unit/test.h" -#include "component/simComponent.h" -#include "component/componentInterface.h" - -// This is commented out because I want to keep the explicit namespace referencing -// so that the multiple inheritances from UnitTest doesn't screw anyone up. It will -// also make for more readable code in the derived test-interfaces. -patw -//using namespace UnitTesting; - -/// This is a class that will make it very easy for a component author to provide -/// unit testing functionality from within an instantiated component. -class UnitTestComponentInterface : public ComponentInterface, UnitTesting::UnitTest -{ - typedef ComponentInterface Parent; - -private: - StringTableEntry mName; - UnitTesting::DynamicTestRegistration *mTestReg; - - // Constructors/Destructors -public: - UnitTestComponentInterface( const char *name ) - { - mName = StringTable->insert( name ); - - mTestReg = new UnitTesting::DynamicTestRegistration( name, this ); - } - - virtual ~UnitTestComponentInterface() - { - delete mTestReg; - } - - // ComponentInterface overrides -public: - virtual bool isValid() const - { - return Parent::isValid() && ( mTestReg != NULL ); - } - - // UnitTest overrides -public: - /// This is the only function you need to overwrite to add a unit test interface - /// your component. - virtual void run() = 0; -}; - -// Macros -#ifndef TORQUE_DEBUG -# define DECLARE_UNITTEST_INTERFACE(x) -# define CACHE_UNITTEST_INTERFACE(x) -#else -//----------------------------------------------------------------------------- -# define DECLARE_UNITTEST_INTERFACE(x) \ - class x##_UnitTestInterface : public UnitTestComponentInterface \ - {\ - typedef UnitTestComponentInterface Parent; \ - public: \ - x##_UnitTestInterface() : UnitTestComponentInterface( #x ) {}; \ - virtual void run(); \ - } _##x##UnitTestInterface -//----------------------------------------------------------------------------- -# define CACHE_UNITTEST_INTERFACE(x) registerCachedInterface( "unittest", #x, this, &_##x##UnitTestInterface ) -#endif - -#endif \ No newline at end of file From 2c2284e699569c72eadecb5d0ae875aeac03dd83 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 8 Jul 2014 23:12:49 +0200 Subject: [PATCH 102/317] Ported maths tests and added a test for #570. --- Engine/source/math/test/mBoxTest.cpp | 42 ++++++++ Engine/source/math/test/mPlaneTest.cpp | 74 ++++++++++++++ Engine/source/math/test/mPolyhedronTest.cpp | 70 +++++++++++++ Engine/source/math/test/testMathPlane.cpp | 79 --------------- Engine/source/math/test/testPolyhedron.cpp | 104 -------------------- 5 files changed, 186 insertions(+), 183 deletions(-) create mode 100644 Engine/source/math/test/mBoxTest.cpp create mode 100644 Engine/source/math/test/mPlaneTest.cpp create mode 100644 Engine/source/math/test/mPolyhedronTest.cpp delete mode 100644 Engine/source/math/test/testMathPlane.cpp delete mode 100644 Engine/source/math/test/testPolyhedron.cpp diff --git a/Engine/source/math/test/mBoxTest.cpp b/Engine/source/math/test/mBoxTest.cpp new file mode 100644 index 000000000..cd09a0109 --- /dev/null +++ b/Engine/source/math/test/mBoxTest.cpp @@ -0,0 +1,42 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "math/mBox.h" + +TEST(Box3F, GetOverlap) +{ + Box3F b1(Point3F(-1, -1, -1), Point3F(1, 1, 1)); + EXPECT_EQ(b1.getOverlap(b1), b1) + << "A box's overlap with itself should be itself."; + + Box3F b2(Point3F(0, 0, 0), Point3F(1, 1, 1)); + EXPECT_EQ(b1.getOverlap(b2), b2) + << "Box's overlap should be the intersection of two boxes."; + + Box3F b3(Point3F(10, 10, 10), Point3F(11, 11, 11)); + EXPECT_TRUE(b1.getOverlap(b3).isEmpty()) + << "Overlap of boxes that do not overlap should be empty."; +} + +#endif \ No newline at end of file diff --git a/Engine/source/math/test/mPlaneTest.cpp b/Engine/source/math/test/mPlaneTest.cpp new file mode 100644 index 000000000..0013c63c6 --- /dev/null +++ b/Engine/source/math/test/mPlaneTest.cpp @@ -0,0 +1,74 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "math/mPlane.h" + +// Static test data. All combinations of position and normal are tested in each +// test case. This allows a large number of tests without introducing non- +// deterministic test behavior. + +static const Point3F positions[] = {Point3F(0, 0, 0), Point3F(1, -2, 3), Point3F(1e5, 2e5, -3e6)}; +static const U32 numPositions = sizeof(positions) / sizeof(Point3F); + +static const Point3F normals[] = {Point3F(1, 0, 0), Point3F(-4, -2, 6), Point3F(1e8, 2e7, 5e-2)}; +static const U32 numNormals = sizeof(normals) / sizeof(Point3F); + +/// Tests that points in the direction of the normal are in 'Front' of the +/// plane, while points in the reverse direction of the normal are in +/// 'Back' of the plane. +TEST(Plane, WhichSide) +{ + for(U32 i = 0; i < numPositions; i++) { + for(U32 j = 0; i < numNormals; j++) { + Point3F position = positions[i]; + Point3F normal = normals[j]; + + PlaneF p(position, normal); + + EXPECT_EQ(p.whichSide(position + normal), PlaneF::Front ); + EXPECT_EQ(p.whichSide(position - normal), PlaneF::Back ); + EXPECT_EQ(p.whichSide(position), PlaneF::On ); + } + } +} + +/// Tests that the distToPlane method returns the exact length that the test +/// point is offset by in the direction of the normal. +TEST(Plane, DistToPlane) +{ + for(U32 i = 0; i < numPositions; i++) { + for(U32 j = 0; i < numNormals; j++) { + Point3F position = positions[i]; + Point3F normal = normals[j]; + + PlaneF p(position, normal); + + EXPECT_FLOAT_EQ(p.distToPlane(position + normal), normal.len()); + EXPECT_FLOAT_EQ(p.distToPlane(position - normal), -normal.len()); + EXPECT_FLOAT_EQ(p.distToPlane(position), 0); + } + } +} + +#endif diff --git a/Engine/source/math/test/mPolyhedronTest.cpp b/Engine/source/math/test/mPolyhedronTest.cpp new file mode 100644 index 000000000..c2a949573 --- /dev/null +++ b/Engine/source/math/test/mPolyhedronTest.cpp @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "math/mPolyhedron.h" + +FIXTURE(Polyhedron) +{ +protected: + Vector planes; + + virtual void SetUp() + { + // Build planes for a unit cube centered at the origin. + // Note that the normals must be facing inwards. + planes.push_back(PlaneF(Point3F(-0.5f, 0.f, 0.f ), Point3F( 1.f, 0.f, 0.f))); + planes.push_back(PlaneF(Point3F( 0.5f, 0.f, 0.f ), Point3F(-1.f, 0.f, 0.f))); + planes.push_back(PlaneF(Point3F( 0.f, -0.5f, 0.f ), Point3F( 0.f, 1.f, 0.f))); + planes.push_back(PlaneF(Point3F( 0.f, 0.5f, 0.f ), Point3F( 0.f, -1.f, 0.f))); + planes.push_back(PlaneF(Point3F( 0.f, 0.f, -0.5f), Point3F( 0.f, 0.f, 1.f))); + planes.push_back(PlaneF(Point3F( 0.f, 0.f, 0.5f), Point3F( 0.f, 0.f, -1.f))); + } +}; + +TEST_FIX(Polyhedron, BuildFromPlanes) +{ + // Turn planes into a polyhedron. + Polyhedron p1; + p1.buildFromPlanes(PlaneSetF(planes.address(), planes.size())); + + // Check if we got a cube back. + EXPECT_EQ(p1.getNumPoints(), 8); + EXPECT_EQ(p1.getNumPlanes(), 6); + EXPECT_EQ(p1.getNumEdges(), 12); + + // Add extra plane that doesn't contribute a new edge. + Vector planes2 = planes; + planes2.push_back( PlaneF( Point3F( 0.5f, 0.5f, 0.5f ), Point3F( -1.f, -1.f, -1.f ) ) ); + + // Turn them into another polyhedron. + Polyhedron p2; + p2.buildFromPlanes(PlaneSetF(planes2.address(), planes2.size())); + + // Check if we got a cube back. + EXPECT_EQ(p2.getNumPoints(), 8); + EXPECT_EQ(p2.getNumPlanes(), 6); + EXPECT_EQ(p2.getNumEdges(), 12); +} + +#endif diff --git a/Engine/source/math/test/testMathPlane.cpp b/Engine/source/math/test/testMathPlane.cpp deleted file mode 100644 index 6600c235e..000000000 --- a/Engine/source/math/test/testMathPlane.cpp +++ /dev/null @@ -1,79 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "unit/test.h" -#include "math/mPlane.h" -#include "math/mRandom.h" - - -#ifndef TORQUE_SHIPPING - -using namespace UnitTesting; - -#define TEST( x ) test( ( x ), "FAIL: " #x ) -#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x ) - -CreateUnitTest( TestMathPlane, "Math/Plane" ) -{ - static F32 randF() - { - return gRandGen.randF( -1.f, 1.f ); - } - - void test_whichSide() - { - for( U32 i = 0; i < 100; ++ i ) - { - Point3F position( randF(), randF(), randF() ); - Point3F normal( randF(), randF(), randF() ); - - PlaneF p1( position, normal ); - - TEST( p1.whichSide( position + normal ) == PlaneF::Front ); - TEST( p1.whichSide( position + ( - normal ) ) == PlaneF::Back ); - TEST( p1.whichSide( position ) == PlaneF::On ); - } - } - - void test_distToPlane() - { - for( U32 i = 0; i < 100; ++ i ) - { - Point3F position( randF(), randF(), randF() ); - Point3F normal( randF(), randF(), randF() ); - - PlaneF p1( position, normal ); - - TEST( mIsEqual( p1.distToPlane( position + normal ), normal.len() ) ); - TEST( mIsEqual( p1.distToPlane( position + ( - normal ) ), - normal.len() ) ); - TEST( mIsZero( p1.distToPlane( position ) ) ); - } - } - - void run() - { - test_whichSide(); - test_distToPlane(); - } -}; - -#endif // !TORQUE_SHIPPING diff --git a/Engine/source/math/test/testPolyhedron.cpp b/Engine/source/math/test/testPolyhedron.cpp deleted file mode 100644 index 2bfe9b13f..000000000 --- a/Engine/source/math/test/testPolyhedron.cpp +++ /dev/null @@ -1,104 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "unit/test.h" -#include "math/mPolyhedron.h" - - -#ifndef TORQUE_SHIPPING - -using namespace UnitTesting; - -#define TEST( x ) test( ( x ), "FAIL: " #x ) -#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x ) - - -CreateUnitTest( TestMathPolyhedronBuildFromPlanes, "Math/Polyhedron/BuildFromPlanes" ) -{ - void test_unitCube() - { - Vector< PlaneF > planes; - - // Build planes for a unit cube centered at the origin. - // Note that the normals must be facing inwards. - - planes.push_back( PlaneF( Point3F( -0.5f, 0.f, 0.f ), Point3F( 1.f, 0.f, 0.f ) ) ); - planes.push_back( PlaneF( Point3F( 0.5f, 0.f, 0.f ), Point3F( -1.f, 0.f, 0.f ) ) ); - planes.push_back( PlaneF( Point3F( 0.f, -0.5f, 0.f ), Point3F( 0.f, 1.f, 0.f ) ) ); - planes.push_back( PlaneF( Point3F( 0.f, 0.5f, 0.f ), Point3F( 0.f, -1.f, 0.f ) ) ); - planes.push_back( PlaneF( Point3F( 0.f, 0.f, -0.5f ), Point3F( 0.f, 0.f, 1.f ) ) ); - planes.push_back( PlaneF( Point3F( 0.f, 0.f, 0.5f ), Point3F( 0.f, 0.f, -1.f ) ) ); - - // Turn it into a polyhedron. - - Polyhedron polyhedron; - polyhedron.buildFromPlanes( - PlaneSetF( planes.address(), planes.size() ) - ); - - // Check if we got a cube back. - - TEST( polyhedron.getNumPoints() == 8 ); - TEST( polyhedron.getNumPlanes() == 6 ); - TEST( polyhedron.getNumEdges() == 12 ); - } - - void test_extraPlane() - { - Vector< PlaneF > planes; - - // Build planes for a unit cube centered at the origin. - // Note that the normals must be facing inwards. - - planes.push_back( PlaneF( Point3F( -0.5f, 0.f, 0.f ), Point3F( 1.f, 0.f, 0.f ) ) ); - planes.push_back( PlaneF( Point3F( 0.5f, 0.f, 0.f ), Point3F( -1.f, 0.f, 0.f ) ) ); - planes.push_back( PlaneF( Point3F( 0.f, -0.5f, 0.f ), Point3F( 0.f, 1.f, 0.f ) ) ); - planes.push_back( PlaneF( Point3F( 0.f, 0.5f, 0.f ), Point3F( 0.f, -1.f, 0.f ) ) ); - planes.push_back( PlaneF( Point3F( 0.f, 0.f, -0.5f ), Point3F( 0.f, 0.f, 1.f ) ) ); - planes.push_back( PlaneF( Point3F( 0.f, 0.f, 0.5f ), Point3F( 0.f, 0.f, -1.f ) ) ); - - // Add extra plane that doesn't contribute a new edge. - - planes.push_back( PlaneF( Point3F( 0.5f, 0.5f, 0.5f ), Point3F( -1.f, -1.f, -1.f ) ) ); - - // Turn it into a polyhedron. - - Polyhedron polyhedron; - polyhedron.buildFromPlanes( - PlaneSetF( planes.address(), planes.size() ) - ); - - // Check if we got a cube back. - - TEST( polyhedron.getNumPoints() == 8 ); - TEST( polyhedron.getNumPlanes() == 6 ); - TEST( polyhedron.getNumEdges() == 12 ); - } - - void run() - { - test_unitCube(); - //test_extraPlane(); - } -}; - -#endif // !TORQUE_SHIPPING From 901ceb943d735915113c468bd67d050c25a41c7d Mon Sep 17 00:00:00 2001 From: Azaezel Date: Wed, 9 Jul 2014 15:46:15 -0500 Subject: [PATCH 103/317] deprecated functionality. T3D handles this in the reflector class. --- Engine/source/gfx/sim/cubemapData.cpp | 163 +++----------------------- Engine/source/gfx/sim/cubemapData.h | 13 +- 2 files changed, 16 insertions(+), 160 deletions(-) diff --git a/Engine/source/gfx/sim/cubemapData.cpp b/Engine/source/gfx/sim/cubemapData.cpp index aa06144af..2d0100c77 100644 --- a/Engine/source/gfx/sim/cubemapData.cpp +++ b/Engine/source/gfx/sim/cubemapData.cpp @@ -39,14 +39,6 @@ IMPLEMENT_CONOBJECT( CubemapData ); CubemapData::CubemapData() { mCubemap = NULL; - mDynamic = false; - mDynamicSize = 512; - mDynamicNearDist = 0.1f; - mDynamicFarDist = 100.0f; - mDynamicObjectTypeMask = 0; -#ifdef INIT_HACK - mInit = false; -#endif } CubemapData::~CubemapData() @@ -84,22 +76,6 @@ void CubemapData::initPersistFields() " - cubeFace[3] is +Z\n" " - cubeFace[4] is -Y\n" " - cubeFace[5] is +Y\n" ); - - addField("dynamic", TypeBool, Offset(mDynamic, CubemapData), - "Set to true if this is a dynamic cubemap. The default is false." ); - - addField("dynamicSize", TypeS32, Offset(mDynamicSize, CubemapData), - "The size of each dynamic cubemap face in pixels." ); - - addField("dynamicNearDist", TypeF32, Offset(mDynamicNearDist, CubemapData), - "The near clip distance used when rendering to the dynamic cubemap." ); - - addField("dynamicFarDist", TypeF32, Offset(mDynamicFarDist, CubemapData), - "The far clip distance used when rendering to the dynamic cubemap." ); - - addField("dynamicObjectTypeMask", TypeS32, Offset(mDynamicObjectTypeMask, CubemapData), - "The typemask used to filter the objects rendered to the dynamic cubemap." ); - Parent::initPersistFields(); } @@ -118,136 +94,27 @@ void CubemapData::createMap() { if( !mCubemap ) { - if( mDynamic ) - { - mCubemap = GFX->createCubemap(); - mCubemap->initDynamic( mDynamicSize ); - mDepthBuff = GFXTexHandle( mDynamicSize, mDynamicSize, GFXFormatD24S8, - &GFXDefaultZTargetProfile, avar("%s() - mDepthBuff (line %d)", __FUNCTION__, __LINE__)); - mRenderTarget = GFX->allocRenderToTextureTarget(); - } - else - { - bool initSuccess = true; - - for( U32 i=0; i<6; i++ ) - { - if( !mCubeFaceFile[i].isEmpty() ) - { + bool initSuccess = true; + + for( U32 i=0; i<6; i++ ) + { + if( !mCubeFaceFile[i].isEmpty() ) + { if(!mCubeFace[i].set(mCubeFaceFile[i], &GFXDefaultStaticDiffuseProfile, avar("%s() - mCubeFace[%d] (line %d)", __FUNCTION__, i, __LINE__) )) { - Con::errorf("CubemapData::createMap - Failed to load texture '%s'", mCubeFaceFile[i].c_str()); - initSuccess = false; + Con::errorf("CubemapData::createMap - Failed to load texture '%s'", mCubeFaceFile[i].c_str()); + initSuccess = false; } - } - } - - if( initSuccess ) - { - mCubemap = GFX->createCubemap(); - mCubemap->initStatic( mCubeFace ); - } - } + } + } + if( initSuccess ) + { + mCubemap = GFX->createCubemap(); + mCubemap->initStatic( mCubeFace ); + } } } -void CubemapData::updateDynamic(SceneManager* sm, const Point3F& pos) -{ - AssertFatal(mDynamic, "This is not a dynamic cubemap!"); - - GFXDEBUGEVENT_SCOPE( CubemapData_updateDynamic, ColorI::WHITE ); - -#ifdef INIT_HACK - if( mInit ) return; - mInit = true; -#endif - - GFX->pushActiveRenderTarget(); - mRenderTarget->attachTexture(GFXTextureTarget::DepthStencil, mDepthBuff ); - - // store current matrices - GFXTransformSaver saver; - F32 oldVisibleDist = sm->getVisibleDistance(); - - // set projection to 90 degrees vertical and horizontal - { - F32 left, right, top, bottom; - MathUtils::makeFrustum( &left, &right, &top, &bottom, M_HALFPI_F, 1.0f, mDynamicNearDist ); - GFX->setFrustum( left, right, bottom, top, mDynamicNearDist, mDynamicFarDist ); - } - sm->setVisibleDistance(mDynamicFarDist); - - // We don't use a special clipping projection, but still need to initialize - // this for objects like SkyBox which will use it during a reflect pass. - gClientSceneGraph->setNonClipProjection( (MatrixF&) GFX->getProjectionMatrix() ); - - // Loop through the six faces of the cube map. - for(U32 i=0; i<6; i++) - { - // Standard view that will be overridden below. - VectorF vLookatPt(0.0f, 0.0f, 0.0f), vUpVec(0.0f, 0.0f, 0.0f), vRight(0.0f, 0.0f, 0.0f); - - switch( i ) - { - case 0 : // D3DCUBEMAP_FACE_POSITIVE_X: - vLookatPt = VectorF( 1.0f, 0.0f, 0.0f ); - vUpVec = VectorF( 0.0f, 1.0f, 0.0f ); - break; - case 1 : // D3DCUBEMAP_FACE_NEGATIVE_X: - vLookatPt = VectorF( -1.0f, 0.0f, 0.0f ); - vUpVec = VectorF( 0.0f, 1.0f, 0.0f ); - break; - case 2 : // D3DCUBEMAP_FACE_POSITIVE_Y: - vLookatPt = VectorF( 0.0f, 1.0f, 0.0f ); - vUpVec = VectorF( 0.0f, 0.0f,-1.0f ); - break; - case 3 : // D3DCUBEMAP_FACE_NEGATIVE_Y: - vLookatPt = VectorF( 0.0f, -1.0f, 0.0f ); - vUpVec = VectorF( 0.0f, 0.0f, 1.0f ); - break; - case 4 : // D3DCUBEMAP_FACE_POSITIVE_Z: - vLookatPt = VectorF( 0.0f, 0.0f, 1.0f ); - vUpVec = VectorF( 0.0f, 1.0f, 0.0f ); - break; - case 5: // D3DCUBEMAP_FACE_NEGATIVE_Z: - vLookatPt = VectorF( 0.0f, 0.0f, -1.0f ); - vUpVec = VectorF( 0.0f, 1.0f, 0.0f ); - break; - } - - // create camera matrix - VectorF cross = mCross( vUpVec, vLookatPt ); - cross.normalizeSafe(); - - MatrixF matView(true); - matView.setColumn( 0, cross ); - matView.setColumn( 1, vLookatPt ); - matView.setColumn( 2, vUpVec ); - matView.setPosition( pos ); - matView.inverse(); - - GFX->pushWorldMatrix(); - GFX->setWorldMatrix(matView); - - mRenderTarget->attachTexture( GFXTextureTarget::Color0, mCubemap, i ); - GFX->setActiveRenderTarget( mRenderTarget ); - GFX->clear( GFXClearStencil | GFXClearTarget | GFXClearZBuffer, ColorI( 64, 64, 64 ), 1.f, 0 ); - - // render scene - sm->renderScene( SPT_Reflect, mDynamicObjectTypeMask ); - - // Resolve render target for each face - mRenderTarget->resolve(); - GFX->popWorldMatrix(); - } - - // restore render surface and depth buffer - GFX->popActiveRenderTarget(); - - mRenderTarget->attachTexture(GFXTextureTarget::Color0, NULL); - sm->setVisibleDistance(oldVisibleDist); -} - void CubemapData::updateFaces() { bool initSuccess = true; diff --git a/Engine/source/gfx/sim/cubemapData.h b/Engine/source/gfx/sim/cubemapData.h index 76604a886..cd0560ac0 100644 --- a/Engine/source/gfx/sim/cubemapData.h +++ b/Engine/source/gfx/sim/cubemapData.h @@ -60,16 +60,8 @@ public: // Force creation of cubemap void createMap(); - // Update a dynamic cubemap @ pos - void updateDynamic(SceneManager* sm, const Point3F& pos); + // Update a static cubemap @ pos void updateFaces(); - - // Dynamic cube map support - bool mDynamic; - U32 mDynamicSize; - F32 mDynamicNearDist; - F32 mDynamicFarDist; - U32 mDynamicObjectTypeMask; protected: @@ -78,9 +70,6 @@ protected: GFXTexHandle mDepthBuff; GFXTextureTargetRef mRenderTarget; -#ifdef INIT_HACK - bool mInit; -#endif }; #endif // CUBEMAPDATA From c3813ad9136a56b1e37d2e8da5afea39283670b4 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Wed, 9 Jul 2014 16:25:42 -0500 Subject: [PATCH 104/317] implicit truncation warning cleanup. --- Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp b/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp index 01a2af3f0..586978eec 100644 --- a/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp +++ b/Engine/source/shaderGen/HLSL/shaderFeatureHLSL.cpp @@ -1679,7 +1679,7 @@ void ReflectCubeFeatHLSL::processVert( Vector &componentList, cubeNormal->setType( "float3" ); LangElement *cubeNormDecl = new DecOp( cubeNormal ); - meta->addStatement( new GenOp( " @ = normalize( mul(@, normalize(@)).xyz );\r\n", + meta->addStatement( new GenOp( " @ = normalize( mul(@, float4(normalize(@),0.0)).xyz );\r\n", cubeNormDecl, cubeTrans, inNormal ) ); // grab the eye position From d6cc399813352bc68e58f28a058fbb751a565050 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Wed, 9 Jul 2014 18:06:31 -0500 Subject: [PATCH 105/317] col is a float4, LUMINANCE_VECTOR a float3. results seemed cleaner this end doing a dot of the rgb than padding the lum --- .../game/shaders/common/postFx/lightRay/lightRayOccludeP.hlsl | 2 +- .../game/shaders/common/postFx/lightRay/lightRayOccludeP.hlsl | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Templates/Empty/game/shaders/common/postFx/lightRay/lightRayOccludeP.hlsl b/Templates/Empty/game/shaders/common/postFx/lightRay/lightRayOccludeP.hlsl index 8769905f6..e8870b3c4 100644 --- a/Templates/Empty/game/shaders/common/postFx/lightRay/lightRayOccludeP.hlsl +++ b/Templates/Empty/game/shaders/common/postFx/lightRay/lightRayOccludeP.hlsl @@ -45,7 +45,7 @@ float4 main( PFXVertToPix IN ) : COLOR0 col = tex2D( backBuffer, IN.uv0 ); //col = 1 - exp(-120000 * col); - col += dot( col, LUMINANCE_VECTOR ) + 0.0001f; + col += dot( col.rgb, LUMINANCE_VECTOR ) + 0.0001f; col *= brightScalar; } diff --git a/Templates/Full/game/shaders/common/postFx/lightRay/lightRayOccludeP.hlsl b/Templates/Full/game/shaders/common/postFx/lightRay/lightRayOccludeP.hlsl index 8769905f6..e8870b3c4 100644 --- a/Templates/Full/game/shaders/common/postFx/lightRay/lightRayOccludeP.hlsl +++ b/Templates/Full/game/shaders/common/postFx/lightRay/lightRayOccludeP.hlsl @@ -45,7 +45,7 @@ float4 main( PFXVertToPix IN ) : COLOR0 col = tex2D( backBuffer, IN.uv0 ); //col = 1 - exp(-120000 * col); - col += dot( col, LUMINANCE_VECTOR ) + 0.0001f; + col += dot( col.rgb, LUMINANCE_VECTOR ) + 0.0001f; col *= brightScalar; } From 4b3ede66749185ea167c5494930d83902cf1d0a3 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Wed, 9 Jul 2014 18:07:23 -0500 Subject: [PATCH 106/317] explicit downcasting for some halfs --- .../game/shaders/common/postFx/lightRay/lightRayP.hlsl | 6 +++--- .../Full/game/shaders/common/postFx/lightRay/lightRayP.hlsl | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/Templates/Empty/game/shaders/common/postFx/lightRay/lightRayP.hlsl b/Templates/Empty/game/shaders/common/postFx/lightRay/lightRayP.hlsl index 7dd334607..ff44abfae 100644 --- a/Templates/Empty/game/shaders/common/postFx/lightRay/lightRayP.hlsl +++ b/Templates/Empty/game/shaders/common/postFx/lightRay/lightRayP.hlsl @@ -59,7 +59,7 @@ float4 main( PFXVertToPix IN ) : COLOR0 half2 deltaTexCoord = (half2)( texCoord.xy - screenSunPos ); // Divide by number of samples and scale by control factor. - deltaTexCoord *= 1.0 / (half)samples * density; + deltaTexCoord *= (half)(1.0 / samples * density); // Evaluate summation from Equation 3 NUM_SAMPLES iterations. for ( int i = 0; i < samples; i++ ) @@ -71,13 +71,13 @@ float4 main( PFXVertToPix IN ) : COLOR0 half3 sample = (half3)tex2Dlod( frameSampler, texCoord ); // Apply sample attenuation scale/decay factors. - sample *= illuminationDecay * weight; + sample *= half(illuminationDecay * weight); // Accumulate combined color. color += sample; // Update exponential decay factor. - illuminationDecay *= decay; + illuminationDecay *= half(decay); } //return saturate( amount ) * color * Exposure; diff --git a/Templates/Full/game/shaders/common/postFx/lightRay/lightRayP.hlsl b/Templates/Full/game/shaders/common/postFx/lightRay/lightRayP.hlsl index 7dd334607..ff44abfae 100644 --- a/Templates/Full/game/shaders/common/postFx/lightRay/lightRayP.hlsl +++ b/Templates/Full/game/shaders/common/postFx/lightRay/lightRayP.hlsl @@ -59,7 +59,7 @@ float4 main( PFXVertToPix IN ) : COLOR0 half2 deltaTexCoord = (half2)( texCoord.xy - screenSunPos ); // Divide by number of samples and scale by control factor. - deltaTexCoord *= 1.0 / (half)samples * density; + deltaTexCoord *= (half)(1.0 / samples * density); // Evaluate summation from Equation 3 NUM_SAMPLES iterations. for ( int i = 0; i < samples; i++ ) @@ -71,13 +71,13 @@ float4 main( PFXVertToPix IN ) : COLOR0 half3 sample = (half3)tex2Dlod( frameSampler, texCoord ); // Apply sample attenuation scale/decay factors. - sample *= illuminationDecay * weight; + sample *= half(illuminationDecay * weight); // Accumulate combined color. color += sample; // Update exponential decay factor. - illuminationDecay *= decay; + illuminationDecay *= half(decay); } //return saturate( amount ) * color * Exposure; From c0851e9fc2f3fa3cd4146d4e8e2a2e9814fc4f8a Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Thu, 10 Jul 2014 10:35:16 +0200 Subject: [PATCH 107/317] Fixed plane tests. --- Engine/source/math/test/mPlaneTest.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/Engine/source/math/test/mPlaneTest.cpp b/Engine/source/math/test/mPlaneTest.cpp index 0013c63c6..777296184 100644 --- a/Engine/source/math/test/mPlaneTest.cpp +++ b/Engine/source/math/test/mPlaneTest.cpp @@ -28,10 +28,10 @@ // test case. This allows a large number of tests without introducing non- // deterministic test behavior. -static const Point3F positions[] = {Point3F(0, 0, 0), Point3F(1, -2, 3), Point3F(1e5, 2e5, -3e6)}; +static const Point3F positions[] = {Point3F(0, 0, 0), Point3F(1, -2, 3), Point3F(1e-2, -2e-2, 1)}; static const U32 numPositions = sizeof(positions) / sizeof(Point3F); -static const Point3F normals[] = {Point3F(1, 0, 0), Point3F(-4, -2, 6), Point3F(1e8, 2e7, 5e-2)}; +static const Point3F normals[] = {Point3F(1, 0, 0), Point3F(-4, -2, 6)}; static const U32 numNormals = sizeof(normals) / sizeof(Point3F); /// Tests that points in the direction of the normal are in 'Front' of the @@ -40,7 +40,7 @@ static const U32 numNormals = sizeof(normals) / sizeof(Point3F); TEST(Plane, WhichSide) { for(U32 i = 0; i < numPositions; i++) { - for(U32 j = 0; i < numNormals; j++) { + for(U32 j = 0; j < numNormals; j++) { Point3F position = positions[i]; Point3F normal = normals[j]; @@ -58,7 +58,7 @@ TEST(Plane, WhichSide) TEST(Plane, DistToPlane) { for(U32 i = 0; i < numPositions; i++) { - for(U32 j = 0; i < numNormals; j++) { + for(U32 j = 0; j < numNormals; j++) { Point3F position = positions[i]; Point3F normal = normals[j]; From 9a05899d8e5d989bcffec5930102ad07c025fd7d Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Thu, 10 Jul 2014 11:30:56 +0200 Subject: [PATCH 108/317] Allow drawing 2D squares with 0 rotation angle. --- Engine/source/gfx/gfxDrawUtil.cpp | 7 ++++++- Engine/source/gfx/gfxDrawUtil.h | 2 +- 2 files changed, 7 insertions(+), 2 deletions(-) diff --git a/Engine/source/gfx/gfxDrawUtil.cpp b/Engine/source/gfx/gfxDrawUtil.cpp index c03aecb5c..d21cdb88d 100644 --- a/Engine/source/gfx/gfxDrawUtil.cpp +++ b/Engine/source/gfx/gfxDrawUtil.cpp @@ -558,7 +558,12 @@ void GFXDrawUtil::draw2DSquare( const Point2F &screenPoint, F32 width, F32 spinA verts[0].color = verts[1].color = verts[2].color = verts[3].color = mBitmapModulation; - if(spinAngle != 0.f) + if (spinAngle == 0.0f) + { + for( S32 i = 0; i < 4; i++ ) + verts[i].point += offset; + } + else { MatrixF rotMatrix( EulerF( 0.0, 0.0, spinAngle ) ); diff --git a/Engine/source/gfx/gfxDrawUtil.h b/Engine/source/gfx/gfxDrawUtil.h index 906342428..7decad785 100644 --- a/Engine/source/gfx/gfxDrawUtil.h +++ b/Engine/source/gfx/gfxDrawUtil.h @@ -58,7 +58,7 @@ public: void drawRectFill( const Point2I &upperLeft, const Point2I &lowerRight, const ColorI &color ); void drawRectFill( const RectI &rect, const ColorI &color ); - void draw2DSquare( const Point2F &screenPoint, F32 width, F32 spinAngle ); + void draw2DSquare( const Point2F &screenPoint, F32 width, F32 spinAngle = 0.0f ); //----------------------------------------------------------------------------- // Draw Lines From ec78e4d50262028116d170506c304619581cb370 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Thu, 10 Jul 2014 11:56:33 +0200 Subject: [PATCH 109/317] Append ' DLL' to DLL filename to improve VS link times. --- Engine/source/main/main.cpp | 30 +++++++++++++++---- .../templates/vc2010_dll_proj.tpl | 12 ++++---- .../templates/vc2k8_dll_proj.tpl | 10 +++---- 3 files changed, 36 insertions(+), 16 deletions(-) diff --git a/Engine/source/main/main.cpp b/Engine/source/main/main.cpp index fbc97226a..a1d2bd1f1 100644 --- a/Engine/source/main/main.cpp +++ b/Engine/source/main/main.cpp @@ -32,29 +32,49 @@ extern "C" int (*torque_winmain)( HINSTANCE hInstance, HINSTANCE h, LPSTR lpszCmdLine, int nShow) = NULL; }; -bool getDllName(std::wstring& dllName) +bool getDllName(std::wstring& dllName, const std::wstring suffix) { wchar_t filenameBuf[MAX_PATH]; DWORD length = GetModuleFileNameW( NULL, filenameBuf, MAX_PATH ); if(length == 0) return false; dllName = std::wstring(filenameBuf); size_t dotPos = dllName.find_last_of(L"."); - if(dotPos == std::wstring::npos) return false; + if(dotPos == std::wstring::npos) + { + dllName.clear(); + return false; + } dllName.erase(dotPos); - dllName += L".dll"; + dllName += suffix + L".dll"; return true; } int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdLine, int nCommandShow) { + // Try to find the game DLL, which may have one of several file names. + HMODULE hGame = NULL; std::wstring dllName = std::wstring(); - if(!getDllName(dllName)) + // The file name is the same as this executable's name, plus a suffix. + const std::wstring dllSuffices[] = {L"", L" DLL"}; + const unsigned int numSuffices = sizeof(dllSuffices) / sizeof(std::wstring); + + for (unsigned int i = 0; i < numSuffices; i++) + { + // Attempt to glue the suffix onto the current filename. + if(!getDllName(dllName, dllSuffices[i])) + continue; + // Load the DLL at that address. + hGame = LoadLibraryW(dllName.c_str()); + if (hGame) + break; + } + + if(!dllName.length()) { MessageBoxW(NULL, L"Unable to find game dll", L"Error", MB_OK|MB_ICONWARNING); return -1; } - HMODULE hGame = LoadLibraryW(dllName.c_str()); if (!hGame) { wchar_t error[4096]; diff --git a/Tools/projectGenerator/templates/vc2010_dll_proj.tpl b/Tools/projectGenerator/templates/vc2010_dll_proj.tpl index cd520d051..a134420f1 100644 --- a/Tools/projectGenerator/templates/vc2010_dll_proj.tpl +++ b/Tools/projectGenerator/templates/vc2010_dll_proj.tpl @@ -50,15 +50,15 @@ {$projectOffset}../../{$gameFolder}/ {$projectOffset}../Link/VC2010.$(Configuration).$(PlatformName)/$(ProjectName)/ true - {$projOutName}_DEBUG + {$projOutName}_DEBUG DLL {$projectOffset}../../{$gameFolder}/ {$projectOffset}../Link/VC2010.$(Configuration).$(PlatformName)/$(ProjectName)/ false - {$projOutName}_OPTIMIZEDDEBUG + {$projOutName}_OPTIMIZEDDEBUG DLL {$projectOffset}../../{$gameFolder}/ {$projectOffset}../Link/VC2010.$(Configuration).$(PlatformName)/$(ProjectName)/ false - {$projOutName} + {$projOutName} DLL @@ -96,7 +96,7 @@ {foreach item=def from=$projLibsDebug}{$def};{/foreach}%(AdditionalDependencies) - $(OutDir){$projOutName}_DEBUG.dll + $(OutDir)$(TargetName).dll true {foreach item=def from=$projLibDirs}{$def};{/foreach}{$projectOffset}../Link/VC2010.$(Configuration).$(PlatformName);$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories) LIBC;LIBCD;{foreach item=def from=$projLibsIgnore}{$def};{/foreach}%(IgnoreSpecificDefaultLibraries) @@ -148,7 +148,7 @@ {foreach item=def from=$projLibsDebug}{$def};{/foreach}%(AdditionalDependencies) - $(OutDir){$projOutName}_OPTIMIZEDDEBUG.dll + $(OutDir)$(TargetName).dll true {foreach item=def from=$projLibDirs}{$def};{/foreach}{$projectOffset}../Link/VC2010.$(Configuration).$(PlatformName);%(AdditionalLibraryDirectories) LIBC;LIBCD;{foreach item=def from=$projLibsIgnore}{$def};{/foreach}%(IgnoreSpecificDefaultLibraries) @@ -200,7 +200,7 @@ {foreach item=def from=$projLibs}{$def};{/foreach}%(AdditionalDependencies) - $(OutDir){$projOutName}.dll + $(OutDir)$(TargetName).dll true {foreach item=def from=$projLibDirs}{$def};{/foreach}{$projectOffset}../Link/VC2010.$(Configuration).$(PlatformName);$(DXSDK_DIR)/Lib/x86;%(AdditionalLibraryDirectories) LIBC;LIBCD;{foreach item=def from=$projLibsIgnore}{$def};{/foreach}%(IgnoreSpecificDefaultLibraries) diff --git a/Tools/projectGenerator/templates/vc2k8_dll_proj.tpl b/Tools/projectGenerator/templates/vc2k8_dll_proj.tpl index f9d02a7eb..e255fc0f4 100644 --- a/Tools/projectGenerator/templates/vc2k8_dll_proj.tpl +++ b/Tools/projectGenerator/templates/vc2k8_dll_proj.tpl @@ -81,9 +81,9 @@ AdditionalDependencies="{foreach item=def from=$projLibsDebug}{$def} {/foreach}" {if $uniformOutputFile eq 1} - OutputFile="{$projectOffset}../../{$gameFolder}/{$projOutName}.dll" + OutputFile="{$projectOffset}../../{$gameFolder}/{$projOutName} DLL.dll" {else} - OutputFile="{$projectOffset}../../{$gameFolder}/{$projOutName}_DEBUG.dll" + OutputFile="{$projectOffset}../../{$gameFolder}/{$projOutName}_DEBUG DLL.dll" {/if} LinkIncremental="2" @@ -191,9 +191,9 @@ AdditionalDependencies="{foreach item=def from=$projLibsDebug}{$def} {/foreach}" {if $uniformOutputFile eq 1} - OutputFile="{$projectOffset}../../{$gameFolder}/{$projOutName}.dll" + OutputFile="{$projectOffset}../../{$gameFolder}/{$projOutName} DLL.dll" {else} - OutputFile="{$projectOffset}../../{$gameFolder}/{$projOutName}_OPTIMIZEDDEBUG.dll" + OutputFile="{$projectOffset}../../{$gameFolder}/{$projOutName}_OPTIMIZEDDEBUG DLL.dll" {/if} LinkIncremental="1" @@ -299,7 +299,7 @@ Date: Fri, 11 Jul 2014 06:58:19 -0500 Subject: [PATCH 110/317] revised checkInLos and CheckFoV. boith now take all parameters as optional as suggested, with a target value of NULL resulting in checking the present one. cleaned up internal usage of checkInLos, tightened the typemask used, and provided further documentation. --- Engine/source/T3D/aiPlayer.cpp | 61 +++++++++++++++++++++++----------- Engine/source/T3D/aiPlayer.h | 4 +-- 2 files changed, 44 insertions(+), 21 deletions(-) diff --git a/Engine/source/T3D/aiPlayer.cpp b/Engine/source/T3D/aiPlayer.cpp index d3aed1fd4..fd0c179a3 100644 --- a/Engine/source/T3D/aiPlayer.cpp +++ b/Engine/source/T3D/aiPlayer.cpp @@ -29,8 +29,7 @@ #include "console/engineAPI.h" static U32 sAIPlayerLoSMask = TerrainObjectType | WaterObjectType | - ShapeBaseObjectType | StaticShapeObjectType | - PlayerObjectType | ItemObjectType; + ShapeBaseObjectType | StaticShapeObjectType; IMPLEMENT_CO_NETOBJECT_V1(AIPlayer); @@ -423,13 +422,15 @@ bool AIPlayer::getAIMove(Move *movePtr) // which is not very accurate. if (mAimObject) { - mTargetInLOS = checkInLos(mAimObject.getPointer(), false); - if (mTargetInLOS) + if (checkInLos(mAimObject.getPointer())) { - throwCallback("onTargetEnterLOS"); - mTargetInLOS = true; + if (!mTargetInLOS) + { + throwCallback("onTargetEnterLOS"); + mTargetInLOS = true; + } } - else + else if (mTargetInLOS) { throwCallback("onTargetExitLOS"); mTargetInLOS = false; @@ -605,11 +606,16 @@ DefineEngineMethod( AIPlayer, getAimObject, S32, (),, GameBase* obj = object->getAimObject(); return obj? obj->getId(): -1; } - -bool AIPlayer::checkInLos(GameBase* target, bool _checkEnabled = false) + +bool AIPlayer::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabled) { if (!isServerObject()) return false; - if (!(bool(target))) return false; + if (!(bool(target))) + { + target = mAimObject.getPointer(); + if (!(bool(target))) + return false; + } if (_checkEnabled) { ShapeBase *shapeBaseCheck = dynamic_cast(target); @@ -626,9 +632,18 @@ bool AIPlayer::checkInLos(GameBase* target, bool _checkEnabled = false) { target->getMountedObject(i)->disableCollision(); } - Point3F muzzlePoint; - getMuzzlePointAI(0, &muzzlePoint); - bool hit = gServerContainer.castRay(muzzlePoint, target->getBoxCenter(), sAIPlayerLoSMask, &ri); + + Point3F checkPoint ; + if (_useMuzzle) + getMuzzlePointAI(0, &checkPoint ); + else + { + MatrixF eyeMat; + getEyeTransform(&eyeMat); + eyeMat.getColumn(3, &checkPoint ); + } + + bool hit = gServerContainer.castRay(checkPoint , target->getBoxCenter(), sAIPlayerLoSMask, &ri); enableCollision(); for (S32 i = 0; i < mountCount; i++) @@ -644,6 +659,7 @@ bool AIPlayer::checkInLos(GameBase* target, bool _checkEnabled = false) return hit; } + bool AIPlayer::checkLosClear(Point3F _pos) { if (!isServerObject()) return false; @@ -660,13 +676,17 @@ bool AIPlayer::checkLosClear(Point3F _pos) return emptySpace; } -DefineEngineMethod(AIPlayer, checkInLos, bool, (ShapeBase* obj, bool checkEnabled), (0, false), - "@brief Check for an object in line of sight.\n") +DefineEngineMethod(AIPlayer, checkInLos, bool, (ShapeBase* obj, bool useMuzzle, bool checkEnabled),(NULL, false, false), + "@brief Check for an object in line of sight.\n" + "@obj Object to check. if blank it will check the current target.\n" + "@useMuzzle Use muzzle position (otherwise use eye position).\n" + "@checkEnabled check if the object is not disabled.\n") { - return object->checkInLos(obj, checkEnabled); + return object->checkInLos(obj, useMuzzle, checkEnabled); } -bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled = false) + +bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled) { if (!isServerObject()) return false; if (!(bool(target))) return false; @@ -700,8 +720,11 @@ bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled = fal return (dot > camFov); } -DefineEngineMethod(AIPlayer, checkInFoV, bool, (ShapeBase* obj, F32 fov, bool checkEnabled), (0, 45, false), - "@brief Check for an object within a specified veiw cone.\n") +DefineEngineMethod(AIPlayer, checkInFoV, bool, (ShapeBase* obj, F32 fov, bool checkEnabled), (NULL, 45.0f, false), + "@brief Check for an object within a specified veiw cone.\n" + "@obj Object to check. if blank it will check the current target.\n" + "@fov view angle (in degrees)\n" + "@checkEnabled check if the object is not disabled.\n") { return object->checkInFoV(obj, fov, checkEnabled); } \ No newline at end of file diff --git a/Engine/source/T3D/aiPlayer.h b/Engine/source/T3D/aiPlayer.h index 283db14db..851482021 100644 --- a/Engine/source/T3D/aiPlayer.h +++ b/Engine/source/T3D/aiPlayer.h @@ -80,9 +80,9 @@ public: void setAimLocation( const Point3F &location ); Point3F getAimLocation() const { return mAimLocation; } void clearAim(); - bool checkInLos(GameBase* target, bool _checkEnabled); + bool checkInLos(GameBase* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false); bool checkLosClear(Point3F _pos); - bool checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled); + bool checkInFoV(GameBase* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false); // Movement sets/gets void setMoveSpeed( const F32 speed ); From 9bed39b7d08b52d6ec7f3ed642d0afa2d113175c Mon Sep 17 00:00:00 2001 From: Azaezel Date: Fri, 11 Jul 2014 07:47:31 -0500 Subject: [PATCH 111/317] logical inversion to stock functionality as requested. Now assumes if it has a target and does not hit anything Static by the time the ray terminates, then it must be the right target. --- Engine/source/T3D/aiPlayer.cpp | 11 ++--------- 1 file changed, 2 insertions(+), 9 deletions(-) diff --git a/Engine/source/T3D/aiPlayer.cpp b/Engine/source/T3D/aiPlayer.cpp index fd0c179a3..0c9b930fe 100644 --- a/Engine/source/T3D/aiPlayer.cpp +++ b/Engine/source/T3D/aiPlayer.cpp @@ -28,8 +28,7 @@ #include "T3D/gameBase/moveManager.h" #include "console/engineAPI.h" -static U32 sAIPlayerLoSMask = TerrainObjectType | WaterObjectType | - ShapeBaseObjectType | StaticShapeObjectType; +static U32 sAIPlayerLoSMask = TerrainObjectType | StaticShapeObjectType | StaticObjectType; IMPLEMENT_CO_NETOBJECT_V1(AIPlayer); @@ -643,19 +642,13 @@ bool AIPlayer::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabled) eyeMat.getColumn(3, &checkPoint ); } - bool hit = gServerContainer.castRay(checkPoint , target->getBoxCenter(), sAIPlayerLoSMask, &ri); + bool hit = !gServerContainer.castRay(checkPoint, target->getBoxCenter(), sAIPlayerLoSMask, &ri); enableCollision(); for (S32 i = 0; i < mountCount; i++) { target->getMountedObject(i)->enableCollision(); } - - if (hit) - { - if (target != dynamic_cast(ri.object)) hit = false; - } - return hit; } From d1070210c21ecb6ee7b0928097f83eab1906b08b Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sat, 12 Jul 2014 00:00:21 +0200 Subject: [PATCH 112/317] Fixed tool support. --- Tools/CMake/modules/module_testing.cmake | 9 ++++++ Tools/projectGenerator/libs/libgtest.conf | 34 ---------------------- Tools/projectGenerator/modules/testing.inc | 1 - 3 files changed, 9 insertions(+), 35 deletions(-) create mode 100644 Tools/CMake/modules/module_testing.cmake delete mode 100644 Tools/projectGenerator/libs/libgtest.conf diff --git a/Tools/CMake/modules/module_testing.cmake b/Tools/CMake/modules/module_testing.cmake new file mode 100644 index 000000000..5b2fedbd0 --- /dev/null +++ b/Tools/CMake/modules/module_testing.cmake @@ -0,0 +1,9 @@ +# Project defines +addDef( "TORQUE_TESTS_ENABLED" ) +addDef( "_VARIADIC_MAX" 10 ) + +# Add source files +addPathRec( "${srcDir}/testing" ) + +# Add include paths +addInclude( "${libDir}/gtest/fused-src/" ) diff --git a/Tools/projectGenerator/libs/libgtest.conf b/Tools/projectGenerator/libs/libgtest.conf deleted file mode 100644 index 3d66b9f4a..000000000 --- a/Tools/projectGenerator/libs/libgtest.conf +++ /dev/null @@ -1,34 +0,0 @@ - diff --git a/Tools/projectGenerator/modules/testing.inc b/Tools/projectGenerator/modules/testing.inc index cf9f21c26..384ab14e8 100644 --- a/Tools/projectGenerator/modules/testing.inc +++ b/Tools/projectGenerator/modules/testing.inc @@ -27,7 +27,6 @@ beginModule( 'testing' ); addProjectDefine( '_VARIADIC_MAX', 10 ); addSrcDir(getEngineSrcDir() . 'testing', true); - includeLib( 'libgtest' ); addLibIncludePath( 'gtest/fused-src/' ); endModule(); From a3bdaf128fffef2c692ada611c4ac3ab9d178aa6 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sat, 12 Jul 2014 01:23:00 +0200 Subject: [PATCH 113/317] Fix memory corruption on Precipitation::destroySplash. --- Engine/source/T3D/fx/precipitation.cpp | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/Engine/source/T3D/fx/precipitation.cpp b/Engine/source/T3D/fx/precipitation.cpp index 9205238e0..6f7b7ac0a 100644 --- a/Engine/source/T3D/fx/precipitation.cpp +++ b/Engine/source/T3D/fx/precipitation.cpp @@ -1168,9 +1168,7 @@ void Precipitation::destroySplash(Raindrop *drop) PROFILE_START(PrecipDestroySplash); if (drop == mSplashHead) { - mSplashHead = NULL; - PROFILE_END(); - return; + mSplashHead = mSplashHead->nextSplashDrop; } if (drop->nextSplashDrop) From a030f47178cdf3e5b0788de5f709578ae2836917 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sat, 12 Jul 2014 13:44:48 +0200 Subject: [PATCH 114/317] Fix CMake testing module. --- Tools/CMake/modules/module_testing.cmake | 20 +++++++++++++------- Tools/CMake/torque3d.cmake | 2 ++ 2 files changed, 15 insertions(+), 7 deletions(-) diff --git a/Tools/CMake/modules/module_testing.cmake b/Tools/CMake/modules/module_testing.cmake index 5b2fedbd0..35c2f7a3a 100644 --- a/Tools/CMake/modules/module_testing.cmake +++ b/Tools/CMake/modules/module_testing.cmake @@ -1,9 +1,15 @@ -# Project defines -addDef( "TORQUE_TESTS_ENABLED" ) -addDef( "_VARIADIC_MAX" 10 ) +option(TORQUE_TESTS_ENABLED "TORQUE_TESTS_ENABLED" OFF) -# Add source files -addPathRec( "${srcDir}/testing" ) +if(TORQUE_TESTS_ENABLED) -# Add include paths -addInclude( "${libDir}/gtest/fused-src/" ) + # Project defines + addDef( "TORQUE_TESTS_ENABLED" ) + addDef( "_VARIADIC_MAX" 10 ) + + # Add source files + addPathRec( "${srcDir}/testing" ) + + # Add include paths + addInclude( "${libDir}/gtest/fused-src/" ) + +endif() \ No newline at end of file diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index c17009c1d..61d9fea54 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -325,6 +325,8 @@ if(TORQUE_DEDICATED) addDef(TORQUE_DEDICATED) endif() +include( "modules/module_testing.cmake" ) + ############################################################################### # platform specific things From 242bce3b622a92fcb9111d810d6bd50facd0f233 Mon Sep 17 00:00:00 2001 From: rextimmy Date: Sat, 12 Jul 2014 21:50:09 +1000 Subject: [PATCH 115/317] Removing Featherstone and MLCP solvers. --- .../Featherstone/btMultiBody.cpp | 1009 -------- .../BulletDynamics/Featherstone/btMultiBody.h | 466 ---- .../Featherstone/btMultiBodyConstraint.cpp | 527 ----- .../Featherstone/btMultiBodyConstraint.h | 166 -- .../btMultiBodyConstraintSolver.cpp | 795 ------- .../btMultiBodyConstraintSolver.h | 85 - .../Featherstone/btMultiBodyDynamicsWorld.cpp | 578 ----- .../Featherstone/btMultiBodyDynamicsWorld.h | 56 - .../btMultiBodyJointLimitConstraint.cpp | 133 -- .../btMultiBodyJointLimitConstraint.h | 44 - .../Featherstone/btMultiBodyJointMotor.cpp | 89 - .../Featherstone/btMultiBodyJointMotor.h | 47 - .../Featherstone/btMultiBodyLink.h | 110 - .../Featherstone/btMultiBodyLinkCollider.h | 92 - .../Featherstone/btMultiBodyPoint2Point.cpp | 143 -- .../Featherstone/btMultiBodyPoint2Point.h | 60 - .../btMultiBodySolverConstraint.h | 82 - .../MLCPSolvers/btDantzigLCP.cpp | 2079 ----------------- .../BulletDynamics/MLCPSolvers/btDantzigLCP.h | 77 - .../MLCPSolvers/btDantzigSolver.h | 112 - .../MLCPSolvers/btMLCPSolver.cpp | 626 ----- .../BulletDynamics/MLCPSolvers/btMLCPSolver.h | 81 - .../MLCPSolvers/btMLCPSolverInterface.h | 33 - .../BulletDynamics/MLCPSolvers/btPATHSolver.h | 151 -- .../MLCPSolvers/btSolveProjectedGaussSeidel.h | 80 - 25 files changed, 7721 deletions(-) delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.cpp delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointMotor.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyLink.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyLinkCollider.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodySolverConstraint.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigLCP.cpp delete mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigLCP.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigSolver.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btMLCPSolver.cpp delete mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btMLCPSolver.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btMLCPSolverInterface.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btPATHSolver.h delete mode 100644 Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btSolveProjectedGaussSeidel.h diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.cpp b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.cpp deleted file mode 100644 index 56a1c55d9..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.cpp +++ /dev/null @@ -1,1009 +0,0 @@ -/* - * PURPOSE: - * Class representing an articulated rigid body. Stores the body's - * current state, allows forces and torques to be set, handles - * timestepping and implements Featherstone's algorithm. - * - * COPYRIGHT: - * Copyright (C) Stephen Thompson, , 2011-2013 - * Portions written By Erwin Coumans: replacing Eigen math library by Bullet LinearMath and a dedicated 6x6 matrix inverse (solveImatrix) - - This software is provided 'as-is', without any express or implied warranty. - In no event will the authors be held liable for any damages arising from the use of this software. - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it freely, - subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - */ - - -#include "btMultiBody.h" -#include "btMultiBodyLink.h" -#include "btMultiBodyLinkCollider.h" - -// #define INCLUDE_GYRO_TERM - -namespace { - const btScalar SLEEP_EPSILON = btScalar(0.05); // this is a squared velocity (m^2 s^-2) - const btScalar SLEEP_TIMEOUT = btScalar(2); // in seconds -} - - - - -// -// Various spatial helper functions -// - -namespace { - void SpatialTransform(const btMatrix3x3 &rotation_matrix, // rotates vectors in 'from' frame to vectors in 'to' frame - const btVector3 &displacement, // vector from origin of 'from' frame to origin of 'to' frame, in 'to' coordinates - const btVector3 &top_in, // top part of input vector - const btVector3 &bottom_in, // bottom part of input vector - btVector3 &top_out, // top part of output vector - btVector3 &bottom_out) // bottom part of output vector - { - top_out = rotation_matrix * top_in; - bottom_out = -displacement.cross(top_out) + rotation_matrix * bottom_in; - } - - void InverseSpatialTransform(const btMatrix3x3 &rotation_matrix, - const btVector3 &displacement, - const btVector3 &top_in, - const btVector3 &bottom_in, - btVector3 &top_out, - btVector3 &bottom_out) - { - top_out = rotation_matrix.transpose() * top_in; - bottom_out = rotation_matrix.transpose() * (bottom_in + displacement.cross(top_in)); - } - - btScalar SpatialDotProduct(const btVector3 &a_top, - const btVector3 &a_bottom, - const btVector3 &b_top, - const btVector3 &b_bottom) - { - return a_bottom.dot(b_top) + a_top.dot(b_bottom); - } -} - - -// -// Implementation of class btMultiBody -// - -btMultiBody::btMultiBody(int n_links, - btScalar mass, - const btVector3 &inertia, - bool fixed_base_, - bool can_sleep_) - : base_quat(0, 0, 0, 1), - base_mass(mass), - base_inertia(inertia), - - fixed_base(fixed_base_), - awake(true), - can_sleep(can_sleep_), - sleep_timer(0), - m_baseCollider(0), - m_linearDamping(0.04f), - m_angularDamping(0.04f), - m_useGyroTerm(true), - m_maxAppliedImpulse(1000.f), - m_hasSelfCollision(true) -{ - links.resize(n_links); - - vector_buf.resize(2*n_links); - matrix_buf.resize(n_links + 1); - m_real_buf.resize(6 + 2*n_links); - base_pos.setValue(0, 0, 0); - base_force.setValue(0, 0, 0); - base_torque.setValue(0, 0, 0); -} - -btMultiBody::~btMultiBody() -{ -} - -void btMultiBody::setupPrismatic(int i, - btScalar mass, - const btVector3 &inertia, - int parent, - const btQuaternion &rot_parent_to_this, - const btVector3 &joint_axis, - const btVector3 &r_vector_when_q_zero, - bool disableParentCollision) -{ - links[i].mass = mass; - links[i].inertia = inertia; - links[i].parent = parent; - links[i].zero_rot_parent_to_this = rot_parent_to_this; - links[i].axis_top.setValue(0,0,0); - links[i].axis_bottom = joint_axis; - links[i].e_vector = r_vector_when_q_zero; - links[i].is_revolute = false; - links[i].cached_rot_parent_to_this = rot_parent_to_this; - if (disableParentCollision) - links[i].m_flags |=BT_MULTIBODYLINKFLAGS_DISABLE_PARENT_COLLISION; - - links[i].updateCache(); -} - -void btMultiBody::setupRevolute(int i, - btScalar mass, - const btVector3 &inertia, - int parent, - const btQuaternion &zero_rot_parent_to_this, - const btVector3 &joint_axis, - const btVector3 &parent_axis_position, - const btVector3 &my_axis_position, - bool disableParentCollision) -{ - links[i].mass = mass; - links[i].inertia = inertia; - links[i].parent = parent; - links[i].zero_rot_parent_to_this = zero_rot_parent_to_this; - links[i].axis_top = joint_axis; - links[i].axis_bottom = joint_axis.cross(my_axis_position); - links[i].d_vector = my_axis_position; - links[i].e_vector = parent_axis_position; - links[i].is_revolute = true; - if (disableParentCollision) - links[i].m_flags |=BT_MULTIBODYLINKFLAGS_DISABLE_PARENT_COLLISION; - links[i].updateCache(); -} - - - - - -int btMultiBody::getParent(int i) const -{ - return links[i].parent; -} - -btScalar btMultiBody::getLinkMass(int i) const -{ - return links[i].mass; -} - -const btVector3 & btMultiBody::getLinkInertia(int i) const -{ - return links[i].inertia; -} - -btScalar btMultiBody::getJointPos(int i) const -{ - return links[i].joint_pos; -} - -btScalar btMultiBody::getJointVel(int i) const -{ - return m_real_buf[6 + i]; -} - -void btMultiBody::setJointPos(int i, btScalar q) -{ - links[i].joint_pos = q; - links[i].updateCache(); -} - -void btMultiBody::setJointVel(int i, btScalar qdot) -{ - m_real_buf[6 + i] = qdot; -} - -const btVector3 & btMultiBody::getRVector(int i) const -{ - return links[i].cached_r_vector; -} - -const btQuaternion & btMultiBody::getParentToLocalRot(int i) const -{ - return links[i].cached_rot_parent_to_this; -} - -btVector3 btMultiBody::localPosToWorld(int i, const btVector3 &local_pos) const -{ - btVector3 result = local_pos; - while (i != -1) { - // 'result' is in frame i. transform it to frame parent(i) - result += getRVector(i); - result = quatRotate(getParentToLocalRot(i).inverse(),result); - i = getParent(i); - } - - // 'result' is now in the base frame. transform it to world frame - result = quatRotate(getWorldToBaseRot().inverse() ,result); - result += getBasePos(); - - return result; -} - -btVector3 btMultiBody::worldPosToLocal(int i, const btVector3 &world_pos) const -{ - if (i == -1) { - // world to base - return quatRotate(getWorldToBaseRot(),(world_pos - getBasePos())); - } else { - // find position in parent frame, then transform to current frame - return quatRotate(getParentToLocalRot(i),worldPosToLocal(getParent(i), world_pos)) - getRVector(i); - } -} - -btVector3 btMultiBody::localDirToWorld(int i, const btVector3 &local_dir) const -{ - btVector3 result = local_dir; - while (i != -1) { - result = quatRotate(getParentToLocalRot(i).inverse() , result); - i = getParent(i); - } - result = quatRotate(getWorldToBaseRot().inverse() , result); - return result; -} - -btVector3 btMultiBody::worldDirToLocal(int i, const btVector3 &world_dir) const -{ - if (i == -1) { - return quatRotate(getWorldToBaseRot(), world_dir); - } else { - return quatRotate(getParentToLocalRot(i) ,worldDirToLocal(getParent(i), world_dir)); - } -} - -void btMultiBody::compTreeLinkVelocities(btVector3 *omega, btVector3 *vel) const -{ - int num_links = getNumLinks(); - // Calculates the velocities of each link (and the base) in its local frame - omega[0] = quatRotate(base_quat ,getBaseOmega()); - vel[0] = quatRotate(base_quat ,getBaseVel()); - - for (int i = 0; i < num_links; ++i) { - const int parent = links[i].parent; - - // transform parent vel into this frame, store in omega[i+1], vel[i+1] - SpatialTransform(btMatrix3x3(links[i].cached_rot_parent_to_this), links[i].cached_r_vector, - omega[parent+1], vel[parent+1], - omega[i+1], vel[i+1]); - - // now add qidot * shat_i - omega[i+1] += getJointVel(i) * links[i].axis_top; - vel[i+1] += getJointVel(i) * links[i].axis_bottom; - } -} - -btScalar btMultiBody::getKineticEnergy() const -{ - int num_links = getNumLinks(); - // TODO: would be better not to allocate memory here - btAlignedObjectArray omega;omega.resize(num_links+1); - btAlignedObjectArray vel;vel.resize(num_links+1); - compTreeLinkVelocities(&omega[0], &vel[0]); - - // we will do the factor of 0.5 at the end - btScalar result = base_mass * vel[0].dot(vel[0]); - result += omega[0].dot(base_inertia * omega[0]); - - for (int i = 0; i < num_links; ++i) { - result += links[i].mass * vel[i+1].dot(vel[i+1]); - result += omega[i+1].dot(links[i].inertia * omega[i+1]); - } - - return 0.5f * result; -} - -btVector3 btMultiBody::getAngularMomentum() const -{ - int num_links = getNumLinks(); - // TODO: would be better not to allocate memory here - btAlignedObjectArray omega;omega.resize(num_links+1); - btAlignedObjectArray vel;vel.resize(num_links+1); - btAlignedObjectArray rot_from_world;rot_from_world.resize(num_links+1); - compTreeLinkVelocities(&omega[0], &vel[0]); - - rot_from_world[0] = base_quat; - btVector3 result = quatRotate(rot_from_world[0].inverse() , (base_inertia * omega[0])); - - for (int i = 0; i < num_links; ++i) { - rot_from_world[i+1] = links[i].cached_rot_parent_to_this * rot_from_world[links[i].parent+1]; - result += (quatRotate(rot_from_world[i+1].inverse() , (links[i].inertia * omega[i+1]))); - } - - return result; -} - - -void btMultiBody::clearForcesAndTorques() -{ - base_force.setValue(0, 0, 0); - base_torque.setValue(0, 0, 0); - - for (int i = 0; i < getNumLinks(); ++i) { - links[i].applied_force.setValue(0, 0, 0); - links[i].applied_torque.setValue(0, 0, 0); - links[i].joint_torque = 0; - } -} - -void btMultiBody::clearVelocities() -{ - for (int i = 0; i < 6 + getNumLinks(); ++i) - { - m_real_buf[i] = 0.f; - } -} -void btMultiBody::addLinkForce(int i, const btVector3 &f) -{ - links[i].applied_force += f; -} - -void btMultiBody::addLinkTorque(int i, const btVector3 &t) -{ - links[i].applied_torque += t; -} - -void btMultiBody::addJointTorque(int i, btScalar Q) -{ - links[i].joint_torque += Q; -} - -const btVector3 & btMultiBody::getLinkForce(int i) const -{ - return links[i].applied_force; -} - -const btVector3 & btMultiBody::getLinkTorque(int i) const -{ - return links[i].applied_torque; -} - -btScalar btMultiBody::getJointTorque(int i) const -{ - return links[i].joint_torque; -} - - -inline btMatrix3x3 vecMulVecTranspose(const btVector3& v0, const btVector3& v1Transposed) -{ - btVector3 row0 = btVector3( - v0.x() * v1Transposed.x(), - v0.x() * v1Transposed.y(), - v0.x() * v1Transposed.z()); - btVector3 row1 = btVector3( - v0.y() * v1Transposed.x(), - v0.y() * v1Transposed.y(), - v0.y() * v1Transposed.z()); - btVector3 row2 = btVector3( - v0.z() * v1Transposed.x(), - v0.z() * v1Transposed.y(), - v0.z() * v1Transposed.z()); - - btMatrix3x3 m(row0[0],row0[1],row0[2], - row1[0],row1[1],row1[2], - row2[0],row2[1],row2[2]); - return m; -} - - -void btMultiBody::stepVelocities(btScalar dt, - btAlignedObjectArray &scratch_r, - btAlignedObjectArray &scratch_v, - btAlignedObjectArray &scratch_m) -{ - // Implement Featherstone's algorithm to calculate joint accelerations (q_double_dot) - // and the base linear & angular accelerations. - - // We apply damping forces in this routine as well as any external forces specified by the - // caller (via addBaseForce etc). - - // output should point to an array of 6 + num_links reals. - // Format is: 3 angular accelerations (in world frame), 3 linear accelerations (in world frame), - // num_links joint acceleration values. - - int num_links = getNumLinks(); - - const btScalar DAMPING_K1_LINEAR = m_linearDamping; - const btScalar DAMPING_K2_LINEAR = m_linearDamping; - - const btScalar DAMPING_K1_ANGULAR = m_angularDamping; - const btScalar DAMPING_K2_ANGULAR= m_angularDamping; - - btVector3 base_vel = getBaseVel(); - btVector3 base_omega = getBaseOmega(); - - // Temporary matrices/vectors -- use scratch space from caller - // so that we don't have to keep reallocating every frame - - scratch_r.resize(2*num_links + 6); - scratch_v.resize(8*num_links + 6); - scratch_m.resize(4*num_links + 4); - - btScalar * r_ptr = &scratch_r[0]; - btScalar * output = &scratch_r[num_links]; // "output" holds the q_double_dot results - btVector3 * v_ptr = &scratch_v[0]; - - // vhat_i (top = angular, bottom = linear part) - btVector3 * vel_top_angular = v_ptr; v_ptr += num_links + 1; - btVector3 * vel_bottom_linear = v_ptr; v_ptr += num_links + 1; - - // zhat_i^A - btVector3 * zero_acc_top_angular = v_ptr; v_ptr += num_links + 1; - btVector3 * zero_acc_bottom_linear = v_ptr; v_ptr += num_links + 1; - - // chat_i (note NOT defined for the base) - btVector3 * coriolis_top_angular = v_ptr; v_ptr += num_links; - btVector3 * coriolis_bottom_linear = v_ptr; v_ptr += num_links; - - // top left, top right and bottom left blocks of Ihat_i^A. - // bottom right block = transpose of top left block and is not stored. - // Note: the top right and bottom left blocks are always symmetric matrices, but we don't make use of this fact currently. - btMatrix3x3 * inertia_top_left = &scratch_m[num_links + 1]; - btMatrix3x3 * inertia_top_right = &scratch_m[2*num_links + 2]; - btMatrix3x3 * inertia_bottom_left = &scratch_m[3*num_links + 3]; - - // Cached 3x3 rotation matrices from parent frame to this frame. - btMatrix3x3 * rot_from_parent = &matrix_buf[0]; - btMatrix3x3 * rot_from_world = &scratch_m[0]; - - // hhat_i, ahat_i - // hhat is NOT stored for the base (but ahat is) - btVector3 * h_top = num_links > 0 ? &vector_buf[0] : 0; - btVector3 * h_bottom = num_links > 0 ? &vector_buf[num_links] : 0; - btVector3 * accel_top = v_ptr; v_ptr += num_links + 1; - btVector3 * accel_bottom = v_ptr; v_ptr += num_links + 1; - - // Y_i, D_i - btScalar * Y = r_ptr; r_ptr += num_links; - btScalar * D = num_links > 0 ? &m_real_buf[6 + num_links] : 0; - - // ptr to the joint accel part of the output - btScalar * joint_accel = output + 6; - - - // Start of the algorithm proper. - - // First 'upward' loop. - // Combines CompTreeLinkVelocities and InitTreeLinks from Mirtich. - - rot_from_parent[0] = btMatrix3x3(base_quat); - - vel_top_angular[0] = rot_from_parent[0] * base_omega; - vel_bottom_linear[0] = rot_from_parent[0] * base_vel; - - if (fixed_base) { - zero_acc_top_angular[0] = zero_acc_bottom_linear[0] = btVector3(0,0,0); - } else { - zero_acc_top_angular[0] = - (rot_from_parent[0] * (base_force - - base_mass*(DAMPING_K1_LINEAR+DAMPING_K2_LINEAR*base_vel.norm())*base_vel)); - - zero_acc_bottom_linear[0] = - - (rot_from_parent[0] * base_torque); - - if (m_useGyroTerm) - zero_acc_bottom_linear[0]+=vel_top_angular[0].cross( base_inertia * vel_top_angular[0] ); - - zero_acc_bottom_linear[0] += base_inertia * vel_top_angular[0] * (DAMPING_K1_ANGULAR + DAMPING_K2_ANGULAR*vel_top_angular[0].norm()); - - } - - - - inertia_top_left[0] = btMatrix3x3(0,0,0,0,0,0,0,0,0);//::Zero(); - - - inertia_top_right[0].setValue(base_mass, 0, 0, - 0, base_mass, 0, - 0, 0, base_mass); - inertia_bottom_left[0].setValue(base_inertia[0], 0, 0, - 0, base_inertia[1], 0, - 0, 0, base_inertia[2]); - - rot_from_world[0] = rot_from_parent[0]; - - for (int i = 0; i < num_links; ++i) { - const int parent = links[i].parent; - rot_from_parent[i+1] = btMatrix3x3(links[i].cached_rot_parent_to_this); - - - rot_from_world[i+1] = rot_from_parent[i+1] * rot_from_world[parent+1]; - - // vhat_i = i_xhat_p(i) * vhat_p(i) - SpatialTransform(rot_from_parent[i+1], links[i].cached_r_vector, - vel_top_angular[parent+1], vel_bottom_linear[parent+1], - vel_top_angular[i+1], vel_bottom_linear[i+1]); - - // we can now calculate chat_i - // remember vhat_i is really vhat_p(i) (but in current frame) at this point - coriolis_bottom_linear[i] = vel_top_angular[i+1].cross(vel_top_angular[i+1].cross(links[i].cached_r_vector)) - + 2 * vel_top_angular[i+1].cross(links[i].axis_bottom) * getJointVel(i); - if (links[i].is_revolute) { - coriolis_top_angular[i] = vel_top_angular[i+1].cross(links[i].axis_top) * getJointVel(i); - coriolis_bottom_linear[i] += (getJointVel(i) * getJointVel(i)) * links[i].axis_top.cross(links[i].axis_bottom); - } else { - coriolis_top_angular[i] = btVector3(0,0,0); - } - - // now set vhat_i to its true value by doing - // vhat_i += qidot * shat_i - vel_top_angular[i+1] += getJointVel(i) * links[i].axis_top; - vel_bottom_linear[i+1] += getJointVel(i) * links[i].axis_bottom; - - // calculate zhat_i^A - zero_acc_top_angular[i+1] = - (rot_from_world[i+1] * (links[i].applied_force)); - zero_acc_top_angular[i+1] += links[i].mass * (DAMPING_K1_LINEAR + DAMPING_K2_LINEAR*vel_bottom_linear[i+1].norm()) * vel_bottom_linear[i+1]; - - zero_acc_bottom_linear[i+1] = - - (rot_from_world[i+1] * links[i].applied_torque); - if (m_useGyroTerm) - { - zero_acc_bottom_linear[i+1] += vel_top_angular[i+1].cross( links[i].inertia * vel_top_angular[i+1] ); - } - - zero_acc_bottom_linear[i+1] += links[i].inertia * vel_top_angular[i+1] * (DAMPING_K1_ANGULAR + DAMPING_K2_ANGULAR*vel_top_angular[i+1].norm()); - - // calculate Ihat_i^A - inertia_top_left[i+1] = btMatrix3x3(0,0,0,0,0,0,0,0,0);//::Zero(); - inertia_top_right[i+1].setValue(links[i].mass, 0, 0, - 0, links[i].mass, 0, - 0, 0, links[i].mass); - inertia_bottom_left[i+1].setValue(links[i].inertia[0], 0, 0, - 0, links[i].inertia[1], 0, - 0, 0, links[i].inertia[2]); - } - - - // 'Downward' loop. - // (part of TreeForwardDynamics in Mirtich.) - for (int i = num_links - 1; i >= 0; --i) { - - h_top[i] = inertia_top_left[i+1] * links[i].axis_top + inertia_top_right[i+1] * links[i].axis_bottom; - h_bottom[i] = inertia_bottom_left[i+1] * links[i].axis_top + inertia_top_left[i+1].transpose() * links[i].axis_bottom; - btScalar val = SpatialDotProduct(links[i].axis_top, links[i].axis_bottom, h_top[i], h_bottom[i]); - D[i] = val; - Y[i] = links[i].joint_torque - - SpatialDotProduct(links[i].axis_top, links[i].axis_bottom, zero_acc_top_angular[i+1], zero_acc_bottom_linear[i+1]) - - SpatialDotProduct(h_top[i], h_bottom[i], coriolis_top_angular[i], coriolis_bottom_linear[i]); - - const int parent = links[i].parent; - - - // Ip += pXi * (Ii - hi hi' / Di) * iXp - const btScalar one_over_di = 1.0f / D[i]; - - - - - const btMatrix3x3 TL = inertia_top_left[i+1] - vecMulVecTranspose(one_over_di * h_top[i] , h_bottom[i]); - const btMatrix3x3 TR = inertia_top_right[i+1] - vecMulVecTranspose(one_over_di * h_top[i] , h_top[i]); - const btMatrix3x3 BL = inertia_bottom_left[i+1]- vecMulVecTranspose(one_over_di * h_bottom[i] , h_bottom[i]); - - - btMatrix3x3 r_cross; - r_cross.setValue( - 0, -links[i].cached_r_vector[2], links[i].cached_r_vector[1], - links[i].cached_r_vector[2], 0, -links[i].cached_r_vector[0], - -links[i].cached_r_vector[1], links[i].cached_r_vector[0], 0); - - inertia_top_left[parent+1] += rot_from_parent[i+1].transpose() * ( TL - TR * r_cross ) * rot_from_parent[i+1]; - inertia_top_right[parent+1] += rot_from_parent[i+1].transpose() * TR * rot_from_parent[i+1]; - inertia_bottom_left[parent+1] += rot_from_parent[i+1].transpose() * - (r_cross * (TL - TR * r_cross) + BL - TL.transpose() * r_cross) * rot_from_parent[i+1]; - - - // Zp += pXi * (Zi + Ii*ci + hi*Yi/Di) - btVector3 in_top, in_bottom, out_top, out_bottom; - const btScalar Y_over_D = Y[i] * one_over_di; - in_top = zero_acc_top_angular[i+1] - + inertia_top_left[i+1] * coriolis_top_angular[i] - + inertia_top_right[i+1] * coriolis_bottom_linear[i] - + Y_over_D * h_top[i]; - in_bottom = zero_acc_bottom_linear[i+1] - + inertia_bottom_left[i+1] * coriolis_top_angular[i] - + inertia_top_left[i+1].transpose() * coriolis_bottom_linear[i] - + Y_over_D * h_bottom[i]; - InverseSpatialTransform(rot_from_parent[i+1], links[i].cached_r_vector, - in_top, in_bottom, out_top, out_bottom); - zero_acc_top_angular[parent+1] += out_top; - zero_acc_bottom_linear[parent+1] += out_bottom; - } - - - // Second 'upward' loop - // (part of TreeForwardDynamics in Mirtich) - - if (fixed_base) - { - accel_top[0] = accel_bottom[0] = btVector3(0,0,0); - } - else - { - if (num_links > 0) - { - //Matrix Imatrix; - //Imatrix.block<3,3>(0,0) = inertia_top_left[0]; - //Imatrix.block<3,3>(3,0) = inertia_bottom_left[0]; - //Imatrix.block<3,3>(0,3) = inertia_top_right[0]; - //Imatrix.block<3,3>(3,3) = inertia_top_left[0].transpose(); - //cached_imatrix_lu.reset(new Eigen::LU >(Imatrix)); // TODO: Avoid memory allocation here? - - cached_inertia_top_left = inertia_top_left[0]; - cached_inertia_top_right = inertia_top_right[0]; - cached_inertia_lower_left = inertia_bottom_left[0]; - cached_inertia_lower_right= inertia_top_left[0].transpose(); - - } - btVector3 rhs_top (zero_acc_top_angular[0][0], zero_acc_top_angular[0][1], zero_acc_top_angular[0][2]); - btVector3 rhs_bot (zero_acc_bottom_linear[0][0], zero_acc_bottom_linear[0][1], zero_acc_bottom_linear[0][2]); - float result[6]; - - solveImatrix(rhs_top, rhs_bot, result); -// printf("result=%f,%f,%f,%f,%f,%f\n",result[0],result[0],result[0],result[0],result[0],result[0]); - for (int i = 0; i < 3; ++i) { - accel_top[0][i] = -result[i]; - accel_bottom[0][i] = -result[i+3]; - } - - } - - // now do the loop over the links - for (int i = 0; i < num_links; ++i) { - const int parent = links[i].parent; - SpatialTransform(rot_from_parent[i+1], links[i].cached_r_vector, - accel_top[parent+1], accel_bottom[parent+1], - accel_top[i+1], accel_bottom[i+1]); - joint_accel[i] = (Y[i] - SpatialDotProduct(h_top[i], h_bottom[i], accel_top[i+1], accel_bottom[i+1])) / D[i]; - accel_top[i+1] += coriolis_top_angular[i] + joint_accel[i] * links[i].axis_top; - accel_bottom[i+1] += coriolis_bottom_linear[i] + joint_accel[i] * links[i].axis_bottom; - } - - // transform base accelerations back to the world frame. - btVector3 omegadot_out = rot_from_parent[0].transpose() * accel_top[0]; - output[0] = omegadot_out[0]; - output[1] = omegadot_out[1]; - output[2] = omegadot_out[2]; - - btVector3 vdot_out = rot_from_parent[0].transpose() * accel_bottom[0]; - output[3] = vdot_out[0]; - output[4] = vdot_out[1]; - output[5] = vdot_out[2]; - // Final step: add the accelerations (times dt) to the velocities. - applyDeltaVee(output, dt); - - -} - - - -void btMultiBody::solveImatrix(const btVector3& rhs_top, const btVector3& rhs_bot, float result[6]) const -{ - int num_links = getNumLinks(); - ///solve I * x = rhs, so the result = invI * rhs - if (num_links == 0) - { - // in the case of 0 links (i.e. a plain rigid body, not a multibody) rhs * invI is easier - result[0] = rhs_bot[0] / base_inertia[0]; - result[1] = rhs_bot[1] / base_inertia[1]; - result[2] = rhs_bot[2] / base_inertia[2]; - result[3] = rhs_top[0] / base_mass; - result[4] = rhs_top[1] / base_mass; - result[5] = rhs_top[2] / base_mass; - } else - { - /// Special routine for calculating the inverse of a spatial inertia matrix - ///the 6x6 matrix is stored as 4 blocks of 3x3 matrices - btMatrix3x3 Binv = cached_inertia_top_right.inverse()*-1.f; - btMatrix3x3 tmp = cached_inertia_lower_right * Binv; - btMatrix3x3 invIupper_right = (tmp * cached_inertia_top_left + cached_inertia_lower_left).inverse(); - tmp = invIupper_right * cached_inertia_lower_right; - btMatrix3x3 invI_upper_left = (tmp * Binv); - btMatrix3x3 invI_lower_right = (invI_upper_left).transpose(); - tmp = cached_inertia_top_left * invI_upper_left; - tmp[0][0]-= 1.0; - tmp[1][1]-= 1.0; - tmp[2][2]-= 1.0; - btMatrix3x3 invI_lower_left = (Binv * tmp); - - //multiply result = invI * rhs - { - btVector3 vtop = invI_upper_left*rhs_top; - btVector3 tmp; - tmp = invIupper_right * rhs_bot; - vtop += tmp; - btVector3 vbot = invI_lower_left*rhs_top; - tmp = invI_lower_right * rhs_bot; - vbot += tmp; - result[0] = vtop[0]; - result[1] = vtop[1]; - result[2] = vtop[2]; - result[3] = vbot[0]; - result[4] = vbot[1]; - result[5] = vbot[2]; - } - - } -} - - -void btMultiBody::calcAccelerationDeltas(const btScalar *force, btScalar *output, - btAlignedObjectArray &scratch_r, btAlignedObjectArray &scratch_v) const -{ - // Temporary matrices/vectors -- use scratch space from caller - // so that we don't have to keep reallocating every frame - int num_links = getNumLinks(); - scratch_r.resize(num_links); - scratch_v.resize(4*num_links + 4); - - btScalar * r_ptr = num_links == 0 ? 0 : &scratch_r[0]; - btVector3 * v_ptr = &scratch_v[0]; - - // zhat_i^A (scratch space) - btVector3 * zero_acc_top_angular = v_ptr; v_ptr += num_links + 1; - btVector3 * zero_acc_bottom_linear = v_ptr; v_ptr += num_links + 1; - - // rot_from_parent (cached from calcAccelerations) - const btMatrix3x3 * rot_from_parent = &matrix_buf[0]; - - // hhat (cached), accel (scratch) - const btVector3 * h_top = num_links > 0 ? &vector_buf[0] : 0; - const btVector3 * h_bottom = num_links > 0 ? &vector_buf[num_links] : 0; - btVector3 * accel_top = v_ptr; v_ptr += num_links + 1; - btVector3 * accel_bottom = v_ptr; v_ptr += num_links + 1; - - // Y_i (scratch), D_i (cached) - btScalar * Y = r_ptr; r_ptr += num_links; - const btScalar * D = num_links > 0 ? &m_real_buf[6 + num_links] : 0; - - btAssert(num_links == 0 || r_ptr - &scratch_r[0] == scratch_r.size()); - btAssert(v_ptr - &scratch_v[0] == scratch_v.size()); - - - - // First 'upward' loop. - // Combines CompTreeLinkVelocities and InitTreeLinks from Mirtich. - - btVector3 input_force(force[3],force[4],force[5]); - btVector3 input_torque(force[0],force[1],force[2]); - - // Fill in zero_acc - // -- set to force/torque on the base, zero otherwise - if (fixed_base) - { - zero_acc_top_angular[0] = zero_acc_bottom_linear[0] = btVector3(0,0,0); - } else - { - zero_acc_top_angular[0] = - (rot_from_parent[0] * input_force); - zero_acc_bottom_linear[0] = - (rot_from_parent[0] * input_torque); - } - for (int i = 0; i < num_links; ++i) - { - zero_acc_top_angular[i+1] = zero_acc_bottom_linear[i+1] = btVector3(0,0,0); - } - - // 'Downward' loop. - for (int i = num_links - 1; i >= 0; --i) - { - - Y[i] = - SpatialDotProduct(links[i].axis_top, links[i].axis_bottom, zero_acc_top_angular[i+1], zero_acc_bottom_linear[i+1]); - Y[i] += force[6 + i]; // add joint torque - - const int parent = links[i].parent; - - // Zp += pXi * (Zi + hi*Yi/Di) - btVector3 in_top, in_bottom, out_top, out_bottom; - const btScalar Y_over_D = Y[i] / D[i]; - in_top = zero_acc_top_angular[i+1] + Y_over_D * h_top[i]; - in_bottom = zero_acc_bottom_linear[i+1] + Y_over_D * h_bottom[i]; - InverseSpatialTransform(rot_from_parent[i+1], links[i].cached_r_vector, - in_top, in_bottom, out_top, out_bottom); - zero_acc_top_angular[parent+1] += out_top; - zero_acc_bottom_linear[parent+1] += out_bottom; - } - - // ptr to the joint accel part of the output - btScalar * joint_accel = output + 6; - - // Second 'upward' loop - if (fixed_base) - { - accel_top[0] = accel_bottom[0] = btVector3(0,0,0); - } else - { - btVector3 rhs_top (zero_acc_top_angular[0][0], zero_acc_top_angular[0][1], zero_acc_top_angular[0][2]); - btVector3 rhs_bot (zero_acc_bottom_linear[0][0], zero_acc_bottom_linear[0][1], zero_acc_bottom_linear[0][2]); - - float result[6]; - solveImatrix(rhs_top,rhs_bot, result); - // printf("result=%f,%f,%f,%f,%f,%f\n",result[0],result[0],result[0],result[0],result[0],result[0]); - - for (int i = 0; i < 3; ++i) { - accel_top[0][i] = -result[i]; - accel_bottom[0][i] = -result[i+3]; - } - - } - - // now do the loop over the links - for (int i = 0; i < num_links; ++i) { - const int parent = links[i].parent; - SpatialTransform(rot_from_parent[i+1], links[i].cached_r_vector, - accel_top[parent+1], accel_bottom[parent+1], - accel_top[i+1], accel_bottom[i+1]); - joint_accel[i] = (Y[i] - SpatialDotProduct(h_top[i], h_bottom[i], accel_top[i+1], accel_bottom[i+1])) / D[i]; - accel_top[i+1] += joint_accel[i] * links[i].axis_top; - accel_bottom[i+1] += joint_accel[i] * links[i].axis_bottom; - } - - // transform base accelerations back to the world frame. - btVector3 omegadot_out; - omegadot_out = rot_from_parent[0].transpose() * accel_top[0]; - output[0] = omegadot_out[0]; - output[1] = omegadot_out[1]; - output[2] = omegadot_out[2]; - - btVector3 vdot_out; - vdot_out = rot_from_parent[0].transpose() * accel_bottom[0]; - - output[3] = vdot_out[0]; - output[4] = vdot_out[1]; - output[5] = vdot_out[2]; -} - -void btMultiBody::stepPositions(btScalar dt) -{ - int num_links = getNumLinks(); - // step position by adding dt * velocity - btVector3 v = getBaseVel(); - base_pos += dt * v; - - // "exponential map" method for the rotation - btVector3 base_omega = getBaseOmega(); - const btScalar omega_norm = base_omega.norm(); - const btScalar omega_times_dt = omega_norm * dt; - const btScalar SMALL_ROTATION_ANGLE = 0.02f; // Theoretically this should be ~ pow(FLT_EPSILON,0.25) which is ~ 0.0156 - if (fabs(omega_times_dt) < SMALL_ROTATION_ANGLE) - { - const btScalar xsq = omega_times_dt * omega_times_dt; // |omega|^2 * dt^2 - const btScalar sin_term = dt * (xsq / 48.0f - 0.5f); // -sin(0.5*dt*|omega|) / |omega| - const btScalar cos_term = 1.0f - xsq / 8.0f; // cos(0.5*dt*|omega|) - base_quat = base_quat * btQuaternion(sin_term * base_omega[0],sin_term * base_omega[1],sin_term * base_omega[2],cos_term); - } else - { - base_quat = base_quat * btQuaternion(base_omega / omega_norm,-omega_times_dt); - } - - // Make sure the quaternion represents a valid rotation. - // (Not strictly necessary, but helps prevent any round-off errors from building up.) - base_quat.normalize(); - - // Finally we can update joint_pos for each of the links - for (int i = 0; i < num_links; ++i) - { - float jointVel = getJointVel(i); - links[i].joint_pos += dt * jointVel; - links[i].updateCache(); - } -} - -void btMultiBody::fillContactJacobian(int link, - const btVector3 &contact_point, - const btVector3 &normal, - btScalar *jac, - btAlignedObjectArray &scratch_r, - btAlignedObjectArray &scratch_v, - btAlignedObjectArray &scratch_m) const -{ - // temporary space - int num_links = getNumLinks(); - scratch_v.resize(2*num_links + 2); - scratch_m.resize(num_links + 1); - - btVector3 * v_ptr = &scratch_v[0]; - btVector3 * p_minus_com = v_ptr; v_ptr += num_links + 1; - btVector3 * n_local = v_ptr; v_ptr += num_links + 1; - btAssert(v_ptr - &scratch_v[0] == scratch_v.size()); - - scratch_r.resize(num_links); - btScalar * results = num_links > 0 ? &scratch_r[0] : 0; - - btMatrix3x3 * rot_from_world = &scratch_m[0]; - - const btVector3 p_minus_com_world = contact_point - base_pos; - - rot_from_world[0] = btMatrix3x3(base_quat); - - p_minus_com[0] = rot_from_world[0] * p_minus_com_world; - n_local[0] = rot_from_world[0] * normal; - - // omega coeffients first. - btVector3 omega_coeffs; - omega_coeffs = p_minus_com_world.cross(normal); - jac[0] = omega_coeffs[0]; - jac[1] = omega_coeffs[1]; - jac[2] = omega_coeffs[2]; - // then v coefficients - jac[3] = normal[0]; - jac[4] = normal[1]; - jac[5] = normal[2]; - - // Set remaining jac values to zero for now. - for (int i = 6; i < 6 + num_links; ++i) { - jac[i] = 0; - } - - // Qdot coefficients, if necessary. - if (num_links > 0 && link > -1) { - - // TODO: speed this up -- don't calculate for links we don't need. - // (Also, we are making 3 separate calls to this function, for the normal & the 2 friction directions, - // which is resulting in repeated work being done...) - - // calculate required normals & positions in the local frames. - for (int i = 0; i < num_links; ++i) { - - // transform to local frame - const int parent = links[i].parent; - const btMatrix3x3 mtx(links[i].cached_rot_parent_to_this); - rot_from_world[i+1] = mtx * rot_from_world[parent+1]; - - n_local[i+1] = mtx * n_local[parent+1]; - p_minus_com[i+1] = mtx * p_minus_com[parent+1] - links[i].cached_r_vector; - - // calculate the jacobian entry - if (links[i].is_revolute) { - results[i] = n_local[i+1].dot( links[i].axis_top.cross(p_minus_com[i+1]) + links[i].axis_bottom ); - } else { - results[i] = n_local[i+1].dot( links[i].axis_bottom ); - } - } - - // Now copy through to output. - while (link != -1) { - jac[6 + link] = results[link]; - link = links[link].parent; - } - } -} - -void btMultiBody::wakeUp() -{ - awake = true; -} - -void btMultiBody::goToSleep() -{ - awake = false; -} - -void btMultiBody::checkMotionAndSleepIfRequired(btScalar timestep) -{ - int num_links = getNumLinks(); - extern bool gDisableDeactivation; - if (!can_sleep || gDisableDeactivation) - { - awake = true; - sleep_timer = 0; - return; - } - - // motion is computed as omega^2 + v^2 + (sum of squares of joint velocities) - btScalar motion = 0; - for (int i = 0; i < 6 + num_links; ++i) { - motion += m_real_buf[i] * m_real_buf[i]; - } - - if (motion < SLEEP_EPSILON) { - sleep_timer += timestep; - if (sleep_timer > SLEEP_TIMEOUT) { - goToSleep(); - } - } else { - sleep_timer = 0; - if (!awake) - wakeUp(); - } -} diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.h b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.h deleted file mode 100644 index 7177bebbf..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBody.h +++ /dev/null @@ -1,466 +0,0 @@ -/* - * PURPOSE: - * Class representing an articulated rigid body. Stores the body's - * current state, allows forces and torques to be set, handles - * timestepping and implements Featherstone's algorithm. - * - * COPYRIGHT: - * Copyright (C) Stephen Thompson, , 2011-2013 - * Portions written By Erwin Coumans: replacing Eigen math library by Bullet LinearMath and a dedicated 6x6 matrix inverse (solveImatrix) - - This software is provided 'as-is', without any express or implied warranty. - In no event will the authors be held liable for any damages arising from the use of this software. - Permission is granted to anyone to use this software for any purpose, - including commercial applications, and to alter it and redistribute it freely, - subject to the following restrictions: - - 1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. - 2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. - 3. This notice may not be removed or altered from any source distribution. - - */ - - -#ifndef BT_MULTIBODY_H -#define BT_MULTIBODY_H - -#include "LinearMath/btScalar.h" -#include "LinearMath/btVector3.h" -#include "LinearMath/btQuaternion.h" -#include "LinearMath/btMatrix3x3.h" -#include "LinearMath/btAlignedObjectArray.h" - - -#include "btMultiBodyLink.h" -class btMultiBodyLinkCollider; - -class btMultiBody -{ -public: - - - BT_DECLARE_ALIGNED_ALLOCATOR(); - - // - // initialization - // - - btMultiBody(int n_links, // NOT including the base - btScalar mass, // mass of base - const btVector3 &inertia, // inertia of base, in base frame; assumed diagonal - bool fixed_base_, // whether the base is fixed (true) or can move (false) - bool can_sleep_); - - ~btMultiBody(); - - void setupPrismatic(int i, // 0 to num_links-1 - btScalar mass, - const btVector3 &inertia, // in my frame; assumed diagonal - int parent, - const btQuaternion &rot_parent_to_this, // rotate points in parent frame to my frame. - const btVector3 &joint_axis, // in my frame - const btVector3 &r_vector_when_q_zero, // vector from parent COM to my COM, in my frame, when q = 0. - bool disableParentCollision=false - ); - - void setupRevolute(int i, // 0 to num_links-1 - btScalar mass, - const btVector3 &inertia, - int parent, - const btQuaternion &zero_rot_parent_to_this, // rotate points in parent frame to this frame, when q = 0 - const btVector3 &joint_axis, // in my frame - const btVector3 &parent_axis_position, // vector from parent COM to joint axis, in PARENT frame - const btVector3 &my_axis_position, // vector from joint axis to my COM, in MY frame - bool disableParentCollision=false); - - const btMultibodyLink& getLink(int index) const - { - return links[index]; - } - - btMultibodyLink& getLink(int index) - { - return links[index]; - } - - - void setBaseCollider(btMultiBodyLinkCollider* collider)//collider can be NULL to disable collision for the base - { - m_baseCollider = collider; - } - const btMultiBodyLinkCollider* getBaseCollider() const - { - return m_baseCollider; - } - btMultiBodyLinkCollider* getBaseCollider() - { - return m_baseCollider; - } - - // - // get parent - // input: link num from 0 to num_links-1 - // output: link num from 0 to num_links-1, OR -1 to mean the base. - // - int getParent(int link_num) const; - - - // - // get number of links, masses, moments of inertia - // - - int getNumLinks() const { return links.size(); } - btScalar getBaseMass() const { return base_mass; } - const btVector3 & getBaseInertia() const { return base_inertia; } - btScalar getLinkMass(int i) const; - const btVector3 & getLinkInertia(int i) const; - - - // - // change mass (incomplete: can only change base mass and inertia at present) - // - - void setBaseMass(btScalar mass) { base_mass = mass; } - void setBaseInertia(const btVector3 &inertia) { base_inertia = inertia; } - - - // - // get/set pos/vel/rot/omega for the base link - // - - const btVector3 & getBasePos() const { return base_pos; } // in world frame - const btVector3 getBaseVel() const - { - return btVector3(m_real_buf[3],m_real_buf[4],m_real_buf[5]); - } // in world frame - const btQuaternion & getWorldToBaseRot() const - { - return base_quat; - } // rotates world vectors into base frame - btVector3 getBaseOmega() const { return btVector3(m_real_buf[0],m_real_buf[1],m_real_buf[2]); } // in world frame - - void setBasePos(const btVector3 &pos) - { - base_pos = pos; - } - void setBaseVel(const btVector3 &vel) - { - - m_real_buf[3]=vel[0]; m_real_buf[4]=vel[1]; m_real_buf[5]=vel[2]; - } - void setWorldToBaseRot(const btQuaternion &rot) - { - base_quat = rot; - } - void setBaseOmega(const btVector3 &omega) - { - m_real_buf[0]=omega[0]; - m_real_buf[1]=omega[1]; - m_real_buf[2]=omega[2]; - } - - - // - // get/set pos/vel for child links (i = 0 to num_links-1) - // - - btScalar getJointPos(int i) const; - btScalar getJointVel(int i) const; - - void setJointPos(int i, btScalar q); - void setJointVel(int i, btScalar qdot); - - // - // direct access to velocities as a vector of 6 + num_links elements. - // (omega first, then v, then joint velocities.) - // - const btScalar * getVelocityVector() const - { - return &m_real_buf[0]; - } -/* btScalar * getVelocityVector() - { - return &real_buf[0]; - } - */ - - // - // get the frames of reference (positions and orientations) of the child links - // (i = 0 to num_links-1) - // - - const btVector3 & getRVector(int i) const; // vector from COM(parent(i)) to COM(i), in frame i's coords - const btQuaternion & getParentToLocalRot(int i) const; // rotates vectors in frame parent(i) to vectors in frame i. - - - // - // transform vectors in local frame of link i to world frame (or vice versa) - // - btVector3 localPosToWorld(int i, const btVector3 &vec) const; - btVector3 localDirToWorld(int i, const btVector3 &vec) const; - btVector3 worldPosToLocal(int i, const btVector3 &vec) const; - btVector3 worldDirToLocal(int i, const btVector3 &vec) const; - - - // - // calculate kinetic energy and angular momentum - // useful for debugging. - // - - btScalar getKineticEnergy() const; - btVector3 getAngularMomentum() const; - - - // - // set external forces and torques. Note all external forces/torques are given in the WORLD frame. - // - - void clearForcesAndTorques(); - void clearVelocities(); - - void addBaseForce(const btVector3 &f) - { - base_force += f; - } - void addBaseTorque(const btVector3 &t) { base_torque += t; } - void addLinkForce(int i, const btVector3 &f); - void addLinkTorque(int i, const btVector3 &t); - void addJointTorque(int i, btScalar Q); - - const btVector3 & getBaseForce() const { return base_force; } - const btVector3 & getBaseTorque() const { return base_torque; } - const btVector3 & getLinkForce(int i) const; - const btVector3 & getLinkTorque(int i) const; - btScalar getJointTorque(int i) const; - - - // - // dynamics routines. - // - - // timestep the velocities (given the external forces/torques set using addBaseForce etc). - // also sets up caches for calcAccelerationDeltas. - // - // Note: the caller must provide three vectors which are used as - // temporary scratch space. The idea here is to reduce dynamic - // memory allocation: the same scratch vectors can be re-used - // again and again for different Multibodies, instead of each - // btMultiBody allocating (and then deallocating) their own - // individual scratch buffers. This gives a considerable speed - // improvement, at least on Windows (where dynamic memory - // allocation appears to be fairly slow). - // - void stepVelocities(btScalar dt, - btAlignedObjectArray &scratch_r, - btAlignedObjectArray &scratch_v, - btAlignedObjectArray &scratch_m); - - // calcAccelerationDeltas - // input: force vector (in same format as jacobian, i.e.: - // 3 torque values, 3 force values, num_links joint torque values) - // output: 3 omegadot values, 3 vdot values, num_links q_double_dot values - // (existing contents of output array are replaced) - // stepVelocities must have been called first. - void calcAccelerationDeltas(const btScalar *force, btScalar *output, - btAlignedObjectArray &scratch_r, - btAlignedObjectArray &scratch_v) const; - - // apply a delta-vee directly. used in sequential impulses code. - void applyDeltaVee(const btScalar * delta_vee) - { - - for (int i = 0; i < 6 + getNumLinks(); ++i) - { - m_real_buf[i] += delta_vee[i]; - } - - } - void applyDeltaVee(const btScalar * delta_vee, btScalar multiplier) - { - btScalar sum = 0; - for (int i = 0; i < 6 + getNumLinks(); ++i) - { - sum += delta_vee[i]*multiplier*delta_vee[i]*multiplier; - } - btScalar l = btSqrt(sum); - /* - static btScalar maxl = -1e30f; - if (l>maxl) - { - maxl=l; - // printf("maxl=%f\n",maxl); - } - */ - if (l>m_maxAppliedImpulse) - { -// printf("exceeds 100: l=%f\n",maxl); - multiplier *= m_maxAppliedImpulse/l; - } - - for (int i = 0; i < 6 + getNumLinks(); ++i) - { - sum += delta_vee[i]*multiplier*delta_vee[i]*multiplier; - m_real_buf[i] += delta_vee[i] * multiplier; - } - } - - // timestep the positions (given current velocities). - void stepPositions(btScalar dt); - - - // - // contacts - // - - // This routine fills out a contact constraint jacobian for this body. - // the 'normal' supplied must be -n for body1 or +n for body2 of the contact. - // 'normal' & 'contact_point' are both given in world coordinates. - void fillContactJacobian(int link, - const btVector3 &contact_point, - const btVector3 &normal, - btScalar *jac, - btAlignedObjectArray &scratch_r, - btAlignedObjectArray &scratch_v, - btAlignedObjectArray &scratch_m) const; - - - // - // sleeping - // - void setCanSleep(bool canSleep) - { - can_sleep = canSleep; - } - - bool isAwake() const { return awake; } - void wakeUp(); - void goToSleep(); - void checkMotionAndSleepIfRequired(btScalar timestep); - - bool hasFixedBase() const - { - return fixed_base; - } - - int getCompanionId() const - { - return m_companionId; - } - void setCompanionId(int id) - { - //printf("for %p setCompanionId(%d)\n",this, id); - m_companionId = id; - } - - void setNumLinks(int numLinks)//careful: when changing the number of links, make sure to re-initialize or update existing links - { - links.resize(numLinks); - } - - btScalar getLinearDamping() const - { - return m_linearDamping; - } - void setLinearDamping( btScalar damp) - { - m_linearDamping = damp; - } - btScalar getAngularDamping() const - { - return m_angularDamping; - } - - bool getUseGyroTerm() const - { - return m_useGyroTerm; - } - void setUseGyroTerm(bool useGyro) - { - m_useGyroTerm = useGyro; - } - btScalar getMaxAppliedImpulse() const - { - return m_maxAppliedImpulse; - } - void setMaxAppliedImpulse(btScalar maxImp) - { - m_maxAppliedImpulse = maxImp; - } - - void setHasSelfCollision(bool hasSelfCollision) - { - m_hasSelfCollision = hasSelfCollision; - } - bool hasSelfCollision() const - { - return m_hasSelfCollision; - } - -private: - btMultiBody(const btMultiBody &); // not implemented - void operator=(const btMultiBody &); // not implemented - - void compTreeLinkVelocities(btVector3 *omega, btVector3 *vel) const; - - void solveImatrix(const btVector3& rhs_top, const btVector3& rhs_bot, float result[6]) const; - - -private: - - btMultiBodyLinkCollider* m_baseCollider;//can be NULL - - btVector3 base_pos; // position of COM of base (world frame) - btQuaternion base_quat; // rotates world points into base frame - - btScalar base_mass; // mass of the base - btVector3 base_inertia; // inertia of the base (in local frame; diagonal) - - btVector3 base_force; // external force applied to base. World frame. - btVector3 base_torque; // external torque applied to base. World frame. - - btAlignedObjectArray links; // array of links, excluding the base. index from 0 to num_links-1. - btAlignedObjectArray m_colliders; - - // - // real_buf: - // offset size array - // 0 6 + num_links v (base_omega; base_vel; joint_vels) - // 6+num_links num_links D - // - // vector_buf: - // offset size array - // 0 num_links h_top - // num_links num_links h_bottom - // - // matrix_buf: - // offset size array - // 0 num_links+1 rot_from_parent - // - - btAlignedObjectArray m_real_buf; - btAlignedObjectArray vector_buf; - btAlignedObjectArray matrix_buf; - - //std::auto_ptr > > cached_imatrix_lu; - - btMatrix3x3 cached_inertia_top_left; - btMatrix3x3 cached_inertia_top_right; - btMatrix3x3 cached_inertia_lower_left; - btMatrix3x3 cached_inertia_lower_right; - - bool fixed_base; - - // Sleep parameters. - bool awake; - bool can_sleep; - btScalar sleep_timer; - - int m_companionId; - btScalar m_linearDamping; - btScalar m_angularDamping; - bool m_useGyroTerm; - btScalar m_maxAppliedImpulse; - bool m_hasSelfCollision; -}; - -#endif diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp deleted file mode 100644 index 44e04c3a1..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.cpp +++ /dev/null @@ -1,527 +0,0 @@ -#include "btMultiBodyConstraint.h" -#include "BulletDynamics/Dynamics/btRigidBody.h" - -btMultiBodyConstraint::btMultiBodyConstraint(btMultiBody* bodyA,btMultiBody* bodyB,int linkA, int linkB, int numRows, bool isUnilateral) - :m_bodyA(bodyA), - m_bodyB(bodyB), - m_linkA(linkA), - m_linkB(linkB), - m_num_rows(numRows), - m_isUnilateral(isUnilateral), - m_maxAppliedImpulse(100) -{ - m_jac_size_A = (6 + bodyA->getNumLinks()); - m_jac_size_both = (m_jac_size_A + (bodyB ? 6 + bodyB->getNumLinks() : 0)); - m_pos_offset = ((1 + m_jac_size_both)*m_num_rows); - m_data.resize((2 + m_jac_size_both) * m_num_rows); -} - -btMultiBodyConstraint::~btMultiBodyConstraint() -{ -} - - - -btScalar btMultiBodyConstraint::fillConstraintRowMultiBodyMultiBody(btMultiBodySolverConstraint& constraintRow, - btMultiBodyJacobianData& data, - btScalar* jacOrgA,btScalar* jacOrgB, - const btContactSolverInfo& infoGlobal, - btScalar desiredVelocity, - btScalar lowerLimit, - btScalar upperLimit) -{ - - - - constraintRow.m_multiBodyA = m_bodyA; - constraintRow.m_multiBodyB = m_bodyB; - - btMultiBody* multiBodyA = constraintRow.m_multiBodyA; - btMultiBody* multiBodyB = constraintRow.m_multiBodyB; - - if (multiBodyA) - { - - const int ndofA = multiBodyA->getNumLinks() + 6; - - constraintRow.m_deltaVelAindex = multiBodyA->getCompanionId(); - - if (constraintRow.m_deltaVelAindex <0) - { - constraintRow.m_deltaVelAindex = data.m_deltaVelocities.size(); - multiBodyA->setCompanionId(constraintRow.m_deltaVelAindex); - data.m_deltaVelocities.resize(data.m_deltaVelocities.size()+ndofA); - } else - { - btAssert(data.m_deltaVelocities.size() >= constraintRow.m_deltaVelAindex+ndofA); - } - - constraintRow.m_jacAindex = data.m_jacobians.size(); - data.m_jacobians.resize(data.m_jacobians.size()+ndofA); - data.m_deltaVelocitiesUnitImpulse.resize(data.m_deltaVelocitiesUnitImpulse.size()+ndofA); - btAssert(data.m_jacobians.size() == data.m_deltaVelocitiesUnitImpulse.size()); - for (int i=0;icalcAccelerationDeltas(&data.m_jacobians[constraintRow.m_jacAindex],delta,data.scratch_r, data.scratch_v); - } - - if (multiBodyB) - { - const int ndofB = multiBodyB->getNumLinks() + 6; - - constraintRow.m_deltaVelBindex = multiBodyB->getCompanionId(); - if (constraintRow.m_deltaVelBindex <0) - { - constraintRow.m_deltaVelBindex = data.m_deltaVelocities.size(); - multiBodyB->setCompanionId(constraintRow.m_deltaVelBindex); - data.m_deltaVelocities.resize(data.m_deltaVelocities.size()+ndofB); - } - - constraintRow.m_jacBindex = data.m_jacobians.size(); - data.m_jacobians.resize(data.m_jacobians.size()+ndofB); - - for (int i=0;icalcAccelerationDeltas(&data.m_jacobians[constraintRow.m_jacBindex],&data.m_deltaVelocitiesUnitImpulse[constraintRow.m_jacBindex],data.scratch_r, data.scratch_v); - } - { - - btVector3 vec; - btScalar denom0 = 0.f; - btScalar denom1 = 0.f; - btScalar* jacB = 0; - btScalar* jacA = 0; - btScalar* lambdaA =0; - btScalar* lambdaB =0; - int ndofA = 0; - if (multiBodyA) - { - ndofA = multiBodyA->getNumLinks() + 6; - jacA = &data.m_jacobians[constraintRow.m_jacAindex]; - lambdaA = &data.m_deltaVelocitiesUnitImpulse[constraintRow.m_jacAindex]; - for (int i = 0; i < ndofA; ++i) - { - btScalar j = jacA[i] ; - btScalar l =lambdaA[i]; - denom0 += j*l; - } - } - if (multiBodyB) - { - const int ndofB = multiBodyB->getNumLinks() + 6; - jacB = &data.m_jacobians[constraintRow.m_jacBindex]; - lambdaB = &data.m_deltaVelocitiesUnitImpulse[constraintRow.m_jacBindex]; - for (int i = 0; i < ndofB; ++i) - { - btScalar j = jacB[i] ; - btScalar l =lambdaB[i]; - denom1 += j*l; - } - - } - - if (multiBodyA && (multiBodyA==multiBodyB)) - { - // ndof1 == ndof2 in this case - for (int i = 0; i < ndofA; ++i) - { - denom1 += jacB[i] * lambdaA[i]; - denom1 += jacA[i] * lambdaB[i]; - } - } - - btScalar d = denom0+denom1; - if (btFabs(d)>SIMD_EPSILON) - { - - constraintRow.m_jacDiagABInv = 1.f/(d); - } else - { - constraintRow.m_jacDiagABInv = 1.f; - } - - } - - - //compute rhs and remaining constraintRow fields - - - - - btScalar rel_vel = 0.f; - int ndofA = 0; - int ndofB = 0; - { - - btVector3 vel1,vel2; - if (multiBodyA) - { - ndofA = multiBodyA->getNumLinks() + 6; - btScalar* jacA = &data.m_jacobians[constraintRow.m_jacAindex]; - for (int i = 0; i < ndofA ; ++i) - rel_vel += multiBodyA->getVelocityVector()[i] * jacA[i]; - } - if (multiBodyB) - { - ndofB = multiBodyB->getNumLinks() + 6; - btScalar* jacB = &data.m_jacobians[constraintRow.m_jacBindex]; - for (int i = 0; i < ndofB ; ++i) - rel_vel += multiBodyB->getVelocityVector()[i] * jacB[i]; - - } - - constraintRow.m_friction = 0.f; - - constraintRow.m_appliedImpulse = 0.f; - constraintRow.m_appliedPushImpulse = 0.f; - - btScalar velocityError = desiredVelocity - rel_vel;// * damping; - - btScalar erp = infoGlobal.m_erp2; - - btScalar velocityImpulse = velocityError *constraintRow.m_jacDiagABInv; - - if (!infoGlobal.m_splitImpulse) - { - //combine position and velocity into rhs - constraintRow.m_rhs = velocityImpulse; - constraintRow.m_rhsPenetration = 0.f; - - } else - { - //split position and velocity into rhs and m_rhsPenetration - constraintRow.m_rhs = velocityImpulse; - constraintRow.m_rhsPenetration = 0.f; - } - - - constraintRow.m_cfm = 0.f; - constraintRow.m_lowerLimit = lowerLimit; - constraintRow.m_upperLimit = upperLimit; - - } - return rel_vel; -} - - -void btMultiBodyConstraint::applyDeltaVee(btMultiBodyJacobianData& data, btScalar* delta_vee, btScalar impulse, int velocityIndex, int ndof) -{ - for (int i = 0; i < ndof; ++i) - data.m_deltaVelocities[velocityIndex+i] += delta_vee[i] * impulse; -} - - -void btMultiBodyConstraint::fillMultiBodyConstraintMixed(btMultiBodySolverConstraint& solverConstraint, - btMultiBodyJacobianData& data, - const btVector3& contactNormalOnB, - const btVector3& posAworld, const btVector3& posBworld, - btScalar position, - const btContactSolverInfo& infoGlobal, - btScalar& relaxation, - bool isFriction, btScalar desiredVelocity, btScalar cfmSlip) -{ - - - btVector3 rel_pos1 = posAworld; - btVector3 rel_pos2 = posBworld; - - solverConstraint.m_multiBodyA = m_bodyA; - solverConstraint.m_multiBodyB = m_bodyB; - solverConstraint.m_linkA = m_linkA; - solverConstraint.m_linkB = m_linkB; - - - btMultiBody* multiBodyA = solverConstraint.m_multiBodyA; - btMultiBody* multiBodyB = solverConstraint.m_multiBodyB; - - const btVector3& pos1 = posAworld; - const btVector3& pos2 = posBworld; - - btSolverBody* bodyA = multiBodyA ? 0 : &data.m_solverBodyPool->at(solverConstraint.m_solverBodyIdA); - btSolverBody* bodyB = multiBodyB ? 0 : &data.m_solverBodyPool->at(solverConstraint.m_solverBodyIdB); - - btRigidBody* rb0 = multiBodyA ? 0 : bodyA->m_originalBody; - btRigidBody* rb1 = multiBodyB ? 0 : bodyB->m_originalBody; - - if (bodyA) - rel_pos1 = pos1 - bodyA->getWorldTransform().getOrigin(); - if (bodyB) - rel_pos2 = pos2 - bodyB->getWorldTransform().getOrigin(); - - relaxation = 1.f; - - if (multiBodyA) - { - const int ndofA = multiBodyA->getNumLinks() + 6; - - solverConstraint.m_deltaVelAindex = multiBodyA->getCompanionId(); - - if (solverConstraint.m_deltaVelAindex <0) - { - solverConstraint.m_deltaVelAindex = data.m_deltaVelocities.size(); - multiBodyA->setCompanionId(solverConstraint.m_deltaVelAindex); - data.m_deltaVelocities.resize(data.m_deltaVelocities.size()+ndofA); - } else - { - btAssert(data.m_deltaVelocities.size() >= solverConstraint.m_deltaVelAindex+ndofA); - } - - solverConstraint.m_jacAindex = data.m_jacobians.size(); - data.m_jacobians.resize(data.m_jacobians.size()+ndofA); - data.m_deltaVelocitiesUnitImpulse.resize(data.m_deltaVelocitiesUnitImpulse.size()+ndofA); - btAssert(data.m_jacobians.size() == data.m_deltaVelocitiesUnitImpulse.size()); - - btScalar* jac1=&data.m_jacobians[solverConstraint.m_jacAindex]; - multiBodyA->fillContactJacobian(solverConstraint.m_linkA, posAworld, contactNormalOnB, jac1, data.scratch_r, data.scratch_v, data.scratch_m); - btScalar* delta = &data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacAindex]; - multiBodyA->calcAccelerationDeltas(&data.m_jacobians[solverConstraint.m_jacAindex],delta,data.scratch_r, data.scratch_v); - } else - { - btVector3 torqueAxis0 = rel_pos1.cross(contactNormalOnB); - solverConstraint.m_angularComponentA = rb0 ? rb0->getInvInertiaTensorWorld()*torqueAxis0*rb0->getAngularFactor() : btVector3(0,0,0); - solverConstraint.m_relpos1CrossNormal = torqueAxis0; - solverConstraint.m_contactNormal1 = contactNormalOnB; - } - - if (multiBodyB) - { - const int ndofB = multiBodyB->getNumLinks() + 6; - - solverConstraint.m_deltaVelBindex = multiBodyB->getCompanionId(); - if (solverConstraint.m_deltaVelBindex <0) - { - solverConstraint.m_deltaVelBindex = data.m_deltaVelocities.size(); - multiBodyB->setCompanionId(solverConstraint.m_deltaVelBindex); - data.m_deltaVelocities.resize(data.m_deltaVelocities.size()+ndofB); - } - - solverConstraint.m_jacBindex = data.m_jacobians.size(); - - data.m_jacobians.resize(data.m_jacobians.size()+ndofB); - data.m_deltaVelocitiesUnitImpulse.resize(data.m_deltaVelocitiesUnitImpulse.size()+ndofB); - btAssert(data.m_jacobians.size() == data.m_deltaVelocitiesUnitImpulse.size()); - - multiBodyB->fillContactJacobian(solverConstraint.m_linkB, posBworld, -contactNormalOnB, &data.m_jacobians[solverConstraint.m_jacBindex], data.scratch_r, data.scratch_v, data.scratch_m); - multiBodyB->calcAccelerationDeltas(&data.m_jacobians[solverConstraint.m_jacBindex],&data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacBindex],data.scratch_r, data.scratch_v); - } else - { - btVector3 torqueAxis1 = rel_pos2.cross(contactNormalOnB); - solverConstraint.m_angularComponentB = rb1 ? rb1->getInvInertiaTensorWorld()*-torqueAxis1*rb1->getAngularFactor() : btVector3(0,0,0); - solverConstraint.m_relpos2CrossNormal = -torqueAxis1; - solverConstraint.m_contactNormal2 = -contactNormalOnB; - } - - { - - btVector3 vec; - btScalar denom0 = 0.f; - btScalar denom1 = 0.f; - btScalar* jacB = 0; - btScalar* jacA = 0; - btScalar* lambdaA =0; - btScalar* lambdaB =0; - int ndofA = 0; - if (multiBodyA) - { - ndofA = multiBodyA->getNumLinks() + 6; - jacA = &data.m_jacobians[solverConstraint.m_jacAindex]; - lambdaA = &data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacAindex]; - for (int i = 0; i < ndofA; ++i) - { - btScalar j = jacA[i] ; - btScalar l =lambdaA[i]; - denom0 += j*l; - } - } else - { - if (rb0) - { - vec = ( solverConstraint.m_angularComponentA).cross(rel_pos1); - denom0 = rb0->getInvMass() + contactNormalOnB.dot(vec); - } - } - if (multiBodyB) - { - const int ndofB = multiBodyB->getNumLinks() + 6; - jacB = &data.m_jacobians[solverConstraint.m_jacBindex]; - lambdaB = &data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacBindex]; - for (int i = 0; i < ndofB; ++i) - { - btScalar j = jacB[i] ; - btScalar l =lambdaB[i]; - denom1 += j*l; - } - - } else - { - if (rb1) - { - vec = ( -solverConstraint.m_angularComponentB).cross(rel_pos2); - denom1 = rb1->getInvMass() + contactNormalOnB.dot(vec); - } - } - - if (multiBodyA && (multiBodyA==multiBodyB)) - { - // ndof1 == ndof2 in this case - for (int i = 0; i < ndofA; ++i) - { - denom1 += jacB[i] * lambdaA[i]; - denom1 += jacA[i] * lambdaB[i]; - } - } - - btScalar d = denom0+denom1; - if (btFabs(d)>SIMD_EPSILON) - { - - solverConstraint.m_jacDiagABInv = relaxation/(d); - } else - { - solverConstraint.m_jacDiagABInv = 1.f; - } - - } - - - //compute rhs and remaining solverConstraint fields - - - - btScalar restitution = 0.f; - btScalar penetration = isFriction? 0 : position+infoGlobal.m_linearSlop; - - btScalar rel_vel = 0.f; - int ndofA = 0; - int ndofB = 0; - { - - btVector3 vel1,vel2; - if (multiBodyA) - { - ndofA = multiBodyA->getNumLinks() + 6; - btScalar* jacA = &data.m_jacobians[solverConstraint.m_jacAindex]; - for (int i = 0; i < ndofA ; ++i) - rel_vel += multiBodyA->getVelocityVector()[i] * jacA[i]; - } else - { - if (rb0) - { - rel_vel += rb0->getVelocityInLocalPoint(rel_pos1).dot(solverConstraint.m_contactNormal1); - } - } - if (multiBodyB) - { - ndofB = multiBodyB->getNumLinks() + 6; - btScalar* jacB = &data.m_jacobians[solverConstraint.m_jacBindex]; - for (int i = 0; i < ndofB ; ++i) - rel_vel += multiBodyB->getVelocityVector()[i] * jacB[i]; - - } else - { - if (rb1) - { - rel_vel += rb1->getVelocityInLocalPoint(rel_pos2).dot(solverConstraint.m_contactNormal2); - } - } - - solverConstraint.m_friction = 0.f;//cp.m_combinedFriction; - - - restitution = restitution * -rel_vel;//restitutionCurve(rel_vel, cp.m_combinedRestitution); - if (restitution <= btScalar(0.)) - { - restitution = 0.f; - }; - } - - - ///warm starting (or zero if disabled) - /* - if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING) - { - solverConstraint.m_appliedImpulse = isFriction ? 0 : cp.m_appliedImpulse * infoGlobal.m_warmstartingFactor; - - if (solverConstraint.m_appliedImpulse) - { - if (multiBodyA) - { - btScalar impulse = solverConstraint.m_appliedImpulse; - btScalar* deltaV = &data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacAindex]; - multiBodyA->applyDeltaVee(deltaV,impulse); - applyDeltaVee(data,deltaV,impulse,solverConstraint.m_deltaVelAindex,ndofA); - } else - { - if (rb0) - bodyA->internalApplyImpulse(solverConstraint.m_contactNormal1*bodyA->internalGetInvMass()*rb0->getLinearFactor(),solverConstraint.m_angularComponentA,solverConstraint.m_appliedImpulse); - } - if (multiBodyB) - { - btScalar impulse = solverConstraint.m_appliedImpulse; - btScalar* deltaV = &data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacBindex]; - multiBodyB->applyDeltaVee(deltaV,impulse); - applyDeltaVee(data,deltaV,impulse,solverConstraint.m_deltaVelBindex,ndofB); - } else - { - if (rb1) - bodyB->internalApplyImpulse(-solverConstraint.m_contactNormal2*bodyB->internalGetInvMass()*rb1->getLinearFactor(),-solverConstraint.m_angularComponentB,-(btScalar)solverConstraint.m_appliedImpulse); - } - } - } else - */ - { - solverConstraint.m_appliedImpulse = 0.f; - } - - solverConstraint.m_appliedPushImpulse = 0.f; - - { - - - btScalar positionalError = 0.f; - btScalar velocityError = restitution - rel_vel;// * damping; - - - btScalar erp = infoGlobal.m_erp2; - if (!infoGlobal.m_splitImpulse || (penetration > infoGlobal.m_splitImpulsePenetrationThreshold)) - { - erp = infoGlobal.m_erp; - } - - if (penetration>0) - { - positionalError = 0; - velocityError = -penetration / infoGlobal.m_timeStep; - - } else - { - positionalError = -penetration * erp/infoGlobal.m_timeStep; - } - - btScalar penetrationImpulse = positionalError*solverConstraint.m_jacDiagABInv; - btScalar velocityImpulse = velocityError *solverConstraint.m_jacDiagABInv; - - if (!infoGlobal.m_splitImpulse || (penetration > infoGlobal.m_splitImpulsePenetrationThreshold)) - { - //combine position and velocity into rhs - solverConstraint.m_rhs = penetrationImpulse+velocityImpulse; - solverConstraint.m_rhsPenetration = 0.f; - - } else - { - //split position and velocity into rhs and m_rhsPenetration - solverConstraint.m_rhs = velocityImpulse; - solverConstraint.m_rhsPenetration = penetrationImpulse; - } - - solverConstraint.m_cfm = 0.f; - solverConstraint.m_lowerLimit = -m_maxAppliedImpulse; - solverConstraint.m_upperLimit = m_maxAppliedImpulse; - } - -} diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.h b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.h deleted file mode 100644 index 9fa317330..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraint.h +++ /dev/null @@ -1,166 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2013 Erwin Coumans http://bulletphysics.org - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef BT_MULTIBODY_CONSTRAINT_H -#define BT_MULTIBODY_CONSTRAINT_H - -#include "LinearMath/btScalar.h" -#include "LinearMath/btAlignedObjectArray.h" -#include "btMultiBody.h" - -class btMultiBody; -struct btSolverInfo; - -#include "btMultiBodySolverConstraint.h" - -struct btMultiBodyJacobianData -{ - btAlignedObjectArray m_jacobians; - btAlignedObjectArray m_deltaVelocitiesUnitImpulse; - btAlignedObjectArray m_deltaVelocities; - btAlignedObjectArray scratch_r; - btAlignedObjectArray scratch_v; - btAlignedObjectArray scratch_m; - btAlignedObjectArray* m_solverBodyPool; - int m_fixedBodyId; - -}; - - -class btMultiBodyConstraint -{ -protected: - - btMultiBody* m_bodyA; - btMultiBody* m_bodyB; - int m_linkA; - int m_linkB; - - int m_num_rows; - int m_jac_size_A; - int m_jac_size_both; - int m_pos_offset; - - bool m_isUnilateral; - - btScalar m_maxAppliedImpulse; - - - // data block laid out as follows: - // cached impulses. (one per row.) - // jacobians. (interleaved, row1 body1 then row1 body2 then row2 body 1 etc) - // positions. (one per row.) - btAlignedObjectArray m_data; - - void applyDeltaVee(btMultiBodyJacobianData& data, btScalar* delta_vee, btScalar impulse, int velocityIndex, int ndof); - - void fillMultiBodyConstraintMixed(btMultiBodySolverConstraint& solverConstraint, - btMultiBodyJacobianData& data, - const btVector3& contactNormalOnB, - const btVector3& posAworld, const btVector3& posBworld, - btScalar position, - const btContactSolverInfo& infoGlobal, - btScalar& relaxation, - bool isFriction, btScalar desiredVelocity=0, btScalar cfmSlip=0); - - btScalar fillConstraintRowMultiBodyMultiBody(btMultiBodySolverConstraint& constraintRow, - btMultiBodyJacobianData& data, - btScalar* jacOrgA,btScalar* jacOrgB, - const btContactSolverInfo& infoGlobal, - btScalar desiredVelocity, - btScalar lowerLimit, - btScalar upperLimit); - -public: - - btMultiBodyConstraint(btMultiBody* bodyA,btMultiBody* bodyB,int linkA, int linkB, int numRows, bool isUnilateral); - virtual ~btMultiBodyConstraint(); - - - - virtual int getIslandIdA() const =0; - virtual int getIslandIdB() const =0; - - virtual void createConstraintRows(btMultiBodyConstraintArray& constraintRows, - btMultiBodyJacobianData& data, - const btContactSolverInfo& infoGlobal)=0; - - int getNumRows() const - { - return m_num_rows; - } - - btMultiBody* getMultiBodyA() - { - return m_bodyA; - } - btMultiBody* getMultiBodyB() - { - return m_bodyB; - } - - // current constraint position - // constraint is pos >= 0 for unilateral, or pos = 0 for bilateral - // NOTE: ignored position for friction rows. - btScalar getPosition(int row) const - { - return m_data[m_pos_offset + row]; - } - - void setPosition(int row, btScalar pos) - { - m_data[m_pos_offset + row] = pos; - } - - - bool isUnilateral() const - { - return m_isUnilateral; - } - - // jacobian blocks. - // each of size 6 + num_links. (jacobian2 is null if no body2.) - // format: 3 'omega' coefficients, 3 'v' coefficients, then the 'qdot' coefficients. - btScalar* jacobianA(int row) - { - return &m_data[m_num_rows + row * m_jac_size_both]; - } - const btScalar* jacobianA(int row) const - { - return &m_data[m_num_rows + (row * m_jac_size_both)]; - } - btScalar* jacobianB(int row) - { - return &m_data[m_num_rows + (row * m_jac_size_both) + m_jac_size_A]; - } - const btScalar* jacobianB(int row) const - { - return &m_data[m_num_rows + (row * m_jac_size_both) + m_jac_size_A]; - } - - btScalar getMaxAppliedImpulse() const - { - return m_maxAppliedImpulse; - } - void setMaxAppliedImpulse(btScalar maxImp) - { - m_maxAppliedImpulse = maxImp; - } - - -}; - -#endif //BT_MULTIBODY_CONSTRAINT_H - diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp deleted file mode 100644 index 577f84622..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.cpp +++ /dev/null @@ -1,795 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2013 Erwin Coumans http://bulletphysics.org - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#include "btMultiBodyConstraintSolver.h" -#include "BulletCollision/NarrowPhaseCollision/btPersistentManifold.h" -#include "btMultiBodyLinkCollider.h" - -#include "BulletDynamics/ConstraintSolver/btSolverBody.h" -#include "btMultiBodyConstraint.h" -#include "BulletDynamics/ConstraintSolver/btContactSolverInfo.h" - -#include "LinearMath/btQuickprof.h" - -btScalar btMultiBodyConstraintSolver::solveSingleIteration(int iteration, btCollisionObject** bodies ,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer) -{ - btScalar val = btSequentialImpulseConstraintSolver::solveSingleIteration(iteration, bodies ,numBodies,manifoldPtr, numManifolds,constraints,numConstraints,infoGlobal,debugDrawer); - - //solve featherstone non-contact constraints - - //printf("m_multiBodyNonContactConstraints = %d\n",m_multiBodyNonContactConstraints.size()); - for (int j=0;jm_multiBodyFrictionContactConstraints.size();j++) - { - if (iteration < infoGlobal.m_numIterations) - { - btMultiBodySolverConstraint& frictionConstraint = m_multiBodyFrictionContactConstraints[j]; - btScalar totalImpulse = m_multiBodyNormalContactConstraints[frictionConstraint.m_frictionIndex].m_appliedImpulse; - //adjust friction limits here - if (totalImpulse>btScalar(0)) - { - frictionConstraint.m_lowerLimit = -(frictionConstraint.m_friction*totalImpulse); - frictionConstraint.m_upperLimit = frictionConstraint.m_friction*totalImpulse; - resolveSingleConstraintRowGeneric(frictionConstraint); - } - } - } - return val; -} - -btScalar btMultiBodyConstraintSolver::solveGroupCacheFriendlySetup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer) -{ - m_multiBodyNonContactConstraints.resize(0); - m_multiBodyNormalContactConstraints.resize(0); - m_multiBodyFrictionContactConstraints.resize(0); - m_data.m_jacobians.resize(0); - m_data.m_deltaVelocitiesUnitImpulse.resize(0); - m_data.m_deltaVelocities.resize(0); - - for (int i=0;im_multiBody->setCompanionId(-1); - } - } - - btScalar val = btSequentialImpulseConstraintSolver::solveGroupCacheFriendlySetup( bodies,numBodies,manifoldPtr, numManifolds, constraints,numConstraints,infoGlobal,debugDrawer); - - return val; -} - -void btMultiBodyConstraintSolver::applyDeltaVee(btScalar* delta_vee, btScalar impulse, int velocityIndex, int ndof) -{ - for (int i = 0; i < ndof; ++i) - m_data.m_deltaVelocities[velocityIndex+i] += delta_vee[i] * impulse; -} - -void btMultiBodyConstraintSolver::resolveSingleConstraintRowGeneric(const btMultiBodySolverConstraint& c) -{ - - btScalar deltaImpulse = c.m_rhs-btScalar(c.m_appliedImpulse)*c.m_cfm; - btScalar deltaVelADotn=0; - btScalar deltaVelBDotn=0; - btSolverBody* bodyA = 0; - btSolverBody* bodyB = 0; - int ndofA=0; - int ndofB=0; - - if (c.m_multiBodyA) - { - ndofA = c.m_multiBodyA->getNumLinks() + 6; - for (int i = 0; i < ndofA; ++i) - deltaVelADotn += m_data.m_jacobians[c.m_jacAindex+i] * m_data.m_deltaVelocities[c.m_deltaVelAindex+i]; - } else - { - bodyA = &m_tmpSolverBodyPool[c.m_solverBodyIdA]; - deltaVelADotn += c.m_contactNormal1.dot(bodyA->internalGetDeltaLinearVelocity()) + c.m_relpos1CrossNormal.dot(bodyA->internalGetDeltaAngularVelocity()); - } - - if (c.m_multiBodyB) - { - ndofB = c.m_multiBodyB->getNumLinks() + 6; - for (int i = 0; i < ndofB; ++i) - deltaVelBDotn += m_data.m_jacobians[c.m_jacBindex+i] * m_data.m_deltaVelocities[c.m_deltaVelBindex+i]; - } else - { - bodyB = &m_tmpSolverBodyPool[c.m_solverBodyIdB]; - deltaVelBDotn += c.m_contactNormal2.dot(bodyB->internalGetDeltaLinearVelocity()) + c.m_relpos2CrossNormal.dot(bodyB->internalGetDeltaAngularVelocity()); - } - - - deltaImpulse -= deltaVelADotn*c.m_jacDiagABInv;//m_jacDiagABInv = 1./denom - deltaImpulse -= deltaVelBDotn*c.m_jacDiagABInv; - const btScalar sum = btScalar(c.m_appliedImpulse) + deltaImpulse; - - if (sum < c.m_lowerLimit) - { - deltaImpulse = c.m_lowerLimit-c.m_appliedImpulse; - c.m_appliedImpulse = c.m_lowerLimit; - } - else if (sum > c.m_upperLimit) - { - deltaImpulse = c.m_upperLimit-c.m_appliedImpulse; - c.m_appliedImpulse = c.m_upperLimit; - } - else - { - c.m_appliedImpulse = sum; - } - - if (c.m_multiBodyA) - { - applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex],deltaImpulse,c.m_deltaVelAindex,ndofA); - c.m_multiBodyA->applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex],deltaImpulse); - } else - { - bodyA->internalApplyImpulse(c.m_contactNormal1*bodyA->internalGetInvMass(),c.m_angularComponentA,deltaImpulse); - - } - if (c.m_multiBodyB) - { - applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex],deltaImpulse,c.m_deltaVelBindex,ndofB); - c.m_multiBodyB->applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex],deltaImpulse); - } else - { - bodyB->internalApplyImpulse(c.m_contactNormal2*bodyB->internalGetInvMass(),c.m_angularComponentB,deltaImpulse); - } - -} - - -void btMultiBodyConstraintSolver::resolveSingleConstraintRowGenericMultiBody(const btMultiBodySolverConstraint& c) -{ - - btScalar deltaImpulse = c.m_rhs-btScalar(c.m_appliedImpulse)*c.m_cfm; - btScalar deltaVelADotn=0; - btScalar deltaVelBDotn=0; - int ndofA=0; - int ndofB=0; - - if (c.m_multiBodyA) - { - ndofA = c.m_multiBodyA->getNumLinks() + 6; - for (int i = 0; i < ndofA; ++i) - deltaVelADotn += m_data.m_jacobians[c.m_jacAindex+i] * m_data.m_deltaVelocities[c.m_deltaVelAindex+i]; - } - - if (c.m_multiBodyB) - { - ndofB = c.m_multiBodyB->getNumLinks() + 6; - for (int i = 0; i < ndofB; ++i) - deltaVelBDotn += m_data.m_jacobians[c.m_jacBindex+i] * m_data.m_deltaVelocities[c.m_deltaVelBindex+i]; - } - - - deltaImpulse -= deltaVelADotn*c.m_jacDiagABInv;//m_jacDiagABInv = 1./denom - deltaImpulse -= deltaVelBDotn*c.m_jacDiagABInv; - const btScalar sum = btScalar(c.m_appliedImpulse) + deltaImpulse; - - if (sum < c.m_lowerLimit) - { - deltaImpulse = c.m_lowerLimit-c.m_appliedImpulse; - c.m_appliedImpulse = c.m_lowerLimit; - } - else if (sum > c.m_upperLimit) - { - deltaImpulse = c.m_upperLimit-c.m_appliedImpulse; - c.m_appliedImpulse = c.m_upperLimit; - } - else - { - c.m_appliedImpulse = sum; - } - - if (c.m_multiBodyA) - { - applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex],deltaImpulse,c.m_deltaVelAindex,ndofA); - c.m_multiBodyA->applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacAindex],deltaImpulse); - } - if (c.m_multiBodyB) - { - applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex],deltaImpulse,c.m_deltaVelBindex,ndofB); - c.m_multiBodyB->applyDeltaVee(&m_data.m_deltaVelocitiesUnitImpulse[c.m_jacBindex],deltaImpulse); - } -} - - -void btMultiBodyConstraintSolver::setupMultiBodyContactConstraint(btMultiBodySolverConstraint& solverConstraint, - const btVector3& contactNormal, - btManifoldPoint& cp, const btContactSolverInfo& infoGlobal, - btScalar& relaxation, - bool isFriction, btScalar desiredVelocity, btScalar cfmSlip) -{ - - BT_PROFILE("setupMultiBodyContactConstraint"); - btVector3 rel_pos1; - btVector3 rel_pos2; - - btMultiBody* multiBodyA = solverConstraint.m_multiBodyA; - btMultiBody* multiBodyB = solverConstraint.m_multiBodyB; - - const btVector3& pos1 = cp.getPositionWorldOnA(); - const btVector3& pos2 = cp.getPositionWorldOnB(); - - btSolverBody* bodyA = multiBodyA ? 0 : &m_tmpSolverBodyPool[solverConstraint.m_solverBodyIdA]; - btSolverBody* bodyB = multiBodyB ? 0 : &m_tmpSolverBodyPool[solverConstraint.m_solverBodyIdB]; - - btRigidBody* rb0 = multiBodyA ? 0 : bodyA->m_originalBody; - btRigidBody* rb1 = multiBodyB ? 0 : bodyB->m_originalBody; - - if (bodyA) - rel_pos1 = pos1 - bodyA->getWorldTransform().getOrigin(); - if (bodyB) - rel_pos2 = pos2 - bodyB->getWorldTransform().getOrigin(); - - relaxation = 1.f; - - if (multiBodyA) - { - const int ndofA = multiBodyA->getNumLinks() + 6; - - solverConstraint.m_deltaVelAindex = multiBodyA->getCompanionId(); - - if (solverConstraint.m_deltaVelAindex <0) - { - solverConstraint.m_deltaVelAindex = m_data.m_deltaVelocities.size(); - multiBodyA->setCompanionId(solverConstraint.m_deltaVelAindex); - m_data.m_deltaVelocities.resize(m_data.m_deltaVelocities.size()+ndofA); - } else - { - btAssert(m_data.m_deltaVelocities.size() >= solverConstraint.m_deltaVelAindex+ndofA); - } - - solverConstraint.m_jacAindex = m_data.m_jacobians.size(); - m_data.m_jacobians.resize(m_data.m_jacobians.size()+ndofA); - m_data.m_deltaVelocitiesUnitImpulse.resize(m_data.m_deltaVelocitiesUnitImpulse.size()+ndofA); - btAssert(m_data.m_jacobians.size() == m_data.m_deltaVelocitiesUnitImpulse.size()); - - btScalar* jac1=&m_data.m_jacobians[solverConstraint.m_jacAindex]; - multiBodyA->fillContactJacobian(solverConstraint.m_linkA, cp.getPositionWorldOnA(), contactNormal, jac1, m_data.scratch_r, m_data.scratch_v, m_data.scratch_m); - btScalar* delta = &m_data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacAindex]; - multiBodyA->calcAccelerationDeltas(&m_data.m_jacobians[solverConstraint.m_jacAindex],delta,m_data.scratch_r, m_data.scratch_v); - } else - { - btVector3 torqueAxis0 = rel_pos1.cross(contactNormal); - solverConstraint.m_angularComponentA = rb0 ? rb0->getInvInertiaTensorWorld()*torqueAxis0*rb0->getAngularFactor() : btVector3(0,0,0); - solverConstraint.m_relpos1CrossNormal = torqueAxis0; - solverConstraint.m_contactNormal1 = contactNormal; - } - - if (multiBodyB) - { - const int ndofB = multiBodyB->getNumLinks() + 6; - - solverConstraint.m_deltaVelBindex = multiBodyB->getCompanionId(); - if (solverConstraint.m_deltaVelBindex <0) - { - solverConstraint.m_deltaVelBindex = m_data.m_deltaVelocities.size(); - multiBodyB->setCompanionId(solverConstraint.m_deltaVelBindex); - m_data.m_deltaVelocities.resize(m_data.m_deltaVelocities.size()+ndofB); - } - - solverConstraint.m_jacBindex = m_data.m_jacobians.size(); - - m_data.m_jacobians.resize(m_data.m_jacobians.size()+ndofB); - m_data.m_deltaVelocitiesUnitImpulse.resize(m_data.m_deltaVelocitiesUnitImpulse.size()+ndofB); - btAssert(m_data.m_jacobians.size() == m_data.m_deltaVelocitiesUnitImpulse.size()); - - multiBodyB->fillContactJacobian(solverConstraint.m_linkB, cp.getPositionWorldOnB(), -contactNormal, &m_data.m_jacobians[solverConstraint.m_jacBindex], m_data.scratch_r, m_data.scratch_v, m_data.scratch_m); - multiBodyB->calcAccelerationDeltas(&m_data.m_jacobians[solverConstraint.m_jacBindex],&m_data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacBindex],m_data.scratch_r, m_data.scratch_v); - } else - { - btVector3 torqueAxis1 = rel_pos2.cross(contactNormal); - solverConstraint.m_angularComponentB = rb1 ? rb1->getInvInertiaTensorWorld()*-torqueAxis1*rb1->getAngularFactor() : btVector3(0,0,0); - solverConstraint.m_relpos2CrossNormal = -torqueAxis1; - solverConstraint.m_contactNormal2 = -contactNormal; - } - - { - - btVector3 vec; - btScalar denom0 = 0.f; - btScalar denom1 = 0.f; - btScalar* jacB = 0; - btScalar* jacA = 0; - btScalar* lambdaA =0; - btScalar* lambdaB =0; - int ndofA = 0; - if (multiBodyA) - { - ndofA = multiBodyA->getNumLinks() + 6; - jacA = &m_data.m_jacobians[solverConstraint.m_jacAindex]; - lambdaA = &m_data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacAindex]; - for (int i = 0; i < ndofA; ++i) - { - btScalar j = jacA[i] ; - btScalar l =lambdaA[i]; - denom0 += j*l; - } - } else - { - if (rb0) - { - vec = ( solverConstraint.m_angularComponentA).cross(rel_pos1); - denom0 = rb0->getInvMass() + contactNormal.dot(vec); - } - } - if (multiBodyB) - { - const int ndofB = multiBodyB->getNumLinks() + 6; - jacB = &m_data.m_jacobians[solverConstraint.m_jacBindex]; - lambdaB = &m_data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacBindex]; - for (int i = 0; i < ndofB; ++i) - { - btScalar j = jacB[i] ; - btScalar l =lambdaB[i]; - denom1 += j*l; - } - - } else - { - if (rb1) - { - vec = ( -solverConstraint.m_angularComponentB).cross(rel_pos2); - denom1 = rb1->getInvMass() + contactNormal.dot(vec); - } - } - - if (multiBodyA && (multiBodyA==multiBodyB)) - { - // ndof1 == ndof2 in this case - for (int i = 0; i < ndofA; ++i) - { - denom1 += jacB[i] * lambdaA[i]; - denom1 += jacA[i] * lambdaB[i]; - } - } - - btScalar d = denom0+denom1; - if (btFabs(d)>SIMD_EPSILON) - { - - solverConstraint.m_jacDiagABInv = relaxation/(d); - } else - { - solverConstraint.m_jacDiagABInv = 1.f; - } - - } - - - //compute rhs and remaining solverConstraint fields - - - - btScalar restitution = 0.f; - btScalar penetration = isFriction? 0 : cp.getDistance()+infoGlobal.m_linearSlop; - - btScalar rel_vel = 0.f; - int ndofA = 0; - int ndofB = 0; - { - - btVector3 vel1,vel2; - if (multiBodyA) - { - ndofA = multiBodyA->getNumLinks() + 6; - btScalar* jacA = &m_data.m_jacobians[solverConstraint.m_jacAindex]; - for (int i = 0; i < ndofA ; ++i) - rel_vel += multiBodyA->getVelocityVector()[i] * jacA[i]; - } else - { - if (rb0) - { - rel_vel += rb0->getVelocityInLocalPoint(rel_pos1).dot(solverConstraint.m_contactNormal1); - } - } - if (multiBodyB) - { - ndofB = multiBodyB->getNumLinks() + 6; - btScalar* jacB = &m_data.m_jacobians[solverConstraint.m_jacBindex]; - for (int i = 0; i < ndofB ; ++i) - rel_vel += multiBodyB->getVelocityVector()[i] * jacB[i]; - - } else - { - if (rb1) - { - rel_vel += rb1->getVelocityInLocalPoint(rel_pos2).dot(solverConstraint.m_contactNormal2); - } - } - - solverConstraint.m_friction = cp.m_combinedFriction; - - - restitution = restitutionCurve(rel_vel, cp.m_combinedRestitution); - if (restitution <= btScalar(0.)) - { - restitution = 0.f; - }; - } - - - ///warm starting (or zero if disabled) - if (infoGlobal.m_solverMode & SOLVER_USE_WARMSTARTING) - { - solverConstraint.m_appliedImpulse = isFriction ? 0 : cp.m_appliedImpulse * infoGlobal.m_warmstartingFactor; - - if (solverConstraint.m_appliedImpulse) - { - if (multiBodyA) - { - btScalar impulse = solverConstraint.m_appliedImpulse; - btScalar* deltaV = &m_data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacAindex]; - multiBodyA->applyDeltaVee(deltaV,impulse); - applyDeltaVee(deltaV,impulse,solverConstraint.m_deltaVelAindex,ndofA); - } else - { - if (rb0) - bodyA->internalApplyImpulse(solverConstraint.m_contactNormal1*bodyA->internalGetInvMass()*rb0->getLinearFactor(),solverConstraint.m_angularComponentA,solverConstraint.m_appliedImpulse); - } - if (multiBodyB) - { - btScalar impulse = solverConstraint.m_appliedImpulse; - btScalar* deltaV = &m_data.m_deltaVelocitiesUnitImpulse[solverConstraint.m_jacBindex]; - multiBodyB->applyDeltaVee(deltaV,impulse); - applyDeltaVee(deltaV,impulse,solverConstraint.m_deltaVelBindex,ndofB); - } else - { - if (rb1) - bodyB->internalApplyImpulse(-solverConstraint.m_contactNormal2*bodyB->internalGetInvMass()*rb1->getLinearFactor(),-solverConstraint.m_angularComponentB,-(btScalar)solverConstraint.m_appliedImpulse); - } - } - } else - { - solverConstraint.m_appliedImpulse = 0.f; - } - - solverConstraint.m_appliedPushImpulse = 0.f; - - { - - - btScalar positionalError = 0.f; - btScalar velocityError = restitution - rel_vel;// * damping; - - - btScalar erp = infoGlobal.m_erp2; - if (!infoGlobal.m_splitImpulse || (penetration > infoGlobal.m_splitImpulsePenetrationThreshold)) - { - erp = infoGlobal.m_erp; - } - - if (penetration>0) - { - positionalError = 0; - velocityError = -penetration / infoGlobal.m_timeStep; - - } else - { - positionalError = -penetration * erp/infoGlobal.m_timeStep; - } - - btScalar penetrationImpulse = positionalError*solverConstraint.m_jacDiagABInv; - btScalar velocityImpulse = velocityError *solverConstraint.m_jacDiagABInv; - - if (!infoGlobal.m_splitImpulse || (penetration > infoGlobal.m_splitImpulsePenetrationThreshold)) - { - //combine position and velocity into rhs - solverConstraint.m_rhs = penetrationImpulse+velocityImpulse; - solverConstraint.m_rhsPenetration = 0.f; - - } else - { - //split position and velocity into rhs and m_rhsPenetration - solverConstraint.m_rhs = velocityImpulse; - solverConstraint.m_rhsPenetration = penetrationImpulse; - } - - solverConstraint.m_cfm = 0.f; - solverConstraint.m_lowerLimit = 0; - solverConstraint.m_upperLimit = 1e10f; - } - -} - - - - -btMultiBodySolverConstraint& btMultiBodyConstraintSolver::addMultiBodyFrictionConstraint(const btVector3& normalAxis,btPersistentManifold* manifold,int frictionIndex,btManifoldPoint& cp,btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity, btScalar cfmSlip) -{ - BT_PROFILE("addMultiBodyFrictionConstraint"); - btMultiBodySolverConstraint& solverConstraint = m_multiBodyFrictionContactConstraints.expandNonInitializing(); - solverConstraint.m_frictionIndex = frictionIndex; - bool isFriction = true; - - const btMultiBodyLinkCollider* fcA = btMultiBodyLinkCollider::upcast(manifold->getBody0()); - const btMultiBodyLinkCollider* fcB = btMultiBodyLinkCollider::upcast(manifold->getBody1()); - - btMultiBody* mbA = fcA? fcA->m_multiBody : 0; - btMultiBody* mbB = fcB? fcB->m_multiBody : 0; - - int solverBodyIdA = mbA? -1 : getOrInitSolverBody(*colObj0,infoGlobal.m_timeStep); - int solverBodyIdB = mbB ? -1 : getOrInitSolverBody(*colObj1,infoGlobal.m_timeStep); - - solverConstraint.m_solverBodyIdA = solverBodyIdA; - solverConstraint.m_solverBodyIdB = solverBodyIdB; - solverConstraint.m_multiBodyA = mbA; - if (mbA) - solverConstraint.m_linkA = fcA->m_link; - - solverConstraint.m_multiBodyB = mbB; - if (mbB) - solverConstraint.m_linkB = fcB->m_link; - - solverConstraint.m_originalContactPoint = &cp; - - setupMultiBodyContactConstraint(solverConstraint, normalAxis, cp, infoGlobal,relaxation,isFriction, desiredVelocity, cfmSlip); - return solverConstraint; -} - -void btMultiBodyConstraintSolver::convertMultiBodyContact(btPersistentManifold* manifold,const btContactSolverInfo& infoGlobal) -{ - const btMultiBodyLinkCollider* fcA = btMultiBodyLinkCollider::upcast(manifold->getBody0()); - const btMultiBodyLinkCollider* fcB = btMultiBodyLinkCollider::upcast(manifold->getBody1()); - - btMultiBody* mbA = fcA? fcA->m_multiBody : 0; - btMultiBody* mbB = fcB? fcB->m_multiBody : 0; - - btCollisionObject* colObj0=0,*colObj1=0; - - colObj0 = (btCollisionObject*)manifold->getBody0(); - colObj1 = (btCollisionObject*)manifold->getBody1(); - - int solverBodyIdA = mbA? -1 : getOrInitSolverBody(*colObj0,infoGlobal.m_timeStep); - int solverBodyIdB = mbB ? -1 : getOrInitSolverBody(*colObj1,infoGlobal.m_timeStep); - - btSolverBody* solverBodyA = mbA ? 0 : &m_tmpSolverBodyPool[solverBodyIdA]; - btSolverBody* solverBodyB = mbB ? 0 : &m_tmpSolverBodyPool[solverBodyIdB]; - - - ///avoid collision response between two static objects -// if (!solverBodyA || (solverBodyA->m_invMass.isZero() && (!solverBodyB || solverBodyB->m_invMass.isZero()))) - // return; - - int rollingFriction=1; - - for (int j=0;jgetNumContacts();j++) - { - - btManifoldPoint& cp = manifold->getContactPoint(j); - - if (cp.getDistance() <= manifold->getContactProcessingThreshold()) - { - - btScalar relaxation; - - int frictionIndex = m_multiBodyNormalContactConstraints.size(); - - btMultiBodySolverConstraint& solverConstraint = m_multiBodyNormalContactConstraints.expandNonInitializing(); - - btRigidBody* rb0 = btRigidBody::upcast(colObj0); - btRigidBody* rb1 = btRigidBody::upcast(colObj1); - solverConstraint.m_solverBodyIdA = solverBodyIdA; - solverConstraint.m_solverBodyIdB = solverBodyIdB; - solverConstraint.m_multiBodyA = mbA; - if (mbA) - solverConstraint.m_linkA = fcA->m_link; - - solverConstraint.m_multiBodyB = mbB; - if (mbB) - solverConstraint.m_linkB = fcB->m_link; - - solverConstraint.m_originalContactPoint = &cp; - - bool isFriction = false; - setupMultiBodyContactConstraint(solverConstraint, cp.m_normalWorldOnB,cp, infoGlobal, relaxation, isFriction); - -// const btVector3& pos1 = cp.getPositionWorldOnA(); -// const btVector3& pos2 = cp.getPositionWorldOnB(); - - /////setup the friction constraints -#define ENABLE_FRICTION -#ifdef ENABLE_FRICTION - solverConstraint.m_frictionIndex = frictionIndex; -#if ROLLING_FRICTION - btVector3 angVelA(0,0,0),angVelB(0,0,0); - if (rb0) - angVelA = rb0->getAngularVelocity(); - if (rb1) - angVelB = rb1->getAngularVelocity(); - btVector3 relAngVel = angVelB-angVelA; - - if ((cp.m_combinedRollingFriction>0.f) && (rollingFriction>0)) - { - //only a single rollingFriction per manifold - rollingFriction--; - if (relAngVel.length()>infoGlobal.m_singleAxisRollingFrictionThreshold) - { - relAngVel.normalize(); - applyAnisotropicFriction(colObj0,relAngVel,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION); - applyAnisotropicFriction(colObj1,relAngVel,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION); - if (relAngVel.length()>0.001) - addRollingFrictionConstraint(relAngVel,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); - - } else - { - addRollingFrictionConstraint(cp.m_normalWorldOnB,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); - btVector3 axis0,axis1; - btPlaneSpace1(cp.m_normalWorldOnB,axis0,axis1); - applyAnisotropicFriction(colObj0,axis0,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION); - applyAnisotropicFriction(colObj1,axis0,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION); - applyAnisotropicFriction(colObj0,axis1,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION); - applyAnisotropicFriction(colObj1,axis1,btCollisionObject::CF_ANISOTROPIC_ROLLING_FRICTION); - if (axis0.length()>0.001) - addRollingFrictionConstraint(axis0,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); - if (axis1.length()>0.001) - addRollingFrictionConstraint(axis1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); - - } - } -#endif //ROLLING_FRICTION - - ///Bullet has several options to set the friction directions - ///By default, each contact has only a single friction direction that is recomputed automatically very frame - ///based on the relative linear velocity. - ///If the relative velocity it zero, it will automatically compute a friction direction. - - ///You can also enable two friction directions, using the SOLVER_USE_2_FRICTION_DIRECTIONS. - ///In that case, the second friction direction will be orthogonal to both contact normal and first friction direction. - /// - ///If you choose SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION, then the friction will be independent from the relative projected velocity. - /// - ///The user can manually override the friction directions for certain contacts using a contact callback, - ///and set the cp.m_lateralFrictionInitialized to true - ///In that case, you can set the target relative motion in each friction direction (cp.m_contactMotion1 and cp.m_contactMotion2) - ///this will give a conveyor belt effect - /// - if (!(infoGlobal.m_solverMode & SOLVER_ENABLE_FRICTION_DIRECTION_CACHING) || !cp.m_lateralFrictionInitialized) - {/* - cp.m_lateralFrictionDir1 = vel - cp.m_normalWorldOnB * rel_vel; - btScalar lat_rel_vel = cp.m_lateralFrictionDir1.length2(); - if (!(infoGlobal.m_solverMode & SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION) && lat_rel_vel > SIMD_EPSILON) - { - cp.m_lateralFrictionDir1 *= 1.f/btSqrt(lat_rel_vel); - if((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) - { - cp.m_lateralFrictionDir2 = cp.m_lateralFrictionDir1.cross(cp.m_normalWorldOnB); - cp.m_lateralFrictionDir2.normalize();//?? - applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION); - applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION); - addMultiBodyFrictionConstraint(cp.m_lateralFrictionDir2,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); - - } - - applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); - applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); - addMultiBodyFrictionConstraint(cp.m_lateralFrictionDir1,solverBodyIdA,solverBodyIdB,frictionIndex,cp,rel_pos1,rel_pos2,colObj0,colObj1, relaxation); - - } else - */ - { - btPlaneSpace1(cp.m_normalWorldOnB,cp.m_lateralFrictionDir1,cp.m_lateralFrictionDir2); - - if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) - { - applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION); - applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir2,btCollisionObject::CF_ANISOTROPIC_FRICTION); - addMultiBodyFrictionConstraint(cp.m_lateralFrictionDir2,manifold,frictionIndex,cp,colObj0,colObj1, relaxation,infoGlobal); - } - - applyAnisotropicFriction(colObj0,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); - applyAnisotropicFriction(colObj1,cp.m_lateralFrictionDir1,btCollisionObject::CF_ANISOTROPIC_FRICTION); - addMultiBodyFrictionConstraint(cp.m_lateralFrictionDir1,manifold,frictionIndex,cp,colObj0,colObj1, relaxation,infoGlobal); - - if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS) && (infoGlobal.m_solverMode & SOLVER_DISABLE_VELOCITY_DEPENDENT_FRICTION_DIRECTION)) - { - cp.m_lateralFrictionInitialized = true; - } - } - - } else - { - addMultiBodyFrictionConstraint(cp.m_lateralFrictionDir1,manifold,frictionIndex,cp,colObj0,colObj1, relaxation,infoGlobal,cp.m_contactMotion1, cp.m_contactCFM1); - - if ((infoGlobal.m_solverMode & SOLVER_USE_2_FRICTION_DIRECTIONS)) - addMultiBodyFrictionConstraint(cp.m_lateralFrictionDir2,manifold,frictionIndex,cp,colObj0,colObj1, relaxation, infoGlobal,cp.m_contactMotion2, cp.m_contactCFM2); - - //setMultiBodyFrictionConstraintImpulse( solverConstraint, solverBodyIdA, solverBodyIdB, cp, infoGlobal); - //todo: - solverConstraint.m_appliedImpulse = 0.f; - solverConstraint.m_appliedPushImpulse = 0.f; - } - - -#endif //ENABLE_FRICTION - - } - } -} - -void btMultiBodyConstraintSolver::convertContacts(btPersistentManifold** manifoldPtr,int numManifolds, const btContactSolverInfo& infoGlobal) -{ - btPersistentManifold* manifold = 0; - - for (int i=0;igetBody0()); - const btMultiBodyLinkCollider* fcB = btMultiBodyLinkCollider::upcast(manifold->getBody1()); - if (!fcA && !fcB) - { - //the contact doesn't involve any Featherstone btMultiBody, so deal with the regular btRigidBody/btCollisionObject case - convertContact(manifold,infoGlobal); - } else - { - convertMultiBodyContact(manifold,infoGlobal); - } - } - - //also convert the multibody constraints, if any - - - for (int i=0;icreateConstraintRows(m_multiBodyNonContactConstraints,m_data, infoGlobal); - } - -} - - - -btScalar btMultiBodyConstraintSolver::solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& info, btIDebugDraw* debugDrawer,btDispatcher* dispatcher) -{ - return btSequentialImpulseConstraintSolver::solveGroup(bodies,numBodies,manifold,numManifolds,constraints,numConstraints,info,debugDrawer,dispatcher); -} - - -void btMultiBodyConstraintSolver::solveMultiBodyGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,btMultiBodyConstraint** multiBodyConstraints, int numMultiBodyConstraints, const btContactSolverInfo& info, btIDebugDraw* debugDrawer,btDispatcher* dispatcher) -{ - //printf("solveMultiBodyGroup start\n"); - m_tmpMultiBodyConstraints = multiBodyConstraints; - m_tmpNumMultiBodyConstraints = numMultiBodyConstraints; - - btSequentialImpulseConstraintSolver::solveGroup(bodies,numBodies,manifold,numManifolds,constraints,numConstraints,info,debugDrawer,dispatcher); - - m_tmpMultiBodyConstraints = 0; - m_tmpNumMultiBodyConstraints = 0; - - -} diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h deleted file mode 100644 index 0f4cd69c0..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyConstraintSolver.h +++ /dev/null @@ -1,85 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2013 Erwin Coumans http://bulletphysics.org - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef BT_MULTIBODY_CONSTRAINT_SOLVER_H -#define BT_MULTIBODY_CONSTRAINT_SOLVER_H - -#include "BulletDynamics/ConstraintSolver/btSequentialImpulseConstraintSolver.h" -#include "btMultiBodySolverConstraint.h" - - -class btMultiBody; - -#include "btMultiBodyConstraint.h" - - - -ATTRIBUTE_ALIGNED16(class) btMultiBodyConstraintSolver : public btSequentialImpulseConstraintSolver -{ - -protected: - - btMultiBodyConstraintArray m_multiBodyNonContactConstraints; - - btMultiBodyConstraintArray m_multiBodyNormalContactConstraints; - btMultiBodyConstraintArray m_multiBodyFrictionContactConstraints; - - btMultiBodyJacobianData m_data; - - //temp storage for multi body constraints for a specific island/group called by 'solveGroup' - btMultiBodyConstraint** m_tmpMultiBodyConstraints; - int m_tmpNumMultiBodyConstraints; - - void resolveSingleConstraintRowGeneric(const btMultiBodySolverConstraint& c); - void resolveSingleConstraintRowGenericMultiBody(const btMultiBodySolverConstraint& c); - - void convertContacts(btPersistentManifold** manifoldPtr,int numManifolds, const btContactSolverInfo& infoGlobal); - btMultiBodySolverConstraint& addMultiBodyFrictionConstraint(const btVector3& normalAxis,btPersistentManifold* manifold,int frictionIndex,btManifoldPoint& cp,btCollisionObject* colObj0,btCollisionObject* colObj1, btScalar relaxation, const btContactSolverInfo& infoGlobal, btScalar desiredVelocity=0, btScalar cfmSlip=0); - - - void setupMultiBodyJointLimitConstraint(btMultiBodySolverConstraint& constraintRow, - btScalar* jacA,btScalar* jacB, - btScalar penetration,btScalar combinedFrictionCoeff, btScalar combinedRestitutionCoeff, - const btContactSolverInfo& infoGlobal); - - void setupMultiBodyContactConstraint(btMultiBodySolverConstraint& solverConstraint, - const btVector3& contactNormal, - btManifoldPoint& cp, const btContactSolverInfo& infoGlobal, - btScalar& relaxation, - bool isFriction, btScalar desiredVelocity=0, btScalar cfmSlip=0); - - void convertMultiBodyContact(btPersistentManifold* manifold,const btContactSolverInfo& infoGlobal); - virtual btScalar solveGroupCacheFriendlySetup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); -// virtual btScalar solveGroupCacheFriendlyIterations(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); - - virtual btScalar solveSingleIteration(int iteration, btCollisionObject** bodies ,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); - void applyDeltaVee(btScalar* deltaV, btScalar impulse, int velocityIndex, int ndof); - -public: - - BT_DECLARE_ALIGNED_ALLOCATOR(); - - ///this method should not be called, it was just used during porting/integration of Featherstone btMultiBody, providing backwards compatibility but no support for btMultiBodyConstraint (only contact constraints) - virtual btScalar solveGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& info, btIDebugDraw* debugDrawer,btDispatcher* dispatcher); - - virtual void solveMultiBodyGroup(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifold,int numManifolds,btTypedConstraint** constraints,int numConstraints,btMultiBodyConstraint** multiBodyConstraints, int numMultiBodyConstraints, const btContactSolverInfo& info, btIDebugDraw* debugDrawer,btDispatcher* dispatcher); -}; - - - - - -#endif //BT_MULTIBODY_CONSTRAINT_SOLVER_H - diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp deleted file mode 100644 index 0910f8f6a..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.cpp +++ /dev/null @@ -1,578 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2013 Erwin Coumans http://bulletphysics.org - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#include "btMultiBodyDynamicsWorld.h" -#include "btMultiBodyConstraintSolver.h" -#include "btMultiBody.h" -#include "btMultiBodyLinkCollider.h" -#include "BulletCollision/CollisionDispatch/btSimulationIslandManager.h" -#include "LinearMath/btQuickprof.h" -#include "btMultiBodyConstraint.h" - - - - -void btMultiBodyDynamicsWorld::addMultiBody(btMultiBody* body, short group, short mask) -{ - m_multiBodies.push_back(body); - -} - -void btMultiBodyDynamicsWorld::removeMultiBody(btMultiBody* body) -{ - m_multiBodies.remove(body); -} - -void btMultiBodyDynamicsWorld::calculateSimulationIslands() -{ - BT_PROFILE("calculateSimulationIslands"); - - getSimulationIslandManager()->updateActivationState(getCollisionWorld(),getCollisionWorld()->getDispatcher()); - - { - //merge islands based on speculative contact manifolds too - for (int i=0;im_predictiveManifolds.size();i++) - { - btPersistentManifold* manifold = m_predictiveManifolds[i]; - - const btCollisionObject* colObj0 = manifold->getBody0(); - const btCollisionObject* colObj1 = manifold->getBody1(); - - if (((colObj0) && (!(colObj0)->isStaticOrKinematicObject())) && - ((colObj1) && (!(colObj1)->isStaticOrKinematicObject()))) - { - getSimulationIslandManager()->getUnionFind().unite((colObj0)->getIslandTag(),(colObj1)->getIslandTag()); - } - } - } - - { - int i; - int numConstraints = int(m_constraints.size()); - for (i=0;i< numConstraints ; i++ ) - { - btTypedConstraint* constraint = m_constraints[i]; - if (constraint->isEnabled()) - { - const btRigidBody* colObj0 = &constraint->getRigidBodyA(); - const btRigidBody* colObj1 = &constraint->getRigidBodyB(); - - if (((colObj0) && (!(colObj0)->isStaticOrKinematicObject())) && - ((colObj1) && (!(colObj1)->isStaticOrKinematicObject()))) - { - getSimulationIslandManager()->getUnionFind().unite((colObj0)->getIslandTag(),(colObj1)->getIslandTag()); - } - } - } - } - - //merge islands linked by Featherstone link colliders - for (int i=0;igetBaseCollider(); - - for (int b=0;bgetNumLinks();b++) - { - btMultiBodyLinkCollider* cur = body->getLink(b).m_collider; - - if (((cur) && (!(cur)->isStaticOrKinematicObject())) && - ((prev) && (!(prev)->isStaticOrKinematicObject()))) - { - int tagPrev = prev->getIslandTag(); - int tagCur = cur->getIslandTag(); - getSimulationIslandManager()->getUnionFind().unite(tagPrev, tagCur); - } - if (cur && !cur->isStaticOrKinematicObject()) - prev = cur; - - } - } - } - - //merge islands linked by multibody constraints - { - for (int i=0;im_multiBodyConstraints.size();i++) - { - btMultiBodyConstraint* c = m_multiBodyConstraints[i]; - int tagA = c->getIslandIdA(); - int tagB = c->getIslandIdB(); - if (tagA>=0 && tagB>=0) - getSimulationIslandManager()->getUnionFind().unite(tagA, tagB); - } - } - - //Store the island id in each body - getSimulationIslandManager()->storeIslandActivationState(getCollisionWorld()); - -} - - -void btMultiBodyDynamicsWorld::updateActivationState(btScalar timeStep) -{ - BT_PROFILE("btMultiBodyDynamicsWorld::updateActivationState"); - - - - for ( int i=0;icheckMotionAndSleepIfRequired(timeStep); - if (!body->isAwake()) - { - btMultiBodyLinkCollider* col = body->getBaseCollider(); - if (col && col->getActivationState() == ACTIVE_TAG) - { - col->setActivationState( WANTS_DEACTIVATION); - col->setDeactivationTime(0.f); - } - for (int b=0;bgetNumLinks();b++) - { - btMultiBodyLinkCollider* col = body->getLink(b).m_collider; - if (col && col->getActivationState() == ACTIVE_TAG) - { - col->setActivationState( WANTS_DEACTIVATION); - col->setDeactivationTime(0.f); - } - } - } else - { - btMultiBodyLinkCollider* col = body->getBaseCollider(); - if (col && col->getActivationState() != DISABLE_DEACTIVATION) - col->setActivationState( ACTIVE_TAG ); - - for (int b=0;bgetNumLinks();b++) - { - btMultiBodyLinkCollider* col = body->getLink(b).m_collider; - if (col && col->getActivationState() != DISABLE_DEACTIVATION) - col->setActivationState( ACTIVE_TAG ); - } - } - - } - } - - btDiscreteDynamicsWorld::updateActivationState(timeStep); -} - - -SIMD_FORCE_INLINE int btGetConstraintIslandId2(const btTypedConstraint* lhs) -{ - int islandId; - - const btCollisionObject& rcolObj0 = lhs->getRigidBodyA(); - const btCollisionObject& rcolObj1 = lhs->getRigidBodyB(); - islandId= rcolObj0.getIslandTag()>=0?rcolObj0.getIslandTag():rcolObj1.getIslandTag(); - return islandId; - -} - - -class btSortConstraintOnIslandPredicate2 -{ - public: - - bool operator() ( const btTypedConstraint* lhs, const btTypedConstraint* rhs ) const - { - int rIslandId0,lIslandId0; - rIslandId0 = btGetConstraintIslandId2(rhs); - lIslandId0 = btGetConstraintIslandId2(lhs); - return lIslandId0 < rIslandId0; - } -}; - - - -SIMD_FORCE_INLINE int btGetMultiBodyConstraintIslandId(const btMultiBodyConstraint* lhs) -{ - int islandId; - - int islandTagA = lhs->getIslandIdA(); - int islandTagB = lhs->getIslandIdB(); - islandId= islandTagA>=0?islandTagA:islandTagB; - return islandId; - -} - - -class btSortMultiBodyConstraintOnIslandPredicate -{ - public: - - bool operator() ( const btMultiBodyConstraint* lhs, const btMultiBodyConstraint* rhs ) const - { - int rIslandId0,lIslandId0; - rIslandId0 = btGetMultiBodyConstraintIslandId(rhs); - lIslandId0 = btGetMultiBodyConstraintIslandId(lhs); - return lIslandId0 < rIslandId0; - } -}; - -struct MultiBodyInplaceSolverIslandCallback : public btSimulationIslandManager::IslandCallback -{ - btContactSolverInfo* m_solverInfo; - btMultiBodyConstraintSolver* m_solver; - btMultiBodyConstraint** m_multiBodySortedConstraints; - int m_numMultiBodyConstraints; - - btTypedConstraint** m_sortedConstraints; - int m_numConstraints; - btIDebugDraw* m_debugDrawer; - btDispatcher* m_dispatcher; - - btAlignedObjectArray m_bodies; - btAlignedObjectArray m_manifolds; - btAlignedObjectArray m_constraints; - btAlignedObjectArray m_multiBodyConstraints; - - - MultiBodyInplaceSolverIslandCallback( btMultiBodyConstraintSolver* solver, - btDispatcher* dispatcher) - :m_solverInfo(NULL), - m_solver(solver), - m_multiBodySortedConstraints(NULL), - m_numConstraints(0), - m_debugDrawer(NULL), - m_dispatcher(dispatcher) - { - - } - - MultiBodyInplaceSolverIslandCallback& operator=(MultiBodyInplaceSolverIslandCallback& other) - { - btAssert(0); - (void)other; - return *this; - } - - SIMD_FORCE_INLINE void setup ( btContactSolverInfo* solverInfo, btTypedConstraint** sortedConstraints, int numConstraints, btMultiBodyConstraint** sortedMultiBodyConstraints, int numMultiBodyConstraints, btIDebugDraw* debugDrawer) - { - btAssert(solverInfo); - m_solverInfo = solverInfo; - - m_multiBodySortedConstraints = sortedMultiBodyConstraints; - m_numMultiBodyConstraints = numMultiBodyConstraints; - m_sortedConstraints = sortedConstraints; - m_numConstraints = numConstraints; - - m_debugDrawer = debugDrawer; - m_bodies.resize (0); - m_manifolds.resize (0); - m_constraints.resize (0); - m_multiBodyConstraints.resize(0); - } - - - virtual void processIsland(btCollisionObject** bodies,int numBodies,btPersistentManifold** manifolds,int numManifolds, int islandId) - { - if (islandId<0) - { - ///we don't split islands, so all constraints/contact manifolds/bodies are passed into the solver regardless the island id - m_solver->solveMultiBodyGroup( bodies,numBodies,manifolds, numManifolds,m_sortedConstraints, m_numConstraints, &m_multiBodySortedConstraints[0],m_numConstraints,*m_solverInfo,m_debugDrawer,m_dispatcher); - } else - { - //also add all non-contact constraints/joints for this island - btTypedConstraint** startConstraint = 0; - btMultiBodyConstraint** startMultiBodyConstraint = 0; - - int numCurConstraints = 0; - int numCurMultiBodyConstraints = 0; - - int i; - - //find the first constraint for this island - - for (i=0;im_minimumSolverBatchSize<=1) - { - m_solver->solveGroup( bodies,numBodies,manifolds, numManifolds,startConstraint,numCurConstraints,*m_solverInfo,m_debugDrawer,m_dispatcher); - } else - { - - for (i=0;im_solverInfo->m_minimumSolverBatchSize) - { - processConstraints(); - } else - { - //printf("deferred\n"); - } - } - } - } - void processConstraints() - { - - btCollisionObject** bodies = m_bodies.size()? &m_bodies[0]:0; - btPersistentManifold** manifold = m_manifolds.size()?&m_manifolds[0]:0; - btTypedConstraint** constraints = m_constraints.size()?&m_constraints[0]:0; - btMultiBodyConstraint** multiBodyConstraints = m_multiBodyConstraints.size() ? &m_multiBodyConstraints[0] : 0; - - m_solver->solveMultiBodyGroup( bodies,m_bodies.size(),manifold, m_manifolds.size(),constraints, m_constraints.size() ,multiBodyConstraints, m_multiBodyConstraints.size(), *m_solverInfo,m_debugDrawer,m_dispatcher); - m_bodies.resize(0); - m_manifolds.resize(0); - m_constraints.resize(0); - m_multiBodyConstraints.resize(0); - } - -}; - - - -btMultiBodyDynamicsWorld::btMultiBodyDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btMultiBodyConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration) - :btDiscreteDynamicsWorld(dispatcher,pairCache,constraintSolver,collisionConfiguration), - m_multiBodyConstraintSolver(constraintSolver) -{ - //split impulse is not yet supported for Featherstone hierarchies - getSolverInfo().m_splitImpulse = false; - getSolverInfo().m_solverMode |=SOLVER_USE_2_FRICTION_DIRECTIONS; - m_solverMultiBodyIslandCallback = new MultiBodyInplaceSolverIslandCallback(constraintSolver,dispatcher); -} - -btMultiBodyDynamicsWorld::~btMultiBodyDynamicsWorld () -{ - delete m_solverMultiBodyIslandCallback; -} - - - - -void btMultiBodyDynamicsWorld::solveConstraints(btContactSolverInfo& solverInfo) -{ - - btAlignedObjectArray scratch_r; - btAlignedObjectArray scratch_v; - btAlignedObjectArray scratch_m; - - - BT_PROFILE("solveConstraints"); - - m_sortedConstraints.resize( m_constraints.size()); - int i; - for (i=0;isetup(&solverInfo,constraintsPtr,m_sortedConstraints.size(),sortedMultiBodyConstraints,m_sortedMultiBodyConstraints.size(), getDebugDrawer()); - m_constraintSolver->prepareSolve(getCollisionWorld()->getNumCollisionObjects(), getCollisionWorld()->getDispatcher()->getNumManifolds()); - - /// solve all the constraints for this island - m_islandManager->buildAndProcessIslands(getCollisionWorld()->getDispatcher(),getCollisionWorld(),m_solverMultiBodyIslandCallback); - - - { - BT_PROFILE("btMultiBody addForce and stepVelocities"); - for (int i=0;im_multiBodies.size();i++) - { - btMultiBody* bod = m_multiBodies[i]; - - bool isSleeping = false; - - if (bod->getBaseCollider() && bod->getBaseCollider()->getActivationState() == ISLAND_SLEEPING) - { - isSleeping = true; - } - for (int b=0;bgetNumLinks();b++) - { - if (bod->getLink(b).m_collider && bod->getLink(b).m_collider->getActivationState()==ISLAND_SLEEPING) - isSleeping = true; - } - - if (!isSleeping) - { - scratch_r.resize(bod->getNumLinks()+1); - scratch_v.resize(bod->getNumLinks()+1); - scratch_m.resize(bod->getNumLinks()+1); - - bod->clearForcesAndTorques(); - bod->addBaseForce(m_gravity * bod->getBaseMass()); - - for (int j = 0; j < bod->getNumLinks(); ++j) - { - bod->addLinkForce(j, m_gravity * bod->getLinkMass(j)); - } - - bod->stepVelocities(solverInfo.m_timeStep, scratch_r, scratch_v, scratch_m); - } - } - } - - m_solverMultiBodyIslandCallback->processConstraints(); - - m_constraintSolver->allSolved(solverInfo, m_debugDrawer); - -} - -void btMultiBodyDynamicsWorld::integrateTransforms(btScalar timeStep) -{ - btDiscreteDynamicsWorld::integrateTransforms(timeStep); - - { - BT_PROFILE("btMultiBody stepPositions"); - //integrate and update the Featherstone hierarchies - btAlignedObjectArray world_to_local; - btAlignedObjectArray local_origin; - - for (int b=0;bgetBaseCollider() && bod->getBaseCollider()->getActivationState() == ISLAND_SLEEPING) - { - isSleeping = true; - } - for (int b=0;bgetNumLinks();b++) - { - if (bod->getLink(b).m_collider && bod->getLink(b).m_collider->getActivationState()==ISLAND_SLEEPING) - isSleeping = true; - } - - - if (!isSleeping) - { - int nLinks = bod->getNumLinks(); - - ///base + num links - world_to_local.resize(nLinks+1); - local_origin.resize(nLinks+1); - - bod->stepPositions(timeStep); - - - - world_to_local[0] = bod->getWorldToBaseRot(); - local_origin[0] = bod->getBasePos(); - - if (bod->getBaseCollider()) - { - btVector3 posr = local_origin[0]; - float pos[4]={posr.x(),posr.y(),posr.z(),1}; - float quat[4]={-world_to_local[0].x(),-world_to_local[0].y(),-world_to_local[0].z(),world_to_local[0].w()}; - btTransform tr; - tr.setIdentity(); - tr.setOrigin(posr); - tr.setRotation(btQuaternion(quat[0],quat[1],quat[2],quat[3])); - - bod->getBaseCollider()->setWorldTransform(tr); - - } - - for (int k=0;kgetNumLinks();k++) - { - const int parent = bod->getParent(k); - world_to_local[k+1] = bod->getParentToLocalRot(k) * world_to_local[parent+1]; - local_origin[k+1] = local_origin[parent+1] + (quatRotate(world_to_local[k+1].inverse() , bod->getRVector(k))); - } - - - for (int m=0;mgetNumLinks();m++) - { - btMultiBodyLinkCollider* col = bod->getLink(m).m_collider; - if (col) - { - int link = col->m_link; - btAssert(link == m); - - int index = link+1; - - btVector3 posr = local_origin[index]; - float pos[4]={posr.x(),posr.y(),posr.z(),1}; - float quat[4]={-world_to_local[index].x(),-world_to_local[index].y(),-world_to_local[index].z(),world_to_local[index].w()}; - btTransform tr; - tr.setIdentity(); - tr.setOrigin(posr); - tr.setRotation(btQuaternion(quat[0],quat[1],quat[2],quat[3])); - - col->setWorldTransform(tr); - } - } - } else - { - bod->clearVelocities(); - } - } - } -} - - - -void btMultiBodyDynamicsWorld::addMultiBodyConstraint( btMultiBodyConstraint* constraint) -{ - m_multiBodyConstraints.push_back(constraint); -} - -void btMultiBodyDynamicsWorld::removeMultiBodyConstraint( btMultiBodyConstraint* constraint) -{ - m_multiBodyConstraints.remove(constraint); -} diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h deleted file mode 100644 index ad57a346d..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyDynamicsWorld.h +++ /dev/null @@ -1,56 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2013 Erwin Coumans http://bulletphysics.org - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef BT_MULTIBODY_DYNAMICS_WORLD_H -#define BT_MULTIBODY_DYNAMICS_WORLD_H - -#include "BulletDynamics/Dynamics/btDiscreteDynamicsWorld.h" - - -class btMultiBody; -class btMultiBodyConstraint; -class btMultiBodyConstraintSolver; -struct MultiBodyInplaceSolverIslandCallback; - -///The btMultiBodyDynamicsWorld adds Featherstone multi body dynamics to Bullet -///This implementation is still preliminary/experimental. -class btMultiBodyDynamicsWorld : public btDiscreteDynamicsWorld -{ -protected: - btAlignedObjectArray m_multiBodies; - btAlignedObjectArray m_multiBodyConstraints; - btAlignedObjectArray m_sortedMultiBodyConstraints; - btMultiBodyConstraintSolver* m_multiBodyConstraintSolver; - MultiBodyInplaceSolverIslandCallback* m_solverMultiBodyIslandCallback; - - virtual void calculateSimulationIslands(); - virtual void updateActivationState(btScalar timeStep); - virtual void solveConstraints(btContactSolverInfo& solverInfo); - virtual void integrateTransforms(btScalar timeStep); -public: - - btMultiBodyDynamicsWorld(btDispatcher* dispatcher,btBroadphaseInterface* pairCache,btMultiBodyConstraintSolver* constraintSolver,btCollisionConfiguration* collisionConfiguration); - - virtual ~btMultiBodyDynamicsWorld (); - - virtual void addMultiBody(btMultiBody* body, short group= btBroadphaseProxy::DefaultFilter, short mask=btBroadphaseProxy::AllFilter); - - virtual void removeMultiBody(btMultiBody* body); - - virtual void addMultiBodyConstraint( btMultiBodyConstraint* constraint); - - virtual void removeMultiBodyConstraint( btMultiBodyConstraint* constraint); -}; -#endif //BT_MULTIBODY_DYNAMICS_WORLD_H diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp deleted file mode 100644 index ea309e885..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.cpp +++ /dev/null @@ -1,133 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2013 Erwin Coumans http://bulletphysics.org - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -///This file was written by Erwin Coumans - -#include "btMultiBodyJointLimitConstraint.h" -#include "btMultiBody.h" -#include "btMultiBodyLinkCollider.h" -#include "BulletCollision/CollisionDispatch/btCollisionObject.h" - - -btMultiBodyJointLimitConstraint::btMultiBodyJointLimitConstraint(btMultiBody* body, int link, btScalar lower, btScalar upper) - :btMultiBodyConstraint(body,body,link,link,2,true), - m_lowerBound(lower), - m_upperBound(upper) -{ - // the data.m_jacobians never change, so may as well - // initialize them here - - // note: we rely on the fact that data.m_jacobians are - // always initialized to zero by the Constraint ctor - - // row 0: the lower bound - jacobianA(0)[6 + link] = 1; - - // row 1: the upper bound - jacobianB(1)[6 + link] = -1; -} -btMultiBodyJointLimitConstraint::~btMultiBodyJointLimitConstraint() -{ -} - -int btMultiBodyJointLimitConstraint::getIslandIdA() const -{ - btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); - if (col) - return col->getIslandTag(); - for (int i=0;igetNumLinks();i++) - { - if (m_bodyA->getLink(i).m_collider) - return m_bodyA->getLink(i).m_collider->getIslandTag(); - } - return -1; -} - -int btMultiBodyJointLimitConstraint::getIslandIdB() const -{ - btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); - if (col) - return col->getIslandTag(); - - for (int i=0;igetNumLinks();i++) - { - col = m_bodyB->getLink(i).m_collider; - if (col) - return col->getIslandTag(); - } - return -1; -} - - -void btMultiBodyJointLimitConstraint::createConstraintRows(btMultiBodyConstraintArray& constraintRows, - btMultiBodyJacobianData& data, - const btContactSolverInfo& infoGlobal) -{ - // only positions need to be updated -- data.m_jacobians and force - // directions were set in the ctor and never change. - - // row 0: the lower bound - setPosition(0, m_bodyA->getJointPos(m_linkA) - m_lowerBound); - - // row 1: the upper bound - setPosition(1, m_upperBound - m_bodyA->getJointPos(m_linkA)); - - for (int row=0;row infoGlobal.m_splitImpulsePenetrationThreshold)) - { - erp = infoGlobal.m_erp; - } - if (penetration>0) - { - positionalError = 0; - velocityError = -penetration / infoGlobal.m_timeStep; - } else - { - positionalError = -penetration * erp/infoGlobal.m_timeStep; - } - - btScalar penetrationImpulse = positionalError*constraintRow.m_jacDiagABInv; - btScalar velocityImpulse = velocityError *constraintRow.m_jacDiagABInv; - if (!infoGlobal.m_splitImpulse || (penetration > infoGlobal.m_splitImpulsePenetrationThreshold)) - { - //combine position and velocity into rhs - constraintRow.m_rhs = penetrationImpulse+velocityImpulse; - constraintRow.m_rhsPenetration = 0.f; - - } else - { - //split position and velocity into rhs and m_rhsPenetration - constraintRow.m_rhs = velocityImpulse; - constraintRow.m_rhsPenetration = penetrationImpulse; - } - } - } - -} - - - - diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.h b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.h deleted file mode 100644 index 0c7fc1708..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointLimitConstraint.h +++ /dev/null @@ -1,44 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2013 Erwin Coumans http://bulletphysics.org - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef BT_MULTIBODY_JOINT_LIMIT_CONSTRAINT_H -#define BT_MULTIBODY_JOINT_LIMIT_CONSTRAINT_H - -#include "btMultiBodyConstraint.h" -struct btSolverInfo; - -class btMultiBodyJointLimitConstraint : public btMultiBodyConstraint -{ -protected: - - btScalar m_lowerBound; - btScalar m_upperBound; -public: - - btMultiBodyJointLimitConstraint(btMultiBody* body, int link, btScalar lower, btScalar upper); - virtual ~btMultiBodyJointLimitConstraint(); - - virtual int getIslandIdA() const; - virtual int getIslandIdB() const; - - virtual void createConstraintRows(btMultiBodyConstraintArray& constraintRows, - btMultiBodyJacobianData& data, - const btContactSolverInfo& infoGlobal); - - -}; - -#endif //BT_MULTIBODY_JOINT_LIMIT_CONSTRAINT_H - diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp deleted file mode 100644 index ab5a43023..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyJointMotor.cpp +++ /dev/null @@ -1,89 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2013 Erwin Coumans http://bulletphysics.org - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -///This file was written by Erwin Coumans - -#include "btMultiBodyJointMotor.h" -#include "btMultiBody.h" -#include "btMultiBodyLinkCollider.h" -#include "BulletCollision/CollisionDispatch/btCollisionObject.h" - - -btMultiBodyJointMotor::btMultiBodyJointMotor(btMultiBody* body, int link, btScalar desiredVelocity, btScalar maxMotorImpulse) - :btMultiBodyConstraint(body,body,link,link,1,true), - m_desiredVelocity(desiredVelocity) -{ - m_maxAppliedImpulse = maxMotorImpulse; - // the data.m_jacobians never change, so may as well - // initialize them here - - // note: we rely on the fact that data.m_jacobians are - // always initialized to zero by the Constraint ctor - - // row 0: the lower bound - jacobianA(0)[6 + link] = 1; -} -btMultiBodyJointMotor::~btMultiBodyJointMotor() -{ -} - -int btMultiBodyJointMotor::getIslandIdA() const -{ - btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); - if (col) - return col->getIslandTag(); - for (int i=0;igetNumLinks();i++) - { - if (m_bodyA->getLink(i).m_collider) - return m_bodyA->getLink(i).m_collider->getIslandTag(); - } - return -1; -} - -int btMultiBodyJointMotor::getIslandIdB() const -{ - btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); - if (col) - return col->getIslandTag(); - - for (int i=0;igetNumLinks();i++) - { - col = m_bodyB->getLink(i).m_collider; - if (col) - return col->getIslandTag(); - } - return -1; -} - - -void btMultiBodyJointMotor::createConstraintRows(btMultiBodyConstraintArray& constraintRows, - btMultiBodyJacobianData& data, - const btContactSolverInfo& infoGlobal) -{ - // only positions need to be updated -- data.m_jacobians and force - // directions were set in the ctor and never change. - - - - for (int row=0;row=0 || (multiBody && !multiBody->hasFixedBase())) - { - m_collisionFlags &= (~btCollisionObject::CF_STATIC_OBJECT); - } - // else - //{ - // m_collisionFlags |= (btCollisionObject::CF_STATIC_OBJECT); - //} - - m_internalType = CO_FEATHERSTONE_LINK; - } - static btMultiBodyLinkCollider* upcast(btCollisionObject* colObj) - { - if (colObj->getInternalType()&btCollisionObject::CO_FEATHERSTONE_LINK) - return (btMultiBodyLinkCollider*)colObj; - return 0; - } - static const btMultiBodyLinkCollider* upcast(const btCollisionObject* colObj) - { - if (colObj->getInternalType()&btCollisionObject::CO_FEATHERSTONE_LINK) - return (btMultiBodyLinkCollider*)colObj; - return 0; - } - - virtual bool checkCollideWithOverride(const btCollisionObject* co) const - { - const btMultiBodyLinkCollider* other = btMultiBodyLinkCollider::upcast(co); - if (!other) - return true; - if (other->m_multiBody != this->m_multiBody) - return true; - if (!m_multiBody->hasSelfCollision()) - return false; - - //check if 'link' has collision disabled - if (m_link>=0) - { - const btMultibodyLink& link = m_multiBody->getLink(this->m_link); - if ((link.m_flags&BT_MULTIBODYLINKFLAGS_DISABLE_PARENT_COLLISION) && link.parent == other->m_link) - return false; - } - - if (other->m_link>=0) - { - const btMultibodyLink& otherLink = other->m_multiBody->getLink(other->m_link); - if ((otherLink.m_flags& BT_MULTIBODYLINKFLAGS_DISABLE_PARENT_COLLISION) && otherLink.parent == this->m_link) - return false; - } - return true; - } -}; - -#endif //BT_FEATHERSTONE_LINK_COLLIDER_H - diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp deleted file mode 100644 index f66900491..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.cpp +++ /dev/null @@ -1,143 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2013 Erwin Coumans http://bulletphysics.org - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -///This file was written by Erwin Coumans - -#include "btMultiBodyPoint2Point.h" -#include "btMultiBodyLinkCollider.h" -#include "BulletDynamics/Dynamics/btRigidBody.h" - -btMultiBodyPoint2Point::btMultiBodyPoint2Point(btMultiBody* body, int link, btRigidBody* bodyB, const btVector3& pivotInA, const btVector3& pivotInB) - :btMultiBodyConstraint(body,0,link,-1,3,false), - m_rigidBodyA(0), - m_rigidBodyB(bodyB), - m_pivotInA(pivotInA), - m_pivotInB(pivotInB) -{ -} - -btMultiBodyPoint2Point::btMultiBodyPoint2Point(btMultiBody* bodyA, int linkA, btMultiBody* bodyB, int linkB, const btVector3& pivotInA, const btVector3& pivotInB) - :btMultiBodyConstraint(bodyA,bodyB,linkA,linkB,3,false), - m_rigidBodyA(0), - m_rigidBodyB(0), - m_pivotInA(pivotInA), - m_pivotInB(pivotInB) -{ -} - - -btMultiBodyPoint2Point::~btMultiBodyPoint2Point() -{ -} - - -int btMultiBodyPoint2Point::getIslandIdA() const -{ - if (m_rigidBodyA) - return m_rigidBodyA->getIslandTag(); - - if (m_bodyA) - { - btMultiBodyLinkCollider* col = m_bodyA->getBaseCollider(); - if (col) - return col->getIslandTag(); - for (int i=0;igetNumLinks();i++) - { - if (m_bodyA->getLink(i).m_collider) - return m_bodyA->getLink(i).m_collider->getIslandTag(); - } - } - return -1; -} - -int btMultiBodyPoint2Point::getIslandIdB() const -{ - if (m_rigidBodyB) - return m_rigidBodyB->getIslandTag(); - if (m_bodyB) - { - btMultiBodyLinkCollider* col = m_bodyB->getBaseCollider(); - if (col) - return col->getIslandTag(); - - for (int i=0;igetNumLinks();i++) - { - col = m_bodyB->getLink(i).m_collider; - if (col) - return col->getIslandTag(); - } - } - return -1; -} - - - -void btMultiBodyPoint2Point::createConstraintRows(btMultiBodyConstraintArray& constraintRows, - btMultiBodyJacobianData& data, - const btContactSolverInfo& infoGlobal) -{ - -// int i=1; - for (int i=0;i<3;i++) - { - - btMultiBodySolverConstraint& constraintRow = constraintRows.expandNonInitializing(); - - constraintRow.m_solverBodyIdA = data.m_fixedBodyId; - constraintRow.m_solverBodyIdB = data.m_fixedBodyId; - - - btVector3 contactNormalOnB(0,0,0); - contactNormalOnB[i] = -1; - - btScalar penetration = 0; - - // Convert local points back to world - btVector3 pivotAworld = m_pivotInA; - if (m_rigidBodyA) - { - - constraintRow.m_solverBodyIdA = m_rigidBodyA->getCompanionId(); - pivotAworld = m_rigidBodyA->getCenterOfMassTransform()*m_pivotInA; - } else - { - if (m_bodyA) - pivotAworld = m_bodyA->localPosToWorld(m_linkA, m_pivotInA); - } - btVector3 pivotBworld = m_pivotInB; - if (m_rigidBodyB) - { - constraintRow.m_solverBodyIdB = m_rigidBodyB->getCompanionId(); - pivotBworld = m_rigidBodyB->getCenterOfMassTransform()*m_pivotInB; - } else - { - if (m_bodyB) - pivotBworld = m_bodyB->localPosToWorld(m_linkB, m_pivotInB); - - } - btScalar position = (pivotAworld-pivotBworld).dot(contactNormalOnB); - btScalar relaxation = 1.f; - fillMultiBodyConstraintMixed(constraintRow, data, - contactNormalOnB, - pivotAworld, pivotBworld, - position, - infoGlobal, - relaxation, - false); - constraintRow.m_lowerLimit = -m_maxAppliedImpulse; - constraintRow.m_upperLimit = m_maxAppliedImpulse; - - } -} diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.h b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.h deleted file mode 100644 index 26ca12b40..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodyPoint2Point.h +++ /dev/null @@ -1,60 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2013 Erwin Coumans http://bulletphysics.org - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -///This file was written by Erwin Coumans - -#ifndef BT_MULTIBODY_POINT2POINT_H -#define BT_MULTIBODY_POINT2POINT_H - -#include "btMultiBodyConstraint.h" - -class btMultiBodyPoint2Point : public btMultiBodyConstraint -{ -protected: - - btRigidBody* m_rigidBodyA; - btRigidBody* m_rigidBodyB; - btVector3 m_pivotInA; - btVector3 m_pivotInB; - - -public: - - btMultiBodyPoint2Point(btMultiBody* body, int link, btRigidBody* bodyB, const btVector3& pivotInA, const btVector3& pivotInB); - btMultiBodyPoint2Point(btMultiBody* bodyA, int linkA, btMultiBody* bodyB, int linkB, const btVector3& pivotInA, const btVector3& pivotInB); - - virtual ~btMultiBodyPoint2Point(); - - virtual int getIslandIdA() const; - virtual int getIslandIdB() const; - - virtual void createConstraintRows(btMultiBodyConstraintArray& constraintRows, - btMultiBodyJacobianData& data, - const btContactSolverInfo& infoGlobal); - - const btVector3& getPivotInB() const - { - return m_pivotInB; - } - - void setPivotInB(const btVector3& pivotInB) - { - m_pivotInB = pivotInB; - } - - -}; - -#endif //BT_MULTIBODY_POINT2POINT_H diff --git a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodySolverConstraint.h b/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodySolverConstraint.h deleted file mode 100644 index cf06dfb9e..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/Featherstone/btMultiBodySolverConstraint.h +++ /dev/null @@ -1,82 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2013 Erwin Coumans http://bulletphysics.org - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ - -#ifndef BT_MULTIBODY_SOLVER_CONSTRAINT_H -#define BT_MULTIBODY_SOLVER_CONSTRAINT_H - -#include "LinearMath/btVector3.h" -#include "LinearMath/btAlignedObjectArray.h" - -class btMultiBody; -#include "BulletDynamics/ConstraintSolver/btSolverBody.h" -#include "BulletDynamics/ConstraintSolver/btContactSolverInfo.h" - -///1D constraint along a normal axis between bodyA and bodyB. It can be combined to solve contact and friction constraints. -ATTRIBUTE_ALIGNED16 (struct) btMultiBodySolverConstraint -{ - BT_DECLARE_ALIGNED_ALLOCATOR(); - - - int m_deltaVelAindex;//more generic version of m_relpos1CrossNormal/m_contactNormal1 - btVector3 m_relpos1CrossNormal; - btVector3 m_contactNormal1; - int m_jacAindex; - - int m_deltaVelBindex; - btVector3 m_relpos2CrossNormal; - btVector3 m_contactNormal2; //usually m_contactNormal2 == -m_contactNormal1, but not always - int m_jacBindex; - - btVector3 m_angularComponentA; - btVector3 m_angularComponentB; - - mutable btSimdScalar m_appliedPushImpulse; - mutable btSimdScalar m_appliedImpulse; - - btScalar m_friction; - btScalar m_jacDiagABInv; - btScalar m_rhs; - btScalar m_cfm; - - btScalar m_lowerLimit; - btScalar m_upperLimit; - btScalar m_rhsPenetration; - union - { - void* m_originalContactPoint; - btScalar m_unusedPadding4; - }; - - int m_overrideNumSolverIterations; - int m_frictionIndex; - - int m_solverBodyIdA; - btMultiBody* m_multiBodyA; - int m_linkA; - - int m_solverBodyIdB; - btMultiBody* m_multiBodyB; - int m_linkB; - - enum btSolverConstraintType - { - BT_SOLVER_CONTACT_1D = 0, - BT_SOLVER_FRICTION_1D - }; -}; - -typedef btAlignedObjectArray btMultiBodyConstraintArray; - -#endif //BT_MULTIBODY_SOLVER_CONSTRAINT_H diff --git a/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigLCP.cpp b/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigLCP.cpp deleted file mode 100644 index 3bf7b5c13..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigLCP.cpp +++ /dev/null @@ -1,2079 +0,0 @@ -/************************************************************************* -* * -* Open Dynamics Engine, Copyright (C) 2001,2002 Russell L. Smith. * -* All rights reserved. Email: russ@q12.org Web: www.q12.org * -* * -* This library is free software; you can redistribute it and/or * -* modify it under the terms of EITHER: * -* (1) The GNU Lesser General Public License as published by the Free * -* Software Foundation; either version 2.1 of the License, or (at * -* your option) any later version. The text of the GNU Lesser * -* General Public License is included with this library in the * -* file LICENSE.TXT. * -* (2) The BSD-style license that is included with this library in * -* the file LICENSE-BSD.TXT. * -* * -* This library is distributed in the hope that it will be useful, * -* but WITHOUT ANY WARRANTY; without even the implied warranty of * -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the files * -* LICENSE.TXT and LICENSE-BSD.TXT for more details. * -* * -*************************************************************************/ - -/* - - -THE ALGORITHM -------------- - -solve A*x = b+w, with x and w subject to certain LCP conditions. -each x(i),w(i) must lie on one of the three line segments in the following -diagram. each line segment corresponds to one index set : - - w(i) - /|\ | : - | | : - | |i in N : - w>0 | |state[i]=0 : - | | : - | | : i in C - w=0 + +-----------------------+ - | : | - | : | - w<0 | : |i in N - | : |state[i]=1 - | : | - | : | - +-------|-----------|-----------|----------> x(i) - lo 0 hi - -the Dantzig algorithm proceeds as follows: - for i=1:n - * if (x(i),w(i)) is not on the line, push x(i) and w(i) positive or - negative towards the line. as this is done, the other (x(j),w(j)) - for j= 0. this makes the algorithm a bit -simpler, because the starting point for x(i),w(i) is always on the dotted -line x=0 and x will only ever increase in one direction, so it can only hit -two out of the three line segments. - - -NOTES ------ - -this is an implementation of "lcp_dantzig2_ldlt.m" and "lcp_dantzig_lohi.m". -the implementation is split into an LCP problem object (btLCP) and an LCP -driver function. most optimization occurs in the btLCP object. - -a naive implementation of the algorithm requires either a lot of data motion -or a lot of permutation-array lookup, because we are constantly re-ordering -rows and columns. to avoid this and make a more optimized algorithm, a -non-trivial data structure is used to represent the matrix A (this is -implemented in the fast version of the btLCP object). - -during execution of this algorithm, some indexes in A are clamped (set C), -some are non-clamped (set N), and some are "don't care" (where x=0). -A,x,b,w (and other problem vectors) are permuted such that the clamped -indexes are first, the unclamped indexes are next, and the don't-care -indexes are last. this permutation is recorded in the array `p'. -initially p = 0..n-1, and as the rows and columns of A,x,b,w are swapped, -the corresponding elements of p are swapped. - -because the C and N elements are grouped together in the rows of A, we can do -lots of work with a fast dot product function. if A,x,etc were not permuted -and we only had a permutation array, then those dot products would be much -slower as we would have a permutation array lookup in some inner loops. - -A is accessed through an array of row pointers, so that element (i,j) of the -permuted matrix is A[i][j]. this makes row swapping fast. for column swapping -we still have to actually move the data. - -during execution of this algorithm we maintain an L*D*L' factorization of -the clamped submatrix of A (call it `AC') which is the top left nC*nC -submatrix of A. there are two ways we could arrange the rows/columns in AC. - -(1) AC is always permuted such that L*D*L' = AC. this causes a problem -when a row/column is removed from C, because then all the rows/columns of A -between the deleted index and the end of C need to be rotated downward. -this results in a lot of data motion and slows things down. -(2) L*D*L' is actually a factorization of a *permutation* of AC (which is -itself a permutation of the underlying A). this is what we do - the -permutation is recorded in the vector C. call this permutation A[C,C]. -when a row/column is removed from C, all we have to do is swap two -rows/columns and manipulate C. - -*/ - - -#include "btDantzigLCP.h" - -#include //memcpy - -bool s_error = false; - -//*************************************************************************** -// code generation parameters - - -#define btLCP_FAST // use fast btLCP object - -// option 1 : matrix row pointers (less data copying) -#define BTROWPTRS -#define BTATYPE btScalar ** -#define BTAROW(i) (m_A[i]) - -// option 2 : no matrix row pointers (slightly faster inner loops) -//#define NOROWPTRS -//#define BTATYPE btScalar * -//#define BTAROW(i) (m_A+(i)*m_nskip) - -#define BTNUB_OPTIMIZATIONS - - - -/* solve L*X=B, with B containing 1 right hand sides. - * L is an n*n lower triangular matrix with ones on the diagonal. - * L is stored by rows and its leading dimension is lskip. - * B is an n*1 matrix that contains the right hand sides. - * B is stored by columns and its leading dimension is also lskip. - * B is overwritten with X. - * this processes blocks of 2*2. - * if this is in the factorizer source file, n must be a multiple of 2. - */ - -static void btSolveL1_1 (const btScalar *L, btScalar *B, int n, int lskip1) -{ - /* declare variables - Z matrix, p and q vectors, etc */ - btScalar Z11,m11,Z21,m21,p1,q1,p2,*ex; - const btScalar *ell; - int i,j; - /* compute all 2 x 1 blocks of X */ - for (i=0; i < n; i+=2) { - /* compute all 2 x 1 block of X, from rows i..i+2-1 */ - /* set the Z matrix to 0 */ - Z11=0; - Z21=0; - ell = L + i*lskip1; - ex = B; - /* the inner loop that computes outer products and adds them to Z */ - for (j=i-2; j >= 0; j -= 2) { - /* compute outer product and add it to the Z matrix */ - p1=ell[0]; - q1=ex[0]; - m11 = p1 * q1; - p2=ell[lskip1]; - m21 = p2 * q1; - Z11 += m11; - Z21 += m21; - /* compute outer product and add it to the Z matrix */ - p1=ell[1]; - q1=ex[1]; - m11 = p1 * q1; - p2=ell[1+lskip1]; - m21 = p2 * q1; - /* advance pointers */ - ell += 2; - ex += 2; - Z11 += m11; - Z21 += m21; - /* end of inner loop */ - } - /* compute left-over iterations */ - j += 2; - for (; j > 0; j--) { - /* compute outer product and add it to the Z matrix */ - p1=ell[0]; - q1=ex[0]; - m11 = p1 * q1; - p2=ell[lskip1]; - m21 = p2 * q1; - /* advance pointers */ - ell += 1; - ex += 1; - Z11 += m11; - Z21 += m21; - } - /* finish computing the X(i) block */ - Z11 = ex[0] - Z11; - ex[0] = Z11; - p1 = ell[lskip1]; - Z21 = ex[1] - Z21 - p1*Z11; - ex[1] = Z21; - /* end of outer loop */ - } -} - -/* solve L*X=B, with B containing 2 right hand sides. - * L is an n*n lower triangular matrix with ones on the diagonal. - * L is stored by rows and its leading dimension is lskip. - * B is an n*2 matrix that contains the right hand sides. - * B is stored by columns and its leading dimension is also lskip. - * B is overwritten with X. - * this processes blocks of 2*2. - * if this is in the factorizer source file, n must be a multiple of 2. - */ - -static void btSolveL1_2 (const btScalar *L, btScalar *B, int n, int lskip1) -{ - /* declare variables - Z matrix, p and q vectors, etc */ - btScalar Z11,m11,Z12,m12,Z21,m21,Z22,m22,p1,q1,p2,q2,*ex; - const btScalar *ell; - int i,j; - /* compute all 2 x 2 blocks of X */ - for (i=0; i < n; i+=2) { - /* compute all 2 x 2 block of X, from rows i..i+2-1 */ - /* set the Z matrix to 0 */ - Z11=0; - Z12=0; - Z21=0; - Z22=0; - ell = L + i*lskip1; - ex = B; - /* the inner loop that computes outer products and adds them to Z */ - for (j=i-2; j >= 0; j -= 2) { - /* compute outer product and add it to the Z matrix */ - p1=ell[0]; - q1=ex[0]; - m11 = p1 * q1; - q2=ex[lskip1]; - m12 = p1 * q2; - p2=ell[lskip1]; - m21 = p2 * q1; - m22 = p2 * q2; - Z11 += m11; - Z12 += m12; - Z21 += m21; - Z22 += m22; - /* compute outer product and add it to the Z matrix */ - p1=ell[1]; - q1=ex[1]; - m11 = p1 * q1; - q2=ex[1+lskip1]; - m12 = p1 * q2; - p2=ell[1+lskip1]; - m21 = p2 * q1; - m22 = p2 * q2; - /* advance pointers */ - ell += 2; - ex += 2; - Z11 += m11; - Z12 += m12; - Z21 += m21; - Z22 += m22; - /* end of inner loop */ - } - /* compute left-over iterations */ - j += 2; - for (; j > 0; j--) { - /* compute outer product and add it to the Z matrix */ - p1=ell[0]; - q1=ex[0]; - m11 = p1 * q1; - q2=ex[lskip1]; - m12 = p1 * q2; - p2=ell[lskip1]; - m21 = p2 * q1; - m22 = p2 * q2; - /* advance pointers */ - ell += 1; - ex += 1; - Z11 += m11; - Z12 += m12; - Z21 += m21; - Z22 += m22; - } - /* finish computing the X(i) block */ - Z11 = ex[0] - Z11; - ex[0] = Z11; - Z12 = ex[lskip1] - Z12; - ex[lskip1] = Z12; - p1 = ell[lskip1]; - Z21 = ex[1] - Z21 - p1*Z11; - ex[1] = Z21; - Z22 = ex[1+lskip1] - Z22 - p1*Z12; - ex[1+lskip1] = Z22; - /* end of outer loop */ - } -} - - -void btFactorLDLT (btScalar *A, btScalar *d, int n, int nskip1) -{ - int i,j; - btScalar sum,*ell,*dee,dd,p1,p2,q1,q2,Z11,m11,Z21,m21,Z22,m22; - if (n < 1) return; - - for (i=0; i<=n-2; i += 2) { - /* solve L*(D*l)=a, l is scaled elements in 2 x i block at A(i,0) */ - btSolveL1_2 (A,A+i*nskip1,i,nskip1); - /* scale the elements in a 2 x i block at A(i,0), and also */ - /* compute Z = the outer product matrix that we'll need. */ - Z11 = 0; - Z21 = 0; - Z22 = 0; - ell = A+i*nskip1; - dee = d; - for (j=i-6; j >= 0; j -= 6) { - p1 = ell[0]; - p2 = ell[nskip1]; - dd = dee[0]; - q1 = p1*dd; - q2 = p2*dd; - ell[0] = q1; - ell[nskip1] = q2; - m11 = p1*q1; - m21 = p2*q1; - m22 = p2*q2; - Z11 += m11; - Z21 += m21; - Z22 += m22; - p1 = ell[1]; - p2 = ell[1+nskip1]; - dd = dee[1]; - q1 = p1*dd; - q2 = p2*dd; - ell[1] = q1; - ell[1+nskip1] = q2; - m11 = p1*q1; - m21 = p2*q1; - m22 = p2*q2; - Z11 += m11; - Z21 += m21; - Z22 += m22; - p1 = ell[2]; - p2 = ell[2+nskip1]; - dd = dee[2]; - q1 = p1*dd; - q2 = p2*dd; - ell[2] = q1; - ell[2+nskip1] = q2; - m11 = p1*q1; - m21 = p2*q1; - m22 = p2*q2; - Z11 += m11; - Z21 += m21; - Z22 += m22; - p1 = ell[3]; - p2 = ell[3+nskip1]; - dd = dee[3]; - q1 = p1*dd; - q2 = p2*dd; - ell[3] = q1; - ell[3+nskip1] = q2; - m11 = p1*q1; - m21 = p2*q1; - m22 = p2*q2; - Z11 += m11; - Z21 += m21; - Z22 += m22; - p1 = ell[4]; - p2 = ell[4+nskip1]; - dd = dee[4]; - q1 = p1*dd; - q2 = p2*dd; - ell[4] = q1; - ell[4+nskip1] = q2; - m11 = p1*q1; - m21 = p2*q1; - m22 = p2*q2; - Z11 += m11; - Z21 += m21; - Z22 += m22; - p1 = ell[5]; - p2 = ell[5+nskip1]; - dd = dee[5]; - q1 = p1*dd; - q2 = p2*dd; - ell[5] = q1; - ell[5+nskip1] = q2; - m11 = p1*q1; - m21 = p2*q1; - m22 = p2*q2; - Z11 += m11; - Z21 += m21; - Z22 += m22; - ell += 6; - dee += 6; - } - /* compute left-over iterations */ - j += 6; - for (; j > 0; j--) { - p1 = ell[0]; - p2 = ell[nskip1]; - dd = dee[0]; - q1 = p1*dd; - q2 = p2*dd; - ell[0] = q1; - ell[nskip1] = q2; - m11 = p1*q1; - m21 = p2*q1; - m22 = p2*q2; - Z11 += m11; - Z21 += m21; - Z22 += m22; - ell++; - dee++; - } - /* solve for diagonal 2 x 2 block at A(i,i) */ - Z11 = ell[0] - Z11; - Z21 = ell[nskip1] - Z21; - Z22 = ell[1+nskip1] - Z22; - dee = d + i; - /* factorize 2 x 2 block Z,dee */ - /* factorize row 1 */ - dee[0] = btRecip(Z11); - /* factorize row 2 */ - sum = 0; - q1 = Z21; - q2 = q1 * dee[0]; - Z21 = q2; - sum += q1*q2; - dee[1] = btRecip(Z22 - sum); - /* done factorizing 2 x 2 block */ - ell[nskip1] = Z21; - } - /* compute the (less than 2) rows at the bottom */ - switch (n-i) { - case 0: - break; - - case 1: - btSolveL1_1 (A,A+i*nskip1,i,nskip1); - /* scale the elements in a 1 x i block at A(i,0), and also */ - /* compute Z = the outer product matrix that we'll need. */ - Z11 = 0; - ell = A+i*nskip1; - dee = d; - for (j=i-6; j >= 0; j -= 6) { - p1 = ell[0]; - dd = dee[0]; - q1 = p1*dd; - ell[0] = q1; - m11 = p1*q1; - Z11 += m11; - p1 = ell[1]; - dd = dee[1]; - q1 = p1*dd; - ell[1] = q1; - m11 = p1*q1; - Z11 += m11; - p1 = ell[2]; - dd = dee[2]; - q1 = p1*dd; - ell[2] = q1; - m11 = p1*q1; - Z11 += m11; - p1 = ell[3]; - dd = dee[3]; - q1 = p1*dd; - ell[3] = q1; - m11 = p1*q1; - Z11 += m11; - p1 = ell[4]; - dd = dee[4]; - q1 = p1*dd; - ell[4] = q1; - m11 = p1*q1; - Z11 += m11; - p1 = ell[5]; - dd = dee[5]; - q1 = p1*dd; - ell[5] = q1; - m11 = p1*q1; - Z11 += m11; - ell += 6; - dee += 6; - } - /* compute left-over iterations */ - j += 6; - for (; j > 0; j--) { - p1 = ell[0]; - dd = dee[0]; - q1 = p1*dd; - ell[0] = q1; - m11 = p1*q1; - Z11 += m11; - ell++; - dee++; - } - /* solve for diagonal 1 x 1 block at A(i,i) */ - Z11 = ell[0] - Z11; - dee = d + i; - /* factorize 1 x 1 block Z,dee */ - /* factorize row 1 */ - dee[0] = btRecip(Z11); - /* done factorizing 1 x 1 block */ - break; - - //default: *((char*)0)=0; /* this should never happen! */ - } -} - -/* solve L*X=B, with B containing 1 right hand sides. - * L is an n*n lower triangular matrix with ones on the diagonal. - * L is stored by rows and its leading dimension is lskip. - * B is an n*1 matrix that contains the right hand sides. - * B is stored by columns and its leading dimension is also lskip. - * B is overwritten with X. - * this processes blocks of 4*4. - * if this is in the factorizer source file, n must be a multiple of 4. - */ - -void btSolveL1 (const btScalar *L, btScalar *B, int n, int lskip1) -{ - /* declare variables - Z matrix, p and q vectors, etc */ - btScalar Z11,Z21,Z31,Z41,p1,q1,p2,p3,p4,*ex; - const btScalar *ell; - int lskip2,lskip3,i,j; - /* compute lskip values */ - lskip2 = 2*lskip1; - lskip3 = 3*lskip1; - /* compute all 4 x 1 blocks of X */ - for (i=0; i <= n-4; i+=4) { - /* compute all 4 x 1 block of X, from rows i..i+4-1 */ - /* set the Z matrix to 0 */ - Z11=0; - Z21=0; - Z31=0; - Z41=0; - ell = L + i*lskip1; - ex = B; - /* the inner loop that computes outer products and adds them to Z */ - for (j=i-12; j >= 0; j -= 12) { - /* load p and q values */ - p1=ell[0]; - q1=ex[0]; - p2=ell[lskip1]; - p3=ell[lskip2]; - p4=ell[lskip3]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - Z21 += p2 * q1; - Z31 += p3 * q1; - Z41 += p4 * q1; - /* load p and q values */ - p1=ell[1]; - q1=ex[1]; - p2=ell[1+lskip1]; - p3=ell[1+lskip2]; - p4=ell[1+lskip3]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - Z21 += p2 * q1; - Z31 += p3 * q1; - Z41 += p4 * q1; - /* load p and q values */ - p1=ell[2]; - q1=ex[2]; - p2=ell[2+lskip1]; - p3=ell[2+lskip2]; - p4=ell[2+lskip3]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - Z21 += p2 * q1; - Z31 += p3 * q1; - Z41 += p4 * q1; - /* load p and q values */ - p1=ell[3]; - q1=ex[3]; - p2=ell[3+lskip1]; - p3=ell[3+lskip2]; - p4=ell[3+lskip3]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - Z21 += p2 * q1; - Z31 += p3 * q1; - Z41 += p4 * q1; - /* load p and q values */ - p1=ell[4]; - q1=ex[4]; - p2=ell[4+lskip1]; - p3=ell[4+lskip2]; - p4=ell[4+lskip3]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - Z21 += p2 * q1; - Z31 += p3 * q1; - Z41 += p4 * q1; - /* load p and q values */ - p1=ell[5]; - q1=ex[5]; - p2=ell[5+lskip1]; - p3=ell[5+lskip2]; - p4=ell[5+lskip3]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - Z21 += p2 * q1; - Z31 += p3 * q1; - Z41 += p4 * q1; - /* load p and q values */ - p1=ell[6]; - q1=ex[6]; - p2=ell[6+lskip1]; - p3=ell[6+lskip2]; - p4=ell[6+lskip3]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - Z21 += p2 * q1; - Z31 += p3 * q1; - Z41 += p4 * q1; - /* load p and q values */ - p1=ell[7]; - q1=ex[7]; - p2=ell[7+lskip1]; - p3=ell[7+lskip2]; - p4=ell[7+lskip3]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - Z21 += p2 * q1; - Z31 += p3 * q1; - Z41 += p4 * q1; - /* load p and q values */ - p1=ell[8]; - q1=ex[8]; - p2=ell[8+lskip1]; - p3=ell[8+lskip2]; - p4=ell[8+lskip3]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - Z21 += p2 * q1; - Z31 += p3 * q1; - Z41 += p4 * q1; - /* load p and q values */ - p1=ell[9]; - q1=ex[9]; - p2=ell[9+lskip1]; - p3=ell[9+lskip2]; - p4=ell[9+lskip3]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - Z21 += p2 * q1; - Z31 += p3 * q1; - Z41 += p4 * q1; - /* load p and q values */ - p1=ell[10]; - q1=ex[10]; - p2=ell[10+lskip1]; - p3=ell[10+lskip2]; - p4=ell[10+lskip3]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - Z21 += p2 * q1; - Z31 += p3 * q1; - Z41 += p4 * q1; - /* load p and q values */ - p1=ell[11]; - q1=ex[11]; - p2=ell[11+lskip1]; - p3=ell[11+lskip2]; - p4=ell[11+lskip3]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - Z21 += p2 * q1; - Z31 += p3 * q1; - Z41 += p4 * q1; - /* advance pointers */ - ell += 12; - ex += 12; - /* end of inner loop */ - } - /* compute left-over iterations */ - j += 12; - for (; j > 0; j--) { - /* load p and q values */ - p1=ell[0]; - q1=ex[0]; - p2=ell[lskip1]; - p3=ell[lskip2]; - p4=ell[lskip3]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - Z21 += p2 * q1; - Z31 += p3 * q1; - Z41 += p4 * q1; - /* advance pointers */ - ell += 1; - ex += 1; - } - /* finish computing the X(i) block */ - Z11 = ex[0] - Z11; - ex[0] = Z11; - p1 = ell[lskip1]; - Z21 = ex[1] - Z21 - p1*Z11; - ex[1] = Z21; - p1 = ell[lskip2]; - p2 = ell[1+lskip2]; - Z31 = ex[2] - Z31 - p1*Z11 - p2*Z21; - ex[2] = Z31; - p1 = ell[lskip3]; - p2 = ell[1+lskip3]; - p3 = ell[2+lskip3]; - Z41 = ex[3] - Z41 - p1*Z11 - p2*Z21 - p3*Z31; - ex[3] = Z41; - /* end of outer loop */ - } - /* compute rows at end that are not a multiple of block size */ - for (; i < n; i++) { - /* compute all 1 x 1 block of X, from rows i..i+1-1 */ - /* set the Z matrix to 0 */ - Z11=0; - ell = L + i*lskip1; - ex = B; - /* the inner loop that computes outer products and adds them to Z */ - for (j=i-12; j >= 0; j -= 12) { - /* load p and q values */ - p1=ell[0]; - q1=ex[0]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - /* load p and q values */ - p1=ell[1]; - q1=ex[1]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - /* load p and q values */ - p1=ell[2]; - q1=ex[2]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - /* load p and q values */ - p1=ell[3]; - q1=ex[3]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - /* load p and q values */ - p1=ell[4]; - q1=ex[4]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - /* load p and q values */ - p1=ell[5]; - q1=ex[5]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - /* load p and q values */ - p1=ell[6]; - q1=ex[6]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - /* load p and q values */ - p1=ell[7]; - q1=ex[7]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - /* load p and q values */ - p1=ell[8]; - q1=ex[8]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - /* load p and q values */ - p1=ell[9]; - q1=ex[9]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - /* load p and q values */ - p1=ell[10]; - q1=ex[10]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - /* load p and q values */ - p1=ell[11]; - q1=ex[11]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - /* advance pointers */ - ell += 12; - ex += 12; - /* end of inner loop */ - } - /* compute left-over iterations */ - j += 12; - for (; j > 0; j--) { - /* load p and q values */ - p1=ell[0]; - q1=ex[0]; - /* compute outer product and add it to the Z matrix */ - Z11 += p1 * q1; - /* advance pointers */ - ell += 1; - ex += 1; - } - /* finish computing the X(i) block */ - Z11 = ex[0] - Z11; - ex[0] = Z11; - } -} - -/* solve L^T * x=b, with b containing 1 right hand side. - * L is an n*n lower triangular matrix with ones on the diagonal. - * L is stored by rows and its leading dimension is lskip. - * b is an n*1 matrix that contains the right hand side. - * b is overwritten with x. - * this processes blocks of 4. - */ - -void btSolveL1T (const btScalar *L, btScalar *B, int n, int lskip1) -{ - /* declare variables - Z matrix, p and q vectors, etc */ - btScalar Z11,m11,Z21,m21,Z31,m31,Z41,m41,p1,q1,p2,p3,p4,*ex; - const btScalar *ell; - int lskip2,lskip3,i,j; - /* special handling for L and B because we're solving L1 *transpose* */ - L = L + (n-1)*(lskip1+1); - B = B + n-1; - lskip1 = -lskip1; - /* compute lskip values */ - lskip2 = 2*lskip1; - lskip3 = 3*lskip1; - /* compute all 4 x 1 blocks of X */ - for (i=0; i <= n-4; i+=4) { - /* compute all 4 x 1 block of X, from rows i..i+4-1 */ - /* set the Z matrix to 0 */ - Z11=0; - Z21=0; - Z31=0; - Z41=0; - ell = L - i; - ex = B; - /* the inner loop that computes outer products and adds them to Z */ - for (j=i-4; j >= 0; j -= 4) { - /* load p and q values */ - p1=ell[0]; - q1=ex[0]; - p2=ell[-1]; - p3=ell[-2]; - p4=ell[-3]; - /* compute outer product and add it to the Z matrix */ - m11 = p1 * q1; - m21 = p2 * q1; - m31 = p3 * q1; - m41 = p4 * q1; - ell += lskip1; - Z11 += m11; - Z21 += m21; - Z31 += m31; - Z41 += m41; - /* load p and q values */ - p1=ell[0]; - q1=ex[-1]; - p2=ell[-1]; - p3=ell[-2]; - p4=ell[-3]; - /* compute outer product and add it to the Z matrix */ - m11 = p1 * q1; - m21 = p2 * q1; - m31 = p3 * q1; - m41 = p4 * q1; - ell += lskip1; - Z11 += m11; - Z21 += m21; - Z31 += m31; - Z41 += m41; - /* load p and q values */ - p1=ell[0]; - q1=ex[-2]; - p2=ell[-1]; - p3=ell[-2]; - p4=ell[-3]; - /* compute outer product and add it to the Z matrix */ - m11 = p1 * q1; - m21 = p2 * q1; - m31 = p3 * q1; - m41 = p4 * q1; - ell += lskip1; - Z11 += m11; - Z21 += m21; - Z31 += m31; - Z41 += m41; - /* load p and q values */ - p1=ell[0]; - q1=ex[-3]; - p2=ell[-1]; - p3=ell[-2]; - p4=ell[-3]; - /* compute outer product and add it to the Z matrix */ - m11 = p1 * q1; - m21 = p2 * q1; - m31 = p3 * q1; - m41 = p4 * q1; - ell += lskip1; - ex -= 4; - Z11 += m11; - Z21 += m21; - Z31 += m31; - Z41 += m41; - /* end of inner loop */ - } - /* compute left-over iterations */ - j += 4; - for (; j > 0; j--) { - /* load p and q values */ - p1=ell[0]; - q1=ex[0]; - p2=ell[-1]; - p3=ell[-2]; - p4=ell[-3]; - /* compute outer product and add it to the Z matrix */ - m11 = p1 * q1; - m21 = p2 * q1; - m31 = p3 * q1; - m41 = p4 * q1; - ell += lskip1; - ex -= 1; - Z11 += m11; - Z21 += m21; - Z31 += m31; - Z41 += m41; - } - /* finish computing the X(i) block */ - Z11 = ex[0] - Z11; - ex[0] = Z11; - p1 = ell[-1]; - Z21 = ex[-1] - Z21 - p1*Z11; - ex[-1] = Z21; - p1 = ell[-2]; - p2 = ell[-2+lskip1]; - Z31 = ex[-2] - Z31 - p1*Z11 - p2*Z21; - ex[-2] = Z31; - p1 = ell[-3]; - p2 = ell[-3+lskip1]; - p3 = ell[-3+lskip2]; - Z41 = ex[-3] - Z41 - p1*Z11 - p2*Z21 - p3*Z31; - ex[-3] = Z41; - /* end of outer loop */ - } - /* compute rows at end that are not a multiple of block size */ - for (; i < n; i++) { - /* compute all 1 x 1 block of X, from rows i..i+1-1 */ - /* set the Z matrix to 0 */ - Z11=0; - ell = L - i; - ex = B; - /* the inner loop that computes outer products and adds them to Z */ - for (j=i-4; j >= 0; j -= 4) { - /* load p and q values */ - p1=ell[0]; - q1=ex[0]; - /* compute outer product and add it to the Z matrix */ - m11 = p1 * q1; - ell += lskip1; - Z11 += m11; - /* load p and q values */ - p1=ell[0]; - q1=ex[-1]; - /* compute outer product and add it to the Z matrix */ - m11 = p1 * q1; - ell += lskip1; - Z11 += m11; - /* load p and q values */ - p1=ell[0]; - q1=ex[-2]; - /* compute outer product and add it to the Z matrix */ - m11 = p1 * q1; - ell += lskip1; - Z11 += m11; - /* load p and q values */ - p1=ell[0]; - q1=ex[-3]; - /* compute outer product and add it to the Z matrix */ - m11 = p1 * q1; - ell += lskip1; - ex -= 4; - Z11 += m11; - /* end of inner loop */ - } - /* compute left-over iterations */ - j += 4; - for (; j > 0; j--) { - /* load p and q values */ - p1=ell[0]; - q1=ex[0]; - /* compute outer product and add it to the Z matrix */ - m11 = p1 * q1; - ell += lskip1; - ex -= 1; - Z11 += m11; - } - /* finish computing the X(i) block */ - Z11 = ex[0] - Z11; - ex[0] = Z11; - } -} - - - -void btVectorScale (btScalar *a, const btScalar *d, int n) -{ - btAssert (a && d && n >= 0); - for (int i=0; i 0 && nskip >= n); - btSolveL1 (L,b,n,nskip); - btVectorScale (b,d,n); - btSolveL1T (L,b,n,nskip); -} - - - -//*************************************************************************** - -// swap row/column i1 with i2 in the n*n matrix A. the leading dimension of -// A is nskip. this only references and swaps the lower triangle. -// if `do_fast_row_swaps' is nonzero and row pointers are being used, then -// rows will be swapped by exchanging row pointers. otherwise the data will -// be copied. - -static void btSwapRowsAndCols (BTATYPE A, int n, int i1, int i2, int nskip, - int do_fast_row_swaps) -{ - btAssert (A && n > 0 && i1 >= 0 && i2 >= 0 && i1 < n && i2 < n && - nskip >= n && i1 < i2); - -# ifdef BTROWPTRS - btScalar *A_i1 = A[i1]; - btScalar *A_i2 = A[i2]; - for (int i=i1+1; i0 && i1 >=0 && i2 >= 0 && i1 < n && i2 < n && nskip >= n && i1 <= i2); - if (i1==i2) return; - - btSwapRowsAndCols (A,n,i1,i2,nskip,do_fast_row_swaps); - - tmpr = x[i1]; - x[i1] = x[i2]; - x[i2] = tmpr; - - tmpr = b[i1]; - b[i1] = b[i2]; - b[i2] = tmpr; - - tmpr = w[i1]; - w[i1] = w[i2]; - w[i2] = tmpr; - - tmpr = lo[i1]; - lo[i1] = lo[i2]; - lo[i2] = tmpr; - - tmpr = hi[i1]; - hi[i1] = hi[i2]; - hi[i2] = tmpr; - - tmpi = p[i1]; - p[i1] = p[i2]; - p[i2] = tmpi; - - tmpb = state[i1]; - state[i1] = state[i2]; - state[i2] = tmpb; - - if (findex) { - tmpi = findex[i1]; - findex[i1] = findex[i2]; - findex[i2] = tmpi; - } -} - - - - -//*************************************************************************** -// btLCP manipulator object. this represents an n*n LCP problem. -// -// two index sets C and N are kept. each set holds a subset of -// the variable indexes 0..n-1. an index can only be in one set. -// initially both sets are empty. -// -// the index set C is special: solutions to A(C,C)\A(C,i) can be generated. - -//*************************************************************************** -// fast implementation of btLCP. see the above definition of btLCP for -// interface comments. -// -// `p' records the permutation of A,x,b,w,etc. p is initially 1:n and is -// permuted as the other vectors/matrices are permuted. -// -// A,x,b,w,lo,hi,state,findex,p,c are permuted such that sets C,N have -// contiguous indexes. the don't-care indexes follow N. -// -// an L*D*L' factorization is maintained of A(C,C), and whenever indexes are -// added or removed from the set C the factorization is updated. -// thus L*D*L'=A[C,C], i.e. a permuted top left nC*nC submatrix of A. -// the leading dimension of the matrix L is always `nskip'. -// -// at the start there may be other indexes that are unbounded but are not -// included in `nub'. btLCP will permute the matrix so that absolutely all -// unbounded vectors are at the start. thus there may be some initial -// permutation. -// -// the algorithms here assume certain patterns, particularly with respect to -// index transfer. - -#ifdef btLCP_FAST - -struct btLCP -{ - const int m_n; - const int m_nskip; - int m_nub; - int m_nC, m_nN; // size of each index set - BTATYPE const m_A; // A rows - btScalar *const m_x, * const m_b, *const m_w, *const m_lo,* const m_hi; // permuted LCP problem data - btScalar *const m_L, *const m_d; // L*D*L' factorization of set C - btScalar *const m_Dell, *const m_ell, *const m_tmp; - bool *const m_state; - int *const m_findex, *const m_p, *const m_C; - - btLCP (int _n, int _nskip, int _nub, btScalar *_Adata, btScalar *_x, btScalar *_b, btScalar *_w, - btScalar *_lo, btScalar *_hi, btScalar *_L, btScalar *_d, - btScalar *_Dell, btScalar *_ell, btScalar *_tmp, - bool *_state, int *_findex, int *_p, int *_C, btScalar **Arows); - int getNub() const { return m_nub; } - void transfer_i_to_C (int i); - void transfer_i_to_N (int i) { m_nN++; } // because we can assume C and N span 1:i-1 - void transfer_i_from_N_to_C (int i); - void transfer_i_from_C_to_N (int i, btAlignedObjectArray& scratch); - int numC() const { return m_nC; } - int numN() const { return m_nN; } - int indexC (int i) const { return i; } - int indexN (int i) const { return i+m_nC; } - btScalar Aii (int i) const { return BTAROW(i)[i]; } - btScalar AiC_times_qC (int i, btScalar *q) const { return btLargeDot (BTAROW(i), q, m_nC); } - btScalar AiN_times_qN (int i, btScalar *q) const { return btLargeDot (BTAROW(i)+m_nC, q+m_nC, m_nN); } - void pN_equals_ANC_times_qC (btScalar *p, btScalar *q); - void pN_plusequals_ANi (btScalar *p, int i, int sign=1); - void pC_plusequals_s_times_qC (btScalar *p, btScalar s, btScalar *q); - void pN_plusequals_s_times_qN (btScalar *p, btScalar s, btScalar *q); - void solve1 (btScalar *a, int i, int dir=1, int only_transfer=0); - void unpermute(); -}; - - -btLCP::btLCP (int _n, int _nskip, int _nub, btScalar *_Adata, btScalar *_x, btScalar *_b, btScalar *_w, - btScalar *_lo, btScalar *_hi, btScalar *_L, btScalar *_d, - btScalar *_Dell, btScalar *_ell, btScalar *_tmp, - bool *_state, int *_findex, int *_p, int *_C, btScalar **Arows): - m_n(_n), m_nskip(_nskip), m_nub(_nub), m_nC(0), m_nN(0), -# ifdef BTROWPTRS - m_A(Arows), -#else - m_A(_Adata), -#endif - m_x(_x), m_b(_b), m_w(_w), m_lo(_lo), m_hi(_hi), - m_L(_L), m_d(_d), m_Dell(_Dell), m_ell(_ell), m_tmp(_tmp), - m_state(_state), m_findex(_findex), m_p(_p), m_C(_C) -{ - { - btSetZero (m_x,m_n); - } - - { -# ifdef BTROWPTRS - // make matrix row pointers - btScalar *aptr = _Adata; - BTATYPE A = m_A; - const int n = m_n, nskip = m_nskip; - for (int k=0; k nub - { - const int n = m_n; - const int nub = m_nub; - if (nub < n) { - for (int k=0; k<100; k++) { - int i1,i2; - do { - i1 = dRandInt(n-nub)+nub; - i2 = dRandInt(n-nub)+nub; - } - while (i1 > i2); - //printf ("--> %d %d\n",i1,i2); - btSwapProblem (m_A,m_x,m_b,m_w,m_lo,m_hi,m_p,m_state,m_findex,n,i1,i2,m_nskip,0); - } - } - */ - - // permute the problem so that *all* the unbounded variables are at the - // start, i.e. look for unbounded variables not included in `nub'. we can - // potentially push up `nub' this way and get a bigger initial factorization. - // note that when we swap rows/cols here we must not just swap row pointers, - // as the initial factorization relies on the data being all in one chunk. - // variables that have findex >= 0 are *not* considered to be unbounded even - // if lo=-inf and hi=inf - this is because these limits may change during the - // solution process. - - { - int *findex = m_findex; - btScalar *lo = m_lo, *hi = m_hi; - const int n = m_n; - for (int k = m_nub; k= 0) continue; - if (lo[k]==-BT_INFINITY && hi[k]==BT_INFINITY) { - btSwapProblem (m_A,m_x,m_b,m_w,lo,hi,m_p,m_state,findex,n,m_nub,k,m_nskip,0); - m_nub++; - } - } - } - - // if there are unbounded variables at the start, factorize A up to that - // point and solve for x. this puts all indexes 0..nub-1 into C. - if (m_nub > 0) { - const int nub = m_nub; - { - btScalar *Lrow = m_L; - const int nskip = m_nskip; - for (int j=0; j nub such that all findex variables are at the end - if (m_findex) { - const int nub = m_nub; - int *findex = m_findex; - int num_at_end = 0; - for (int k=m_n-1; k >= nub; k--) { - if (findex[k] >= 0) { - btSwapProblem (m_A,m_x,m_b,m_w,m_lo,m_hi,m_p,m_state,findex,m_n,k,m_n-1-num_at_end,m_nskip,1); - num_at_end++; - } - } - } - - // print info about indexes - /* - { - const int n = m_n; - const int nub = m_nub; - for (int k=0; k 0) { - // ell,Dell were computed by solve1(). note, ell = D \ L1solve (L,A(i,C)) - { - const int nC = m_nC; - btScalar *const Ltgt = m_L + nC*m_nskip, *ell = m_ell; - for (int j=0; j 0) { - { - btScalar *const aptr = BTAROW(i); - btScalar *Dell = m_Dell; - const int *C = m_C; -# ifdef BTNUB_OPTIMIZATIONS - // if nub>0, initial part of aptr unpermuted - const int nub = m_nub; - int j=0; - for ( ; j 0 && nskip >= n && r >= 0 && r < n); - if (r >= n-1) return; - if (r > 0) { - { - const size_t move_size = (n-r-1)*sizeof(btScalar); - btScalar *Adst = A + r; - for (int i=0; i& scratch) -{ - btAssert (L && d && a && n > 0 && nskip >= n); - - if (n < 2) return; - scratch.resize(2*nskip); - btScalar *W1 = &scratch[0]; - - btScalar *W2 = W1 + nskip; - - W1[0] = btScalar(0.0); - W2[0] = btScalar(0.0); - for (int j=1; j j) ? _BTGETA(i,j) : _BTGETA(j,i)) - -inline size_t btEstimateLDLTAddTLTmpbufSize(int nskip) -{ - return nskip * 2 * sizeof(btScalar); -} - - -void btLDLTRemove (btScalar **A, const int *p, btScalar *L, btScalar *d, - int n1, int n2, int r, int nskip, btAlignedObjectArray& scratch) -{ - btAssert(A && p && L && d && n1 > 0 && n2 > 0 && r >= 0 && r < n2 && - n1 >= n2 && nskip >= n1); - #ifdef BT_DEBUG - for (int i=0; i= 0 && p[i] < n1); - #endif - - if (r==n2-1) { - return; // deleting last row/col is easy - } - else { - size_t LDLTAddTL_size = btEstimateLDLTAddTLTmpbufSize(nskip); - btAssert(LDLTAddTL_size % sizeof(btScalar) == 0); - scratch.resize(nskip * 2+n2); - btScalar *tmp = &scratch[0]; - if (r==0) { - btScalar *a = (btScalar *)((char *)tmp + LDLTAddTL_size); - const int p_0 = p[0]; - for (int i=0; i& scratch) -{ - { - int *C = m_C; - // remove a row/column from the factorization, and adjust the - // indexes (black magic!) - int last_idx = -1; - const int nC = m_nC; - int j = 0; - for ( ; j 0) { - const int nN = m_nN; - for (int j=0; j 0) { - { - btScalar *Dell = m_Dell; - int *C = m_C; - btScalar *aptr = BTAROW(i); -# ifdef BTNUB_OPTIMIZATIONS - // if nub>0, initial part of aptr[] is guaranteed unpermuted - const int nub = m_nub; - int j=0; - for ( ; j 0) { - int *C = m_C; - btScalar *tmp = m_tmp; - const int nC = m_nC; - for (int j=0; j0 && A && x && b && lo && hi && nub >= 0 && nub <= n); - btAssert(outer_w); - -#ifdef BT_DEBUG - { - // check restrictions on lo and hi - for (int k=0; k= 0); - } -# endif - - - // if all the variables are unbounded then we can just factor, solve, - // and return - if (nub >= n) - { - - - int nskip = (n); - btFactorLDLT (A, outer_w, n, nskip); - btSolveLDLT (A, outer_w, b, n, nskip); - memcpy (x, b, n*sizeof(btScalar)); - - return !s_error; - } - - const int nskip = (n); - scratchMem.L.resize(n*nskip); - - scratchMem.d.resize(n); - - btScalar *w = outer_w; - scratchMem.delta_w.resize(n); - scratchMem.delta_x.resize(n); - scratchMem.Dell.resize(n); - scratchMem.ell.resize(n); - scratchMem.Arows.resize(n); - scratchMem.p.resize(n); - scratchMem.C.resize(n); - - // for i in N, state[i] is 0 if x(i)==lo(i) or 1 if x(i)==hi(i) - scratchMem.state.resize(n); - - - // create LCP object. note that tmp is set to delta_w to save space, this - // optimization relies on knowledge of how tmp is used, so be careful! - btLCP lcp(n,nskip,nub,A,x,b,w,lo,hi,&scratchMem.L[0],&scratchMem.d[0],&scratchMem.Dell[0],&scratchMem.ell[0],&scratchMem.delta_w[0],&scratchMem.state[0],findex,&scratchMem.p[0],&scratchMem.C[0],&scratchMem.Arows[0]); - int adj_nub = lcp.getNub(); - - // loop over all indexes adj_nub..n-1. for index i, if x(i),w(i) satisfy the - // LCP conditions then i is added to the appropriate index set. otherwise - // x(i),w(i) is driven either +ve or -ve to force it to the valid region. - // as we drive x(i), x(C) is also adjusted to keep w(C) at zero. - // while driving x(i) we maintain the LCP conditions on the other variables - // 0..i-1. we do this by watching out for other x(i),w(i) values going - // outside the valid region, and then switching them between index sets - // when that happens. - - bool hit_first_friction_index = false; - for (int i=adj_nub; i= 0) { - // un-permute x into delta_w, which is not being used at the moment - for (int j=0; j= 0) { - lcp.transfer_i_to_N (i); - scratchMem.state[i] = false; - } - else if (hi[i]==0 && w[i] <= 0) { - lcp.transfer_i_to_N (i); - scratchMem.state[i] = true; - } - else if (w[i]==0) { - // this is a degenerate case. by the time we get to this test we know - // that lo != 0, which means that lo < 0 as lo is not allowed to be +ve, - // and similarly that hi > 0. this means that the line segment - // corresponding to set C is at least finite in extent, and we are on it. - // NOTE: we must call lcp.solve1() before lcp.transfer_i_to_C() - lcp.solve1 (&scratchMem.delta_x[0],i,0,1); - - lcp.transfer_i_to_C (i); - } - else { - // we must push x(i) and w(i) - for (;;) { - int dir; - btScalar dirf; - // find direction to push on x(i) - if (w[i] <= 0) { - dir = 1; - dirf = btScalar(1.0); - } - else { - dir = -1; - dirf = btScalar(-1.0); - } - - // compute: delta_x(C) = -dir*A(C,C)\A(C,i) - lcp.solve1 (&scratchMem.delta_x[0],i,dir); - - // note that delta_x[i] = dirf, but we wont bother to set it - - // compute: delta_w = A*delta_x ... note we only care about - // delta_w(N) and delta_w(i), the rest is ignored - lcp.pN_equals_ANC_times_qC (&scratchMem.delta_w[0],&scratchMem.delta_x[0]); - lcp.pN_plusequals_ANi (&scratchMem.delta_w[0],i,dir); - scratchMem.delta_w[i] = lcp.AiC_times_qC (i,&scratchMem.delta_x[0]) + lcp.Aii(i)*dirf; - - // find largest step we can take (size=s), either to drive x(i),w(i) - // to the valid LCP region or to drive an already-valid variable - // outside the valid region. - - int cmd = 1; // index switching command - int si = 0; // si = index to switch if cmd>3 - btScalar s = -w[i]/scratchMem.delta_w[i]; - if (dir > 0) { - if (hi[i] < BT_INFINITY) { - btScalar s2 = (hi[i]-x[i])*dirf; // was (hi[i]-x[i])/dirf // step to x(i)=hi(i) - if (s2 < s) { - s = s2; - cmd = 3; - } - } - } - else { - if (lo[i] > -BT_INFINITY) { - btScalar s2 = (lo[i]-x[i])*dirf; // was (lo[i]-x[i])/dirf // step to x(i)=lo(i) - if (s2 < s) { - s = s2; - cmd = 2; - } - } - } - - { - const int numN = lcp.numN(); - for (int k=0; k < numN; ++k) { - const int indexN_k = lcp.indexN(k); - if (!scratchMem.state[indexN_k] ? scratchMem.delta_w[indexN_k] < 0 : scratchMem.delta_w[indexN_k] > 0) { - // don't bother checking if lo=hi=0 - if (lo[indexN_k] == 0 && hi[indexN_k] == 0) continue; - btScalar s2 = -w[indexN_k] / scratchMem.delta_w[indexN_k]; - if (s2 < s) { - s = s2; - cmd = 4; - si = indexN_k; - } - } - } - } - - { - const int numC = lcp.numC(); - for (int k=adj_nub; k < numC; ++k) { - const int indexC_k = lcp.indexC(k); - if (scratchMem.delta_x[indexC_k] < 0 && lo[indexC_k] > -BT_INFINITY) { - btScalar s2 = (lo[indexC_k]-x[indexC_k]) / scratchMem.delta_x[indexC_k]; - if (s2 < s) { - s = s2; - cmd = 5; - si = indexC_k; - } - } - if (scratchMem.delta_x[indexC_k] > 0 && hi[indexC_k] < BT_INFINITY) { - btScalar s2 = (hi[indexC_k]-x[indexC_k]) / scratchMem.delta_x[indexC_k]; - if (s2 < s) { - s = s2; - cmd = 6; - si = indexC_k; - } - } - } - } - - //static char* cmdstring[8] = {0,"->C","->NL","->NH","N->C", - // "C->NL","C->NH"}; - //printf ("cmd=%d (%s), si=%d\n",cmd,cmdstring[cmd],(cmd>3) ? si : i); - - // if s <= 0 then we've got a problem. if we just keep going then - // we're going to get stuck in an infinite loop. instead, just cross - // our fingers and exit with the current solution. - if (s <= btScalar(0.0)) - { -// printf("LCP internal error, s <= 0 (s=%.4e)",(double)s); - if (i < n) { - btSetZero (x+i,n-i); - btSetZero (w+i,n-i); - } - s_error = true; - break; - } - - // apply x = x + s * delta_x - lcp.pC_plusequals_s_times_qC (x, s, &scratchMem.delta_x[0]); - x[i] += s * dirf; - - // apply w = w + s * delta_w - lcp.pN_plusequals_s_times_qN (w, s, &scratchMem.delta_w[0]); - w[i] += s * scratchMem.delta_w[i]; - -// void *tmpbuf; - // switch indexes between sets if necessary - switch (cmd) { - case 1: // done - w[i] = 0; - lcp.transfer_i_to_C (i); - break; - case 2: // done - x[i] = lo[i]; - scratchMem.state[i] = false; - lcp.transfer_i_to_N (i); - break; - case 3: // done - x[i] = hi[i]; - scratchMem.state[i] = true; - lcp.transfer_i_to_N (i); - break; - case 4: // keep going - w[si] = 0; - lcp.transfer_i_from_N_to_C (si); - break; - case 5: // keep going - x[si] = lo[si]; - scratchMem.state[si] = false; - lcp.transfer_i_from_C_to_N (si, scratchMem.m_scratch); - break; - case 6: // keep going - x[si] = hi[si]; - scratchMem.state[si] = true; - lcp.transfer_i_from_C_to_N (si, scratchMem.m_scratch); - break; - } - - if (cmd <= 3) break; - } // for (;;) - } // else - - if (s_error) - { - break; - } - } // for (int i=adj_nub; i= 0 - (2) x = hi, w <= 0 - (3) lo < x < hi, w = 0 -A is a matrix of dimension n*n, everything else is a vector of size n*1. -lo and hi can be +/- dInfinity as needed. the first `nub' variables are -unbounded, i.e. hi and lo are assumed to be +/- dInfinity. - -we restrict lo(i) <= 0 and hi(i) >= 0. - -the original data (A,b) may be modified by this function. - -if the `findex' (friction index) parameter is nonzero, it points to an array -of index values. in this case constraints that have findex[i] >= 0 are -special. all non-special constraints are solved for, then the lo and hi values -for the special constraints are set: - hi[i] = abs( hi[i] * x[findex[i]] ) - lo[i] = -hi[i] -and the solution continues. this mechanism allows a friction approximation -to be implemented. the first `nub' variables are assumed to have findex < 0. - -*/ - - -#ifndef _BT_LCP_H_ -#define _BT_LCP_H_ - -#include -#include -#include - - -#include "LinearMath/btScalar.h" -#include "LinearMath/btAlignedObjectArray.h" - -struct btDantzigScratchMemory -{ - btAlignedObjectArray m_scratch; - btAlignedObjectArray L; - btAlignedObjectArray d; - btAlignedObjectArray delta_w; - btAlignedObjectArray delta_x; - btAlignedObjectArray Dell; - btAlignedObjectArray ell; - btAlignedObjectArray Arows; - btAlignedObjectArray p; - btAlignedObjectArray C; - btAlignedObjectArray state; -}; - -//return false if solving failed -bool btSolveDantzigLCP (int n, btScalar *A, btScalar *x, btScalar *b, btScalar *w, - int nub, btScalar *lo, btScalar *hi, int *findex,btDantzigScratchMemory& scratch); - - - -#endif //_BT_LCP_H_ diff --git a/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigSolver.h b/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigSolver.h deleted file mode 100644 index 2a2f2d3d3..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btDantzigSolver.h +++ /dev/null @@ -1,112 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ -///original version written by Erwin Coumans, October 2013 - -#ifndef BT_DANTZIG_SOLVER_H -#define BT_DANTZIG_SOLVER_H - -#include "btMLCPSolverInterface.h" -#include "btDantzigLCP.h" - - -class btDantzigSolver : public btMLCPSolverInterface -{ -protected: - - btScalar m_acceptableUpperLimitSolution; - - btAlignedObjectArray m_tempBuffer; - - btAlignedObjectArray m_A; - btAlignedObjectArray m_b; - btAlignedObjectArray m_x; - btAlignedObjectArray m_lo; - btAlignedObjectArray m_hi; - btAlignedObjectArray m_dependencies; - btDantzigScratchMemory m_scratchMemory; -public: - - btDantzigSolver() - :m_acceptableUpperLimitSolution(btScalar(1000)) - { - } - - virtual bool solveMLCP(const btMatrixXu & A, const btVectorXu & b, btVectorXu& x, const btVectorXu & lo,const btVectorXu & hi,const btAlignedObjectArray& limitDependency, int numIterations, bool useSparsity = true) - { - bool result = true; - int n = b.rows(); - if (n) - { - int nub = 0; - btAlignedObjectArray ww; - ww.resize(n); - - - const btScalar* Aptr = A.getBufferPointer(); - m_A.resize(n*n); - for (int i=0;i= m_acceptableUpperLimitSolution) - { - return false; - } - - if (x[i] <= -m_acceptableUpperLimitSolution) - { - return false; - } - } - - for (int i=0;i limitDependenciesCopy = m_limitDependencies; -// printf("solve first LCP\n"); - result = m_solver->solveMLCP(m_A, m_b, m_x, m_lo,m_hi, m_limitDependencies,infoGlobal.m_numIterations ); - if (result) - result = m_solver->solveMLCP(Acopy, m_bSplit, m_xSplit, m_lo,m_hi, limitDependenciesCopy,infoGlobal.m_numIterations ); - - } else - { - result = m_solver->solveMLCP(m_A, m_b, m_x, m_lo,m_hi, m_limitDependencies,infoGlobal.m_numIterations ); - } - return result; -} - -struct btJointNode -{ - int jointIndex; // pointer to enclosing dxJoint object - int otherBodyIndex; // *other* body this joint is connected to - int nextJointNodeIndex;//-1 for null - int constraintRowIndex; -}; - - - -void btMLCPSolver::createMLCPFast(const btContactSolverInfo& infoGlobal) -{ - int numContactRows = interleaveContactAndFriction ? 3 : 1; - - int numConstraintRows = m_allConstraintArray.size(); - int n = numConstraintRows; - { - BT_PROFILE("init b (rhs)"); - m_b.resize(numConstraintRows); - m_bSplit.resize(numConstraintRows); - //m_b.setZero(); - for (int i=0;i=0) - { - m_lo[i] = -BT_INFINITY; - m_hi[i] = BT_INFINITY; - } else - { - m_lo[i] = m_allConstraintArray[i].m_lowerLimit; - m_hi[i] = m_allConstraintArray[i].m_upperLimit; - } - } - } - - // - int m=m_allConstraintArray.size(); - - int numBodies = m_tmpSolverBodyPool.size(); - btAlignedObjectArray bodyJointNodeArray; - { - BT_PROFILE("bodyJointNodeArray.resize"); - bodyJointNodeArray.resize(numBodies,-1); - } - btAlignedObjectArray jointNodeArray; - { - BT_PROFILE("jointNodeArray.reserve"); - jointNodeArray.reserve(2*m_allConstraintArray.size()); - } - - static btMatrixXu J3; - { - BT_PROFILE("J3.resize"); - J3.resize(2*m,8); - } - static btMatrixXu JinvM3; - { - BT_PROFILE("JinvM3.resize/setZero"); - - JinvM3.resize(2*m,8); - JinvM3.setZero(); - J3.setZero(); - } - int cur=0; - int rowOffset = 0; - static btAlignedObjectArray ofs; - { - BT_PROFILE("ofs resize"); - ofs.resize(0); - ofs.resizeNoInitialize(m_allConstraintArray.size()); - } - { - BT_PROFILE("Compute J and JinvM"); - int c=0; - - int numRows = 0; - - for (int i=0;igetInvMass(); - btVector3 relPosCrossNormalInvInertia = m_allConstraintArray[i+row].m_relpos1CrossNormal * orgBodyA->getInvInertiaTensorWorld(); - - for (int r=0;r<3;r++) - { - J3.setElem(cur,r,m_allConstraintArray[i+row].m_contactNormal1[r]); - J3.setElem(cur,r+4,m_allConstraintArray[i+row].m_relpos1CrossNormal[r]); - JinvM3.setElem(cur,r,normalInvMass[r]); - JinvM3.setElem(cur,r+4,relPosCrossNormalInvInertia[r]); - } - J3.setElem(cur,3,0); - JinvM3.setElem(cur,3,0); - J3.setElem(cur,7,0); - JinvM3.setElem(cur,7,0); - } - } else - { - cur += numRows; - } - if (orgBodyB) - { - - { - int slotB=-1; - //find free jointNode slot for sbA - slotB =jointNodeArray.size(); - jointNodeArray.expand();//NonInitializing(); - int prevSlot = bodyJointNodeArray[sbB]; - bodyJointNodeArray[sbB] = slotB; - jointNodeArray[slotB].nextJointNodeIndex = prevSlot; - jointNodeArray[slotB].jointIndex = c; - jointNodeArray[slotB].otherBodyIndex = orgBodyA ? sbA : -1; - jointNodeArray[slotB].constraintRowIndex = i; - } - - for (int row=0;rowgetInvMass(); - btVector3 relPosInvInertiaB = m_allConstraintArray[i+row].m_relpos2CrossNormal * orgBodyB->getInvInertiaTensorWorld(); - - for (int r=0;r<3;r++) - { - J3.setElem(cur,r,m_allConstraintArray[i+row].m_contactNormal2[r]); - J3.setElem(cur,r+4,m_allConstraintArray[i+row].m_relpos2CrossNormal[r]); - JinvM3.setElem(cur,r,normalInvMassB[r]); - JinvM3.setElem(cur,r+4,relPosInvInertiaB[r]); - } - J3.setElem(cur,3,0); - JinvM3.setElem(cur,3,0); - J3.setElem(cur,7,0); - JinvM3.setElem(cur,7,0); - } - } - else - { - cur += numRows; - } - rowOffset+=numRows; - - } - - } - - - //compute JinvM = J*invM. - const btScalar* JinvM = JinvM3.getBufferPointer(); - - const btScalar* Jptr = J3.getBufferPointer(); - { - BT_PROFILE("m_A.resize"); - m_A.resize(n,n); - } - - { - BT_PROFILE("m_A.setZero"); - m_A.setZero(); - } - int c=0; - { - int numRows = 0; - BT_PROFILE("Compute A"); - for (int i=0;i=0) - { - int j0 = jointNodeArray[startJointNodeA].jointIndex; - int cr0 = jointNodeArray[startJointNodeA].constraintRowIndex; - if (j0=0) - { - int j1 = jointNodeArray[startJointNodeB].jointIndex; - int cj1 = jointNodeArray[startJointNodeB].constraintRowIndex; - - if (j1m_tmpSolverBodyPool.size(); - int numConstraintRows = m_allConstraintArray.size(); - - m_b.resize(numConstraintRows); - if (infoGlobal.m_splitImpulse) - m_bSplit.resize(numConstraintRows); - - for (int i=0;igetInvInertiaTensorWorld()[r][c] : 0); - } - - static btMatrixXu J; - J.resize(numConstraintRows,6*numBodies); - J.setZero(); - - m_lo.resize(numConstraintRows); - m_hi.resize(numConstraintRows); - - for (int i=0;i m_limitDependencies; - btConstraintArray m_allConstraintArray; - btMLCPSolverInterface* m_solver; - int m_fallback; - - virtual btScalar solveGroupCacheFriendlySetup(btCollisionObject** bodies, int numBodies, btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); - virtual btScalar solveGroupCacheFriendlyIterations(btCollisionObject** bodies ,int numBodies,btPersistentManifold** manifoldPtr, int numManifolds,btTypedConstraint** constraints,int numConstraints,const btContactSolverInfo& infoGlobal,btIDebugDraw* debugDrawer); - virtual void createMLCP(const btContactSolverInfo& infoGlobal); - virtual void createMLCPFast(const btContactSolverInfo& infoGlobal); - - //return true is it solves the problem successfully - virtual bool solveMLCP(const btContactSolverInfo& infoGlobal); - -public: - - btMLCPSolver( btMLCPSolverInterface* solver); - virtual ~btMLCPSolver(); - - void setMLCPSolver(btMLCPSolverInterface* solver) - { - m_solver = solver; - } - - int getNumFallbacks() const - { - return m_fallback; - } - void setNumFallbacks(int num) - { - m_fallback = num; - } - - virtual btConstraintSolverType getSolverType() const - { - return BT_MLCP_SOLVER; - } - -}; - - -#endif //BT_MLCP_SOLVER_H diff --git a/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btMLCPSolverInterface.h b/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btMLCPSolverInterface.h deleted file mode 100644 index 25bb3f6d3..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btMLCPSolverInterface.h +++ /dev/null @@ -1,33 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ -///original version written by Erwin Coumans, October 2013 - -#ifndef BT_MLCP_SOLVER_INTERFACE_H -#define BT_MLCP_SOLVER_INTERFACE_H - -#include "LinearMath/btMatrixX.h" - -class btMLCPSolverInterface -{ -public: - virtual ~btMLCPSolverInterface() - { - } - - //return true is it solves the problem successfully - virtual bool solveMLCP(const btMatrixXu & A, const btVectorXu & b, btVectorXu& x, const btVectorXu & lo,const btVectorXu & hi,const btAlignedObjectArray& limitDependency, int numIterations, bool useSparsity = true)=0; -}; - -#endif //BT_MLCP_SOLVER_INTERFACE_H diff --git a/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btPATHSolver.h b/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btPATHSolver.h deleted file mode 100644 index 9ec31a6d4..000000000 --- a/Engine/lib/bullet/src/BulletDynamics/MLCPSolvers/btPATHSolver.h +++ /dev/null @@ -1,151 +0,0 @@ -/* -Bullet Continuous Collision Detection and Physics Library -Copyright (c) 2003-2013 Erwin Coumans http://bulletphysics.org - -This software is provided 'as-is', without any express or implied warranty. -In no event will the authors be held liable for any damages arising from the use of this software. -Permission is granted to anyone to use this software for any purpose, -including commercial applications, and to alter it and redistribute it freely, -subject to the following restrictions: - -1. The origin of this software must not be misrepresented; you must not claim that you wrote the original software. If you use this software in a product, an acknowledgment in the product documentation would be appreciated but is not required. -2. Altered source versions must be plainly marked as such, and must not be misrepresented as being the original software. -3. This notice may not be removed or altered from any source distribution. -*/ -///original version written by Erwin Coumans, October 2013 - - -#ifndef BT_PATH_SOLVER_H -#define BT_PATH_SOLVER_H - -//#define BT_USE_PATH -#ifdef BT_USE_PATH - -extern "C" { -#include "PATH/SimpleLCP.h" -#include "PATH/License.h" -#include "PATH/Error_Interface.h" -}; - void __stdcall MyError(Void *data, Char *msg) -{ - printf("Path Error: %s\n",msg); -} - void __stdcall MyWarning(Void *data, Char *msg) -{ - printf("Path Warning: %s\n",msg); -} - -Error_Interface e; - - - -#include "btMLCPSolverInterface.h" -#include "Dantzig/lcp.h" - -class btPathSolver : public btMLCPSolverInterface -{ -public: - - btPathSolver() - { - License_SetString("2069810742&Courtesy_License&&&USR&2013&14_12_2011&1000&PATH&GEN&31_12_2013&0_0_0&0&0_0"); - e.error_data = 0; - e.warning = MyWarning; - e.error = MyError; - Error_SetInterface(&e); - } - - - virtual bool solveMLCP(const btMatrixXu & A, const btVectorXu & b, btVectorXu& x, const btVectorXu & lo,const btVectorXu & hi,const btAlignedObjectArray& limitDependency, int numIterations, bool useSparsity = true) - { - MCP_Termination status; - - - int numVariables = b.rows(); - if (0==numVariables) - return true; - - /* - variables - the number of variables in the problem - - m_nnz - the number of nonzeros in the M matrix - - m_i - a vector of size m_nnz containing the row indices for M - - m_j - a vector of size m_nnz containing the column indices for M - - m_ij - a vector of size m_nnz containing the data for M - - q - a vector of size variables - - lb - a vector of size variables containing the lower bounds on x - - ub - a vector of size variables containing the upper bounds on x - */ - btAlignedObjectArray values; - btAlignedObjectArray rowIndices; - btAlignedObjectArray colIndices; - - for (int i=0;i zResult; - zResult.resize(numVariables); - btAlignedObjectArray rhs; - btAlignedObjectArray upperBounds; - btAlignedObjectArray lowerBounds; - for (int i=0;i& limitDependency, int numIterations, bool useSparsity = true) - { - //A is a m-n matrix, m rows, n columns - btAssert(A.rows() == b.rows()); - - int i, j, numRows = A.rows(); - - float delta; - - for (int k = 0; k =0) - { - s = x[limitDependency[i]]; - if (s<0) - s=1; - } - - if (x[i]hi[i]*s) - x[i]=hi[i]*s; - } - } - return true; - } - -}; - -#endif //BT_SOLVE_PROJECTED_GAUSS_SEIDEL_H From 7b184312df2a2dfcc337b624dfd8f50179bd9d1d Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 13 Jul 2014 09:12:43 +0200 Subject: [PATCH 116/317] Moved matrix multiplication implementation test. --- Engine/source/math/test/mMatrixTest.cpp | 106 +++++++++++++++++++ Engine/source/unit/tests/testMatrixMul.cpp | 116 --------------------- 2 files changed, 106 insertions(+), 116 deletions(-) create mode 100644 Engine/source/math/test/mMatrixTest.cpp delete mode 100644 Engine/source/unit/tests/testMatrixMul.cpp diff --git a/Engine/source/math/test/mMatrixTest.cpp b/Engine/source/math/test/mMatrixTest.cpp new file mode 100644 index 000000000..c582c0932 --- /dev/null +++ b/Engine/source/math/test/mMatrixTest.cpp @@ -0,0 +1,106 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "platform/platform.h" +#include "math/mMatrix.h" +#include "math/mRandom.h" + +extern void default_matF_x_matF_C(const F32 *a, const F32 *b, F32 *mresult); +extern void mInstallLibrary_ASM(); + +// If we're x86 and not Mac, then include these. There's probably a better way to do this. +#if defined(TORQUE_CPU_X86) && !defined(TORQUE_OS_MAC) +extern "C" void Athlon_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result); +extern "C" void SSE_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result); +#endif + +#if defined( __VEC__ ) +extern void vec_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result); +#endif + +TEST(MatrixF, MultiplyImplmentations) +{ + F32 m1[16], m2[16], mrC[16]; + + // I am not positive that the best way to do this is to use random numbers + // but I think that using some kind of standard matrix may not always catch + // all problems. + for (S32 i = 0; i < 16; i++) + { + m1[i] = gRandGen.randF(); + m2[i] = gRandGen.randF(); + } + + // C will be the baseline + default_matF_x_matF_C(m1, m2, mrC); + +#if defined(TORQUE_CPU_X86) && !defined(TORQUE_OS_MAC) + // Check the CPU info + U32 cpuProperties = Platform::SystemInfo.processor.properties; + bool same; + + // Test 3D NOW! if it is available + F32 mrAMD[16]; + if (cpuProperties & CPU_PROP_3DNOW) + { + Athlon_MatrixF_x_MatrixF(m1, m2, mrAMD); + + same = true; + for (S32 i = 0; i < 16; i++) + same &= mIsEqual(mrC[i], mrAMD[i]); + + EXPECT_TRUE(same) << "Matrix multiplication verification failed. (C vs. 3D NOW!)"; + } + + // Test SSE if it is available + F32 mrSSE[16]; + if (cpuProperties & CPU_PROP_SSE) + { + SSE_MatrixF_x_MatrixF(m1, m2, mrSSE); + + same = true; + for (S32 i = 0; i < 16; i++) + same &= mIsEqual(mrC[i], mrSSE[i]); + + EXPECT_TRUE(same) << "Matrix multiplication verification failed. (C vs. SSE)"; + } + + same = true; +#endif + + // If Altivec exists, test it! +#if defined(__VEC__) + bool same = false; + F32 mrVEC[16]; + + vec_MatrixF_x_MatrixF(m1, m2, mrVEC); + + for (S32 i = 0; i < 16; i++) + same &= isEqual(mrC[i], mrVEC[i]); + + EXPECT_TRUE(same) << "Matrix multiplication verification failed. (C vs. Altivec)"; +#endif +} + +#endif \ No newline at end of file diff --git a/Engine/source/unit/tests/testMatrixMul.cpp b/Engine/source/unit/tests/testMatrixMul.cpp deleted file mode 100644 index 939d35318..000000000 --- a/Engine/source/unit/tests/testMatrixMul.cpp +++ /dev/null @@ -1,116 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "platform/platform.h" -#include "unit/test.h" -#include "math/mMath.h" -#include "math/mRandom.h" - -extern void default_matF_x_matF_C(const F32 *a, const F32 *b, F32 *mresult); -extern void mInstallLibrary_ASM(); - -// If we're x86 and not Mac, then include these. There's probably a better way to do this. -#if defined(TORQUE_CPU_X86) && !defined(TORQUE_OS_MAC) -extern "C" void Athlon_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result); -extern "C" void SSE_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result); -#endif - -#if defined( __VEC__ ) -extern void vec_MatrixF_x_MatrixF(const F32 *matA, const F32 *matB, F32 *result); -#endif - -using namespace UnitTesting; - -CreateUnitTest( TestMatrixMul, "Math/Matrix/Multiply" ) -{ - // The purpose of this test is to verify that the matrix multiplication operation - // always agrees with the different implementations of itself within a reasonable - // epsilon. - void run() - { - F32 m1[16], m2[16], mrC[16]; - - // I am not positive that the best way to do this is to use random numbers - // but I think that using some kind of standard matrix may not always catch - // all problems. - for( S32 i = 0; i < 16; i++ ) - { - m1[i] = gRandGen.randF(); - m2[i] = gRandGen.randF(); - } - - // C will be the baseline - default_matF_x_matF_C( m1, m2, mrC ); - -#if defined(TORQUE_CPU_X86) && !defined(TORQUE_OS_MAC) - // Check the CPU info - U32 cpuProperties = Platform::SystemInfo.processor.properties; - bool same = true; - // Test 3D NOW! if it is available - F32 mrAMD[16]; - if( cpuProperties & CPU_PROP_3DNOW ) - { - Athlon_MatrixF_x_MatrixF( m1, m2, mrAMD ); - - for( S32 i = 0; i < 16; i++ ) - same &= mIsEqual( mrC[i], mrAMD[i] ); - - test( same, "Matrix multiplication verification failed. (C vs. 3D NOW!)" ); - } - else - warn( "Could not test 3D NOW! matrix multiplication because CPU does not support 3D NOW!." ); - - same = true; - - // Test SSE if it is available - F32 mrSSE[16]; - if( cpuProperties & CPU_PROP_SSE ) - { - SSE_MatrixF_x_MatrixF( m1, m2, mrSSE ); - - for( S32 i = 0; i < 16; i++ ) - same &= mIsEqual( mrC[i], mrSSE[i] ); - - test( same, "Matrix multiplication verification failed. (C vs. SSE)" ); - } - else - warn( "Could not test SSE matrix multiplication because CPU does not support SSE." ); - - same = true; -#endif - - // If Altivec exists, test it! -#if defined( __VEC__ ) - bool same = false; - F32 mrVEC[16]; - - vec_MatrixF_x_MatrixF( m1, m2, mrVEC ); - - for( S32 i = 0; i < 16; i++ ) - same &= isEqual( mrC[i], mrVEC[i] ); - - test( same, "Matrix multiplication verification failed. (C vs. Altivec)" ); -#else - warn( "Could not test Altivec matrix multiplication because CPU does not support Altivec." ); -#endif - } -}; From 0a8eb6fbcd4dd76644338b333d1f5e704d2bead6 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 13 Jul 2014 09:17:07 +0200 Subject: [PATCH 117/317] Remove default construction test. --- .../unit/tests/testDefaultConstruction.cpp | 67 ------------------- 1 file changed, 67 deletions(-) delete mode 100644 Engine/source/unit/tests/testDefaultConstruction.cpp diff --git a/Engine/source/unit/tests/testDefaultConstruction.cpp b/Engine/source/unit/tests/testDefaultConstruction.cpp deleted file mode 100644 index 0eaac89a7..000000000 --- a/Engine/source/unit/tests/testDefaultConstruction.cpp +++ /dev/null @@ -1,67 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "unit/test.h" -#include "console/simObject.h" - - -using namespace UnitTesting; - - -// Test to ensure that all console classes in the system are default-constructible. - -CreateUnitTest( TestDefaultConstruction, "Console/DefaultConstruction" ) -{ - void run() - { - for( AbstractClassRep* classRep = AbstractClassRep::getClassList(); - classRep != NULL; - classRep = classRep->getNextClass() ) - { - // Create object. - - ConsoleObject* object = classRep->create(); - test( object, avar( "AbstractClassRep::create failed for class '%s'", classRep->getClassName() ) ); - if( !object ) - continue; - - // Make sure it's a SimObject. - - SimObject* simObject = dynamic_cast< SimObject* >( object ); - if( !simObject ) - { - SAFE_DELETE( object ); - continue; - } - - // Register the object. - - bool result = simObject->registerObject(); - test( result, avar( "registerObject failed for object of class '%s'", classRep->getClassName() ) ); - - if( result ) - simObject->deleteObject(); - else - SAFE_DELETE( simObject ); - } - } -}; From f64e711f68dd0270687a8204aa5d8ae7be4395bf Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 13 Jul 2014 09:17:53 +0200 Subject: [PATCH 118/317] Removed component tests as they're redundant. --- Engine/source/unit/tests/testComponents.cpp | 154 -------------------- 1 file changed, 154 deletions(-) delete mode 100644 Engine/source/unit/tests/testComponents.cpp diff --git a/Engine/source/unit/tests/testComponents.cpp b/Engine/source/unit/tests/testComponents.cpp deleted file mode 100644 index b4a78ca39..000000000 --- a/Engine/source/unit/tests/testComponents.cpp +++ /dev/null @@ -1,154 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "unit/test.h" -#include "unit/memoryTester.h" -#include "component/simComponent.h" - -using namespace UnitTesting; - -////////////////////////////////////////////////////////////////////////// - -class CachedInterfaceExampleComponent : public SimComponent -{ - typedef SimComponent Parent; - - ComponentProperty mMyId; - static U32 smNumInstances; - ComponentProperty *mpU32; // CodeReview [patw, 2, 17, 2007] Make ref objects when this is in Jugg - -public: - DECLARE_CONOBJECT( CachedInterfaceExampleComponent ); - - CachedInterfaceExampleComponent() : mpU32( NULL ) - { - mMyId = ( ( 1 << 24 ) | smNumInstances++ ); - } - virtual ~CachedInterfaceExampleComponent() - { - smNumInstances--; - } - -public: - ////////////////////////////////////////////////////////////////////////// - - virtual void registerInterfaces( const SimComponent *owner ) - { - // Register a cached interface for this - registerCachedInterface( NULL, "aU32", this, &mMyId ); - } - - ////////////////////////////////////////////////////////////////////////// - - bool onComponentRegister( SimComponent *owner ) - { - // Call up to the parent first - if( !Parent::onComponentRegister( owner ) ) - return false; - - // We want to get an interface from another object in our containing component - // to simulate component interdependency. - ComponentInterfaceList list; - - // Enumerate the interfaces on the owner, only ignore interfaces that this object owns - if( !_getOwner()->getInterfaces( &list, NULL, "aU32", this, true ) ) - return false; - - // Sanity check before just assigning all willy-nilly - for( ComponentInterfaceListIterator i = list.begin(); i != list.end(); i++ ) - { - mpU32 = dynamic_cast *>( (*i) ); - - if( mpU32 != NULL ) - return true; - } - - return false; - } - - ////////////////////////////////////////////////////////////////////////// - - // CodeReview [patw, 2, 17, 2007] I'm going to make another lightweight interface - // for this functionality later - void unit_test( UnitTest *test ) - { - AssertFatal(test, "CachedInterfaceExampleComponent::unit_test - NULL UnitTest"); - - if( !test ) - return; - - test->test( mpU32 != NULL, "Pointer to dependent interface is NULL" ); - if( mpU32 ) - { - test->test( *(*mpU32) & ( 1 << 24 ), "Pointer to interface data is bogus." ); - test->test( *(*mpU32) != *mMyId, "Two of me have the same ID, bad!" ); - } - } -}; - -IMPLEMENT_CONOBJECT( CachedInterfaceExampleComponent ); -U32 CachedInterfaceExampleComponent::smNumInstances = 0; - -ConsoleDocClass( CachedInterfaceExampleComponent, - "@brief Legacy from older component system.\n\n" - "Not intended for game development, for editors or internal use only.\n\n " - "@internal"); - -////////////////////////////////////////////////////////////////////////// - -CreateUnitTest(TestComponentInterfacing, "Components/Composition") -{ - void run() - { - MemoryTester m; - m.mark(); - - SimComponent *testComponent = new SimComponent(); - CachedInterfaceExampleComponent *componentA = new CachedInterfaceExampleComponent(); - CachedInterfaceExampleComponent *componentB = new CachedInterfaceExampleComponent(); - - // Register sub-components - test( componentA->registerObject(), "Failed to register componentA" ); - test( componentB->registerObject(), "Failed to register componentB" ); - - // Add the components - test( testComponent->addComponent( componentA ), "Failed to add component a to testComponent" ); - test( testComponent->addComponent( componentB ), "Failed to add component b to testComponent" ); - - test( componentA->getOwner() == testComponent, "testComponent did not properly set the mOwner field of componentA to NULL." ); - test( componentB->getOwner() == testComponent, "testComponent did not properly set the mOwner field of componentB to NULL." ); - - // Register the object with the simulation, kicking off the interface registration - const bool registered = testComponent->registerObject(); - test( registered, "Failed to register testComponent" ); - - // Interface tests - if( registered ) - { - componentA->unit_test( this ); - componentB->unit_test( this ); - testComponent->deleteObject(); - } - - test( m.check(), "Component composition test leaked memory." ); - } -}; \ No newline at end of file From 523adea85da13dce4b19cb1e9fcdada14083454f Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 13 Jul 2014 10:14:10 +0200 Subject: [PATCH 119/317] Ported runtime class rep test. --- .../console/test/runtimeClassRepTest.cpp | 108 ++++++++++++++++++ .../source/unit/tests/testRuntimeClassRep.cpp | 102 ----------------- 2 files changed, 108 insertions(+), 102 deletions(-) create mode 100644 Engine/source/console/test/runtimeClassRepTest.cpp delete mode 100644 Engine/source/unit/tests/testRuntimeClassRep.cpp diff --git a/Engine/source/console/test/runtimeClassRepTest.cpp b/Engine/source/console/test/runtimeClassRepTest.cpp new file mode 100644 index 000000000..0787e4af5 --- /dev/null +++ b/Engine/source/console/test/runtimeClassRepTest.cpp @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "platform/platform.h" +#include "console/simBase.h" +#include "console/consoleTypes.h" +#include "console/runtimeClassRep.h" + +class RuntimeRegisteredSimObject : public SimObject +{ + typedef SimObject Parent; + +protected: + bool mFoo; + +public: + RuntimeRegisteredSimObject() : mFoo(false) {}; + + DECLARE_RUNTIME_CONOBJECT(RuntimeRegisteredSimObject); + + static void initPersistFields(); +}; + +IMPLEMENT_RUNTIME_CONOBJECT(RuntimeRegisteredSimObject); + +void RuntimeRegisteredSimObject::initPersistFields() +{ + addField("fooField", TypeBool, Offset(mFoo, RuntimeRegisteredSimObject)); +} + +TEST(Console, RuntimeClassRep) +{ + // First test to make sure that the test class is not registered (don't + // know how it could be, but that's programming for you). Stop the test if + // it is. + ASSERT_TRUE(!RuntimeRegisteredSimObject::dynRTClassRep.isRegistered()) + << "RuntimeRegisteredSimObject class was already registered with the console"; + + // This should not be able to find the class, and return null (this may + // AssertWarn as well). + ConsoleObject *conobj = ConsoleObject::create("RuntimeRegisteredSimObject"); + EXPECT_TRUE(conobj == NULL) + << "Unregistered AbstractClassRep returned non-NULL value! That is really bad!"; + + // Register with console system. + RuntimeRegisteredSimObject::dynRTClassRep.consoleRegister(); + + // Make sure that the object knows it's registered. + EXPECT_TRUE(RuntimeRegisteredSimObject::dynRTClassRep.isRegistered()) + << "RuntimeRegisteredSimObject class failed console registration"; + + // Now try again to create the instance. + conobj = ConsoleObject::create("RuntimeRegisteredSimObject"); + EXPECT_TRUE(conobj != NULL) + << "AbstractClassRep::create method failed!"; + + // Cast the instance, and test it. + RuntimeRegisteredSimObject *rtinst = + dynamic_cast(conobj); + EXPECT_TRUE(rtinst != NULL) + << "Casting failed for some reason"; + + // Register it with a name. + rtinst->registerObject("_utRRTestObject"); + EXPECT_TRUE(rtinst->isProperlyAdded()) + << "registerObject failed on test object"; + + // Now execute some script on it. + Con::evaluate("_utRRTestObject.fooField = true;"); + + // Test to make sure field worked. + EXPECT_TRUE(dAtob(rtinst->getDataField(StringTable->insert("fooField"), NULL))) + << "Set property failed on instance."; + + // BALETED + rtinst->deleteObject(); + + // Unregister the type. + RuntimeRegisteredSimObject::dynRTClassRep.consoleUnRegister(); + + // And make sure we can't create another one. + conobj = ConsoleObject::create("RuntimeRegisteredSimObject"); + EXPECT_TRUE(conobj == NULL) + << "Unregistration of type failed"; +} + +#endif \ No newline at end of file diff --git a/Engine/source/unit/tests/testRuntimeClassRep.cpp b/Engine/source/unit/tests/testRuntimeClassRep.cpp deleted file mode 100644 index 5f4d9ff7d..000000000 --- a/Engine/source/unit/tests/testRuntimeClassRep.cpp +++ /dev/null @@ -1,102 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "platform/platform.h" -#include "console/simBase.h" -#include "console/consoleTypes.h" -#include "console/runtimeClassRep.h" -#include "unit/test.h" - -using namespace UnitTesting; - -//----------------------------------------------------------------------------- - -class RuntimeRegisteredSimObject : public SimObject -{ - typedef SimObject Parent; - -protected: - bool mFoo; - -public: - RuntimeRegisteredSimObject() : mFoo( false ) {}; - - DECLARE_RUNTIME_CONOBJECT(RuntimeRegisteredSimObject); - - static void initPersistFields(); -}; - -IMPLEMENT_RUNTIME_CONOBJECT(RuntimeRegisteredSimObject); - -void RuntimeRegisteredSimObject::initPersistFields() -{ - addField( "fooField", TypeBool, Offset( mFoo, RuntimeRegisteredSimObject ) ); -} - -//----------------------------------------------------------------------------- - -CreateUnitTest( RuntimeClassRepUnitTest, "Console/RuntimeClassRep" ) -{ - void run() - { - // First test to make sure that the test class is not registered (don't know how it could be, but that's programming for you) - test( !RuntimeRegisteredSimObject::dynRTClassRep.isRegistered(), "RuntimeRegisteredSimObject class was already registered with the console" ); - - // This should not be able to find the class, and return null (this may AssertWarn as well) - ConsoleObject *conobj = ConsoleObject::create( "RuntimeRegisteredSimObject" ); - test( conobj == NULL, "AbstractClassRep returned non-NULL value! That is really bad!" ); - - // Register with console system - RuntimeRegisteredSimObject::dynRTClassRep.consoleRegister(); - - // Make sure that the object knows it's registered - test( RuntimeRegisteredSimObject::dynRTClassRep.isRegistered(), "RuntimeRegisteredSimObject class failed console registration" ); - - // Now try again to create the instance - conobj = ConsoleObject::create( "RuntimeRegisteredSimObject" ); - test( conobj != NULL, "AbstractClassRep::create method failed!" ); - - // Cast the instance, and test it - RuntimeRegisteredSimObject *rtinst = dynamic_cast( conobj ); - test( rtinst != NULL, "Casting failed for some reason" ); - - // Register it - rtinst->registerObject( "_utRRTestObject" ); - test( rtinst->isProperlyAdded(), "registerObject failed on test object" ); - - // Now execute some script on it - Con::evaluate( "_utRRTestObject.fooField = true;" ); - - // Test to make sure field worked - test( dAtob( rtinst->getDataField( StringTable->insert( "fooField" ), NULL ) ), "Script test failed!" ); - - // BALETED - rtinst->deleteObject(); - - // Unregister the class - RuntimeRegisteredSimObject::dynRTClassRep.consoleUnRegister(); - - // And make sure we can't create another one - conobj = ConsoleObject::create( "RuntimeRegisteredSimObject" ); - test( conobj == NULL, "Unregistration of type failed" ); - } -}; \ No newline at end of file From e332609003048069cd6d236d629e3d4b71b05efc Mon Sep 17 00:00:00 2001 From: Azaezel Date: Sun, 13 Jul 2014 12:24:17 -0500 Subject: [PATCH 120/317] requested alterations. bool AIPlayer::checkLosClear(Point3F _pos) removed as not fit for purpose at the present time. Something to revisit when I'm spread less thin, or give someone else a crack at it. --- Engine/source/T3D/aiPlayer.cpp | 73 +++++++++++++++------------------- Engine/source/T3D/aiPlayer.h | 1 - 2 files changed, 33 insertions(+), 41 deletions(-) diff --git a/Engine/source/T3D/aiPlayer.cpp b/Engine/source/T3D/aiPlayer.cpp index 0c9b930fe..31ada9970 100644 --- a/Engine/source/T3D/aiPlayer.cpp +++ b/Engine/source/T3D/aiPlayer.cpp @@ -609,17 +609,22 @@ DefineEngineMethod( AIPlayer, getAimObject, S32, (),, bool AIPlayer::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabled) { if (!isServerObject()) return false; - if (!(bool(target))) + if (!target) { target = mAimObject.getPointer(); - if (!(bool(target))) + if (!target) return false; } if (_checkEnabled) { - ShapeBase *shapeBaseCheck = dynamic_cast(target); - if (shapeBaseCheck) - if (shapeBaseCheck->getDamageState() != Enabled) return false; + if (target->getTypeMask() & ShapeBaseObjectType) + { + ShapeBase *shapeBaseCheck = static_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != Enabled) return false; + } + else + return false; } RayInfo ri; @@ -652,42 +657,34 @@ bool AIPlayer::checkInLos(GameBase* target, bool _useMuzzle, bool _checkEnabled) return hit; } - -bool AIPlayer::checkLosClear(Point3F _pos) -{ - if (!isServerObject()) return false; - - RayInfo ri; - - disableCollision(); - - Point3F muzzlePoint; - getMuzzlePointAI(0, &muzzlePoint); - gServerContainer.castRay(muzzlePoint, _pos, sAIPlayerLoSMask, &ri); - bool emptySpace = bool(ri.object == NULL); - enableCollision(); - return emptySpace; -} - DefineEngineMethod(AIPlayer, checkInLos, bool, (ShapeBase* obj, bool useMuzzle, bool checkEnabled),(NULL, false, false), - "@brief Check for an object in line of sight.\n" - "@obj Object to check. if blank it will check the current target.\n" - "@useMuzzle Use muzzle position (otherwise use eye position).\n" - "@checkEnabled check if the object is not disabled.\n") + "@brief Check whether an object is in line of sight.\n" + "@obj Object to check. (If blank, it will check the current target).\n" + "@useMuzzle Use muzzle position. Otherwise use eye position. (defaults to false).\n" + "@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n") { return object->checkInLos(obj, useMuzzle, checkEnabled); } - bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled) { if (!isServerObject()) return false; - if (!(bool(target))) return false; + if (!target) + { + target = mAimObject.getPointer(); + if (!target) + return false; + } if (_checkEnabled) { - ShapeBase *shapeBaseCheck = dynamic_cast(target); - if (shapeBaseCheck) - if (shapeBaseCheck->getDamageState() != Enabled) return false; + if (target->getTypeMask() & ShapeBaseObjectType) + { + ShapeBase *shapeBaseCheck = static_cast(target); + if (shapeBaseCheck) + if (shapeBaseCheck->getDamageState() != Enabled) return false; + } + else + return false; } MatrixF cam = getTransform(); @@ -699,11 +696,7 @@ bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled) camFov = mDegToRad(camFov) / 2; - Point3F shapePos; - // Use the render transform instead of the box center - // otherwise it'll jitter. - MatrixF srtMat = target->getTransform(); - srtMat.getColumn(3, &shapePos); + Point3F shapePos = target->getBoxCenter(); VectorF shapeDir = shapePos - camPos; // Test to see if it's within our viewcone, this test doesn't // actually match the viewport very well, should consider @@ -714,10 +707,10 @@ bool AIPlayer::checkInFoV(GameBase* target, F32 camFov, bool _checkEnabled) } DefineEngineMethod(AIPlayer, checkInFoV, bool, (ShapeBase* obj, F32 fov, bool checkEnabled), (NULL, 45.0f, false), - "@brief Check for an object within a specified veiw cone.\n" - "@obj Object to check. if blank it will check the current target.\n" - "@fov view angle (in degrees)\n" - "@checkEnabled check if the object is not disabled.\n") + "@brief Check whether an object is within a specified veiw cone.\n" + "@obj Object to check. (If blank, it will check the current target).\n" + "@fov view angle in degrees.(Defaults to 45)\n" + "@checkEnabled check whether the object can take damage and if so is still alive.(Defaults to false)\n") { return object->checkInFoV(obj, fov, checkEnabled); } \ No newline at end of file diff --git a/Engine/source/T3D/aiPlayer.h b/Engine/source/T3D/aiPlayer.h index 851482021..86da43edb 100644 --- a/Engine/source/T3D/aiPlayer.h +++ b/Engine/source/T3D/aiPlayer.h @@ -81,7 +81,6 @@ public: Point3F getAimLocation() const { return mAimLocation; } void clearAim(); bool checkInLos(GameBase* target = NULL, bool _useMuzzle = false, bool _checkEnabled = false); - bool checkLosClear(Point3F _pos); bool checkInFoV(GameBase* target = NULL, F32 camFov = 45.0f, bool _checkEnabled = false); // Movement sets/gets From b7c720d01de4efbbec98067d1ae2401624a33b1f Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Mon, 14 Jul 2014 15:13:30 +0200 Subject: [PATCH 121/317] Reverted #540 --- Engine/source/platform/platformInput.h | 3 --- Engine/source/platformWin32/winInput.cpp | 18 ------------------ Engine/source/sim/actionMap.cpp | 8 ++------ Templates/Empty/source/torqueConfig.h | 7 ------- Templates/Full/source/torqueConfig.h | 7 ------- 5 files changed, 2 insertions(+), 41 deletions(-) diff --git a/Engine/source/platform/platformInput.h b/Engine/source/platform/platformInput.h index 083e7ea85..6abc459e7 100644 --- a/Engine/source/platform/platformInput.h +++ b/Engine/source/platform/platformInput.h @@ -118,9 +118,6 @@ public: static U8 getModifierKeys() {return smModifierKeys;} static void setModifierKeys(U8 mod) {smModifierKeys = mod;} - - static void attemptSwitchToKeyboardLayout( U32 layout ); - #ifdef LOG_INPUT static void log( const char* format, ... ); #endif diff --git a/Engine/source/platformWin32/winInput.cpp b/Engine/source/platformWin32/winInput.cpp index 6d5f87248..e4fcdba57 100644 --- a/Engine/source/platformWin32/winInput.cpp +++ b/Engine/source/platformWin32/winInput.cpp @@ -33,8 +33,6 @@ #include #endif -#include - // Static class variables: InputManager* Input::smManager; bool Input::smActive; @@ -81,10 +79,6 @@ void Input::init() destroy(); -#ifdef TORQUE_DEFAULT_KEYBOARD_LAYOUT - attemptSwitchToKeyboardLayout( TORQUE_DEFAULT_KEYBOARD_LAYOUT ); -#endif - #ifdef LOG_INPUT struct tm* newTime; time_t aclock; @@ -493,18 +487,6 @@ InputManager* Input::getManager() return( smManager ); } -//------------------------------------------------------------------------------ -void Input::attemptSwitchToKeyboardLayout( U32 layout ) -{ - const LANGID lang = MAKELANGID( layout, SUBLANG_DEFAULT ); - std::wstringstream ss; - ss << std::hex << lang; - const wchar_t* hexLang = ss.str().c_str(); - ActivateKeyboardLayout( LoadKeyboardLayout( - hexLang, KLF_ACTIVATE | KLF_REPLACELANG - ), KLF_REORDER ); -} - #ifdef LOG_INPUT //------------------------------------------------------------------------------ void Input::log( const char* format, ... ) diff --git a/Engine/source/sim/actionMap.cpp b/Engine/source/sim/actionMap.cpp index 05feff688..43169f471 100644 --- a/Engine/source/sim/actionMap.cpp +++ b/Engine/source/sim/actionMap.cpp @@ -458,12 +458,8 @@ bool ActionMap::createEventDescriptor(const char* pEventString, EventDescriptor* } // Now we need to map the key string to the proper KEY code from event.h - AssertFatal( - dStrlen( pObjectString ) > 0, - "Error, no key was specified!\n" - "Review file 'scripts/client/config.cs' and remove symbols" - " which is not latin. Or delete this file." - ); + // + AssertFatal(dStrlen(pObjectString) != 0, "Error, no key was specified!"); if (dStrlen(pObjectString) == 1) { diff --git a/Templates/Empty/source/torqueConfig.h b/Templates/Empty/source/torqueConfig.h index e18b00f63..d9de1b1d1 100644 --- a/Templates/Empty/source/torqueConfig.h +++ b/Templates/Empty/source/torqueConfig.h @@ -148,13 +148,6 @@ /// texture manager. #define TORQUE_FRAME_SIZE 16 << 20 -// Default keyboard layout for launching the game. It's fixed crash when a -// game running with the extend unicode keyboard (cyrillic, for example). -// Windows only. -// @see For choice language > -// http://msdn.microsoft.com/en-us/library/windows/desktop/dd318693%28v=vs.85%29.aspx -#define TORQUE_DEFAULT_KEYBOARD_LAYOUT LANG_ENGLISH - // Finally, we define some dependent #defines. This enables some subsidiary // functionality to get automatically turned on in certain configurations. diff --git a/Templates/Full/source/torqueConfig.h b/Templates/Full/source/torqueConfig.h index 775fa91f4..4d8f124f2 100644 --- a/Templates/Full/source/torqueConfig.h +++ b/Templates/Full/source/torqueConfig.h @@ -169,13 +169,6 @@ /// texture manager. #define TORQUE_FRAME_SIZE 16 << 20 -// Default keyboard layout for launching the game. It's fixed crash when a -// game running with the extend unicode keyboard (cyrillic, for example). -// Windows only. -// @see For choice language > -// http://msdn.microsoft.com/en-us/library/windows/desktop/dd318693%28v=vs.85%29.aspx -#define TORQUE_DEFAULT_KEYBOARD_LAYOUT LANG_ENGLISH - // Finally, we define some dependent #defines. This enables some subsidiary // functionality to get automatically turned on in certain configurations. From 369cb2f6947fefa50c459bb5a30f92e050616094 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 15 Jul 2014 12:09:47 +0200 Subject: [PATCH 122/317] Revert "Removed component tests as they're redundant." This reverts commit f64e711f68dd0270687a8204aa5d8ae7be4395bf. --- Engine/source/unit/tests/testComponents.cpp | 154 ++++++++++++++++++++ 1 file changed, 154 insertions(+) create mode 100644 Engine/source/unit/tests/testComponents.cpp diff --git a/Engine/source/unit/tests/testComponents.cpp b/Engine/source/unit/tests/testComponents.cpp new file mode 100644 index 000000000..b4a78ca39 --- /dev/null +++ b/Engine/source/unit/tests/testComponents.cpp @@ -0,0 +1,154 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "unit/test.h" +#include "unit/memoryTester.h" +#include "component/simComponent.h" + +using namespace UnitTesting; + +////////////////////////////////////////////////////////////////////////// + +class CachedInterfaceExampleComponent : public SimComponent +{ + typedef SimComponent Parent; + + ComponentProperty mMyId; + static U32 smNumInstances; + ComponentProperty *mpU32; // CodeReview [patw, 2, 17, 2007] Make ref objects when this is in Jugg + +public: + DECLARE_CONOBJECT( CachedInterfaceExampleComponent ); + + CachedInterfaceExampleComponent() : mpU32( NULL ) + { + mMyId = ( ( 1 << 24 ) | smNumInstances++ ); + } + virtual ~CachedInterfaceExampleComponent() + { + smNumInstances--; + } + +public: + ////////////////////////////////////////////////////////////////////////// + + virtual void registerInterfaces( const SimComponent *owner ) + { + // Register a cached interface for this + registerCachedInterface( NULL, "aU32", this, &mMyId ); + } + + ////////////////////////////////////////////////////////////////////////// + + bool onComponentRegister( SimComponent *owner ) + { + // Call up to the parent first + if( !Parent::onComponentRegister( owner ) ) + return false; + + // We want to get an interface from another object in our containing component + // to simulate component interdependency. + ComponentInterfaceList list; + + // Enumerate the interfaces on the owner, only ignore interfaces that this object owns + if( !_getOwner()->getInterfaces( &list, NULL, "aU32", this, true ) ) + return false; + + // Sanity check before just assigning all willy-nilly + for( ComponentInterfaceListIterator i = list.begin(); i != list.end(); i++ ) + { + mpU32 = dynamic_cast *>( (*i) ); + + if( mpU32 != NULL ) + return true; + } + + return false; + } + + ////////////////////////////////////////////////////////////////////////// + + // CodeReview [patw, 2, 17, 2007] I'm going to make another lightweight interface + // for this functionality later + void unit_test( UnitTest *test ) + { + AssertFatal(test, "CachedInterfaceExampleComponent::unit_test - NULL UnitTest"); + + if( !test ) + return; + + test->test( mpU32 != NULL, "Pointer to dependent interface is NULL" ); + if( mpU32 ) + { + test->test( *(*mpU32) & ( 1 << 24 ), "Pointer to interface data is bogus." ); + test->test( *(*mpU32) != *mMyId, "Two of me have the same ID, bad!" ); + } + } +}; + +IMPLEMENT_CONOBJECT( CachedInterfaceExampleComponent ); +U32 CachedInterfaceExampleComponent::smNumInstances = 0; + +ConsoleDocClass( CachedInterfaceExampleComponent, + "@brief Legacy from older component system.\n\n" + "Not intended for game development, for editors or internal use only.\n\n " + "@internal"); + +////////////////////////////////////////////////////////////////////////// + +CreateUnitTest(TestComponentInterfacing, "Components/Composition") +{ + void run() + { + MemoryTester m; + m.mark(); + + SimComponent *testComponent = new SimComponent(); + CachedInterfaceExampleComponent *componentA = new CachedInterfaceExampleComponent(); + CachedInterfaceExampleComponent *componentB = new CachedInterfaceExampleComponent(); + + // Register sub-components + test( componentA->registerObject(), "Failed to register componentA" ); + test( componentB->registerObject(), "Failed to register componentB" ); + + // Add the components + test( testComponent->addComponent( componentA ), "Failed to add component a to testComponent" ); + test( testComponent->addComponent( componentB ), "Failed to add component b to testComponent" ); + + test( componentA->getOwner() == testComponent, "testComponent did not properly set the mOwner field of componentA to NULL." ); + test( componentB->getOwner() == testComponent, "testComponent did not properly set the mOwner field of componentB to NULL." ); + + // Register the object with the simulation, kicking off the interface registration + const bool registered = testComponent->registerObject(); + test( registered, "Failed to register testComponent" ); + + // Interface tests + if( registered ) + { + componentA->unit_test( this ); + componentB->unit_test( this ); + testComponent->deleteObject(); + } + + test( m.check(), "Component composition test leaked memory." ); + } +}; \ No newline at end of file From ad0899ae27d2870801081e301ba2137acb707a84 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 15 Jul 2014 12:25:06 +0200 Subject: [PATCH 123/317] Capitalisation, which actually doesn't matter. --- Engine/source/testing/unitTesting.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/testing/unitTesting.cpp b/Engine/source/testing/unitTesting.cpp index 0c42577ac..7e1cd63ab 100644 --- a/Engine/source/testing/unitTesting.cpp +++ b/Engine/source/testing/unitTesting.cpp @@ -88,7 +88,7 @@ DefineConsoleFunction( runAllUnitTests, int, (),, // Release the default listener. delete listeners.Release( listeners.default_result_printer() ); - if ( Con::getBoolVariable( "$testing::checkMemoryLeaks", false ) ) { + if ( Con::getBoolVariable( "$Testing::CheckMemoryLeaks", false ) ) { // Add the memory leak tester. listeners.Append( new testing::MemoryLeakDetector ); } From 21ecb6f50d4aee89e3d9b8dcaf0f32eaa6b191ca Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 15 Jul 2014 12:31:56 +0200 Subject: [PATCH 124/317] Moved component unit tests. --- .../component/moreAdvancedComponent.cpp | 49 +------ Engine/source/component/simpleComponent.cpp | 122 +--------------- .../test/moreAdvancedComponentTest.cpp | 68 +++++++++ .../test/simComponentTest.cpp} | 101 +++++++------- .../component/test/simpleComponentTest.cpp | 131 ++++++++++++++++++ 5 files changed, 251 insertions(+), 220 deletions(-) create mode 100644 Engine/source/component/test/moreAdvancedComponentTest.cpp rename Engine/source/{unit/tests/testComponents.cpp => component/test/simComponentTest.cpp} (62%) create mode 100644 Engine/source/component/test/simpleComponentTest.cpp diff --git a/Engine/source/component/moreAdvancedComponent.cpp b/Engine/source/component/moreAdvancedComponent.cpp index 02f8f4fe8..7ddfac688 100644 --- a/Engine/source/component/moreAdvancedComponent.cpp +++ b/Engine/source/component/moreAdvancedComponent.cpp @@ -21,7 +21,6 @@ //----------------------------------------------------------------------------- #include "component/moreAdvancedComponent.h" -#include "unit/test.h" // unitTest_runTests("Component/MoreAdvancedComponent"); @@ -58,50 +57,4 @@ bool MoreAdvancedComponent::testDependentInterface() return false; return mSCInterface->isFortyTwo( 42 ); -} - -////////////////////////////////////////////////////////////////////////// - -using namespace UnitTesting; - -CreateUnitTest(MoreAdvancedComponentTest, "Component/MoreAdvancedComponent") -{ - void run() - { - // Create component instances and compose them. - SimComponent *parentComponent = new SimComponent(); - SimpleComponent *simpleComponent = new SimpleComponent(); - MoreAdvancedComponent *moreAdvComponent = new MoreAdvancedComponent(); - // CodeReview note that the interface pointer isn't initialized in a ctor - // on the components, so it's bad memory against which you might - // be checking in testDependentInterface [3/3/2007 justind] - parentComponent->addComponent( simpleComponent ); - parentComponent->addComponent( moreAdvComponent ); - - simpleComponent->registerObject(); - moreAdvComponent->registerObject(); - - // Put a break-point here, follow the onAdd call, and observe the order in - // which the SimComponent::onAdd function executes. You will see the interfaces - // get cached, and the dependent interface query being made. - parentComponent->registerObject(); - - // If the MoreAdvancedComponent found an interface, than the parentComponent - // should have returned true, from onAdd, and should therefore be registered - // properly with the Sim - test( parentComponent->isProperlyAdded(), "Parent component not properly added!" ); - - // Now lets test the interface. You can step through this, as well. - test( moreAdvComponent->testDependentInterface(), "Dependent interface test failed." ); - - // CodeReview is there a reason we can't just delete the parentComponent here? [3/3/2007 justind] - // - // Clean up - parentComponent->removeComponent( simpleComponent ); - parentComponent->removeComponent( moreAdvComponent ); - - parentComponent->deleteObject(); - moreAdvComponent->deleteObject(); - simpleComponent->deleteObject(); - } -}; \ No newline at end of file +} \ No newline at end of file diff --git a/Engine/source/component/simpleComponent.cpp b/Engine/source/component/simpleComponent.cpp index aa1147c63..246672c28 100644 --- a/Engine/source/component/simpleComponent.cpp +++ b/Engine/source/component/simpleComponent.cpp @@ -28,124 +28,4 @@ ConsoleDocClass( SimpleComponent, "@brief The purpose of this component is to provide a minimalistic component that " "exposes a simple, cached interface\n\n" "Soon to be deprecated, internal only.\n\n " - "@internal"); - -////////////////////////////////////////////////////////////////////////// -// It may seem like some weak sauce to use a unit test for this, however -// it is very, very easy to set breakpoints in a unit test, and trace -// execution in the debugger, so I will use a unit test. -// -// Note I am not using much actual 'test' functionality, just providing -// an easy place to examine the functionality that was described and implemented -// in the header file. -// -// If you want to run this code, simply run Torque, pull down the console, and -// type: -// unitTest_runTests("Components/SimpleComponent"); - -#include "unit/test.h" -using namespace UnitTesting; - -CreateUnitTest(TestSimpleComponent, "Components/SimpleComponent") -{ - void run() - { - // When instantiating, and working with a SimObject in C++ code, such as - // a unit test, you *may not* allocate a SimObject off of the stack. - // - // For example: - // SimpleComponent sc; - // is a stack allocation. This memory is allocated off of the program stack - // when the function is called. SimObject deletion is done via SimObject::deleteObject() - // and the last command of this method is 'delete this;' That command will - // cause an assert if it is called on stack-allocated memory. Therefor, when - // instantiating SimObjects in C++ code, it is imperitive that you keep in - // mind that if any script calls 'delete()' on that SimObject, or any other - // C++ code calls 'deleteObject()' on that SimObject, it will crash. - SimpleComponent *sc = new SimpleComponent(); - - // SimObject::registerObject must be called on a SimObject before it is - // fully 'hooked in' to the engine. - // - // Tracing execution of this function will let you see onAdd get called on - // the component, and you will see it cache the interface we exposed. - sc->registerObject(); - - // It is *not* required that a component always be owned by a component (obviously) - // however I am using an owner so that you can trace execution of recursive - // calls to cache interfaces and such. - SimComponent *testOwner = new SimComponent(); - - // Add the test component to it's owner. This will set the 'mOwner' field - // of 'sc' to the address of 'testOwner' - testOwner->addComponent( sc ); - - // If you step-into this registerObject the same way as the previous one, - // you will be able to see the recursive caching of the exposed interface. - testOwner->registerObject(); - - // Now to prove that object composition is working properly, lets ask - // both of these components for their interface lists... - - // The ComponentInterfaceList is a typedef for type 'VectorPtr' - // and it will be used by getInterfaces() to store the results of the interface - // query. This is the "complete" way to obtain an interface, and it is too - // heavy-weight for most cases. A simplified query will be performed next, - // to demonstrate the usage of both. - ComponentInterfaceList iLst; - - // This query requests all interfaces, on all components, regardless of name - // or owner. - sc->getInterfaces( &iLst, - // This is the type field. I am passing NULL here to signify that the query - // should match all values of 'type' in the list. - NULL, - - // The name field, let's pass NULL again just so when you trace execution - // you can see how queries work in the simple case, first. - NULL ); - - // Lets process the list that we've gotten back, and find the interface that - // we want. - SimpleComponentInterface *scQueriedInterface = NULL; - - for( ComponentInterfaceListIterator i = iLst.begin(); i != iLst.end(); i++ ) - { - scQueriedInterface = dynamic_cast( *i ); - - if( scQueriedInterface != NULL ) - break; - } - - AssertFatal( scQueriedInterface != NULL, "No valid SimpleComponentInterface was found in query" ); - - // Lets do it again, only we will execute the query on the parent instead, - // in a simplified way. Remember the parent component doesn't expose any - // interfaces at all, so the success of this behavior is entirely dependent - // on the recursive registration that occurs in registerInterfaces() - SimpleComponentInterface *ownerQueriedInterface = testOwner->getInterface(); - - AssertFatal( ownerQueriedInterface != NULL, "No valid SimpleComponentInterface was found in query" ); - - // We should now have two pointers to the same interface obtained by querying - // different components. - test( ownerQueriedInterface == scQueriedInterface, "This really shouldn't be possible to fail given the setup of the test" ); - - // Lets call the method that was exposed on the component via the interface. - // Trace the execution of this function, if you wish. - test( ownerQueriedInterface->isFortyTwo( 42 ), "Don't panic, but it's a bad day in the component system." ); - test( scQueriedInterface->isFortyTwo( 42 ), "Don't panic, but it's a bad day in the component system." ); - - // So there you have it. Writing a simple component that exposes a cached - // interface, and testing it. It's time to clean up. - testOwner->removeComponent( sc ); - - sc->deleteObject(); - testOwner->deleteObject(); - - // Interfaces do not need to be freed. In Juggernaught, these will be ref-counted - // for more robust behavior. Right now, however, the values of our two interface - // pointers, scQueriedInterface and ownerQueriedInterface, reference invalid - // memory. - } -}; \ No newline at end of file + "@internal"); \ No newline at end of file diff --git a/Engine/source/component/test/moreAdvancedComponentTest.cpp b/Engine/source/component/test/moreAdvancedComponentTest.cpp new file mode 100644 index 000000000..a6a7335f4 --- /dev/null +++ b/Engine/source/component/test/moreAdvancedComponentTest.cpp @@ -0,0 +1,68 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "component/moreAdvancedComponent.h" + +TEST(MoreAdvancedComponent, MoreAdvancedComponent) +{ + // Create component instances and compose them. + SimComponent *parentComponent = new SimComponent(); + SimpleComponent *simpleComponent = new SimpleComponent(); + MoreAdvancedComponent *moreAdvComponent = new MoreAdvancedComponent(); + // CodeReview note that the interface pointer isn't initialized in a ctor + // on the components, so it's bad memory against which you might + // be checking in testDependentInterface [3/3/2007 justind] + parentComponent->addComponent( simpleComponent ); + parentComponent->addComponent( moreAdvComponent ); + + simpleComponent->registerObject(); + moreAdvComponent->registerObject(); + + // Put a break-point here, follow the onAdd call, and observe the order in + // which the SimComponent::onAdd function executes. You will see the interfaces + // get cached, and the dependent interface query being made. + parentComponent->registerObject(); + + // If the MoreAdvancedComponent found an interface, than the parentComponent + // should have returned true, from onAdd, and should therefore be registered + // properly with the Sim + EXPECT_TRUE( parentComponent->isProperlyAdded() ) + << "Parent component not properly added!"; + + // Now lets test the interface. You can step through this, as well. + EXPECT_TRUE( moreAdvComponent->testDependentInterface() ) + << "Dependent interface test failed."; + + // CodeReview is there a reason we can't just delete the parentComponent here? [3/3/2007 justind] + // + // Clean up + parentComponent->removeComponent( simpleComponent ); + parentComponent->removeComponent( moreAdvComponent ); + + parentComponent->deleteObject(); + moreAdvComponent->deleteObject(); + simpleComponent->deleteObject(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/unit/tests/testComponents.cpp b/Engine/source/component/test/simComponentTest.cpp similarity index 62% rename from Engine/source/unit/tests/testComponents.cpp rename to Engine/source/component/test/simComponentTest.cpp index b4a78ca39..d7d135a08 100644 --- a/Engine/source/unit/tests/testComponents.cpp +++ b/Engine/source/component/test/simComponentTest.cpp @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC +// Copyright (c) 2014 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to @@ -20,14 +20,10 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -#include "unit/test.h" -#include "unit/memoryTester.h" +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" #include "component/simComponent.h" -using namespace UnitTesting; - -////////////////////////////////////////////////////////////////////////// - class CachedInterfaceExampleComponent : public SimComponent { typedef SimComponent Parent; @@ -89,18 +85,16 @@ public: // CodeReview [patw, 2, 17, 2007] I'm going to make another lightweight interface // for this functionality later - void unit_test( UnitTest *test ) + void unit_test() { - AssertFatal(test, "CachedInterfaceExampleComponent::unit_test - NULL UnitTest"); - - if( !test ) - return; - - test->test( mpU32 != NULL, "Pointer to dependent interface is NULL" ); + EXPECT_TRUE( mpU32 != NULL ) + << "Pointer to dependent interface is NULL"; if( mpU32 ) { - test->test( *(*mpU32) & ( 1 << 24 ), "Pointer to interface data is bogus." ); - test->test( *(*mpU32) != *mMyId, "Two of me have the same ID, bad!" ); + EXPECT_TRUE( *(*mpU32) & ( 1 << 24 )) + << "Pointer to interface data is bogus."; + EXPECT_TRUE( *(*mpU32) != *mMyId) + << "Two of me have the same ID, bad!"; } } }; @@ -113,42 +107,47 @@ ConsoleDocClass( CachedInterfaceExampleComponent, "Not intended for game development, for editors or internal use only.\n\n " "@internal"); -////////////////////////////////////////////////////////////////////////// - -CreateUnitTest(TestComponentInterfacing, "Components/Composition") +TEST(SimComponent, Composition) { - void run() + SimComponent *testComponent = new SimComponent(); + CachedInterfaceExampleComponent *componentA = new CachedInterfaceExampleComponent(); + CachedInterfaceExampleComponent *componentB = new CachedInterfaceExampleComponent(); + + // Register sub-components + EXPECT_TRUE( componentA->registerObject()) + << "Failed to register componentA"; + EXPECT_TRUE( componentB->registerObject()) + << "Failed to register componentB"; + + // Add the components + EXPECT_TRUE( testComponent->addComponent( componentA )) + << "Failed to add component a to testComponent"; + EXPECT_TRUE( testComponent->addComponent( componentB )) + << "Failed to add component b to testComponent"; + + EXPECT_EQ( componentA->getOwner(), testComponent) + << "testComponent did not properly set the mOwner field of componentA to NULL."; + EXPECT_EQ( componentB->getOwner(), testComponent) + << "testComponent did not properly set the mOwner field of componentB to NULL."; + + // Register the object with the simulation, kicking off the interface registration + const bool registered = testComponent->registerObject(); + EXPECT_TRUE( registered ) + << "Failed to register testComponent"; + + // Interface tests + if( registered ) { - MemoryTester m; - m.mark(); - - SimComponent *testComponent = new SimComponent(); - CachedInterfaceExampleComponent *componentA = new CachedInterfaceExampleComponent(); - CachedInterfaceExampleComponent *componentB = new CachedInterfaceExampleComponent(); - - // Register sub-components - test( componentA->registerObject(), "Failed to register componentA" ); - test( componentB->registerObject(), "Failed to register componentB" ); - - // Add the components - test( testComponent->addComponent( componentA ), "Failed to add component a to testComponent" ); - test( testComponent->addComponent( componentB ), "Failed to add component b to testComponent" ); - - test( componentA->getOwner() == testComponent, "testComponent did not properly set the mOwner field of componentA to NULL." ); - test( componentB->getOwner() == testComponent, "testComponent did not properly set the mOwner field of componentB to NULL." ); - - // Register the object with the simulation, kicking off the interface registration - const bool registered = testComponent->registerObject(); - test( registered, "Failed to register testComponent" ); - - // Interface tests - if( registered ) { - componentA->unit_test( this ); - componentB->unit_test( this ); - testComponent->deleteObject(); - } - - test( m.check(), "Component composition test leaked memory." ); + SCOPED_TRACE("componentA"); + componentA->unit_test(); + } + { + SCOPED_TRACE("componentB"); + componentB->unit_test(); + } + testComponent->deleteObject(); } -}; \ No newline at end of file +}; + +#endif \ No newline at end of file diff --git a/Engine/source/component/test/simpleComponentTest.cpp b/Engine/source/component/test/simpleComponentTest.cpp new file mode 100644 index 000000000..a528e918f --- /dev/null +++ b/Engine/source/component/test/simpleComponentTest.cpp @@ -0,0 +1,131 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "component/simpleComponent.h" + +TEST(SimpleComponent, SimpleComponent) +{ + // When instantiating, and working with a SimObject in C++ code, such as + // a unit test, you *may not* allocate a SimObject off of the stack. + // + // For example: + // SimpleComponent sc; + // is a stack allocation. This memory is allocated off of the program stack + // when the function is called. SimObject deletion is done via SimObject::deleteObject() + // and the last command of this method is 'delete this;' That command will + // cause an assert if it is called on stack-allocated memory. Therefor, when + // instantiating SimObjects in C++ code, it is imperitive that you keep in + // mind that if any script calls 'delete()' on that SimObject, or any other + // C++ code calls 'deleteObject()' on that SimObject, it will crash. + SimpleComponent *sc = new SimpleComponent(); + + // SimObject::registerObject must be called on a SimObject before it is + // fully 'hooked in' to the engine. + // + // Tracing execution of this function will let you see onAdd get called on + // the component, and you will see it cache the interface we exposed. + sc->registerObject(); + + // It is *not* required that a component always be owned by a component (obviously) + // however I am using an owner so that you can trace execution of recursive + // calls to cache interfaces and such. + SimComponent *testOwner = new SimComponent(); + + // Add the test component to it's owner. This will set the 'mOwner' field + // of 'sc' to the address of 'testOwner' + testOwner->addComponent( sc ); + + // If you step-into this registerObject the same way as the previous one, + // you will be able to see the recursive caching of the exposed interface. + testOwner->registerObject(); + + // Now to prove that object composition is working properly, lets ask + // both of these components for their interface lists... + + // The ComponentInterfaceList is a typedef for type 'VectorPtr' + // and it will be used by getInterfaces() to store the results of the interface + // query. This is the "complete" way to obtain an interface, and it is too + // heavy-weight for most cases. A simplified query will be performed next, + // to demonstrate the usage of both. + ComponentInterfaceList iLst; + + // This query requests all interfaces, on all components, regardless of name + // or owner. + sc->getInterfaces( &iLst, + // This is the type field. I am passing NULL here to signify that the query + // should match all values of 'type' in the list. + NULL, + + // The name field, let's pass NULL again just so when you trace execution + // you can see how queries work in the simple case, first. + NULL ); + + // Lets process the list that we've gotten back, and find the interface that + // we want. + SimpleComponentInterface *scQueriedInterface = NULL; + + for( ComponentInterfaceListIterator i = iLst.begin(); i != iLst.end(); i++ ) + { + scQueriedInterface = dynamic_cast( *i ); + + if( scQueriedInterface != NULL ) + break; + } + + AssertFatal( scQueriedInterface != NULL, "No valid SimpleComponentInterface was found in query" ); + + // Lets do it again, only we will execute the query on the parent instead, + // in a simplified way. Remember the parent component doesn't expose any + // interfaces at all, so the success of this behavior is entirely dependent + // on the recursive registration that occurs in registerInterfaces() + SimpleComponentInterface *ownerQueriedInterface = testOwner->getInterface(); + + AssertFatal( ownerQueriedInterface != NULL, "No valid SimpleComponentInterface was found in query" ); + + // We should now have two pointers to the same interface obtained by querying + // different components. + EXPECT_EQ( ownerQueriedInterface, scQueriedInterface ) + << "This really shouldn't be possible to fail given the setup of the test"; + + // Lets call the method that was exposed on the component via the interface. + // Trace the execution of this function, if you wish. + EXPECT_TRUE( ownerQueriedInterface->isFortyTwo( 42 ) ) + << "Don't panic, but it's a bad day in the component system."; + EXPECT_TRUE( scQueriedInterface->isFortyTwo( 42 ) ) + << "Don't panic, but it's a bad day in the component system."; + + // So there you have it. Writing a simple component that exposes a cached + // interface, and testing it. It's time to clean up. + testOwner->removeComponent( sc ); + + sc->deleteObject(); + testOwner->deleteObject(); + + // Interfaces do not need to be freed. In Juggernaught, these will be ref-counted + // for more robust behavior. Right now, however, the values of our two interface + // pointers, scQueriedInterface and ownerQueriedInterface, reference invalid + // memory. +}; + +#endif \ No newline at end of file From 56d434132ed76cdfb34ce85e295a743f1b3d5231 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 15 Jul 2014 12:44:10 +0200 Subject: [PATCH 125/317] Added optimistic profiler test. --- Engine/source/platform/test/profilerTest.cpp | 49 ++++++++++++++++++++ 1 file changed, 49 insertions(+) create mode 100644 Engine/source/platform/test/profilerTest.cpp diff --git a/Engine/source/platform/test/profilerTest.cpp b/Engine/source/platform/test/profilerTest.cpp new file mode 100644 index 000000000..64ccc36d3 --- /dev/null +++ b/Engine/source/platform/test/profilerTest.cpp @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "platform/platform.h" // Allows us to see TORQUE_ENABLE_PROFILER + +#ifdef TORQUE_ENABLE_PROFILER +#include "testing/unitTesting.h" +#include "platform/profiler.h" + +TEST(Profiler, ProfileStartEnd) +{ + PROFILE_START(ProfileStartEndTest); + // Do work. + if(true) + { + PROFILE_END(ProfileStartEndTest); + return; + } + PROFILE_END(ProfileStartEndTest); +} + +TEST(Profiler, ProfileScope) +{ + PROFILE_SCOPE(ScopedProfilerTest); + // Do work and return whenever you want. +} + +#endif +#endif \ No newline at end of file From 2f2d7cf3885b990b957bc4ab55abf8f83756903c Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 15 Jul 2014 14:12:31 +0200 Subject: [PATCH 126/317] Ported swizzle test. --- Engine/source/core/util/test/swizzleTest.cpp | 129 +++++++++++++++++++ Engine/source/unit/tests/testSwizzle.cpp | 126 ------------------ 2 files changed, 129 insertions(+), 126 deletions(-) create mode 100644 Engine/source/core/util/test/swizzleTest.cpp delete mode 100644 Engine/source/unit/tests/testSwizzle.cpp diff --git a/Engine/source/core/util/test/swizzleTest.cpp b/Engine/source/core/util/test/swizzleTest.cpp new file mode 100644 index 000000000..942150022 --- /dev/null +++ b/Engine/source/core/util/test/swizzleTest.cpp @@ -0,0 +1,129 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "platform/platform.h" +#include "testing/unitTesting.h" +#include "core/util/swizzle.h" +#include "math/mRandom.h" + +class TestStruct +{ +private: + static U32 smIdx; + U32 mIdx; + U32 mData; + +public: + TestStruct( const S32 data = -1 ) : mData( data ), mIdx( smIdx++ ) {}; + + dsize_t Idx() const { return mIdx; } + + U32 Data() const { return mData; } + void Data(U32 val) { mData = val; } +}; + +U32 TestStruct::smIdx = 0; + +TEST(Swizzle, Swizzle) +{ + //------------------------------------------------------------------------ + // Debugger-Observable Functionality Tests + //------------------------------------------------------------------------ + U8 simpleBuffer[] = { 0, 1, 2, 3 }; + U8 simpleTest[] = { 0, 1, 2, 3 }; + +#define RESET_SIMPLE() dMemcpy( simpleTest, simpleBuffer, sizeof( simpleBuffer ) ) + + //------------------------------------------------------------------------ + // No-switch test + dsize_t noSwzl4[] = { 0, 1, 2, 3 }; + Swizzle noSwizzle4( noSwzl4 ); + + noSwizzle4.InPlace( simpleTest, sizeof( simpleTest ) ); + EXPECT_EQ( dMemcmp( simpleTest, simpleBuffer, sizeof( simpleBuffer ) ), 0 ) + << "No-switch test failed!"; + RESET_SIMPLE(); + + //------------------------------------------------------------------------ + // No-brainer RGBA->BGRA test + dsize_t bgraSwzl[] = { 2, 1, 0, 3 }; + Swizzle bgraSwizzle( bgraSwzl ); + + U8 bgraTest[] = { 2, 1, 0, 3 }; + bgraSwizzle.InPlace( simpleTest, sizeof( simpleTest ) ); + EXPECT_EQ( dMemcmp( simpleTest, bgraTest, sizeof( bgraTest ) ), 0 ) + << "U8 RGBA->BGRA test failed"; + + //------------------------------------------------------------------------ + // Reverse test + bgraSwizzle.InPlace( simpleTest, sizeof( simpleTest ) ); + EXPECT_EQ( dMemcmp( simpleTest, simpleBuffer, sizeof( simpleBuffer ) ), 0 ) + << "U8 RGBA->BGRA reverse test failed"; + + RESET_SIMPLE(); + + //------------------------------------------------------------------------ + // Object support test + Swizzle bgraObjSwizzle( bgraSwzl ); + { + U32 objIdx[] = { 0, 1, 2, 3 }; + + FrameTemp objTest( sizeof( objIdx ) / sizeof( U32 ) ); + FrameTemp randData( sizeof( objIdx ) / sizeof( U32 ) ); + + bool same = true; + + for( U32 i = 0; i < sizeof( objIdx ) / sizeof( U32 ); i++ ) + { + // Make random data and assign it + randData[i] = gRandGen.randI(); + objTest[i].Data( randData[i] ); + + // Continue object sanity check + same &= ( objTest[i].Idx() == objIdx[i] ); + } + + EXPECT_TRUE( same ) + << "Test object failed to be competent"; + + bgraObjSwizzle.InPlace( ~objTest, sizeof( TestStruct ) * ( sizeof( objIdx ) / sizeof( U32 ) ) ); + same = true; + + for( U32 i = 0; i < sizeof( objIdx ) / sizeof( U32 ); i++ ) + same &= ( objTest[i].Idx() == bgraTest[i] ) && ( objTest[i].Data() == randData[ (U32)bgraTest[ i ] ] ); + + EXPECT_TRUE( same ) + << "Object RGBA->BGRA test failed."; + + bgraObjSwizzle.InPlace( ~objTest, sizeof( TestStruct ) * ( sizeof( objIdx ) / sizeof( U32 ) ) ); + same = true; + + for( U32 i = 0; i < sizeof( objIdx ) / sizeof( U32 ); i++ ) + same &= ( objTest[i].Idx() == objIdx[i] ) && ( objTest[i].Data() == randData[i] ); + + EXPECT_TRUE( same ) + << "Object RGBA->BGRA reverse test failed."; + } +}; + +#endif \ No newline at end of file diff --git a/Engine/source/unit/tests/testSwizzle.cpp b/Engine/source/unit/tests/testSwizzle.cpp deleted file mode 100644 index 1c90394ec..000000000 --- a/Engine/source/unit/tests/testSwizzle.cpp +++ /dev/null @@ -1,126 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "platform/platform.h" -#include "unit/test.h" -#include "unit/memoryTester.h" -#include "core/util/swizzle.h" -#include "math/mRandom.h" - -using namespace UnitTesting; - -class TestStruct -{ -private: - static U32 smIdx; - U32 mIdx; - U32 mData; - -public: - TestStruct( const S32 data = -1 ) : mData( data ), mIdx( smIdx++ ) {}; - - dsize_t Idx() const { return mIdx; } - - U32 Data() const { return mData; } - void Data(U32 val) { mData = val; } -}; - -U32 TestStruct::smIdx = 0; - -CreateUnitTest(TestSwizzle, "Utils/Swizzle") -{ - void run() - { - //------------------------------------------------------------------------ - // Debugger-Observable Functionality Tests - //------------------------------------------------------------------------ - U8 simpleBuffer[] = { 0, 1, 2, 3 }; - U8 simpleTest[] = { 0, 1, 2, 3 }; - -#define RESET_SIMPLE() dMemcpy( simpleTest, simpleBuffer, sizeof( simpleBuffer ) ) - - //------------------------------------------------------------------------ - // No-switch test - dsize_t noSwzl4[] = { 0, 1, 2, 3 }; - Swizzle noSwizzle4( noSwzl4 ); - - noSwizzle4.InPlace( simpleTest, sizeof( simpleTest ) ); - test( dMemcmp( simpleTest, simpleBuffer, sizeof( simpleBuffer ) ) == 0, "No-switch test failed!" ); - RESET_SIMPLE(); - - //------------------------------------------------------------------------ - // No-brainer RGBA->BGRA test - dsize_t bgraSwzl[] = { 2, 1, 0, 3 }; - Swizzle bgraSwizzle( bgraSwzl ); - - U8 bgraTest[] = { 2, 1, 0, 3 }; - bgraSwizzle.InPlace( simpleTest, sizeof( simpleTest ) ); - test( dMemcmp( simpleTest, bgraTest, sizeof( bgraTest ) ) == 0, "U8 RGBA->BGRA test failed" ); - - //------------------------------------------------------------------------ - // Reverse test - bgraSwizzle.InPlace( simpleTest, sizeof( simpleTest ) ); - test( dMemcmp( simpleTest, simpleBuffer, sizeof( simpleBuffer ) ) == 0, "U8 RGBA->BGRA reverse test failed" ); - - RESET_SIMPLE(); - - //------------------------------------------------------------------------ - // Object support test - Swizzle bgraObjSwizzle( bgraSwzl ); - { - U32 objIdx[] = { 0, 1, 2, 3 }; - - FrameTemp objTest( sizeof( objIdx ) / sizeof( U32 ) ); - FrameTemp randData( sizeof( objIdx ) / sizeof( U32 ) ); - - bool same = true; - - for( U32 i = 0; i < sizeof( objIdx ) / sizeof( U32 ); i++ ) - { - // Make random data and assign it - randData[i] = gRandGen.randI(); - objTest[i].Data( randData[i] ); - - // Continue object sanity check - same &= ( objTest[i].Idx() == objIdx[i] ); - } - - test( same, "Test object failed to be competent" ); - - bgraObjSwizzle.InPlace( ~objTest, sizeof( TestStruct ) * ( sizeof( objIdx ) / sizeof( U32 ) ) ); - same = true; - - for( U32 i = 0; i < sizeof( objIdx ) / sizeof( U32 ); i++ ) - same &= ( objTest[i].Idx() == bgraTest[i] ) && ( objTest[i].Data() == randData[ (U32)bgraTest[ i ] ] ); - - test( same, "Object RGBA->BGRA test failed." ); - - bgraObjSwizzle.InPlace( ~objTest, sizeof( TestStruct ) * ( sizeof( objIdx ) / sizeof( U32 ) ) ); - same = true; - - for( U32 i = 0; i < sizeof( objIdx ) / sizeof( U32 ); i++ ) - same &= ( objTest[i].Idx() == objIdx[i] ) && ( objTest[i].Data() == randData[i] ); - - test( same, "Object RGBA->BGRA reverse test failed." ); - } - } -}; \ No newline at end of file From 3cdfcb19d4a79201ded24d232ad08159389efe08 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 15 Jul 2014 14:46:27 +0200 Subject: [PATCH 127/317] Ported string tests. --- Engine/source/core/util/test/strTest.cpp | 332 ++++++++++++++++++ Engine/source/core/util/test/testString.cpp | 358 -------------------- 2 files changed, 332 insertions(+), 358 deletions(-) create mode 100644 Engine/source/core/util/test/strTest.cpp delete mode 100644 Engine/source/core/util/test/testString.cpp diff --git a/Engine/source/core/util/test/strTest.cpp b/Engine/source/core/util/test/strTest.cpp new file mode 100644 index 000000000..3bb381d56 --- /dev/null +++ b/Engine/source/core/util/test/strTest.cpp @@ -0,0 +1,332 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "core/util/str.h" +#include "core/util/tVector.h" +#include "core/strings/stringFunctions.h" +#include "core/strings/unicode.h" + +/// This is called Str, not String, because googletest doesn't let you use both +/// TEST(x) and TEST_FIX(x). So this fixture is called Str, to match the StrTest +/// struct, and the remaining fixture-les tests are named String. +FIXTURE(Str) +{ +protected: + struct StrTest + { + const UTF8* mData; + const UTF16* mUTF16; + U32 mLength; + + StrTest() : mData( 0 ), mUTF16( 0 ) {} + StrTest( const char* str ) + : mData( str ), mLength( str ? dStrlen( str ) : 0 ), mUTF16( NULL ) + { + if( str ) + mUTF16 = convertUTF8toUTF16( mData ); + } + ~StrTest() + { + if( mUTF16 ) + delete [] mUTF16; + } + }; + + Vector< StrTest* > mStrings; + + virtual void SetUp() + { + mStrings.push_back( new StrTest( NULL ) ); + mStrings.push_back( new StrTest( "" ) ); + mStrings.push_back( new StrTest( "Torque" ) ); + mStrings.push_back( new StrTest( "TGEA" ) ); + mStrings.push_back( new StrTest( "GarageGames" ) ); + mStrings.push_back( new StrTest( "TGB" ) ); + mStrings.push_back( new StrTest( "games" ) ); + mStrings.push_back( new StrTest( "engine" ) ); + mStrings.push_back( new StrTest( "rocks" ) ); + mStrings.push_back( new StrTest( "technology" ) ); + mStrings.push_back( new StrTest( "Torque 3D" ) ); + mStrings.push_back( new StrTest( "Torque 2D" ) ); + } + + virtual void TearDown() + { + for( U32 i = 0; i < mStrings.size(); ++ i ) + delete mStrings[ i ]; + mStrings.clear(); + } +}; + +#define EACH_STRING(i) \ + for( U32 i = 0; i < mStrings.size(); ++ i ) +#define EACH_PAIR(i, j) \ + for( U32 i = 0; i < mStrings.size(); ++ i ) \ + for( U32 j = 0; j < mStrings.size(); ++ j ) + +TEST_FIX(Str, Test1) +{ + EACH_STRING(i) + { + StrTest& data = *mStrings[i]; + String str( data.mData ); + String str16( data.mUTF16 ); + + EXPECT_TRUE( str.length() == data.mLength ); + EXPECT_TRUE( str.size() == data.mLength + 1 ); + EXPECT_TRUE( str.isEmpty() || str.length() > 0 ); + EXPECT_TRUE( str.length() == str16.length() ); + EXPECT_TRUE( str.size() == str16.size() ); + + EXPECT_TRUE( dMemcmp( str.utf16(), str16.utf16(), str.length() * sizeof( UTF16 ) ) == 0 ); + EXPECT_TRUE( !data.mData || dMemcmp( str.utf16(), data.mUTF16, str.length() * sizeof( UTF16 ) ) == 0 ); + EXPECT_TRUE( !data.mData || dMemcmp( str16.utf8(), data.mData, str.length() ) == 0 ); + + EXPECT_TRUE( !data.mData || dStrcmp( str.utf8(), data.mData ) == 0 ); + EXPECT_TRUE( !data.mData || dStrcmp( str.utf16(), data.mUTF16 ) == 0 ); + } +} + +TEST_FIX(Str, Test2) +{ + EACH_STRING(i) + { + StrTest& data = *mStrings[i]; + String str( data.mData ); + + EXPECT_TRUE( str == str ); + EXPECT_FALSE( str != str ); + EXPECT_FALSE( str < str ); + EXPECT_FALSE( str > str ); + EXPECT_TRUE( str.equal( str ) ); + EXPECT_TRUE( str.equal( str, String::NoCase ) ); + } +} + +TEST_FIX(Str, Test3) +{ + EACH_PAIR(i, j) + { + StrTest& d1 = *mStrings[i]; + StrTest& d2 = *mStrings[j]; + + if( &d1 != &d2 ) + EXPECT_TRUE( String( d1.mData ) != String( d2.mData ) + || ( String( d1.mData ).isEmpty() && String( d2.mData ).isEmpty() ) ); + else + EXPECT_TRUE( String( d1.mData ) == String( d2.mData ) ); + } +} + +TEST(String, Empty) +{ + EXPECT_TRUE( String().length() == 0 ); + EXPECT_TRUE( String( "" ).length() == 0 ); + EXPECT_TRUE( String().size() == 1 ); + EXPECT_TRUE( String( "" ).size() == 1 ); + EXPECT_TRUE( String().isEmpty() ); + EXPECT_TRUE( String( "" ).isEmpty() ); +} + +TEST(String, Trim) +{ + EXPECT_TRUE( String( " Foobar Barfoo \n\t " ).trim() == String( "Foobar Barfoo" ) ); + EXPECT_TRUE( String( "Foobar" ).trim() == String( "Foobar" ) ); + EXPECT_TRUE( String( " " ).trim().isEmpty() ); +} + +TEST(String, Compare) +{ + String str( "Foobar" ); + + EXPECT_TRUE( str.compare( "Foo", 3 ) == 0 ); + EXPECT_TRUE( str.compare( "bar", 3, String::NoCase | String::Right ) == 0 ); + EXPECT_TRUE( str.compare( "foo", 3, String::NoCase ) == 0 ); + EXPECT_TRUE( str.compare( "BAR", 3, String::NoCase | String::Right ) == 0 ); + EXPECT_TRUE( str.compare( "Foobar" ) == 0 ); + EXPECT_TRUE( str.compare( "Foo" ) != 0 ); + EXPECT_TRUE( str.compare( "foobar", 0, String::NoCase ) == 0 ); + EXPECT_TRUE( str.compare( "FOOBAR", 0, String::NoCase ) == 0 ); + EXPECT_TRUE( str.compare( "Foobar", 0, String::Right ) == 0 ); + EXPECT_TRUE( str.compare( "foobar", 0, String::NoCase | String::Right ) == 0 ); +} + +TEST(String, Order) +{ + Vector< String > strs; + + strs.push_back( "a" ); + strs.push_back( "a0" ); + strs.push_back( "a1" ); + strs.push_back( "a1a" ); + strs.push_back( "a1b" ); + strs.push_back( "a2" ); + strs.push_back( "a10" ); + strs.push_back( "a20" ); + + for( U32 i = 0; i < strs.size(); ++ i ) + { + for( U32 j = 0; j < i; ++ j ) + { + EXPECT_TRUE( strs[ j ] < strs[ i ] ); + EXPECT_TRUE( strs[ i ] > strs[ j ] ); + + EXPECT_TRUE( !( strs[ j ] > strs[ i ] ) ); + EXPECT_TRUE( !( strs[ i ] < strs[ i ] ) ); + + EXPECT_TRUE( strs[ j ] <= strs[ i ] ); + EXPECT_TRUE( strs[ i ] >= strs[ j ] ); + } + + EXPECT_TRUE( !( strs[ i ] < strs[ i ] ) ); + EXPECT_TRUE( !( strs[ i ] > strs[ i ] ) ); + EXPECT_TRUE( strs[ i ] <= strs[ i ] ); + EXPECT_TRUE( strs[ i ] >= strs[ i ] ); + + for( U32 j = i + 1; j < strs.size(); ++ j ) + { + EXPECT_TRUE( strs[ j ] > strs[ i ] ); + EXPECT_TRUE( strs[ i ] < strs[ j ] ); + + EXPECT_TRUE( !( strs[ j ] < strs[ i ] ) ); + EXPECT_TRUE( !( strs[ i ] > strs[ j ] ) ); + + EXPECT_TRUE( strs[ j ] >= strs[ i ] ); + EXPECT_TRUE( strs[ i ] <= strs[ j ] ); + } + } +} + +/// TODO +TEST(String, Find) +{ +} + +TEST(String, Insert) +{ + // String.insert( Pos, Char ) + EXPECT_TRUE( String( "aa" ).insert( 1, 'c' ) == String( "aca" ) ); + + // String.insert( Pos, String ) + EXPECT_TRUE( String( "aa" ).insert( 1, "cc" ) == String( "acca" ) ); + EXPECT_TRUE( String( "aa" ).insert( 1, String( "cc" ) ) == String( "acca" ) ); + + // String.insert( Pos, String, Len ) + EXPECT_TRUE( String( "aa" ).insert( 1, "ccdddd", 2 ) == String( "acca" ) ); +} + +TEST(String, Erase) +{ + EXPECT_TRUE( String( "abba" ).erase( 1, 2 ) == String( "aa" ) ); + EXPECT_TRUE( String( "abba" ).erase( 0, 4 ).isEmpty() ); +} + +TEST(String, Replace) +{ + // String.replace( Pos, Len, String ) + EXPECT_TRUE( String( "abba" ).replace( 1, 2, "ccc" ) == String( "accca" ) ); + EXPECT_TRUE( String( "abba" ).replace( 1, 2, String( "ccc" ) ) == String( "accca" ) ); + EXPECT_TRUE( String( "abba" ).replace( 0, 4, "" ).isEmpty() ); + EXPECT_TRUE( String( "abba" ).replace( 2, 2, "c" ) == String( "abc" ) ); + + // String.replace( Char, Char ) + EXPECT_TRUE( String().replace( 'a', 'b' ).isEmpty() ); + EXPECT_TRUE( String( "ababc" ).replace( 'a', 'b' ) == String( "bbbbc" ) ); + EXPECT_TRUE( String( "ababc" ).replace( 'd', 'e' ) == String( "ababc" ) ); + + // String.replace( String, String ) + EXPECT_TRUE( String().replace( "foo", "bar" ).isEmpty() ); + EXPECT_TRUE( String( "foobarfoo" ).replace( "foo", "bar" ) == String( "barbarbar" ) ); + EXPECT_TRUE( String( "foobar" ).replace( "xx", "yy" ) == String( "foobar" ) ); + EXPECT_TRUE( String( "foofoofoo" ).replace( "foo", "" ).isEmpty() ); +} + +TEST(String, SubStr) +{ + EXPECT_TRUE( String( "foobar" ).substr( 0, 3 ) == String( "foo" ) ); + EXPECT_TRUE( String( "foobar" ).substr( 3 ) == String( "bar" ) ); + EXPECT_TRUE( String( "foobar" ).substr( 2, 2 ) == String( "ob" ) ); + EXPECT_TRUE( String( "foobar" ).substr( 2, 0 ).isEmpty() ); + EXPECT_TRUE( String( "foobar" ).substr( 0, 6 ) == String( "foobar" ) ); +} + +TEST(String, ToString) +{ + EXPECT_TRUE( String::ToString( U32( 1 ) ) == String( "1" ) ); + EXPECT_TRUE( String::ToString( S32( -1 ) ) == String( "-1" ) ); + EXPECT_TRUE( String::ToString( F32( 1.01 ) ) == String( "1.01" ) ); + EXPECT_TRUE( String::ToString( "%s%i", "foo", 1 ) == String( "foo1" ) ); +} + +TEST(String, CaseConversion) +{ + EXPECT_TRUE( String::ToLower( "foobar123." ) == String( "foobar123." ) ); + EXPECT_TRUE( String::ToLower( "FOOBAR123." ) == String( "foobar123." ) ); + EXPECT_TRUE( String::ToUpper( "barfoo123." ) == String( "BARFOO123." ) ); + EXPECT_TRUE( String::ToUpper( "BARFOO123." ) == String( "BARFOO123." ) ); +} + +TEST(String, Concat) +{ + EXPECT_TRUE( String( "foo" ) + String( "bar" ) == String( "foobar" ) ); + EXPECT_TRUE( String() + String( "bar" ) == String( "bar" ) ); + EXPECT_TRUE( String( "foo" ) + String() == String( "foo" ) ); + EXPECT_TRUE( String() + String() == String() ); + EXPECT_TRUE( String( "fo" ) + 'o' == String( "foo" ) ); + EXPECT_TRUE( 'f' + String( "oo" ) == String( "foo" ) ); + EXPECT_TRUE( String( "foo" ) + "bar" == String( "foobar" ) ); + EXPECT_TRUE( "foo" + String( "bar" ) == String( "foobar" ) ); +} + +TEST(String, Hash) +{ + EXPECT_TRUE( String( "foo" ).getHashCaseSensitive() == String( "foo" ).getHashCaseSensitive() ); + EXPECT_TRUE( String( "foo" ).getHashCaseSensitive() != String( "bar" ).getHashCaseSensitive() ); + EXPECT_TRUE( String( "foo" ).getHashCaseInsensitive() == String( "FOO" ).getHashCaseInsensitive() ); +} + +TEST(String, Intern) +{ + EXPECT_TRUE( String( "foo" ).intern().isSame( String( "foo" ).intern() ) ); + EXPECT_TRUE( !String( "foo" ).intern().isSame( String( "bar" ).intern() ) ); + EXPECT_TRUE( !String( "foo" ).intern().isSame( String( "Foo" ).intern() ) ); + EXPECT_TRUE( String( "foo" ).intern() == String( "foo" ).intern() ); + EXPECT_TRUE( String( "foo" ).intern() != String( "bar" ).intern() ); + EXPECT_TRUE( String( "foo" ).intern().isInterned() ); +} + +TEST(StringBuilder, StringBuilder) +{ + StringBuilder str; + + str.append( 'f' ); + str.append( "oo" ); + str.append( String( "ba" ) ); + str.append( "rfajskfdj", 1 ); + str.format( "%s", "barfoo" ); + + EXPECT_TRUE( str.end() == String( "foobarbarfoo" ) ); +} + +#endif diff --git a/Engine/source/core/util/test/testString.cpp b/Engine/source/core/util/test/testString.cpp deleted file mode 100644 index 33c6ea6d4..000000000 --- a/Engine/source/core/util/test/testString.cpp +++ /dev/null @@ -1,358 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "unit/test.h" -#include "core/util/str.h" -#include "core/util/tVector.h" -#include "core/strings/stringFunctions.h" -#include "core/strings/unicode.h" - - -#ifndef TORQUE_SHIPPING - -using namespace UnitTesting; - -#define TEST( x ) test( ( x ), "FAIL: " #x ) -#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x ) - -CreateUnitTest( TestString, "Util/String" ) -{ - struct StrTest - { - const UTF8* mData; - const UTF16* mUTF16; - U32 mLength; - - StrTest() : mData( 0 ), mUTF16( 0 ) {} - StrTest( const char* str ) - : mData( str ), mLength( str ? dStrlen( str ) : 0 ), mUTF16( NULL ) - { - if( str ) - mUTF16 = convertUTF8toUTF16( mData ); - } - ~StrTest() - { - if( mUTF16 ) - delete [] mUTF16; - } - }; - - Vector< StrTest* > mStrings; - - template< class T > - void runTestOnStrings() - { - for( U32 i = 0; i < mStrings.size(); ++ i ) - T::run( this, *mStrings[ i ] ); - } - template< class T > - void runPairwiseTestOnStrings() - { - for( U32 i = 0; i < mStrings.size(); ++ i ) - for( U32 j = 0; j < mStrings.size(); ++ j ) - T::run( this, *mStrings[ i ], *mStrings[ j ] ); - } - - struct Test1 - { - static void run( TestString* test, StrTest& data ) - { - String str( data.mData ); - String str16( data.mUTF16 ); - - XTEST( test, str.length() == data.mLength ); - XTEST( test, str.size() == data.mLength + 1 ); - XTEST( test, str.isEmpty() || str.length() > 0 ); - XTEST( test, str.length() == str16.length() ); - XTEST( test, str.size() == str16.size() ); - - XTEST( test, dMemcmp( str.utf16(), str16.utf16(), str.length() * sizeof( UTF16 ) ) == 0 ); - XTEST( test, !data.mData || dMemcmp( str.utf16(), data.mUTF16, str.length() * sizeof( UTF16 ) ) == 0 ); - XTEST( test, !data.mData || dMemcmp( str16.utf8(), data.mData, str.length() ) == 0 ); - - XTEST( test, !data.mData || dStrcmp( str.utf8(), data.mData ) == 0 ); - XTEST( test, !data.mData || dStrcmp( str.utf16(), data.mUTF16 ) == 0 ); - } - }; - - struct Test2 - { - static void run( TestString* test, StrTest& data ) - { - String str( data.mData ); - - XTEST( test, str == str ); - XTEST( test, !( str != str ) ); - XTEST( test, !( str < str ) ); - XTEST( test, !( str > str ) ); - XTEST( test, str.equal( str ) ); - XTEST( test, str.equal( str, String::NoCase ) ); - } - }; - - struct Test3 - { - static void run( TestString* test, StrTest& d1, StrTest& d2 ) - { - if( &d1 != &d2 ) - XTEST( test, String( d1.mData ) != String( d2.mData ) - || ( String( d1.mData ).isEmpty() && String( d2.mData ).isEmpty() ) ); - else - XTEST( test, String( d1.mData ) == String( d2.mData ) ); - } - }; - - void testEmpty() - { - TEST( String().length() == 0 ); - TEST( String( "" ).length() == 0 ); - TEST( String().size() == 1 ); - TEST( String( "" ).size() == 1 ); - TEST( String().isEmpty() ); - TEST( String( "" ).isEmpty() ); - } - - void testTrim() - { - TEST( String( " Foobar Barfoo \n\t " ).trim() == String( "Foobar Barfoo" ) ); - TEST( String( "Foobar" ).trim() == String( "Foobar" ) ); - TEST( String( " " ).trim().isEmpty() ); - } - - void testCompare() - { - String str( "Foobar" ); - - TEST( str.compare( "Foo", 3 ) == 0 ); - TEST( str.compare( "bar", 3, String::NoCase | String::Right ) == 0 ); - TEST( str.compare( "foo", 3, String::NoCase ) == 0 ); - TEST( str.compare( "BAR", 3, String::NoCase | String::Right ) == 0 ); - TEST( str.compare( "Foobar" ) == 0 ); - TEST( str.compare( "Foo" ) != 0 ); - TEST( str.compare( "foobar", 0, String::NoCase ) == 0 ); - TEST( str.compare( "FOOBAR", 0, String::NoCase ) == 0 ); - TEST( str.compare( "Foobar", 0, String::Right ) == 0 ); - TEST( str.compare( "foobar", 0, String::NoCase | String::Right ) == 0 ); - } - - void testOrder() - { - Vector< String > strs; - - strs.push_back( "a" ); - strs.push_back( "a0" ); - strs.push_back( "a1" ); - strs.push_back( "a1a" ); - strs.push_back( "a1b" ); - strs.push_back( "a2" ); - strs.push_back( "a10" ); - strs.push_back( "a20" ); - - for( U32 i = 0; i < strs.size(); ++ i ) - { - for( U32 j = 0; j < i; ++ j ) - { - TEST( strs[ j ] < strs[ i ] ); - TEST( strs[ i ] > strs[ j ] ); - - TEST( !( strs[ j ] > strs[ i ] ) ); - TEST( !( strs[ i ] < strs[ i ] ) ); - - TEST( strs[ j ] <= strs[ i ] ); - TEST( strs[ i ] >= strs[ j ] ); - } - - TEST( !( strs[ i ] < strs[ i ] ) ); - TEST( !( strs[ i ] > strs[ i ] ) ); - TEST( strs[ i ] <= strs[ i ] ); - TEST( strs[ i ] >= strs[ i ] ); - - for( U32 j = i + 1; j < strs.size(); ++ j ) - { - TEST( strs[ j ] > strs[ i ] ); - TEST( strs[ i ] < strs[ j ] ); - - TEST( !( strs[ j ] < strs[ i ] ) ); - TEST( !( strs[ i ] > strs[ j ] ) ); - - TEST( strs[ j ] >= strs[ i ] ); - TEST( strs[ i ] <= strs[ j ] ); - } - } - } - - void testFind() - { - //TODO - } - - void testInsert() - { - // String.insert( Pos, Char ) - TEST( String( "aa" ).insert( 1, 'c' ) == String( "aca" ) ); - - // String.insert( Pos, String ) - TEST( String( "aa" ).insert( 1, "cc" ) == String( "acca" ) ); - TEST( String( "aa" ).insert( 1, String( "cc" ) ) == String( "acca" ) ); - - // String.insert( Pos, String, Len ) - TEST( String( "aa" ).insert( 1, "ccdddd", 2 ) == String( "acca" ) ); - } - - void testErase() - { - TEST( String( "abba" ).erase( 1, 2 ) == String( "aa" ) ); - TEST( String( "abba" ).erase( 0, 4 ).isEmpty() ); - } - - void testReplace() - { - // String.replace( Pos, Len, String ) - TEST( String( "abba" ).replace( 1, 2, "ccc" ) == String( "accca" ) ); - TEST( String( "abba" ).replace( 1, 2, String( "ccc" ) ) == String( "accca" ) ); - TEST( String( "abba" ).replace( 0, 4, "" ).isEmpty() ); - TEST( String( "abba" ).replace( 2, 2, "c" ) == String( "abc" ) ); - - // String.replace( Char, Char ) - TEST( String().replace( 'a', 'b' ).isEmpty() ); - TEST( String( "ababc" ).replace( 'a', 'b' ) == String( "bbbbc" ) ); - TEST( String( "ababc" ).replace( 'd', 'e' ) == String( "ababc" ) ); - - // String.replace( String, String ) - TEST( String().replace( "foo", "bar" ).isEmpty() ); - TEST( String( "foobarfoo" ).replace( "foo", "bar" ) == String( "barbarbar" ) ); - TEST( String( "foobar" ).replace( "xx", "yy" ) == String( "foobar" ) ); - TEST( String( "foofoofoo" ).replace( "foo", "" ).isEmpty() ); - } - - void testSubstr() - { - TEST( String( "foobar" ).substr( 0, 3 ) == String( "foo" ) ); - TEST( String( "foobar" ).substr( 3 ) == String( "bar" ) ); - TEST( String( "foobar" ).substr( 2, 2 ) == String( "ob" ) ); - TEST( String( "foobar" ).substr( 2, 0 ).isEmpty() ); - TEST( String( "foobar" ).substr( 0, 6 ) == String( "foobar" ) ); - } - - void testToString() - { - TEST( String::ToString( U32( 1 ) ) == String( "1" ) ); - TEST( String::ToString( S32( -1 ) ) == String( "-1" ) ); - TEST( String::ToString( F32( 1.01 ) ) == String( "1.01" ) ); - TEST( String::ToString( "%s%i", "foo", 1 ) == String( "foo1" ) ); - } - - void testCaseConversion() - { - TEST( String::ToLower( "foobar123." ) == String( "foobar123." ) ); - TEST( String::ToLower( "FOOBAR123." ) == String( "foobar123." ) ); - TEST( String::ToUpper( "barfoo123." ) == String( "BARFOO123." ) ); - TEST( String::ToUpper( "BARFOO123." ) == String( "BARFOO123." ) ); - } - - void testConcat() - { - TEST( String( "foo" ) + String( "bar" ) == String( "foobar" ) ); - TEST( String() + String( "bar" ) == String( "bar" ) ); - TEST( String( "foo" ) + String() == String( "foo" ) ); - TEST( String() + String() == String() ); - TEST( String( "fo" ) + 'o' == String( "foo" ) ); - TEST( 'f' + String( "oo" ) == String( "foo" ) ); - TEST( String( "foo" ) + "bar" == String( "foobar" ) ); - TEST( "foo" + String( "bar" ) == String( "foobar" ) ); - } - - void testHash() - { - TEST( String( "foo" ).getHashCaseSensitive() == String( "foo" ).getHashCaseSensitive() ); - TEST( String( "foo" ).getHashCaseSensitive() != String( "bar" ).getHashCaseSensitive() ); - TEST( String( "foo" ).getHashCaseInsensitive() == String( "FOO" ).getHashCaseInsensitive() ); - } - - void testIntern() - { - TEST( String( "foo" ).intern().isSame( String( "foo" ).intern() ) ); - TEST( !String( "foo" ).intern().isSame( String( "bar" ).intern() ) ); - TEST( !String( "foo" ).intern().isSame( String( "Foo" ).intern() ) ); - TEST( String( "foo" ).intern() == String( "foo" ).intern() ); - TEST( String( "foo" ).intern() != String( "bar" ).intern() ); - TEST( String( "foo" ).intern().isInterned() ); - } - - void run() - { - mStrings.push_back( new StrTest( NULL ) ); - mStrings.push_back( new StrTest( "" ) ); - mStrings.push_back( new StrTest( "Torque" ) ); - mStrings.push_back( new StrTest( "TGEA" ) ); - mStrings.push_back( new StrTest( "GarageGames" ) ); - mStrings.push_back( new StrTest( "TGB" ) ); - mStrings.push_back( new StrTest( "games" ) ); - mStrings.push_back( new StrTest( "engine" ) ); - mStrings.push_back( new StrTest( "rocks" ) ); - mStrings.push_back( new StrTest( "technology" ) ); - mStrings.push_back( new StrTest( "Torque 3D" ) ); - mStrings.push_back( new StrTest( "Torque 2D" ) ); - - runTestOnStrings< Test1 >(); - runTestOnStrings< Test2 >(); - - runPairwiseTestOnStrings< Test3 >(); - - testEmpty(); - testTrim(); - testCompare(); - testOrder(); - testFind(); - testInsert(); - testReplace(); - testErase(); - testSubstr(); - testToString(); - testCaseConversion(); - testConcat(); - testHash(); - testIntern(); - - for( U32 i = 0; i < mStrings.size(); ++ i ) - delete mStrings[ i ]; - mStrings.clear(); - } -}; - -CreateUnitTest( TestStringBuilder, "Util/StringBuilder" ) -{ - void run() - { - StringBuilder str; - - str.append( 'f' ); - str.append( "oo" ); - str.append( String( "ba" ) ); - str.append( "rfajskfdj", 1 ); - str.format( "%s", "barfoo" ); - - TEST( str.end() == String( "foobarbarfoo" ) ); - } -}; - -#endif // !TORQUE_SHIPPING From 18c5c252976da63a9ef232350a36d958efc2b1ef Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 15 Jul 2014 23:57:02 +0200 Subject: [PATCH 128/317] Ported path test. --- Engine/source/core/util/test/pathTest.cpp | 40 +++++++++++++++++++++ Engine/source/core/util/test/testPath.cpp | 44 ----------------------- 2 files changed, 40 insertions(+), 44 deletions(-) create mode 100644 Engine/source/core/util/test/pathTest.cpp delete mode 100644 Engine/source/core/util/test/testPath.cpp diff --git a/Engine/source/core/util/test/pathTest.cpp b/Engine/source/core/util/test/pathTest.cpp new file mode 100644 index 000000000..67009da6a --- /dev/null +++ b/Engine/source/core/util/test/pathTest.cpp @@ -0,0 +1,40 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "core/util/path.h" + +TEST(MakeRelativePath, MakeRelativePath) +{ + EXPECT_EQ(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/interiors/"), "burg/file.png"); + EXPECT_EQ(Torque::Path::MakeRelativePath("art/interiors/file.png", "art/interiors/burg/"), "../file.png"); + EXPECT_EQ(Torque::Path::MakeRelativePath("art/file.png", "art/interiors/burg/"), "../../file.png"); + EXPECT_EQ(Torque::Path::MakeRelativePath("file.png", "art/interiors/burg/"), "../../../file.png"); + EXPECT_EQ(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/interiors/burg/"), "file.png"); + EXPECT_EQ(Torque::Path::MakeRelativePath("art/interiors/camp/file.png", "art/interiors/burg/"), "../camp/file.png"); + EXPECT_EQ(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/shapes/"), "../interiors/burg/file.png"); + EXPECT_EQ(Torque::Path::MakeRelativePath("levels/den/file.png", "art/interiors/burg/"), "../../../levels/den/file.png"); + EXPECT_EQ(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/dts/burg/"), "../../interiors/burg/file.png"); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/core/util/test/testPath.cpp b/Engine/source/core/util/test/testPath.cpp deleted file mode 100644 index 94593185b..000000000 --- a/Engine/source/core/util/test/testPath.cpp +++ /dev/null @@ -1,44 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "unit/test.h" -#include "core/util/path.h" - -using namespace UnitTesting; - -#define TEST( x ) test( ( x ), "FAIL: " #x ) - -CreateUnitTest(TestPathMakeRelativePath, "Core/Util/Path/MakeRelativePath") -{ - void run() - { - TEST(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/interiors/") == "burg/file.png"); - TEST(Torque::Path::MakeRelativePath("art/interiors/file.png", "art/interiors/burg/") == "../file.png"); - TEST(Torque::Path::MakeRelativePath("art/file.png", "art/interiors/burg/") == "../../file.png"); - TEST(Torque::Path::MakeRelativePath("file.png", "art/interiors/burg/") == "../../../file.png"); - TEST(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/interiors/burg/") == "file.png"); - TEST(Torque::Path::MakeRelativePath("art/interiors/camp/file.png", "art/interiors/burg/") == "../camp/file.png"); - TEST(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/shapes/") == "../interiors/burg/file.png"); - TEST(Torque::Path::MakeRelativePath("levels/den/file.png", "art/interiors/burg/") == "../../../levels/den/file.png"); - TEST(Torque::Path::MakeRelativePath("art/interiors/burg/file.png", "art/dts/burg/") == "../../interiors/burg/file.png"); - } -}; From c2da67d47302111514cd9bcf189a84e7c45e139e Mon Sep 17 00:00:00 2001 From: Areloch Date: Tue, 15 Jul 2014 17:44:19 -0500 Subject: [PATCH 129/317] Fixes the splash screen so that it displays until the game has actually done initial setup and can display GUI elements. This prevents the ugly blank window just sitting there while the GUI loads. --- Engine/source/console/consoleFunctions.cpp | 7 +++++++ Engine/source/gui/core/guiCanvas.cpp | 18 ++++++++++++++++++ Engine/source/platform/platform.h | 3 +++ Engine/source/windowManager/platformWindow.h | 7 +++++++ .../source/windowManager/platformWindowMgr.h | 3 +++ .../windowManager/win32/win32SplashScreen.cpp | 7 +++++++ .../source/windowManager/win32/win32Window.cpp | 8 ++++++-- .../windowManager/win32/win32WindowMgr.cpp | 9 +++++---- .../windowManager/win32/win32WindowMgr.h | 6 ++++++ Templates/Empty/game/scripts/gui/startupGui.cs | 6 ++++++ Templates/Full/game/scripts/gui/startupGui.cs | 6 ++++++ 11 files changed, 74 insertions(+), 6 deletions(-) diff --git a/Engine/source/console/consoleFunctions.cpp b/Engine/source/console/consoleFunctions.cpp index f5c90194e..2dea93f44 100644 --- a/Engine/source/console/consoleFunctions.cpp +++ b/Engine/source/console/consoleFunctions.cpp @@ -1589,6 +1589,13 @@ DefineEngineFunction( displaySplashWindow, bool, (const char* path), ("art/gui/s return Platform::displaySplashWindow(path); } +DefineEngineFunction( closeSplashWindow, void, (),, + "Close our startup splash window.\n\n" + "@note This is currently only implemented on Windows.\n\n" + "@ingroup Platform" ) +{ + Platform::closeSplashWindow(); +} //----------------------------------------------------------------------------- DefineEngineFunction( getWebDeployment, bool, (),, diff --git a/Engine/source/gui/core/guiCanvas.cpp b/Engine/source/gui/core/guiCanvas.cpp index cbef4673c..f5a84e77e 100644 --- a/Engine/source/gui/core/guiCanvas.cpp +++ b/Engine/source/gui/core/guiCanvas.cpp @@ -2683,3 +2683,21 @@ ConsoleMethod( GuiCanvas, setVideoMode, void, 5, 8, // Store the new mode into a pref. Con::setVariable( "$pref::Video::mode", vm.toString() ); } + +ConsoleMethod( GuiCanvas, showWindow, void, 2, 2, "" ) +{ + if (!object->getPlatformWindow()) + return; + + object->getPlatformWindow()->show(); + WindowManager->setDisplayWindow(true); + object->getPlatformWindow()->setDisplayWindow(true); +} + +ConsoleMethod( GuiCanvas, hideWindow, void, 2, 2, "" ) +{ + if (!object->getPlatformWindow()) + return; + + object->getPlatformWindow()->hide(); +} \ No newline at end of file diff --git a/Engine/source/platform/platform.h b/Engine/source/platform/platform.h index 3b5a57358..d390eef5c 100644 --- a/Engine/source/platform/platform.h +++ b/Engine/source/platform/platform.h @@ -338,6 +338,9 @@ namespace Platform // display Splash Window bool displaySplashWindow( String path ); + // close Splash Window + bool closeSplashWindow(); + void openFolder( const char* path ); // Open file at the OS level, according to registered file-types. diff --git a/Engine/source/windowManager/platformWindow.h b/Engine/source/windowManager/platformWindow.h index 11d28d384..680e8b12b 100644 --- a/Engine/source/windowManager/platformWindow.h +++ b/Engine/source/windowManager/platformWindow.h @@ -89,6 +89,10 @@ protected: /// Offscreen Render bool mOffscreenRender; + /// This is set as part of the canvas being shown, and flags that the windows should render as normal from now on. + // Basically a flag that lets the window manager know that we've handled the splash screen, and to operate as normal. + bool mDisplayWindow; + /// Protected constructor so that the win PlatformWindow() { @@ -104,6 +108,7 @@ protected: mSuppressReset = false; mOffscreenRender = false; + mDisplayWindow = false; } public: @@ -180,6 +185,8 @@ public: /// This is called to poll the window as to it's idle state. virtual bool getOffscreenRender() { return mOffscreenRender; }; + /// Set whether this window is should display as normal + virtual void setDisplayWindow(bool val ) { mDisplayWindow = val; }; /// Set Focused State (Foreground) /// diff --git a/Engine/source/windowManager/platformWindowMgr.h b/Engine/source/windowManager/platformWindowMgr.h index b744af56b..949e581a8 100644 --- a/Engine/source/windowManager/platformWindowMgr.h +++ b/Engine/source/windowManager/platformWindowMgr.h @@ -133,6 +133,9 @@ public: /// This method removes the curtain window. virtual void raiseCurtain()=0; + /// This method indicates to created windows to show as normal. + virtual void setDisplayWindow(bool set)=0; + private: /// Process command line arguments from StandardMainLoop. This is done to /// allow web plugin functionality, where we are passed platform-specific diff --git a/Engine/source/windowManager/win32/win32SplashScreen.cpp b/Engine/source/windowManager/win32/win32SplashScreen.cpp index 83189dac3..29542ef41 100644 --- a/Engine/source/windowManager/win32/win32SplashScreen.cpp +++ b/Engine/source/windowManager/win32/win32SplashScreen.cpp @@ -121,6 +121,13 @@ void CloseSplashWindow(HINSTANCE hinst) } +bool Platform::closeSplashWindow() +{ + CloseSplashWindow(GetModuleHandle(NULL)); + + return true; +} + bool Platform::displaySplashWindow( String path ) { if(path.isEmpty()) diff --git a/Engine/source/windowManager/win32/win32Window.cpp b/Engine/source/windowManager/win32/win32Window.cpp index 0cbb59433..eb72a3153 100644 --- a/Engine/source/windowManager/win32/win32Window.cpp +++ b/Engine/source/windowManager/win32/win32Window.cpp @@ -153,7 +153,9 @@ void Win32Window::setVideoMode( const GFXVideoMode &mode ) { SetWindowLong( getHWND(), GWL_STYLE, WS_POPUP); SetWindowPos( getHWND(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); - ShowWindow(getHWND(), SW_SHOWNORMAL); + + if(mDisplayWindow) + ShowWindow(getHWND(), SW_SHOWNORMAL); // Clear the menu bar from the window for full screen HMENU menu = GetMenu(getHWND()); @@ -216,7 +218,9 @@ void Win32Window::setVideoMode( const GFXVideoMode &mode ) // We have to force Win32 to update the window frame and make the window // visible and no longer topmost - this code might be possible to simplify. SetWindowPos( getHWND(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); - ShowWindow( getHWND(), SW_SHOWNORMAL); + + if(mDisplayWindow) + ShowWindow( getHWND(), SW_SHOWNORMAL); } mFullscreen = false; diff --git a/Engine/source/windowManager/win32/win32WindowMgr.cpp b/Engine/source/windowManager/win32/win32WindowMgr.cpp index 3777a01f6..266049b6b 100644 --- a/Engine/source/windowManager/win32/win32WindowMgr.cpp +++ b/Engine/source/windowManager/win32/win32WindowMgr.cpp @@ -54,6 +54,8 @@ Win32WindowManager::Win32WindowManager() mOffscreenRender = false; + mDisplayWindow = false; + buildMonitorsList(); } @@ -263,11 +265,10 @@ PlatformWindow *Win32WindowManager::createWindow(GFXDevice *device, const GFXVid // If we're not rendering offscreen, make sure our window is shown and drawn to. - if (!mOffscreenRender) - ShowWindow( w32w->mWindowHandle, SW_SHOWDEFAULT ); + w32w->setDisplayWindow(mDisplayWindow); - // Close any splash screen we created - CloseSplashWindow(winState.appInstance); + if (!mOffscreenRender && mDisplayWindow) + ShowWindow( w32w->mWindowHandle, SW_SHOWDEFAULT ); // Bind the window to the specified device. if(device) diff --git a/Engine/source/windowManager/win32/win32WindowMgr.h b/Engine/source/windowManager/win32/win32WindowMgr.h index be9cdaa42..5282d116b 100644 --- a/Engine/source/windowManager/win32/win32WindowMgr.h +++ b/Engine/source/windowManager/win32/win32WindowMgr.h @@ -56,6 +56,10 @@ class Win32WindowManager : public PlatformWindowManager // is intended for offscreen rendering bool mOffscreenRender; + /// This is set as part of the canvas being shown, and flags that the windows should render as normal from now on. + // Basically a flag that lets the window manager know that we've handled the splash screen, and to operate as normal. + bool mDisplayWindow; + /// Internal structure used when enumerating monitors struct MonitorInfo { HMONITOR monitorHandle; @@ -117,6 +121,8 @@ public: virtual void lowerCurtain(); virtual void raiseCurtain(); + + virtual void setDisplayWindow(bool set) { mDisplayWindow = set; } }; #endif \ No newline at end of file diff --git a/Templates/Empty/game/scripts/gui/startupGui.cs b/Templates/Empty/game/scripts/gui/startupGui.cs index 675daeafa..d0226a9bf 100644 --- a/Templates/Empty/game/scripts/gui/startupGui.cs +++ b/Templates/Empty/game/scripts/gui/startupGui.cs @@ -29,6 +29,12 @@ function loadStartup() // The index of the current splash screen $StartupIdx = 0; + // As we know at this point that the initial load is complete, + // we can hide any splash screen we have, and show the canvas. + // This keeps things looking nice, instead of having a blank window + closeSplashWindow(); + Canvas.showWindow(); + // A list of the splash screens and logos // to cycle through. Note that they have to // be in consecutive numerical order diff --git a/Templates/Full/game/scripts/gui/startupGui.cs b/Templates/Full/game/scripts/gui/startupGui.cs index 675daeafa..d0226a9bf 100644 --- a/Templates/Full/game/scripts/gui/startupGui.cs +++ b/Templates/Full/game/scripts/gui/startupGui.cs @@ -29,6 +29,12 @@ function loadStartup() // The index of the current splash screen $StartupIdx = 0; + // As we know at this point that the initial load is complete, + // we can hide any splash screen we have, and show the canvas. + // This keeps things looking nice, instead of having a blank window + closeSplashWindow(); + Canvas.showWindow(); + // A list of the splash screens and logos // to cycle through. Note that they have to // be in consecutive numerical order From 2864edf2915d47bd4cc5194c792c70eefefa5396 Mon Sep 17 00:00:00 2001 From: Areloch Date: Tue, 15 Jul 2014 17:52:27 -0500 Subject: [PATCH 130/317] Fixed spacing --- Engine/source/windowManager/platformWindowMgr.h | 2 +- Engine/source/windowManager/win32/win32Window.cpp | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine/source/windowManager/platformWindowMgr.h b/Engine/source/windowManager/platformWindowMgr.h index 949e581a8..f2089f51d 100644 --- a/Engine/source/windowManager/platformWindowMgr.h +++ b/Engine/source/windowManager/platformWindowMgr.h @@ -133,7 +133,7 @@ public: /// This method removes the curtain window. virtual void raiseCurtain()=0; - /// This method indicates to created windows to show as normal. + /// This method indicates to created windows to show as normal. virtual void setDisplayWindow(bool set)=0; private: diff --git a/Engine/source/windowManager/win32/win32Window.cpp b/Engine/source/windowManager/win32/win32Window.cpp index eb72a3153..3b95a223a 100644 --- a/Engine/source/windowManager/win32/win32Window.cpp +++ b/Engine/source/windowManager/win32/win32Window.cpp @@ -155,7 +155,7 @@ void Win32Window::setVideoMode( const GFXVideoMode &mode ) SetWindowPos( getHWND(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); if(mDisplayWindow) - ShowWindow(getHWND(), SW_SHOWNORMAL); + ShowWindow(getHWND(), SW_SHOWNORMAL); // Clear the menu bar from the window for full screen HMENU menu = GetMenu(getHWND()); @@ -220,7 +220,7 @@ void Win32Window::setVideoMode( const GFXVideoMode &mode ) SetWindowPos( getHWND(), HWND_NOTOPMOST, 0, 0, 0, 0, SWP_NOMOVE | SWP_NOSIZE | SWP_FRAMECHANGED); if(mDisplayWindow) - ShowWindow( getHWND(), SW_SHOWNORMAL); + ShowWindow( getHWND(), SW_SHOWNORMAL); } mFullscreen = false; From 552cb87770afea8f7faa9354e40d7f2f258d1918 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Thu, 17 Jul 2014 10:22:02 +0200 Subject: [PATCH 131/317] Ported FixedSizeDeque test. --- ...dSizeDeque.cpp => tFixedSizeDequeTest.cpp} | 45 +++++++++---------- 1 file changed, 20 insertions(+), 25 deletions(-) rename Engine/source/core/util/test/{testFixedSizeDeque.cpp => tFixedSizeDequeTest.cpp} (63%) diff --git a/Engine/source/core/util/test/testFixedSizeDeque.cpp b/Engine/source/core/util/test/tFixedSizeDequeTest.cpp similarity index 63% rename from Engine/source/core/util/test/testFixedSizeDeque.cpp rename to Engine/source/core/util/test/tFixedSizeDequeTest.cpp index e9b3f76f5..1fcc94e20 100644 --- a/Engine/source/core/util/test/testFixedSizeDeque.cpp +++ b/Engine/source/core/util/test/tFixedSizeDequeTest.cpp @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC +// Copyright (c) 2014 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to @@ -20,35 +20,30 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -#include "unit/test.h" +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" #include "core/util/tFixedSizeDeque.h" - -using namespace UnitTesting; - -#define TEST( x ) test( ( x ), "FAIL: " #x ) - -CreateUnitTest( TestFixedSizeDeque, "Util/FixedSizeDeque" ) +TEST(FixedSizeDeque, FixedSizeDeque) { - void run() - { - enum { DEQUE_SIZE = 3 }; - FixedSizeDeque< U32 > deque( DEQUE_SIZE ); + enum { DEQUE_SIZE = 3 }; + FixedSizeDeque< U32 > deque( DEQUE_SIZE ); - TEST( deque.capacity() == DEQUE_SIZE ); - TEST( deque.size() == 0 ); + EXPECT_EQ( deque.capacity(), DEQUE_SIZE ); + EXPECT_EQ( deque.size(), 0 ); - deque.pushFront( 1 ); - TEST( deque.capacity() == ( DEQUE_SIZE - 1 ) ); - TEST( deque.size() == 1 ); - TEST( !deque.isEmpty() ); + deque.pushFront( 1 ); + EXPECT_EQ( deque.capacity(), ( DEQUE_SIZE - 1 ) ); + EXPECT_EQ( deque.size(), 1 ); + EXPECT_FALSE( deque.isEmpty() ); - deque.pushBack( 2 ); - TEST( deque.capacity() == ( DEQUE_SIZE - 2 ) ); - TEST( deque.size() == 2 ); + deque.pushBack( 2 ); + EXPECT_EQ( deque.capacity(), ( DEQUE_SIZE - 2 ) ); + EXPECT_EQ( deque.size(), 2 ); - TEST( deque.popFront() == 1 ); - TEST( deque.popFront() == 2 ); - TEST( deque.isEmpty() ); - } + EXPECT_EQ( deque.popFront(), 1 ); + EXPECT_EQ( deque.popFront(), 2 ); + EXPECT_TRUE( deque.isEmpty() ); }; + +#endif \ No newline at end of file From b43b6d22422efeff831b14f6d1e41a9e954b6f07 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Thu, 17 Jul 2014 10:32:14 +0200 Subject: [PATCH 132/317] Ported basic platform types test. --- .../platform/test/platformTypesTest.cpp | 118 +++++++++++++++++ .../source/platform/test/testBasicTypes.cpp | 124 ------------------ 2 files changed, 118 insertions(+), 124 deletions(-) create mode 100644 Engine/source/platform/test/platformTypesTest.cpp delete mode 100644 Engine/source/platform/test/testBasicTypes.cpp diff --git a/Engine/source/platform/test/platformTypesTest.cpp b/Engine/source/platform/test/platformTypesTest.cpp new file mode 100644 index 000000000..b08194c24 --- /dev/null +++ b/Engine/source/platform/test/platformTypesTest.cpp @@ -0,0 +1,118 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "platform/platform.h" +#include "core/util/endian.h" + +TEST(PlatformTypes, Sizes) +{ + // Run through all the types and ensure they're the right size. +#define CheckType(typeName, expectedSize) \ + EXPECT_EQ( sizeof(typeName), expectedSize) \ + << "Wrong size for a " #typeName ", expected " #expectedSize; + + // One byte types. + CheckType(bool, 1); + CheckType(U8, 1); + CheckType(S8, 1); + CheckType(UTF8, 1); + + // Two byte types. + CheckType(U16, 2); + CheckType(S16, 2); + CheckType(UTF16, 2); + + // Four byte types. + CheckType(U32, 4); + CheckType(S32, 4); + CheckType(F32, 4); + CheckType(UTF32, 4); + + // Eight byte types. + CheckType(U64, 8); + CheckType(S64, 8); + CheckType(F64, 8); + + // 16 byte (128bit) types will go here, when we get some. +#undef CheckType +}; + +TEST(PlatformTypes, EndianConversion) +{ + // Convenient and non-palindrome byte patterns to test with. + const U16 U16Test = 0xA1B2; + const S16 S16Test = 0x52A1; + + const U32 U32Test = 0xA1B2C3D4; + const S32 S32Test = 0xD4C3B2A1; + const F32 F32Test = 1234.5678f; + + //const U64 U64Test = 0xA1B2C3D4E3F2E10A; + //const S64 S64Test = 0x1A2B3C4D3E2F1EA0; + const F64 F64Test = 12345678.9101112131415; + + // Run through all the conversions - bump stuff from host to little or big + // endian and back again. +#define CheckEndianRoundTrip(type, b_or_l) \ + EXPECT_EQ( type##Test, convert##b_or_l##EndianToHost(convertHostTo##b_or_l##Endian(type##Test))) \ + << "Failed to convert the " #type " test value to " #b_or_l " endian and back to host endian order."; + +#define CheckTypeBothWays(type) \ + CheckEndianRoundTrip(type, B); \ + CheckEndianRoundTrip(type, L); + +#define CheckIntsForBitSize(bits) \ + CheckTypeBothWays( U##bits ); \ + CheckTypeBothWays( S##bits ); + + // Don't check 8-bit types - they aren't affected by endian issues. + + // Check the >1 byte int types, though. + CheckIntsForBitSize(16); + CheckIntsForBitSize(32); + // CheckIntsForBitSize(64); // don't have convertHostToLEndian(const U64/S64) so this doesn't work + + // And check the float types. + CheckTypeBothWays(F32); + CheckTypeBothWays(F64); + + // We'd check 128bit types here, if we had any. + +#undef CheckIntsForBitSize +#undef CheckTypeBothWays +#undef CheckEndianRoundTrip +}; + +TEST(PlatformTypes, EndianSwap) +{ + U32 swap32 = 0xABCDEF12; + U16 swap16 = 0xABCD; + + EXPECT_EQ(endianSwap(swap32), 0x12EFCDAB) + << "32 bit endianSwap should reverse byte order, but didn't."; + EXPECT_EQ(endianSwap(swap16), 0xCDAB) + << "16 bit endianSwap should reverse byte order, but didn't."; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/platform/test/testBasicTypes.cpp b/Engine/source/platform/test/testBasicTypes.cpp deleted file mode 100644 index 848356e2f..000000000 --- a/Engine/source/platform/test/testBasicTypes.cpp +++ /dev/null @@ -1,124 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "platform/platform.h" -#include "core/util/endian.h" -#include "unit/test.h" - -using namespace UnitTesting; - -CreateUnitTest(CheckTypeSizes, "Platform/Types/Sizes") -{ - void run() - { - // Run through all the types and ensure they're the right size. - -#define CheckType(typeName, expectedSize) \ - test( sizeof(typeName) == expectedSize, "Wrong size for a " #typeName ", expected " #expectedSize); - - // One byte types. - CheckType(bool, 1); - CheckType(U8, 1); - CheckType(S8, 1); - CheckType(UTF8, 1); - - // Two byte types. - CheckType(U16, 2); - CheckType(S16, 2); - CheckType(UTF16, 2); - - // Four byte types. - CheckType(U32, 4); - CheckType(S32, 4); - CheckType(F32, 4); - CheckType(UTF32, 4); - - // Eight byte types. - CheckType(U64, 8); - CheckType(S64, 8); - CheckType(F64, 8); - - // 16 byte (128bit) types will go here, when we get some. -#undef CheckType - } -}; - -CreateUnitTest(CheckEndianConversion, "Platform/Types/EndianRoundTrip") -{ - void run() - { - // Convenient and non-palindrome byte patterns to test with. - const U16 U16Test = 0xA1B2; - const S16 S16Test = 0x52A1; - - const U32 U32Test = 0xA1B2C3D4; - const S32 S32Test = 0xD4C3B2A1; - const F32 F32Test = 1234.5678f; - - //const U64 U64Test = 0xA1B2C3D4E3F2E10A; - //const S64 S64Test = 0x1A2B3C4D3E2F1EA0; - const F64 F64Test = 12345678.9101112131415; - - // Run through all the conversions - bump stuff from host to little or big - // endian and back again. -#define CheckEndianRoundTrip(type, b_or_l) \ - test( type##Test == convert##b_or_l##EndianToHost(convertHostTo##b_or_l##Endian(type##Test)), "Failed to convert the " #type " test value to " #b_or_l " endian and back to host endian order."); - -#define CheckTypeBothWays(type) \ - CheckEndianRoundTrip(type, B); \ - CheckEndianRoundTrip(type, L); - -#define CheckIntsForBitSize(bits) \ - CheckTypeBothWays( U##bits ); \ - CheckTypeBothWays( S##bits ); - - // Don't check 8-bit types - they aren't affected by endian issues. - - // Check the >1 byte int types, though. - CheckIntsForBitSize(16); - CheckIntsForBitSize(32); - // CheckIntsForBitSize(64); // don't have convertHostToLEndian(const U64/S64) so this doesn't work - - // And check the float types. - CheckTypeBothWays(F32); - CheckTypeBothWays(F64); - - // We'd check 128bit types here, if we had any. - -#undef CheckIntsForBitSize -#undef CheckTypeBothWays -#undef CheckEndianRoundTrip - } -}; - -CreateUnitTest(CheckEndianSwap, "Platform/Types/EndianSwap") -{ - void run() - { - U32 swap32 = 0xABCDEF12; - U16 swap16 = 0xABCD; - - test(endianSwap(swap32) == 0x12EFCDAB, "32 bit endianSwap should reverse byte order, but didn't."); - test(endianSwap(swap16) == 0xCDAB, "16 bit endianSwap should reverse byte order, but didn't."); - } -}; - From e5bb2183770af7c653debc145cd3704ec74c54d3 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Thu, 17 Jul 2014 13:24:04 +0200 Subject: [PATCH 133/317] Turns out we don't actually need a Canvas! Cool. --- Templates/Empty/game/runTests.cs | 10 +--------- Templates/Full/game/runTests.cs | 10 +--------- 2 files changed, 2 insertions(+), 18 deletions(-) diff --git a/Templates/Empty/game/runTests.cs b/Templates/Empty/game/runTests.cs index 9099a6e68..e44d2fbd4 100644 --- a/Templates/Empty/game/runTests.cs +++ b/Templates/Empty/game/runTests.cs @@ -1,13 +1,5 @@ -new GuiControlProfile(GuiDefaultProfile); -new GuiControlProfile(GuiToolTipProfile); -new GuiCanvas(Canvas); -function onLightManagerActivate() {} -function onLightManagerDeactivate() {} -Canvas.setWindowTitle("Torque 3D Unit Tests"); -new RenderPassManager(DiffuseRenderPassManager); -setLightManager("Basic Lighting"); setLogMode(2); $Con::LogBufferEnabled = false; -$Testing::checkMemoryLeaks = false; +$Testing::CheckMemoryLeaks = false; runAllUnitTests(); quit(); diff --git a/Templates/Full/game/runTests.cs b/Templates/Full/game/runTests.cs index 9099a6e68..e44d2fbd4 100644 --- a/Templates/Full/game/runTests.cs +++ b/Templates/Full/game/runTests.cs @@ -1,13 +1,5 @@ -new GuiControlProfile(GuiDefaultProfile); -new GuiControlProfile(GuiToolTipProfile); -new GuiCanvas(Canvas); -function onLightManagerActivate() {} -function onLightManagerDeactivate() {} -Canvas.setWindowTitle("Torque 3D Unit Tests"); -new RenderPassManager(DiffuseRenderPassManager); -setLightManager("Basic Lighting"); setLogMode(2); $Con::LogBufferEnabled = false; -$Testing::checkMemoryLeaks = false; +$Testing::CheckMemoryLeaks = false; runAllUnitTests(); quit(); From 4f3be256991ff9eae19ada54a3e3a1c7bf9e7d74 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Fri, 18 Jul 2014 00:40:11 -0500 Subject: [PATCH 134/317] prevents an infinite while loop by putting a cap of 4MS on occlusion queries. --- Engine/source/gfx/D3D9/gfxD3D9OcclusionQuery.cpp | 11 +++++++++-- Engine/source/gfx/gfxDevice.cpp | 2 +- Engine/source/gfx/gfxDevice.h | 4 ++++ 3 files changed, 14 insertions(+), 3 deletions(-) diff --git a/Engine/source/gfx/D3D9/gfxD3D9OcclusionQuery.cpp b/Engine/source/gfx/D3D9/gfxD3D9OcclusionQuery.cpp index 142896f68..99e664652 100644 --- a/Engine/source/gfx/D3D9/gfxD3D9OcclusionQuery.cpp +++ b/Engine/source/gfx/D3D9/gfxD3D9OcclusionQuery.cpp @@ -124,9 +124,16 @@ GFXD3D9OcclusionQuery::OcclusionQueryStatus GFXD3D9OcclusionQuery::getStatus( bo DWORD dwOccluded = 0; if ( block ) - { + { while( ( hRes = mQuery->GetData( &dwOccluded, sizeof(DWORD), D3DGETDATA_FLUSH ) ) == S_FALSE ) - ; + { + //If we're stalled out, proceed with worst-case scenario -BJR + if(GFX->mFrameTime->getElapsedMs()>4) + { + this->end(); + return NotOccluded; + } + } } else { diff --git a/Engine/source/gfx/gfxDevice.cpp b/Engine/source/gfx/gfxDevice.cpp index 3bb634027..528467f56 100644 --- a/Engine/source/gfx/gfxDevice.cpp +++ b/Engine/source/gfx/gfxDevice.cpp @@ -804,7 +804,7 @@ inline bool GFXDevice::beginScene() // Send the start of frame signal. getDeviceEventSignal().trigger( GFXDevice::deStartOfFrame ); - + mFrameTime->reset(); return beginSceneInternal(); } diff --git a/Engine/source/gfx/gfxDevice.h b/Engine/source/gfx/gfxDevice.h index 44a889f66..13620755e 100644 --- a/Engine/source/gfx/gfxDevice.h +++ b/Engine/source/gfx/gfxDevice.h @@ -54,6 +54,9 @@ #include "math/util/frustum.h" #endif +#ifndef _PLATFORM_PLATFORMTIMER_H_ +#include "platform/platformTimer.h" +#endif class FontRenderBatcher; class GFont; @@ -743,6 +746,7 @@ public: virtual void endScene(); virtual void beginField(); virtual void endField(); + PlatformTimer *mFrameTime; virtual GFXTexHandle & getFrontBuffer(){ return mFrontBuffer[mCurrentFrontBufferIdx]; } From 262a08b854a503853769f24e0ac5b585c9a9b687 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Fri, 18 Jul 2014 21:59:09 +0200 Subject: [PATCH 135/317] Remove interactive alert tests. --- Engine/source/platform/test/testAlerts.cpp | 50 ---------------------- 1 file changed, 50 deletions(-) delete mode 100644 Engine/source/platform/test/testAlerts.cpp diff --git a/Engine/source/platform/test/testAlerts.cpp b/Engine/source/platform/test/testAlerts.cpp deleted file mode 100644 index c79d9ff44..000000000 --- a/Engine/source/platform/test/testAlerts.cpp +++ /dev/null @@ -1,50 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "platform/platform.h" -#include "unit/test.h" - -using namespace UnitTesting; - -CreateInteractiveTest(CheckPlatformAlerts, "Platform/Alerts") -{ - void run() - { - // Run through all the alert types. - Platform::AlertOK("Test #1 - AlertOK", "This is a test of Platform::AlertOK. I am a blocking dialog with an OK button. Please hit OK to continue."); - test(true, "AlertOK should return when the user clicks on it..."); // <-- gratuitous test point. - - bool res; - - res = Platform::AlertOKCancel("Test #2 - AlertOKCancel", "This is a test of Platform::alertOKCancel. I am a blocking dialog with an OK and a Cancel button. Please hit Cancel to continue."); - test(res==false,"AlertOKCancel - Didn't get cancel. User error, or just bad code?"); - - res = Platform::AlertOKCancel("Test #3 - AlertOKCancel", "This is a test of Platform::alertOKCancel. I am a blocking dialog with an OK and a Cancel button. Please hit OK to continue."); - test(res==true,"AlertOKCancel - Didn't get ok. User error, or just bad code?"); - - res = Platform::AlertRetry("Test #4 - AlertRetry", "This is a test of Platform::AlertRetry. I am a blocking dialog with an Retry and a Cancel button. Please hit Retry to continue."); - test(res==true,"AlertRetry - Didn't get retry. User error, or just bad code?"); - - res = Platform::AlertRetry("Test #5 - AlertRetry", "This is a test of Platform::AlertRetry. I am a blocking dialog with an Retry and a Cancel button. Please hit Cancel to continue."); - test(res==false,"AlertRetry - Didn't get cancel. User error, or just bad code?"); - } -}; \ No newline at end of file From 57ac2f854694fd62e2055fd15e32effd63cf3de5 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Fri, 18 Jul 2014 22:07:38 +0200 Subject: [PATCH 136/317] Add GG(c) and MIT license to CMake files. --- Tools/CMake/CMakeLists.txt | 22 +++++++++++++++++++++ Tools/CMake/basics.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/collada.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/convexDecomp.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/libogg.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/libtheora.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/libvorbis.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/ljpeg.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/lmng.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/lpng.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/lungif.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/opcode.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/pcre.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/recast.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/squish.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/tinyxml.cmake | 22 +++++++++++++++++++++ Tools/CMake/libraries/zlib.cmake | 22 +++++++++++++++++++++ Tools/CMake/modules/module_hydra.cmake | 22 +++++++++++++++++++++ Tools/CMake/modules/module_navigation.cmake | 22 +++++++++++++++++++++ Tools/CMake/modules/module_oculusVR.cmake | 22 +++++++++++++++++++++ Tools/CMake/modules/module_testing.cmake | 22 +++++++++++++++++++++ Tools/CMake/template.cmake | 22 +++++++++++++++++++++ Tools/CMake/torque3d.cmake | 22 +++++++++++++++++++++ 23 files changed, 506 insertions(+) diff --git a/Tools/CMake/CMakeLists.txt b/Tools/CMake/CMakeLists.txt index 766c1c771..741cc6b1c 100644 --- a/Tools/CMake/CMakeLists.txt +++ b/Tools/CMake/CMakeLists.txt @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + include(basics.cmake) setupVersionNumbers() diff --git a/Tools/CMake/basics.cmake b/Tools/CMake/basics.cmake index f1da3e2bf..6f844afc4 100644 --- a/Tools/CMake/basics.cmake +++ b/Tools/CMake/basics.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project("Torque3DEngine") set(TORQUE_TEMPLATE "Full" CACHE STRING "the template to use") diff --git a/Tools/CMake/libraries/collada.cmake b/Tools/CMake/libraries/collada.cmake index 00fa94197..aa8ada02d 100644 --- a/Tools/CMake/libraries/collada.cmake +++ b/Tools/CMake/libraries/collada.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(collada) addPath("${libDir}/collada/src/1.4/dom") diff --git a/Tools/CMake/libraries/convexDecomp.cmake b/Tools/CMake/libraries/convexDecomp.cmake index 3cb4c2950..6bf2c29d8 100644 --- a/Tools/CMake/libraries/convexDecomp.cmake +++ b/Tools/CMake/libraries/convexDecomp.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(convexDecomp) if(UNIX) diff --git a/Tools/CMake/libraries/libogg.cmake b/Tools/CMake/libraries/libogg.cmake index 1b3fdf37f..c68617264 100644 --- a/Tools/CMake/libraries/libogg.cmake +++ b/Tools/CMake/libraries/libogg.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(libogg) addPath("${libDir}/libogg" REC) diff --git a/Tools/CMake/libraries/libtheora.cmake b/Tools/CMake/libraries/libtheora.cmake index 3a95275dd..ca003a9ce 100644 --- a/Tools/CMake/libraries/libtheora.cmake +++ b/Tools/CMake/libraries/libtheora.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(libtheora) addPath( "${libDir}/libtheora" ) diff --git a/Tools/CMake/libraries/libvorbis.cmake b/Tools/CMake/libraries/libvorbis.cmake index 01d71a675..43913eb64 100644 --- a/Tools/CMake/libraries/libvorbis.cmake +++ b/Tools/CMake/libraries/libvorbis.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(libvorbis) addPathRec("${libDir}/libvorbis") diff --git a/Tools/CMake/libraries/ljpeg.cmake b/Tools/CMake/libraries/ljpeg.cmake index 74ea3d8fb..dbe65cb7f 100644 --- a/Tools/CMake/libraries/ljpeg.cmake +++ b/Tools/CMake/libraries/ljpeg.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(ljpeg) finishLibrary("${libDir}/ljpeg") diff --git a/Tools/CMake/libraries/lmng.cmake b/Tools/CMake/libraries/lmng.cmake index c526d50c9..3564e362d 100644 --- a/Tools/CMake/libraries/lmng.cmake +++ b/Tools/CMake/libraries/lmng.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(lmng) diff --git a/Tools/CMake/libraries/lpng.cmake b/Tools/CMake/libraries/lpng.cmake index a62c98bee..44271904a 100644 --- a/Tools/CMake/libraries/lpng.cmake +++ b/Tools/CMake/libraries/lpng.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(lpng) # addDef(PNG_NO_ASSEMBLER_CODE) diff --git a/Tools/CMake/libraries/lungif.cmake b/Tools/CMake/libraries/lungif.cmake index a3db81c93..910a9922f 100644 --- a/Tools/CMake/libraries/lungif.cmake +++ b/Tools/CMake/libraries/lungif.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(lungif) addDef(_GBA_NO_FILEIO) diff --git a/Tools/CMake/libraries/opcode.cmake b/Tools/CMake/libraries/opcode.cmake index 1b2a54e03..e41b61af3 100644 --- a/Tools/CMake/libraries/opcode.cmake +++ b/Tools/CMake/libraries/opcode.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(opcode) addPath("${libDir}/${PROJECT_NAME}") diff --git a/Tools/CMake/libraries/pcre.cmake b/Tools/CMake/libraries/pcre.cmake index c060fbdec..9cf0ce67c 100644 --- a/Tools/CMake/libraries/pcre.cmake +++ b/Tools/CMake/libraries/pcre.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(pcre) addDef(PCRE_STATIC) diff --git a/Tools/CMake/libraries/recast.cmake b/Tools/CMake/libraries/recast.cmake index cf0f4ff96..5422f73cd 100644 --- a/Tools/CMake/libraries/recast.cmake +++ b/Tools/CMake/libraries/recast.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + # Recast library project(recast) diff --git a/Tools/CMake/libraries/squish.cmake b/Tools/CMake/libraries/squish.cmake index 5b38bd8e7..9eff59405 100644 --- a/Tools/CMake/libraries/squish.cmake +++ b/Tools/CMake/libraries/squish.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(squish) finishLibrary("${libDir}/${PROJECT_NAME}") diff --git a/Tools/CMake/libraries/tinyxml.cmake b/Tools/CMake/libraries/tinyxml.cmake index 1b59d54a8..0c535bbbe 100644 --- a/Tools/CMake/libraries/tinyxml.cmake +++ b/Tools/CMake/libraries/tinyxml.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(tinyxml) finishLibrary("${libDir}/${PROJECT_NAME}") diff --git a/Tools/CMake/libraries/zlib.cmake b/Tools/CMake/libraries/zlib.cmake index 7c5397b09..72a5df4c5 100644 --- a/Tools/CMake/libraries/zlib.cmake +++ b/Tools/CMake/libraries/zlib.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(zlib) finishLibrary("${libDir}/${PROJECT_NAME}") diff --git a/Tools/CMake/modules/module_hydra.cmake b/Tools/CMake/modules/module_hydra.cmake index 2ef5c97bd..32e5d6ef6 100644 --- a/Tools/CMake/modules/module_hydra.cmake +++ b/Tools/CMake/modules/module_hydra.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + # module OculusVR # Source diff --git a/Tools/CMake/modules/module_navigation.cmake b/Tools/CMake/modules/module_navigation.cmake index d25d7adad..04064cd26 100644 --- a/Tools/CMake/modules/module_navigation.cmake +++ b/Tools/CMake/modules/module_navigation.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + # Navigation module addDef( "TORQUE_NAVIGATION_ENABLED" ) diff --git a/Tools/CMake/modules/module_oculusVR.cmake b/Tools/CMake/modules/module_oculusVR.cmake index 45f9b780f..17a37b7d6 100644 --- a/Tools/CMake/modules/module_oculusVR.cmake +++ b/Tools/CMake/modules/module_oculusVR.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + # module OculusVR # Source diff --git a/Tools/CMake/modules/module_testing.cmake b/Tools/CMake/modules/module_testing.cmake index 35c2f7a3a..f1b128403 100644 --- a/Tools/CMake/modules/module_testing.cmake +++ b/Tools/CMake/modules/module_testing.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + option(TORQUE_TESTS_ENABLED "TORQUE_TESTS_ENABLED" OFF) if(TORQUE_TESTS_ENABLED) diff --git a/Tools/CMake/template.cmake b/Tools/CMake/template.cmake index 52faefe0e..c48aaa676 100644 --- a/Tools/CMake/template.cmake +++ b/Tools/CMake/template.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + # this is a template file that should help you write a new cmake build script for a new library diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 61d9fea54..9b87752ea 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -1,3 +1,25 @@ +# ----------------------------------------------------------------------------- +# Copyright (c) 2014 GarageGames, LLC +# +# Permission is hereby granted, free of charge, to any person obtaining a copy +# of this software and associated documentation files (the "Software"), to +# deal in the Software without restriction, including without limitation the +# rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +# sell copies of the Software, and to permit persons to whom the Software is +# furnished to do so, subject to the following conditions: +# +# The above copyright notice and this permission notice shall be included in +# all copies or substantial portions of the Software. +# +# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +# FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +# IN THE SOFTWARE. +# ----------------------------------------------------------------------------- + project(${TORQUE_APP_NAME}) if(UNIX) From 0ca4dc75a8d5df4fa2aa33d564346fae3c515bf6 Mon Sep 17 00:00:00 2001 From: Anders Dahnielson Date: Mon, 28 Jul 2014 12:30:38 +0200 Subject: [PATCH 137/317] Improving CMake build system for power user * Making it possible to override the project directory location by setting TORQUE_APP_DIR. * Making it possible to skip template installation (copying) to project directory by setting TORQUE_TEMPLATE to OFF. --- Tools/CMake/basics.cmake | 15 ++++++++------- Tools/CMake/torque3d.cmake | 10 +++++----- 2 files changed, 13 insertions(+), 12 deletions(-) diff --git a/Tools/CMake/basics.cmake b/Tools/CMake/basics.cmake index 6f844afc4..41ae6b8ad 100644 --- a/Tools/CMake/basics.cmake +++ b/Tools/CMake/basics.cmake @@ -22,16 +22,17 @@ project("Torque3DEngine") -set(TORQUE_TEMPLATE "Full" CACHE STRING "the template to use") - -if(NOT projectDir) - set(projectDir "${CMAKE_SOURCE_DIR}/My Projects/${TORQUE_APP_NAME}") +if(NOT TORQUE_TEMPLATE) + set(TORQUE_TEMPLATE "Full" CACHE STRING "the template to use") +endif() +if(NOT TORQUE_APP_DIR) + set(TORQUE_APP_DIR "${CMAKE_SOURCE_DIR}/My Projects/${TORQUE_APP_NAME}") endif() if(NOT projectOutDir) - set(projectOutDir "${projectDir}/game") + set(projectOutDir "${TORQUE_APP_DIR}/game") endif() if(NOT projectSrcDir) - set(projectSrcDir "${projectDir}/source") + set(projectSrcDir "${TORQUE_APP_DIR}/source") endif() set(libDir "${CMAKE_SOURCE_DIR}/Engine/lib") set(srcDir "${CMAKE_SOURCE_DIR}/Engine/source") @@ -317,7 +318,7 @@ macro(setupPackaging) SET(CPACK_PACKAGE_VENDOR "${PROJECT_NAME}") SET(CPACK_PACKAGE_DESCRIPTION_SUMMARY "${PROJECT_NAME}") SET(CPACK_INCLUDE_TOPLEVEL_DIRECTORY 1) - SET(CPACK_OUTPUT_FILE_PREFIX "${projectDir}/packages/${PROJECT_NAME}") + SET(CPACK_OUTPUT_FILE_PREFIX "${TORQUE_APP_DIR}/packages/${PROJECT_NAME}") SET(CPACK_PACKAGE_INSTALL_DIRECTORY "") #SET(CPACK_PACKAGE_DESCRIPTION_FILE "${CMAKE_CURRENT_SOURCE_DIR}/ReadMe.txt") #SET(CPACK_RESOURCE_FILE_LICENSE "${CMAKE_CURRENT_SOURCE_DIR}/Copyright.txt") diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 9b87752ea..4c6869610 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -575,11 +575,11 @@ endif() if(TORQUE_TEMPLATE) message("Prepare Template(${TORQUE_TEMPLATE}) install...") - INSTALL(DIRECTORY "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/game" DESTINATION "${projectDir}") + INSTALL(DIRECTORY "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/game" DESTINATION "${TORQUE_APP_DIR}") if(WIN32) - INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/cleanShaders.bat" DESTINATION "${projectDir}") - INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/DeleteCachedDTSs.bat" DESTINATION "${projectDir}") - INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/DeleteDSOs.bat" DESTINATION "${projectDir}") - INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/DeletePrefs.bat" DESTINATION "${projectDir}") + INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/cleanShaders.bat" DESTINATION "${TORQUE_APP_DIR}") + INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/DeleteCachedDTSs.bat" DESTINATION "${TORQUE_APP_DIR}") + INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/DeleteDSOs.bat" DESTINATION "${TORQUE_APP_DIR}") + INSTALL(FILES "${CMAKE_SOURCE_DIR}/Templates/${TORQUE_TEMPLATE}/DeletePrefs.bat" DESTINATION "${TORQUE_APP_DIR}") endif() endif() From b34b1a70fa79cf806ffd3d1a70ecb575a9a0de9c Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Tue, 29 Jul 2014 09:16:11 +1000 Subject: [PATCH 138/317] Port file test. --- .../platform/test/platformFileIOTest.cpp | 118 ++++++++++++++ Engine/source/platform/test/testFile.cpp | 150 ------------------ 2 files changed, 118 insertions(+), 150 deletions(-) create mode 100644 Engine/source/platform/test/platformFileIOTest.cpp delete mode 100644 Engine/source/platform/test/testFile.cpp diff --git a/Engine/source/platform/test/platformFileIOTest.cpp b/Engine/source/platform/test/platformFileIOTest.cpp new file mode 100644 index 000000000..c27278431 --- /dev/null +++ b/Engine/source/platform/test/platformFileIOTest.cpp @@ -0,0 +1,118 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "platform/platform.h" +#include "core/fileio.h" +#include "core/util/tVector.h" +#include "console/console.h" + +TEST(Platform, ExcludedDirectories) +{ + // Just dump everything under the current directory. We should + // find at least one file. + + // Exclude .svn and CVS + Platform::clearExcludedDirectories(); + Platform::addExcludedDirectory(".svn"); + Platform::addExcludedDirectory("CVS"); + + EXPECT_TRUE(Platform::isExcludedDirectory(".svn")) + << "On list, should be excluded."; + EXPECT_TRUE(Platform::isExcludedDirectory("CVS")) + << "On list, should be excluded."; + EXPECT_FALSE(Platform::isExcludedDirectory("foo")) + << "Doesn't match list, shouldn't be excluded."; + EXPECT_FALSE(Platform::isExcludedDirectory(".svnCVS")) + << "Looks like a duck, but it shouldn't be excluded cuz it's distinct from all entries on the exclusion list."; + + // Ok, now our exclusion list is setup, so let's dump some paths. + Vector pathInfo; + Platform::dumpPath(Platform::getCurrentDirectory(), pathInfo, 2); + EXPECT_GT(pathInfo.size(), 0) + << "Should find at least SOMETHING in the current directory!"; + + // This'll nuke info if we run it in a live situation... so don't run unit + // tests in a live situation. ;) + Platform::clearExcludedDirectories(); +}; + +TEST(File, TouchAndTime) +{ + FileTime create[2], modify[2]; + + // Create a file and sleep for a second. + File f; + f.open("testTouch.file", File::WriteAppend); + f.close(); + + // Touch a file and note its last-modified. + dFileTouch("testTouch.file"); + EXPECT_TRUE(Platform::isFile("testTouch.file")) + << "We just touched this file - it should exist."; + EXPECT_TRUE(Platform::getFileTimes("testTouch.file", &create[0], &modify[0])) + << "Failed to get filetimes for a file we just created."; + + // Sleep for a tick + Platform::sleep(10); + + // Touch it again, and compare the last-modifieds. + EXPECT_TRUE(Platform::isFile("testTouch.file")) + << "We just touched this file - it should exist."; + dFileTouch("testTouch.file"); + EXPECT_TRUE(Platform::isFile("testTouch.file")) + << "We just touched this file - it should exist."; + EXPECT_TRUE(Platform::getFileTimes("testTouch.file", &create[1], &modify[1])) + << "Failed to get filetimes for a file we just created."; + + // Now compare the times... + EXPECT_LT(Platform::compareFileTimes(modify[0], modify[1]), 0) + << "Timestamps are wrong - modify[0] should be before modify[1]!"; + EXPECT_EQ(Platform::compareFileTimes(create[0], create[1]), 0) + << "Create timestamps should match - we didn't delete the file during this test."; + + // Clean up.. + dFileDelete("testTouch.file"); + EXPECT_FALSE(Platform::isFile("testTouch.file")) + << "Somehow failed to delete our test file."; +}; + +// Mac has no implementations for these functions, so we 'def it out for now. +#ifndef __MACOSX__ +TEST(Platform, Volumes) +{ + Vector names; + Platform::getVolumeNamesList(names); + + EXPECT_GT(names.size(), 0) + << "We should have at least one volume..."; + + Vector info; + Platform::getVolumeInformationList(info); + + EXPECT_EQ(names.size(), info.size()) + << "Got inconsistent number of volumes back from info vs. name list functions!"; +}; +#endif + +#endif diff --git a/Engine/source/platform/test/testFile.cpp b/Engine/source/platform/test/testFile.cpp deleted file mode 100644 index 667f9fbee..000000000 --- a/Engine/source/platform/test/testFile.cpp +++ /dev/null @@ -1,150 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "platform/platform.h" -#include "core/fileio.h" -#include "unit/test.h" -#include "core/util/tVector.h" -#include "console/console.h" - -using namespace UnitTesting; - -CreateUnitTest(CheckFileListingAndExclusion, "File/ListDirectoryAndExclusions") -{ - void run() - { - // Just dump everything under the current directory. We should - // find at least one file. - - // Exclude .svn and CVS - Platform::clearExcludedDirectories(); - Platform::addExcludedDirectory(".svn"); - Platform::addExcludedDirectory("CVS"); - - test(Platform::isExcludedDirectory("foo") == false, "Doesn't match list, shouldn't be excluded."); - test(Platform::isExcludedDirectory(".svn") == true, "On list, should be excluded."); - test(Platform::isExcludedDirectory("CVS") == true, "On list, should be excluded."); - test(Platform::isExcludedDirectory(".svnCVS") == false, "Looks like a duck, but it shouldn't be excluded cuz it's distinct from all entries on the exclusion list."); - - // Ok, now our exclusion list is setup, so let's dump some paths. - Vector < Platform::FileInfo > pathInfo; - Platform::dumpPath (Platform::getCurrentDirectory(), pathInfo, 2); - - Con::printf("Dump of files in '%s', up to 2 levels deep...", Platform::getCurrentDirectory()); - for(S32 i=0; i 0, "Should find at least SOMETHING in the current directory!"); - - // This'll nuke info if we run it in a live situation... so don't run unit - // tests in a live situation. ;) - Platform::clearExcludedDirectories(); - } -}; - -CreateUnitTest(CheckFileTouchAndTime, "File/TouchAndTime") -{ - void run() - { - FileTime create[2], modify[2]; - - // Create a file and sleep for a second. - File f; - f.open("testTouch.file", File::WriteAppend); - f.close(); - - Platform::sleep(2000); - - // Touch a file and note its last-modified. - dFileTouch("testTouch.file"); - test(Platform::isFile("testTouch.file"), "We just touched this file - it should exist."); - test(Platform::getFileTimes("testTouch.file", &create[0], &modify[0]), "Failed to get filetimes for a file we just created."); - - // Sleep for a few seconds... - Platform::sleep(5000); - - // Touch it again, and compare the last-modifieds. - test(Platform::isFile("testTouch.file"), "We just touched this file - it should exist."); - dFileTouch("testTouch.file"); - test(Platform::isFile("testTouch.file"), "We just touched this file - it should exist."); - test(Platform::getFileTimes("testTouch.file", &create[1], &modify[1]), "Failed to get filetimes for a file we just created."); - - // Now compare the times... - test(Platform::compareFileTimes(modify[0], modify[1]) < 0, "Timestamps are wrong - modify[0] should be before modify[1]!"); - - // This seems to fail even on a valid case... - // test(Platform::compareFileTimes(create[0], create[1]) == 0, "Create timestamps should match - we didn't delete the file during this test."); - - // Clean up.. - dFileDelete("testTouch.file"); - test(!Platform::isFile("testTouch.file"), "Somehow failed to delete our test file."); - } -}; - -// Mac has no implementations for these functions, so we 'def it out for now. -#if 0 -CreateUnitTest(CheckVolumes, "File/Volumes") -{ - void run() - { - Con::printf("Dumping volumes by name:"); - - Vector names; - Platform::getVolumeNamesList(names); - - test(names.size() > 0, "We should have at least one volume..."); - - for(S32 i=0; i info; - Platform::getVolumeInformationList(info); - - test(names.size() == info.size(), "Got inconsistent number of volumes back from info vs. name list functions!"); - - for(S32 i=0; i Date: Thu, 31 Jul 2014 00:22:45 +0200 Subject: [PATCH 139/317] Added PCNTT vertex type. This is a prerequisite change for the Ribbon implementation. --- Engine/source/gfx/gfxVertexTypes.cpp | 9 +++++++++ Engine/source/gfx/gfxVertexTypes.h | 8 ++++++++ 2 files changed, 17 insertions(+) diff --git a/Engine/source/gfx/gfxVertexTypes.cpp b/Engine/source/gfx/gfxVertexTypes.cpp index ca4598270..dbee17550 100644 --- a/Engine/source/gfx/gfxVertexTypes.cpp +++ b/Engine/source/gfx/gfxVertexTypes.cpp @@ -99,6 +99,15 @@ GFXImplementVertexFormat( GFXVertexPNTT ) addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); } +GFXImplementVertexFormat( GFXVertexPCNTT ) +{ + addElement( "POSITION", GFXDeclType_Float3 ); + addElement( "COLOR", GFXDeclType_Color ); + addElement( "NORMAL", GFXDeclType_Float3 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 0 ); + addElement( "TEXCOORD", GFXDeclType_Float2, 1 ); +} + GFXImplementVertexFormat( GFXVertexPNTBT ) { addElement( "POSITION", GFXDeclType_Float3 ); diff --git a/Engine/source/gfx/gfxVertexTypes.h b/Engine/source/gfx/gfxVertexTypes.h index 9072f533c..8af285f5d 100644 --- a/Engine/source/gfx/gfxVertexTypes.h +++ b/Engine/source/gfx/gfxVertexTypes.h @@ -112,6 +112,14 @@ GFXDeclareVertexFormat( GFXVertexPNTT ) Point2F texCoord; }; +GFXDeclareVertexFormat( GFXVertexPCNTT ) +{ + Point3F point; + GFXVertexColor color; + Point3F normal; + Point2F texCoord[2]; +}; + GFXDeclareVertexFormat( GFXVertexPNTBT ) { Point3F point; From 867997f398892a67373bc76dfcfb99b26ccfd7b5 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Fri, 1 Aug 2014 15:00:57 +1000 Subject: [PATCH 140/317] Allow stress tests to be quarantined. --- Engine/source/testing/unitTesting.cpp | 18 +++++++++++++++--- Engine/source/testing/unitTesting.h | 18 ++++++++++++++---- 2 files changed, 29 insertions(+), 7 deletions(-) diff --git a/Engine/source/testing/unitTesting.cpp b/Engine/source/testing/unitTesting.cpp index 7e1cd63ab..d9304e5b3 100644 --- a/Engine/source/testing/unitTesting.cpp +++ b/Engine/source/testing/unitTesting.cpp @@ -69,12 +69,24 @@ class TorqueUnitTestListener : public ::testing::EmptyTestEventListener } }; -DefineConsoleFunction( runAllUnitTests, int, (),, - "" ) +DefineConsoleFunction( runAllUnitTests, int, (bool includeStressTests), (false), + "Runs all engine unit tests. Some tests are marked as 'stress' tests which do " + "not necessarily check correctness, just performance or possible nondeterministic " + "glitches. These tests can take some time, so they are not included unless " + "specified.\n\n" + "@param includeStressTests Run stress tests as well as unit tests. Default is false." ) { - // Set-up some empty arguments. S32 testArgc = 0; char** testArgv = NULL; + if ( includeStressTests ) + { + // Yes, I never free this memory, because it seems to be mangled by gtest. + // Also it's a negligible space leak that will only occur once. + testArgv = new char*[2]; + testArgv[0] = new char( '\0' ); + testArgv[1] = new char[26]; + dStrcpy( testArgv[1], "--gtest_filter=-*Stress.*" ); + } // Initialize Google Test. testing::InitGoogleTest( &testArgc, testArgv ); diff --git a/Engine/source/testing/unitTesting.h b/Engine/source/testing/unitTesting.h index 7002de344..5db45d775 100644 --- a/Engine/source/testing/unitTesting.h +++ b/Engine/source/testing/unitTesting.h @@ -27,16 +27,26 @@ #include +/// Convenience to define a test fixture with a Fixture suffix for use with +/// TEST_FIX. +#define FIXTURE(test_fixture)\ + class test_fixture##Fixture : public ::testing::Test + /// Allow test fixtures named with a Fixture suffix, so that we can name tests /// after a class name rather than having to call them XXTest. #define TEST_FIX(test_fixture, test_name)\ GTEST_TEST_(test_fixture, test_name, test_fixture##Fixture, \ ::testing::internal::GetTypeId()) -/// Convenience to define a test fixture with a Fixture suffix for use with -/// TEST_FIX. -#define FIXTURE(test_fixture)\ - class test_fixture##Fixture : public ::testing::Test +/// Define a stress test. The test name is suffixed with Stress, so it will be +/// excluded from normal unit test runs. +#define TEST_STRESS(test_case_name, test_name)\ + TEST(test_case_name##Stress, test_name) + +/// Define a stress test with a fixture. +#define TEST_STRESS_FIX(test_fixture, test_name)\ + GTEST_TEST_(test_fixture##Stress, test_name, test_fixture##Fixture, \ + ::testing::internal::GetTypeId()) #endif // TORQUE_TESTS_ENABLED From 00ec14561b353931bb4dbfc327d9c47a61b926b7 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Fri, 1 Aug 2014 15:02:24 +1000 Subject: [PATCH 141/317] Add component test directory to project generator. --- Tools/projectGenerator/modules/core.inc | 1 + 1 file changed, 1 insertion(+) diff --git a/Tools/projectGenerator/modules/core.inc b/Tools/projectGenerator/modules/core.inc index 10e870025..ede86f187 100644 --- a/Tools/projectGenerator/modules/core.inc +++ b/Tools/projectGenerator/modules/core.inc @@ -29,6 +29,7 @@ addEngineSrcDir('sfx'); // Components addEngineSrcDir('component'); addEngineSrcDir('component/interfaces'); +addEngineSrcDir('component/test'); // Core if (T3D_Generator::isApp()) From 85a0c1c59f06b4102109bbb6622ae69b0157df9a Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sat, 2 Aug 2014 10:14:08 +1000 Subject: [PATCH 142/317] Allow test flags to be specified, so we don't need TEST_STRESS. --- Engine/source/testing/unitTesting.cpp | 36 ++++++++++++++++----------- Engine/source/testing/unitTesting.h | 10 -------- Templates/Empty/game/runTests.cs | 2 +- Templates/Full/game/runTests.cs | 2 +- 4 files changed, 23 insertions(+), 27 deletions(-) diff --git a/Engine/source/testing/unitTesting.cpp b/Engine/source/testing/unitTesting.cpp index d9304e5b3..bf4a2bc0a 100644 --- a/Engine/source/testing/unitTesting.cpp +++ b/Engine/source/testing/unitTesting.cpp @@ -69,23 +69,30 @@ class TorqueUnitTestListener : public ::testing::EmptyTestEventListener } }; -DefineConsoleFunction( runAllUnitTests, int, (bool includeStressTests), (false), - "Runs all engine unit tests. Some tests are marked as 'stress' tests which do " - "not necessarily check correctness, just performance or possible nondeterministic " - "glitches. These tests can take some time, so they are not included unless " - "specified.\n\n" - "@param includeStressTests Run stress tests as well as unit tests. Default is false." ) +DefineConsoleFunction( runAllUnitTests, int, (const char* testSpecs), (""), + "Runs engine unit tests. Some tests are marked as 'stress' tests which do not " + "necessarily check correctness, just performance or possible nondeterministic " + "glitches. There may also be interactive or networking tests which may be " + "excluded by using the testSpecs argument.\n" + "This function should only be called once per executable run, because of " + "googletest's design.\n\n" + + "@param testSpecs A space-sepatated list of filters for test cases. " + "See https://code.google.com/p/googletest/wiki/AdvancedGuide#Running_a_Subset_of_the_Tests " + "for a description of the flag format.") { S32 testArgc = 0; char** testArgv = NULL; - if ( includeStressTests ) + if ( dStrlen( testSpecs ) > 0 ) { - // Yes, I never free this memory, because it seems to be mangled by gtest. - // Also it's a negligible space leak that will only occur once. + String specs(testSpecs); + specs.replace(' ', ':'); + specs.insert(0, "--gtest_filter="); + testArgc = 2; testArgv = new char*[2]; - testArgv[0] = new char( '\0' ); - testArgv[1] = new char[26]; - dStrcpy( testArgv[1], "--gtest_filter=-*Stress.*" ); + testArgv[0] = NULL; // Program name is unused by googletest. + testArgv[1] = new char[specs.length()+1]; + dStrcpy(testArgv[1], specs); } // Initialize Google Test. @@ -108,11 +115,10 @@ DefineConsoleFunction( runAllUnitTests, int, (bool includeStressTests), (false), // Add the Torque unit test listener. listeners.Append( new TorqueUnitTestListener ); + // Perform googletest run. Con::printf( "\nUnit Tests Starting...\n" ); - const S32 result = RUN_ALL_TESTS(); - - Con::printf( "\n... Unit Tests Ended.\n" ); + Con::printf( "... Unit Tests Ended.\n" ); return result; } diff --git a/Engine/source/testing/unitTesting.h b/Engine/source/testing/unitTesting.h index 5db45d775..158c3e034 100644 --- a/Engine/source/testing/unitTesting.h +++ b/Engine/source/testing/unitTesting.h @@ -38,16 +38,6 @@ GTEST_TEST_(test_fixture, test_name, test_fixture##Fixture, \ ::testing::internal::GetTypeId()) -/// Define a stress test. The test name is suffixed with Stress, so it will be -/// excluded from normal unit test runs. -#define TEST_STRESS(test_case_name, test_name)\ - TEST(test_case_name##Stress, test_name) - -/// Define a stress test with a fixture. -#define TEST_STRESS_FIX(test_fixture, test_name)\ - GTEST_TEST_(test_fixture##Stress, test_name, test_fixture##Fixture, \ - ::testing::internal::GetTypeId()) - #endif // TORQUE_TESTS_ENABLED #endif // _UNIT_TESTING_H_ diff --git a/Templates/Empty/game/runTests.cs b/Templates/Empty/game/runTests.cs index e44d2fbd4..b6d903ff0 100644 --- a/Templates/Empty/game/runTests.cs +++ b/Templates/Empty/game/runTests.cs @@ -1,5 +1,5 @@ setLogMode(2); $Con::LogBufferEnabled = false; $Testing::CheckMemoryLeaks = false; -runAllUnitTests(); +runAllUnitTests("-*.Stress*"); quit(); diff --git a/Templates/Full/game/runTests.cs b/Templates/Full/game/runTests.cs index e44d2fbd4..b6d903ff0 100644 --- a/Templates/Full/game/runTests.cs +++ b/Templates/Full/game/runTests.cs @@ -1,5 +1,5 @@ setLogMode(2); $Con::LogBufferEnabled = false; $Testing::CheckMemoryLeaks = false; -runAllUnitTests(); +runAllUnitTests("-*.Stress*"); quit(); From 2f95583df8e55d80ad76c13e6b10996bf418f467 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sat, 2 Aug 2014 16:42:07 +1000 Subject: [PATCH 143/317] Ported thread tests without the stress tests. --- Engine/source/platform/test/testThreading.cpp | 417 ------------------ .../platform/threads/test/mutexTest.cpp | 70 +++ .../platform/threads/test/semaphoreTest.cpp | 90 ++++ .../platform/threads/test/threadTest.cpp | 56 +++ Tools/projectGenerator/modules/core.inc | 1 + 5 files changed, 217 insertions(+), 417 deletions(-) delete mode 100644 Engine/source/platform/test/testThreading.cpp create mode 100644 Engine/source/platform/threads/test/mutexTest.cpp create mode 100644 Engine/source/platform/threads/test/semaphoreTest.cpp create mode 100644 Engine/source/platform/threads/test/threadTest.cpp diff --git a/Engine/source/platform/test/testThreading.cpp b/Engine/source/platform/test/testThreading.cpp deleted file mode 100644 index 6eea80ee3..000000000 --- a/Engine/source/platform/test/testThreading.cpp +++ /dev/null @@ -1,417 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "platform/platform.h" -#include "platform/threads/thread.h" -#include "platform/threads/semaphore.h" -#include "platform/threads/mutex.h" -#include "unit/test.h" -#include "core/util/tVector.h" -#include "console/console.h" - -using namespace UnitTesting; - -class ThreadTestHarness -{ - U32 mStartTime, mEndTime, mCleanupTime; - void (*mThreadBody)(void*); - S32 mThreadCount; - Thread **mThreads; - -public: - ThreadTestHarness() - { - mStartTime = mEndTime = mCleanupTime = 0; - mThreadBody = NULL; - mThreadCount = 1; - mThreads = NULL; - } - - void startThreads(void (*threadBody)(void*), void *arg, U32 threadCount) - { - mThreadCount = threadCount; - mThreadBody = threadBody; - - // Start up threadCount threads... - mThreads = new Thread*[threadCount]; - - mStartTime = Platform::getRealMilliseconds(); - - //Con::printf(" Running with %d threads...", threadCount); - for(S32 i=0; istart(); - } - } - - void waitForThreadExit(U32 checkFrequencyMs) - { - // And wait for them to complete. - bool someAlive = true; - S32 liveCount = mThreadCount; - - while(someAlive) - { - //Con::printf(" - Sleeping for %dms with %d live threads.", checkFrequencyMs, liveCount); - Platform::sleep(checkFrequencyMs); - - someAlive = false; - liveCount = 0; - - for(S32 i=0; iisAlive()) - continue; - - someAlive = true; - liveCount++; - } - - } - - mEndTime = Platform::getRealMilliseconds(); - - // Clean up memory at this point. - for(S32 i=0; iacquire(false), "Should succeed at acquiring a new semaphore with count 1."); - test(sem2->acquire(false), "This one should succeed too, see previous test."); - - // Test that we can do non-blocking acquires that fail. - test(sem1->acquire(false)==false, "Should failed, as we've already got the sem."); - sem1->release(); - test(sem2->acquire(false)==false, "Should also fail."); - sem2->release(); - - // Test that we can do blocking acquires that succeed. - test(sem1->acquire(true)==true, "Should succeed as we just released."); - test(sem2->acquire(true)==true, "Should succeed as we just released."); - - // Can't test blocking acquires that never happen... :) - - // Clean up. - delete sem1; - delete sem2; - } -}; - -CreateUnitTest( SemaphoreWaitTest, "Platform/Threads/SemaphoreWaitTest") -{ - static void threadBody(void *self) - { - SemaphoreWaitTest *me = (SemaphoreWaitTest*)self; - - // Wait for the semaphore to get released. - me->mSemaphore->acquire(); - - // Increment the counter. - Mutex::lockMutex(me->mMutex); - me->mDoneCount++; - Mutex::unlockMutex(me->mMutex); - - // Signal back to the main thread we're done. - me->mPostbackSemaphore->release(); - } - - Semaphore *mSemaphore; - Semaphore *mPostbackSemaphore; - void *mMutex; - U32 mDoneCount; - - const static S32 csmThreadCount = 10; - - void run() - { - ThreadTestHarness tth; - - mDoneCount = 0; - mSemaphore = new Semaphore(0); - mPostbackSemaphore = new Semaphore(0); - mMutex = Mutex::createMutex(); - - tth.startThreads(&threadBody, this, csmThreadCount); - - Platform::sleep(500); - - Mutex::lockMutex(mMutex); - test(mDoneCount == 0, "no threads should have touched the counter yet."); - Mutex::unlockMutex(mMutex); - - // Let 500 come out. - for(S32 i=0; irelease(); - - // And wait for 500 postbacks. - for(S32 i=0; iacquire(); - - Mutex::lockMutex(mMutex); - test(mDoneCount == csmThreadCount / 2, "Didn't get expected number of done threads! (a)"); - Mutex::unlockMutex(mMutex); - - // Ok, now do the rest. - // Let 500 come out. - for(S32 i=0; irelease(); - - // And wait for 500 postbacks. - for(S32 i=0; iacquire(); - - Mutex::lockMutex(mMutex); - test(mDoneCount == csmThreadCount, "Didn't get expected number of done threads! (b)"); - Mutex::unlockMutex(mMutex); - - // Wait for the threads to exit - shouldn't have to wait ever though. - tth.waitForThreadExit(10); - - // Make sure no one touched our data after shutdown time. - Mutex::lockMutex(mMutex); - test(mDoneCount == csmThreadCount, "Didn't get expected number of done threads! (c)"); - Mutex::unlockMutex(mMutex); - } -}; - -CreateUnitTest( MutexWaitTest, "Platform/Threads/MutexWaitTest") -{ - static void threadBody(void *self) - { - MutexWaitTest *me = (MutexWaitTest*)self; - - // Increment the counter. We'll block until the mutex - // is open. - Mutex::lockMutex(me->mMutex); - me->mDoneCount++; - Mutex::unlockMutex(me->mMutex); - } - - void *mMutex; - U32 mDoneCount; - - const static S32 csmThreadCount = 10; - - void run() - { - mMutex = Mutex::createMutex(); - mDoneCount = 0; - - // We lock the mutex before we create any threads, so that all the threads - // block on the mutex. Then we unlock it and let them all work their way - // through the increment. - Mutex::lockMutex(mMutex); - - ThreadTestHarness tth; - tth.startThreads(&threadBody, this, csmThreadCount); - - Platform::sleep(5000); - - // Check count is still zero. - test(mDoneCount == 0, "Uh oh - a thread somehow didn't get blocked by the locked mutex!"); - - // Open the flood gates... - Mutex::unlockMutex(mMutex); - - // Wait for the threads to all finish executing. - tth.waitForThreadExit(10); - - Mutex::lockMutex(mMutex); - test(mDoneCount == csmThreadCount, "Hmm - all threads reported done, but we didn't get the expected count."); - Mutex::unlockMutex(mMutex); - - // Kill the mutex. - Mutex::destroyMutex(mMutex); - } -}; \ No newline at end of file diff --git a/Engine/source/platform/threads/test/mutexTest.cpp b/Engine/source/platform/threads/test/mutexTest.cpp new file mode 100644 index 000000000..53379651b --- /dev/null +++ b/Engine/source/platform/threads/test/mutexTest.cpp @@ -0,0 +1,70 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "platform/threads/mutex.h" +#include "platform/threads/thread.h" + +TEST(Mutex, BasicSynchronization) +{ + // We test various scenarios wrt to locking and unlocking, in a single + // thread, just to make sure our basic primitives are working in the + // most basic case. + void *mutex1 = Mutex::createMutex(); + EXPECT_TRUE(mutex1 != NULL) + << "First Mutex::createMutex call failed - that's pretty bad!"; + + // This mutex is intentionally unused. + void *mutex2 = Mutex::createMutex(); + EXPECT_TRUE(mutex2 != NULL) + << "Second Mutex::createMutex call failed - that's pretty bad, too!"; + + EXPECT_TRUE(Mutex::lockMutex(mutex1, false)) + << "Nonblocking call to brand new mutex failed - should not be."; + EXPECT_TRUE(Mutex::lockMutex(mutex1, true)) + << "Failed relocking a mutex from the same thread - should be able to do this."; + + // Try to acquire the mutex from another thread. + struct thread + { + static void body(void* mutex) + { + // We should not be able to lock the mutex from a separate thread, but + // we don't want to block either. + EXPECT_FALSE(Mutex::lockMutex(mutex, false)); + } + }; + Thread thread(&thread::body, mutex1); + thread.start(); + thread.join(); + + // Unlock & kill mutex 1 + Mutex::unlockMutex(mutex1); + Mutex::unlockMutex(mutex1); + Mutex::destroyMutex(mutex1); + + // Kill mutex2, which was never touched. + Mutex::destroyMutex(mutex2); +} + +#endif \ No newline at end of file diff --git a/Engine/source/platform/threads/test/semaphoreTest.cpp b/Engine/source/platform/threads/test/semaphoreTest.cpp new file mode 100644 index 000000000..d74971074 --- /dev/null +++ b/Engine/source/platform/threads/test/semaphoreTest.cpp @@ -0,0 +1,90 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "platform/threads/semaphore.h" +#include "platform/threads/thread.h" + +TEST(Semaphore, BasicSynchronization) +{ + Semaphore *sem1 = new Semaphore(1); + Semaphore *sem2 = new Semaphore(1); + + // Test that we can do non-blocking acquires that succeed. + EXPECT_TRUE(sem1->acquire(false)) + << "Should succeed at acquiring a new semaphore with count 1."; + EXPECT_TRUE(sem2->acquire(false)) + << "This one should succeed too, see previous test."; + + // Test that we can do non-blocking acquires that fail. + EXPECT_FALSE(sem1->acquire(false)) + << "Should failed, as we've already got the sem."; + sem1->release(); + EXPECT_FALSE(sem2->acquire(false)) + << "Should also fail."; + sem2->release(); + + // Test that we can do blocking acquires that succeed. + EXPECT_TRUE(sem1->acquire(true)) + << "Should succeed as we just released."; + EXPECT_TRUE(sem2->acquire(true)) + << "Should succeed as we just released."; + + // Clean up. + delete sem1; + delete sem2; +} + +TEST(Semaphore, MultiThreadSynchronization) +{ + Semaphore semaphore(1); + + struct thread + { + // Try to acquire the semaphore from another thread. + static void body1(void* sem) + { + Semaphore *semaphore = reinterpret_cast(sem); + EXPECT_TRUE(semaphore->acquire(true)); + // Note that this semaphore is never released. Bad programmer! + } + // One more acquisition should fail! + static void body2(void* sem) + { + Semaphore *semaphore = reinterpret_cast(sem); + EXPECT_FALSE(semaphore->acquire(false)); + } + }; + + Thread thread1(&thread::body1, &semaphore); + EXPECT_TRUE(semaphore.acquire(true)); + thread1.start(); + semaphore.release(); + thread1.join(); + + Thread thread2(&thread::body2, &semaphore); + thread2.start(); + thread2.join(); +} + +#endif \ No newline at end of file diff --git a/Engine/source/platform/threads/test/threadTest.cpp b/Engine/source/platform/threads/test/threadTest.cpp new file mode 100644 index 000000000..9ff5af3aa --- /dev/null +++ b/Engine/source/platform/threads/test/threadTest.cpp @@ -0,0 +1,56 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "platform/threads/thread.h" + +TEST(Thread, BasicAPI) +{ +#define VALUE_TO_SET 10 + + // This struct exists just so we can define run as a local function. + struct thread + { + // Do some work we can observe. + static void body(void* arg) + { + U32* value = reinterpret_cast(arg); + *value = VALUE_TO_SET; + } + }; + + // Test most basic Thread API functions. + U32 value = ~VALUE_TO_SET; + Thread thread(&thread::body, reinterpret_cast(&value)); + thread.start(); + EXPECT_TRUE(thread.isAlive()); + thread.join(); + EXPECT_FALSE(thread.isAlive()); + + EXPECT_EQ(value, VALUE_TO_SET) + << "Thread did not set expected value!"; + +#undef VALUE_TO_SET +}; + +#endif \ No newline at end of file diff --git a/Tools/projectGenerator/modules/core.inc b/Tools/projectGenerator/modules/core.inc index ede86f187..0e981dca5 100644 --- a/Tools/projectGenerator/modules/core.inc +++ b/Tools/projectGenerator/modules/core.inc @@ -72,6 +72,7 @@ switch( T3D_Generator::$platform ) } addEngineSrcDir('platform/threads'); +addEngineSrcDir('platform/threads/test'); addEngineSrcDir('platform/async'); addEngineSrcDir('platform/input'); addEngineSrcDir('platform/output'); From ada24b3d8ca32ae9bc26bed4a1ee1c608a592be2 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 3 Aug 2014 12:56:11 +0200 Subject: [PATCH 144/317] Fix WaterObject loose reflection bug. --- Engine/source/environment/waterObject.cpp | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/Engine/source/environment/waterObject.cpp b/Engine/source/environment/waterObject.cpp index 51e737717..dbf6cec69 100644 --- a/Engine/source/environment/waterObject.cpp +++ b/Engine/source/environment/waterObject.cpp @@ -732,6 +732,11 @@ void WaterObject::renderObject( ObjectRenderInst *ri, SceneRenderState *state, B bool doQuery = ( !mPlaneReflector.mQueryPending && query && mReflectorDesc.useOcclusionQuery ); + // We need to call this for avoid a DX9 or Nvidia bug. + // At some resollutions read from render target, + // break current occlusion query. + REFLECTMGR->getRefractTex(); + if ( doQuery ) query->begin(); From 5c6d22a05988272b8130313ff002bbd9a635f9f1 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 3 Aug 2014 21:20:18 +1000 Subject: [PATCH 145/317] Ported process and journal tests. --- .../core/util/journal/test/journalTest.cpp | 180 +++++++++++++++++ .../test/{testProcess.cpp => processTest.cpp} | 56 +++--- .../core/util/journal/test/testJournal.cpp | 185 ------------------ 3 files changed, 209 insertions(+), 212 deletions(-) create mode 100644 Engine/source/core/util/journal/test/journalTest.cpp rename Engine/source/core/util/journal/test/{testProcess.cpp => processTest.cpp} (62%) delete mode 100644 Engine/source/core/util/journal/test/testJournal.cpp diff --git a/Engine/source/core/util/journal/test/journalTest.cpp b/Engine/source/core/util/journal/test/journalTest.cpp new file mode 100644 index 000000000..01212f53d --- /dev/null +++ b/Engine/source/core/util/journal/test/journalTest.cpp @@ -0,0 +1,180 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "core/util/journal/journaledSignal.h" +#include "core/util/safeDelete.h" + +TEST(Journal, BasicAPI) +{ + struct receiver + { + U32 lastTriggerValue; + void triggerReceiver(U16 msg) + { + lastTriggerValue = msg; + } + } rec; + + rec.lastTriggerValue = 0; + + // Set up a journaled signal to test with. + JournaledSignal testEvent; + testEvent.notify(&rec, &receiver::triggerReceiver); + + // Initialize journal recording and fire off some events... + Journal::Record("test.jrn"); + ASSERT_TRUE(Journal::IsRecording()); + + testEvent.trigger(16); + testEvent.trigger(17); + testEvent.trigger(18); + + EXPECT_EQ(rec.lastTriggerValue, 18) + << "Should encounter last triggered value (18)."; + + Journal::Stop(); + ASSERT_FALSE(Journal::IsRecording()); + + // Clear it... + rec.lastTriggerValue = 0; + + // and play back - should get same thing. + Journal::Play("test.jrn"); + + // Since we fired 3 events, it should take three loops. + EXPECT_TRUE(Journal::PlayNext()) << "Should be two more events."; + EXPECT_TRUE(Journal::PlayNext()) << "Should be one more event."; + EXPECT_FALSE(Journal::PlayNext()) << "Should be no more events."; + + EXPECT_EQ(rec.lastTriggerValue, 18) + << "Should encounter last journaled value (18)."; +} + +TEST(Journal, DynamicSignals) +{ + typedef JournaledSignal EventA; + typedef JournaledSignal EventB; + typedef JournaledSignal EventC; + + // Root, non-dynamic signal receiver. + struct receiver { + U32 recvA, recvB, recvC; + + EventA *dynamicA; + EventB *dynamicB; + EventC *dynamicC; + + void receiverRoot(U8 msg) + { + if(msg==1) + { + dynamicA = new EventA(); + dynamicA->notify(this, &receiver::receiverA); + } + + if(msg==2) + { + dynamicB = new EventB(); + dynamicB->notify(this, &receiver::receiverB); + } + + if(msg==3) + { + dynamicC = new EventC(); + dynamicC->notify(this, &receiver::receiverC); + } + } + + void receiverA(U32, U16 d) + { + recvA += d; + } + + void receiverB(U8, S8 d) + { + recvB += d; + } + + void receiverC(U32, S32 d) + { + recvC += d; + } + } rec; + + // Reset our state values. + rec.recvA = rec.recvB = rec.recvC = 0; + + // Set up a signal to start with. + JournaledSignal testEvent; + testEvent.notify(&rec, &receiver::receiverRoot); + + // Initialize journal recording and fire off some events... + Journal::Record("test.jrn"); + ASSERT_TRUE(Journal::IsRecording()); + + testEvent.trigger(1); + rec.dynamicA->trigger(8, 100); + testEvent.trigger(2); + rec.dynamicA->trigger(8, 8); + rec.dynamicB->trigger(9, 'a'); + testEvent.trigger(3); + SAFE_DELETE(rec.dynamicB); // Test a deletion. + rec.dynamicC->trigger(8, 1); + rec.dynamicC->trigger(8, 1); + + // Did we end up with expected values? Check before clearing. + EXPECT_EQ(rec.recvA, 108) << "recvA wasn't 108 - something broken in signals?"; + EXPECT_EQ(rec.recvB, 'a') << "recvB wasn't 'a' - something broken in signals?"; + EXPECT_EQ(rec.recvC, 2) << "recvC wasn't 2 - something broken in signals?"; + + // Reset our state values. + rec.recvA = rec.recvB = rec.recvC = 0; + + // And kill the journal... + Journal::Stop(); + + // Also kill our remaining dynamic signals. + SAFE_DELETE(rec.dynamicA); + SAFE_DELETE(rec.dynamicB); + SAFE_DELETE(rec.dynamicC); + + // Play back - should get same thing. + Journal::Play("test.jrn"); + + // Since we fired 8 events, it should take 7+1=8 loops. + for(S32 i = 0; i < 7; i++) + { + EXPECT_TRUE(Journal::PlayNext()) + << "Should be more events."; + } + + EXPECT_FALSE(Journal::PlayNext()) + << "Should be no more events."; + + EXPECT_EQ(rec.recvA, 108) << "recvA wasn't 108 - something broken in journal?"; + EXPECT_EQ(rec.recvB, 'a') << "recvB wasn't 'a' - something broken in journal?"; + EXPECT_EQ(rec.recvC, 2) << "recvC wasn't 2 - something broken in journal?"; +} + +#endif \ No newline at end of file diff --git a/Engine/source/core/util/journal/test/testProcess.cpp b/Engine/source/core/util/journal/test/processTest.cpp similarity index 62% rename from Engine/source/core/util/journal/test/testProcess.cpp rename to Engine/source/core/util/journal/test/processTest.cpp index 0f8da95a5..479ea9274 100644 --- a/Engine/source/core/util/journal/test/testProcess.cpp +++ b/Engine/source/core/util/journal/test/processTest.cpp @@ -20,39 +20,41 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -#include "unit/test.h" +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" #include "core/util/journal/process.h" -#include "core/util/safeDelete.h" -using namespace UnitTesting; - -CreateUnitTest(TestingProcess, "Journal/Process") +FIXTURE(Process) { - // How many ticks remaining? - U32 _remainingTicks; - - // Callback for process list. - void process() +public: + U32 remainingTicks; + void notification() { - if(_remainingTicks==0) + if(remainingTicks == 0) Process::requestShutdown(); - - _remainingTicks--; + remainingTicks--; } +}; - void run() +TEST_FIX(Process, BasicAPI) +{ + // We'll run 30 ticks, then quit. + remainingTicks = 30; + + // Register with the process list. + Process::notify(this, &ProcessFixture::notification); + + // And do 30 notifies, making sure we end on the 30th. + for(S32 i = 0; i < 30; i++) { - // We'll run 30 ticks, then quit. - _remainingTicks = 30; - - // Register with the process list. - Process::notify(this, &TestingProcess::process); - - // And do 30 notifies, making sure we end on the 30th. - for(S32 i=0; i<30; i++) - test(Process::processEvents(), "Should quit after 30 ProcessEvents() calls - not before!"); - test(!Process::processEvents(), "Should quit after the 30th ProcessEvent() call!"); - - Process::remove(this, &TestingProcess::process); + EXPECT_TRUE(Process::processEvents()) + << "Should quit after 30 ProcessEvents() calls - not before!"; } -}; \ No newline at end of file + + EXPECT_FALSE(Process::processEvents()) + << "Should quit after the 30th ProcessEvent() call!"; + + Process::remove(this, &ProcessFixture::notification); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/core/util/journal/test/testJournal.cpp b/Engine/source/core/util/journal/test/testJournal.cpp deleted file mode 100644 index 9bf13fbf2..000000000 --- a/Engine/source/core/util/journal/test/testJournal.cpp +++ /dev/null @@ -1,185 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "unit/test.h" -#include "core/util/journal/journaledSignal.h" -#include "core/util/safeDelete.h" - -using namespace UnitTesting; - -CreateUnitTest(TestsJournalRecordAndPlayback, "Journal/Basic") -{ - U32 _lastTriggerValue; - - void triggerReceiver(U16 msg) - { - _lastTriggerValue = msg; - } - - void run() - { - // Reset the last trigger value just in case... - _lastTriggerValue = 0; - - // Set up a journaled signal to test with. - JournaledSignal testEvent; - - testEvent.notify(this, &TestsJournalRecordAndPlayback::triggerReceiver); - - // Initialize journal recording and fire off some events... - Journal::Record("test.jrn"); - - if( !Journal::IsRecording() ) - { - test(false, "Fail"); - return; - } - - testEvent.trigger(16); - testEvent.trigger(17); - testEvent.trigger(18); - - test(_lastTriggerValue == 18, "Should encounter last triggered value (18)."); - - Journal::Stop(); - - // Clear it... - _lastTriggerValue = 0; - - // and play back - should get same thing. - Journal::Play("test.jrn"); - - // Since we fired 3 events, it should take three loops. - test(Journal::PlayNext(), "Should be two more events."); - test(Journal::PlayNext(), "Should be one more event."); - test(!Journal::PlayNext(), "Should be no more events."); - - test(_lastTriggerValue == 18, "Should encounter last journaled value (18)."); - } -}; - -CreateUnitTest(TestsJournalDynamicSignals, "Journal/DynamicSignals") -{ - typedef JournaledSignal EventA; - typedef JournaledSignal EventB; - typedef JournaledSignal EventC; - - EventA *dynamicA; - EventB *dynamicB; - EventC *dynamicC; - - // Root, non-dynamic signal receiver. - void receiverRoot(U8 msg) - { - if(msg==1) - { - dynamicA = new EventA(); - dynamicA->notify(this, &TestsJournalDynamicSignals::receiverA); - } - - if(msg==2) - { - dynamicB = new EventB(); - dynamicB->notify(this, &TestsJournalDynamicSignals::receiverB); - } - - if(msg==3) - { - dynamicC = new EventC(); - dynamicC->notify(this, &TestsJournalDynamicSignals::receiverC); - } - } - - U32 recvA, recvB, recvC; - - void receiverA(U32, U16 d) - { - recvA += d; - } - - void receiverB(U8, S8 d) - { - recvB += d; - } - - void receiverC(U32, S32 d) - { - recvC += d; - } - - void run() - { - // Reset our state values. - recvA = recvB = recvC = 0; - - // Set up a signal to start with. - JournaledSignal testEvent; - testEvent.notify(this, &TestsJournalDynamicSignals::receiverRoot); - - // Initialize journal recording and fire off some events... - Journal::Record("test.jrn"); - - if( !Journal::IsRecording() ) - { - test(false, "Fail"); - return; - } - - testEvent.trigger(1); - dynamicA->trigger(8, 100); - testEvent.trigger(2); - dynamicA->trigger(8, 8); - dynamicB->trigger(9, 'a'); - testEvent.trigger(3); - SAFE_DELETE(dynamicB); // Test a deletion. - dynamicC->trigger(8, 1); - dynamicC->trigger(8, 1); - - // Did we end up with expected values? Check before clearing. - test(recvA == 108, "recvA wasn't 108 - something broken in signals?"); - test(recvB == 'a', "recvB wasn't 'a' - something broken in signals?"); - test(recvC == 2, "recvC wasn't 2 - something broken in signals?"); - - // Reset our state values. - recvA = recvB = recvC = 0; - - // And kill the journal... - Journal::Stop(); - - // Also kill our remaining dynamic signals. - SAFE_DELETE(dynamicA); - SAFE_DELETE(dynamicB); - SAFE_DELETE(dynamicC); - - // Play back - should get same thing. - Journal::Play("test.jrn"); - - // Since we fired 8 events, it should take 7+1=8 loops. - for(S32 i=0; i<7; i++) - test(Journal::PlayNext(), "Should be more events."); - test(!Journal::PlayNext(), "Should be no more events."); - - test(recvA == 108, "recvA wasn't 108 - something broken in journal?"); - test(recvB == 'a', "recvB wasn't 'a' - something broken in journal?"); - test(recvC == 2, "recvC wasn't 2 - something broken in journal?"); - } -}; From 0878f005c05baf50696bf1dbc67fb7bad2b775a4 Mon Sep 17 00:00:00 2001 From: Areloch Date: Mon, 4 Aug 2014 21:28:36 -0500 Subject: [PATCH 146/317] Fixed the pure virtual function to be an empty one, which should resolve the linux build failure. Also tweaked the loading methodology to have the canvas by default assume it should force the window display as soon as it can. You can set the canvas to not do that when it's created via the new displayWindow variable. This way, old templates and projects should work as normal without changes, while the new templates are built to take advantage of the nicer splash screen arrangement. --- Engine/source/gui/core/guiCanvas.cpp | 20 ++++++++++++++++++- Engine/source/gui/core/guiCanvas.h | 2 ++ .../source/windowManager/platformWindowMgr.h | 2 +- .../windowManager/win32/win32WindowMgr.cpp | 3 +++ Templates/Empty/game/main.cs | 5 ++++- Templates/Full/game/main.cs | 5 ++++- 6 files changed, 33 insertions(+), 4 deletions(-) diff --git a/Engine/source/gui/core/guiCanvas.cpp b/Engine/source/gui/core/guiCanvas.cpp index f5a84e77e..1d5166fce 100644 --- a/Engine/source/gui/core/guiCanvas.cpp +++ b/Engine/source/gui/core/guiCanvas.cpp @@ -121,7 +121,8 @@ GuiCanvas::GuiCanvas(): GuiControl(), mMiddleMouseLast(false), mRightMouseLast(false), mPlatformWindow(NULL), - mLastRenderMs(0) + mLastRenderMs(0), + mDisplayWindow(true) { setBounds(0, 0, 640, 480); mAwake = true; @@ -176,6 +177,8 @@ void GuiCanvas::initPersistFields() addGroup("Canvas Rendering"); addProtectedField( "numFences", TypeS32, Offset( mNumFences, GuiCanvas ), &setProtectedNumFences, &defaultProtectedGetFn, "The number of GFX fences to use." ); + + addField("displayWindow", TypeBool, Offset(mDisplayWindow, GuiCanvas), "Controls if the canvas window is rendered or not." ); endGroup("Canvas Rendering"); Parent::initPersistFields(); @@ -252,6 +255,19 @@ bool GuiCanvas::onAdd() // Make sure we're able to render. newDevice->setAllowRender( true ); + if(mDisplayWindow) + { + getPlatformWindow()->show(); + WindowManager->setDisplayWindow(true); + getPlatformWindow()->setDisplayWindow(true); + } + else + { + getPlatformWindow()->hide(); + WindowManager->setDisplayWindow(false); + getPlatformWindow()->setDisplayWindow(false); + } + // Propagate add to parents. // CodeReview - if GuiCanvas fails to add for whatever reason, what happens to // all the event registration above? @@ -2700,4 +2716,6 @@ ConsoleMethod( GuiCanvas, hideWindow, void, 2, 2, "" ) return; object->getPlatformWindow()->hide(); + WindowManager->setDisplayWindow(false); + object->getPlatformWindow()->setDisplayWindow(false); } \ No newline at end of file diff --git a/Engine/source/gui/core/guiCanvas.h b/Engine/source/gui/core/guiCanvas.h index 04af6d692..9d3ed8b10 100644 --- a/Engine/source/gui/core/guiCanvas.h +++ b/Engine/source/gui/core/guiCanvas.h @@ -108,6 +108,8 @@ protected: bool mClampTorqueCursor; bool mAlwaysHandleMouseButtons; + bool mDisplayWindow; + /// @} /// @name Mouse Input diff --git a/Engine/source/windowManager/platformWindowMgr.h b/Engine/source/windowManager/platformWindowMgr.h index f2089f51d..41fcd8c9d 100644 --- a/Engine/source/windowManager/platformWindowMgr.h +++ b/Engine/source/windowManager/platformWindowMgr.h @@ -134,7 +134,7 @@ public: virtual void raiseCurtain()=0; /// This method indicates to created windows to show as normal. - virtual void setDisplayWindow(bool set)=0; + virtual void setDisplayWindow(bool set){} private: /// Process command line arguments from StandardMainLoop. This is done to diff --git a/Engine/source/windowManager/win32/win32WindowMgr.cpp b/Engine/source/windowManager/win32/win32WindowMgr.cpp index 266049b6b..d9e0cc22d 100644 --- a/Engine/source/windowManager/win32/win32WindowMgr.cpp +++ b/Engine/source/windowManager/win32/win32WindowMgr.cpp @@ -268,7 +268,10 @@ PlatformWindow *Win32WindowManager::createWindow(GFXDevice *device, const GFXVid w32w->setDisplayWindow(mDisplayWindow); if (!mOffscreenRender && mDisplayWindow) + { ShowWindow( w32w->mWindowHandle, SW_SHOWDEFAULT ); + CloseSplashWindow(winState.appInstance); + } // Bind the window to the specified device. if(device) diff --git a/Templates/Empty/game/main.cs b/Templates/Empty/game/main.cs index bd1333e6a..512c0cc4e 100644 --- a/Templates/Empty/game/main.cs +++ b/Templates/Empty/game/main.cs @@ -38,7 +38,10 @@ function createCanvas(%windowTitle) } // Create the Canvas - %foo = new GuiCanvas(Canvas); + %foo = new GuiCanvas(Canvas) + { + displayWindow = false; + }; // Set the window title if (isObject(Canvas)) diff --git a/Templates/Full/game/main.cs b/Templates/Full/game/main.cs index 11c7348d0..d8fc36dbc 100644 --- a/Templates/Full/game/main.cs +++ b/Templates/Full/game/main.cs @@ -38,7 +38,10 @@ function createCanvas(%windowTitle) } // Create the Canvas - %foo = new GuiCanvas(Canvas); + %foo = new GuiCanvas(Canvas) + { + displayWindow = false; + }; // Set the window title if (isObject(Canvas)) From 981b37e5487a73deddce7070c1e57cb3627a0a9c Mon Sep 17 00:00:00 2001 From: Lukas Joergensen Date: Fri, 8 Aug 2014 00:34:32 +0200 Subject: [PATCH 147/317] Re-enable MixedParticleRendering --- .../renderInstance/renderParticleMgr.cpp | 25 +++++++++++++------ .../game/core/scripts/client/postFx/edgeAA.cs | 2 +- .../game/core/scripts/client/postFx/edgeAA.cs | 2 +- 3 files changed, 19 insertions(+), 10 deletions(-) diff --git a/Engine/source/renderInstance/renderParticleMgr.cpp b/Engine/source/renderInstance/renderParticleMgr.cpp index d1d36f39e..2e72e67a7 100644 --- a/Engine/source/renderInstance/renderParticleMgr.cpp +++ b/Engine/source/renderInstance/renderParticleMgr.cpp @@ -74,14 +74,15 @@ RenderParticleMgr::RenderParticleMgr() { // Render particles at 1/4 resolution mTargetSizeType = WindowSizeScaled; - mTargetScale.set(0.25f, 0.25f); + mTargetScale.set(0.5f, 0.5f); // We use the target chain like a texture pool, not like a swap chain if(!RenderToSingleTarget) setTargetChainLength(5); else mOffscreenSystems.setSize(1); - + + notifyType( RenderPassManager::RIT_Particle ); LightManager::smActivateSignal.notify( this, &RenderParticleMgr::_onLMActivate ); } @@ -377,7 +378,7 @@ void RenderParticleMgr::renderInstance(ParticleRenderInst *ri, SceneRenderState // Draw system path, or draw composite path if(ri->systemState == PSS_DrawComplete) return; - + if(ri->systemState != PSS_AwaitingCompositeDraw) { // Set proper stateblock, and update state @@ -386,7 +387,7 @@ void RenderParticleMgr::renderInstance(ParticleRenderInst *ri, SceneRenderState GFX->setStateBlock( _getOffscreenStateBlock(ri) ); ri->systemState = PSS_AwaitingCompositeDraw; mParticleShaderConsts.mShaderConsts->setSafe( mParticleShaderConsts.mModelViewProjSC, - *ri->modelViewProj * mOffscreenSystems[ri->targetIndex].clipMatrix ); + *ri->modelViewProj * mOffscreenSystems[ri->targetIndex].clipMatrix ); } else { @@ -419,6 +420,7 @@ void RenderParticleMgr::renderInstance(ParticleRenderInst *ri, SceneRenderState alphaScale = 0.0f; break; } + mParticleShaderConsts.mShaderConsts->setSafe( mParticleShaderConsts.mAlphaFactorSC, alphaFactor ); mParticleShaderConsts.mShaderConsts->setSafe( mParticleShaderConsts.mAlphaScaleSC, alphaScale ); @@ -490,6 +492,13 @@ void RenderParticleMgr::renderInstance(ParticleRenderInst *ri, SceneRenderState ScreenSpace::RenderTargetParameters(texObject->getSize(), mEdgeTarget->getViewport(), rtParams); mParticleCompositeShaderConsts.mShaderConsts->setSafe( mParticleCompositeShaderConsts.mEdgeTargetParamsSC, rtParams ); } + else + { + AssertWarn(false, "No edge texture target defined, if you want to use mixed particle" + "rendering, then make sure that the EdgeDetectPostEffect is enabled."); + ri->systemState == PSS_AwaitingHighResDraw; + return; + } // Set shader and constant buffer GFX->setShader( mParticleCompositeShader ); @@ -686,8 +695,8 @@ GFXStateBlockRef RenderParticleMgr::_getHighResStateBlock(ParticleRenderInst *ri GFXStateBlockRef RenderParticleMgr::_getMixedResStateBlock(ParticleRenderInst *ri) { const U8 blendStyle = ri->blendStyle; - if ( mHighResBlocks[blendStyle].isValid() ) - return mHighResBlocks[blendStyle]; + if ( mMixedResBlocks[blendStyle].isValid() ) + return mMixedResBlocks[blendStyle]; GFXStateBlockDesc d; @@ -751,8 +760,8 @@ GFXStateBlockRef RenderParticleMgr::_getMixedResStateBlock(ParticleRenderInst *r // Prepass sampler d.samplers[1] = GFXSamplerStateDesc::getClampPoint(); - mHighResBlocks[blendStyle] = GFX->createStateBlock(d); - return mHighResBlocks[blendStyle]; + mMixedResBlocks[blendStyle] = GFX->createStateBlock(d); + return mMixedResBlocks[blendStyle]; } GFXStateBlockRef RenderParticleMgr::_getCompositeStateBlock(ParticleRenderInst *ri) diff --git a/Templates/Empty/game/core/scripts/client/postFx/edgeAA.cs b/Templates/Empty/game/core/scripts/client/postFx/edgeAA.cs index 2735df855..a5401d794 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/edgeAA.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/edgeAA.cs @@ -83,7 +83,7 @@ singleton PostEffect( EdgeDetectPostEffect ) texture[0] = "#prepass"; target = "#edge"; - isEnabled = false; + isEnabled = true; }; singleton PostEffect( EdgeAAPostEffect ) diff --git a/Templates/Full/game/core/scripts/client/postFx/edgeAA.cs b/Templates/Full/game/core/scripts/client/postFx/edgeAA.cs index 2735df855..a5401d794 100644 --- a/Templates/Full/game/core/scripts/client/postFx/edgeAA.cs +++ b/Templates/Full/game/core/scripts/client/postFx/edgeAA.cs @@ -83,7 +83,7 @@ singleton PostEffect( EdgeDetectPostEffect ) texture[0] = "#prepass"; target = "#edge"; - isEnabled = false; + isEnabled = true; }; singleton PostEffect( EdgeAAPostEffect ) From 77381ac4942c7cee6b80a14515c7b55abb16501d Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Fri, 8 Aug 2014 13:17:14 +1000 Subject: [PATCH 148/317] Ported networking test. --- Engine/source/platform/test/netTest.cpp | 182 +++++++++++++++++++++ Engine/source/platform/test/testNet.cpp | 200 ------------------------ 2 files changed, 182 insertions(+), 200 deletions(-) create mode 100644 Engine/source/platform/test/netTest.cpp delete mode 100644 Engine/source/platform/test/testNet.cpp diff --git a/Engine/source/platform/test/netTest.cpp b/Engine/source/platform/test/netTest.cpp new file mode 100644 index 000000000..083ca9c97 --- /dev/null +++ b/Engine/source/platform/test/netTest.cpp @@ -0,0 +1,182 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "platform/platformNet.h" +#include "core/util/journal/process.h" + +TEST(Net, TCPRequest) +{ + struct handle + { + NetSocket mSocket; + S32 mDataReceived; + + void notify(NetSocket sock, U32 state) + { + // Only consider our own socket. + if(mSocket != sock) + return; + + // Ok - what's the state? We do some dumb responses to given states + // in order to fulfill the request. + if(state == Net::Connected) + { + U8 reqBuffer[] = { + "GET / HTTP/1.0\nUser-Agent: Torque/1.0\n\n" + }; + + Net::Error e = Net::sendtoSocket(mSocket, reqBuffer, sizeof(reqBuffer)); + + EXPECT_EQ(e, Net::NoError) + << "Got an error sending our HTTP request!"; + } + else if(state == Net::Disconnected) + { + Process::requestShutdown(); + mSocket = NULL; + } + } + + void receive(NetSocket sock, RawData incomingData) + { + // Only consider our own socket. + if(mSocket != sock) + return; + + char buff[4096]; + dMemcpy(buff, incomingData.data, incomingData.size); + buff[incomingData.size] = 0; + + mDataReceived += incomingData.size; + } + } handler; + + handler.mSocket = InvalidSocket; + handler.mDataReceived = 0; + + // Hook into the signals. + Net::smConnectionNotify. notify(&handler, &handle::notify); + Net::smConnectionReceive.notify(&handler, &handle::receive); + + // Open a TCP connection to garagegames.com + handler.mSocket = Net::openConnectTo("72.246.107.193:80"); + const U32 limit = Platform::getRealMilliseconds() + (5*1000); + while(Process::processEvents() && (Platform::getRealMilliseconds() < limit) ) {} + + // Unhook from the signals. + Net::smConnectionNotify. remove(&handler, &handle::notify); + Net::smConnectionReceive.remove(&handler, &handle::receive); + + EXPECT_GT(handler.mDataReceived, 0) + << "Didn't get any data back!"; +} + +TEST(Net, JournalTCPRequest) +{ + struct handle + { + NetSocket mSocket; + S32 mDataReceived; + + void notify(NetSocket sock, U32 state) + { + // Only consider our own socket. + if(mSocket != sock) + return; + + // Ok - what's the state? We do some dumb responses to given states + // in order to fulfill the request. + if(state == Net::Connected) + { + U8 reqBuffer[] = { + "GET / HTTP/1.0\nUser-Agent: Torque/1.0\n\n" + }; + + Net::Error e = Net::sendtoSocket(mSocket, reqBuffer, sizeof(reqBuffer)); + + EXPECT_EQ(e, Net::NoError) + << "Got an error sending our HTTP request!"; + } + else if(state == Net::Disconnected) + { + Process::requestShutdown(); + mSocket = NULL; + } + } + + void receive(NetSocket sock, RawData incomingData) + { + // Only consider our own socket. + if(mSocket != sock) + return; + + char buff[4096]; + dMemcpy(buff, incomingData.data, incomingData.size); + buff[incomingData.size] = 0; + + mDataReceived += incomingData.size; + } + + void makeRequest() + { + mSocket = InvalidSocket; + mDataReceived = 0; + + // Initialize networking - done by initLibraries currently + //test(Net::init(), "Failed to initialize networking!"); + + // Hook into the signals. + Net::smConnectionNotify. notify(this, &handle::notify); + Net::smConnectionReceive.notify(this, &handle::receive); + + // Open a TCP connection to garagegames.com + mSocket = Net::openConnectTo("72.246.107.193:80"); + + // Let the callbacks enable things to process. + while(Process::processEvents()) {} + + // Unhook from the signals. + Net::smConnectionNotify. remove(this, &handle::notify); + Net::smConnectionReceive.remove(this, &handle::receive); + + EXPECT_GT(mDataReceived, 0) + << "Didn't get any data back!"; + } + } handler; + + Journal::Record("journalTCP.jrn"); + ASSERT_TRUE(Journal::IsRecording()); + handler.makeRequest(); + S32 bytesRead = handler.mDataReceived; + Journal::Stop(); + + Journal::Play("journalTCP.jrn"); + handler.makeRequest(); + Journal::Stop(); + + EXPECT_EQ(bytesRead, handler.mDataReceived) + << "Didn't get same data back from journal playback."; +} + +#endif \ No newline at end of file diff --git a/Engine/source/platform/test/testNet.cpp b/Engine/source/platform/test/testNet.cpp deleted file mode 100644 index ae2a0c226..000000000 --- a/Engine/source/platform/test/testNet.cpp +++ /dev/null @@ -1,200 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "platform/platformNet.h" -#include "unit/test.h" -#include "core/util/journal/process.h" - -using namespace UnitTesting; - -CreateUnitTest( TestTCPRequest, "Platform/Net/TCPRequest") -{ - NetSocket mSocket; - S32 mDataRecved; - - void handleNotify(NetSocket sock, U32 state) - { - // Only consider our own socket. - if(mSocket != sock) - return; - - // Ok - what's the state? We do some dumb responses to given states - // in order to fulfill the request. - if(state == Net::Connected) - { - U8 reqBuffer[] = { - "GET / HTTP/1.0\nUser-Agent: Torque/1.0\n\n" - }; - - Net::Error e = Net::sendtoSocket(mSocket, reqBuffer, sizeof(reqBuffer)); - - test(e == Net::NoError, "Got an error sending our HTTP request!"); - } - else if(state == Net::Disconnected) - { - Process::requestShutdown(); - mSocket = NULL; - } - } - - void handleReceive(NetSocket sock, RawData incomingData) - { - // Only consider our own socket. - if(mSocket != sock) - return; - - char buff[4096]; - dMemcpy(buff, incomingData.data, incomingData.size); - buff[incomingData.size] = 0; - - UnitPrint("Got a message...\n"); - UnitPrint(buff); - UnitPrint("------\n"); - - mDataRecved += incomingData.size; - } - - void run() - { - mSocket = InvalidSocket; - mDataRecved = 0; - - // Initialize networking - done by initLibraries currently - //test(Net::init(), "Failed to initialize networking!"); - - // Hook into the signals. - Net::smConnectionNotify. notify(this, &TestTCPRequest::handleNotify); - Net::smConnectionReceive.notify(this, &TestTCPRequest::handleReceive); - - // Open a TCP connection to garagegames.com - mSocket = Net::openConnectTo("ip:72.246.107.193:80"); - U32 limit = Platform::getRealMilliseconds() + (5*1000); - while(Process::processEvents() && (Platform::getRealMilliseconds() < limit) ) - ; - - // Unhook from the signals. - Net::smConnectionNotify. remove(this, &TestTCPRequest::handleNotify); - Net::smConnectionReceive.remove(this, &TestTCPRequest::handleReceive); - - test(mDataRecved > 0, "Didn't get any data back!"); - } -}; - -CreateUnitTest( TestTCPRequestJournal, "Platform/Net/JournalTCPRequest") -{ - NetSocket mSocket; - S32 mDataRecved; - - void handleNotify(NetSocket sock, U32 state) - { - // Only consider our own socket. - if(mSocket != sock) - return; - - // Ok - what's the state? We do some dumb responses to given states - // in order to fulfill the request. - if(state == Net::Connected) - { - U8 reqBuffer[] = { - "GET / HTTP/1.0\nUser-Agent: Torque/1.0\n\n" - }; - - Net::Error e = Net::sendtoSocket(mSocket, reqBuffer, sizeof(reqBuffer)); - - test(e == Net::NoError, "Got an error sending our HTTP request!"); - } - else if(state == Net::Disconnected) - { - Process::requestShutdown(); - mSocket = NULL; - } - } - - void handleReceive(NetSocket sock, RawData incomingData) - { - // Only consider our own socket. - if(mSocket != sock) - return; - - char buff[4096]; - dMemcpy(buff, incomingData.data, incomingData.size); - buff[incomingData.size] = 0; - - UnitPrint("Got a message...\n"); - UnitPrint(buff); - UnitPrint("------\n"); - - mDataRecved += incomingData.size; - } - - void makeRequest() - { - mSocket = InvalidSocket; - mDataRecved = 0; - - // Initialize networking - done by initLibraries currently - //test(Net::init(), "Failed to initialize networking!"); - - // Hook into the signals. - Net::smConnectionNotify. notify(this, &TestTCPRequestJournal::handleNotify); - Net::smConnectionReceive.notify(this, &TestTCPRequestJournal::handleReceive); - - // Open a TCP connection to garagegames.com - mSocket = Net::openConnectTo("ip:72.246.107.193:80"); - - // Let the callbacks enable things to process. - while(Process::processEvents()) - ; - - // Unhook from the signals. - Net::smConnectionNotify. remove(this, &TestTCPRequestJournal::handleNotify); - Net::smConnectionReceive.remove(this, &TestTCPRequestJournal::handleReceive); - - test(mDataRecved > 0, "Didn't get any data back!"); - } - - void run() - { - Journal::Record("journalTCP.jrn"); - - if( !Journal::IsRecording() ) - { - test(0, "Failed."); - return; - } - - makeRequest(); - - S32 bytesRead = mDataRecved; - - Journal::Stop(); - - Journal::Play("journalTCP.jrn"); - - makeRequest(); - - Journal::Stop(); - - test(bytesRead == mDataRecved, "Didn't get same data back from journal playback."); - - } -}; \ No newline at end of file From e6d47c58f7c883604f9d666494580e7ea0156a73 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Fri, 8 Aug 2014 14:31:42 +1000 Subject: [PATCH 149/317] Avoid infinite loop waiting for DNS resolution. --- Engine/source/platform/test/netTest.cpp | 24 ++++++++---------------- 1 file changed, 8 insertions(+), 16 deletions(-) diff --git a/Engine/source/platform/test/netTest.cpp b/Engine/source/platform/test/netTest.cpp index 083ca9c97..63c38229a 100644 --- a/Engine/source/platform/test/netTest.cpp +++ b/Engine/source/platform/test/netTest.cpp @@ -48,13 +48,15 @@ TEST(Net, TCPRequest) Net::Error e = Net::sendtoSocket(mSocket, reqBuffer, sizeof(reqBuffer)); - EXPECT_EQ(e, Net::NoError) + ASSERT_EQ(Net::NoError, e) << "Got an error sending our HTTP request!"; } - else if(state == Net::Disconnected) + else { Process::requestShutdown(); mSocket = NULL; + ASSERT_EQ(Net::Disconnected, state) + << "Ended with a network error!"; } } @@ -64,10 +66,6 @@ TEST(Net, TCPRequest) if(mSocket != sock) return; - char buff[4096]; - dMemcpy(buff, incomingData.data, incomingData.size); - buff[incomingData.size] = 0; - mDataReceived += incomingData.size; } } handler; @@ -115,13 +113,15 @@ TEST(Net, JournalTCPRequest) Net::Error e = Net::sendtoSocket(mSocket, reqBuffer, sizeof(reqBuffer)); - EXPECT_EQ(e, Net::NoError) + ASSERT_EQ(Net::NoError, e) << "Got an error sending our HTTP request!"; } - else if(state == Net::Disconnected) + else { Process::requestShutdown(); mSocket = NULL; + ASSERT_EQ(Net::Disconnected, state) + << "Ended with a network error!"; } } @@ -130,11 +130,6 @@ TEST(Net, JournalTCPRequest) // Only consider our own socket. if(mSocket != sock) return; - - char buff[4096]; - dMemcpy(buff, incomingData.data, incomingData.size); - buff[incomingData.size] = 0; - mDataReceived += incomingData.size; } @@ -143,9 +138,6 @@ TEST(Net, JournalTCPRequest) mSocket = InvalidSocket; mDataReceived = 0; - // Initialize networking - done by initLibraries currently - //test(Net::init(), "Failed to initialize networking!"); - // Hook into the signals. Net::smConnectionNotify. notify(this, &handle::notify); Net::smConnectionReceive.notify(this, &handle::receive); From 5f42f6307841d1612229846ff2c850cff7cd32bc Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sat, 9 Aug 2014 11:31:29 +1000 Subject: [PATCH 150/317] Fixed dates. --- Engine/source/core/util/journal/test/journalTest.cpp | 2 +- Engine/source/core/util/journal/test/processTest.cpp | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/source/core/util/journal/test/journalTest.cpp b/Engine/source/core/util/journal/test/journalTest.cpp index 01212f53d..f44a6f157 100644 --- a/Engine/source/core/util/journal/test/journalTest.cpp +++ b/Engine/source/core/util/journal/test/journalTest.cpp @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC +// Copyright (c) 2014 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to diff --git a/Engine/source/core/util/journal/test/processTest.cpp b/Engine/source/core/util/journal/test/processTest.cpp index 479ea9274..1b011a397 100644 --- a/Engine/source/core/util/journal/test/processTest.cpp +++ b/Engine/source/core/util/journal/test/processTest.cpp @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC +// Copyright (c) 2014 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to From 96011623ef8111dceece256171a48d9a316c4a98 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sat, 9 Aug 2014 11:32:46 +1000 Subject: [PATCH 151/317] =?UTF-8?q?Ported=20thread=20pool=20and=20refcount?= =?UTF-8?q?=20tests.=C2=A0Failures,=20not=20sure=20why=20yet.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../platform/test/testThreadSafeRefCount.cpp | 227 ------------------ .../test/threadPoolTest.cpp} | 88 +++---- .../threads/test/threadSafeRefCountTest.cpp | 204 ++++++++++++++++ 3 files changed, 241 insertions(+), 278 deletions(-) delete mode 100644 Engine/source/platform/test/testThreadSafeRefCount.cpp rename Engine/source/platform/{test/testThreadPool.cpp => threads/test/threadPoolTest.cpp} (51%) create mode 100644 Engine/source/platform/threads/test/threadSafeRefCountTest.cpp diff --git a/Engine/source/platform/test/testThreadSafeRefCount.cpp b/Engine/source/platform/test/testThreadSafeRefCount.cpp deleted file mode 100644 index 8ad371797..000000000 --- a/Engine/source/platform/test/testThreadSafeRefCount.cpp +++ /dev/null @@ -1,227 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "unit/test.h" -#include "platform/threads/threadSafeRefCount.h" -#include "platform/threads/thread.h" -#include "core/util/tVector.h" -#include "console/console.h" - -#ifndef TORQUE_SHIPPING - -using namespace UnitTesting; - -#define TEST( x ) test( ( x ), "FAIL: " #x ) - -CreateUnitTest( TestThreadSafeRefCountSerial, "Platform/ThreadSafeRefCount/Serial" ) -{ - struct TestObject : public ThreadSafeRefCount< TestObject > - { - static bool smDeleted; - - TestObject() - { - smDeleted = false; - } - ~TestObject() - { - smDeleted = true; - } - }; - - typedef ThreadSafeRef< TestObject > TestObjectRef; - - void run() - { - TestObjectRef ref1 = new TestObject; - TEST( !ref1->isShared() ); - TEST( ref1 != NULL ); - - TestObjectRef ref2 = ref1; - TEST( ref1->isShared() ); - TEST( ref2->isShared() ); - TEST( ref1 == ref2 ); - - ref1 = NULL; - TEST( !ref2->isShared() ); - - ref2 = NULL; - TEST( TestObject::smDeleted ); - } -}; - -bool TestThreadSafeRefCountSerial::TestObject::smDeleted; - -CreateUnitTest( TestThreadSafeRefCountConcurrent, "Platform/ThreadSafeRefCount/Concurrent" ) -{ -public: - typedef TestThreadSafeRefCountConcurrent TestType; - enum - { - NUM_ADD_REFS_PER_THREAD = 1000, - NUM_EXTRA_REFS_PER_THREAD = 1000, - NUM_THREADS = 10 - }; - - class TestObject : public ThreadSafeRefCount< TestObject > - { - public: - }; - - ThreadSafeRef< TestObject > mRef; - - class TestThread : public Thread - { - public: - TestType* mTest; - Vector< ThreadSafeRef< TestObject > > mExtraRefs; - - TestThread( TestType* test ) - : mTest( test ) {} - - void run( void* arg ) - { - if( !arg ) - { - for( U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; ++ i ) - mTest->mRef->addRef(); - - mExtraRefs.setSize( NUM_EXTRA_REFS_PER_THREAD ); - for( U32 i = 0; i < NUM_EXTRA_REFS_PER_THREAD; ++ i ) - mExtraRefs[ i ] = mTest->mRef; - } - else - { - mExtraRefs.clear(); - - for( U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; ++ i ) - mTest->mRef->release(); - } - } - }; - - void run() - { - mRef = new TestObject; - TEST( mRef->getRefCount() == 2 ); // increments of 2 - - Vector< TestThread* > threads; - threads.setSize( NUM_THREADS ); - - // Create threads. - for( U32 i = 0; i < NUM_THREADS; ++ i ) - threads[ i ] = new TestThread( this ); - - // Run phase 1: create references. - for( U32 i = 0; i < NUM_THREADS; ++ i ) - threads[ i ]->start( NULL ); - - // Wait for completion. - for( U32 i = 0; i < NUM_THREADS; ++ i ) - threads[ i ]->join(); - - Con::printf( "REF: %i", mRef->getRefCount() ); - TEST( mRef->getRefCount() == 2 + ( ( NUM_ADD_REFS_PER_THREAD + NUM_EXTRA_REFS_PER_THREAD ) * NUM_THREADS * 2 ) ); - - // Run phase 2: release references. - for( U32 i = 0; i < NUM_THREADS; ++ i ) - threads[ i ]->start( ( void* ) 1 ); - - // Wait for completion. - for( U32 i = 0; i < NUM_THREADS; ++ i ) - { - threads[ i ]->join(); - delete threads[ i ]; - } - - TEST( mRef->getRefCount() == 2 ); // increments of two - - mRef = NULL; - } -}; - -CreateUnitTest( TestThreadSafeRefCountTagging, "Platform/ThreadSafeRefCount/Tagging" ) -{ - struct TestObject : public ThreadSafeRefCount< TestObject > {}; - - typedef ThreadSafeRef< TestObject > TestObjectRef; - - void run() - { - TestObjectRef ref; - - TEST( !ref.isTagged() ); - TEST( !ref ); - TEST( !ref.ptr() ); - - TEST( ref.trySetFromTo( ref, NULL ) ); - TEST( !ref.isTagged() ); - - TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Set ) ); - TEST( ref.isTagged() ); - TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Set ) ); - TEST( ref.isTagged() ); - - TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Unset ) ); - TEST( !ref.isTagged() ); - TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_Unset ) ); - TEST( !ref.isTagged() ); - - TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_SetOrFail ) ); - TEST( ref.isTagged() ); - TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_SetOrFail ) ); - TEST( ref.isTagged() ); - TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_FailIfSet ) ); - - TEST( ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_UnsetOrFail ) ); - TEST( !ref.isTagged() ); - TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_UnsetOrFail ) ); - TEST( !ref.isTagged() ); - TEST( !ref.trySetFromTo( ref, NULL, TestObjectRef::TAG_FailIfUnset ) ); - - TestObjectRef objectA = new TestObject; - TestObjectRef objectB = new TestObject; - - TEST( !objectA->isShared() ); - TEST( !objectB->isShared() ); - - ref = objectA; - TEST( !ref.isTagged() ); - TEST( ref == objectA ); - TEST( ref == objectA.ptr() ); - TEST( objectA->isShared() ); - - TEST( ref.trySetFromTo( objectA, objectB, TestObjectRef::TAG_Set ) ); - TEST( ref.isTagged() ); - TEST( ref == objectB ); - TEST( ref == objectB.ptr() ); - TEST( objectB->isShared() ); - TEST( !objectA->isShared() ); - - TEST( ref.trySetFromTo( ref, objectA ) ); - TEST( ref.isTagged() ); - TEST( ref == objectA ); - TEST( ref == objectA.ptr() ); - } -}; - -#endif // !TORQUE_SHIPPING diff --git a/Engine/source/platform/test/testThreadPool.cpp b/Engine/source/platform/threads/test/threadPoolTest.cpp similarity index 51% rename from Engine/source/platform/test/testThreadPool.cpp rename to Engine/source/platform/threads/test/threadPoolTest.cpp index 4055ce3fb..95eacd920 100644 --- a/Engine/source/platform/test/testThreadPool.cpp +++ b/Engine/source/platform/threads/test/threadPoolTest.cpp @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC +// Copyright (c) 2014 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to @@ -20,66 +20,52 @@ // IN THE SOFTWARE. //----------------------------------------------------------------------------- -#include "unit/test.h" +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" #include "platform/threads/threadPool.h" #include "console/console.h" #include "core/util/tVector.h" -#ifndef TORQUE_SHIPPING - -using namespace UnitTesting; - -#define TEST( x ) test( ( x ), "FAIL: " #x ) - -// Simple test that creates and verifies an array of numbers using -// thread pool work items. - -CreateUnitTest( TestThreadPool, "Platform/ThreadPool/Simple" ) +TEST(ThreadPool, BasicAPI) { - enum { DEFAULT_NUM_ITEMS = 4000 }; - - static Vector< U32 > results; - + // Represents a single unit of work. In this test we just set an element in + // a result vector. struct TestItem : public ThreadPool::WorkItem { - typedef ThreadPool::WorkItem Parent; - - U32 mIndex; - - TestItem( U32 index ) - : mIndex( index ) {} - - protected: - virtual void execute() - { - results[ mIndex ] = mIndex; - } - }; - - void run() - { - U32 numItems = Con::getIntVariable( "$testThreadPool::numValues", DEFAULT_NUM_ITEMS ); - ThreadPool* pool = &ThreadPool::GLOBAL(); - results.setSize( numItems ); + U32 mIndex; + Vector& mResults; + TestItem(U32 index, Vector& results) + : mIndex(index), mResults(results) {} - for( U32 i = 0; i < numItems; ++ i ) - results[ i ] = U32( -1 ); - - for( U32 i = 0; i < numItems; ++ i ) + protected: + virtual void execute() { - ThreadSafeRef< TestItem > item( new TestItem( i ) ); - pool->queueWorkItem( item ); + mResults[mIndex] = mIndex; } - - pool->flushWorkItems(); - - for( U32 i = 0; i < numItems; ++ i ) - test( results[ i ] == i, "result mismatch" ); - - results.clear(); + }; + + // Construct the vector of results from the work items. + const U32 numItems = 100; + Vector results(__FILE__, __LINE__); + results.setSize(numItems); + for (U32 i = 0; i < numItems; i++) + results[i] = U32(-1); + + // Launch the work items. + ThreadPool* pool = &ThreadPool::GLOBAL(); + for (U32 i = 0; i < numItems; i++) + { + ThreadSafeRef item(new TestItem(i, results)); + pool->queueWorkItem(item); } -}; -Vector< U32 > TestThreadPool::results( __FILE__, __LINE__ ); + // Wait for all items to complete. + pool->flushWorkItems(); -#endif // !TORQUE_SHIPPING + // Verify. + for (U32 i = 0; i < numItems; i++) + EXPECT_EQ(results[i], i) << "result mismatch"; + results.clear(); +} + +#endif \ No newline at end of file diff --git a/Engine/source/platform/threads/test/threadSafeRefCountTest.cpp b/Engine/source/platform/threads/test/threadSafeRefCountTest.cpp new file mode 100644 index 000000000..698637422 --- /dev/null +++ b/Engine/source/platform/threads/test/threadSafeRefCountTest.cpp @@ -0,0 +1,204 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "platform/threads/threadSafeRefCount.h" +#include "platform/threads/thread.h" +#include "core/util/tVector.h" +#include "console/console.h" + +TEST(ThreadSafeRefCount, Serial) +{ + struct TestObject : public ThreadSafeRefCount + { + bool &flag; + TestObject(bool &f) : flag(f) + { + flag = false; + } + ~TestObject() + { + flag = true; + } + }; + typedef ThreadSafeRef TestObjectRef; + + bool deleted = false; + TestObjectRef ref1 = new TestObject(deleted); + ASSERT_FALSE(deleted); + EXPECT_FALSE(ref1->isShared()); + EXPECT_TRUE(ref1 != NULL); + + TestObjectRef ref2 = ref1; + EXPECT_TRUE(ref1->isShared()); + EXPECT_TRUE(ref2->isShared()); + EXPECT_EQ(ref1, ref2); + + ref1 = NULL; + EXPECT_FALSE(ref2->isShared()); + + ref2 = NULL; + ASSERT_TRUE(deleted); +} + +TEST(ThreadSafeRefCount, Concurrent) +{ + enum + { + NUM_ADD_REFS_PER_THREAD = 10, + NUM_EXTRA_REFS_PER_THREAD = 10, + NUM_THREADS = 10 + }; + + class TestObject : public ThreadSafeRefCount {}; + typedef ThreadSafeRef TestObjectRef; + TestObjectRef mRef; + + class TestThread : public Thread + { + public: + TestObjectRef mRef; + Vector mExtraRefs; + + TestThread(TestObjectRef ref) : mRef(ref) {} + + void run(void* arg) + { + if (!arg) + { + // Create references. + for (U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; i++) + mRef->addRef(); + + mExtraRefs.setSize(NUM_EXTRA_REFS_PER_THREAD); + for (U32 i = 0; i < NUM_EXTRA_REFS_PER_THREAD; i++) + mExtraRefs[i] = mRef; + } + else + { + // Clear references. + mExtraRefs.clear(); + for (U32 i = 0; i < NUM_ADD_REFS_PER_THREAD; i++) + mRef->release(); + } + } + }; + + mRef = new TestObject; + EXPECT_EQ(mRef->getRefCount(), 2); // increments of 2 + + Vector threads; + threads.setSize(NUM_THREADS); + + // Create threads. + for (U32 i = 0; i < NUM_THREADS; i++) + threads[i] = new TestThread(mRef); + + // Run phase 1: create references. + for (U32 i = 0; i < NUM_THREADS; i++) + threads[i]->start(NULL); + + // Wait for completion. + for (U32 i = 0; i < NUM_THREADS; i++) + threads[i]->join(); + + Con::printf("REF: %i", mRef->getRefCount()); + EXPECT_EQ(mRef->getRefCount(), 2 + ((NUM_ADD_REFS_PER_THREAD + NUM_EXTRA_REFS_PER_THREAD) * NUM_THREADS * 2)); + + // Run phase 2: release references. + for (U32 i = 0; i < NUM_THREADS; i++) + threads[i]->start((void*) 1); + + // Wait for completion. + for (U32 i = 0; i < NUM_THREADS; i++) + { + threads[i]->join(); + delete threads[i]; + } + + EXPECT_EQ(mRef->getRefCount(), 2); // increments of two + + mRef = NULL; +} + +TEST(ThreadSafeRefCount, Tagging) +{ + struct TestObject : public ThreadSafeRefCount {}; + typedef ThreadSafeRef TestObjectRef; + + TestObjectRef ref; + EXPECT_FALSE(ref.isTagged()); + EXPECT_TRUE(bool(ref)); + EXPECT_FALSE(bool(ref.ptr())); + + EXPECT_TRUE(ref.trySetFromTo(ref, NULL)); + EXPECT_FALSE(ref.isTagged()); + + EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_Set)); + EXPECT_TRUE(ref.isTagged()); + EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_Set)); + EXPECT_TRUE(ref.isTagged()); + + EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_Unset)); + EXPECT_FALSE(ref.isTagged()); + EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_Unset)); + EXPECT_FALSE(ref.isTagged()); + + EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_SetOrFail)); + EXPECT_TRUE(ref.isTagged()); + EXPECT_FALSE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_SetOrFail)); + EXPECT_TRUE(ref.isTagged()); + EXPECT_FALSE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_FailIfSet)); + + EXPECT_TRUE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_UnsetOrFail)); + EXPECT_FALSE(ref.isTagged()); + EXPECT_FALSE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_UnsetOrFail)); + EXPECT_FALSE(ref.isTagged()); + EXPECT_FALSE(ref.trySetFromTo(ref, NULL, TestObjectRef::TAG_FailIfUnset)); + + TestObjectRef objectA = new TestObject; + TestObjectRef objectB = new TestObject; + + EXPECT_FALSE(objectA->isShared()); + EXPECT_FALSE(objectB->isShared()); + + ref = objectA; + EXPECT_FALSE(ref.isTagged()); + EXPECT_TRUE(ref == objectA); + EXPECT_TRUE(ref == objectA.ptr()); + EXPECT_TRUE(objectA->isShared()); + + EXPECT_TRUE(ref.trySetFromTo(objectA, objectB, TestObjectRef::TAG_Set)); + EXPECT_TRUE(ref.isTagged()); + EXPECT_EQ(ref, objectB); + EXPECT_EQ(ref, objectB.ptr()); + EXPECT_TRUE(objectB->isShared()); + EXPECT_FALSE(objectA->isShared()); + + EXPECT_TRUE(ref.trySetFromTo(ref, objectA)); + EXPECT_TRUE(ref.isTagged()); + EXPECT_EQ(ref, objectA); + EXPECT_EQ(ref, objectA.ptr()); +} + +#endif \ No newline at end of file From 9e404b3707c8787da35e74f5b9427fd2c5ff44d4 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Thu, 14 Aug 2014 21:46:48 -0500 Subject: [PATCH 152/317] kills off the presence of a fake light when there are no others in a given scene --- Engine/source/lighting/advanced/advancedLightBinManager.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/lighting/advanced/advancedLightBinManager.cpp b/Engine/source/lighting/advanced/advancedLightBinManager.cpp index d8620e856..c90d18bac 100644 --- a/Engine/source/lighting/advanced/advancedLightBinManager.cpp +++ b/Engine/source/lighting/advanced/advancedLightBinManager.cpp @@ -243,7 +243,7 @@ void AdvancedLightBinManager::render( SceneRenderState *state ) return; // Get the sunlight. If there's no sun, and no lights in the bins, no draw - LightInfo *sunLight = mLightManager->getSpecialLight( LightManager::slSunLightType ); + LightInfo *sunLight = mLightManager->getSpecialLight( LightManager::slSunLightType, false ); if( !sunLight && mLightBin.empty() ) return; From 346ad5bdaad625dca798354f109eb004e7df7b2a Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sat, 16 Aug 2014 11:28:53 +1000 Subject: [PATCH 153/317] Finished up splash screen implementation. * Added stub methods for non-Windows platforms * Move canvas show to after tools have loaded * Fix a tab --- Engine/source/gui/core/guiCanvas.cpp | 2 +- Engine/source/platformMac/macCocoaPlatform.mm | 5 +++++ Engine/source/platformX86UNIX/x86UNIXStub.dedicated.cpp | 1 + Templates/Empty/game/main.cs | 9 +++++++++ Templates/Empty/game/main.cs.in | 9 +++++++++ Templates/Empty/game/scripts/gui/startupGui.cs | 6 ------ Templates/Full/game/main.cs | 9 +++++++++ Templates/Full/game/main.cs.in | 9 +++++++++ Templates/Full/game/scripts/gui/startupGui.cs | 6 ------ 9 files changed, 43 insertions(+), 13 deletions(-) diff --git a/Engine/source/gui/core/guiCanvas.cpp b/Engine/source/gui/core/guiCanvas.cpp index 1d5166fce..ad5646fe6 100644 --- a/Engine/source/gui/core/guiCanvas.cpp +++ b/Engine/source/gui/core/guiCanvas.cpp @@ -2716,6 +2716,6 @@ ConsoleMethod( GuiCanvas, hideWindow, void, 2, 2, "" ) return; object->getPlatformWindow()->hide(); - WindowManager->setDisplayWindow(false); + WindowManager->setDisplayWindow(false); object->getPlatformWindow()->setDisplayWindow(false); } \ No newline at end of file diff --git a/Engine/source/platformMac/macCocoaPlatform.mm b/Engine/source/platformMac/macCocoaPlatform.mm index 24b8808e9..286aa44e2 100644 --- a/Engine/source/platformMac/macCocoaPlatform.mm +++ b/Engine/source/platformMac/macCocoaPlatform.mm @@ -70,6 +70,11 @@ bool Platform::displaySplashWindow() return false; } +bool Platform::closeSplashWindow() +{ + return false; +} + #pragma mark ---- File IO ---- //----------------------------------------------------------------------------- bool dPathCopy(const char* source, const char* dest, bool nooverwrite) diff --git a/Engine/source/platformX86UNIX/x86UNIXStub.dedicated.cpp b/Engine/source/platformX86UNIX/x86UNIXStub.dedicated.cpp index 770fd0680..39948914b 100644 --- a/Engine/source/platformX86UNIX/x86UNIXStub.dedicated.cpp +++ b/Engine/source/platformX86UNIX/x86UNIXStub.dedicated.cpp @@ -93,6 +93,7 @@ void Platform::openFile(const char *path) { } // window bool Platform::displaySplashWindow(String path) { return false; } +bool Platform::closeSplashWindow() { return false; } // font PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset) { return NULL; } diff --git a/Templates/Empty/game/main.cs b/Templates/Empty/game/main.cs index 512c0cc4e..5d26b7fca 100644 --- a/Templates/Empty/game/main.cs +++ b/Templates/Empty/game/main.cs @@ -249,6 +249,15 @@ if ($displayHelp) { else { onStart(); echo("Engine initialized..."); + + if( !$isDedicated ) + { + // As we know at this point that the initial load is complete, + // we can hide any splash screen we have, and show the canvas. + // This keeps things looking nice, instead of having a blank window + closeSplashWindow(); + Canvas.showWindow(); + } // Auto-load on the 360 if( $platform $= "xenon" ) diff --git a/Templates/Empty/game/main.cs.in b/Templates/Empty/game/main.cs.in index db8436e7d..dbabc4a2f 100644 --- a/Templates/Empty/game/main.cs.in +++ b/Templates/Empty/game/main.cs.in @@ -246,6 +246,15 @@ if ($displayHelp) { else { onStart(); echo("Engine initialized..."); + + if( !$isDedicated ) + { + // As we know at this point that the initial load is complete, + // we can hide any splash screen we have, and show the canvas. + // This keeps things looking nice, instead of having a blank window + closeSplashWindow(); + Canvas.showWindow(); + } // Auto-load on the 360 if( $platform $= "xenon" ) diff --git a/Templates/Empty/game/scripts/gui/startupGui.cs b/Templates/Empty/game/scripts/gui/startupGui.cs index d0226a9bf..675daeafa 100644 --- a/Templates/Empty/game/scripts/gui/startupGui.cs +++ b/Templates/Empty/game/scripts/gui/startupGui.cs @@ -29,12 +29,6 @@ function loadStartup() // The index of the current splash screen $StartupIdx = 0; - // As we know at this point that the initial load is complete, - // we can hide any splash screen we have, and show the canvas. - // This keeps things looking nice, instead of having a blank window - closeSplashWindow(); - Canvas.showWindow(); - // A list of the splash screens and logos // to cycle through. Note that they have to // be in consecutive numerical order diff --git a/Templates/Full/game/main.cs b/Templates/Full/game/main.cs index d8fc36dbc..96794899a 100644 --- a/Templates/Full/game/main.cs +++ b/Templates/Full/game/main.cs @@ -249,6 +249,15 @@ if ($displayHelp) { else { onStart(); echo("Engine initialized..."); + + if( !$isDedicated ) + { + // As we know at this point that the initial load is complete, + // we can hide any splash screen we have, and show the canvas. + // This keeps things looking nice, instead of having a blank window + closeSplashWindow(); + Canvas.showWindow(); + } // Auto-load on the 360 if( $platform $= "xenon" ) diff --git a/Templates/Full/game/main.cs.in b/Templates/Full/game/main.cs.in index db8436e7d..dbabc4a2f 100644 --- a/Templates/Full/game/main.cs.in +++ b/Templates/Full/game/main.cs.in @@ -246,6 +246,15 @@ if ($displayHelp) { else { onStart(); echo("Engine initialized..."); + + if( !$isDedicated ) + { + // As we know at this point that the initial load is complete, + // we can hide any splash screen we have, and show the canvas. + // This keeps things looking nice, instead of having a blank window + closeSplashWindow(); + Canvas.showWindow(); + } // Auto-load on the 360 if( $platform $= "xenon" ) diff --git a/Templates/Full/game/scripts/gui/startupGui.cs b/Templates/Full/game/scripts/gui/startupGui.cs index d0226a9bf..675daeafa 100644 --- a/Templates/Full/game/scripts/gui/startupGui.cs +++ b/Templates/Full/game/scripts/gui/startupGui.cs @@ -29,12 +29,6 @@ function loadStartup() // The index of the current splash screen $StartupIdx = 0; - // As we know at this point that the initial load is complete, - // we can hide any splash screen we have, and show the canvas. - // This keeps things looking nice, instead of having a blank window - closeSplashWindow(); - Canvas.showWindow(); - // A list of the splash screens and logos // to cycle through. Note that they have to // be in consecutive numerical order From 76b2e4e9756ea379e939249874764abc44527756 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sat, 16 Aug 2014 11:52:50 +1000 Subject: [PATCH 154/317] Fixed template scripts. --- Templates/Empty/game/main.cs.in | 5 ++++- Templates/Full/game/main.cs.in | 5 ++++- 2 files changed, 8 insertions(+), 2 deletions(-) diff --git a/Templates/Empty/game/main.cs.in b/Templates/Empty/game/main.cs.in index dbabc4a2f..d3b5791ef 100644 --- a/Templates/Empty/game/main.cs.in +++ b/Templates/Empty/game/main.cs.in @@ -38,7 +38,10 @@ function createCanvas(%windowTitle) } // Create the Canvas - %foo = new GuiCanvas(Canvas); + %foo = new GuiCanvas(Canvas) + { + displayWindow = false; + }; // Set the window title if (isObject(Canvas)) diff --git a/Templates/Full/game/main.cs.in b/Templates/Full/game/main.cs.in index dbabc4a2f..d3b5791ef 100644 --- a/Templates/Full/game/main.cs.in +++ b/Templates/Full/game/main.cs.in @@ -38,7 +38,10 @@ function createCanvas(%windowTitle) } // Create the Canvas - %foo = new GuiCanvas(Canvas); + %foo = new GuiCanvas(Canvas) + { + displayWindow = false; + }; // Set the window title if (isObject(Canvas)) From 868b20cfb53105f3b2417a1f0b8ad9c8dc5c84ac Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sat, 16 Aug 2014 11:55:21 +1000 Subject: [PATCH 155/317] Load new DLL first so old projects don't see odd behavior. --- Engine/source/main/main.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/main/main.cpp b/Engine/source/main/main.cpp index a1d2bd1f1..1638b5b33 100644 --- a/Engine/source/main/main.cpp +++ b/Engine/source/main/main.cpp @@ -55,7 +55,7 @@ int PASCAL WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, LPSTR lpszCmdL HMODULE hGame = NULL; std::wstring dllName = std::wstring(); // The file name is the same as this executable's name, plus a suffix. - const std::wstring dllSuffices[] = {L"", L" DLL"}; + const std::wstring dllSuffices[] = {L" DLL", L""}; const unsigned int numSuffices = sizeof(dllSuffices) / sizeof(std::wstring); for (unsigned int i = 0; i < numSuffices; i++) From eeac48b4ea6124c6327a88c5fa88ea29ac3c68dc Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sat, 16 Aug 2014 13:48:01 +1000 Subject: [PATCH 156/317] Ported platform timer test. --- .../platform/test/platformTimerTest.cpp | 93 ++++++++++++++++ .../source/platform/test/testTimeManager.cpp | 105 ------------------ 2 files changed, 93 insertions(+), 105 deletions(-) create mode 100644 Engine/source/platform/test/platformTimerTest.cpp delete mode 100644 Engine/source/platform/test/testTimeManager.cpp diff --git a/Engine/source/platform/test/platformTimerTest.cpp b/Engine/source/platform/test/platformTimerTest.cpp new file mode 100644 index 000000000..dddf58830 --- /dev/null +++ b/Engine/source/platform/test/platformTimerTest.cpp @@ -0,0 +1,93 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "platform/platformTimer.h" +#include "core/util/journal/process.h" +#include "math/mMath.h" + +TEST(Platform, AdvanceTime) +{ + U32 time = Platform::getVirtualMilliseconds(); + Platform::advanceTime(10); + U32 newTime = Platform::getVirtualMilliseconds(); + EXPECT_EQ(10, newTime - time) + << "We advanced 10ms but didn't get a 10ms delta!"; +} + +TEST(Platform, Sleep) +{ + U32 start = Platform::getRealMilliseconds(); + Platform::sleep(500); + U32 end = Platform::getRealMilliseconds(); + EXPECT_GE(end - start, 500) + << "We didn't sleep at least as long as we requested!"; +}; + +TEST(TimeManager, BasicAPI) +{ + struct handle + { + S32 mElapsedTime; + S32 mNumberCalls; + + void timeEvent(S32 timeDelta) + { + mElapsedTime += timeDelta; + mNumberCalls++; + + if(mElapsedTime >= 1000) + Process::requestShutdown(); + } + } handler; + + handler.mElapsedTime = handler.mNumberCalls = 0; + + // Initialize the time manager... + TimeManager time; + time.timeEvent.notify(&handler, &handle::timeEvent); + + // Event loop till at least one second has passed. + const U32 start = Platform::getRealMilliseconds(); + + while(Process::processEvents()) + { + // If we go too long, kill it off... + if(Platform::getRealMilliseconds() - start > 30*1000) + { + EXPECT_TRUE(false) + << "Terminated process loop due to watchdog, not due to time manager event, after 30 seconds."; + Process::requestShutdown(); + } + } + const U32 end = Platform::getRealMilliseconds(); + + // Now, confirm we have approximately similar elapsed times. + S32 elapsedRealTime = end - start; + EXPECT_LT(mAbs(elapsedRealTime - handler.mElapsedTime), 50) + << "Failed to elapse time to within the desired tolerance."; + EXPECT_GT(handler.mNumberCalls, 0) + << "Somehow got no event callbacks from TimeManager?"; +}; + +#endif \ No newline at end of file diff --git a/Engine/source/platform/test/testTimeManager.cpp b/Engine/source/platform/test/testTimeManager.cpp deleted file mode 100644 index 7b157035c..000000000 --- a/Engine/source/platform/test/testTimeManager.cpp +++ /dev/null @@ -1,105 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "platform/platform.h" -#include "platform/platformTimer.h" -#include "core/util/journal/journaledSignal.h" -#include "core/util/journal/process.h" -#include "math/mMath.h" -#include "console/console.h" - -#include "unit/test.h" -using namespace UnitTesting; - -CreateUnitTest(Check_advanceTime, "Platform/Time/advanceTime") -{ - void run() - { - U32 time = Platform::getVirtualMilliseconds(); - Platform::advanceTime(10); - U32 newTime = Platform::getVirtualMilliseconds(); - - test(newTime - time == 10, "Platform::advanceTime is borked, we advanced 10ms but didn't get a 10ms delta!"); - } -}; - -CreateUnitTest(Check_platformSleep, "Platform/Time/Sleep") -{ - const static S32 sleepTimeMs = 500; - void run() - { - U32 start = Platform::getRealMilliseconds(); - Platform::sleep(sleepTimeMs); - U32 end = Platform::getRealMilliseconds(); - - test(end - start >= sleepTimeMs, "We didn't sleep at least as long as we requested!"); - } -}; - -CreateUnitTest(Check_timeManager, "Platform/Time/Manager") -{ - void handleTimeEvent(S32 timeDelta) - { - mElapsedTime += timeDelta; - mNumberCalls++; - - if(mElapsedTime >= 1000) - Process::requestShutdown(); - } - - S32 mElapsedTime; - S32 mNumberCalls; - - void run() - { - mElapsedTime = mNumberCalls = 0; - - // Initialize the time manager... - TimeManager time; - time.timeEvent.notify(this, &Check_timeManager::handleTimeEvent); - - // Event loop till at least one second has passed. - const U32 start = Platform::getRealMilliseconds(); - - while(Process::processEvents()) - { - // If we go too long, kill it off... - if(Platform::getRealMilliseconds() - start > 30*1000) - { - test(false, "Terminated process loop due to watchdog, not due to time manager event, after 30 seconds."); - Process::requestShutdown(); - } - } - - const U32 end = Platform::getRealMilliseconds(); - - // Now, confirm we have approximately similar elapsed times. - S32 elapsedRealTime = end - start; - test(mAbs(elapsedRealTime - mElapsedTime) < 50, "Failed to elapse time to within the desired tolerance."); - - test(mNumberCalls > 0, "Somehow got no event callbacks from TimeManager?"); - - Con::printf(" Got %d time events, and elapsed %dms from TimeManager, " - "%dms according to Platform::getRealMilliseconds()", - mNumberCalls, mElapsedTime, elapsedRealTime); - } -}; \ No newline at end of file From c328cb53b1fdbaadf165f84a49dc5070a89f0fc6 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Mon, 18 Aug 2014 08:19:45 +1000 Subject: [PATCH 157/317] Implemented physics collision for MeshRoad. According to deepscratch's post in the forum: http://www.garagegames.com/community/forums/viewthread/130181/1#comment-826897 --- Engine/source/environment/meshRoad.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/Engine/source/environment/meshRoad.cpp b/Engine/source/environment/meshRoad.cpp index 6a5e3fbd1..453250568 100644 --- a/Engine/source/environment/meshRoad.cpp +++ b/Engine/source/environment/meshRoad.cpp @@ -49,6 +49,7 @@ #include "collision/concretePolyList.h" #include "T3D/physics/physicsPlugin.h" #include "T3D/physics/physicsBody.h" +#include "T3D/physics/physicsCollision.h" #include "environment/nodeListManager.h" #define MIN_METERS_PER_SEGMENT 1.0f @@ -1722,6 +1723,8 @@ void MeshRoad::_generateSlices() void MeshRoad::_generateSegments() { + SAFE_DELETE( mPhysicsRep ); + mSegments.clear(); for ( U32 i = 0; i < mSlices.size() - 1; i++ ) @@ -1736,8 +1739,22 @@ void MeshRoad::_generateSegments() if ( PHYSICSMGR ) { - SAFE_DELETE( mPhysicsRep ); - //mPhysicsRep = PHYSICSMGR->createBody(); + ConcretePolyList polylist; + if ( buildPolyList( PLC_Collision, &polylist, getWorldBox(), getWorldSphere() ) ) + { + polylist.triangulate(); + + PhysicsCollision *colShape = PHYSICSMGR->createCollision(); + colShape->addTriangleMesh( polylist.mVertexList.address(), + polylist.mVertexList.size(), + polylist.mIndexList.address(), + polylist.mIndexList.size() / 3, + MatrixF::Identity ); + + PhysicsWorld *world = PHYSICSMGR->getWorld( isServerObject() ? "server" : "client" ); + mPhysicsRep = PHYSICSMGR->createBody(); + mPhysicsRep->init( colShape, 0, 0, this, world ); + } } } From 4110fe51b27c8fa50ef853fdb170d25cc62984cf Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Wed, 20 Aug 2014 09:59:12 +1000 Subject: [PATCH 158/317] Fixed thread statics. --- Engine/source/core/threadStatic.h | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/Engine/source/core/threadStatic.h b/Engine/source/core/threadStatic.h index 2f5a16cbf..830ba260c 100644 --- a/Engine/source/core/threadStatic.h +++ b/Engine/source/core/threadStatic.h @@ -52,6 +52,7 @@ public: static const U32 getListIndex(){ return mListIndex; } virtual void *getMemInstPtr() = 0; + virtual const void *getConstMemInstPtr() const = 0; virtual const dsize_t getMemInstSize() const = 0; #ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS @@ -143,6 +144,7 @@ private: public: TorqueThreadStatic( T instanceVal ) : mInstance( instanceVal ) {} virtual void *getMemInstPtr() { return &mInstance; } + virtual const void *getConstMemInstPtr() const { return &mInstance; } // I am not sure these are needed, and I don't want to create confusing-to-debug code #if 0 @@ -181,7 +183,7 @@ public: \ _##name##TorqueThreadStatic() : TorqueThreadStatic( initalvalue ) {} \ virtual const dsize_t getMemInstSize() const { return sizeof( type ); } \ type &_cast() { return *reinterpret_cast( getMemInstPtr() ); } \ - const type &_const_cast() const { return *reinterpret_cast( getMemInstPtr() ); } \ + const type &_const_cast() const { return *reinterpret_cast( getConstMemInstPtr() ); } \ }; \ static _##name##TorqueThreadStatic name##TorqueThreadStatic; \ static _TorqueThreadStaticReg _##name##TTSReg( reinterpret_cast<_TorqueThreadStatic *>( & name##TorqueThreadStatic ) ) From fb408f3f018871265ebcb747c8f036e8606e1f7f Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Wed, 20 Aug 2014 09:55:51 +1000 Subject: [PATCH 159/317] Ported thread statics test. --- .../source/platform/test/threadStaticTest.cpp | 91 ++++++++++++++++ Engine/source/unit/tests/testThreadStatic.cpp | 101 ------------------ 2 files changed, 91 insertions(+), 101 deletions(-) create mode 100644 Engine/source/platform/test/threadStaticTest.cpp delete mode 100644 Engine/source/unit/tests/testThreadStatic.cpp diff --git a/Engine/source/platform/test/threadStaticTest.cpp b/Engine/source/platform/test/threadStaticTest.cpp new file mode 100644 index 000000000..5f1f4d483 --- /dev/null +++ b/Engine/source/platform/test/threadStaticTest.cpp @@ -0,0 +1,91 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" + +// This unit test will blow up without thread static support +#include "core/threadStatic.h" +#ifdef TORQUE_ENABLE_THREAD_STATICS + +// Declare a test thread static +DITTS(U32, gUnitTestFoo, 42); +DITTS(F32, gUnitTestF32, 1.0); + +TEST(ThreadStatic, BasicAPI) +{ + // ThreadStatic list should be initialized right now, so lets see if it has + // any entries. + EXPECT_FALSE(_TorqueThreadStaticReg::getStaticList().empty()) + << "Self-registration has failed, or no statics declared"; + + // Spawn a new copy. + TorqueThreadStaticListHandle testInstance = _TorqueThreadStaticReg::spawnThreadStaticsInstance(); + + // Test the copy + ASSERT_EQ(_TorqueThreadStaticReg::getStaticList(0).size(), testInstance->size()) + << "Spawned static list has a different size from master copy."; + + // Traverse the list and compare it to the initial value copy (index 0) + for(S32 i = 0; i < _TorqueThreadStaticReg::getStaticList().size(); i++) + { + _TorqueThreadStatic *master = _TorqueThreadStaticReg::getStaticList()[i]; + _TorqueThreadStatic *cpy = (*testInstance)[i]; + + // Make sure it is not the same memory + EXPECT_NE(master, cpy) + << "Copy not spawned properly."; + + // Make sure the sizes are the same + ASSERT_EQ(master->getMemInstSize(), cpy->getMemInstSize()) + << "Size mismatch between master and copy"; + + // Make sure the initialization occurred properly + EXPECT_EQ(0, dMemcmp(master->getMemInstPtr(), cpy->getMemInstPtr(), master->getMemInstSize())) + << "Initial value for spawned list is not correct"; + } + + // Test metrics if enabled +#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS + U32 fooHitCount = (*testInstance)[_gUnitTestFooTorqueThreadStatic::getListIndex()]->getHitCount(); +#endif + + // Set/get some values (If we test static metrics, this is hit 1) + ATTS_(gUnitTestFoo, 1) = 55; + + // Test to see that the master copy and the spawned copy differ + // (If we test metrics, this is hit 2, for the instance, and hit 1 for the master) + EXPECT_NE(ATTS_(gUnitTestFoo, 0), ATTS_(gUnitTestFoo, 1)) + << "Assignment for spawned instanced memory failed"; + +#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS + U32 fooHitCount2 = (*testInstance)[_gUnitTestFooTorqueThreadStatic::getListIndex()]->getHitCount(); + EXPECT_EQ(fooHitCount2, (fooHitCount + 2)) + << "Thread static metric hit count failed"; +#endif + + // Destroy instances + _TorqueThreadStaticReg::destroyInstance(testInstance); +} + +#endif +#endif \ No newline at end of file diff --git a/Engine/source/unit/tests/testThreadStatic.cpp b/Engine/source/unit/tests/testThreadStatic.cpp deleted file mode 100644 index 2b51cd766..000000000 --- a/Engine/source/unit/tests/testThreadStatic.cpp +++ /dev/null @@ -1,101 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "platform/platform.h" -#include "unit/test.h" -#include "core/threadStatic.h" -#include "unit/memoryTester.h" - -using namespace UnitTesting; - -//----------------------------------------------------------------------------- -// This unit test will blow up without thread static support -#ifdef TORQUE_ENABLE_THREAD_STATICS - -// Declare a test thread static -DITTS( U32, gUnitTestFoo, 42 ); -DITTS( F32, gUnitTestF32, 1.0 ); - -CreateUnitTest( TestThreadStatic, "Core/ThreadStatic" ) -{ - void run() - { - MemoryTester m; - m.mark(); - - // ThreadStatic list should be initialized right now, so lets see if it has - // any entries. - test( !_TorqueThreadStaticReg::getStaticList().empty(), "Self-registration has failed, or no statics declared" ); - - // Spawn a new copy. - TorqueThreadStaticListHandle testInstance = _TorqueThreadStaticReg::spawnThreadStaticsInstance(); - - // Test the copy - test( _TorqueThreadStaticReg::getStaticList( 0 ).size() == testInstance->size(), "Spawned static list has a different size from master copy." ); - - // Make sure the size test passed before this is attempted - if( lastTestPassed() ) - { - // Traverse the list and compare it to the initial value copy (index 0) - for( S32 i = 0; i < _TorqueThreadStaticReg::getStaticList().size(); i++ ) - { - _TorqueThreadStatic *master = _TorqueThreadStaticReg::getStaticList()[i]; - _TorqueThreadStatic *cpy = (*testInstance)[i]; - - // Make sure it is not the same memory - test( master != cpy, "Copy not spawned properly." ); - - // Make sure the sizes are the same - test( master->getMemInstSize() == cpy->getMemInstSize(), "Size mismatch between master and copy" ); - - // Make sure the initialization occurred properly - if( lastTestPassed() ) - test( dMemcmp( master->getMemInstPtr(), cpy->getMemInstPtr(), master->getMemInstSize() ) == 0, "Initial value for spawned list is not correct" ); - } - } - - // Test metrics if enabled -#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS - U32 fooHitCount = (*testInstance)[_gUnitTestFooTorqueThreadStatic::getListIndex()]->getHitCount(); -#endif - - // Set/get some values (If we test static metrics, this is hit 1) - ATTS_(gUnitTestFoo, 1) = 55; - - // Test to see that the master copy and the spawned copy differ - // (If we test metrics, this is hit 2, for the instance, and hit 1 for the master) - test( ATTS_(gUnitTestFoo, 0) != ATTS_(gUnitTestFoo, 1 ) , "Assignment for spawned instanced memory failed" ); - -#ifdef TORQUE_ENABLE_THREAD_STATIC_METRICS - U32 fooHitCount2 = (*testInstance)[_gUnitTestFooTorqueThreadStatic::getListIndex()]->getHitCount(); - test( fooHitCount2 == ( fooHitCount + 2 ), "Thread static metric hit count failed" ); -#endif - - // Destroy instances - _TorqueThreadStaticReg::destroyInstance( testInstance ); - - // Now test the cleanup - test( m.check(), "Memory leak in dynamic static allocation stuff." ); - } -}; - -#endif // TORQUE_ENABLE_THREAD_STATICS \ No newline at end of file From 4c769627c3d9891ea1db5706366df8e506afc1fa Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Wed, 20 Aug 2014 11:55:21 +1000 Subject: [PATCH 160/317] Ported thread static stress test. --- .../source/platform/test/threadStaticTest.cpp | 48 +++++++++++ .../tests/testThreadStaticPerformance.cpp | 82 ------------------- 2 files changed, 48 insertions(+), 82 deletions(-) delete mode 100644 Engine/source/unit/tests/testThreadStaticPerformance.cpp diff --git a/Engine/source/platform/test/threadStaticTest.cpp b/Engine/source/platform/test/threadStaticTest.cpp index 5f1f4d483..ec4c5f2ad 100644 --- a/Engine/source/platform/test/threadStaticTest.cpp +++ b/Engine/source/platform/test/threadStaticTest.cpp @@ -87,5 +87,53 @@ TEST(ThreadStatic, BasicAPI) _TorqueThreadStaticReg::destroyInstance(testInstance); } +#ifdef TORQUE_ENABLE_PROFILER +#include "math/mRandom.h" +#include "platform/profiler.h" + +// Declare a test thread static +DITTS(U32, gInstancedStaticFoo, 42); +static U32 gTrueStaticFoo = 42; + +TEST(ThreadStatic, StressThreadStatic) +{ + ASSERT_FALSE(gProfiler->isEnabled()) + << "Profiler is currently enabled, test cannot continue"; + + // Spawn an instance + TorqueThreadStaticListHandle testInstance = _TorqueThreadStaticReg::spawnThreadStaticsInstance(); + + static const dsize_t TEST_SIZE = 100000; + + // What we are going to do in this test is to test some U32 static + // performance. The test will be run TEST_SIZE times, and so first create + // an array of values to standardize the tests on. + U32 testValue[TEST_SIZE]; + + for(S32 i = 0; i < TEST_SIZE; i++) + testValue[i] = gRandGen.randI(); + + // Reset the profiler, tell it to dump to console when done + gProfiler->dumpToConsole(); + gProfiler->enable(true); + + // Value array is initialized, so lets put the foo's through the paces + PROFILE_START(ThreadStaticPerf_TrueStaticAssign); + for(int i = 0; i < TEST_SIZE; i++) + gTrueStaticFoo = testValue[i]; + PROFILE_END(); + + PROFILE_START(ThreadStaticPerf_InstanceStaticAssign); + for(S32 i = 0; i < TEST_SIZE; i++) + ATTS_(gInstancedStaticFoo, 1) = testValue[i]; + PROFILE_END(); + + gProfiler->enable(false); + + // Clean up instance + _TorqueThreadStaticReg::destroyInstance(testInstance); +} +#endif + #endif #endif \ No newline at end of file diff --git a/Engine/source/unit/tests/testThreadStaticPerformance.cpp b/Engine/source/unit/tests/testThreadStaticPerformance.cpp deleted file mode 100644 index 550f0fb3c..000000000 --- a/Engine/source/unit/tests/testThreadStaticPerformance.cpp +++ /dev/null @@ -1,82 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "platform/platform.h" -#include "unit/test.h" -#include "core/threadStatic.h" -#include "math/mRandom.h" -#include "platform/profiler.h" - -using namespace UnitTesting; - -//----------------------------------------------------------------------------- -// This unit test will blow up without thread static support -#if defined(TORQUE_ENABLE_THREAD_STATICS) && defined(TORQUE_ENABLE_PROFILER) - -// Declare a test thread static -DITTS( U32, gInstancedStaticFoo, 42 ); -static U32 gTrueStaticFoo = 42; - -CreateUnitTest( TestThreadStaticPerformance, "Core/ThreadStaticPerformance" ) -{ - void run() - { - // Bail if the profiler is turned on right now - if( !test( !gProfiler->isEnabled(), "Profiler is currently enabled, test cannot continue" ) ) - return; - - // Spawn an instance - TorqueThreadStaticListHandle testInstance = _TorqueThreadStaticReg::spawnThreadStaticsInstance(); - - static const dsize_t TEST_SIZE = 100000; - - // What we are going to do in this test is to test some U32 static - // performance. The test will be run TEST_SIZE times, and so first create - // an array of values to standardize the tests on. - U32 testValue[TEST_SIZE]; - - for( S32 i = 0; i < TEST_SIZE; i++ ) - testValue[i] = gRandGen.randI(); - - // Reset the profiler, tell it to dump to console when done - gProfiler->dumpToConsole(); - gProfiler->enable( true ); - - // Value array is initialized, so lets put the foo's through the paces - PROFILE_START(ThreadStaticPerf_TrueStaticAssign); - for( int i = 0; i < TEST_SIZE; i++ ) - gTrueStaticFoo = testValue[i]; - PROFILE_END(); - - PROFILE_START(ThreadStaticPerf_InstanceStaticAssign); - for( S32 i = 0; i < TEST_SIZE; i++ ) - ATTS_( gInstancedStaticFoo, 1 ) = testValue[i]; - PROFILE_END(); - - gProfiler->enable( false ); - - // Clean up instance - _TorqueThreadStaticReg::destroyInstance( testInstance ); - } -}; - -#endif // TORQUE_ENABLE_THREAD_STATICS \ No newline at end of file From 83c8274952ea68d47f5baaa0b968d1a9c0dca531 Mon Sep 17 00:00:00 2001 From: J0linar Date: Wed, 20 Aug 2014 06:22:10 +0200 Subject: [PATCH 161/317] -added Vignette PostFx -added vignette to the Full and Empty Template -added vignette to the PostEffect Manager -added vignette Shader and .cs File --- .../scripts/client/postFx/postFXManager.gui | 49 +++++++++++++++++++ .../client/postFx/postFxManager.gui.cs | 26 +++++++++- .../postFx/postFxManager.gui.settings.cs | 31 ++++++++++++ .../core/scripts/client/postFx/vignette.cs | 49 +++++++++++++++++++ .../common/postFx/vignette/VignetteP.hlsl | 36 ++++++++++++++ .../scripts/client/postFx/postFXManager.gui | 49 +++++++++++++++++++ .../client/postFx/postFxManager.gui.cs | 26 +++++++++- .../postFx/postFxManager.gui.settings.cs | 31 ++++++++++++ .../core/scripts/client/postFx/vignette.cs | 49 +++++++++++++++++++ .../common/postFx/vignette/VignetteP.hlsl | 36 ++++++++++++++ 10 files changed, 380 insertions(+), 2 deletions(-) create mode 100644 Templates/Empty/game/core/scripts/client/postFx/vignette.cs create mode 100644 Templates/Empty/game/shaders/common/postFx/vignette/VignetteP.hlsl create mode 100644 Templates/Full/game/core/scripts/client/postFx/vignette.cs create mode 100644 Templates/Full/game/shaders/common/postFx/vignette/VignetteP.hlsl diff --git a/Templates/Empty/game/core/scripts/client/postFx/postFXManager.gui b/Templates/Empty/game/core/scripts/client/postFx/postFXManager.gui index 0f7fae83b..c97ab1c2c 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/postFXManager.gui +++ b/Templates/Empty/game/core/scripts/client/postFx/postFXManager.gui @@ -2199,6 +2199,55 @@ canSaveDynamicFields = "0"; }; }; + new GuiTabPageCtrl(ppOptionsVignetteTab) { + fitBook = "0"; + text = "Vignette"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 40"; + extent = "394 193"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Options for the Vignette postFX"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + Enabled = "1"; + + new GuiCheckBoxCtrl(ppOptionsEnableVignette) { + text = "Enable"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "329 7"; + extent = "53 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable/Disable the vignette postFX"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + Enabled = "1"; + }; + }; new GuiTabPageCtrl() { fitBook = "0"; text = "Color Correction"; diff --git a/Templates/Empty/game/core/scripts/client/postFx/postFxManager.gui.cs b/Templates/Empty/game/core/scripts/client/postFx/postFxManager.gui.cs index 4f440684d..b9e86f4e7 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/postFxManager.gui.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/postFxManager.gui.cs @@ -94,7 +94,13 @@ function ppOptionsEnableDOF::onAction(%this) %toEnable = PostFXManager.getEnableResultFromControl(%this); PostFXManager.settingsEffectSetEnabled("DOF", %toEnable); } - + +function ppOptionsEnableVignette::onAction(%this) +{ + %toEnable = PostFXManager.getEnableResultFromControl(%this); + PostFXManager.settingsEffectSetEnabled("Vignette", %toEnable); +} + function ppOptionsSavePreset::onClick(%this) { //Stores the current settings into a preset file for loading and use later on @@ -379,6 +385,24 @@ function ppOptionsEnableHDRDebug::onAction(%this) LuminanceVisPostFX.disable(); } +function ppOptionsUpdateVignetteSettings() +{ + if($PostFXManager::PostFX::EnableVignette) + { + VignettePostEffect.enable(); + } + else + { + VignettePostEffect.disable(); + } +} + +function ppOptionsVignetteEnableVignette::onAction(%this) +{ + $PostFXManager::PostFX::EnableVignette = %this.getValue(); + ppOptionsUpdateVignetteSettings(); +} + function ppColorCorrection_selectFile() { %filter = "Image Files (*.png, *.jpg, *.dds, *.bmp, *.gif, *.jng. *.tga)|*.png;*.jpg;*.dds;*.bmp;*.gif;*.jng;*.tga|All Files (*.*)|*.*|"; diff --git a/Templates/Empty/game/core/scripts/client/postFx/postFxManager.gui.settings.cs b/Templates/Empty/game/core/scripts/client/postFx/postFxManager.gui.settings.cs index 63c1b7424..a6c29f58a 100644 --- a/Templates/Empty/game/core/scripts/client/postFx/postFxManager.gui.settings.cs +++ b/Templates/Empty/game/core/scripts/client/postFx/postFxManager.gui.settings.cs @@ -50,6 +50,11 @@ function PostFXManager::settingsSetEnabled(%this, %bEnablePostFX) DOFPostEffect.enable(); else DOFPostEffect.disable(); + + if ( $PostFXManager::PostFX::EnableVignette ) + VignettePostEffect.enable(); + else + VignettePostEffect.disable(); postVerbose("% - PostFX Manager - PostFX enabled"); } @@ -61,6 +66,7 @@ function PostFXManager::settingsSetEnabled(%this, %bEnablePostFX) HDRPostFX.disable(); LightRayPostFX.disable(); DOFPostEffect.disable(); + VignettePostEffect.disable(); postVerbose("% - PostFX Manager - PostFX disabled"); } @@ -95,6 +101,12 @@ function PostFXManager::settingsEffectSetEnabled(%this, %sName, %bEnable) $PostFXManager::PostFX::EnableDOF = %bEnable; //$pref::PostFX::DOF::Enabled = %bEnable; } + else if(%sName $= "Vignette") + { + %postEffect = VignettePostEffect; + $PostFXManager::PostFX::EnableVignette = %bEnable; + //$pref::PostFX::Vignette::Enabled = %bEnable; + } // Apply the change if ( %bEnable == true ) @@ -196,6 +208,13 @@ function PostFXManager::settingsRefreshDOF(%this) } +function PostFXManager::settingsRefreshVignette(%this) +{ + //Apply the enabled flag + ppOptionsEnableVignette.setValue($PostFXManager::PostFX::EnableVignette); + +} + function PostFXManager::settingsRefreshAll(%this) { $PostFXManager::PostFX::Enabled = $pref::enablePostEffects; @@ -203,6 +222,7 @@ function PostFXManager::settingsRefreshAll(%this) $PostFXManager::PostFX::EnableHDR = HDRPostFX.isEnabled(); $PostFXManager::PostFX::EnableLightRays = LightRayPostFX.isEnabled(); $PostFXManager::PostFX::EnableDOF = DOFPostEffect.isEnabled(); + $PostFXManager::PostFX::EnableVignette = VignettePostEffect.isEnabled(); //For all the postFX here, apply the active settings in the system //to the gui controls. @@ -211,6 +231,7 @@ function PostFXManager::settingsRefreshAll(%this) %this.settingsRefreshHDR(); %this.settingsRefreshLightrays(); %this.settingsRefreshDOF(); + %this.settingsRefreshVignette(); ppOptionsEnable.setValue($PostFXManager::PostFX::Enabled); @@ -272,6 +293,7 @@ function PostFXManager::settingsApplyFromPreset(%this) { $PostFXManager::PostFX::Enabled = $PostFXManager::Settings::EnablePostFX; $PostFXManager::PostFX::EnableDOF = $PostFXManager::Settings::EnableDOF; + $PostFXManager::PostFX::EnableVignette = $PostFXManager::Settings::EnableVignette; $PostFXManager::PostFX::EnableLightRays = $PostFXManager::Settings::EnableLightRays; $PostFXManager::PostFX::EnableHDR = $PostFXManager::Settings::EnableHDR; $PostFXManager::PostFX::EnableSSAO = $PostFXManager::Settings::EnabledSSAO; @@ -353,11 +375,18 @@ function PostFXManager::settingsApplyDOF(%this) } +function PostFXManager::settingsApplyVignette(%this) +{ + postVerbose("% - PostFX Manager - Settings Saved - Vignette"); + +} + function PostFXManager::settingsApplyAll(%this, %sFrom) { // Apply settings which control if effects are on/off altogether. $PostFXManager::Settings::EnablePostFX = $PostFXManager::PostFX::Enabled; $PostFXManager::Settings::EnableDOF = $PostFXManager::PostFX::EnableDOF; + $PostFXManager::Settings::EnableVignette = $PostFXManager::PostFX::EnableVignette; $PostFXManager::Settings::EnableLightRays = $PostFXManager::PostFX::EnableLightRays; $PostFXManager::Settings::EnableHDR = $PostFXManager::PostFX::EnableHDR; $PostFXManager::Settings::EnabledSSAO = $PostFXManager::PostFX::EnableSSAO; @@ -373,6 +402,8 @@ function PostFXManager::settingsApplyAll(%this, %sFrom) %this.settingsApplyLightRays(); // DOF %this.settingsApplyDOF(); + // Vignette + %this.settingsApplyVignette(); postVerbose("% - PostFX Manager - All Settings applied to $PostFXManager::Settings"); } diff --git a/Templates/Empty/game/core/scripts/client/postFx/vignette.cs b/Templates/Empty/game/core/scripts/client/postFx/vignette.cs new file mode 100644 index 000000000..86b8ede13 --- /dev/null +++ b/Templates/Empty/game/core/scripts/client/postFx/vignette.cs @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +$VignettePostEffect::RadiusX = 0.6; +$VignettePostEffect::RadiusY = 0.2; + +singleton ShaderData( VignetteShader ) +{ + DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; + DXPixelShaderFile = "shaders/common/postFx/vignette/VignetteP.hlsl"; + pixVersion = 2.0; +}; + +singleton PostEffect( VignettePostEffect ) +{ + isEnabled = false; + allowReflectPass = false; + renderTime = "PFXAfterBin"; + renderBin = "GlowBin"; + shader = VignetteShader; + stateBlock = PFX_DefaultStateBlock; + texture[0] = "$backBuffer"; + renderPriority = 10; +}; + +function VignettePostEffect::setShaderConsts(%this) +{ + %this.setShaderConst("$radiusX", $VignettePostEffect::RadiusX); + %this.setShaderConst("$radiusY", $VignettePostEffect::RadiusY); +} \ No newline at end of file diff --git a/Templates/Empty/game/shaders/common/postFx/vignette/VignetteP.hlsl b/Templates/Empty/game/shaders/common/postFx/vignette/VignetteP.hlsl new file mode 100644 index 000000000..c67c7ed2b --- /dev/null +++ b/Templates/Empty/game/shaders/common/postFx/vignette/VignetteP.hlsl @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "../postFx.hlsl" +#include "shadergen:/autogenConditioners.h" + +uniform sampler2D backBuffer : register(S0); +uniform float radiusX; +uniform float radiusY; + +float4 main(PFXVertToPix IN) : COLOR0 +{ + float4 base = tex2D(backBuffer, IN.uv0); + float dist = distance(IN.uv0, float2(0.5,0.5)) * 0.7 ; + base.rgb *= smoothstep(radiusX, radiusY, dist); + return base; +} \ No newline at end of file diff --git a/Templates/Full/game/core/scripts/client/postFx/postFXManager.gui b/Templates/Full/game/core/scripts/client/postFx/postFXManager.gui index 0f7fae83b..c97ab1c2c 100644 --- a/Templates/Full/game/core/scripts/client/postFx/postFXManager.gui +++ b/Templates/Full/game/core/scripts/client/postFx/postFXManager.gui @@ -2199,6 +2199,55 @@ canSaveDynamicFields = "0"; }; }; + new GuiTabPageCtrl(ppOptionsVignetteTab) { + fitBook = "0"; + text = "Vignette"; + maxLength = "1024"; + docking = "Client"; + margin = "0 0 0 0"; + padding = "0 0 0 0"; + anchorTop = "1"; + anchorBottom = "1"; + anchorLeft = "1"; + anchorRight = "1"; + position = "0 40"; + extent = "394 193"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiTabPageProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Options for the Vignette postFX"; + hovertime = "1000"; + isContainer = "1"; + canSave = "1"; + canSaveDynamicFields = "0"; + Enabled = "1"; + + new GuiCheckBoxCtrl(ppOptionsEnableVignette) { + text = "Enable"; + groupNum = "-1"; + buttonType = "ToggleButton"; + useMouseEvents = "0"; + position = "329 7"; + extent = "53 20"; + minExtent = "8 2"; + horizSizing = "right"; + vertSizing = "bottom"; + profile = "GuiCheckBoxProfile"; + visible = "1"; + active = "1"; + tooltipProfile = "GuiToolTipProfile"; + tooltip = "Enable/Disable the vignette postFX"; + hovertime = "1000"; + isContainer = "0"; + canSave = "1"; + canSaveDynamicFields = "0"; + Enabled = "1"; + }; + }; new GuiTabPageCtrl() { fitBook = "0"; text = "Color Correction"; diff --git a/Templates/Full/game/core/scripts/client/postFx/postFxManager.gui.cs b/Templates/Full/game/core/scripts/client/postFx/postFxManager.gui.cs index 4f440684d..b9e86f4e7 100644 --- a/Templates/Full/game/core/scripts/client/postFx/postFxManager.gui.cs +++ b/Templates/Full/game/core/scripts/client/postFx/postFxManager.gui.cs @@ -94,7 +94,13 @@ function ppOptionsEnableDOF::onAction(%this) %toEnable = PostFXManager.getEnableResultFromControl(%this); PostFXManager.settingsEffectSetEnabled("DOF", %toEnable); } - + +function ppOptionsEnableVignette::onAction(%this) +{ + %toEnable = PostFXManager.getEnableResultFromControl(%this); + PostFXManager.settingsEffectSetEnabled("Vignette", %toEnable); +} + function ppOptionsSavePreset::onClick(%this) { //Stores the current settings into a preset file for loading and use later on @@ -379,6 +385,24 @@ function ppOptionsEnableHDRDebug::onAction(%this) LuminanceVisPostFX.disable(); } +function ppOptionsUpdateVignetteSettings() +{ + if($PostFXManager::PostFX::EnableVignette) + { + VignettePostEffect.enable(); + } + else + { + VignettePostEffect.disable(); + } +} + +function ppOptionsVignetteEnableVignette::onAction(%this) +{ + $PostFXManager::PostFX::EnableVignette = %this.getValue(); + ppOptionsUpdateVignetteSettings(); +} + function ppColorCorrection_selectFile() { %filter = "Image Files (*.png, *.jpg, *.dds, *.bmp, *.gif, *.jng. *.tga)|*.png;*.jpg;*.dds;*.bmp;*.gif;*.jng;*.tga|All Files (*.*)|*.*|"; diff --git a/Templates/Full/game/core/scripts/client/postFx/postFxManager.gui.settings.cs b/Templates/Full/game/core/scripts/client/postFx/postFxManager.gui.settings.cs index 63c1b7424..a6c29f58a 100644 --- a/Templates/Full/game/core/scripts/client/postFx/postFxManager.gui.settings.cs +++ b/Templates/Full/game/core/scripts/client/postFx/postFxManager.gui.settings.cs @@ -50,6 +50,11 @@ function PostFXManager::settingsSetEnabled(%this, %bEnablePostFX) DOFPostEffect.enable(); else DOFPostEffect.disable(); + + if ( $PostFXManager::PostFX::EnableVignette ) + VignettePostEffect.enable(); + else + VignettePostEffect.disable(); postVerbose("% - PostFX Manager - PostFX enabled"); } @@ -61,6 +66,7 @@ function PostFXManager::settingsSetEnabled(%this, %bEnablePostFX) HDRPostFX.disable(); LightRayPostFX.disable(); DOFPostEffect.disable(); + VignettePostEffect.disable(); postVerbose("% - PostFX Manager - PostFX disabled"); } @@ -95,6 +101,12 @@ function PostFXManager::settingsEffectSetEnabled(%this, %sName, %bEnable) $PostFXManager::PostFX::EnableDOF = %bEnable; //$pref::PostFX::DOF::Enabled = %bEnable; } + else if(%sName $= "Vignette") + { + %postEffect = VignettePostEffect; + $PostFXManager::PostFX::EnableVignette = %bEnable; + //$pref::PostFX::Vignette::Enabled = %bEnable; + } // Apply the change if ( %bEnable == true ) @@ -196,6 +208,13 @@ function PostFXManager::settingsRefreshDOF(%this) } +function PostFXManager::settingsRefreshVignette(%this) +{ + //Apply the enabled flag + ppOptionsEnableVignette.setValue($PostFXManager::PostFX::EnableVignette); + +} + function PostFXManager::settingsRefreshAll(%this) { $PostFXManager::PostFX::Enabled = $pref::enablePostEffects; @@ -203,6 +222,7 @@ function PostFXManager::settingsRefreshAll(%this) $PostFXManager::PostFX::EnableHDR = HDRPostFX.isEnabled(); $PostFXManager::PostFX::EnableLightRays = LightRayPostFX.isEnabled(); $PostFXManager::PostFX::EnableDOF = DOFPostEffect.isEnabled(); + $PostFXManager::PostFX::EnableVignette = VignettePostEffect.isEnabled(); //For all the postFX here, apply the active settings in the system //to the gui controls. @@ -211,6 +231,7 @@ function PostFXManager::settingsRefreshAll(%this) %this.settingsRefreshHDR(); %this.settingsRefreshLightrays(); %this.settingsRefreshDOF(); + %this.settingsRefreshVignette(); ppOptionsEnable.setValue($PostFXManager::PostFX::Enabled); @@ -272,6 +293,7 @@ function PostFXManager::settingsApplyFromPreset(%this) { $PostFXManager::PostFX::Enabled = $PostFXManager::Settings::EnablePostFX; $PostFXManager::PostFX::EnableDOF = $PostFXManager::Settings::EnableDOF; + $PostFXManager::PostFX::EnableVignette = $PostFXManager::Settings::EnableVignette; $PostFXManager::PostFX::EnableLightRays = $PostFXManager::Settings::EnableLightRays; $PostFXManager::PostFX::EnableHDR = $PostFXManager::Settings::EnableHDR; $PostFXManager::PostFX::EnableSSAO = $PostFXManager::Settings::EnabledSSAO; @@ -353,11 +375,18 @@ function PostFXManager::settingsApplyDOF(%this) } +function PostFXManager::settingsApplyVignette(%this) +{ + postVerbose("% - PostFX Manager - Settings Saved - Vignette"); + +} + function PostFXManager::settingsApplyAll(%this, %sFrom) { // Apply settings which control if effects are on/off altogether. $PostFXManager::Settings::EnablePostFX = $PostFXManager::PostFX::Enabled; $PostFXManager::Settings::EnableDOF = $PostFXManager::PostFX::EnableDOF; + $PostFXManager::Settings::EnableVignette = $PostFXManager::PostFX::EnableVignette; $PostFXManager::Settings::EnableLightRays = $PostFXManager::PostFX::EnableLightRays; $PostFXManager::Settings::EnableHDR = $PostFXManager::PostFX::EnableHDR; $PostFXManager::Settings::EnabledSSAO = $PostFXManager::PostFX::EnableSSAO; @@ -373,6 +402,8 @@ function PostFXManager::settingsApplyAll(%this, %sFrom) %this.settingsApplyLightRays(); // DOF %this.settingsApplyDOF(); + // Vignette + %this.settingsApplyVignette(); postVerbose("% - PostFX Manager - All Settings applied to $PostFXManager::Settings"); } diff --git a/Templates/Full/game/core/scripts/client/postFx/vignette.cs b/Templates/Full/game/core/scripts/client/postFx/vignette.cs new file mode 100644 index 000000000..86b8ede13 --- /dev/null +++ b/Templates/Full/game/core/scripts/client/postFx/vignette.cs @@ -0,0 +1,49 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +$VignettePostEffect::RadiusX = 0.6; +$VignettePostEffect::RadiusY = 0.2; + +singleton ShaderData( VignetteShader ) +{ + DXVertexShaderFile = "shaders/common/postFx/postFxV.hlsl"; + DXPixelShaderFile = "shaders/common/postFx/vignette/VignetteP.hlsl"; + pixVersion = 2.0; +}; + +singleton PostEffect( VignettePostEffect ) +{ + isEnabled = false; + allowReflectPass = false; + renderTime = "PFXAfterBin"; + renderBin = "GlowBin"; + shader = VignetteShader; + stateBlock = PFX_DefaultStateBlock; + texture[0] = "$backBuffer"; + renderPriority = 10; +}; + +function VignettePostEffect::setShaderConsts(%this) +{ + %this.setShaderConst("$radiusX", $VignettePostEffect::RadiusX); + %this.setShaderConst("$radiusY", $VignettePostEffect::RadiusY); +} \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/postFx/vignette/VignetteP.hlsl b/Templates/Full/game/shaders/common/postFx/vignette/VignetteP.hlsl new file mode 100644 index 000000000..c67c7ed2b --- /dev/null +++ b/Templates/Full/game/shaders/common/postFx/vignette/VignetteP.hlsl @@ -0,0 +1,36 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "../postFx.hlsl" +#include "shadergen:/autogenConditioners.h" + +uniform sampler2D backBuffer : register(S0); +uniform float radiusX; +uniform float radiusY; + +float4 main(PFXVertToPix IN) : COLOR0 +{ + float4 base = tex2D(backBuffer, IN.uv0); + float dist = distance(IN.uv0, float2(0.5,0.5)) * 0.7 ; + base.rgb *= smoothstep(radiusX, radiusY, dist); + return base; +} \ No newline at end of file From 9612d3c290b0d0d5f2b224c7a075e8d7541ef5ee Mon Sep 17 00:00:00 2001 From: Lukas Joergensen Date: Mon, 25 Aug 2014 21:00:40 +0200 Subject: [PATCH 162/317] BlendTotal is now an actual total rather than a max --- Engine/source/terrain/hlsl/terrFeatureHLSL.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp b/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp index 6c94e0743..e5025cf30 100644 --- a/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp +++ b/Engine/source/terrain/hlsl/terrFeatureHLSL.cpp @@ -446,7 +446,7 @@ void TerrainDetailMapFeatHLSL::processPix( Vector &component } // Add to the blend total. - meta->addStatement( new GenOp( " @ = max( @, @ );\r\n", blendTotal, blendTotal, detailBlend ) ); + meta->addStatement( new GenOp( " @ += @;\r\n", blendTotal, detailBlend ) ); // If we had a parallax feature... then factor in the parallax // amount so that it fades out with the layer blending. From 53f241974bf425f94e76dedbcd8d86a8d9b52658 Mon Sep 17 00:00:00 2001 From: Lukas Joergensen Date: Mon, 25 Aug 2014 21:02:28 +0200 Subject: [PATCH 163/317] Doing a hard compare instead of using step for blending factors --- Templates/Empty/game/shaders/common/terrain/terrain.hlsl | 7 +++++-- Templates/Full/game/shaders/common/terrain/terrain.hlsl | 7 +++++-- 2 files changed, 10 insertions(+), 4 deletions(-) diff --git a/Templates/Empty/game/shaders/common/terrain/terrain.hlsl b/Templates/Empty/game/shaders/common/terrain/terrain.hlsl index 328acae7f..8ce497012 100644 --- a/Templates/Empty/game/shaders/common/terrain/terrain.hlsl +++ b/Templates/Empty/game/shaders/common/terrain/terrain.hlsl @@ -32,9 +32,12 @@ float calcBlend( float texId, float2 layerCoord, float layerSize, float4 layerSa float4 diff = saturate( abs( layerSample - texId ) ); float noBlend = any( 1 - diff ); - // Use step to see if any of the layer samples + // Check if any of the layer samples // match the current texture id. - float4 factors = step( texId, layerSample ); + float4 factors = 0; + for(int i = 0; i < 4; i++) + if(layerSample[i] == texId) + factors[i] = 1; // This is a custom bilinear filter. diff --git a/Templates/Full/game/shaders/common/terrain/terrain.hlsl b/Templates/Full/game/shaders/common/terrain/terrain.hlsl index 328acae7f..8ce497012 100644 --- a/Templates/Full/game/shaders/common/terrain/terrain.hlsl +++ b/Templates/Full/game/shaders/common/terrain/terrain.hlsl @@ -32,9 +32,12 @@ float calcBlend( float texId, float2 layerCoord, float layerSize, float4 layerSa float4 diff = saturate( abs( layerSample - texId ) ); float noBlend = any( 1 - diff ); - // Use step to see if any of the layer samples + // Check if any of the layer samples // match the current texture id. - float4 factors = step( texId, layerSample ); + float4 factors = 0; + for(int i = 0; i < 4; i++) + if(layerSample[i] == texId) + factors[i] = 1; // This is a custom bilinear filter. From 1829deb4323786c6879826f12026b752409e0026 Mon Sep 17 00:00:00 2001 From: rextimmy Date: Wed, 27 Aug 2014 11:55:59 +1000 Subject: [PATCH 164/317] - Added check in tsMesh::createTangents to check size of incoming normals --- Engine/source/ts/tsMesh.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/Engine/source/ts/tsMesh.cpp b/Engine/source/ts/tsMesh.cpp index f1af288f1..6648ae931 100644 --- a/Engine/source/ts/tsMesh.cpp +++ b/Engine/source/ts/tsMesh.cpp @@ -2949,7 +2949,11 @@ inline void TSMesh::findTangent( U32 index1, void TSMesh::createTangents(const Vector &_verts, const Vector &_norms) { U32 numVerts = _verts.size(); - if ( numVerts == 0 ) + U32 numNorms = _norms.size(); + if ( numVerts <= 0 || numNorms <= 0 ) + return; + + if( numVerts != numNorms) return; Vector tan0; From 93325df0c45d11f74a82b51c9d13fc2bf6b27f10 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 31 Aug 2014 22:21:21 +1000 Subject: [PATCH 165/317] Added thread safe container tests. --- .../threads/test/threadSafeDequeTest.cpp | 181 ++++++++++++++++++ .../test/threadSafePriorityQueueTest.cpp | 146 ++++++++++++++ 2 files changed, 327 insertions(+) create mode 100644 Engine/source/platform/threads/test/threadSafeDequeTest.cpp create mode 100644 Engine/source/platform/threads/test/threadSafePriorityQueueTest.cpp diff --git a/Engine/source/platform/threads/test/threadSafeDequeTest.cpp b/Engine/source/platform/threads/test/threadSafeDequeTest.cpp new file mode 100644 index 000000000..183c31d3f --- /dev/null +++ b/Engine/source/platform/threads/test/threadSafeDequeTest.cpp @@ -0,0 +1,181 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" + +#include "platform/threads/threadSafeDeque.h" +#include "platform/threads/thread.h" +#include "core/util/tVector.h" +#include "console/console.h" + +// Test deque without concurrency. +TEST(ThreadSafeDeque, PopFront) +{ + ThreadSafeDeque deque; + String str = "teststring"; + + for(U32 i = 0; i < str.length(); i++) + deque.pushBack(str[i]); + + EXPECT_FALSE(deque.isEmpty()); + + char ch; + for(U32 i = 0; i < str.length(); i++) + { + EXPECT_TRUE(deque.tryPopFront(ch)); + EXPECT_EQ(str[i], ch); + } + + ASSERT_TRUE(deque.isEmpty()); +} + +TEST(ThreadSafeDeque, PopBack) +{ + ThreadSafeDeque deque; + String str = "teststring"; + + const char* p1 = str.c_str() + 4; + const char* p2 = p1 + 1; + while(*p2) + { + deque.pushFront(*p1); + deque.pushBack(*p2); + --p1; + ++p2; + } + + char ch; + for(S32 i = str.length()-1; i >= 0; i--) + { + EXPECT_TRUE(deque.tryPopBack(ch)); + EXPECT_EQ(str[i], ch); + } + + ASSERT_TRUE(deque.isEmpty()); +} + +// Test deque in a concurrent setting. +TEST(ThreadSafeDeque, Concurrent1) +{ + const U32 NumValues = 100; + + struct Value : public ThreadSafeRefCount + { + U32 mIndex; + U32 mTick; + + Value() {} + Value(U32 index, U32 tick) + : mIndex(index), mTick(tick) {} + }; + + typedef ThreadSafeRef ValueRef; + + struct Deque : public ThreadSafeDeque + { + typedef ThreadSafeDeque Parent; + + U32 mPushIndex; + U32 mPopIndex; + + Deque() + : mPushIndex(0), mPopIndex(0) {} + + void pushBack(const ValueRef& value) + { + EXPECT_EQ(value->mIndex, mPushIndex) << "index out of line"; + mPushIndex++; + Parent::pushBack(value); + } + + bool tryPopFront(ValueRef& outValue) + { + if(Parent::tryPopFront(outValue)) + { + EXPECT_EQ(outValue->mIndex, mPopIndex) << "index out of line"; + mPopIndex++; + return true; + } + else + return false; + } + }; + + Deque mDeque; + Vector mValues; + + struct ProducerThread : public Thread + { + Vector& mValues; + Deque& mDeque; + ProducerThread(Vector& values, Deque& deque) + : mValues(values), mDeque(deque) {} + + virtual void run(void*) + { + for(U32 i = 0; i < mValues.size(); i++) + { + U32 tick = Platform::getRealMilliseconds(); + mValues[i] = tick; + + ValueRef val = new Value(i, tick); + mDeque.pushBack(val); + } + } + }; + + struct ConsumerThread : public Thread + { + Vector& mValues; + Deque& mDeque; + ConsumerThread(Vector& values, Deque& deque) + : mValues(values), mDeque(deque) {} + + virtual void run(void*) + { + for(U32 i = 0; i < mValues.size(); i++) + { + ValueRef value; + while(!mDeque.tryPopFront(value)); + + EXPECT_EQ(i, value->mIndex); + EXPECT_EQ(value->mTick, mValues[i]); + } + } + }; + + mValues.setSize(NumValues); + + ProducerThread pThread(mValues, mDeque); + ConsumerThread cThread(mValues, mDeque); + + pThread.start(); + cThread.start(); + + pThread.join(); + cThread.join(); + + mValues.clear(); +}; + +#endif \ No newline at end of file diff --git a/Engine/source/platform/threads/test/threadSafePriorityQueueTest.cpp b/Engine/source/platform/threads/test/threadSafePriorityQueueTest.cpp new file mode 100644 index 000000000..668117633 --- /dev/null +++ b/Engine/source/platform/threads/test/threadSafePriorityQueueTest.cpp @@ -0,0 +1,146 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "platform/threads/threadSafePriorityQueue.h" +#include "platform/threads/thread.h" +#include "core/util/tVector.h" +#include "console/console.h" + +// Test queue without concurrency. +TEST(ThreadSafePriorityQueue, Serial) +{ + const U32 min = 0; + const U32 max = 9; + const U32 len = 11; + + U32 indices[len] = { 2, 7, 4, 6, 1, 5, 3, 8, 6, 9, 0}; + F32 priorities[len] = {0.2, 0.7, 0.4, 0.6, 0.1, 0.5, 0.3, 0.8, 0.6, 0.9, 0}; + + ThreadSafePriorityQueue minQueue; + ThreadSafePriorityQueue maxQueue; + + for(U32 i = 0; i < len; i++) + { + minQueue.insert(priorities[i], indices[i]); + maxQueue.insert(priorities[i], indices[i]); + } + + EXPECT_FALSE(minQueue.isEmpty()); + EXPECT_FALSE(maxQueue.isEmpty()); + + U32 index = min; + for(U32 i = 0; i < len; i++) + { + U32 popped; + EXPECT_TRUE(minQueue.takeNext(popped)) + << "Failed to pop element from minQueue"; + EXPECT_LE(index, popped) + << "Element from minQueue was not in sort order"; + index = popped; + } + + index = max; + for(U32 i = 0; i < len; i++) + { + U32 popped; + EXPECT_TRUE(maxQueue.takeNext(popped)) + << "Failed to pop element from maxQueue"; + EXPECT_GE(index, popped) + << "Element from maxQueue was not in sort order"; + index = popped; + } +} + +// Test queue with concurrency. +TEST(ThreadSafePriorityQueue, Concurrent) +{ +#define MIN 0 +#define MAX 9 +#define LEN 11 + + typedef ThreadSafePriorityQueue MinQueue; + typedef ThreadSafePriorityQueue MaxQueue; + + struct ProducerThread : public Thread + { + MinQueue& minQueue; + MaxQueue& maxQueue; + ProducerThread(MinQueue& min, MaxQueue& max) + : minQueue(min), maxQueue(max) {} + + virtual void run(void*) + { + U32 indices[LEN] = { 2, 7, 4, 6, 1, 5, 3, 8, 6, 9, 0}; + F32 priorities[LEN] = {0.2, 0.7, 0.4, 0.6, 0.1, 0.5, 0.3, 0.8, 0.6, 0.9, 0}; + + for(U32 i = 0; i < LEN; i++) + { + minQueue.insert(priorities[i], indices[i]); + maxQueue.insert(priorities[i], indices[i]); + } + } + }; + + MinQueue minQueue; + MaxQueue maxQueue; + ProducerThread producers[] = { + ProducerThread(minQueue, maxQueue), + ProducerThread(minQueue, maxQueue), + ProducerThread(minQueue, maxQueue) + }; + + const U32 len = sizeof(producers) / sizeof(ProducerThread); + for(U32 i = 0; i < len; i++) + producers[i].start(); + for(U32 i = 0; i < len; i++) + producers[i].join(); + + U32 index = MIN; + for(U32 i = 0; i < LEN * len; i++) + { + U32 popped; + EXPECT_TRUE(minQueue.takeNext(popped)) + << "Failed to pop element from minQueue"; + EXPECT_LE(index, popped) + << "Element from minQueue was not in sort order"; + index = popped; + } + + index = MAX; + for(U32 i = 0; i < LEN * len; i++) + { + U32 popped; + EXPECT_TRUE(maxQueue.takeNext(popped)) + << "Failed to pop element from maxQueue"; + EXPECT_GE(index, popped) + << "Element from maxQueue was not in sort order"; + index = popped; + } + +#undef MIN +#undef MAX +#undef LEN +} + +#endif \ No newline at end of file From b34afee97914686ad9a388db7fcc96fefe5f8424 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 31 Aug 2014 22:21:42 +1000 Subject: [PATCH 166/317] Test Thread inheritance as well as callbacks. --- .../platform/threads/test/threadTest.cpp | 35 +++++++++++++++++-- 1 file changed, 33 insertions(+), 2 deletions(-) diff --git a/Engine/source/platform/threads/test/threadTest.cpp b/Engine/source/platform/threads/test/threadTest.cpp index 9ff5af3aa..2c5015686 100644 --- a/Engine/source/platform/threads/test/threadTest.cpp +++ b/Engine/source/platform/threads/test/threadTest.cpp @@ -24,7 +24,7 @@ #include "testing/unitTesting.h" #include "platform/threads/thread.h" -TEST(Thread, BasicAPI) +TEST(Thread, CallbackAPI) { #define VALUE_TO_SET 10 @@ -51,6 +51,37 @@ TEST(Thread, BasicAPI) << "Thread did not set expected value!"; #undef VALUE_TO_SET -}; +} + +TEST(Thread, InheritanceAPI) +{ +#define VALUE_TO_SET 10 + + // This struct exists just so we can define run as a local function. + struct thread : public Thread + { + U32* mPtr; + thread(U32* ptr): mPtr(ptr) {} + + // Do some work we can observe. + virtual void run(void*) + { + *mPtr = VALUE_TO_SET; + } + }; + + // Test most basic Thread API functions. + U32 value = ~VALUE_TO_SET; + thread thread(&value); + thread.start(); + EXPECT_TRUE(thread.isAlive()); + thread.join(); + EXPECT_FALSE(thread.isAlive()); + + EXPECT_EQ(value, VALUE_TO_SET) + << "Thread did not set expected value!"; + +#undef VALUE_TO_SET +} #endif \ No newline at end of file From 7df5684af190fc2cb283fd688dfd49f67cb4d508 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Mon, 1 Sep 2014 07:58:19 +1000 Subject: [PATCH 167/317] Removed old tests. --- .../platform/test/testThreadSafeDeque.cpp | 403 ------------------ .../test/testThreadSafePriorityQueue.cpp | 245 ----------- 2 files changed, 648 deletions(-) delete mode 100644 Engine/source/platform/test/testThreadSafeDeque.cpp delete mode 100644 Engine/source/platform/test/testThreadSafePriorityQueue.cpp diff --git a/Engine/source/platform/test/testThreadSafeDeque.cpp b/Engine/source/platform/test/testThreadSafeDeque.cpp deleted file mode 100644 index 8b3e019a4..000000000 --- a/Engine/source/platform/test/testThreadSafeDeque.cpp +++ /dev/null @@ -1,403 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "unit/test.h" -#include "platform/threads/threadSafeDeque.h" -#include "platform/threads/thread.h" -#include "core/util/tVector.h" -#include "console/console.h" - - -#ifndef TORQUE_SHIPPING - -using namespace UnitTesting; - -#define TEST( x ) test( ( x ), "FAIL: " #x ) -#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x ) - - -// Test deque without concurrency. - -CreateUnitTest( TestThreadSafeDequeSerial, "Platform/ThreadSafeDeque/Serial" ) -{ - void test1() - { - ThreadSafeDeque< char > deque; - String str = "teststring"; - - for( U32 i = 0; i < str.length(); ++ i ) - deque.pushBack( str[ i ] ); - - TEST( !deque.isEmpty() ); - - for( U32 i = 0; i < str.length(); ++ i ) - { - char ch; - TEST( deque.tryPopFront( ch ) && ch == str[ i ] ); - } - } - - void test2() - { - ThreadSafeDeque< char > deque; - String str = "teststring"; - - const char* p1 = str.c_str() + 4; - const char* p2 = p1 + 1; - while( *p2 ) - { - deque.pushFront( *p1 ); - deque.pushBack( *p2 ); - - -- p1; - ++ p2; - } - -#ifdef TORQUE_DEBUG - deque.dumpDebug(); -#endif - - for( U32 i = 0; i < str.length(); ++ i ) - { - char ch; - TEST( deque.tryPopFront( ch ) && ch == str[ i ] ); - } - } - - void test3() - { - ThreadSafeDeque< char > deque; - String str = "teststring"; - - const char* p1 = str.c_str() + 4; - const char* p2 = p1 + 1; - while( *p2 ) - { - deque.pushFront( *p1 ); - deque.pushBack( *p2 ); - - -- p1; - ++ p2; - } - -#ifdef TORQUE_DEBUG - deque.dumpDebug(); -#endif - - for( S32 i = ( str.length() - 1 ); i >= 0; -- i ) - { - char ch; - TEST( deque.tryPopBack( ch ) && ch == str[ i ] ); - } - } - - void test4() - { - ThreadSafeDeque< char > deque; - char ch; - - TEST( deque.isEmpty() ); - - deque.pushFront( 'a' ); - TEST( !deque.isEmpty() ); - TEST( deque.tryPopFront( ch ) ); - TEST( ch == 'a' ); - - deque.pushBack( 'a' ); - TEST( !deque.isEmpty() ); - TEST( deque.tryPopFront( ch ) ); - TEST( ch == 'a' ); - - deque.pushBack( 'a' ); - TEST( !deque.isEmpty() ); - TEST( deque.tryPopBack( ch ) ); - TEST( ch == 'a' ); - - deque.pushFront( 'a' ); - TEST( !deque.isEmpty() ); - TEST( deque.tryPopBack( ch ) ); - TEST( ch == 'a' ); - } - - void run() - { - test1(); - test2(); - test3(); - test4(); - } -}; - -// Test deque in a concurrent setting. - -CreateUnitTest( TestThreadSafeDequeConcurrentSimple, "Platform/ThreadSafeDeque/ConcurrentSimple" ) -{ -public: - typedef TestThreadSafeDequeConcurrentSimple TestType; - - enum - { - DEFAULT_NUM_VALUES = 100000, - }; - - struct Value : public ThreadSafeRefCount< Value > - { - U32 mIndex; - U32 mTick; - - Value() {} - Value( U32 index, U32 tick ) - : mIndex( index ), mTick( tick ) {} - }; - - typedef ThreadSafeRef< Value > ValueRef; - - struct Deque : public ThreadSafeDeque< ValueRef > - { - typedef ThreadSafeDeque Parent; - - U32 mPushIndex; - U32 mPopIndex; - - Deque() - : mPushIndex( 0 ), mPopIndex( 0 ) {} - - void pushBack( const ValueRef& value ) - { - AssertFatal( value->mIndex == mPushIndex, "index out of line" ); - mPushIndex ++; - Parent::pushBack( value ); - } - bool tryPopFront( ValueRef& outValue ) - { - if( Parent::tryPopFront( outValue ) ) - { - AssertFatal( outValue->mIndex == mPopIndex, "index out of line" ); - mPopIndex ++; - return true; - } - else - return false; - } - }; - - Deque mDeque; - Vector< U32 > mValues; - - struct ProducerThread : public Thread - { - ProducerThread( TestType* test ) - : Thread( 0, test ) {} - - virtual void run( void* arg ) - { - _setName( "ProducerThread" ); - Platform::outputDebugString( "Starting ProducerThread" ); - - TestType* test = ( TestType* ) arg; - - for( U32 i = 0; i < test->mValues.size(); ++ i ) - { - U32 tick = Platform::getRealMilliseconds(); - test->mValues[ i ] = tick; - - ValueRef val = new Value( i, tick ); - test->mDeque.pushBack( val ); - } - Platform::outputDebugString( "Stopping ProducerThread" ); - } - }; - struct ConsumerThread : public Thread - { - ConsumerThread( TestType* test ) - : Thread( 0, test ) {} - - virtual void run( void* arg ) - { - _setName( "ConsumerThread" ); - Platform::outputDebugString( "Starting CosumerThread" ); - TestType* t = ( TestType* ) arg; - - for( U32 i = 0; i < t->mValues.size(); ++ i ) - { - ValueRef value; - while( !t->mDeque.tryPopFront( value ) ); - - XTEST( t, value->mIndex == i ); - XTEST( t, t->mValues[ i ] == value->mTick ); - } - Platform::outputDebugString( "Stopping ConsumerThread" ); - } - }; - - void run() - { - U32 numValues = Con::getIntVariable( "$testThreadSafeDeque::numValues", DEFAULT_NUM_VALUES ); - mValues.setSize( numValues ); - - ProducerThread pThread( this ); - ConsumerThread cThread( this ); - - pThread.start(); - cThread.start(); - - pThread.join(); - cThread.join(); - - mValues.clear(); - } -}; - -CreateUnitTest( TestThreadSafeDequeConcurrent, "Platform/ThreadSafeDeque/Concurrent" ) -{ -public: - typedef TestThreadSafeDequeConcurrent TestType; - - enum - { - DEFAULT_NUM_VALUES = 100000, - DEFAULT_NUM_CONSUMERS = 10, - DEFAULT_NUM_PRODUCERS = 10 - }; - - struct Value : public ThreadSafeRefCount< Value > - { - U32 mIndex; - U32 mTick; - - Value() {} - Value( U32 index, U32 tick ) - : mIndex( index ), mTick( tick ) {} - }; - - typedef ThreadSafeRef< Value > ValueRef; - - U32 mProducerIndex; - U32 mConsumerIndex; - ThreadSafeDeque< ValueRef > mDeque; - Vector< U32 > mValues; - - struct ProducerThread : public Thread - { - ProducerThread( TestType* test ) - : Thread( 0, test ) {} - - virtual void run( void* arg ) - { - _setName( "ProducerThread" ); - Platform::outputDebugString( "Starting ProducerThread" ); - TestType* test = ( TestType* ) arg; - - while( 1 ) - { - U32 index = test->mProducerIndex; - if( index == test->mValues.size() ) - break; - - if( dCompareAndSwap( test->mProducerIndex, index, index + 1 ) ) - { - U32 tick = Platform::getRealMilliseconds(); - test->mValues[ index ] = tick; - - ValueRef val = new Value( index, tick ); - test->mDeque.pushBack( val ); - } - } - Platform::outputDebugString( "Stopping ProducerThread" ); - } - }; - struct ConsumerThread : public Thread - { - ConsumerThread( TestType* test ) - : Thread( 0, test ) {} - - virtual void run( void* arg ) - { - _setName( "ConsumerThread" ); - Platform::outputDebugString( "Starting ConsumerThread" ); - TestType* t = ( TestType* ) arg; - - while( t->mConsumerIndex < t->mValues.size() ) - { - ValueRef value; - if( t->mDeque.tryPopFront( value ) ) - { - dFetchAndAdd( t->mConsumerIndex, 1 ); - XTEST( t, t->mValues[ value->mIndex ] == value->mTick ); - t->mValues[ value->mIndex ] = 0; - } - } - - Platform::outputDebugString( "Stopping ConsumerThread" ); - } - }; - - void run() - { - U32 numValues = Con::getIntVariable( "$testThreadSafeDeque::numValues", DEFAULT_NUM_VALUES ); - U32 numConsumers = Con::getIntVariable( "$testThreadSafeDeque::numConsumers", DEFAULT_NUM_CONSUMERS ); - U32 numProducers = Con::getIntVariable( "$testThreadSafeDeque::numProducers", DEFAULT_NUM_PRODUCERS ); - - mProducerIndex = 0; - mConsumerIndex = 0; - mValues.setSize( numValues ); - - U32 tick = Platform::getRealMilliseconds(); - for( U32 i = 0; i < numValues; ++ i ) - mValues[ i ] = tick; - - Vector< ProducerThread* > producers; - Vector< ConsumerThread* > consumers; - - producers.setSize( numProducers ); - consumers.setSize( numConsumers ); - - for( U32 i = 0; i < numProducers; ++ i ) - { - producers[ i ] = new ProducerThread( this ); - producers[ i ]->start(); - } - for( U32 i = 0; i < numConsumers; ++ i ) - { - consumers[ i ] = new ConsumerThread( this ); - consumers[ i ]->start(); - } - - for( U32 i = 0; i < numProducers; ++ i ) - { - producers[ i ]->join(); - delete producers[ i ]; - } - for( U32 i = 0; i < numConsumers; ++ i ) - { - consumers[ i ]->join(); - delete consumers[ i ]; - } - - for( U32 i = 0; i < mValues.size(); ++ i ) - TEST( mValues[ i ] == 0 ); - - mValues.clear(); - } -}; - -#endif // !TORQUE_SHIPPING diff --git a/Engine/source/platform/test/testThreadSafePriorityQueue.cpp b/Engine/source/platform/test/testThreadSafePriorityQueue.cpp deleted file mode 100644 index 5933eae2e..000000000 --- a/Engine/source/platform/test/testThreadSafePriorityQueue.cpp +++ /dev/null @@ -1,245 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "unit/test.h" -#include "platform/threads/threadSafePriorityQueue.h" -#include "platform/threads/thread.h" -#include "core/util/tVector.h" -#include "console/console.h" - - -#ifndef TORQUE_SHIPPING - -using namespace UnitTesting; - -#define TEST( x ) test( ( x ), "FAIL: " #x ) -#define XTEST( t, x ) t->test( ( x ), "FAIL: " #x ) - - -// Test queue without concurrency. - -CreateUnitTest( TestThreadSafePriorityQueueSerial, "Platform/ThreadSafePriorityQueue/Serial" ) -{ - struct Value - { - F32 mPriority; - U32 mIndex; - - Value() {} - Value( F32 priority, U32 index ) - : mPriority( priority ), mIndex( index ) {} - }; - - template< bool SORT_MIN_TO_MAX > - void test1() - { - Vector< Value > values; - - values.push_back( Value( 0.2f, 2 ) ); - values.push_back( Value( 0.7f, 7 ) ); - values.push_back( Value( 0.4f, 4 ) ); - values.push_back( Value( 0.6f, 6 ) ); - values.push_back( Value( 0.1f, 1 ) ); - values.push_back( Value( 0.5f, 5 ) ); - values.push_back( Value( 0.3f, 3 ) ); - values.push_back( Value( 0.8f, 8 ) ); - values.push_back( Value( 0.6f, 6 ) ); - values.push_back( Value( 0.9f, 9 ) ); - values.push_back( Value( 0.0f, 0 ) ); - - const S32 min = 0; - const S32 max = 9; - - ThreadSafePriorityQueue< U32, F32, SORT_MIN_TO_MAX > queue; - - for( U32 i = 0; i < values.size(); ++ i ) - queue.insert( values[ i ].mPriority, values[ i ].mIndex ); - - TEST( !queue.isEmpty() ); - - S32 index; - if( SORT_MIN_TO_MAX ) - index = min - 1; - else - index = max + 1; - - for( U32 i = 0; i < values.size(); ++ i ) - { - U32 value; - TEST( queue.takeNext( value ) ); - - if( value != index ) - { - if( SORT_MIN_TO_MAX ) - index ++; - else - index --; - } - - TEST( value == index ); - } - } - - void run() - { - test1< true >(); - test1< false >(); - } -}; - -// Test queue with concurrency. - -CreateUnitTest( TestThreadSafePriorityQueueConcurrent, "Platform/ThreadSafePriorityQueue/Concurrent" ) -{ -public: - typedef TestThreadSafePriorityQueueConcurrent TestType; - - enum - { - DEFAULT_NUM_VALUES = 100000, - DEFAULT_NUM_CONSUMERS = 10, - DEFAULT_NUM_PRODUCERS = 10 - }; - - struct Value : public ThreadSafeRefCount< Value > - { - U32 mIndex; - F32 mPriority; - bool mCheck; - - Value() : mCheck( false ) {} - Value( U32 index, F32 priority ) - : mIndex( index ), mPriority( priority ), mCheck( false ) {} - }; - - typedef ThreadSafeRef< Value > ValueRef; - - U32 mProducerIndex; - U32 mConsumerIndex; - ThreadSafePriorityQueue< ValueRef > mQueue; - Vector< ValueRef > mValues; - - struct ProducerThread : public Thread - { - ProducerThread( TestType* test ) - : Thread( 0, test ) {} - - virtual void run( void* arg ) - { - _setName( "ProducerThread" ); - Platform::outputDebugString( "Starting ProducerThread" ); - TestType* test = ( TestType* ) arg; - - while( 1 ) - { - U32 index = test->mProducerIndex; - if( index == test->mValues.size() ) - break; - - if( dCompareAndSwap( test->mProducerIndex, index, index + 1 ) ) - { - F32 priority = Platform::getRandom(); - ValueRef val = new Value( index, priority ); - test->mValues[ index ] = val; - test->mQueue.insert( priority, val ); - } - } - Platform::outputDebugString( "Stopping ProducerThread" ); - } - }; - struct ConsumerThread : public Thread - { - ConsumerThread( TestType* test ) - : Thread( 0, test ) {} - - virtual void run( void* arg ) - { - _setName( "ConsumerThread" ); - Platform::outputDebugString( "Starting ConsumerThread" ); - TestType* t = ( TestType* ) arg; - - while( t->mConsumerIndex < t->mValues.size() ) - { - ValueRef value; - if( t->mQueue.takeNext( value ) ) - { - dFetchAndAdd( t->mConsumerIndex, 1 ); - XTEST( t, t->mValues[ value->mIndex ] == value ); - value->mCheck = true; - } - else - Platform::sleep( 5 ); - } - Platform::outputDebugString( "Stopping ConsumerThread" ); - } - }; - - void run() - { - U32 numValues = Con::getIntVariable( "$testThreadSafePriorityQueue::numValues", DEFAULT_NUM_VALUES ); - U32 numConsumers = Con::getIntVariable( "$testThreadSafePriorityQueue::numConsumers", DEFAULT_NUM_CONSUMERS ); - U32 numProducers = Con::getIntVariable( "$testThreadSafePriorityQueue::numProducers", DEFAULT_NUM_PRODUCERS ); - - mProducerIndex = 0; - mConsumerIndex = 0; - mValues.setSize( numValues ); - - Vector< ProducerThread* > producers; - Vector< ConsumerThread* > consumers; - - producers.setSize( numProducers ); - consumers.setSize( numConsumers ); - - for( U32 i = 0; i < numProducers; ++ i ) - { - producers[ i ] = new ProducerThread( this ); - producers[ i ]->start(); - } - for( U32 i = 0; i < numConsumers; ++ i ) - { - consumers[ i ] = new ConsumerThread( this ); - consumers[ i ]->start(); - } - - for( U32 i = 0; i < numProducers; ++ i ) - { - producers[ i ]->join(); - delete producers[ i ]; - } - for( U32 i = 0; i < numConsumers; ++ i ) - { - consumers[ i ]->join(); - delete consumers[ i ]; - } - - for( U32 i = 0; i < mValues.size(); ++ i ) - { - TEST( mValues[ i ] != NULL ); - if( mValues[ i ] != NULL ) - TEST( mValues[ i ]->mCheck ); - } - - mValues.clear(); - } -}; - -#endif // !TORQUE_SHIPPING From fbd97b0cf88e9a3a8cd750980248df612df74029 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Mon, 1 Sep 2014 08:19:13 +1000 Subject: [PATCH 168/317] Ported non-interactive window manager test. --- .../source/windowManager/test/testWinMgr.cpp | 538 ------------------ .../windowManager/test/windowManagerTest.cpp | 58 ++ 2 files changed, 58 insertions(+), 538 deletions(-) delete mode 100644 Engine/source/windowManager/test/testWinMgr.cpp create mode 100644 Engine/source/windowManager/test/windowManagerTest.cpp diff --git a/Engine/source/windowManager/test/testWinMgr.cpp b/Engine/source/windowManager/test/testWinMgr.cpp deleted file mode 100644 index efe3ec770..000000000 --- a/Engine/source/windowManager/test/testWinMgr.cpp +++ /dev/null @@ -1,538 +0,0 @@ -//----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC -// -// Permission is hereby granted, free of charge, to any person obtaining a copy -// of this software and associated documentation files (the "Software"), to -// deal in the Software without restriction, including without limitation the -// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -// sell copies of the Software, and to permit persons to whom the Software is -// furnished to do so, subject to the following conditions: -// -// The above copyright notice and this permission notice shall be included in -// all copies or substantial portions of the Software. -// -// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -// IN THE SOFTWARE. -//----------------------------------------------------------------------------- - -#include "console/console.h" -#include "windowManager/platformWindowMgr.h" -#include "unit/test.h" -#include "core/util/tVector.h" -#include "gfx/gfxStructs.h" -#include "core/util/journal/process.h" -#include "gfx/gfxInit.h" - -using namespace UnitTesting; - -CreateUnitTest(TestWinMgrQueries, "WindowManager/BasicQueries") -{ - void run() - { - PlatformWindowManager *pwm = CreatePlatformWindowManager(); - - // Check out the primary desktop area... - RectI primary = pwm->getPrimaryDesktopArea(); - - Con::printf("Primary desktop area is (%d,%d) (%d,%d)", - primary.point.x, primary.point.y, primary.extent.x, primary.extent.y); - - test(primary.isValidRect(), "Got some sort of invalid rect from the window manager!"); - - // Now try to get info about all the monitors. - Vector monitorRects; - pwm->getMonitorRegions(monitorRects); - - test(monitorRects.size() > 0, "Should get at least one monitor rect back from getMonitorRegions!"); - - // This test is here just to detect overflow/runaway situations. -- BJG - test(monitorRects.size() < 64, "Either something's wrong, or you have a lot of monitors..."); - - for(S32 i=0; icreateWindow(NULL, vm); - - test(pw, "Didn't get a window back from the window manager, no good."); - if(!pw) - return; - - // Setup our events. - pw->mouseEvent.notify(this, &TestWinMgrCreate::handleMouseEvent); - pw->appEvent.notify(this, &TestWinMgrCreate::handleAppEvent); - - // And, go on our way. - while(Process::processEvents()) - ; - - SAFE_DELETE(pw); - } -}; - -CreateInteractiveTest(TestWinMgrGFXInit, "WindowManager/SimpleGFX") -{ - PlatformWindow *mWindow; - GFXDevice *mDevice; - - void handleDrawEvent(WindowId id) - { - mDevice->beginScene(); - mDevice->setActiveRenderTarget(mWindow->getGFXTarget()); - mDevice->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 255, 255, 0 ), 1.0f, 0 ); - mDevice->endScene(); - mWindow->getGFXTarget()->present(); - } - - void forceDraw() - { - handleDrawEvent(0); - } - - void handleAppEvent(WindowId, S32 event) - { - if(event == WindowClose) - Process::requestShutdown(); - } - - void run() - { - PlatformWindowManager *pwm = CreatePlatformWindowManager(); - - // Create a device. - GFXAdapter a; - a.mType = Direct3D9; - a.mIndex = 0; - - mDevice = GFXInit::createDevice(&a); - test(mDevice, "Unable to create d3d9 device #0."); - - // Initialize the window... - GFXVideoMode vm; - vm.resolution.x = 400; - vm.resolution.y = 400; - - mWindow = pwm->createWindow(mDevice, vm); - - test(mWindow, "Didn't get a window back from the window manager, no good."); - if(!mWindow) - return; - - // Setup our events. - mWindow->displayEvent.notify(this, &TestWinMgrGFXInit::handleDrawEvent); - mWindow->idleEvent.notify(this, &TestWinMgrGFXInit::forceDraw); - mWindow->appEvent.notify(this, &TestWinMgrGFXInit::handleAppEvent); - - // And, go on our way. - while(Process::processEvents()) - ; - - mWindow->displayEvent.remove(this, &TestWinMgrGFXInit::handleDrawEvent); - mWindow->idleEvent.remove(this, &TestWinMgrGFXInit::forceDraw); - mWindow->appEvent.remove(this, &TestWinMgrGFXInit::handleAppEvent); - - // Clean up! - SAFE_DELETE(mDevice); - SAFE_DELETE(mWindow); - } -}; - -CreateInteractiveTest(TestWinMgrGFXInitMultiWindow, "WindowManager/GFXMultiWindow") -{ - enum { - NumWindows = 4, - }; - - PlatformWindowManager *mWindowManager; - PlatformWindow *mWindows[NumWindows]; - GFXDevice *mDevice; - - void handleDrawEvent(WindowId id) - { - // Which window are we getting this event on? - PlatformWindow *w = mWindowManager->getWindowById(id); - - mDevice->beginScene(); - mDevice->setActiveRenderTarget(w->getGFXTarget()); - - // Vary clear color by window to discern which window is which. - mDevice->clear( GFXClearTarget, - ColorI( 255 - (id * 50), 255, id * 100 ), 1.0f, 0 ); - mDevice->endScene(); - - // Call swap on the window's render target. - ((GFXWindowTarget*)w->getGFXTarget())->present(); - } - - void handleAppEvent(WindowId, S32 event) - { - if(event == WindowClose) - Process::requestShutdown(); - } - - void handleIdleEvent() - { - for(S32 i=0; igetWindowId()); - } - - void run() - { - mWindowManager = CreatePlatformWindowManager(); - - // Create a device. - GFXAdapter a; - a.mType = Direct3D9; - a.mIndex = 0; - - mDevice = GFXInit::createDevice(&a); - test(mDevice, "Unable to create d3d9 device #0."); - - // Initialize the windows... - GFXVideoMode vm; - vm.resolution.x = 400; - vm.resolution.y = 400; - - for(S32 i=0; icreateWindow(mDevice, vm); - - test(mWindows[i], "Didn't get a window back from the window manager, no good."); - if(!mWindows[i]) - continue; - - // Setup our events. - mWindows[i]->displayEvent.notify(this, &TestWinMgrGFXInitMultiWindow::handleDrawEvent); - mWindows[i]->appEvent.notify(this, &TestWinMgrGFXInitMultiWindow::handleAppEvent); - mWindows[i]->idleEvent.notify(this, &TestWinMgrGFXInitMultiWindow::handleIdleEvent); - } - - // And, go on our way. - while(Process::processEvents()) - ; - - SAFE_DELETE(mWindowManager); - SAFE_DELETE(mDevice); - } -}; - -CreateInteractiveTest(TestJournaledMultiWindowGFX, "WindowManager/GFXJournaledMultiWindow") -{ - enum { - NumWindows = 2, - }; - - PlatformWindowManager *mWindowManager; - PlatformWindow *mWindows[NumWindows]; - GFXDevice *mDevice; - - S32 mNumDraws; - S32 mNumResize; - - void drawToWindow(PlatformWindow *win) - { - // Do some simple checks to make sure we draw the same number of times - // on both runs. - if(Journal::IsPlaying()) - mNumDraws--; - else - mNumDraws++; - - // Render! - mDevice->beginScene(); - mDevice->setActiveRenderTarget(win->getGFXTarget()); - - // Vary clear color by window to discern which window is which. - static S32 timeVariance = 0; - - mDevice->clear( GFXClearTarget, - ColorI( 0xFF - (++timeVariance * 5), 0xFF, win->getWindowId() * 0x0F ), 1.0f, 0 ); - - mDevice->endScene(); - - // Call swap on the window's render target. - win->getGFXTarget()->present(); - - } - - void handleDrawEvent(WindowId id) - { - // Which window are we getting this event on? - PlatformWindow *w = mWindowManager->getWindowById(id); - - drawToWindow(w); - } - - void handleAppEvent(WindowId, S32 event) - { - if(event == WindowClose) - Process::requestShutdown(); - } - - void handleIdleEvent() - { - for(S32 i=0; igetWindowById(id)->setSize(Point2I(width, height)); - - mNumResize--; - } - else - { - // If we're not playing back, do nothing except note it. - mNumResize++; - } - - // Which window are we getting this event on? - PlatformWindow *w = mWindowManager->getWindowById(id); - - drawToWindow(w); - } - - /// The mainloop of our app - we'll run this twice, once to create - /// a journal and again to play it back. - void mainLoop() - { - mWindowManager = CreatePlatformWindowManager(); - - // Create a device. - GFXAdapter a; - a.mType = Direct3D9; - a.mIndex = 0; - - mDevice = GFXInit::createDevice(&a); - test(mDevice, "Unable to create ogl device #0."); - - // Initialize the windows... - GFXVideoMode vm; - vm.resolution.x = 400; - vm.resolution.y = 400; - - for(S32 i=0; icreateWindow(mDevice, vm); - - test(mWindows[i], "Didn't get a window back from the window manager, no good."); - if(!mWindows[i]) - continue; - - // Setup our events. - mWindows[i]->displayEvent.notify(this, &TestJournaledMultiWindowGFX::handleDrawEvent); - mWindows[i]->appEvent.notify(this, &TestJournaledMultiWindowGFX::handleAppEvent); - mWindows[i]->resizeEvent.notify(this, &TestJournaledMultiWindowGFX::handleResizeEvent); - - // Only subscribe to the first idle event. - if(i==0) - mWindows[i]->idleEvent.notify(this, &TestJournaledMultiWindowGFX::handleIdleEvent); - } - - // And, go on our way. - while(Process::processEvents()) - ; - - // Finally, clean up. - for(S32 i=0; i 0, "No draws occurred!"); - test(mNumResize > 0, "No resizes occurred!"); - - // And play it back. - Journal::Play("multiwindow.jrn"); - mainLoop(); - Journal::Stop(); - - test(mNumDraws == 0, "Failed to play journal back with same number of draws."); - test(mNumResize == 0, "Failed to play journal back with same number of resizes."); -#endif - } -}; - -CreateInteractiveTest(GFXTestFullscreenToggle, "GFX/TestFullscreenToggle") -{ - enum Constants - { - NumWindows = 1, - }; - - PlatformWindowManager *mWindowManager; - PlatformWindow *mWindows[NumWindows]; - GFXDevice *mDevice; - - void drawToWindow(PlatformWindow *win) - { - // Render! - mDevice->beginScene(); - mDevice->setActiveRenderTarget(win->getGFXTarget()); - - // Vary clear color by window to discern which window is which. - static S32 timeVariance = 0; - - mDevice->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, - ColorI( 0xFF - (++timeVariance * 5), 0xFF, win->getWindowId() * 0x40 ), 1.0f, 0 ); - - mDevice->endScene(); - - // Call swap on the window's render target. - win->getGFXTarget()->present(); - } - - void handleDrawEvent(WindowId id) - { - // Which window are we getting this event on? - PlatformWindow *w = mWindowManager->getWindowById(id); - - drawToWindow(w); - } - - void handleAppEvent(WindowId, S32 event) - { - if(event == WindowClose) - Process::requestShutdown(); - } - - void handleIdleEvent() - { - // Redraw everything. - for(S32 i=0; igetWindowById(did); - GFXVideoMode winVm = win->getVideoMode(); - - // If the window is not full screen, make it full screen 800x600x32 - if(winVm.fullScreen == false) - { - winVm.fullScreen = true; - winVm.resolution.set(800,600); - } - else - { - // If the window is full screen, then bump it to 400x400x32 - winVm.fullScreen = false; - winVm.resolution.set(400,400); - } - - win->setVideoMode(winVm); - } - - void run() - { - mWindowManager = CreatePlatformWindowManager(); - - // Create a device. - GFXAdapter a; - a.mType = Direct3D9; - a.mIndex = 0; - - mDevice = GFXInit::createDevice(&a); - test(mDevice, "Unable to create d3d9 device #0."); - - // Initialize the windows... - GFXVideoMode vm; - vm.resolution.x = 400; - vm.resolution.y = 400; - - for(S32 i=0; icreateWindow(mDevice, vm); - - test(mWindows[i], "Didn't get a window back from the window manager, no good."); - if(!mWindows[i]) - continue; - - // Setup our events. - mWindows[i]->appEvent.notify(this, &GFXTestFullscreenToggle::handleAppEvent); - mWindows[i]->buttonEvent.notify(this, &GFXTestFullscreenToggle::handleButtonEvent); - mWindows[i]->displayEvent.notify(this, &GFXTestFullscreenToggle::handleDrawEvent); - - // Only subscribe to the first idle event. - if(i==0) - mWindows[i]->idleEvent.notify(this, &GFXTestFullscreenToggle::handleIdleEvent); - } - - // And, go on our way. - while(Process::processEvents()) - ; - - // Finally, clean up. - for(S32 i=0; ipreDestroy(); - SAFE_DELETE(mDevice); - SAFE_DELETE(mWindowManager); - } -}; \ No newline at end of file diff --git a/Engine/source/windowManager/test/windowManagerTest.cpp b/Engine/source/windowManager/test/windowManagerTest.cpp new file mode 100644 index 000000000..d6179c96b --- /dev/null +++ b/Engine/source/windowManager/test/windowManagerTest.cpp @@ -0,0 +1,58 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2014 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifdef TORQUE_TESTS_ENABLED +#include "testing/unitTesting.h" +#include "windowManager/platformWindowMgr.h" + +// Mysteriously, TEST(WindowManager, BasicAPI) gives an error. Huh. +TEST(WinMgr, BasicAPI) +{ + PlatformWindowManager *pwm = CreatePlatformWindowManager(); + + // Check out the primary desktop area... + RectI primary = pwm->getPrimaryDesktopArea(); + + EXPECT_TRUE(primary.isValidRect()) + << "Got some sort of invalid rect from the window manager!"; + + // Now try to get info about all the monitors. + Vector monitorRects; + pwm->getMonitorRegions(monitorRects); + + EXPECT_GT(monitorRects.size(), 0) + << "Should get at least one monitor rect back from getMonitorRegions!"; + + // This test is here just to detect overflow/runaway situations. -- BJG + EXPECT_LT(monitorRects.size(), 64) + << "Either something's wrong, or you have a lot of monitors..."; + + for(S32 i=0; i Date: Mon, 1 Sep 2014 08:22:16 +1000 Subject: [PATCH 169/317] Removed unused GFX interactive tests. --- Engine/source/gfx/test/stanfordBunny.cpp | 13623 --------------------- Engine/source/gfx/test/testGfx.cpp | 1556 --- Tools/projectGenerator/modules/core.inc | 1 - 3 files changed, 15180 deletions(-) delete mode 100644 Engine/source/gfx/test/stanfordBunny.cpp delete mode 100644 Engine/source/gfx/test/testGfx.cpp diff --git a/Engine/source/gfx/test/stanfordBunny.cpp b/Engine/source/gfx/test/stanfordBunny.cpp deleted file mode 100644 index 31a3c571b..000000000 --- a/Engine/source/gfx/test/stanfordBunny.cpp +++ /dev/null @@ -1,13623 +0,0 @@ -/* - - File: stanfordbunny.c - - Abstract: See - - Version: 1.0 - - Disclaimer: IMPORTANT: This Apple software is supplied to you by Apple - Computer, Inc. ("Apple") in consideration of your agreement to the - following terms, and your use, installation, modification or - redistribution of this Apple software constitutes acceptance of these - terms. If you do not agree with these terms, please do not use, - install, modify or redistribute this Apple software. - - In consideration of your agreement to abide by the following terms, and - subject to these terms, Apple grants you a personal, non-exclusive - license, under Apple's copyrights in this original Apple software (the - "Apple Software"), to use, reproduce, modify and redistribute the Apple - Software, with or without modifications, in source and/or binary forms; - provided that if you redistribute the Apple Software in its entirety and - without modifications, you must retain this notice and the following - text and disclaimers in all such redistributions of the Apple Software. - Neither the name, trademarks, service marks or logos of Apple Computer, - Inc. may be used to endorse or promote products derived from the Apple - Software without specific prior written permission from Apple. Except - as expressly stated in this notice, no other rights or licenses, express - or implied, are granted by Apple herein, including but not limited to - any patent rights that may be infringed by your derivative works or by - other works in which the Apple Software may be incorporated. - - The Apple Software is provided by Apple on an "AS IS" basis. APPLE - MAKES NO WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION - THE IMPLIED WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND - OPERATION ALONE OR IN COMBINATION WITH YOUR PRODUCTS. - - IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL - OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF - SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS - INTERRUPTION) ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, - MODIFICATION AND/OR DISTRIBUTION OF THE APPLE SOFTWARE, HOWEVER CAUSED - AND WHETHER UNDER THEORY OF CONTRACT, TORT (INCLUDING NEGLIGENCE), - STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. - - Copyright 2006 Apple Computer, Inc., All Rights Reserved - -*/ - -/* - stanfordbunny.c - - This file contains the data and associated APIs to create a display list - of a complex model, the "Stanford Bunny" a reconstructed 3D scan of a Bunny. - - It provides GenStanfordBunnyWireList() and GenStanfordBunnySolidList() - to construct a display list and return it's ID using 3 arrays of data: - face_indicies, vertices and normals that you see below. -*/ - - - -/* - Stanford Bunny Data (culled) - - See for original data -*/ - -#include "gfx/gfxDevice.h" -#include "gfx/gfxVertexBuffer.h" -#include "gfx/gfxStructs.h" - -// 8146 Verticies -// 8127 Normals -// 16301 Triangles - -short face_indicies[16301][6] = { -// surface - {1538,2410,1101 ,0,1,2 }, {713,6196,101 ,3,4,5 }, {696,704,101 ,6,7,5 }, - {1192,117,1113 ,8,9,10 }, {1260,173,1857 ,11,12,13 }, {540,1192,1113 ,14,8,10 }, - {704,696,398 ,7,6,15 }, {398,696,458 ,15,6,16 }, {1605,772,4540 ,17,18,19 }, - {1192,540,1857 ,8,14,13 }, {772,713,809 ,18,3,20 }, {713,173,1260 ,3,12,11 }, - {173,1192,1857 ,12,8,13 }, {809,713,1260 ,20,3,11 }, {804,4128,4120 ,21,22,23 }, - {1133,804,4120 ,24,21,23 }, {4128,4140,4531 ,22,25,26 }, {4140,1534,4136 ,25,27,28 }, - {4531,4140,4136 ,26,25,28 }, {4136,1534,1254 ,28,27,29 }, {4515,1609,4485 ,30,31,32 }, - {710,1396,889 ,33,34,35 }, {1245,3618,4204 ,36,37,38 }, {2046,1905,768 ,39,40,41 }, - {2453,5699,5698 ,42,43,44 }, {1254,2046,768 ,29,39,41 }, {768,1905,1393 ,41,40,45 }, - {1393,1490,4112 ,45,46,47 }, {5657,5658,5680 ,48,49,50 }, {1490,4281,839 ,46,51,52 }, - {4112,1490,839 ,47,46,52 }, {839,4281,1330 ,52,51,53 }, {6897,8079,3286 ,54,55,56 }, - {920,4291,1937 ,57,58,59 }, {321,1132,1222 ,60,61,62 }, {5338,4915,4914 ,63,64,65 }, - {1093,4537,840 ,66,67,68 }, {4537,1697,840 ,67,69,68 }, {705,659,4158 ,70,71,72 }, - {7248,7323,7322 ,73,74,75 }, {5413,5103,798 ,76,77,78 }, {5244,5289,5266 ,79,80,81 }, - {4197,4217,4258 ,82,83,84 }, {277,360,214 ,85,86,87 }, {4498,4536,4535 ,88,89,90 }, - {4497,4498,4535 ,91,88,90 }, {231,654,775 ,92,93,94 }, {4535,4536,792 ,90,89,95 }, - {8065,6577,8094 ,96,97,98 }, {1595,703,4145 ,99,100,101 }, {5203,1089,4369 ,102,103,104 }, - {2698,5203,4369 ,105,102,104 }, {1653,4491,226 ,106,107,108 }, - {2941,2940,2907 ,109,110,111 }, {6413,6414,3340 ,112,113,114 }, - {5231,3276,922 ,115,116,117 }, {1628,792,883 ,118,95,119 }, {4273,2042,542 ,120,121,122 }, - {4091,4126,4125 ,123,124,125 }, {4077,4091,4125 ,126,123,125 }, - {5618,103,2011 ,127,128,129 }, {2026,934,4251 ,130,131,132 }, - {4123,4173,4172 ,133,134,135 }, {3397,4123,4172 ,136,133,135 }, - {4122,4123,3397 ,137,133,136 }, {82,284,80 ,138,139,140 }, {5113,6120,5845 ,141,142,143 }, - {803,696,101 ,144,6,5 }, {139,1420,2137 ,145,146,147 }, {1348,1310,737 ,148,149,150 }, - {1423,196,396 ,151,152,153 }, {4403,4356,4432 ,154,155,156 }, - {143,781,142 ,157,158,159 }, {1148,1925,4132 ,160,161,162 }, - {1031,804,1133 ,163,21,24 }, {458,1031,1133 ,16,163,24 }, {1318,2137,1420 ,164,147,146 }, - {4255,4328,4301 ,165,166,167 }, {715,174,1652 ,168,169,170 }, - {5483,8116,5261 ,171,172,173 }, {1468,1451,542 ,174,175,122 }, - {2206,2219,4978 ,176,177,178 }, {804,4140,4128 ,21,25,22 }, {1222,1132,4521 ,62,61,179 }, - {4521,1318,606 ,179,164,180 }, {5162,5618,1443 ,181,127,182 }, - {3221,5261,8119 ,183,173,184 }, {4067,4077,4071 ,185,126,186 }, - {4526,1651,4515 ,187,188,30 }, {4526,891,1651 ,187,189,188 }, - {6320,6345,3533 ,190,191,192 }, {4140,779,1534 ,25,193,27 }, - {839,1330,1481 ,52,53,194 }, {1534,544,1254 ,27,195,29 }, {139,1318,1420 ,145,164,146 }, - {1842,940,1840 ,196,197,198 }, {5730,5729,3615 ,199,200,201 }, - {174,715,1147 ,169,168,202 }, {1345,1846,1093 ,203,204,66 }, - {4084,1178,4113 ,205,206,207 }, {3397,4172,1257 ,136,135,208 }, - {2042,1468,542 ,121,174,122 }, {787,4130,175 ,209,210,211 }, - {1461,1311,1310 ,212,213,149 }, {1311,457,1310 ,213,214,149 }, - {4211,321,885 ,215,60,216 }, {116,4211,885 ,217,215,216 }, {3761,4326,1463 ,218,219,220 }, - {544,1905,2046 ,195,40,39 }, {1254,544,2046 ,29,195,39 }, {1310,457,229 ,149,214,221 }, - {57,4160,735 ,222,223,224 }, {4029,4214,4019 ,225,226,227 }, - {4367,4421,4390 ,228,229,230 }, {4255,4301,4300 ,165,167,231 }, - {4411,962,1006 ,232,233,234 }, {2707,1335,4446 ,235,236,237 }, - {888,757,120 ,238,239,240 }, {4980,4547,2163 ,241,242,243 }, - {891,1461,200 ,189,212,244 }, {891,200,1651 ,189,244,188 }, {4838,311,24 ,245,246,247 }, - {169,105,1880 ,248,249,250 }, {1393,4179,1490 ,45,251,46 }, {1490,4179,4281 ,46,251,51 }, - {3263,8102,8089 ,252,253,254 }, {700,757,896 ,255,239,256 }, - {4465,4484,4514 ,257,258,259 }, {4202,700,464 ,260,255,261 }, - {3283,2968,1693 ,262,263,264 }, {1609,4514,4484 ,31,259,258 }, - {4485,1609,4484 ,32,31,258 }, {1609,714,4514 ,31,265,259 }, {606,1318,139 ,180,164,145 }, - {1747,979,970 ,266,267,268 }, {4136,1254,399 ,28,29,269 }, {4473,4451,4497 ,270,271,91 }, - {4057,2420,4263 ,272,273,274 }, {1396,770,889 ,34,275,35 }, {4421,4420,4390 ,229,276,230 }, - {1069,4138,1209 ,277,278,279 }, {3948,3670,1877 ,280,281,282 }, - {2707,5162,1335 ,235,181,236 }, {3484,863,1300 ,283,284,285 }, - {4978,5000,6414 ,178,286,113 }, {190,1161,1021 ,287,288,289 }, - {896,757,888 ,256,239,238 }, {1348,737,497 ,148,150,290 }, {639,1348,497 ,291,148,290 }, - {1112,4202,4194 ,292,260,293 }, {3958,3957,3903 ,294,295,296 }, - {64,613,11 ,297,298,299 }, {1344,456,795 ,300,301,302 }, {27,43,545 ,303,304,305 }, - {1033,888,30 ,306,238,307 }, {1023,4224,1033 ,308,309,306 }, - {4194,4202,464 ,293,260,261 }, {497,737,190 ,290,150,287 }, {737,356,190 ,150,310,287 }, - {2872,1245,4204 ,311,36,38 }, {4375,4531,4136 ,312,26,28 }, {1241,241,1389 ,313,314,315 }, - {4128,4531,4375 ,22,26,312 }, {4120,4128,4375 ,23,22,312 }, {709,1833,653 ,316,317,318 }, - {1793,4150,69 ,319,320,321 }, {69,1319,70 ,321,322,323 }, {1792,1375,24 ,324,325,247 }, - {6320,7500,6345 ,190,326,191 }, {1225,57,1245 ,327,222,36 }, - {1257,4172,4214 ,208,135,226 }, {997,190,1021 ,328,287,289 }, - {464,4224,1023 ,261,309,308 }, {3,464,1023 ,329,261,308 }, {714,639,950 ,265,291,330 }, - {714,950,4514 ,265,330,259 }, {7747,7746,7698 ,331,332,333 }, - {652,1296,1383 ,334,335,336 }, {1203,1204,1202 ,337,338,339 }, - {970,6018,1747 ,268,340,266 }, {4430,4451,4473 ,341,271,270 }, - {120,4362,4411 ,240,342,232 }, {7848,7874,7823 ,343,344,345 }, - {467,466,360 ,346,347,86 }, {1259,4095,4179 ,348,349,251 }, {5,413,315 ,350,351,352 }, - {4098,4057,4263 ,353,272,274 }, {1057,1596,1069 ,354,355,277 }, - {971,1057,1069 ,356,354,277 }, {44,412,1793 ,357,358,319 }, {4019,4255,4300 ,227,165,231 }, - {5378,5412,5377 ,359,360,361 }, {1259,4114,4059 ,348,362,363 }, - {4982,5000,4978 ,364,286,178 }, {4334,965,1419 ,365,366,367 }, - {1564,1085,1595 ,368,369,99 }, {4172,4215,4214 ,135,370,226 }, - {1225,2872,2428 ,327,311,371 }, {4110,1633,503 ,372,373,374 }, - {4328,4378,4354 ,166,375,376 }, {513,1343,376 ,377,378,379 }, - {3997,3998,4039 ,380,381,382 }, {8101,8122,8087 ,383,384,385 }, - {5000,6415,6414 ,286,386,113 }, {660,703,1625 ,387,100,388 }, - {805,2352,24 ,389,390,247 }, {1599,799,1194 ,391,392,393 }, {5850,4011,5784 ,394,395,396 }, - {3871,5850,5784 ,397,394,396 }, {2872,1225,1245 ,311,327,36 }, - {1633,513,376 ,373,377,379 }, {3871,5784,878 ,397,396,398 }, - {659,4171,802 ,71,399,400 }, {708,1222,365 ,401,62,402 }, {117,458,1133 ,9,16,24 }, - {4497,4535,4534 ,91,90,403 }, {1967,1966,1903 ,404,405,406 }, - {3259,4091,4068 ,407,123,408 }, {4185,3113,52 ,409,410,411 }, - {8090,8132,3006 ,412,413,414 }, {211,965,4334 ,415,366,365 }, - {4214,4254,4019 ,226,416,227 }, {1023,2045,211 ,308,417,415 }, - {1923,1023,211 ,418,308,415 }, {4043,2798,2956 ,419,420,399 }, - {4535,792,1628 ,90,95,118 }, {398,458,117 ,15,16,9 }, {139,2137,777 ,145,147,421 }, - {4534,4535,1628 ,403,90,118 }, {4411,4362,962 ,232,342,233 }, - {5199,5198,5177 ,422,423,424 }, {1838,4427,363 ,425,426,427 }, - {5213,5233,5212 ,428,429,430 }, {1419,799,1599 ,367,392,391 }, - {211,30,965 ,415,307,366 }, {347,102,2423 ,431,432,433 }, {138,3871,878 ,434,397,398 }, - {4182,4201,4051 ,435,436,437 }, {2313,1222,708 ,438,62,401 }, - {885,2313,708 ,216,438,401 }, {3327,3977,3513 ,439,440,441 }, - {3535,1345,840 ,442,203,68 }, {915,913,916 ,443,444,445 }, {678,634,682 ,446,447,448 }, - {634,346,682 ,447,449,448 }, {7254,6347,7860 ,450,451,452 }, - {8061,8082,8102 ,453,454,253 }, {1595,1085,703 ,99,369,100 }, - {2660,2753,2349 ,455,456,457 }, {2338,2680,4039 ,458,459,382 }, - {4040,2338,4039 ,460,458,382 }, {5307,5306,4393 ,461,462,463 }, - {4537,705,1697 ,67,70,69 }, {6563,8061,8063 ,464,453,465 }, {1305,4446,1300 ,466,237,285 }, - {4312,4311,490 ,467,468,469 }, {4301,4328,4354 ,167,166,376 }, - {467,503,466 ,346,374,347 }, {4051,1933,1695 ,437,470,471 }, - {6859,6860,6911 ,472,473,474 }, {2455,2212,5001 ,475,476,477 }, - {1197,6341,4983 ,478,479,480 }, {5240,5239,5218 ,481,482,483 }, - {705,1346,1697 ,70,484,69 }, {1731,1747,604 ,485,266,486 }, {491,586,678 ,487,488,446 }, - {833,491,678 ,489,487,446 }, {586,679,678 ,488,490,446 }, {679,86,634 ,490,491,447 }, - {678,679,634 ,446,490,447 }, {1485,346,634 ,492,449,447 }, {86,1485,634 ,491,492,447 }, - {1485,496,346 ,492,493,449 }, {591,638,346 ,494,495,449 }, {496,591,346 ,493,494,449 }, - {29,15,638 ,496,497,495 }, {591,29,638 ,494,496,495 }, {29,438,15 ,496,498,497 }, - {745,961,438 ,499,500,498 }, {745,446,961 ,499,501,500 }, {4187,1023,1923 ,502,308,418 }, - {189,1021,4187 ,503,289,502 }, {365,606,139 ,402,180,145 }, {883,792,146 ,119,95,504 }, - {174,1597,892 ,169,505,506 }, {826,1021,189 ,507,289,503 }, {5570,1350,5641 ,508,509,510 }, - {8072,530,3668 ,511,512,513 }, {5699,5727,5726 ,43,514,515 }, - {2927,2926,2882 ,516,517,518 }, {6912,6943,6942 ,519,520,521 }, - {6341,1197,2808 ,479,478,522 }, {536,1305,1300 ,523,466,285 }, - {5178,5199,5177 ,524,422,424 }, {4311,4383,490 ,468,399,469 }, - {174,1422,1597 ,169,525,505 }, {5642,5658,5657 ,526,49,48 }, - {586,86,679 ,488,491,490 }, {1921,446,745 ,527,501,499 }, {1199,39,446 ,528,529,501 }, - {1625,4222,4251 ,388,530,132 }, {1487,304,2680 ,531,532,459 }, - {304,1487,488 ,532,531,533 }, {2150,964,3084 ,534,535,536 }, - {5726,5727,5764 ,515,514,537 }, {803,4,696 ,144,538,6 }, {3511,4029,4019 ,539,225,227 }, - {4,1031,458 ,538,163,16 }, {696,4,458 ,6,538,16 }, {5727,5765,5764 ,514,540,537 }, - {4369,1089,4393 ,104,103,463 }, {4326,3341,3258 ,219,541,542 }, - {4158,659,802 ,72,71,400 }, {5834,5833,5816 ,543,544,545 }, {3008,7051,7029 ,546,547,548 }, - {965,888,799 ,366,238,392 }, {950,1652,4483 ,330,170,549 }, {4514,950,4483 ,259,330,549 }, - {1112,116,885 ,292,217,216 }, {3862,1973,7810 ,550,551,552 }, - {2093,2257,1749 ,553,554,555 }, {466,416,143 ,347,556,157 }, - {811,4954,812 ,557,558,559 }, {92,1921,745 ,560,527,499 }, {92,446,1921 ,560,501,527 }, - {317,344,93 ,561,562,563 }, {93,344,39 ,563,562,529 }, {1626,3253,349 ,564,565,566 }, - {4379,4431,4430 ,567,568,341 }, {4378,4379,4430 ,375,567,341 }, - {4503,779,4140 ,569,193,25 }, {5283,5284,5337 ,570,571,572 }, - {804,4503,4140 ,21,569,25 }, {2070,1367,1244 ,573,574,575 }, - {360,466,214 ,86,347,87 }, {229,116,1112 ,221,217,292 }, {4077,4109,4076 ,126,576,577 }, - {1023,1033,2045 ,308,306,417 }, {314,1419,1599 ,578,367,391 }, - {875,2155,1920 ,579,580,581 }, {4444,4482,4464 ,582,583,584 }, - {30,888,965 ,307,238,366 }, {4900,4545,5578 ,585,586,587 }, {1260,1461,891 ,11,212,189 }, - {809,1260,891 ,20,11,189 }, {1271,4548,2252 ,588,589,590 }, {2246,351,352 ,591,592,593 }, - {6946,6968,6916 ,594,595,596 }, {6555,2093,1749 ,597,553,555 }, - {235,1292,1517 ,598,599,600 }, {1292,1850,1517 ,599,601,600 }, - {1520,586,491 ,602,488,487 }, {251,1520,491 ,603,602,487 }, {1520,2225,586 ,602,604,488 }, - {63,86,586 ,605,491,488 }, {2225,63,586 ,604,605,488 }, {63,244,1485 ,605,606,492 }, - {86,63,1485 ,491,605,492 }, {1485,244,496 ,492,606,493 }, {244,590,591 ,606,607,494 }, - {496,244,591 ,493,606,494 }, {590,1198,29 ,607,608,496 }, {591,590,29 ,494,607,496 }, - {1198,441,438 ,608,609,498 }, {29,1198,438 ,496,608,498 }, {441,956,745 ,609,610,499 }, - {438,441,745 ,498,609,499 }, {745,956,92 ,499,610,560 }, {92,868,446 ,560,611,501 }, - {1247,1199,446 ,612,528,501 }, {868,1247,446 ,611,612,501 }, - {93,39,1199 ,563,529,528 }, {1247,93,1199 ,612,563,528 }, {2150,3084,832 ,534,536,613 }, - {488,2150,832 ,533,534,613 }, {6044,5933,5934 ,614,615,616 }, - {8076,1295,3040 ,617,618,619 }, {779,544,1534 ,193,195,27 }, - {2338,1487,2680 ,458,531,459 }, {1487,2150,488 ,531,534,533 }, - {1905,4179,1393 ,40,251,45 }, {681,683,302 ,620,621,622 }, {1260,1311,1461 ,11,213,212 }, - {5162,1443,1335 ,181,182,236 }, {4482,4525,4513 ,583,623,624 }, - {4334,1419,314 ,365,367,578 }, {950,715,1652 ,330,168,170 }, - {1305,2707,4446 ,466,235,237 }, {120,4411,1006 ,240,232,234 }, - {4545,3621,5578 ,586,625,587 }, {7264,7263,7222 ,626,627,628 }, - {6698,6699,6750 ,629,630,631 }, {1520,534,63 ,602,632,605 }, - {2225,1520,63 ,604,602,605 }, {63,534,244 ,605,632,606 }, {317,93,4539 ,561,563,633 }, - {3084,964,3094 ,536,535,634 }, {1294,874,169 ,635,636,248 }, - {2789,2832,2831 ,637,638,639 }, {4165,87,2477 ,640,641,642 }, - {832,3084,3094 ,613,536,634 }, {4430,4431,4451 ,341,568,271 }, - {348,249,268 ,643,644,645 }, {5618,5616,1443 ,127,646,182 }, - {715,497,1147 ,168,290,202 }, {4468,3673,3473 ,647,648,649 }, - {7746,7745,7697 ,332,650,651 }, {3749,5380,5304 ,652,653,654 }, - {888,120,1006 ,238,240,234 }, {2132,2131,2085 ,655,656,657 }, - {8107,3006,2731 ,658,414,659 }, {5812,5823,7073 ,660,661,662 }, - {153,1243,1292 ,663,664,599 }, {235,153,1292 ,598,663,599 }, - {1292,1243,1850 ,599,664,601 }, {112,251,1850 ,665,603,601 }, - {1243,112,1850 ,664,665,601 }, {1593,1520,251 ,666,602,603 }, - {112,1593,251 ,665,666,603 }, {1593,172,534 ,666,667,632 }, {1520,1593,534 ,602,666,632 }, - {727,726,627 ,668,669,670 }, {590,589,1198 ,607,671,608 }, {1198,357,441 ,608,672,609 }, - {442,310,956 ,673,674,610 }, {441,442,956 ,609,673,610 }, {310,60,92 ,674,675,560 }, - {956,310,92 ,610,674,560 }, {92,60,868 ,560,675,611 }, {60,1788,1247 ,675,676,612 }, - {868,60,1247 ,611,675,612 }, {1788,113,93 ,676,677,563 }, {1247,1788,93 ,612,676,563 }, - {1059,1294,169 ,678,635,248 }, {105,783,1066 ,249,679,680 }, - {141,4206,4186 ,681,682,683 }, {6860,6912,6911 ,473,519,474 }, - {5013,6484,5455 ,684,685,686 }, {964,1294,1059 ,535,635,678 }, - {3094,964,1059 ,634,535,678 }, {128,883,4594 ,687,119,688 }, - {883,128,76 ,119,687,689 }, {3750,1463,3681 ,690,220,691 }, {6094,3373,3300 ,692,693,694 }, - {4179,1490,4281 ,251,46,51 }, {1490,4310,4281 ,46,695,51 }, {1461,1310,1348 ,212,149,148 }, - {700,896,4224 ,255,256,309 }, {1021,1161,3 ,289,288,329 }, {1350,5642,5641 ,509,526,510 }, - {459,211,4334 ,696,415,365 }, {4237,4257,4275 ,697,698,699 }, - {5801,5817,5800 ,700,701,702 }, {4444,4464,4443 ,582,584,703 }, - {6350,6349,6321 ,704,705,706 }, {2045,1033,30 ,417,306,307 }, - {1742,7051,3008 ,707,547,546 }, {7052,3660,3454 ,708,709,710 }, - {6790,6809,6754 ,711,712,713 }, {1593,16,172 ,666,714,667 }, - {3399,4550,4447 ,715,716,717 }, {526,582,625 ,718,719,720 }, - {725,724,625 ,721,722,720 }, {8129,7693,8116 ,723,724,172 }, - {1195,4395,5194 ,725,726,727 }, {3008,3975,3944 ,546,728,729 }, - {129,128,695 ,730,687,731 }, {129,76,128 ,730,689,687 }, {6149,3701,3648 ,732,733,734 }, - {3495,3494,3434 ,735,736,737 }, {8116,7693,8071 ,172,724,738 }, - {195,4350,4098 ,739,740,353 }, {1150,38,110 ,741,742,743 }, {1311,540,457 ,213,14,214 }, - {497,190,997 ,290,287,328 }, {459,4334,4412 ,696,365,744 }, {1750,4894,1698 ,745,746,747 }, - {4412,4334,314 ,744,365,578 }, {4544,6353,6686 ,748,749,750 }, - {5521,8071,8125 ,751,738,752 }, {314,1599,4244 ,578,391,753 }, - {5536,5522,5557 ,754,755,756 }, {5850,3749,4011 ,394,652,395 }, - {1995,2049,2015 ,757,758,759 }, {6299,404,6417 ,760,761,762 }, - {172,16,1743 ,667,714,763 }, {582,583,625 ,719,764,720 }, {783,688,1066 ,679,765,680 }, - {169,874,105 ,248,636,249 }, {874,783,105 ,636,679,249 }, {615,4281,4310 ,766,51,695 }, - {226,1150,110 ,108,741,743 }, {774,1634,683 ,767,768,621 }, {4174,4217,4197 ,769,83,82 }, - {540,4211,116 ,14,215,217 }, {200,1461,1348 ,244,212,148 }, {464,700,4224 ,261,255,309 }, - {1147,497,997 ,202,290,328 }, {103,2030,2011 ,128,770,129 }, - {4429,1923,459 ,771,418,696 }, {1699,1751,1750 ,772,773,745 }, - {1698,1699,1750 ,747,772,745 }, {4587,4588,4589 ,774,775,776 }, - {1811,1885,1810 ,777,778,779 }, {6553,3252,3283 ,780,781,262 }, - {4482,4513,4464 ,583,624,584 }, {976,4964,4965 ,782,783,784 }, - {2050,2049,1995 ,785,758,757 }, {1950,2050,1995 ,786,785,757 }, - {2050,2075,2049 ,785,787,758 }, {2075,2096,2049 ,787,788,758 }, - {3661,2212,2455 ,789,476,475 }, {1331,1243,153 ,790,664,663 }, - {597,1331,153 ,791,790,663 }, {553,112,1243 ,792,665,664 }, {1331,553,1243 ,790,792,664 }, - {553,1727,1593 ,792,793,666 }, {112,553,1593 ,665,792,666 }, - {1727,1743,16 ,793,763,714 }, {1593,1727,16 ,666,793,714 }, {170,1290,1240 ,794,795,796 }, - {1184,999,1188 ,797,798,799 }, {526,527,582 ,718,800,719 }, {583,626,625 ,764,801,720 }, - {3598,3547,6598 ,802,803,804 }, {2680,304,2662 ,459,532,805 }, - {2853,3190,2416 ,806,807,808 }, {3300,5977,6094 ,694,809,692 }, - {60,145,1788 ,675,810,676 }, {310,2694,60 ,674,811,675 }, {8126,6891,3148 ,812,813,814 }, - {7924,7923,7873 ,815,816,817 }, {3007,4486,6070 ,818,819,820 }, - {1065,1340,1450 ,821,822,823 }, {688,1065,1066 ,765,821,680 }, - {3359,3325,3326 ,824,825,826 }, {5178,5177,4630 ,524,424,827 }, - {1980,2007,1979 ,828,829,830 }, {1984,1916,1917 ,831,832,833 }, - {277,214,1992 ,85,87,834 }, {1539,96,1587 ,835,836,837 }, {4335,4151,1833 ,838,839,317 }, - {768,1393,864 ,41,45,840 }, {457,540,116 ,214,14,217 }, {1652,892,4525 ,170,506,623 }, - {2030,3913,2011 ,770,841,129 }, {2014,1923,736 ,842,418,843 }, - {4392,2341,7051 ,844,845,547 }, {2097,2096,2075 ,846,788,787 }, - {2007,1978,1979 ,829,847,830 }, {1936,1333,2341 ,848,849,845 }, - {1411,1331,597 ,850,790,791 }, {5353,6836,144 ,851,852,853 }, - {1721,1778,1777 ,854,855,856 }, {392,170,1240 ,857,794,796 }, - {1475,1476,1547 ,858,859,860 }, {433,434,485 ,861,862,863 }, - {527,583,582 ,800,764,719 }, {385,386,433 ,864,865,861 }, {434,527,526 ,862,800,718 }, - {337,338,385 ,866,867,864 }, {238,338,237 ,868,867,869 }, {386,434,433 ,865,862,861 }, - {338,386,385 ,867,865,864 }, {338,337,237 ,867,866,869 }, {3382,3381,3356 ,870,871,872 }, - {2582,2474,2585 ,873,874,875 }, {5077,4549,1809 ,876,877,878 }, - {3305,3358,3324 ,879,880,881 }, {3383,3382,3357 ,882,870,883 }, - {3900,3478,3455 ,884,885,886 }, {1066,1065,1450 ,680,821,823 }, - {2036,2081,2035 ,887,888,889 }, {2081,2126,2068 ,888,890,891 }, - {2081,2068,2035 ,888,891,889 }, {2126,2125,2068 ,890,892,891 }, - {2274,1882,4980 ,893,894,241 }, {864,4112,839 ,840,47,52 }, {3341,863,3258 ,541,284,542 }, - {863,536,1300 ,284,523,285 }, {1393,4112,864 ,45,47,840 }, {4211,4212,321 ,215,895,60 }, - {1597,1422,18 ,505,525,896 }, {2014,4187,1923 ,842,502,418 }, - {1923,4429,736 ,418,771,843 }, {736,4429,459 ,843,771,696 }, - {358,189,4184 ,897,503,898 }, {1950,1929,1885 ,786,899,778 }, - {2050,2097,2075 ,785,846,787 }, {1780,1843,1842 ,900,901,196 }, - {1548,1549,1617 ,902,903,904 }, {8130,8120,8098 ,905,906,907 }, - {3080,1302,13 ,908,909,910 }, {1844,1845,1873 ,911,912,913 }, - {2035,2068,1978 ,889,891,847 }, {1059,169,652 ,678,248,334 }, - {485,434,526 ,863,862,718 }, {2650,3981,3054 ,914,915,916 }, - {3453,2650,3054 ,917,914,916 }, {2892,1305,536 ,918,466,523 }, - {7693,8129,3976 ,724,723,919 }, {443,171,444 ,920,921,922 }, - {2743,3687,1378 ,923,924,925 }, {3545,3595,3574 ,926,927,928 }, - {913,2892,536 ,444,918,523 }, {3432,3492,3462 ,929,930,931 }, - {3358,3383,3357 ,880,882,883 }, {5769,5768,5731 ,932,933,934 }, - {6045,6080,5453 ,935,936,937 }, {548,595,645 ,938,939,940 }, - {8070,3534,3668 ,941,942,513 }, {1586,1195,3315 ,943,725,944 }, - {399,1254,768 ,269,29,41 }, {4375,4136,399 ,312,28,269 }, {4212,294,321 ,895,945,60 }, - {356,4194,1161 ,310,293,288 }, {190,356,1161 ,287,310,288 }, - {1422,1147,997 ,525,202,328 }, {1652,174,892 ,170,169,506 }, - {1422,826,18 ,525,507,896 }, {4184,4187,2014 ,898,502,842 }, - {5977,3300,1936 ,809,694,848 }, {1752,1812,1811 ,946,947,777 }, - {1751,1752,1811 ,773,946,777 }, {1886,1885,1811 ,948,778,777 }, - {1812,1886,1811 ,947,948,777 }, {1951,1950,1885 ,949,786,778 }, - {1886,1951,1885 ,948,949,778 }, {1951,1996,1950 ,949,950,786 }, - {2051,2050,1950 ,951,785,786 }, {1996,2051,1950 ,950,951,786 }, - {2098,2097,2050 ,952,846,785 }, {2051,2098,2050 ,951,952,785 }, - {2098,2143,2097 ,952,953,846 }, {595,646,645 ,939,954,940 }, - {3492,3545,3491 ,930,926,955 }, {1045,1144,992 ,956,957,958 }, - {8128,8066,3277 ,959,960,961 }, {1874,1913,1912 ,962,963,964 }, - {386,435,434 ,865,965,862 }, {395,171,59 ,966,921,967 }, {59,171,443 ,967,921,920 }, - {3917,3916,3868 ,968,969,970 }, {3148,6891,3008 ,814,813,546 }, - {743,742,647 ,971,972,973 }, {1403,1404,1475 ,974,975,858 }, - {788,2558,600 ,976,977,978 }, {3542,3573,3541 ,979,980,981 }, - {3958,3959,3998 ,294,982,381 }, {938,4,803 ,983,538,144 }, {4,264,1031 ,538,984,163 }, - {264,4235,804 ,984,985,21 }, {4194,464,3 ,293,261,329 }, {505,5309,469 ,986,987,988 }, - {1635,1699,1442 ,989,772,990 }, {4443,4442,4391 ,703,991,992 }, - {4443,4464,4442 ,703,584,991 }, {4464,4463,4442 ,584,993,991 }, - {1328,1405,1327 ,994,995,996 }, {1838,363,1837 ,425,427,997 }, - {724,816,815 ,722,998,999 }, {1914,1983,1982 ,1000,1001,1002 }, - {1619,1686,1685 ,1003,1004,1005 }, {2124,2193,2123 ,1006,1007,1008 }, - {1681,1682,1739 ,1009,1010,1011 }, {291,336,335 ,1012,1013,1014 }, - {1663,1630,3081 ,1015,1016,1017 }, {726,725,626 ,669,721,801 }, - {627,726,626 ,670,669,801 }, {726,818,725 ,669,1018,721 }, {1476,1548,1547 ,859,902,860 }, - {906,817,818 ,1019,1020,1018 }, {3960,3959,3916 ,1021,982,969 }, - {3917,3960,3916 ,968,1021,969 }, {3960,4000,3999 ,1021,1022,1023 }, - {3959,3960,3999 ,982,1021,1023 }, {7115,7139,7129 ,1024,1025,1026 }, - {3189,3016,2150 ,1027,1028,534 }, {1031,264,804 ,163,984,21 }, - {804,4235,4503 ,21,985,569 }, {4503,4235,779 ,569,985,193 }, - {779,709,544 ,193,316,195 }, {173,704,398 ,12,7,15 }, {1161,4194,3 ,288,293,329 }, - {2352,1792,24 ,390,324,247 }, {4715,4716,4714 ,1029,1030,1031 }, - {6432,3265,1941 ,1032,1033,1034 }, {1603,1636,1635 ,1035,1036,989 }, - {1566,1603,1635 ,1037,1035,989 }, {1703,1752,1702 ,1038,946,1039 }, - {2143,2166,2165 ,953,1040,1041 }, {290,333,289 ,1042,1043,1044 }, - {1405,1404,1327 ,995,975,996 }, {1405,1477,1404 ,995,1045,975 }, - {7003,7002,6963 ,1046,1047,1048 }, {3303,3304,3323 ,1049,1050,1051 }, - {722,723,814 ,1052,1053,1054 }, {627,626,583 ,670,801,764 }, - {528,627,583 ,1055,670,764 }, {4426,6577,6837 ,1056,97,1057 }, - {1908,28,3636 ,1058,1059,1060 }, {3358,3357,3324 ,880,883,881 }, - {4042,2946,4041 ,1061,1062,1063 }, {2946,2571,3194 ,1062,1064,1065 }, - {3190,2555,2416 ,807,1066,808 }, {2558,19,600 ,977,1067,978 }, - {544,4454,1905 ,195,1068,40 }, {4454,4135,1905 ,1068,1069,40 }, - {117,1133,4213 ,9,24,1070 }, {1905,4135,4179 ,40,1069,251 }, - {1805,1490,4179 ,1071,46,251 }, {1805,4310,1490 ,1071,695,46 }, - {1805,615,4310 ,1071,766,695 }, {4521,1591,1318 ,179,1072,164 }, - {106,1207,5146 ,1073,1074,1075 }, {4421,4462,4420 ,229,1076,276 }, - {1338,4214,4029 ,1077,226,225 }, {7457,7515,7539 ,1078,1079,1080 }, - {1495,1567,1566 ,1081,1082,1037 }, {1494,1495,1566 ,1083,1081,1037 }, - {1567,1568,1603 ,1082,1084,1035 }, {1566,1567,1603 ,1037,1082,1035 }, - {1568,1637,1636 ,1084,1085,1036 }, {1603,1568,1636 ,1035,1084,1036 }, - {1637,1704,1703 ,1085,1086,1038 }, {1636,1637,1703 ,1036,1085,1038 }, - {1753,1752,1703 ,1087,946,1038 }, {1704,1753,1703 ,1086,1087,1038 }, - {1753,1795,1752 ,1087,1088,946 }, {1813,1812,1752 ,1089,947,946 }, - {1795,1813,1752 ,1088,1089,946 }, {1887,1886,1812 ,1090,948,947 }, - {1813,1887,1812 ,1089,1090,947 }, {1887,1952,1951 ,1090,1091,949 }, - {1886,1887,1951 ,948,1090,949 }, {1952,1996,1951 ,1091,950,949 }, - {2052,2051,1996 ,1092,951,950 }, {1952,2052,1996 ,1091,1092,950 }, - {2099,2098,2051 ,1093,952,951 }, {2052,2099,2051 ,1092,1093,951 }, - {2099,2143,2098 ,1093,953,952 }, {8080,8094,8069 ,1094,98,1095 }, - {3932,1059,652 ,1096,678,334 }, {6311,8122,1168 ,1097,384,1098 }, - {624,723,623 ,1099,1053,1100 }, {1979,1978,1911 ,830,847,1101 }, - {387,386,338 ,1102,865,867 }, {238,239,338 ,868,1103,867 }, {1687,1782,1781 ,1104,1105,1106 }, - {340,593,909 ,1107,1108,1109 }, {1918,908,1190 ,1110,1111,1112 }, - {5117,350,2291 ,1113,1114,1115 }, {2433,2400,2434 ,1116,1117,1118 }, - {865,776,867 ,1119,1120,1121 }, {4213,1133,4120 ,1070,24,23 }, - {321,294,1132 ,60,945,61 }, {101,173,713 ,5,12,3 }, {655,195,4056 ,1122,739,1123 }, - {195,4098,4056 ,739,353,1123 }, {660,118,67 ,387,1124,1125 }, - {1609,200,714 ,31,244,265 }, {1428,1495,1494 ,1126,1081,1083 }, - {1495,1568,1567 ,1081,1084,1082 }, {1753,1813,1795 ,1087,1089,1088 }, - {3194,2571,3016 ,1065,1064,1028 }, {2032,2123,2122 ,1127,1008,1128 }, - {647,549,2609 ,973,1129,1130 }, {2259,6314,6646 ,1131,1132,1133 }, - {1328,1329,1405 ,994,1134,995 }, {1406,1447,1446 ,1135,1136,1137 }, - {1777,1778,1839 ,856,855,1138 }, {8084,8110,8124 ,1139,1140,1141 }, - {436,435,386 ,1142,965,865 }, {339,387,338 ,1143,1102,867 }, - {486,528,434 ,1144,1055,862 }, {528,583,527 ,1055,764,800 }, - {3456,3566,3861 ,1145,1146,1147 }, {529,528,486 ,1148,1055,1144 }, - {437,486,436 ,1149,1144,1142 }, {1873,1874,1912 ,913,962,964 }, - {1616,1617,1683 ,1150,904,1151 }, {3462,3492,3461 ,931,930,1152 }, - {1880,105,2558 ,250,249,977 }, {3545,3544,3491 ,926,1153,955 }, - {2433,111,2400 ,1116,1154,1117 }, {101,704,173 ,5,7,12 }, {1132,1591,4521 ,61,1072,179 }, - {5765,5801,5764 ,540,700,537 }, {277,781,11 ,85,158,299 }, {613,277,11 ,298,85,299 }, - {879,4123,4122 ,1155,133,137 }, {3421,8113,3278 ,1156,1157,1158 }, - {911,2158,6684 ,1159,1160,1161 }, {1351,1428,8008 ,1162,1126,1163 }, - {1351,1395,1428 ,1162,1164,1126 }, {1567,1568,1495 ,1082,1084,1081 }, - {1814,1813,1753 ,1165,1089,1087 }, {1656,3227,3198 ,1166,1167,1168 }, - {449,547,546 ,1169,1170,1171 }, {3959,3999,3998 ,982,1023,381 }, - {448,449,546 ,1172,1169,1171 }, {647,691,743 ,973,1173,971 }, - {2191,2192,392 ,1174,1175,857 }, {547,644,546 ,1170,1176,1171 }, - {627,628,727 ,670,1177,668 }, {1234,1235,1328 ,1178,1179,994 }, - {1976,2032,2006 ,1180,1127,1181 }, {3323,3322,3303 ,1051,1182,1049 }, - {486,434,435 ,1144,862,965 }, {387,436,386 ,1102,1142,865 }, - {528,527,434 ,1055,800,862 }, {1405,1406,1446 ,995,1135,1137 }, - {304,488,3080 ,532,533,908 }, {3331,3364,3330 ,1183,1184,1185 }, - {1296,1880,2558 ,335,250,977 }, {7397,7453,7424 ,1186,1187,1188 }, - {1318,867,2137 ,164,1121,147 }, {3817,4756,4682 ,1189,1190,1191 }, - {5240,5264,5239 ,481,1192,482 }, {5616,5618,2011 ,646,127,129 }, - {5258,5282,5281 ,1193,1194,1195 }, {2026,1927,4234 ,130,1196,1197 }, - {2258,1263,5455 ,1198,1199,686 }, {3362,6350,6321 ,1200,704,706 }, - {2158,2272,6684 ,1160,1201,1161 }, {7151,3291,3290 ,1202,1203,1204 }, - {1353,1429,1428 ,1205,1206,1126 }, {1395,1353,1428 ,1164,1205,1126 }, - {1496,1495,1428 ,1207,1081,1126 }, {1429,1496,1428 ,1206,1207,1126 }, - {1569,1567,1495 ,1208,1082,1081 }, {1496,1569,1495 ,1207,1208,1081 }, - {1604,1568,1567 ,1209,1084,1082 }, {1569,1604,1567 ,1208,1209,1082 }, - {1638,1637,1568 ,1210,1085,1084 }, {1604,1638,1568 ,1209,1210,1084 }, - {1638,1705,1704 ,1210,1211,1086 }, {1637,1638,1704 ,1085,1210,1086 }, - {1754,1753,1704 ,1212,1087,1086 }, {1705,1754,1704 ,1211,1212,1086 }, - {1815,1814,1753 ,1213,1165,1087 }, {1754,1815,1753 ,1212,1213,1087 }, - {1862,1813,1814 ,1214,1089,1165 }, {1815,1862,1814 ,1213,1214,1165 }, - {1888,1887,1813 ,1215,1090,1089 }, {1862,1888,1813 ,1214,1215,1089 }, - {1953,1952,1887 ,1216,1091,1090 }, {1888,1953,1887 ,1215,1216,1090 }, - {2053,2052,1952 ,1217,1092,1091 }, {1953,2053,1952 ,1216,1217,1091 }, - {2100,2099,2052 ,1218,1093,1092 }, {2053,2100,2052 ,1217,1218,1092 }, - {828,827,741 ,1219,1220,1221 }, {385,433,432 ,864,861,1222 }, - {1842,1843,1910 ,196,901,1223 }, {2009,1188,862 ,1224,799,1225 }, - {1780,1803,1843 ,900,1226,901 }, {436,486,435 ,1142,1144,965 }, - {432,484,483 ,1222,1227,1228 }, {3987,3946,21 ,1229,1230,1231 }, - {1780,1781,1803 ,900,1106,1226 }, {1062,1014,929 ,1232,1233,1234 }, - {1182,1048,953 ,1235,1236,1237 }, {1183,1182,953 ,1238,1235,1237 }, - {2945,3900,3455 ,1239,884,886 }, {7115,7129,7102 ,1024,1026,1240 }, - {4173,4216,4215 ,134,1241,370 }, {4335,4280,642 ,838,1242,1243 }, - {1112,885,4202 ,292,216,260 }, {2026,4234,934 ,130,1197,131 }, - {3818,1907,2785 ,1244,1245,1246 }, {4328,4329,4378 ,166,1247,375 }, - {1207,106,3878 ,1074,1073,1248 }, {6203,322,803 ,1249,1250,144 }, - {4329,4379,4378 ,1247,567,375 }, {2376,6570,1263 ,1251,1252,1199 }, - {5059,1516,3177 ,1253,1254,1255 }, {5502,2208,5081 ,1256,1257,1258 }, - {3319,2245,2336 ,1259,1260,1261 }, {1352,1395,1351 ,1262,1164,1162 }, - {1569,1638,1604 ,1208,1210,1209 }, {2054,2053,1953 ,1263,1217,1216 }, - {3916,3959,3904 ,969,982,1264 }, {908,909,1190 ,1111,1109,1112 }, - {5117,2291,186 ,1113,1115,1265 }, {628,728,727 ,1177,1266,668 }, - {334,335,431 ,1267,1014,1268 }, {2123,2192,2191 ,1008,1175,1174 }, - {1290,1184,2009 ,795,797,1224 }, {1447,1407,1479 ,1136,1269,1270 }, - {3463,3494,3432 ,1271,736,929 }, {1857,540,1311 ,13,14,213 }, - {863,3484,3258 ,284,283,542 }, {4077,4076,4071 ,126,577,186 }, - {4255,4275,4328 ,165,699,166 }, {322,5,4 ,1250,350,538 }, {938,322,4 ,983,1250,538 }, - {5,315,264 ,350,352,984 }, {4,5,264 ,538,350,984 }, {4275,4329,4328 ,699,1247,166 }, - {6619,2245,1265 ,1272,1260,1273 }, {625,624,581 ,720,1099,1274 }, - {582,625,581 ,719,720,1274 }, {1781,1782,1803 ,1106,1105,1226 }, - {1779,1841,1840 ,1275,1276,198 }, {1918,2009,908 ,1110,1224,1111 }, - {2192,2193,170 ,1175,1007,794 }, {1145,1144,1045 ,1277,957,956 }, - {2122,2123,2191 ,1128,1008,1174 }, {524,525,580 ,1278,1279,1280 }, - {4518,1928,1879 ,1281,1282,1283 }, {926,1011,1010 ,1284,1285,1286 }, - {926,925,827 ,1284,1287,1220 }, {2068,2033,2034 ,891,1288,1289 }, - {6295,3283,8103 ,1290,262,1291 }, {3355,3354,3322 ,1292,1293,1182 }, - {1978,2068,2034 ,847,891,1289 }, {1233,1327,1232 ,1294,996,1295 }, - {221,2768,2541 ,1296,1297,1298 }, {264,315,4235 ,984,352,985 }, - {4235,4159,779 ,985,1299,193 }, {779,4159,709 ,193,1299,316 }, - {5056,5005,4950 ,1300,1301,1302 }, {3702,5520,1990 ,1303,1304,1305 }, - {6562,6299,4554 ,1306,760,1307 }, {1354,1430,1429 ,1308,1309,1206 }, - {1353,1354,1429 ,1205,1308,1206 }, {1430,1497,1496 ,1309,1310,1207 }, - {1429,1430,1496 ,1206,1309,1207 }, {1497,1570,1569 ,1310,1311,1208 }, - {1496,1497,1569 ,1207,1310,1208 }, {1570,1639,1638 ,1311,1312,1210 }, - {1569,1570,1638 ,1208,1311,1210 }, {1706,1705,1638 ,1313,1211,1210 }, - {1639,1706,1638 ,1312,1313,1210 }, {1706,1755,1754 ,1313,1314,1212 }, - {1705,1706,1754 ,1211,1313,1212 }, {1816,1815,1754 ,1315,1213,1212 }, - {1755,1816,1754 ,1314,1315,1212 }, {1863,1862,1815 ,1316,1214,1213 }, - {1816,1863,1815 ,1315,1316,1213 }, {1863,1889,1888 ,1316,1317,1215 }, - {1862,1863,1888 ,1214,1316,1215 }, {1889,1954,1953 ,1317,1318,1216 }, - {1888,1889,1953 ,1215,1317,1216 }, {2055,2054,1953 ,1319,1263,1216 }, - {1954,2055,1953 ,1318,1319,1216 }, {2076,2053,2054 ,1320,1217,1263 }, - {2055,2076,2054 ,1319,1320,1263 }, {2101,2100,2053 ,1321,1218,1217 }, - {2076,2101,2053 ,1320,1321,1217 }, {2168,2167,2100 ,1322,1323,1218 }, - {2101,2168,2100 ,1321,1322,1218 }, {2168,2090,2089 ,1322,1324,1325 }, - {2167,2168,2089 ,1323,1322,1325 }, {862,960,340 ,1225,1326,1107 }, - {908,340,909 ,1111,1107,1109 }, {1617,1684,1683 ,904,1327,1151 }, - {1047,906,2844 ,1328,1019,1329 }, {1326,1325,1231 ,1330,1331,1332 }, - {1143,1142,991 ,1333,1334,1335 }, {1232,1326,1231 ,1295,1330,1332 }, - {1188,999,862 ,799,798,1225 }, {524,580,623 ,1278,1280,1100 }, - {2193,2192,2123 ,1007,1175,1008 }, {433,485,484 ,861,863,1227 }, - {432,433,484 ,1222,861,1227 }, {339,387,239 ,1143,1102,1103 }, - {1234,1233,1142 ,1178,1294,1334 }, {108,2134,35 ,1336,1337,1338 }, - {790,742,743 ,1339,972,971 }, {1113,4213,4212 ,10,1070,895 }, - {540,1113,4211 ,14,10,215 }, {364,365,4362 ,1340,402,342 }, {365,139,4362 ,402,145,342 }, - {139,963,962 ,145,1341,233 }, {703,660,4145 ,100,387,101 }, {1168,4571,6311 ,1098,1342,1097 }, - {8079,3421,8104 ,55,1156,1343 }, {544,653,4454 ,195,318,1068 }, - {4569,2454,2272 ,1344,1345,1201 }, {6071,6377,6082 ,1346,1347,1348 }, - {6950,4981,5006 ,1349,1350,1351 }, {6562,5967,404 ,1306,1352,761 }, - {6570,6562,4554 ,1252,1306,1307 }, {1353,1267,1354 ,1205,1353,1308 }, - {1498,1571,1570 ,1354,1355,1311 }, {1497,1498,1570 ,1310,1354,1311 }, - {1570,1571,1639 ,1311,1355,1312 }, {1144,1145,1234 ,957,1277,1178 }, - {2033,2124,2123 ,1288,1006,1008 }, {3719,3515,959 ,1356,1357,1358 }, - {1547,1616,1615 ,860,1150,1359 }, {1546,1547,1615 ,1360,860,1359 }, - {2084,1048,1049 ,1361,1236,1362 }, {742,828,741 ,972,1219,1221 }, - {689,3315,3828 ,1363,944,1364 }, {526,525,484 ,718,1279,1227 }, - {485,526,484 ,863,718,1227 }, {1240,1290,1918 ,796,795,1110 }, - {828,926,827 ,1219,1284,1220 }, {35,751,6228 ,1338,1365,1366 }, - {744,790,743 ,1367,1339,971 }, {829,828,742 ,1368,1219,972 }, - {5801,5800,5764 ,700,702,537 }, {6577,4426,6837 ,97,1056,1057 }, - {469,468,367 ,988,1369,1370 }, {653,4053,4135 ,318,1371,1069 }, - {4454,653,4135 ,1068,318,1069 }, {376,1343,1564 ,379,378,368 }, - {2259,6646,8103 ,1131,1133,1291 }, {4179,4095,1805 ,251,349,1071 }, - {5843,1028,2958 ,1372,1373,1374 }, {5497,4530,2239 ,1375,1376,1377 }, - {2158,4569,2272 ,1160,1344,1201 }, {3582,3454,3660 ,1378,710,709 }, - {6299,6562,404 ,760,1306,761 }, {1465,1498,1497 ,1379,1354,1310 }, - {1430,1465,1497 ,1309,1379,1310 }, {632,1184,1290 ,1380,797,795 }, - {506,632,690 ,1381,1380,1382 }, {170,506,690 ,794,1381,1382 }, - {3903,3957,3956 ,296,295,1383 }, {1000,860,1876 ,1384,1385,1386 }, - {1782,1844,1843 ,1105,911,901 }, {1777,1839,1838 ,856,1138,425 }, - {1803,1782,1843 ,1226,1105,901 }, {401,448,2445 ,1387,1172,1388 }, - {6612,6648,6611 ,1389,1390,1391 }, {790,829,742 ,1339,1368,972 }, - {198,197,126 ,1392,1393,1394 }, {127,198,126 ,1395,1392,1394 }, - {368,367,197 ,1396,1370,1393 }, {198,368,197 ,1392,1396,1393 }, - {368,469,367 ,1396,988,1370 }, {614,615,1805 ,1397,766,1071 }, - {6646,6295,8103 ,1133,1290,1291 }, {1564,1595,4145 ,368,99,101 }, - {1094,839,1481 ,1398,52,194 }, {2408,5231,2219 ,1399,115,177 }, - {6562,3898,5967 ,1306,1400,1352 }, {1116,5497,2239 ,1401,1375,1377 }, - {7478,7477,7422 ,1402,1403,1404 }, {8020,2320,8019 ,1405,1406,1407 }, - {1355,1354,1267 ,1408,1308,1353 }, {1314,1355,1267 ,1409,1408,1353 }, - {1431,1430,1354 ,1410,1309,1308 }, {1355,1431,1354 ,1408,1410,1308 }, - {1431,1466,1465 ,1410,1411,1379 }, {1430,1431,1465 ,1309,1410,1379 }, - {1466,1499,1498 ,1411,1412,1354 }, {1465,1466,1498 ,1379,1411,1354 }, - {1499,1572,1571 ,1412,1413,1355 }, {1498,1499,1571 ,1354,1412,1355 }, - {1640,1639,1571 ,1414,1312,1355 }, {1572,1640,1571 ,1413,1414,1355 }, - {1640,1707,1706 ,1414,1415,1313 }, {1639,1640,1706 ,1312,1414,1313 }, - {1756,1755,1706 ,1416,1314,1313 }, {1707,1756,1706 ,1415,1416,1313 }, - {1756,1817,1816 ,1416,1417,1315 }, {1755,1756,1816 ,1314,1416,1315 }, - {1817,1818,1863 ,1417,1418,1316 }, {1816,1817,1863 ,1315,1417,1316 }, - {1818,1890,1889 ,1418,1419,1317 }, {1863,1818,1889 ,1316,1418,1317 }, - {1890,1955,1954 ,1419,1420,1318 }, {1889,1890,1954 ,1317,1419,1318 }, - {1955,2056,2055 ,1420,1421,1319 }, {1954,1955,2055 ,1318,1420,1319 }, - {2056,2057,2076 ,1421,1422,1320 }, {2055,2056,2076 ,1319,1421,1320 }, - {2102,2101,2076 ,1423,1321,1320 }, {2057,2102,2076 ,1422,1423,1320 }, - {2102,2169,2168 ,1423,1424,1322 }, {2101,2102,2168 ,1321,1423,1322 }, - {2169,2135,2090 ,1424,1425,1324 }, {2168,2169,2090 ,1322,1424,1324 }, - {631,2090,2135 ,1426,1324,1425 }, {992,906,1045 ,958,1019,956 }, - {902,901,814 ,1427,1428,1054 }, {1845,1874,1873 ,912,962,913 }, - {692,293,1182 ,1429,1430,1235 }, {1683,1777,1776 ,1151,856,1431 }, - {1910,940,1842 ,1223,197,196 }, {1739,1775,1801 ,1011,1432,1433 }, - {1236,1235,1145 ,1434,1179,1277 }, {1980,1979,1911 ,828,830,1101 }, - {991,1142,990 ,1335,1334,1435 }, {1848,1916,1915 ,1436,832,1437 }, - {3654,3712,3653 ,1438,1439,1440 }, {927,926,828 ,1441,1284,1219 }, - {3378,3377,3351 ,1442,1443,1444 }, {3534,8081,8072 ,942,1445,511 }, - {829,927,828 ,1368,1441,1219 }, {4329,4276,4355 ,1247,1446,1447 }, - {864,839,1094 ,840,52,1398 }, {193,768,864 ,1448,41,840 }, {4717,367,468 ,1449,1370,1369 }, - {2245,6344,1265 ,1260,1450,1273 }, {1263,6323,6509 ,1199,1451,1452 }, - {6163,6029,6066 ,1453,1454,1455 }, {1431,1499,1466 ,1410,1412,1411 }, - {1756,1818,1817 ,1416,1418,1417 }, {1981,2008,2037 ,1456,1457,1458 }, - {5587,5626,5586 ,1459,1460,1461 }, {595,596,646 ,939,1462,954 }, - {728,726,727 ,1266,669,668 }, {305,440,243 ,1463,1464,1465 }, - {692,1182,1183 ,1429,1235,1238 }, {154,692,1183 ,1466,1429,1238 }, - {1235,1236,1237 ,1179,1434,1467 }, {2128,2129,2149 ,1468,1469,1470 }, - {1012,1011,926 ,1471,1285,1284 }, {927,1012,926 ,1441,1471,1284 }, - {1012,1060,1011 ,1471,1472,1285 }, {1104,1103,1011 ,1473,1474,1285 }, - {1060,1104,1011 ,1472,1473,1285 }, {1104,1157,1103 ,1473,1475,1474 }, - {1157,1040,1201 ,1475,1476,1477 }, {874,36,3866 ,636,1478,1479 }, - {1103,1157,1201 ,1474,1475,1477 }, {409,150,89 ,1480,1481,1482 }, - {880,4093,1441 ,1483,1484,1485 }, {4109,4149,4090 ,576,1486,1487 }, - {4613,278,4590 ,1488,1489,1490 }, {3898,4987,5967 ,1400,1491,1352 }, - {4568,185,3247 ,1492,1493,1494 }, {1955,2057,2056 ,1420,1422,1421 }, - {484,525,524 ,1227,1279,1278 }, {431,432,483 ,1268,1222,1228 }, - {483,484,524 ,1228,1227,1278 }, {596,647,646 ,1462,973,954 }, - {1687,1781,1780 ,1104,1106,900 }, {1327,1404,1403 ,996,975,974 }, - {1190,909,1189 ,1112,1109,1495 }, {2585,2650,3453 ,875,914,917 }, - {933,692,154 ,1496,1429,1466 }, {155,933,154 ,1497,1496,1466 }, - {990,1141,1140 ,1435,1498,1499 }, {1235,1370,1329 ,1179,1500,1134 }, - {1000,1373,1515 ,1384,1501,1502 }, {1978,1977,1867 ,847,1503,1504 }, - {2130,2198,2197 ,1505,1506,1507 }, {1224,1040,1906 ,1508,1476,1509 }, - {2036,2035,1978 ,887,889,847 }, {3286,8104,79 ,56,1343,1510 }, - {2007,2036,1978 ,829,887,847 }, {3855,3854,3812 ,1511,1512,1513 }, - {3381,3380,3354 ,871,1514,1293 }, {3813,3855,3812 ,1515,1511,1513 }, - {2651,2746,3768 ,1516,1517,1518 }, {1463,3258,3681 ,220,542,691 }, - {1874,1847,1913 ,962,1519,963 }, {2082,2081,2036 ,1520,888,887 }, - {6432,1941,3252 ,1032,1034,781 }, {369,470,469 ,1521,1522,988 }, - {368,369,469 ,1396,1521,988 }, {507,505,469 ,1523,986,988 }, - {470,507,469 ,1522,1523,988 }, {4213,4120,294 ,1070,23,945 }, - {277,1992,781 ,85,834,158 }, {1494,3862,1428 ,1083,550,1126 }, - {4569,2808,2454 ,1344,522,1345 }, {2245,6619,2336 ,1260,1272,1261 }, - {6480,5276,5350 ,1524,1525,1526 }, {7239,7261,7219 ,1527,1528,1529 }, - {1356,1432,1431 ,1530,1531,1410 }, {1355,1356,1431 ,1408,1530,1410 }, - {1500,1499,1431 ,1532,1412,1410 }, {1432,1500,1431 ,1531,1532,1410 }, - {1573,1572,1499 ,1533,1413,1412 }, {1500,1573,1499 ,1532,1533,1412 }, - {1573,1641,1640 ,1533,1534,1414 }, {1572,1573,1640 ,1413,1533,1414 }, - {1708,1707,1640 ,1535,1415,1414 }, {1641,1708,1640 ,1534,1535,1414 }, - {1708,1757,1756 ,1535,1536,1416 }, {1707,1708,1756 ,1415,1535,1416 }, - {1757,1796,1756 ,1536,1537,1416 }, {1796,1819,1818 ,1537,1538,1418 }, - {1756,1796,1818 ,1416,1537,1418 }, {1891,1890,1818 ,1539,1419,1418 }, - {1819,1891,1818 ,1538,1539,1418 }, {1956,1955,1890 ,1540,1420,1419 }, - {1891,1956,1890 ,1539,1540,1419 }, {1956,2058,2057 ,1540,1541,1422 }, - {1955,1956,2057 ,1420,1540,1422 }, {2058,2103,2102 ,1541,1542,1423 }, - {2057,2058,2102 ,1422,1541,1423 }, {2103,2170,2169 ,1542,1543,1424 }, - {2102,2103,2169 ,1423,1542,1424 }, {2226,2135,2169 ,1544,1425,1424 }, - {2170,2226,2169 ,1543,1544,1424 }, {2226,2152,2136 ,1544,1545,1546 }, - {2135,2226,2136 ,1425,1544,1546 }, {1184,1188,2009 ,797,799,1224 }, - {1980,1981,2036 ,828,1456,887 }, {1985,729,730 ,1547,1548,1549 }, - {2197,933,155 ,1507,1496,1497 }, {2196,2197,155 ,1550,1507,1497 }, - {2037,2038,2082 ,1458,1551,1520 }, {2083,2128,2127 ,1552,1468,1553 }, - {2038,2083,2082 ,1551,1552,1520 }, {2198,2199,2216 ,1506,1554,1555 }, - {992,1144,991 ,958,957,1335 }, {6393,3358,3305 ,1556,880,879 }, - {1911,1978,1867 ,1101,847,1504 }, {1263,4554,6323 ,1199,1307,1451 }, - {337,385,336 ,866,864,1013 }, {1549,1548,1476 ,903,902,859 }, - {1477,1476,1404 ,1045,859,975 }, {3664,296,3475 ,1557,1558,1559 }, - {2746,3566,3768 ,1517,1146,1518 }, {526,625,582 ,718,720,719 }, - {2122,2031,2032 ,1128,1560,1127 }, {2148,2147,2126 ,1561,1562,890 }, - {1618,1685,1684 ,1563,1005,1327 }, {525,581,580 ,1279,1274,1280 }, - {3987,4227,3946 ,1229,1564,1230 }, {294,4120,4375 ,945,23,312 }, - {4548,4568,3247 ,589,1492,1494 }, {5113,6098,5914 ,141,1565,1566 }, - {1501,1500,1432 ,1567,1532,1531 }, {2259,8103,6351 ,1131,1291,1568 }, - {860,1238,1183 ,1385,1569,1238 }, {2082,2083,2127 ,1520,1552,1553 }, - {2128,2149,2148 ,1468,1470,1561 }, {2149,2197,2196 ,1470,1507,1550 }, - {2127,2128,2148 ,1553,1468,1561 }, {2148,2149,2196 ,1561,1470,1550 }, - {2130,2131,2198 ,1505,656,1506 }, {724,723,624 ,722,1053,1099 }, - {2199,2200,2216 ,1554,1570,1555 }, {1684,1685,1721 ,1327,1005,854 }, - {1683,1721,1777 ,1151,854,856 }, {1618,1619,1685 ,1563,1003,1005 }, - {240,4183,260 ,1571,1572,1573 }, {3263,2746,2651 ,252,1517,1516 }, - {392,305,2190 ,857,1463,1574 }, {3915,3903,3956 ,1575,296,1383 }, - {2869,2832,2868 ,1576,638,1577 }, {1913,1981,1980 ,963,1456,828 }, - {1885,4698,1810 ,778,1578,779 }, {649,444,492 ,1579,922,1580 }, - {399,768,193 ,269,41,1448 }, {1591,399,1032 ,1072,269,1581 }, - {1032,399,193 ,1581,269,1448 }, {4375,399,1591 ,312,269,1072 }, - {3777,4261,5733 ,1582,1583,1584 }, {1492,1749,5075 ,1585,555,1586 }, - {5889,6163,6066 ,1587,1453,1455 }, {5511,5512,5548 ,1588,1589,1590 }, - {3252,6553,6432 ,781,780,1032 }, {1757,1820,1819 ,1536,1591,1538 }, - {1796,1757,1819 ,1537,1536,1538 }, {1820,1891,1819 ,1591,1539,1538 }, - {2129,2130,2149 ,1469,1505,1470 }, {1913,1914,1981 ,963,1000,1456 }, - {1982,1983,2008 ,1002,1001,1457 }, {1983,2038,2037 ,1001,1551,1458 }, - {1981,1982,2008 ,1456,1002,1457 }, {2008,1983,2037 ,1457,1001,1458 }, - {2067,2005,2006 ,1592,1593,1181 }, {1867,1977,2390 ,1504,1503,1594 }, - {2131,2199,2198 ,656,1554,1506 }, {625,724,624 ,720,722,1099 }, - {741,740,644 ,1221,1595,1176 }, {1239,1240,1190 ,1596,796,1112 }, - {645,741,644 ,940,1221,1176 }, {547,548,644 ,1170,938,1176 }, - {690,632,1290 ,1382,1380,795 }, {2034,2033,1977 ,1289,1288,1503 }, - {2120,2121,2189 ,1597,1598,1599 }, {440,394,243 ,1464,1600,1465 }, - {2609,744,691 ,1130,1367,1173 }, {1779,1780,1842 ,1275,900,196 }, - {4035,4036,2151 ,1601,1602,1603 }, {1088,1186,1452 ,1604,1605,1606 }, - {6347,7254,6348 ,451,450,1607 }, {271,370,369 ,1608,1609,1521 }, - {370,418,369 ,1609,1610,1521 }, {418,471,470 ,1610,1611,1522 }, - {508,507,470 ,1612,1523,1522 }, {471,508,470 ,1611,1612,1522 }, - {566,565,507 ,1613,1614,1523 }, {508,566,507 ,1612,1613,1523 }, - {755,4084,4113 ,1615,205,207 }, {2030,5203,2698 ,770,102,105 }, - {229,1112,356 ,221,292,310 }, {7826,7853,7801 ,1616,1617,1618 }, - {1030,978,4991 ,1619,1620,1621 }, {6736,6790,6754 ,1622,711,713 }, - {6092,6176,5940 ,1623,1624,1625 }, {1357,1356,1315 ,1626,1530,1627 }, - {4576,4596,7524 ,1628,1629,1630 }, {1357,1433,1432 ,1626,1631,1531 }, - {1356,1357,1432 ,1530,1626,1531 }, {1433,1502,1501 ,1631,1632,1567 }, - {1432,1433,1501 ,1531,1631,1567 }, {1535,1500,1501 ,1633,1532,1567 }, - {1502,1535,1501 ,1632,1633,1567 }, {1574,1573,1500 ,1634,1533,1532 }, - {1535,1574,1500 ,1633,1634,1532 }, {1574,1642,1641 ,1634,1635,1534 }, - {1573,1574,1641 ,1533,1634,1534 }, {1642,1709,1708 ,1635,1636,1535 }, - {1641,1642,1708 ,1534,1635,1535 }, {1709,1758,1757 ,1636,1637,1536 }, - {1708,1709,1757 ,1535,1636,1536 }, {1758,1821,1820 ,1637,1638,1591 }, - {1757,1758,1820 ,1536,1637,1591 }, {1892,1891,1820 ,1639,1539,1591 }, - {1821,1892,1820 ,1638,1639,1591 }, {1957,1956,1891 ,1640,1540,1539 }, - {1892,1957,1891 ,1639,1640,1539 }, {1957,2016,1956 ,1640,1641,1540 }, - {2016,2059,2058 ,1641,1642,1541 }, {1956,2016,2058 ,1540,1641,1541 }, - {2104,2103,2058 ,1643,1542,1541 }, {2059,2104,2058 ,1642,1643,1541 }, - {2171,2170,2103 ,1644,1543,1542 }, {2104,2171,2103 ,1643,1644,1542 }, - {2171,2227,2226 ,1644,1645,1544 }, {2170,2171,2226 ,1543,1644,1544 }, - {2227,2161,2152 ,1645,1646,1545 }, {2226,2227,2152 ,1544,1645,1545 }, - {2161,391,2152 ,1646,1647,1545 }, {776,782,777 ,1120,1648,421 }, - {1914,1982,1981 ,1000,1002,1456 }, {1142,1141,990 ,1334,1498,1435 }, - {1403,1475,1474 ,974,858,1649 }, {3645,3644,3591 ,1650,1651,1652 }, - {450,548,547 ,1653,938,1170 }, {548,645,644 ,938,940,1176 }, - {1240,1239,305 ,796,1596,1463 }, {1617,1618,1684 ,904,1563,1327 }, - {221,241,240 ,1296,314,1571 }, {102,221,240 ,432,1296,1571 }, - {109,2039,349 ,1654,1655,566 }, {7747,7745,7746 ,331,650,332 }, - {1104,1040,1157 ,1473,1476,1475 }, {3913,2030,2698 ,841,770,105 }, - {5641,5642,5657 ,510,526,48 }, {1463,4326,3258 ,220,219,542 }, - {5337,5338,4914 ,572,63,65 }, {639,715,950 ,291,168,330 }, {4222,4131,4251 ,530,1656,132 }, - {1357,1315,1316 ,1626,1627,1657 }, {1502,1574,1535 ,1632,1634,1633 }, - {1709,1759,1758 ,1636,1658,1637 }, {6686,3534,8070 ,750,942,941 }, - {1785,1849,1848 ,1659,1660,1436 }, {2216,968,293 ,1555,1661,1430 }, - {1784,1785,1848 ,1662,1659,1436 }, {1875,1917,1916 ,1663,833,832 }, - {1848,1875,1916 ,1436,1663,832 }, {449,450,547 ,1169,1653,1170 }, - {1106,1203,1202 ,1664,337,339 }, {1326,1327,1403 ,1330,996,974 }, - {687,1744,2088 ,1665,1666,1667 }, {596,595,548 ,1462,939,938 }, - {1685,1722,1721 ,1005,1668,854 }, {2216,2200,968 ,1555,1570,1661 }, - {102,1553,221 ,432,1669,1296 }, {7194,7208,7225 ,1670,1671,1672 }, - {1144,1234,1142 ,957,1178,1334 }, {471,566,508 ,1611,1613,1612 }, - {200,1348,714 ,244,148,265 }, {1348,639,714 ,148,291,265 }, {737,229,356 ,150,221,310 }, - {1033,896,888 ,306,256,238 }, {4483,4525,4482 ,549,623,583 }, - {4473,4497,4520 ,270,91,1673 }, {826,189,738 ,507,503,1674 }, - {1822,1821,1758 ,1675,1638,1637 }, {1759,1822,1758 ,1658,1675,1637 }, - {1821,1822,1892 ,1638,1675,1639 }, {1957,2059,2016 ,1640,1642,1641 }, - {2172,2171,2104 ,1676,1644,1643 }, {1849,1875,1848 ,1660,1663,1436 }, - {2033,2032,1976 ,1288,1127,1180 }, {1233,1234,1327 ,1294,1178,996 }, - {339,436,387 ,1143,1142,1102 }, {221,748,241 ,1296,1677,314 }, - {284,4283,1241 ,139,1678,313 }, {2893,5307,4393 ,1679,461,463 }, - {272,371,370 ,1680,1681,1609 }, {271,272,370 ,1608,1680,1609 }, - {371,419,418 ,1681,1682,1610 }, {370,371,418 ,1609,1681,1610 }, - {419,472,471 ,1682,1683,1611 }, {418,419,471 ,1610,1682,1611 }, - {567,566,471 ,1684,1613,1611 }, {472,567,471 ,1683,1684,1611 }, - {715,639,497 ,168,291,290 }, {4483,1652,4525 ,549,170,623 }, - {1021,3,4187 ,289,329,502 }, {4497,4534,136 ,91,403,1685 }, {4525,1673,4513 ,623,1686,624 }, - {1274,1275,1316 ,1687,1688,1657 }, {1275,1358,1357 ,1688,1689,1626 }, - {1316,1275,1357 ,1657,1688,1626 }, {1434,1433,1357 ,1690,1631,1626 }, - {1358,1434,1357 ,1689,1690,1626 }, {1503,1502,1433 ,1691,1632,1631 }, - {1434,1503,1433 ,1690,1691,1631 }, {1575,1574,1502 ,1692,1634,1632 }, - {1503,1575,1502 ,1691,1692,1632 }, {1643,1642,1574 ,1693,1635,1634 }, - {1575,1643,1574 ,1692,1693,1634 }, {1643,1710,1709 ,1693,1694,1636 }, - {1642,1643,1709 ,1635,1693,1636 }, {1710,1711,1709 ,1694,1695,1636 }, - {1711,1760,1759 ,1695,1696,1658 }, {1709,1711,1759 ,1636,1695,1658 }, - {1823,1822,1759 ,1697,1675,1658 }, {1760,1823,1759 ,1696,1697,1658 }, - {1823,1893,1892 ,1697,1698,1639 }, {1822,1823,1892 ,1675,1697,1639 }, - {1893,1958,1957 ,1698,1699,1640 }, {1892,1893,1957 ,1639,1698,1640 }, - {2060,2059,1957 ,1700,1642,1640 }, {1958,2060,1957 ,1699,1700,1640 }, - {2060,2077,2059 ,1700,1701,1642 }, {2105,2104,2059 ,1702,1643,1642 }, - {2077,2105,2059 ,1701,1702,1642 }, {2173,2172,2104 ,1703,1676,1643 }, - {2105,2173,2104 ,1702,1703,1643 }, {2173,2174,2171 ,1703,1704,1644 }, - {2172,2173,2171 ,1676,1703,1644 }, {2174,2228,2227 ,1704,1705,1645 }, - {2171,2174,2227 ,1644,1704,1645 }, {2228,2214,2161 ,1705,1706,1646 }, - {2227,2228,2161 ,1645,1705,1646 }, {680,391,2161 ,1707,1647,1646 }, - {2214,680,2161 ,1706,1707,1646 }, {680,1624,1623 ,1707,1708,1709 }, - {391,680,1623 ,1647,1707,1709 }, {8061,8097,8082 ,453,1710,454 }, - {2038,2069,2083 ,1551,1711,1552 }, {1778,1779,1840 ,855,1275,198 }, - {4426,4021,8100 ,1056,1712,1713 }, {2121,2122,2190 ,1598,1128,1574 }, - {170,690,1290 ,794,1382,795 }, {7977,7976,7927 ,1714,1715,1716 }, - {1847,1914,1913 ,1519,1000,963 }, {242,1688,1389 ,1717,1718,315 }, - {241,242,1389 ,314,1717,315 }, {1688,133,1241 ,1718,1719,313 }, - {1389,1688,1241 ,315,1718,313 }, {3451,3516,2582 ,1720,1721,873 }, - {1912,1980,1911 ,964,828,1101 }, {204,272,271 ,1722,1680,1608 }, - {568,567,472 ,1723,1684,1683 }, {4224,896,1033 ,309,256,306 }, - {888,1006,799 ,238,234,392 }, {4520,4497,136 ,1673,91,1685 }, - {7928,7927,7876 ,1724,1716,1725 }, {1576,1575,1503 ,1726,1692,1691 }, - {1576,1643,1575 ,1726,1693,1692 }, {1643,1711,1710 ,1693,1695,1694 }, - {8124,3271,2810 ,1141,1727,1728 }, {905,904,858 ,1729,1730,1731 }, - {2126,2147,2146 ,890,1562,1732 }, {859,905,858 ,1733,1729,1731 }, - {1841,1842,1840 ,1276,196,198 }, {1619,1618,1549 ,1003,1563,903 }, - {815,903,902 ,999,1734,1427 }, {2541,242,241 ,1298,1717,314 }, - {883,76,146 ,119,689,504 }, {3516,3537,2582 ,1721,1735,873 }, - {3477,5785,4162 ,1736,1737,1738 }, {5480,4527,4162 ,1739,1740,1738 }, - {999,1985,960 ,798,1547,1326 }, {272,204,205 ,1680,1722,1741 }, - {4106,8142,8078 ,1742,1743,1744 }, {4483,4482,4444 ,549,583,582 }, - {174,1147,1422 ,169,202,525 }, {136,4534,1628 ,1685,403,118 }, - {189,4187,4184 ,503,502,898 }, {1644,1643,1576 ,1745,1693,1726 }, - {1643,1644,1711 ,1693,1745,1695 }, {2105,2174,2173 ,1702,1704,1703 }, - {1726,1785,1784 ,1746,1659,1662 }, {1725,1726,1784 ,1747,1746,1662 }, - {1838,1839,4427 ,425,1138,426 }, {2031,2067,2029 ,1560,1592,1748 }, - {273,372,371 ,1749,1750,1681 }, {372,420,419 ,1750,1751,1682 }, - {371,372,419 ,1681,1750,1682 }, {420,473,472 ,1751,1752,1683 }, - {419,420,472 ,1682,1751,1683 }, {569,568,472 ,1753,1723,1683 }, - {473,569,472 ,1752,1753,1683 }, {569,665,664 ,1753,1754,1755 }, - {568,569,664 ,1723,1753,1755 }, {1422,997,826 ,525,328,507 }, - {826,997,1021 ,507,328,289 }, {1034,1127,1172 ,1756,1757,1758 }, - {1172,1127,1221 ,1758,1757,1759 }, {1127,1276,1275 ,1757,1760,1688 }, - {1221,1127,1275 ,1759,1757,1688 }, {1276,1359,1358 ,1760,1761,1689 }, - {1275,1276,1358 ,1688,1760,1689 }, {1359,1435,1434 ,1761,1762,1690 }, - {1358,1359,1434 ,1689,1761,1690 }, {1504,1503,1434 ,1763,1691,1690 }, - {1435,1504,1434 ,1762,1763,1690 }, {1577,1576,1503 ,1764,1726,1691 }, - {1504,1577,1503 ,1763,1764,1691 }, {1645,1644,1576 ,1765,1745,1726 }, - {1577,1645,1576 ,1764,1765,1726 }, {1645,1712,1711 ,1765,1766,1695 }, - {1644,1645,1711 ,1745,1765,1695 }, {1761,1760,1711 ,1767,1696,1695 }, - {1712,1761,1711 ,1766,1767,1695 }, {1761,1824,1823 ,1767,1768,1697 }, - {1760,1761,1823 ,1696,1767,1697 }, {1894,1893,1823 ,1769,1698,1697 }, - {1824,1894,1823 ,1768,1769,1697 }, {1894,1959,1958 ,1769,1770,1699 }, - {1893,1894,1958 ,1698,1769,1699 }, {1959,2061,2060 ,1770,1771,1700 }, - {1958,1959,2060 ,1699,1770,1700 }, {2061,2062,2077 ,1771,1772,1701 }, - {2060,2061,2077 ,1700,1771,1701 }, {2062,2106,2105 ,1772,1773,1702 }, - {2077,2062,2105 ,1701,1772,1702 }, {2106,2175,2174 ,1773,1774,1704 }, - {2105,2106,2174 ,1702,1773,1704 }, {2175,2229,2228 ,1774,1775,1705 }, - {2174,2175,2228 ,1704,1774,1705 }, {2229,1559,2214 ,1775,1776,1706 }, - {2228,2229,2214 ,1705,1775,1706 }, {633,680,2214 ,1777,1707,1706 }, - {1559,633,2214 ,1776,1777,1706 }, {633,1624,680 ,1777,1708,1707 }, - {1187,1186,1624 ,1778,1605,1708 }, {633,1187,1624 ,1777,1778,1708 }, - {1187,1452,1186 ,1778,1606,1605 }, {1187,1919,1452 ,1778,1779,1606 }, - {8110,5521,8125 ,1140,751,752 }, {1326,1403,1402 ,1330,974,1780 }, - {1657,1658,1725 ,1781,1782,1747 }, {1688,134,133 ,1718,1783,1719 }, - {134,80,284 ,1783,140,139 }, {133,134,284 ,1719,1783,139 }, {8125,8071,8068 ,752,738,1784 }, - {272,273,371 ,1680,1749,1681 }, {493,175,492 ,1785,211,1580 }, - {6384,3362,3387 ,1786,1200,1787 }, {1034,1035,1127 ,1756,1788,1757 }, - {1959,2062,2061 ,1770,1772,1771 }, {3712,3758,3711 ,1439,1789,1790 }, - {3445,3648,3596 ,1791,734,1792 }, {1017,3513,3452 ,1793,441,1794 }, - {108,35,36 ,1336,1338,1478 }, {3148,3008,2743 ,814,546,923 }, - {1658,1726,1725 ,1782,1746,1747 }, {5057,8096,6122 ,1795,1796,1797 }, - {3456,3405,2920 ,1145,1798,1799 }, {5237,5600,2086 ,1800,1801,1802 }, - {5151,5171,6114 ,1803,1804,1805 }, {1549,1618,1617 ,903,1563,904 }, - {982,1035,1034 ,1806,1788,1756 }, {943,982,1034 ,1807,1806,1756 }, - {1959,2017,2062 ,1770,1808,1772 }, {3687,2743,3873 ,924,923,1809 }, - {3597,3445,3596 ,1810,1791,1792 }, {2121,2120,2028 ,1598,1597,1811 }, - {3537,2072,2474 ,1735,1812,874 }, {581,624,623 ,1274,1099,1100 }, - {903,990,989 ,1734,1435,1813 }, {2148,2196,2147 ,1561,1550,1562 }, - {453,82,80 ,1814,138,140 }, {5137,4631,5136 ,1815,1816,1817 }, - {1477,1549,1476 ,1045,903,859 }, {4367,4390,4343 ,228,230,1818 }, - {412,4150,1793 ,358,320,319 }, {274,373,372 ,1819,1820,1750 }, - {273,274,372 ,1749,1819,1750 }, {373,374,420 ,1820,1821,1751 }, - {372,373,420 ,1750,1820,1751 }, {374,474,473 ,1821,1822,1752 }, - {420,374,473 ,1751,1821,1752 }, {474,570,569 ,1822,1823,1753 }, - {473,474,569 ,1752,1822,1753 }, {570,666,665 ,1823,1824,1754 }, - {569,570,665 ,1753,1823,1754 }, {666,762,761 ,1824,1825,1826 }, - {665,666,761 ,1754,1824,1826 }, {762,846,845 ,1825,1827,1828 }, - {761,762,845 ,1826,1825,1828 }, {846,944,943 ,1827,1829,1807 }, - {845,846,943 ,1828,1827,1807 }, {943,944,982 ,1807,1829,1806 }, - {944,1036,1035 ,1829,1830,1788 }, {982,944,1035 ,1806,1829,1788 }, - {1036,1128,1127 ,1830,1831,1757 }, {1035,1036,1127 ,1788,1830,1757 }, - {1277,1276,1127 ,1832,1760,1757 }, {1128,1277,1127 ,1831,1832,1757 }, - {1360,1359,1276 ,1833,1761,1760 }, {1277,1360,1276 ,1832,1833,1760 }, - {1360,1436,1435 ,1833,1834,1762 }, {1359,1360,1435 ,1761,1833,1762 }, - {1505,1504,1435 ,1835,1763,1762 }, {1436,1505,1435 ,1834,1835,1762 }, - {1505,1578,1577 ,1835,1836,1764 }, {1504,1505,1577 ,1763,1835,1764 }, - {1578,1646,1645 ,1836,1837,1765 }, {1577,1578,1645 ,1764,1836,1765 }, - {1646,1713,1712 ,1837,1838,1766 }, {1645,1646,1712 ,1765,1837,1766 }, - {1713,1762,1761 ,1838,1839,1767 }, {1712,1713,1761 ,1766,1838,1767 }, - {1825,1824,1761 ,1840,1768,1767 }, {1762,1825,1761 ,1839,1840,1767 }, - {1825,1895,1894 ,1840,1841,1769 }, {1824,1825,1894 ,1768,1840,1769 }, - {1895,1960,1959 ,1841,1842,1770 }, {1894,1895,1959 ,1769,1841,1770 }, - {1960,1997,1959 ,1842,1843,1770 }, {1959,1997,2017 ,1770,1843,1808 }, - {1997,2063,2062 ,1843,1844,1772 }, {2017,1997,2062 ,1808,1843,1772 }, - {2063,2107,2106 ,1844,1845,1773 }, {2062,2063,2106 ,1772,1844,1773 }, - {2107,2108,2106 ,1845,1846,1773 }, {2108,2176,2175 ,1846,1847,1774 }, - {2106,2108,2175 ,1773,1846,1774 }, {2176,2230,2229 ,1847,1848,1775 }, - {2175,2176,2229 ,1774,1847,1775 }, {2230,866,1559 ,1848,1849,1776 }, - {2229,2230,1559 ,1775,1848,1776 }, {866,550,633 ,1849,1850,1777 }, - {1559,866,633 ,1776,1849,1777 }, {550,1244,1187 ,1850,575,1778 }, - {633,550,1187 ,1777,1850,1778 }, {1244,1367,1187 ,575,574,1778 }, - {2195,341,2194 ,1851,1852,1853 }, {7275,7254,7253 ,1854,450,1855 }, - {1977,2033,1976 ,1503,1288,1180 }, {2582,3537,2474 ,873,1735,874 }, - {902,903,901 ,1427,1734,1428 }, {1232,1231,1140 ,1295,1332,1499 }, - {1141,1232,1140 ,1498,1295,1499 }, {2082,2127,2081 ,1520,1553,888 }, - {51,301,453 ,1856,1857,1814 }, {80,51,453 ,140,1856,1814 }, {94,82,301 ,1858,138,1857 }, - {1693,3425,3283 ,264,1859,262 }, {5219,5240,5218 ,1860,481,483 }, - {3344,4221,592 ,1861,1862,1863 }, {772,891,4540 ,18,189,19 }, - {274,374,373 ,1819,1821,1820 }, {4314,1561,1525 ,1864,1865,1866 }, - {3591,3644,3590 ,1652,1651,1867 }, {2743,3931,3873 ,923,1868,1809 }, - {2125,2126,2146 ,892,890,1732 }, {723,815,814 ,1053,999,1054 }, - {2196,155,165 ,1550,1497,1869 }, {2195,2196,165 ,1851,1550,1869 }, - {2939,80,134 ,1870,140,1783 }, {5680,2453,5698 ,50,42,44 }, {274,320,374 ,1819,1871,1821 }, - {2063,2108,2107 ,1844,1846,1845 }, {550,1408,1244 ,1850,1872,575 }, - {1367,2070,614 ,574,573,1397 }, {7510,7509,7452 ,1873,1874,1875 }, - {859,858,816 ,1733,1731,998 }, {305,243,2189 ,1463,1465,1599 }, - {51,355,301 ,1856,1876,1857 }, {94,157,196 ,1858,1877,152 }, - {2743,3008,3944 ,923,546,729 }, {5180,5179,5136 ,1878,1879,1817 }, - {5658,2453,5680 ,49,42,50 }, {770,2352,805 ,275,390,389 }, {4064,4069,3317 ,1880,1881,1882 }, - {375,475,474 ,1883,1884,1822 }, {374,375,474 ,1821,1883,1822 }, - {571,570,474 ,1885,1823,1822 }, {475,571,474 ,1884,1885,1822 }, - {667,666,570 ,1886,1824,1823 }, {571,667,570 ,1885,1886,1823 }, - {763,762,666 ,1887,1825,1824 }, {667,763,666 ,1886,1887,1824 }, - {847,846,762 ,1888,1827,1825 }, {763,847,762 ,1887,1888,1825 }, - {945,944,846 ,1889,1829,1827 }, {847,945,846 ,1888,1889,1827 }, - {1037,1036,944 ,1890,1830,1829 }, {945,1037,944 ,1889,1890,1829 }, - {1129,1128,1036 ,1891,1831,1830 }, {1037,1129,1036 ,1890,1891,1830 }, - {1129,1278,1277 ,1891,1892,1832 }, {1128,1129,1277 ,1831,1891,1832 }, - {1361,1360,1277 ,1893,1833,1832 }, {1278,1361,1277 ,1892,1893,1832 }, - {1437,1436,1360 ,1894,1834,1833 }, {1361,1437,1360 ,1893,1894,1833 }, - {1506,1505,1436 ,1895,1835,1834 }, {1437,1506,1436 ,1894,1895,1834 }, - {1506,1579,1578 ,1895,1896,1836 }, {1505,1506,1578 ,1835,1895,1836 }, - {1579,1647,1646 ,1896,1897,1837 }, {1578,1579,1646 ,1836,1896,1837 }, - {1647,1714,1713 ,1897,1898,1838 }, {1646,1647,1713 ,1837,1897,1838 }, - {1714,1763,1762 ,1898,1899,1839 }, {1713,1714,1762 ,1838,1898,1839 }, - {1826,1825,1762 ,1900,1840,1839 }, {1763,1826,1762 ,1899,1900,1839 }, - {1896,1895,1825 ,1901,1841,1840 }, {1826,1896,1825 ,1900,1901,1840 }, - {1961,1960,1895 ,1902,1842,1841 }, {1896,1961,1895 ,1901,1902,1841 }, - {1961,1997,1960 ,1902,1843,1842 }, {1961,2018,1997 ,1902,1903,1843 }, - {2018,2064,2063 ,1903,1904,1844 }, {1997,2018,2063 ,1843,1903,1844 }, - {2109,2108,2063 ,1905,1846,1844 }, {2064,2109,2063 ,1904,1905,1844 }, - {2109,2177,2176 ,1905,1906,1847 }, {2108,2109,2176 ,1846,1905,1847 }, - {2177,780,2230 ,1906,1907,1848 }, {2176,2177,2230 ,1847,1906,1848 }, - {2230,780,866 ,1848,1907,1849 }, {780,389,550 ,1907,1908,1850 }, - {866,780,550 ,1849,1907,1850 }, {389,1409,1408 ,1908,1909,1872 }, - {550,389,1408 ,1850,1908,1872 }, {1409,1095,1244 ,1909,1910,575 }, - {1408,1409,1244 ,1872,1909,575 }, {1095,2070,1244 ,1910,573,575 }, - {3364,3363,3330 ,1184,1911,1185 }, {5817,5816,5800 ,701,545,702 }, - {3869,3890,3854 ,1912,1913,1512 }, {3975,2812,3684 ,728,1914,1915 }, - {1547,1548,1616 ,860,902,1150 }, {723,722,623 ,1053,1052,1100 }, - {905,992,904 ,1729,958,1730 }, {250,157,94 ,1916,1877,1858 }, - {301,250,94 ,1857,1916,1858 }, {3944,3975,3684 ,729,728,1915 }, - {1455,297,140 ,1917,1918,1919 }, {1192,398,117 ,8,15,9 }, {946,945,847 ,1920,1889,1888 }, - {1764,1826,1763 ,1921,1900,1899 }, {1065,921,1340 ,821,1922,822 }, - {2192,170,392 ,1175,794,857 }, {991,990,903 ,1335,1435,1734 }, - {2149,2130,2197 ,1470,1505,1507 }, {2746,3263,8139 ,1517,252,1923 }, - {173,398,1192 ,12,15,8 }, {772,809,891 ,18,20,189 }, {4514,4483,4465 ,259,549,257 }, - {1038,1037,945 ,1924,1890,1889 }, {946,1038,945 ,1920,1924,1889 }, - {1038,1129,1037 ,1924,1891,1890 }, {1714,1764,1763 ,1898,1921,1899 }, - {1827,1826,1764 ,1925,1900,1921 }, {2110,2109,2064 ,1926,1905,1904 }, - {3997,4039,4038 ,380,382,1927 }, {3996,3997,4038 ,1928,380,1927 }, - {3999,4041,4040 ,1023,1063,460 }, {1106,1204,1203 ,1664,338,337 }, - {906,1046,1045 ,1019,1929,956 }, {909,910,1189 ,1109,1930,1495 }, - {3572,3590,3589 ,1931,1867,1932 }, {1302,1303,13 ,909,1933,910 }, - {1844,1873,1912 ,911,913,964 }, {340,4223,593 ,1107,1934,1108 }, - {1850,251,1517 ,601,603,600 }, {1334,3871,138 ,1935,397,434 }, - {4202,562,700 ,260,1936,255 }, {4682,1376,3899 ,1191,1937,1938 }, - {106,5139,5138 ,1073,1939,1940 }, {1245,57,735 ,36,222,224 }, - {463,467,360 ,1941,346,86 }, {3749,3662,4011 ,652,1942,395 }, - {1135,1285,5067 ,1943,1944,1945 }, {6898,6563,8063 ,1946,464,465 }, - {572,611,571 ,1947,1948,1885 }, {668,667,571 ,1949,1886,1885 }, - {611,668,571 ,1948,1949,1885 }, {764,763,667 ,1950,1887,1886 }, - {668,764,667 ,1949,1950,1886 }, {848,847,763 ,1951,1888,1887 }, - {764,848,763 ,1950,1951,1887 }, {947,946,847 ,1952,1920,1888 }, - {848,947,847 ,1951,1952,1888 }, {1039,1038,946 ,1953,1924,1920 }, - {947,1039,946 ,1952,1953,1920 }, {1039,1129,1038 ,1953,1891,1924 }, - {1039,1173,1129 ,1953,1954,1891 }, {1279,1278,1129 ,1955,1892,1891 }, - {1173,1279,1129 ,1954,1955,1891 }, {1362,1361,1278 ,1956,1893,1892 }, - {1279,1362,1278 ,1955,1956,1892 }, {1438,1437,1361 ,1957,1894,1893 }, - {1362,1438,1361 ,1956,1957,1893 }, {1507,1506,1437 ,1958,1895,1894 }, - {1438,1507,1437 ,1957,1958,1894 }, {1580,1579,1506 ,1959,1896,1895 }, - {1507,1580,1506 ,1958,1959,1895 }, {1580,1648,1647 ,1959,1960,1897 }, - {1579,1580,1647 ,1896,1959,1897 }, {1648,1715,1714 ,1960,1961,1898 }, - {1647,1648,1714 ,1897,1960,1898 }, {1715,1735,1714 ,1961,1962,1898 }, - {1735,1765,1764 ,1962,1963,1921 }, {1714,1735,1764 ,1898,1962,1921 }, - {1828,1827,1764 ,1964,1925,1921 }, {1765,1828,1764 ,1963,1964,1921 }, - {1864,1826,1827 ,1965,1900,1925 }, {1828,1864,1827 ,1964,1965,1925 }, - {1897,1896,1826 ,1966,1901,1900 }, {1864,1897,1826 ,1965,1966,1900 }, - {1962,1961,1896 ,1967,1902,1901 }, {1897,1962,1896 ,1966,1967,1901 }, - {2019,2018,1961 ,1968,1903,1902 }, {1962,2019,1961 ,1967,1968,1902 }, - {2065,2064,2018 ,1969,1904,1903 }, {2019,2065,2018 ,1968,1969,1903 }, - {2111,2110,2064 ,1970,1926,1904 }, {2065,2111,2064 ,1969,1970,1904 }, - {2144,2109,2110 ,1971,1905,1926 }, {2111,2144,2110 ,1970,1971,1926 }, - {2178,2177,2109 ,1972,1906,1905 }, {2144,2178,2109 ,1971,1972,1905 }, - {2178,734,780 ,1972,1973,1907 }, {2177,2178,780 ,1906,1972,1907 }, - {390,389,780 ,1974,1908,1907 }, {734,390,780 ,1973,1974,1907 }, - {1372,1409,389 ,1975,1909,1908 }, {390,1372,389 ,1974,1975,1908 }, - {615,1095,1409 ,766,1910,1909 }, {1372,615,1409 ,1975,766,1909 }, - {3430,3489,3429 ,1976,1977,1978 }, {3380,3430,3379 ,1514,1976,1979 }, - {4038,4039,2662 ,1927,382,805 }, {3489,3488,3429 ,1977,1980,1978 }, - {580,581,623 ,1280,1274,1100 }, {293,1181,1182 ,1430,1981,1235 }, - {1616,1683,1682 ,1150,1151,1010 }, {2037,2082,2036 ,1458,1520,887 }, - {239,2290,2292 ,1103,1982,1983 }, {906,992,905 ,1019,958,1729 }, - {157,91,223 ,1877,1984,1985 }, {681,302,14 ,620,622,1986 }, {694,4165,2477 ,1987,640,642 }, - {562,708,700 ,1936,401,255 }, {467,4110,503 ,346,372,374 }, {3293,7133,7110 ,1988,1989,1990 }, - {765,764,668 ,1991,1950,1949 }, {1174,1173,1039 ,1992,1954,1953 }, - {1174,1279,1173 ,1992,1955,1954 }, {1828,1897,1864 ,1964,1966,1965 }, - {1962,2065,2019 ,1967,1969,1968 }, {2111,2178,2144 ,1970,1972,1971 }, - {2243,2325,2324 ,1993,1994,1995 }, {3323,3355,3322 ,1051,1292,1182 }, - {1142,1233,1141 ,1334,1294,1498 }, {830,928,927 ,1996,1997,1441 }, - {829,830,927 ,1368,1996,1441 }, {526,582,525 ,718,719,1279 }, - {6577,8088,8094 ,97,1998,98 }, {2127,2126,2081 ,1553,890,888 }, - {1980,2036,2007 ,828,887,829 }, {1876,460,822 ,1386,1999,2000 }, - {4244,1599,4223 ,753,391,1934 }, {14,38,681 ,1986,742,620 }, - {3662,3749,5304 ,1942,652,654 }, {4631,5180,5136 ,1816,1878,1817 }, - {959,535,6686 ,1358,2001,750 }, {700,708,757 ,255,401,239 }, - {4187,3,1023 ,502,329,308 }, {765,848,764 ,1991,1951,1950 }, - {1280,1279,1174 ,2002,1955,1992 }, {1363,1362,1279 ,2003,1956,1955 }, - {1280,1363,1279 ,2002,2003,1955 }, {1898,1897,1828 ,2004,1966,1964 }, - {2231,734,2178 ,2005,1973,1972 }, {2697,3327,1211 ,2006,439,2007 }, - {254,2662,2472 ,2008,805,2009 }, {1013,1012,927 ,2010,1471,1441 }, - {928,1013,927 ,1997,2010,1441 }, {1686,1687,1780 ,1004,1104,900 }, - {1089,2893,4393 ,103,1679,463 }, {1260,1857,1311 ,11,13,213 }, - {1319,1521,729 ,322,2011,1548 }, {139,777,963 ,145,421,1341 }, - {2785,3878,3419 ,1246,1248,2012 }, {7515,7573,7538 ,1079,2013,2014 }, - {2141,8085,973 ,2015,2016,2017 }, {3534,8138,8081 ,942,2018,1445 }, - {766,765,668 ,2019,1991,1949 }, {669,766,668 ,2020,2019,1949 }, - {849,848,765 ,2021,1951,1991 }, {766,849,765 ,2019,2021,1991 }, - {948,947,848 ,2022,1952,1951 }, {849,948,848 ,2021,2022,1951 }, - {948,983,947 ,2022,2023,1952 }, {1082,1039,947 ,2024,1953,1952 }, - {983,1082,947 ,2023,2024,1952 }, {1175,1174,1039 ,2025,1992,1953 }, - {1082,1175,1039 ,2024,2025,1953 }, {1281,1280,1174 ,2026,2002,1992 }, - {1175,1281,1174 ,2025,2026,1992 }, {1364,1363,1280 ,2027,2003,2002 }, - {1281,1364,1280 ,2026,2027,2002 }, {1364,1362,1363 ,2027,1956,2003 }, - {1439,1438,1362 ,2028,1957,1956 }, {1364,1439,1362 ,2027,2028,1956 }, - {1508,1507,1438 ,2029,1958,1957 }, {1439,1508,1438 ,2028,2029,1957 }, - {1581,1580,1507 ,2030,1959,1958 }, {1508,1581,1507 ,2029,2030,1958 }, - {1581,1649,1648 ,2030,2031,1960 }, {1580,1581,1648 ,1959,2030,1960 }, - {1649,1716,1715 ,2031,2032,1961 }, {1648,1649,1715 ,1960,2031,1961 }, - {1717,1735,1715 ,2033,1962,1961 }, {1716,1717,1715 ,2032,2033,1961 }, - {1766,1765,1735 ,2034,1963,1962 }, {1717,1766,1735 ,2033,2034,1962 }, - {1766,1829,1828 ,2034,2035,1964 }, {1765,1766,1828 ,1963,2034,1964 }, - {1899,1898,1828 ,2036,2004,1964 }, {1829,1899,1828 ,2035,2036,1964 }, - {1930,1897,1898 ,2037,1966,2004 }, {1899,1930,1898 ,2036,2037,2004 }, - {1963,1962,1897 ,2038,1967,1966 }, {1930,1963,1897 ,2037,2038,1966 }, - {2066,2065,1962 ,2039,1969,1967 }, {1963,2066,1962 ,2038,2039,1967 }, - {2066,2078,2065 ,2039,2040,1969 }, {2112,2111,2065 ,2041,1970,1969 }, - {2078,2112,2065 ,2040,2041,1969 }, {2179,2178,2111 ,2042,1972,1970 }, - {2112,2179,2111 ,2041,2042,1970 }, {2232,2231,2178 ,2043,2005,1972 }, - {2179,2232,2178 ,2042,2043,1972 }, {1448,734,2231 ,2044,1973,2005 }, - {2232,1448,2231 ,2043,2044,2005 }, {775,390,734 ,94,1974,1973 }, - {1448,775,734 ,2044,94,1973 }, {654,1372,390 ,93,1975,1974 }, - {775,654,390 ,94,93,1974 }, {1372,654,615 ,1975,93,766 }, {3080,13,2472 ,908,910,2009 }, - {2922,2923,2972 ,2045,2046,2047 }, {1061,1060,1012 ,2048,1472,1471 }, - {1013,1061,1012 ,2010,2048,1471 }, {1105,1104,1060 ,2049,1473,1472 }, - {1061,1105,1060 ,2048,2049,1472 }, {1521,730,729 ,2011,1549,1548 }, - {5180,5199,5179 ,1878,422,1879 }, {1683,1776,1775 ,1151,1431,1432 }, - {6647,5471,6611 ,2050,2051,1391 }, {1776,1777,1802 ,1431,856,2052 }, - {14,74,38 ,1986,2053,742 }, {3349,3422,3684 ,2054,2055,1915 }, - {3289,3307,3288 ,2056,2057,2058 }, {6139,4240,3797 ,2059,2060,2061 }, - {2045,30,211 ,417,307,415 }, {18,826,738 ,896,507,1674 }, {984,983,948 ,2062,2023,2022 }, - {849,984,948 ,2021,2062,2022 }, {1439,1440,1508 ,2028,2063,2029 }, - {1767,1766,1717 ,2064,2034,2033 }, {1716,1767,1717 ,2032,2064,2033 }, - {1330,654,1481 ,53,93,194 }, {2833,3054,3076 ,2065,916,2066 }, - {593,539,910 ,1108,2067,1930 }, {172,1743,3122 ,667,763,2068 }, - {394,440,1449 ,1600,1464,2069 }, {1615,1682,1614 ,1359,1010,2070 }, - {1239,1190,1449 ,1596,1112,2069 }, {1778,1840,1839 ,855,198,1138 }, - {786,963,777 ,2071,1341,421 }, {2588,74,14 ,2072,2053,1986 }, - {5453,5923,6118 ,937,2073,2074 }, {708,365,757 ,401,402,239 }, - {120,364,4362 ,240,1340,342 }, {365,364,120 ,402,1340,240 }, - {4362,139,962 ,342,145,233 }, {757,365,120 ,239,402,240 }, {5569,3121,5570 ,2075,2076,508 }, - {478,277,613 ,2077,85,298 }, {984,1082,983 ,2062,2024,2023 }, - {8079,8077,3421 ,55,2078,1156 }, {2093,4921,2257 ,553,2079,554 }, - {1536,1582,1581 ,2080,2081,2030 }, {1582,1650,1649 ,2081,2082,2031 }, - {1717,1716,1649 ,2033,2032,2031 }, {1672,1717,1649 ,2083,2033,2031 }, - {1327,1326,1232 ,996,1330,1295 }, {3592,3645,3591 ,2084,1650,1652 }, - {908,862,340 ,1111,1225,1107 }, {2029,2121,2028 ,1748,1598,1811 }, - {2127,2148,2126 ,1553,1561,890 }, {1615,1616,1682 ,1359,1150,1010 }, - {2196,2195,2147 ,1550,1851,1562 }, {1365,1440,1439 ,2085,2063,2028 }, - {5199,5219,5198 ,422,1860,423 }, {423,383,325 ,2086,2087,2088 }, - {4691,324,383 ,2089,2090,2087 }, {669,707,766 ,2020,2091,2019 }, - {850,849,766 ,2092,2021,2019 }, {767,850,766 ,2093,2092,2019 }, - {985,984,849 ,2094,2062,2021 }, {850,985,849 ,2092,2094,2021 }, - {985,1083,1082 ,2094,2095,2024 }, {984,985,1082 ,2062,2094,2024 }, - {1083,1176,1175 ,2095,2096,2025 }, {1082,1083,1175 ,2024,2095,2025 }, - {1176,1282,1281 ,2096,2097,2026 }, {1175,1176,1281 ,2025,2096,2026 }, - {1282,1365,1364 ,2097,2085,2027 }, {1281,1282,1364 ,2026,2097,2027 }, - {3347,4060,3317 ,2098,2099,1882 }, {1521,4244,730 ,2011,753,1549 }, - {4481,4480,4421 ,2100,2101,229 }, {2451,549,450 ,2102,1129,1653 }, - {3948,4326,3670 ,280,219,281 }, {3861,8097,8083 ,1147,1710,2103 }, - {732,283,871 ,2104,2105,2106 }, {4481,1674,4462 ,2100,2107,1076 }, - {1718,1716,1717 ,2108,2032,2033 }, {1768,1767,1716 ,2109,2064,2032 }, - {1718,1768,1716 ,2108,2109,2032 }, {1767,1768,1766 ,2064,2109,2034 }, - {1768,1830,1829 ,2109,2110,2035 }, {1766,1768,1829 ,2034,2109,2035 }, - {1830,1900,1899 ,2110,2111,2036 }, {1829,1830,1899 ,2035,2110,2036 }, - {1899,1900,1930 ,2036,2111,2037 }, {1900,1964,1963 ,2111,2112,2038 }, - {1930,1900,1963 ,2037,2111,2038 }, {2020,2066,1963 ,2113,2039,2038 }, - {1964,2020,1963 ,2112,2113,2038 }, {2079,2078,2066 ,2114,2040,2039 }, - {2020,2079,2066 ,2113,2114,2039 }, {2113,2112,2078 ,2115,2041,2040 }, - {2079,2113,2078 ,2114,2115,2040 }, {2180,2179,2112 ,2116,2042,2041 }, - {2113,2180,2112 ,2115,2116,2041 }, {2233,2232,2179 ,2117,2043,2042 }, - {2180,2233,2179 ,2116,2117,2042 }, {1371,1448,2232 ,2118,2044,2043 }, - {2233,1371,2232 ,2117,2118,2043 }, {231,775,1448 ,92,94,2044 }, - {1371,231,1448 ,2118,92,2044 }, {5180,5220,5199 ,1878,2119,422 }, - {5241,5240,5199 ,2120,481,422 }, {3699,3698,3645 ,2121,2122,1650 }, - {3646,3699,3645 ,2123,2121,1650 }, {7232,3288,7233 ,2124,2058,2125 }, - {26,70,460 ,2126,323,1999 }, {290,335,334 ,1042,1014,1267 }, - {2190,305,2189 ,1574,1463,1599 }, {1914,1943,1983 ,1000,2127,1001 }, - {2198,293,692 ,1506,1430,1429 }, {2197,2198,692 ,1507,1506,1429 }, - {1132,981,1591 ,61,2128,1072 }, {8118,8126,4835 ,2129,812,2130 }, - {8133,8107,2301 ,2131,658,2132 }, {965,799,1419 ,366,392,367 }, - {4131,2026,4251 ,1656,130,132 }, {767,766,707 ,2093,2019,2091 }, - {985,949,1083 ,2094,2133,2095 }, {5030,5061,1130 ,2134,2135,2136 }, - {324,4691,325 ,2090,2089,2088 }, {810,732,716 ,2137,2104,2138 }, - {4441,4481,4421 ,2139,2100,229 }, {412,736,1080 ,358,843,2140 }, - {770,1932,2352 ,275,2141,390 }, {2181,2180,2113 ,2142,2116,2115 }, - {5220,5241,5199 ,2119,2120,422 }, {2582,2585,3372 ,873,875,2143 }, - {3355,3381,3354 ,1292,871,1293 }, {3572,3573,3590 ,1931,980,1867 }, - {6230,3436,5876 ,2144,2145,2146 }, {815,857,903 ,999,2147,1734 }, - {1402,1403,1474 ,1780,974,1649 }, {1682,1681,1614 ,1010,1009,2070 }, - {1337,1845,1844 ,2148,912,911 }, {817,816,724 ,1020,998,722 }, - {1182,1181,1049 ,1235,1981,1362 }, {3316,1295,2810 ,2149,618,1728 }, - {440,1239,1449 ,1464,1596,2069 }, {741,2730,740 ,1221,2150,1595 }, - {2029,2080,2121 ,1748,2151,1598 }, {4202,885,562 ,260,216,1936 }, - {326,423,325 ,2152,2086,2088 }, {479,514,423 ,2153,2154,2086 }, - {4378,4430,4354 ,375,341,376 }, {4214,4215,4254 ,226,370,416 }, - {892,1597,810 ,506,505,2137 }, {767,851,850 ,2093,2155,2092 }, - {58,8128,227 ,2156,959,2157 }, {4972,949,851 ,2158,2133,2155 }, - {850,949,985 ,2092,2133,2094 }, {5029,1130,1083 ,2159,2136,2095 }, - {1083,1130,1176 ,2095,2136,2096 }, {5029,5030,1130 ,2159,2134,2136 }, - {884,614,4095 ,2160,1397,349 }, {1080,459,980 ,2140,696,2161 }, - {732,738,283 ,2104,1674,2105 }, {4180,1258,412 ,2162,2163,358 }, - {1319,980,1521 ,322,2161,2011 }, {732,871,716 ,2104,2106,2138 }, - {1224,1904,3048 ,1508,2164,2165 }, {4426,8100,6837 ,1056,1713,1057 }, - {283,1057,871 ,2105,354,2106 }, {1830,1865,1900 ,2110,2166,2111 }, - {8085,8091,8067 ,2016,2167,2168 }, {5285,5284,5240 ,2169,571,481 }, - {3404,2582,3372 ,2170,873,2143 }, {862,999,960 ,1225,798,1326 }, - {2193,506,170 ,1007,1381,794 }, {989,990,1140 ,1813,1435,1499 }, - {1912,1913,1980 ,964,963,828 }, {1722,1686,1779 ,1668,1004,1275 }, - {1404,1476,1475 ,975,859,858 }, {1290,2009,1918 ,795,1224,1110 }, - {5370,5328,6091 ,2171,2172,2173 }, {8107,8137,3006 ,658,2174,414 }, - {280,279,4692 ,2175,2176,2177 }, {5241,5285,5240 ,2120,2169,481 }, - {424,423,326 ,2178,2086,2152 }, {424,479,423 ,2178,2153,2086 }, - {515,514,479 ,2179,2154,2153 }, {575,574,514 ,2180,2181,2154 }, - {515,575,514 ,2179,2180,2154 }, {1223,4160,57 ,2182,223,222 }, - {635,1854,2657 ,2183,2184,2185 }, {1258,2014,736 ,2163,842,843 }, - {1080,980,25 ,2140,2161,2186 }, {1258,736,412 ,2163,843,358 }, - {4471,5295,4472 ,2187,2188,2189 }, {4354,4430,4402 ,376,341,2190 }, - {1866,1865,1830 ,2191,2166,2110 }, {1866,1901,1900 ,2191,2192,2111 }, - {1865,1866,1900 ,2166,2191,2111 }, {1901,1902,1900 ,2192,2193,2111 }, - {1965,1964,1900 ,2194,2112,2111 }, {1902,1965,1900 ,2193,2194,2111 }, - {1965,1998,1964 ,2194,2195,2112 }, {2021,2020,1964 ,2196,2113,2112 }, - {1998,2021,1964 ,2195,2196,2112 }, {2021,2079,2020 ,2196,2114,2113 }, - {2114,2113,2079 ,2197,2115,2114 }, {2021,2114,2079 ,2196,2197,2114 }, - {2182,2181,2113 ,2198,2142,2115 }, {2114,2182,2113 ,2197,2198,2115 }, - {2215,2180,2181 ,2199,2116,2142 }, {2182,2215,2181 ,2198,2199,2142 }, - {9,2233,2180 ,2200,2117,2116 }, {2215,9,2180 ,2199,2200,2116 }, - {9,1371,2233 ,2200,2118,2117 }, {232,231,1371 ,2201,92,2118 }, - {9,232,1371 ,2200,2201,2118 }, {1094,1481,231 ,1398,194,92 }, - {232,1094,231 ,2201,1398,92 }, {2147,2195,2194 ,1562,1851,1853 }, - {2198,2216,293 ,1506,1555,1430 }, {1744,1059,3932 ,1666,678,1096 }, - {2009,862,908 ,1224,1225,1111 }, {388,437,339 ,2202,1149,1143 }, - {388,339,239 ,2202,1143,1103 }, {1144,1143,991 ,957,1333,1335 }, - {1329,1406,1405 ,1134,1135,995 }, {259,410,455 ,2203,2204,2205 }, - {817,859,816 ,1020,1733,998 }, {1345,1093,840 ,203,66,68 }, {196,124,607 ,152,2206,2207 }, - {576,4907,673 ,2208,2209,2210 }, {4525,892,1673 ,623,506,1686 }, - {4160,1163,2012 ,223,2211,2212 }, {810,1597,18 ,2137,505,896 }, - {5599,5640,5639 ,2213,2214,2215 }, {736,459,1080 ,843,696,2140 }, - {5598,5599,5639 ,2216,2213,2215 }, {5237,5573,5307 ,1800,2217,461 }, - {1206,138,1734 ,2218,434,2219 }, {4520,136,4496 ,1673,1685,2220 }, - {4473,4520,4496 ,270,1673,2220 }, {1831,1866,1830 ,2221,2191,2110 }, - {2182,9,2215 ,2198,2200,2199 }, {1052,1094,232 ,2222,1398,2201 }, - {5339,5338,5284 ,2223,63,571 }, {5285,5339,5284 ,2169,2223,571 }, - {2033,2068,2124 ,1288,891,1006 }, {2068,2125,2124 ,891,892,1006 }, - {1144,1142,1143 ,957,1334,1333 }, {336,385,432 ,1013,864,1222 }, - {1722,1779,1778 ,1668,1275,855 }, {2080,2067,2121 ,2151,1592,1598 }, - {2029,2067,2080 ,1748,1592,2151 }, {239,339,338 ,1103,1143,867 }, - {628,627,528 ,1177,670,1055 }, {3697,3698,3743 ,2224,2122,2225 }, - {582,581,525 ,719,1274,1279 }, {327,326,281 ,2226,2152,2227 }, - {327,384,326 ,2226,2228,2152 }, {425,424,326 ,2229,2178,2152 }, - {384,425,326 ,2228,2229,2152 }, {480,479,424 ,2230,2153,2178 }, - {425,480,424 ,2229,2230,2178 }, {516,515,479 ,2231,2179,2153 }, - {480,516,479 ,2230,2231,2153 }, {576,575,515 ,2208,2180,2179 }, - {516,576,515 ,2231,2208,2179 }, {575,576,673 ,2180,2208,2210 }, - {5817,5834,5816 ,701,543,545 }, {892,810,1673 ,506,2137,1686 }, - {4442,4441,4391 ,991,2139,992 }, {283,738,358 ,2105,1674,897 }, - {459,4412,980 ,696,744,2161 }, {46,739,5 ,2232,2233,350 }, {883,146,76 ,119,504,689 }, - {1057,283,188 ,354,2105,2234 }, {1903,1966,1965 ,406,405,2194 }, - {1902,1903,1965 ,2193,406,2194 }, {1999,1998,1965 ,2235,2195,2194 }, - {1966,1999,1965 ,405,2235,2194 }, {2022,2021,1998 ,2236,2196,2195 }, - {1999,2022,1998 ,2235,2236,2195 }, {2115,2114,2021 ,2237,2197,2196 }, - {2022,2115,2021 ,2236,2237,2196 }, {2115,2145,2114 ,2237,2238,2197 }, - {2183,2182,2114 ,2239,2198,2197 }, {2145,2183,2114 ,2238,2239,2197 }, - {2183,9,2182 ,2239,2200,2198 }, {1513,232,9 ,2240,2201,2200 }, - {1632,76,129 ,2241,689,730 }, {2013,58,877 ,2242,2156,2243 }, - {728,819,726 ,1266,2244,669 }, {1153,6797,8123 ,2245,2246,2247 }, - {1182,1049,1048 ,1235,1362,1236 }, {335,336,431 ,1014,1013,1268 }, - {901,903,989 ,1428,1734,1813 }, {5339,4916,4915 ,2223,2248,64 }, - {1868,3760,1783 ,2249,2250,2251 }, {3784,4884,4239 ,2252,2253,2254 }, - {5338,5339,4915 ,63,2223,64 }, {4513,1673,4481 ,624,1686,2100 }, - {1295,3316,3348 ,618,2149,2255 }, {4244,4223,340 ,753,1934,1107 }, - {730,4244,340 ,1549,753,1107 }, {1114,1115,2047 ,2256,2257,2258 }, - {4480,4462,4421 ,2101,1076,229 }, {798,5103,5378 ,78,77,359 }, - {40,9,2183 ,2259,2200,2239 }, {40,163,9 ,2259,2260,2200 }, {233,1513,9 ,2261,2240,2200 }, - {163,233,9 ,2260,2261,2200 }, {1514,232,1513 ,2262,2201,2240 }, - {233,1514,1513 ,2261,2262,2240 }, {1055,1052,232 ,2263,2222,2201 }, - {1514,1055,232 ,2262,2263,2201 }, {1054,1094,1052 ,2264,1398,2222 }, - {1055,1054,1052 ,2263,2264,2222 }, {1054,865,1094 ,2264,1119,1398 }, - {2893,3946,5307 ,1679,1230,461 }, {227,1482,58 ,2157,2265,2156 }, - {1238,860,1000 ,1569,1385,1384 }, {305,1239,440 ,1463,1596,1464 }, - {1657,1656,1552 ,1781,1166,2266 }, {814,815,902 ,1054,999,1427 }, - {8092,8120,8130 ,2267,906,905 }, {2131,2200,2199 ,656,1570,1554 }, - {817,905,859 ,1020,1729,1733 }, {2032,2033,2123 ,1127,1288,1008 }, - {3946,4227,5307 ,1230,1564,461 }, {249,1664,268 ,644,2268,645 }, - {1234,1328,1327 ,1178,994,996 }, {864,865,867 ,840,1119,1121 }, - {8140,8063,8102 ,2269,465,253 }, {577,576,516 ,2270,2208,2231 }, - {576,577,674 ,2208,2270,2271 }, {358,4184,2014 ,897,898,842 }, - {6553,6295,6646 ,780,1290,1133 }, {1441,23,4181 ,1485,2272,2273 }, - {1927,870,710 ,1196,2274,33 }, {810,716,1674 ,2137,2138,2107 }, - {593,1599,539 ,1108,391,2067 }, {4152,4180,4448 ,2275,2162,2276 }, - {358,4180,4152 ,897,2162,2275 }, {188,358,4152 ,2234,897,2275 }, - {4480,4481,4462 ,2101,2100,1076 }, {941,119,1049 ,2277,2278,1362 }, - {188,4152,4138 ,2234,2275,278 }, {4152,188,4138 ,2275,2234,278 }, - {1674,716,4512 ,2107,2138,2279 }, {6509,6531,8092 ,1452,2280,2267 }, - {4060,4064,3317 ,2099,1880,1882 }, {1999,1966,2022 ,2235,405,2236 }, - {233,1055,1514 ,2261,2263,2262 }, {1055,865,1054 ,2263,1119,2264 }, - {2137,867,776 ,147,1121,1120 }, {3758,3757,3711 ,1789,2281,1790 }, - {3194,3016,3189 ,1065,1028,1027 }, {442,441,357 ,673,609,672 }, - {1910,1911,1867 ,1223,1101,1504 }, {1049,1181,1050 ,1362,1981,2282 }, - {1802,1777,1838 ,2052,856,425 }, {336,432,431 ,1013,1222,1268 }, - {5305,5572,3147 ,2283,2284,2285 }, {904,991,903 ,1730,1335,1734 }, - {1406,1407,1447 ,1135,1269,1136 }, {8114,8108,6797 ,2286,2287,2246 }, - {437,436,339 ,1149,1142,1143 }, {1190,1189,1449 ,1112,1495,2069 }, - {8081,8093,8072 ,1445,2288,511 }, {940,1910,1867 ,197,1223,1504 }, - {328,426,425 ,2289,2290,2229 }, {384,328,425 ,2228,2289,2229 }, - {426,481,480 ,2290,2291,2230 }, {425,426,480 ,2229,2290,2230 }, - {481,517,516 ,2291,2292,2231 }, {480,481,516 ,2230,2291,2231 }, - {578,577,516 ,2293,2270,2231 }, {517,578,516 ,2292,2293,2231 }, - {675,676,677 ,2294,2295,2296 }, {577,578,675 ,2270,2293,2294 }, - {738,189,358 ,1674,503,897 }, {661,1441,4181 ,2297,1485,2273 }, - {4513,4481,4463 ,624,2100,993 }, {5,739,413 ,350,2233,351 }, - {980,4244,1521 ,2161,753,2011 }, {1424,379,608 ,2298,2299,2300 }, - {416,604,502 ,556,486,2301 }, {25,980,1319 ,2186,2161,322 }, - {4448,4180,412 ,2276,2162,358 }, {138,878,1734 ,434,398,2219 }, - {5558,3121,5569 ,2302,2076,2075 }, {4430,4473,4402 ,341,270,2190 }, - {879,3397,3370 ,1155,136,2303 }, {4152,4448,4138 ,2275,2276,278 }, - {4108,4148,4124 ,2304,2305,2306 }, {8131,8075,8107 ,2307,2308,658 }, - {1967,1968,1966 ,404,2309,405 }, {1968,2023,2022 ,2309,2310,2236 }, - {1966,1968,2022 ,405,2309,2236 }, {2116,2115,2022 ,2311,2237,2236 }, - {2023,2116,2022 ,2310,2311,2236 }, {2116,2145,2115 ,2311,2238,2237 }, - {2184,2183,2145 ,2312,2239,2238 }, {2116,2184,2145 ,2311,2312,2238 }, - {83,40,2183 ,2313,2259,2239 }, {2184,83,2183 ,2312,2313,2239 }, - {1001,163,40 ,2314,2260,2259 }, {83,1001,40 ,2313,2314,2259 }, - {234,233,163 ,2315,2261,2260 }, {1001,234,163 ,2314,2315,2260 }, - {1053,1055,233 ,2316,2263,2261 }, {234,1053,233 ,2315,2316,2261 }, - {776,865,1055 ,1120,1119,2263 }, {1053,776,1055 ,2316,1120,2263 }, - {5194,1102,1454 ,727,2317,2318 }, {1744,3932,2088 ,1666,1096,1667 }, - {1721,1722,1778 ,854,1668,855 }, {2197,692,933 ,1507,1429,1496 }, - {1775,1776,1802 ,1432,1431,2052 }, {1240,1918,1190 ,796,1110,1112 }, - {1446,1447,1478 ,1137,1136,2319 }, {3880,3868,3831 ,2320,970,2321 }, - {3832,3880,3831 ,2322,2320,2321 }, {2191,392,2190 ,1174,857,1574 }, - {4587,2320,8020 ,774,1406,1405 }, {1843,1911,1910 ,901,1101,1223 }, - {5013,2256,6484 ,684,2323,685 }, {1867,2390,940 ,1504,1594,197 }, - {578,579,676 ,2293,2324,2295 }, {1338,1257,4214 ,1077,208,226 }, - {4215,4255,4254 ,370,165,416 }, {4412,314,980 ,744,578,2161 }, - {810,18,732 ,2137,896,2104 }, {980,314,4244 ,2161,578,753 }, - {4464,4513,4463 ,584,624,993 }, {1673,810,1674 ,1686,2137,2107 }, - {4463,4481,4441 ,993,2100,2139 }, {8066,8092,8098 ,960,2267,907 }, - {1225,490,1223 ,327,469,2182 }, {412,1080,69 ,358,2140,321 }, - {4150,412,69 ,320,358,321 }, {880,1441,661 ,1483,1485,2297 }, - {3644,3697,3696 ,1651,2224,2325 }, {188,4152,4138 ,2234,2275,278 }, - {83,234,1001 ,2313,2315,2314 }, {782,776,1053 ,1648,1120,2316 }, - {1691,1670,1733 ,2326,2327,2328 }, {392,1240,305 ,857,796,1463 }, - {2122,2191,2190 ,1128,1174,1574 }, {2067,2122,2121 ,1592,1128,1598 }, - {1479,1477,1478 ,1270,1045,2319 }, {1602,1610,749 ,2329,2330,2331 }, - {1478,1477,1405 ,2319,1045,995 }, {3704,6140,6169 ,2332,2333,2334 }, - {1365,1439,1364 ,2085,2028,2027 }, {3918,3917,3868 ,2335,968,970 }, - {517,579,578 ,2292,2324,2293 }, {2223,6484,2256 ,2336,685,2323 }, - {5834,1942,141 ,543,2337,681 }, {1462,4177,4116 ,2338,2339,2340 }, - {130,89,150 ,2341,1482,1481 }, {136,1628,883 ,1685,118,119 }, - {4441,4421,4367 ,2139,229,228 }, {1677,1676,1610 ,2342,2343,2330 }, - {69,1080,25 ,321,2140,2186 }, {3643,3644,3696 ,2344,1651,2325 }, - {1069,188,4138 ,277,2234,278 }, {1660,1053,234 ,2345,2316,2315 }, - {1378,3148,2743 ,925,814,923 }, {3880,3918,3868 ,2320,2335,970 }, - {1094,865,864 ,1398,1119,840 }, {1802,1838,1837 ,2052,425,997 }, - {1634,1423,396 ,768,151,153 }, {742,741,645 ,972,1221,940 }, - {1915,1916,1943 ,1437,832,2127 }, {1914,1915,1943 ,1000,1437,2127 }, - {1682,1683,1775 ,1010,1151,1432 }, {1446,1478,1405 ,1137,2319,995 }, - {1235,1329,1328 ,1179,1134,994 }, {3961,3960,3917 ,2346,1021,968 }, - {3918,3961,3917 ,2335,2346,968 }, {1145,1235,1234 ,1277,1179,1178 }, - {7151,3290,7175 ,1202,1204,2347 }, {329,427,426 ,2348,2349,2290 }, - {328,329,426 ,2289,2348,2290 }, {426,427,481 ,2290,2349,2291 }, - {427,518,517 ,2349,2350,2292 }, {481,427,517 ,2291,2349,2292 }, - {518,519,579 ,2350,2351,2324 }, {517,518,579 ,2292,2350,2324 }, - {519,617,579 ,2351,2352,2324 }, {579,617,677 ,2324,2352,2296 }, - {2026,870,1927 ,130,2274,1196 }, {732,18,738 ,2104,896,1674 }, - {4442,4463,4441 ,991,993,2139 }, {4068,4067,1253 ,408,185,2353 }, - {358,2014,4180 ,897,842,2162 }, {4391,4441,4367 ,992,2139,228 }, - {1226,1321,1320 ,2354,2355,2356 }, {1321,1398,1397 ,2355,2357,2358 }, - {1320,1321,1397 ,2356,2355,2358 }, {1397,1398,1445 ,2358,2357,2359 }, - {1398,1470,1469 ,2357,2360,2361 }, {1445,1398,1469 ,2359,2357,2361 }, - {1470,1542,1541 ,2360,2362,2363 }, {1469,1470,1541 ,2361,2360,2363 }, - {1611,1610,1541 ,2364,2330,2363 }, {1542,1611,1541 ,2362,2364,2363 }, - {1678,1677,1610 ,2365,2342,2330 }, {1611,1678,1610 ,2364,2365,2330 }, - {1720,1676,1677 ,2366,2343,2342 }, {1678,1720,1677 ,2365,2366,2342 }, - {1720,1771,4817 ,2366,2367,2368 }, {4138,4448,412 ,278,2276,358 }, - {1674,4512,4462 ,2107,2279,1076 }, {6837,8088,6577 ,1057,1998,97 }, - {4036,109,2151 ,1602,1654,1603 }, {1969,2024,2023 ,2369,2370,2310 }, - {1968,1969,2023 ,2309,2369,2310 }, {2024,2117,2116 ,2370,2371,2311 }, - {2023,2024,2116 ,2310,2370,2311 }, {2185,2184,2116 ,2372,2312,2311 }, - {2117,2185,2116 ,2371,2372,2311 }, {152,83,2184 ,2373,2313,2312 }, - {2185,152,2184 ,2372,2373,2312 }, {1288,234,83 ,2374,2315,2313 }, - {152,1288,83 ,2373,2374,2313 }, {861,1660,234 ,2375,2345,2315 }, - {1288,861,234 ,2374,2375,2315 }, {998,1053,1660 ,2376,2316,2345 }, - {861,998,1660 ,2375,2376,2345 }, {786,782,1053 ,2071,1648,2316 }, - {998,786,1053 ,2376,2071,2316 }, {4327,1427,561 ,2377,2378,2379 }, - {169,1880,1296 ,248,250,335 }, {652,169,1296 ,334,248,335 }, - {4000,4042,4041 ,1022,1061,1063 }, {3999,4000,4041 ,1023,1022,1063 }, - {3961,4001,4000 ,2346,2380,1022 }, {2129,2069,2130 ,1469,1711,1505 }, - {1981,2037,2036 ,1456,1458,887 }, {858,904,903 ,1731,1730,1734 }, - {647,742,645 ,973,972,940 }, {1943,1916,1983 ,2127,832,1001 }, - {1916,1984,1983 ,832,831,1001 }, {3372,2585,3453 ,2143,875,917 }, - {1046,1145,1045 ,1929,1277,956 }, {6098,5113,6174 ,1565,141,2381 }, - {518,427,519 ,2350,2349,2351 }, {870,1396,710 ,2274,34,33 }, - {1673,1674,4481 ,1686,2107,2100 }, {716,1675,4512 ,2138,2382,2279 }, - {4138,412,44 ,278,358,357 }, {2474,2072,8096 ,874,1812,1796 }, - {5021,1969,5019 ,2383,2369,2384 }, {5019,1969,1968 ,2384,2369,2309 }, - {2186,2185,2117 ,2385,2372,2371 }, {861,786,998 ,2375,2071,2376 }, - {3712,3711,3653 ,1439,1790,1440 }, {6169,6141,6154 ,2334,2386,2387 }, - {3356,3355,3323 ,872,1292,1051 }, {3960,3961,4000 ,1021,2346,1022 }, - {1801,1802,1837 ,1433,2052,997 }, {725,817,724 ,721,1020,722 }, - {2518,448,546 ,2388,1172,1171 }, {2069,2038,2530 ,1711,1551,2389 }, - {1784,1848,1847 ,1662,1436,1519 }, {647,645,646 ,973,940,954 }, - {723,724,815 ,1053,722,999 }, {815,816,857 ,999,998,2147 }, {3698,3744,3743 ,2122,2390,2225 }, - {237,337,336 ,869,866,1013 }, {519,618,617 ,2351,2391,2352 }, - {855,897,951 ,2392,2393,2394 }, {897,952,951 ,2393,2395,2394 }, - {4180,2014,1258 ,2162,842,2163 }, {952,5034,951 ,2395,2396,2394 }, - {1321,1369,1398 ,2355,2397,2357 }, {1678,1771,1720 ,2365,2367,2366 }, - {1596,188,1069 ,355,2234,277 }, {4536,4452,4820 ,89,2398,2399 }, - {393,4042,4000 ,2400,1061,1022 }, {4001,393,4000 ,2380,2400,1022 }, - {632,341,1184 ,1380,1852,797 }, {341,1373,1184 ,1852,1501,797 }, - {1682,1775,1739 ,1010,1432,1011 }, {1475,1547,1546 ,858,860,1360 }, - {2083,2069,2128 ,1552,1711,1468 }, {2069,2129,2128 ,1711,1469,1468 }, - {7175,3290,7196 ,2347,1204,2401 }, {857,858,903 ,2147,1731,1734 }, - {816,858,857 ,998,1731,2147 }, {1325,1326,1402 ,1331,1330,1780 }, - {1303,1302,687 ,1933,909,1665 }, {330,331,329 ,2402,2403,2348 }, - {331,428,427 ,2403,2404,2349 }, {329,331,427 ,2348,2403,2349 }, - {428,520,519 ,2404,2405,2351 }, {427,428,519 ,2349,2404,2351 }, - {520,619,618 ,2405,2406,2391 }, {519,520,618 ,2351,2405,2391 }, - {718,717,618 ,2407,2408,2391 }, {619,718,618 ,2406,2407,2391 }, - {717,718,855 ,2408,2407,2392 }, {1042,1041,952 ,2409,2410,2395 }, - {898,1042,952 ,2411,2409,2395 }, {1136,1135,1041 ,2412,1943,2410 }, - {1042,1136,1041 ,2409,2412,2410 }, {1227,1226,1135 ,2413,2354,1943 }, - {1136,1227,1135 ,2412,2413,1943 }, {1227,1322,1321 ,2413,2414,2355 }, - {1226,1227,1321 ,2354,2413,2355 }, {1321,1322,1369 ,2355,2414,2397 }, - {1322,1399,1398 ,2414,2415,2357 }, {1369,1322,1398 ,2397,2414,2357 }, - {1399,1471,1470 ,2415,2416,2360 }, {1398,1399,1470 ,2357,2415,2360 }, - {1471,1543,1542 ,2416,2417,2362 }, {1470,1471,1542 ,2360,2416,2362 }, - {1543,1612,1611 ,2417,2418,2364 }, {1542,1543,1611 ,2362,2417,2364 }, - {1679,1678,1611 ,2419,2365,2364 }, {1612,1679,1611 ,2418,2419,2364 }, - {1772,1771,1678 ,2420,2367,2365 }, {1679,1772,1678 ,2419,2420,2365 }, - {1835,1834,1771 ,2421,2422,2367 }, {1772,1835,1771 ,2420,2421,2367 }, - {322,6203,843 ,1250,1249,2423 }, {3380,3379,3353 ,1514,1979,2424 }, - {3354,3380,3353 ,1293,1514,2424 }, {4644,4740,1970 ,2425,2426,2427 }, - {1970,2000,1969 ,2427,2428,2369 }, {1969,2000,2024 ,2369,2428,2370 }, - {8090,8065,8080 ,412,96,1094 }, {2118,2117,2024 ,2429,2371,2370 }, - {2025,2118,2024 ,2430,2429,2370 }, {2187,2186,2117 ,2431,2385,2371 }, - {2118,2187,2117 ,2429,2431,2371 }, {2187,2185,2186 ,2431,2372,2385 }, - {161,152,2185 ,2432,2373,2372 }, {2187,161,2185 ,2431,2432,2372 }, - {1193,1288,152 ,2433,2374,2373 }, {161,1193,152 ,2432,2433,2373 }, - {1193,1287,1288 ,2433,2434,2374 }, {907,861,1288 ,2435,2375,2374 }, - {1287,907,1288 ,2434,2435,2374 }, {629,786,861 ,2436,2071,2375 }, - {907,629,861 ,2435,2436,2375 }, {629,962,786 ,2436,233,2071 }, - {3356,3381,3355 ,872,871,1292 }, {3004,2946,4042 ,2437,1062,1061 }, - {8106,8135,3449 ,2438,2439,2440 }, {529,628,528 ,1148,1177,1055 }, - {1848,1915,1914 ,1436,1437,1000 }, {1847,1848,1914 ,1519,1436,1000 }, - {2476,2310,3844 ,2441,2442,2443 }, {1686,1780,1779 ,1004,900,1275 }, - {236,237,336 ,2444,869,1013 }, {330,2393,286 ,2402,2445,2446 }, - {520,521,619 ,2405,2447,2406 }, {8115,8136,8140 ,2448,2449,2269 }, - {1519,79,2544 ,2450,1510,2451 }, {4021,4835,8106 ,1712,2130,2438 }, - {4446,1335,2607 ,237,236,2452 }, {5833,5834,141 ,544,543,681 }, - {3430,3429,3379 ,1976,1978,1979 }, {488,832,1302 ,533,613,909 }, - {3080,488,1302 ,908,533,909 }, {1057,188,1596 ,354,2234,355 }, - {871,594,893 ,2106,2453,2454 }, {716,871,893 ,2138,2106,2454 }, - {393,3004,4042 ,2400,2437,1061 }, {8139,3263,8089 ,1923,252,254 }, - {1876,822,1985 ,1386,2000,1547 }, {822,729,1985 ,2000,1548,1547 }, - {1685,1686,1722 ,1005,1004,1668 }, {2193,632,506 ,1007,1380,1381 }, - {1684,1721,1683 ,1327,854,1151 }, {2131,2201,2200 ,656,2455,1570 }, - {2193,2194,341 ,1007,1853,1852 }, {1184,1373,999 ,797,1501,798 }, - {1779,1842,1841 ,1275,196,1276 }, {286,331,330 ,2446,2403,2402 }, - {428,521,520 ,2404,2447,2405 }, {916,913,863 ,445,444,284 }, - {283,358,188 ,2105,897,2234 }, {1227,1286,1322 ,2413,2456,2414 }, - {1773,1772,1679 ,2457,2420,2419 }, {1871,1835,1872 ,2458,2421,2459 }, - {8074,8092,8066 ,2460,2267,960 }, {3354,3353,3321 ,1293,2424,2461 }, - {2796,304,3080 ,2462,532,908 }, {1675,716,893 ,2382,2138,2454 }, - {1193,907,1287 ,2433,2435,2434 }, {820,629,907 ,2463,2436,2435 }, - {6102,3831,3868 ,2464,2321,970 }, {3004,2087,2571 ,2437,2465,1064 }, - {1006,1100,799 ,234,2466,392 }, {729,822,1319 ,1548,2000,322 }, - {1146,1145,1046 ,2467,1277,1929 }, {1047,1146,1046 ,1328,2467,1929 }, - {1474,1475,1546 ,1649,858,1360 }, {1373,1000,999 ,1501,1384,798 }, - {3264,8144,2301 ,2468,2469,2132 }, {1000,1876,999 ,1384,1386,798 }, - {287,332,331 ,2470,2471,2403 }, {332,429,428 ,2471,2472,2404 }, - {331,332,428 ,2403,2471,2404 }, {429,522,521 ,2472,2473,2447 }, - {428,429,521 ,2404,2472,2447 }, {522,620,619 ,2473,2474,2406 }, - {521,522,619 ,2447,2473,2406 }, {720,718,719 ,2475,2407,2476 }, - {619,620,718 ,2406,2474,2407 }, {812,899,898 ,559,2477,2411 }, - {811,812,898 ,557,559,2411 }, {1043,898,899 ,2478,2411,2477 }, - {1043,1137,1136 ,2478,2479,2412 }, {1137,1228,1227 ,2479,2480,2413 }, - {1136,1137,1227 ,2412,2479,2413 }, {1228,1229,1286 ,2480,2481,2456 }, - {1227,1228,1286 ,2413,2480,2456 }, {1229,1323,1322 ,2481,2482,2414 }, - {1286,1229,1322 ,2456,2481,2414 }, {1323,1400,1399 ,2482,2483,2415 }, - {1322,1323,1399 ,2414,2482,2415 }, {1400,1472,1471 ,2483,2484,2416 }, - {1399,1400,1471 ,2415,2483,2416 }, {1472,1544,1543 ,2484,2485,2417 }, - {1471,1472,1543 ,2416,2484,2417 }, {1613,1612,1543 ,2486,2418,2417 }, - {1544,1613,1543 ,2485,2486,2417 }, {1613,1680,1679 ,2486,2487,2419 }, - {1612,1613,1679 ,2418,2486,2419 }, {1680,1774,1773 ,2487,2488,2457 }, - {1679,1680,1773 ,2419,2487,2457 }, {1800,1772,1773 ,2489,2420,2457 }, - {1774,1800,1773 ,2488,2489,2457 }, {1836,1835,1772 ,2490,2421,2420 }, - {1800,1836,1772 ,2489,2490,2420 }, {1836,1872,1835 ,2490,2459,2421 }, - {296,630,3475 ,1558,2491,1559 }, {2027,2000,2002 ,2492,2428,2493 }, - {1675,893,4512 ,2382,2454,2279 }, {8069,8100,3482 ,1095,1713,2494 }, - {2119,2118,2025 ,2495,2429,2430 }, {2027,2119,2025 ,2492,2495,2430 }, - {2188,2187,2118 ,2496,2431,2429 }, {2119,2188,2118 ,2495,2496,2429 }, - {179,161,2187 ,2497,2432,2431 }, {2188,179,2187 ,2496,2497,2431 }, - {1374,1193,161 ,2498,2433,2432 }, {179,1374,161 ,2497,2498,2432 }, - {954,907,1193 ,2499,2435,2433 }, {1374,954,1193 ,2498,2499,2433 }, - {914,820,907 ,2500,2463,2435 }, {954,914,907 ,2499,2500,2435 }, - {487,629,820 ,2501,2436,2463 }, {914,487,820 ,2500,2501,2463 }, - {1100,962,629 ,2466,233,2436 }, {487,1100,629 ,2501,2466,2436 }, - {3491,3490,3461 ,955,2502,1152 }, {2946,3004,2571 ,1062,2437,1064 }, - {3931,3684,3664 ,1868,1915,1557 }, {1843,1844,1911 ,901,911,1101 }, - {8068,8071,3264 ,1784,738,2468 }, {2087,132,3016 ,2465,2503,1028 }, - {909,593,910 ,1109,1108,1930 }, {1233,1232,1141 ,1294,1295,1498 }, - {1447,1479,1478 ,1136,1270,2319 }, {1844,1912,1911 ,911,964,1101 }, - {3434,3494,3463 ,737,736,1271 }, {2571,2087,3016 ,1064,2465,1028 }, - {1775,1802,1801 ,1432,2052,1433 }, {1876,1985,999 ,1386,1547,798 }, - {718,720,4954 ,2407,2475,558 }, {4258,4217,85 ,84,83,2504 }, - {6853,6905,6892 ,2505,2506,2507 }, {1137,1229,1228 ,2479,2481,2480 }, - {1302,832,1744 ,909,613,1666 }, {832,1059,1744 ,613,678,1666 }, - {222,179,2188 ,2508,2497,2496 }, {2222,2210,6563 ,2509,2510,464 }, - {132,2837,584 ,2503,2511,2512 }, {2364,3342,3343 ,2513,2514,2515 }, - {1548,1617,1616 ,902,904,1150 }, {239,238,2290 ,1103,868,1982 }, - {822,460,70 ,2000,1999,323 }, {3698,3697,3644 ,2122,2224,1651 }, - {5988,5972,4147 ,2516,399,2517 }, {620,720,719 ,2474,2475,2476 }, - {5309,505,469 ,987,986,988 }, {1137,1180,1229 ,2479,2518,2481 }, - {1349,2002,1971 ,2519,2493,2520 }, {3647,3646,3594 ,2521,2123,2522 }, - {2698,4369,5335 ,105,104,2523 }, {3016,132,584 ,1028,2503,2512 }, - {626,725,625 ,801,721,720 }, {290,291,335 ,1042,1012,1014 }, - {992,991,904 ,958,1335,1730 }, {1302,1744,687 ,909,1666,1665 }, - {1978,2034,1977 ,847,1289,1503 }, {289,333,332 ,1044,1043,2471 }, - {333,430,429 ,1043,2524,2472 }, {332,333,429 ,2471,1043,2472 }, - {430,482,429 ,2524,2525,2472 }, {482,523,522 ,2525,2526,2473 }, - {429,482,522 ,2472,2525,2473 }, {523,621,620 ,2526,2527,2474 }, - {522,523,620 ,2473,2526,2474 }, {621,721,720 ,2527,2528,2475 }, - {620,621,720 ,2474,2527,2475 }, {721,813,812 ,2528,2529,559 }, - {720,721,812 ,2475,2528,559 }, {813,900,899 ,2529,2530,2477 }, - {812,813,899 ,559,2529,2477 }, {900,988,899 ,2530,2531,2477 }, - {988,1044,1043 ,2531,2532,2478 }, {899,988,1043 ,2477,2531,2478 }, - {1044,1138,1137 ,2532,2533,2479 }, {1043,1044,1137 ,2478,2532,2479 }, - {1138,1139,1180 ,2533,2534,2518 }, {1137,1138,1180 ,2479,2533,2518 }, - {1139,1230,1229 ,2534,2535,2481 }, {1180,1139,1229 ,2518,2534,2481 }, - {1230,1324,1323 ,2535,2536,2482 }, {1229,1230,1323 ,2481,2535,2482 }, - {1324,1401,1400 ,2536,2537,2483 }, {1323,1324,1400 ,2482,2536,2483 }, - {1473,1472,1400 ,2538,2484,2483 }, {1401,1473,1400 ,2537,2538,2483 }, - {1473,1545,1544 ,2538,2539,2485 }, {1472,1473,1544 ,2484,2538,2485 }, - {1614,1613,1544 ,2070,2486,2485 }, {1545,1614,1544 ,2539,2070,2485 }, - {1614,1681,1680 ,2070,1009,2487 }, {1613,1614,1680 ,2486,2070,2487 }, - {1681,1739,1680 ,1009,1011,2487 }, {1739,1774,1680 ,1011,2488,2487 }, - {1801,1800,1774 ,1433,2489,2488 }, {1739,1801,1774 ,1011,1433,2488 }, - {1801,1837,1836 ,1433,997,2490 }, {1800,1801,1836 ,2489,1433,2490 }, - {1837,380,1836 ,997,2540,2490 }, {5497,3350,4530 ,1375,2541,1376 }, - {2003,2002,1349 ,2542,2493,2519 }, {1349,1971,1948 ,2519,2520,2543 }, - {2028,2027,2002 ,1811,2492,2493 }, {2003,2028,2002 ,2542,1811,2493 }, - {2028,2120,2119 ,1811,1597,2495 }, {2027,2028,2119 ,2492,1811,2495 }, - {2189,2188,2119 ,1599,2496,2495 }, {2120,2189,2119 ,1597,1599,2495 }, - {243,222,2188 ,1465,2508,2496 }, {2189,243,2188 ,1599,1465,2496 }, - {394,179,222 ,1600,2497,2508 }, {243,394,222 ,1465,1600,2508 }, - {1449,1374,179 ,2069,2498,2497 }, {394,1449,179 ,1600,2069,2497 }, - {1449,910,954 ,2069,1930,2499 }, {1374,1449,954 ,2498,2069,2499 }, - {539,914,954 ,2067,2500,2499 }, {910,539,954 ,1930,2067,2499 }, - {77,487,914 ,2544,2501,2500 }, {539,77,914 ,2067,2544,2500 }, - {1194,1100,487 ,393,2466,2501 }, {77,1194,487 ,2544,393,2501 }, - {1194,799,1100 ,393,392,2466 }, {3699,3700,3745 ,2121,2545,2546 }, - {2837,2501,108 ,2511,2547,1336 }, {584,2837,108 ,2512,2511,1336 }, - {2501,107,108 ,2547,2548,1336 }, {929,1014,1013 ,1234,1233,2010 }, - {928,929,1013 ,1997,1234,2010 }, {1014,1062,1061 ,1233,1232,2048 }, - {1013,1014,1061 ,2010,1233,2048 }, {1062,1106,1105 ,1232,1664,2049 }, - {1061,1062,1105 ,2048,1232,2049 }, {1105,1106,1202 ,2049,1664,339 }, - {304,2796,2662 ,532,2462,805 }, {3868,3916,3879 ,970,969,2549 }, - {239,387,339 ,1103,1102,1143 }, {621,622,721 ,2527,2550,2528 }, - {1044,1139,1138 ,2532,2534,2533 }, {391,1623,1088 ,1647,1709,1604 }, - {108,107,2134 ,1336,2548,1337 }, {2243,2324,2242 ,1993,1995,2551 }, - {2121,2190,2189 ,1598,1574,1599 }, {1189,910,1449 ,1495,1930,2069 }, - {107,751,35 ,2548,1365,1338 }, {2134,107,35 ,1337,2548,1338 }, - {5698,5697,5679 ,44,2552,2553 }, {523,622,621 ,2526,2550,2527 }, - {1044,988,1139 ,2532,2531,2534 }, {7271,7270,7229 ,2554,2555,2556 }, - {3547,3546,3494 ,803,2557,736 }, {2742,2029,2003 ,2558,1748,2542 }, - {539,1194,77 ,2067,393,2544 }, {7139,7161,7138 ,1025,2559,2560 }, - {2005,2031,2742 ,1593,1560,2558 }, {290,334,333 ,1042,1267,1043 }, - {334,431,430 ,1267,1268,2524 }, {333,334,430 ,1043,1267,2524 }, - {431,483,482 ,1268,1228,2525 }, {430,431,482 ,2524,1268,2525 }, - {483,524,523 ,1228,1278,2526 }, {482,483,523 ,2525,1228,2526 }, - {524,623,622 ,1278,1100,2550 }, {523,524,622 ,2526,1278,2550 }, - {623,722,721 ,1100,1052,2528 }, {622,623,721 ,2550,1100,2528 }, - {814,813,721 ,1054,2529,2528 }, {722,814,721 ,1052,1054,2528 }, - {901,900,813 ,1428,2530,2529 }, {814,901,813 ,1054,1428,2529 }, - {901,989,988 ,1428,1813,2531 }, {900,901,988 ,2530,1428,2531 }, - {989,1140,1139 ,1813,1499,2534 }, {988,989,1139 ,2531,1813,2534 }, - {1140,1231,1230 ,1499,1332,2535 }, {1139,1140,1230 ,2534,1499,2535 }, - {1231,1325,1324 ,1332,1331,2536 }, {1230,1231,1324 ,2535,1332,2536 }, - {1402,1401,1324 ,1780,2537,2536 }, {1325,1402,1324 ,1331,1780,2536 }, - {1474,1473,1401 ,1649,2538,2537 }, {1402,1474,1401 ,1780,1649,2537 }, - {1474,1546,1545 ,1649,1360,2539 }, {1473,1474,1545 ,2538,1649,2539 }, - {1546,1615,1614 ,1360,1359,2070 }, {1545,1546,1614 ,2539,1360,2070 }, - {2293,2358,292 ,2561,2562,2563 }, {2292,2293,292 ,1983,2561,2563 }, - {253,966,921 ,2564,2565,1922 }, {7129,7139,7138 ,1026,1025,2560 }, - {406,2408,2219 ,2566,1399,177 }, {8085,8073,973 ,2016,2567,2017 }, - {2421,4056,2420 ,2568,1123,273 }, {3315,1195,5194 ,944,725,727 }, - {247,253,921 ,2569,2564,1922 }, {8143,8123,8128 ,2570,2247,959 }, - {2363,2364,2440 ,2571,2513,2572 }, {2914,2895,2857 ,2573,2574,2575 }, - {2437,2436,2358 ,2576,2577,2562 }, {2928,2927,2883 ,2578,516,2579 }, - {3152,3196,1619 ,2580,2581,1003 }, {2626,2732,2625 ,2582,2583,2584 }, - {3151,3152,1550 ,2585,2580,2586 }, {1479,3151,1550 ,1270,2585,2586 }, - {1550,3152,1619 ,2586,2580,1003 }, {2942,554,2366 ,2587,2588,2589 }, - {2857,2765,2914 ,2575,2590,2573 }, {2880,2881,2924 ,2591,2592,2593 }, - {2840,3250,2875 ,2594,2595,2596 }, {2895,2380,2581 ,2574,2597,2598 }, - {3197,3196,3152 ,2599,2581,2580 }, {2515,2599,2598 ,2600,2601,2602 }, - {2890,2889,2848 ,2603,2604,2605 }, {1010,2890,2848 ,1286,2603,2605 }, - {1630,56,3081 ,1016,2606,1017 }, {2635,2388,2389 ,2607,2608,2609 }, - {2952,2489,2570 ,2610,2611,2612 }, {2430,91,2133 ,2613,1984,2614 }, - {6951,6870,6796 ,2615,2616,2617 }, {2804,2786,1062 ,2618,2619,1232 }, - {2786,2829,1062 ,2619,2620,1232 }, {2879,2878,2846 ,2621,2622,2623 }, - {2847,2879,2846 ,2624,2621,2623 }, {2736,2737,2786 ,2625,2626,2619 }, - {2544,2744,3137 ,2451,2627,2628 }, {1201,1200,1103 ,1477,2629,1474 }, - {2508,2507,388 ,2630,2631,2202 }, {6945,6944,6913 ,2632,2633,2634 }, - {2566,2526,2527 ,2635,2636,2637 }, {2566,2614,2526 ,2635,2638,2636 }, - {2476,1304,2300 ,2441,2639,2640 }, {3357,3356,3323 ,883,872,1051 }, - {1010,2848,925 ,1286,2605,1287 }, {2738,2737,2685 ,2641,2626,2642 }, - {2787,2786,2737 ,2643,2619,2626 }, {2738,2787,2737 ,2641,2643,2626 }, - {2684,2683,2612 ,2644,2645,2646 }, {437,2559,2590 ,1149,2647,2648 }, - {2900,2633,2300 ,2649,2650,2640 }, {3596,3648,3595 ,1792,734,927 }, - {2507,2508,2559 ,2631,2630,2647 }, {2508,2590,2559 ,2630,2648,2647 }, - {630,3317,1377 ,2491,1882,2651 }, {2787,2788,2786 ,2643,2652,2619 }, - {4587,135,2320 ,774,2653,1406 }, {2830,2829,2786 ,2654,2620,2619 }, - {2788,2830,2786 ,2652,2654,2619 }, {2830,2866,1107 ,2654,2655,2656 }, - {2829,2830,1107 ,2620,2654,2656 }, {2684,2737,2683 ,2644,2626,2645 }, - {2737,2736,2683 ,2626,2625,2645 }, {2633,2552,2300 ,2650,2657,2640 }, - {2507,437,388 ,2631,1149,2202 }, {2507,2559,437 ,2631,2647,1149 }, - {6435,6480,6479 ,2658,1524,2659 }, {406,2224,746 ,2566,2660,2661 }, - {2265,2297,2296 ,2662,2663,2664 }, {2613,2612,2526 ,2665,2646,2636 }, - {172,244,534 ,667,606,632 }, {2725,2726,2724 ,2666,2667,2668 }, - {172,2543,244 ,667,2669,606 }, {2833,3981,3075 ,2065,915,2670 }, - {6321,7251,3288 ,706,2671,2058 }, {2583,2629,685 ,2672,2673,2674 }, - {2718,2717,2665 ,2675,2676,2677 }, {549,2522,2609 ,1129,2678,1130 }, - {2769,2847,2846 ,2679,2624,2623 }, {6479,6480,6507 ,2659,1524,2680 }, - {2718,2770,2769 ,2675,2681,2679 }, {6809,6860,6859 ,712,473,472 }, - {4672,5892,5878 ,2682,2683,2684 }, {2343,8097,8061 ,2685,1710,453 }, - {966,3039,1090 ,2565,2686,2687 }, {819,818,726 ,2244,1018,669 }, - {3468,3439,3440 ,2688,2689,2690 }, {2445,448,2518 ,1388,1172,2388 }, - {2717,2718,2769 ,2676,2675,2679 }, {691,647,2609 ,1173,973,1130 }, - {921,966,1090 ,1922,2565,2687 }, {1047,2921,1146 ,1328,2691,2467 }, - {3154,3197,3153 ,2692,2599,2693 }, {978,1030,1077 ,1620,1619,2694 }, - {1479,1407,1480 ,1270,1269,2695 }, {7196,3288,7232 ,2401,2058,2124 }, - {2770,2815,2769 ,2681,2696,2679 }, {5328,2238,5301 ,2172,2697,2698 }, - {589,590,244 ,671,607,606 }, {2543,589,244 ,2669,671,606 }, {649,175,702 ,1579,211,2699 }, - {3180,2967,2385 ,2700,2701,2702 }, {2141,4985,6707 ,2015,2703,2704 }, - {2671,2724,2723 ,2705,2668,2706 }, {2193,2125,2563 ,1007,892,2707 }, - {3700,3746,3745 ,2545,2708,2546 }, {8140,8102,3263 ,2269,253,252 }, - {3019,3058,1407 ,2709,2710,1269 }, {204,271,203 ,1722,1608,2711 }, - {2769,2815,2847 ,2679,2696,2624 }, {2636,5650,5452 ,2712,2713,2714 }, - {2343,1074,8073 ,2685,2715,2567 }, {744,743,691 ,1367,971,1173 }, - {1370,3019,1407 ,1500,2709,1269 }, {2553,171,395 ,2716,921,966 }, - {3236,2792,3269 ,2717,2718,2719 }, {2084,860,953 ,1361,1385,1237 }, - {8076,2810,1295 ,617,1728,618 }, {2637,165,1238 ,2720,1869,1569 }, - {2664,2717,628 ,2721,2676,1177 }, {584,108,1294 ,2512,1336,635 }, - {3122,2543,172 ,2068,2669,667 }, {2543,2897,589 ,2669,2722,671 }, - {2897,442,589 ,2722,673,671 }, {289,332,288 ,1044,2471,2723 }, - {3352,3353,3378 ,2724,2424,1442 }, {1098,2760,1096 ,2725,2726,2727 }, - {292,388,239 ,2563,2202,1103 }, {2129,2131,2130 ,1469,656,1505 }, - {6039,6134,5488 ,2728,2729,2730 }, {4881,4293,4957 ,2731,2732,2733 }, - {1477,1550,1549 ,1045,2586,903 }, {4827,4887,5334 ,2734,2735,2736 }, - {2041,1743,553 ,2737,763,792 }, {3273,2543,3122 ,2738,2669,2068 }, - {3273,2623,2543 ,2738,2739,2669 }, {2623,2404,2897 ,2739,2740,2722 }, - {2543,2623,2897 ,2669,2739,2722 }, {2404,442,2897 ,2740,673,2722 }, - {2515,2514,2442 ,2600,2741,2742 }, {748,2541,241 ,1677,1298,314 }, - {252,966,1158 ,2743,2565,2744 }, {3461,3490,3430 ,1152,2502,1976 }, - {4924,1166,7073 ,2745,2746,662 }, {7074,4924,7073 ,2747,2745,662 }, - {3247,6430,144 ,1494,2748,853 }, {7344,7343,7285 ,2749,2750,2751 }, - {2903,2425,1411 ,2752,2753,850 }, {597,2903,1411 ,791,2752,850 }, - {2425,2759,2041 ,2753,2754,2737 }, {1411,2425,2041 ,850,2753,2737 }, - {2759,2458,1743 ,2754,2755,763 }, {2041,2759,1743 ,2737,2754,763 }, - {3122,2623,3273 ,2068,2739,2738 }, {2404,2479,442 ,2740,2756,673 }, - {2842,310,442 ,2757,674,673 }, {2479,2842,442 ,2756,2757,673 }, - {2842,2694,310 ,2757,811,674 }, {4887,5377,5376 ,2735,361,2758 }, - {3353,3379,3378 ,2424,1979,1442 }, {2728,2727,2676 ,2759,2760,2761 }, - {2870,2528,2367 ,2762,2763,2764 }, {2666,2719,2718 ,2765,2766,2675 }, - {2687,2635,2761 ,2767,2607,2768 }, {2718,2719,2770 ,2675,2766,2681 }, - {1407,1406,1370 ,1269,1135,1500 }, {8092,6531,8120 ,2267,2280,906 }, - {2614,2612,2613 ,2638,2646,2665 }, {3153,3197,3152 ,2693,2599,2580 }, - {2597,2640,2596 ,2769,2770,2771 }, {2372,597,2654 ,2772,791,2773 }, - {2372,2903,597 ,2772,2752,791 }, {2458,2546,3122 ,2755,2774,2068 }, - {1743,2458,3122 ,763,2755,2068 }, {2546,2699,3122 ,2774,2775,2068 }, - {3122,2699,2623 ,2068,2775,2739 }, {2479,2627,2842 ,2756,2776,2757 }, - {2627,2694,2842 ,2776,811,2757 }, {2411,2528,2870 ,2777,2763,2762 }, - {2424,2411,2870 ,2778,2777,2762 }, {2635,3250,2388 ,2607,2595,2608 }, - {2556,2430,2388 ,2779,2613,2608 }, {2931,2979,2978 ,2780,2781,2782 }, - {3136,2372,2654 ,2783,2772,2773 }, {2372,2425,2903 ,2772,2753,2752 }, - {2458,2457,2546 ,2755,2784,2774 }, {2546,2828,2623 ,2774,2785,2739 }, - {2699,2546,2623 ,2775,2774,2739 }, {2854,2703,2479 ,2786,2787,2756 }, - {2404,2854,2479 ,2740,2786,2756 }, {2479,2703,2627 ,2756,2787,2776 }, - {2615,2614,2566 ,2788,2638,2635 }, {198,127,199 ,1392,1395,2789 }, - {3236,3269,2456 ,2717,2719,2790 }, {2649,3114,2536 ,2791,2792,2793 }, - {2687,2761,2857 ,2767,2768,2575 }, {2840,2388,3250 ,2594,2608,2595 }, - {3089,3112,3132 ,2794,2795,2796 }, {3181,2425,2372 ,2797,2753,2772 }, - {2758,2759,2425 ,2798,2754,2753 }, {3181,2758,2425 ,2797,2798,2753 }, - {2758,2457,2458 ,2798,2784,2755 }, {2759,2758,2458 ,2754,2798,2755 }, - {2828,2992,2623 ,2785,2799,2739 }, {2992,2348,2404 ,2799,2800,2740 }, - {2623,2992,2404 ,2739,2799,2740 }, {2404,2348,2854 ,2740,2800,2786 }, - {3513,2582,3404 ,441,873,2170 }, {2739,2738,2685 ,2801,2641,2642 }, - {2705,2739,2685 ,2802,2801,2642 }, {3048,1904,3049 ,2165,2164,2803 }, - {2728,2756,2727 ,2759,2804,2760 }, {3090,3994,3993 ,2805,2806,2807 }, - {3112,3090,3993 ,2795,2805,2807 }, {1840,940,1869 ,198,197,2808 }, - {3128,3169,3127 ,2809,2810,2811 }, {2616,3136,2548 ,2812,2783,2813 }, - {2486,2485,7615 ,2814,2815,2816 }, {2616,2372,3136 ,2812,2772,2783 }, - {2642,2758,3181 ,2817,2798,2797 }, {2288,2642,3181 ,2818,2817,2797 }, - {2758,2642,2457 ,2798,2817,2784 }, {2546,2992,2828 ,2774,2799,2785 }, - {2348,2757,2703 ,2800,2819,2787 }, {2854,2348,2703 ,2786,2800,2787 }, - {2757,2632,2627 ,2819,2820,2776 }, {2703,2757,2627 ,2787,2819,2776 }, - {2627,2689,145 ,2776,2821,810 }, {162,238,100 ,2822,868,2823 }, - {2777,2823,2822 ,2824,2825,2826 }, {2788,2787,2738 ,2652,2643,2641 }, - {2739,2788,2738 ,2801,2652,2641 }, {3069,3070,3111 ,2827,2828,2829 }, - {2780,2779,2727 ,2830,2831,2760 }, {2756,2780,2727 ,2804,2830,2760 }, - {2528,3183,3135 ,2763,2832,2833 }, {2367,2528,3135 ,2764,2763,2833 }, - {2838,2372,2616 ,2834,2772,2812 }, {2838,2996,2372 ,2834,2835,2772 }, - {2288,3181,2372 ,2818,2797,2772 }, {2996,2288,2372 ,2835,2818,2772 }, - {2642,2605,2457 ,2817,2836,2784 }, {2605,3225,2546 ,2836,2837,2774 }, - {2457,2605,2546 ,2784,2836,2774 }, {2546,3225,2992 ,2774,2837,2799 }, - {2348,2574,2757 ,2800,2838,2819 }, {2574,2448,2632 ,2838,2839,2820 }, - {2757,2574,2632 ,2819,2838,2820 }, {2632,2448,2627 ,2820,2839,2776 }, - {2448,2553,2689 ,2839,2716,2821 }, {2627,2448,2689 ,2776,2839,2821 }, - {5698,5699,5726 ,44,43,515 }, {2622,2549,2567 ,2840,2841,2842 }, - {2890,2936,2889 ,2603,2843,2604 }, {2830,2867,2866 ,2654,2844,2655 }, - {2692,3081,56 ,2845,1017,2606 }, {2534,198,199 ,2846,1392,2789 }, - {2764,2839,3142 ,2847,2848,2849 }, {1907,1248,3878 ,1245,2850,1248 }, - {3142,2839,90 ,2849,2848,2851 }, {2344,2554,2702 ,2852,2853,2854 }, - {2401,2995,2702 ,2855,2856,2854 }, {2554,2401,2702 ,2853,2855,2854 }, - {3070,3069,3031 ,2828,2827,2857 }, {3032,3070,3031 ,2858,2828,2857 }, - {2804,1062,929 ,2618,1232,1234 }, {3097,2838,2616 ,2859,2834,2812 }, - {2834,3097,2616 ,2860,2859,2812 }, {3097,2996,2838 ,2859,2835,2834 }, - {2904,2288,2996 ,2861,2818,2835 }, {3097,2904,2996 ,2859,2861,2835 }, - {2606,2642,2288 ,2862,2817,2818 }, {2904,2606,2288 ,2861,2862,2818 }, - {2606,2783,2605 ,2862,2863,2836 }, {2642,2606,2605 ,2817,2862,2836 }, - {2783,2519,3225 ,2863,2864,2837 }, {2605,2783,3225 ,2836,2863,2837 }, - {3225,2519,2992 ,2837,2864,2799 }, {2767,299,2348 ,2865,2866,2800 }, - {2992,2767,2348 ,2799,2865,2800 }, {2348,299,2574 ,2800,2866,2838 }, - {8083,8099,5771 ,2103,2867,2868 }, {2820,2819,2773 ,2869,2870,2871 }, - {369,199,271 ,1521,2789,1608 }, {2152,821,2136 ,1545,2872,1546 }, - {5013,5950,8108 ,684,2873,2287 }, {1285,1135,1226 ,1944,1943,2354 }, - {2205,2222,6563 ,2874,2509,464 }, {2839,2202,90 ,2848,2875,2851 }, - {1249,2488,2617 ,2876,2877,2878 }, {2488,2542,2371 ,2877,2879,2880 }, - {2617,2488,2371 ,2878,2877,2880 }, {2269,2506,2505 ,2881,2882,2883 }, - {3207,3237,3206 ,2884,2885,2886 }, {2736,2786,929 ,2625,2619,1234 }, - {2735,2736,929 ,2887,2625,1234 }, {2855,3097,2834 ,2888,2859,2860 }, - {2519,2766,2992 ,2864,2889,2799 }, {2992,2766,2767 ,2799,2889,2865 }, - {3020,3021,3059 ,2890,2891,2892 }, {2665,2717,2664 ,2677,2676,2721 }, - {2006,2005,2742 ,1181,1593,2558 }, {2633,2656,2584 ,2650,2893,2894 }, - {2656,2331,2584 ,2893,2895,2894 }, {3054,2763,1787 ,916,2896,2897 }, - {2504,2331,2461 ,2898,2895,2899 }, {5914,6098,5191 ,1566,1565,2900 }, - {3236,3237,2792 ,2717,2885,2718 }, {1663,3081,115 ,1015,1017,2901 }, - {2786,2804,929 ,2619,2618,1234 }, {3317,1804,3347 ,1882,2902,2098 }, - {3191,2403,4961 ,2903,2904,2905 }, {2403,2970,2834 ,2904,2906,2860 }, - {2970,2855,2834 ,2906,2888,2860 }, {2855,2904,3097 ,2888,2861,2859 }, - {2783,2520,2519 ,2863,2907,2864 }, {2519,2520,2766 ,2864,2907,2889 }, - {2766,3015,299 ,2889,2908,2866 }, {2767,2766,299 ,2865,2889,2866 }, - {3015,3078,299 ,2908,2909,2866 }, {3078,2752,2574 ,2909,2910,2838 }, - {299,3078,2574 ,2866,2909,2838 }, {2752,2951,2448 ,2910,2911,2839 }, - {2574,2752,2448 ,2838,2910,2839 }, {2951,2378,2553 ,2911,2912,2716 }, - {2448,2951,2553 ,2839,2911,2716 }, {2553,2378,171 ,2716,2912,921 }, - {1687,1686,3196 ,1104,1004,2581 }, {2886,2885,2821 ,2913,2914,2915 }, - {2774,2773,2721 ,2916,2871,2917 }, {2526,2612,2565 ,2636,2646,2918 }, - {2626,2839,2764 ,2582,2848,2847 }, {411,2495,2899 ,2919,2920,2921 }, - {3223,2495,2911 ,2922,2920,2923 }, {2899,2495,3223 ,2921,2920,2922 }, - {2495,2366,2911 ,2920,2589,2923 }, {2911,2366,1336 ,2923,2589,2924 }, - {2371,2656,2633 ,2880,2893,2650 }, {3747,3746,3700 ,2925,2708,2545 }, - {2946,2338,4040 ,1062,458,460 }, {2696,2970,2403 ,2926,2906,2904 }, - {2696,2855,2970 ,2926,2888,2906 }, {2603,2904,2855 ,2927,2861,2888 }, - {2898,2606,2904 ,2928,2862,2861 }, {2603,2898,2904 ,2927,2928,2861 }, - {2898,2784,2783 ,2928,2929,2863 }, {2606,2898,2783 ,2862,2928,2863 }, - {2783,2784,2520 ,2863,2929,2907 }, {2766,3043,3015 ,2889,2930,2908 }, - {3015,3043,3078 ,2908,2930,2909 }, {3043,2712,2752 ,2930,2931,2910 }, - {3078,3043,2752 ,2909,2930,2910 }, {2641,2951,2752 ,2932,2911,2910 }, - {2712,2641,2752 ,2931,2932,2910 }, {2641,2498,2378 ,2932,2933,2912 }, - {2951,2641,2378 ,2911,2932,2912 }, {5681,5699,2453 ,2934,43,42 }, - {2638,2592,2666 ,2935,2936,2765 }, {2526,2565,2525 ,2636,2918,2937 }, - {8116,8109,8119 ,172,2938,184 }, {1088,1452,1592 ,1604,1606,2939 }, - {2820,2884,2883 ,2869,2940,2579 }, {2444,2443,2368 ,2941,2942,2943 }, - {2715,3095,3013 ,2944,2945,2946 }, {3179,2617,2503 ,2947,2878,2948 }, - {2666,2667,2719 ,2765,2949,2766 }, {987,3452,3977 ,2950,1794,440 }, - {3327,1387,3636 ,439,2951,1060 }, {1336,785,2085 ,2924,2952,657 }, - {3061,3101,3100 ,2953,2954,2955 }, {2868,2869,2907 ,1577,1576,111 }, - {3237,3236,3206 ,2885,2717,2886 }, {2165,2696,2403 ,1041,2926,2904 }, - {2969,2855,2696 ,2956,2888,2926 }, {2483,2898,2603 ,2957,2928,2927 }, - {3186,2483,2603 ,2958,2957,2927 }, {3220,2784,2898 ,2959,2929,2928 }, - {2483,3220,2898 ,2957,2959,2928 }, {2784,3220,2520 ,2929,2959,2907 }, - {2520,2917,2766 ,2907,2960,2889 }, {2766,2917,3043 ,2889,2960,2930 }, - {2498,2496,171 ,2933,2961,921 }, {2378,2498,171 ,2912,2933,921 }, - {3264,2301,872 ,2468,2132,2962 }, {2492,493,2496 ,2963,1785,2961 }, - {1390,131,151 ,2964,2965,2966 }, {3215,3013,2354 ,2967,2946,2968 }, - {4240,3796,3797 ,2060,2969,2061 }, {8084,8127,8110 ,1139,2970,1140 }, - {2631,2840,2586 ,2971,2594,2972 }, {2380,2586,2581 ,2597,2972,2598 }, - {2599,2600,2673 ,2601,2973,2974 }, {2600,2674,2673 ,2973,2975,2974 }, - {3596,3595,3545 ,1792,927,926 }, {3595,3594,3544 ,927,2522,1153 }, - {1002,2431,1283 ,2976,2977,2978 }, {2363,2362,2297 ,2571,2979,2663 }, - {2362,2363,2439 ,2979,2571,2980 }, {2363,2440,2439 ,2571,2572,2980 }, - {2440,2512,2439 ,2572,2981,2980 }, {2671,2670,2595 ,2705,2982,2983 }, - {3138,2537,1050 ,2984,2985,2282 }, {2515,2600,2599 ,2600,2973,2601 }, - {3504,3468,3441 ,2986,2688,2987 }, {2166,2947,2696 ,1040,2988,2926 }, - {2165,2166,2696 ,1041,1040,2926 }, {2253,2603,2855 ,2989,2927,2888 }, - {2969,2253,2855 ,2956,2989,2888 }, {2253,3186,2603 ,2989,2958,2927 }, - {3220,2561,2520 ,2959,2990,2907 }, {2561,2562,2520 ,2990,2991,2907 }, - {2520,2562,2917 ,2907,2991,2960 }, {3043,2539,2712 ,2930,2992,2931 }, - {2539,2716,2712 ,2992,2993,2931 }, {2490,2641,2712 ,2994,2932,2931 }, - {2716,2490,2712 ,2993,2994,2931 }, {2490,2498,2641 ,2994,2933,2932 }, - {2845,2844,2800 ,2995,1329,2996 }, {2977,2999,2976 ,2997,2998,2999 }, - {2364,2441,2440 ,2513,3000,2572 }, {111,258,1653 ,1154,3001,106 }, - {4543,4547,4980 ,3002,242,241 }, {2929,2930,2928 ,3003,3004,2578 }, - {2493,2467,1789 ,3005,3006,3007 }, {2434,2400,2545 ,1118,1117,3008 }, - {3492,3491,3461 ,930,955,1152 }, {2673,2674,2700 ,2974,2975,3009 }, - {2653,2964,2131 ,3010,3011,656 }, {3382,3432,3407 ,870,929,3012 }, - {1049,119,2084 ,1362,2278,1361 }, {2570,2489,2298 ,2612,2611,3013 }, - {2723,2722,2669 ,2706,3014,3015 }, {2671,2723,2669 ,2705,2706,3015 }, - {2671,2669,2670 ,2705,3015,2982 }, {4554,6299,1989 ,1307,760,3016 }, - {355,250,301 ,1876,1916,1857 }, {2947,2969,2696 ,2988,2956,2926 }, - {2919,2253,2969 ,3017,2989,2956 }, {2253,2483,3186 ,2989,2957,2958 }, - {3220,2562,2561 ,2959,2991,2990 }, {2562,114,2917 ,2991,3018,2960 }, - {2989,3043,2917 ,3019,2930,2960 }, {114,2989,2917 ,3018,3019,2960 }, - {2989,2539,3043 ,3019,2992,2930 }, {2751,2498,2490 ,3020,2933,2994 }, - {2751,2492,2496 ,3020,2963,2961 }, {2498,2751,2496 ,2933,3020,2961 }, - {3431,3461,3430 ,3021,1152,1976 }, {2133,157,250 ,2614,1877,1916 }, - {925,2848,2802 ,1287,2605,3022 }, {3123,3153,3152 ,3023,2693,2580 }, - {4563,2382,126 ,3024,3025,1394 }, {1047,2877,2921 ,1328,3026,2691 }, - {3212,2942,2366 ,3027,2587,2589 }, {3154,1590,3198 ,2692,3028,1168 }, - {1480,3123,3152 ,2695,3023,2580 }, {2625,2732,2839 ,2584,2583,2848 }, - {2732,3116,2839 ,2583,3029,2848 }, {3462,3461,3431 ,931,1152,3021 }, - {835,8127,4106 ,3030,2970,1742 }, {2862,1984,1917 ,3031,831,833 }, - {2069,2530,1984 ,1711,2389,831 }, {2862,2069,1984 ,3031,1711,831 }, - {1238,1000,1515 ,1569,1384,1502 }, {2570,2298,2963 ,2612,3013,3032 }, - {2537,2677,1050 ,2985,3033,2282 }, {1972,2003,1349 ,3034,2542,2519 }, - {8109,8110,8127 ,2938,1140,2970 }, {2619,2969,2947 ,3035,2956,2988 }, - {2089,2619,2947 ,1325,3035,2988 }, {2619,2919,2969 ,3035,3017,2956 }, - {2253,2491,2483 ,2989,3036,2957 }, {2491,3214,3220 ,3036,3037,2959 }, - {2483,2491,3220 ,2957,3036,2959 }, {3214,2562,3220 ,3037,2991,2959 }, - {2482,2539,2989 ,3038,2992,3019 }, {2858,2873,2492 ,3039,3040,2963 }, - {2751,2858,2492 ,3020,3039,2963 }, {2873,494,2492 ,3040,3041,2963 }, - {6430,3777,5733 ,2748,1582,1584 }, {3228,1725,178 ,3042,1747,3043 }, - {3221,8127,835 ,183,2970,3030 }, {3116,3219,2839 ,3029,3044,2848 }, - {2988,1336,2653 ,3045,2924,3010 }, {2604,2964,2653 ,3046,3011,3010 }, - {2768,221,1553 ,1297,1296,1669 }, {3698,3699,3744 ,2122,2121,2390 }, - {2475,3215,2793 ,3047,2967,3048 }, {3433,3432,3382 ,3049,929,870 }, - {3957,3997,3996 ,295,380,1928 }, {3956,3957,3996 ,1383,295,1928 }, - {2343,8085,8067 ,2685,2016,2168 }, {3432,3462,3431 ,929,931,3021 }, - {1337,1844,1782 ,2148,911,1105 }, {2537,2572,968 ,2985,3050,1661 }, - {2851,2908,2503 ,3051,3052,2948 }, {2908,3179,2503 ,3052,2947,2948 }, - {5681,5659,5660 ,2934,3053,3054 }, {2943,2851,2503 ,3055,3051,2948 }, - {2919,2679,2253 ,3017,3056,2989 }, {2679,2463,2491 ,3056,3057,3036 }, - {2253,2679,2491 ,2989,3056,3036 }, {2463,2398,3214 ,3057,3058,3037 }, - {2491,2463,3214 ,3036,3057,3037 }, {2575,2562,3214 ,3059,2991,3037 }, - {2398,2575,3214 ,3058,3059,3037 }, {2575,114,2562 ,3059,3018,2991 }, - {2482,2989,114 ,3038,3019,3018 }, {2762,2716,2539 ,3060,2993,2992 }, - {2482,2762,2539 ,3038,3060,2992 }, {2762,3280,2716 ,3060,3061,2993 }, - {3092,2490,2716 ,3062,2994,2993 }, {3280,3092,2716 ,3061,3062,2993 }, - {3092,2858,2751 ,3062,3039,3020 }, {2490,3092,2751 ,2994,3062,3020 }, - {3192,3285,6689 ,3063,3064,3065 }, {2383,199,127 ,3066,2789,1395 }, - {4254,4255,4019 ,416,165,227 }, {2316,2383,2382 ,3067,3066,3025 }, - {2315,2316,2382 ,3068,3067,3025 }, {2383,127,2382 ,3066,1395,3025 }, - {4031,2650,2585 ,3069,914,875 }, {2646,2614,2615 ,3070,2638,2788 }, - {2686,2685,2614 ,3071,2642,2638 }, {925,2782,2730 ,1287,3072,2150 }, - {845,943,4970 ,1828,1807,3073 }, {2839,3219,2202 ,2848,3044,2875 }, - {3219,650,2202 ,3044,3074,2875 }, {3432,3431,3407 ,929,3021,3012 }, - {3543,3542,3488 ,3075,979,1980 }, {2576,1665,2967 ,3076,3077,2701 }, - {3745,3746,3744 ,2546,2708,2390 }, {2628,3180,2835 ,3078,2700,3079 }, - {2293,2263,2294 ,2561,3080,3081 }, {3592,3591,3542 ,2084,1652,979 }, - {3543,3592,3542 ,3075,2084,979 }, {1183,155,154 ,1238,1497,1466 }, - {2537,3138,2572 ,2985,2984,3050 }, {2619,2902,2919 ,3035,3082,3017 }, - {2919,2902,2679 ,3017,3082,3056 }, {3044,114,2575 ,3083,3018,3059 }, - {3044,2402,114 ,3083,3084,3018 }, {2402,2482,114 ,3084,3038,3018 }, - {2540,2762,2482 ,3085,3060,3038 }, {2762,2540,3280 ,3060,3085,3061 }, - {2794,3092,3280 ,3086,3062,3061 }, {2540,2794,3280 ,3085,3086,3061 }, - {2492,494,493 ,2963,3041,1785 }, {3904,3958,3903 ,1264,294,296 }, - {3183,219,1855 ,2832,3087,3088 }, {2646,2686,2614 ,3070,3071,2638 }, - {3671,978,1077 ,3089,1620,2694 }, {1304,2431,4377 ,2639,2977,3090 }, - {2686,2705,2685 ,3071,2802,2642 }, {2544,3137,2966 ,2451,2628,3091 }, - {2298,2793,2744 ,3013,3048,2627 }, {925,2802,2782 ,1287,3022,3072 }, - {6348,7253,6349 ,1607,1855,705 }, {3064,3063,3025 ,3092,3093,3094 }, - {1336,2604,2653 ,2924,3046,3010 }, {3026,3064,3025 ,3095,3092,3094 }, - {2628,2576,2967 ,3078,3076,2701 }, {3061,3062,3101 ,2953,3096,2954 }, - {3062,3102,3101 ,3096,3097,2954 }, {3024,3062,3023 ,3098,3096,3099 }, - {3878,106,1156 ,1248,1073,3100 }, {3431,3430,3380 ,3021,1976,1514 }, - {460,1876,1853 ,1999,1386,3101 }, {2619,631,2902 ,3035,1426,3082 }, - {2902,631,2679 ,3082,1426,3056 }, {2402,3044,2575 ,3084,3083,3059 }, - {2916,2482,2402 ,3102,3038,3084 }, {2714,2858,3092 ,3103,3039,3062 }, - {2794,2714,3092 ,3086,3103,3062 }, {3082,2873,2858 ,3104,3040,3039 }, - {2714,3082,2858 ,3103,3104,3039 }, {3495,3434,3496 ,735,737,3105 }, - {2619,2090,631 ,3035,1324,1426 }, {845,4970,760 ,1828,3073,3106 }, - {2131,2865,2201 ,656,3107,2455 }, {2973,3021,2972 ,3108,2891,2047 }, - {4614,4613,4590 ,3109,1488,1490 }, {2594,2593,2511 ,3110,3111,3112 }, - {3062,3061,3023 ,3096,2953,3099 }, {3215,2354,2426 ,2967,2968,3113 }, - {2354,3013,3173 ,2968,2946,3114 }, {2793,3215,2426 ,3048,2967,3113 }, - {2924,2925,2974 ,2593,3115,3116 }, {2744,2793,3193 ,2627,3048,3117 }, - {631,2944,2679 ,1426,3118,3056 }, {2395,2463,2679 ,3119,3057,3056 }, - {2480,2398,2463 ,3120,3058,3057 }, {2395,2480,2463 ,3119,3120,3057 }, - {2398,2480,2575 ,3058,3120,3059 }, {2575,3174,2402 ,3059,3121,3084 }, - {2289,2482,2916 ,3122,3038,3102 }, {2482,2289,2540 ,3038,3122,3085 }, - {2836,2873,3082 ,3123,3040,3104 }, {1806,494,2873 ,3124,3041,3040 }, - {2836,1806,2873 ,3123,3124,3040 }, {494,1806,802 ,3041,3124,400 }, - {3890,3926,3889 ,1913,3125,3126 }, {6434,6435,6479 ,3127,2658,2659 }, - {4576,7524,4575 ,1628,1630,3128 }, {203,2383,2316 ,2711,3066,3067 }, - {2317,203,2316 ,3129,2711,3067 }, {270,199,2383 ,3130,2789,3066 }, - {203,270,2383 ,2711,3130,3066 }, {2609,790,744 ,1130,1339,1367 }, - {3011,2624,2914 ,3131,3132,2573 }, {6707,6643,8101 ,2704,3133,383 }, - {3149,2537,3143 ,3134,2985,3135 }, {3064,3104,3063 ,3092,3136,3093 }, - {3104,3103,3063 ,3136,3137,3093 }, {2152,391,821 ,1545,1647,2872 }, - {2669,2668,2593 ,3015,3138,3111 }, {2576,795,1665 ,3076,302,3077 }, - {3193,2630,2894 ,3117,3139,3140 }, {2628,2967,3180 ,3078,2701,2700 }, - {3193,2793,2630 ,3117,3048,3139 }, {2570,2963,2544 ,2612,3032,2451 }, - {2341,8064,3008 ,845,3141,546 }, {4212,4213,294 ,895,1070,945 }, - {928,830,929 ,1997,1996,1234 }, {2644,2944,631 ,3142,3118,1426 }, - {2478,2644,631 ,3143,3142,1426 }, {2644,2679,2944 ,3142,3056,3118 }, - {2396,2395,2679 ,3144,3119,3056 }, {2644,2396,2679 ,3142,3144,3056 }, - {2312,2339,2575 ,3145,3146,3059 }, {2480,2312,2575 ,3120,3145,3059 }, - {2575,2339,3174 ,3059,3146,3121 }, {2339,2648,2402 ,3146,3147,3084 }, - {3174,2339,2402 ,3121,3146,3084 }, {2916,3115,2289 ,3102,3148,3122 }, - {3077,2794,2540 ,3149,3086,3085 }, {2289,3077,2540 ,3122,3149,3085 }, - {2347,2714,2794 ,3150,3103,3086 }, {3077,2347,2794 ,3149,3150,3086 }, - {2836,3082,2714 ,3123,3104,3103 }, {2347,2836,2714 ,3150,3123,3103 }, - {3496,3434,3464 ,3105,737,3151 }, {2316,2275,2317 ,3067,3152,3129 }, - {1165,4831,3472 ,3153,3154,3155 }, {2293,2294,2358 ,2561,3081,2562 }, - {3489,3543,3488 ,1977,3075,1980 }, {2544,2966,2622 ,2451,3091,2840 }, - {6796,8063,8136 ,2617,465,2449 }, {2669,2667,2668 ,3015,2949,3138 }, - {2818,2882,2881 ,3156,518,2592 }, {2882,2926,2925 ,518,517,3115 }, - {2881,2882,2925 ,2592,518,3115 }, {1550,1477,1479 ,2586,1045,1270 }, - {953,1048,2084 ,1237,1236,1361 }, {2926,2975,2974 ,517,3157,3116 }, - {2975,2997,2974 ,3157,3158,3116 }, {2925,2926,2974 ,3115,517,3116 }, - {3155,1552,3100 ,3159,2266,2955 }, {3101,3155,3100 ,2954,3159,2955 }, - {2295,2296,2360 ,3160,2664,3161 }, {3024,3023,2997 ,3098,3099,3158 }, - {2511,2510,2438 ,3112,3162,3163 }, {2975,3024,2997 ,3157,3098,3158 }, - {3156,3155,3101 ,3164,3159,2954 }, {3137,3193,2894 ,2628,3117,3140 }, - {2312,2480,2395 ,3145,3120,3119 }, {2396,2312,2395 ,3144,3145,3119 }, - {2648,2806,2916 ,3147,3165,3102 }, {2402,2648,2916 ,3084,3147,3102 }, - {2916,2806,3115 ,3102,3165,3148 }, {2377,2347,3077 ,3166,3150,3149 }, - {3042,2377,3077 ,3167,3166,3149 }, {2355,1806,2836 ,3168,3124,3123 }, - {3490,3489,3430 ,2502,1977,1976 }, {1923,211,459 ,418,415,696 }, - {1145,1146,1236 ,1277,2467,1434 }, {1187,1367,1919 ,1778,574,1779 }, - {2597,2596,2512 ,2769,2771,2981 }, {2691,2353,2570 ,3169,3170,2612 }, - {3149,3143,968 ,3134,3135,1661 }, {5616,5615,3274 ,646,3171,3172 }, - {2721,2720,2667 ,2917,3173,2949 }, {2669,2721,2667 ,3015,2917,2949 }, - {2817,2818,2881 ,3174,3156,2592 }, {6968,7027,7008 ,595,3175,3176 }, - {3277,8066,8098 ,961,960,907 }, {3155,3156,1552 ,3159,3164,2266 }, - {2438,2437,2360 ,3163,2576,3161 }, {2361,2438,2360 ,3177,3163,3161 }, - {2667,2720,2719 ,2949,3173,2766 }, {3137,2744,3193 ,2628,2627,3117 }, - {119,460,2084 ,2278,1999,1361 }, {1443,5616,3274 ,182,646,3172 }, - {2135,2136,2478 ,1425,1546,3143 }, {2648,298,2806 ,3147,3178,3165 }, - {2806,2860,2289 ,3165,3179,3122 }, {3115,2806,2289 ,3148,3165,3122 }, - {2466,3042,3077 ,3180,3167,3149 }, {2289,2466,3077 ,3122,3180,3149 }, - {2377,2355,2836 ,3166,3168,3123 }, {2347,2377,2836 ,3150,3166,3123 }, - {2355,2465,1806 ,3168,3181,3124 }, {3041,1860,3618 ,3182,3183,37 }, - {553,1743,1727 ,792,763,793 }, {6388,6435,6434 ,3184,2658,3127 }, - {204,203,2317 ,1722,2711,3129 }, {2318,204,2317 ,3185,1722,3129 }, - {5191,6388,6434 ,2900,3184,3127 }, {3228,178,2532 ,3042,3043,3186 }, - {1107,2866,1205 ,2656,2655,3187 }, {3260,3149,968 ,3188,3134,1661 }, - {2909,2911,2678 ,3189,2923,3190 }, {2777,2801,2776 ,2824,3191,3192 }, - {3104,3158,3157 ,3136,3193,3194 }, {2963,2744,2544 ,3032,2627,2451 }, - {1657,1725,3228 ,1781,1747,3042 }, {2874,2628,2386 ,3195,3078,3196 }, - {2818,2817,2771 ,3156,3174,3197 }, {2772,2818,2771 ,3198,3156,3197 }, - {2136,1512,2478 ,1546,3199,3143 }, {3246,2644,2478 ,3200,3142,3143 }, - {1512,3246,2478 ,3199,3200,3143 }, {2399,2396,2644 ,3201,3144,3142 }, - {2399,2529,2312 ,3201,3202,3145 }, {2396,2399,2312 ,3144,3201,3145 }, - {2312,2529,2339 ,3145,3202,3146 }, {3245,298,2648 ,3203,3178,3147 }, - {2339,3245,2648 ,3146,3203,3147 }, {2860,2753,2289 ,3179,456,3122 }, - {2289,2753,2466 ,3122,456,3180 }, {110,111,226 ,743,1154,108 }, - {2590,2589,437 ,2648,3204,1149 }, {5390,5037,6317 ,3205,3206,3207 }, - {2521,2533,2506 ,3208,3209,2882 }, {2867,2940,1205 ,2844,110,3187 }, - {2011,3001,5615 ,129,3210,3171 }, {1208,1668,1942 ,3211,3212,2337 }, - {1687,1740,1782 ,1104,3213,1105 }, {2966,3137,2894 ,3091,2628,3140 }, - {3060,3061,3100 ,3214,2953,2955 }, {2779,2778,2727 ,2831,3215,2760 }, - {165,1373,341 ,1869,1501,1852 }, {787,802,4130 ,209,400,210 }, - {3173,2385,2434 ,3114,2702,1118 }, {2385,2433,2434 ,2702,1116,1118 }, - {2011,3913,3001 ,129,841,3210 }, {2296,2361,2360 ,2664,3177,3161 }, - {2720,2772,2771 ,3173,3198,3197 }, {2719,2720,2771 ,2766,3173,3197 }, - {2883,2882,2818 ,2579,518,3156 }, {2386,2628,2835 ,3196,3078,3079 }, - {2549,2894,2370 ,2841,3140,3216 }, {3102,3156,3101 ,3097,3164,2954 }, - {2266,3246,1512 ,3217,3200,3199 }, {821,2266,1512 ,2872,3217,3199 }, - {2266,2644,3246 ,3217,3142,3200 }, {2414,2399,2644 ,3218,3201,3142 }, - {2266,2414,2644 ,3217,3218,3142 }, {2414,2299,2529 ,3218,3219,3202 }, - {2399,2414,2529 ,3201,3218,3202 }, {2299,2447,2339 ,3219,3220,3146 }, - {2529,2299,2339 ,3202,3219,3146 }, {2339,2447,3245 ,3146,3220,3203 }, - {2447,2446,298 ,3220,3221,3178 }, {3245,2447,298 ,3203,3220,3178 }, - {2806,2753,2860 ,3165,456,3179 }, {2753,2538,3042 ,456,3222,3167 }, - {2466,2753,3042 ,3180,456,3167 }, {3119,2377,3042 ,3223,3166,3167 }, - {2538,3119,3042 ,3222,3223,3167 }, {3119,2747,2355 ,3223,3224,3168 }, - {2377,3119,2355 ,3166,3223,3168 }, {2355,2747,2465 ,3168,3224,3181 }, - {3970,3969,3925 ,3225,3226,3227 }, {2319,204,2318 ,3228,1722,3185 }, - {1211,2255,2697 ,2007,3229,2006 }, {2489,2715,2475 ,2611,2944,3047 }, - {2506,2533,2805 ,2882,3209,3230 }, {3990,3299,3254 ,3231,3232,3233 }, - {2602,2675,2600 ,3234,3235,2973 }, {2417,2874,2386 ,3236,3195,3196 }, - {2427,2379,2677 ,3237,3238,3033 }, {286,287,331 ,2446,2470,2403 }, - {2624,2949,3405 ,3132,3239,1798 }, {3429,3460,3459 ,1978,3240,3241 }, - {2886,2863,2931 ,2913,3242,2780 }, {2863,2932,2931 ,3242,3243,2780 }, - {2512,2511,2438 ,2981,3112,3163 }, {593,4223,1599 ,1108,1934,391 }, - {3199,1726,1658 ,3244,1746,1782 }, {2511,2512,2595 ,3112,2981,2983 }, - {2510,2593,2592 ,3162,3111,2936 }, {2532,1874,1337 ,3186,962,2148 }, - {2549,2370,2567 ,2841,3216,2842 }, {2038,1983,1984 ,1551,1001,831 }, - {2963,2298,2744 ,3032,3013,2627 }, {2819,2883,2818 ,2870,2579,3156 }, - {2475,3013,3215 ,3047,2946,2967 }, {2462,2413,2417 ,3245,3246,3236 }, - {2413,2900,2417 ,3246,2649,3236 }, {2715,2462,2417 ,2944,3245,3236 }, - {2900,2551,2874 ,2649,3247,3195 }, {1304,1255,2576 ,2639,3248,3076 }, - {2551,2576,3175 ,3247,3076,3249 }, {2551,1304,2576 ,3247,2639,3076 }, - {5616,2011,5615 ,646,129,3171 }, {2446,2849,2806 ,3221,3250,3165 }, - {298,2446,2806 ,3178,3221,3165 }, {2849,3144,2806 ,3250,3251,3165 }, - {2806,3144,2753 ,3165,3251,456 }, {2346,3119,2538 ,3252,3223,3222 }, - {2573,2346,2538 ,3253,3252,3222 }, {2747,2681,2465 ,3224,3254,3181 }, - {8118,1218,8126 ,2129,3255,812 }, {135,204,2319 ,2653,1722,3228 }, - {2320,135,2319 ,1406,2653,3228 }, {135,205,204 ,2653,1741,1722 }, - {8138,4106,8078 ,2018,1742,1744 }, {135,273,205 ,2653,1749,1741 }, - {3913,4248,3001 ,841,3256,3210 }, {2369,2445,2368 ,3257,1388,2943 }, - {2351,3149,2201 ,3258,3134,2455 }, {2588,2449,2556 ,2072,3259,2779 }, - {6911,6912,6942 ,474,519,521 }, {6943,6963,6962 ,520,1048,3260 }, - {3428,3429,3459 ,3261,1978,3241 }, {788,1390,3091 ,976,2964,3262 }, - {3260,968,2201 ,3188,1661,2455 }, {3325,3359,3305 ,825,824,879 }, - {2417,2386,2715 ,3236,3196,2944 }, {3199,1658,3156 ,3244,1782,3164 }, - {2439,2512,2438 ,2980,2981,3163 }, {6531,6897,8134 ,2280,54,3263 }, - {2354,3173,2434 ,2968,3114,1118 }, {3103,3104,3157 ,3137,3136,3194 }, - {2622,2966,2894 ,2840,3091,3140 }, {795,456,1665 ,302,301,3077 }, - {336,291,236 ,1013,1012,2444 }, {2356,2909,1785 ,3264,3189,1659 }, - {3267,2356,1785 ,3265,3264,1659 }, {3267,1785,1726 ,3265,1659,1746 }, - {3229,3267,1726 ,3266,3265,1746 }, {5346,5364,5363 ,3267,3268,3269 }, - {2754,2349,2849 ,3270,457,3250 }, {2446,2754,2849 ,3221,3270,3250 }, - {2849,2349,3144 ,3250,457,3251 }, {3144,2349,2753 ,3251,457,456 }, - {2811,2573,2538 ,3271,3253,3222 }, {2753,2811,2538 ,456,3271,3222 }, - {2792,2732,2626 ,2718,2583,2582 }, {8062,8065,8090 ,3272,96,412 }, - {2445,2481,2444 ,1388,3273,2941 }, {2822,2886,2821 ,2826,2913,2915 }, - {3212,2587,2942 ,3027,3274,2587 }, {1383,788,3091 ,336,976,3262 }, - {2932,2979,2931 ,3243,2781,2780 }, {2943,2503,2691 ,3055,2948,3169 }, - {2617,2852,2647 ,2878,3275,3276 }, {4571,972,2162 ,1342,3277,3278 }, - {3158,3200,3199 ,3193,3279,3244 }, {3157,3158,3199 ,3194,3193,3244 }, - {2653,2129,2069 ,3010,1469,1711 }, {2900,2874,2417 ,2649,3195,3236 }, - {2678,2911,1917 ,3190,2923,833 }, {2911,1336,2988 ,2923,2924,3045 }, - {821,3185,2266 ,2872,3280,3217 }, {3005,2414,2266 ,3281,3218,3217 }, - {3272,2754,2446 ,3282,3270,3221 }, {2447,3272,2446 ,3220,3282,3221 }, - {2349,890,2660 ,457,3283,455 }, {2753,2660,2811 ,456,455,3271 }, - {2380,2895,2914 ,2597,2574,2573 }, {2370,1482,227 ,3216,2265,2157 }, - {270,203,271 ,3130,2711,1608 }, {3134,3135,1855 ,3284,2833,3088 }, - {2367,3135,3134 ,2764,2833,3284 }, {643,2676,2602 ,3285,2761,3234 }, - {2760,2749,1096 ,2726,3286,2727 }, {4395,4347,4373 ,726,3287,3288 }, - {2807,2555,2545 ,3289,1066,3008 }, {329,2393,330 ,2348,2445,2402 }, - {3293,3291,7151 ,1988,1203,1202 }, {3089,3088,3048 ,2794,3290,2165 }, - {3049,3089,3048 ,2803,2794,2165 }, {554,2569,2366 ,2588,3291,2589 }, - {554,2268,2085 ,2588,3292,657 }, {3013,3095,3173 ,2946,2945,3114 }, - {2894,2630,2370 ,3140,3139,3216 }, {3231,411,2356 ,3293,2919,3264 }, - {3230,3231,2356 ,3294,3293,3264 }, {3199,3200,3229 ,3244,3279,3266 }, - {3200,3230,3229 ,3279,3294,3266 }, {2653,2069,2862 ,3010,1711,3031 }, - {2462,2715,2489 ,3245,2944,2611 }, {3157,3199,3156 ,3194,3244,3164 }, - {1725,1784,178 ,1747,1662,3043 }, {2031,2067,2006 ,1560,1592,1181 }, - {2551,3175,2874 ,3247,3249,3195 }, {8080,8069,8141 ,1094,1095,3295 }, - {1917,2911,2988 ,833,2923,3045 }, {3159,3158,3104 ,3296,3193,3136 }, - {3105,3159,3104 ,3297,3296,3136 }, {3065,3064,3026 ,3298,3092,3095 }, - {2464,840,1697 ,3299,68,69 }, {1088,3185,821 ,1604,3280,2872 }, - {1088,821,391 ,1604,2872,1647 }, {1088,2266,3185 ,1604,3217,3280 }, - {2309,3005,2266 ,3300,3281,3217 }, {1088,2309,2266 ,1604,3300,3217 }, - {3005,2309,2414 ,3281,3300,3218 }, {2309,2734,2299 ,3300,3301,3219 }, - {2414,2309,2299 ,3218,3300,3219 }, {2734,2311,2447 ,3301,3302,3220 }, - {2299,2734,2447 ,3219,3301,3220 }, {2447,2311,3272 ,3220,3302,3282 }, - {3272,2311,2754 ,3282,3302,3270 }, {2811,2660,2573 ,3271,455,3253 }, - {2591,2592,2638 ,3303,2936,2935 }, {2380,2914,2624 ,2597,2573,3132 }, - {2895,2581,2857 ,2574,2598,2575 }, {2930,2931,2977 ,3004,2780,2997 }, - {3000,3031,2980 ,3304,2857,3305 }, {3165,3207,3164 ,3306,2884,3307 }, - {2587,3256,554 ,3274,3308,2588 }, {2821,2885,2820 ,2915,2914,2869 }, - {3673,4185,52 ,648,409,411 }, {2949,2624,3011 ,3239,3132,3131 }, - {3429,3488,3460 ,1978,1980,3240 }, {2726,2777,2776 ,2667,2824,3192 }, - {5284,5338,5337 ,571,63,572 }, {4904,5377,4887 ,3309,361,2735 }, - {368,2534,369 ,1396,2846,1521 }, {3102,3103,3156 ,3097,3137,3164 }, - {2411,3081,2692 ,2777,1017,2845 }, {2936,2935,2889 ,2843,3310,2604 }, - {5284,5283,5239 ,571,570,482 }, {2268,2132,2085 ,3292,655,657 }, - {2979,3029,2978 ,2781,3311,2782 }, {2617,2647,2353 ,2878,3276,3170 }, - {78,2302,2495 ,3312,3313,2920 }, {2503,2617,2353 ,2948,2878,3170 }, - {3204,3234,3233 ,3314,3315,3316 }, {2302,2741,2495 ,3313,3317,2920 }, - {3162,3204,3203 ,3318,3314,3319 }, {3161,3162,3203 ,3320,3318,3319 }, - {3268,2302,3233 ,3321,3313,3316 }, {3234,3268,3233 ,3315,3321,3316 }, - {2302,2332,2741 ,3313,3322,3317 }, {3160,3161,3202 ,3323,3320,3324 }, - {3203,3233,3232 ,3319,3316,3325 }, {3107,3161,3140 ,3326,3320,3327 }, - {3161,3203,3202 ,3320,3319,3324 }, {3200,3201,3230 ,3279,3328,3294 }, - {2988,2653,2862 ,3045,3010,3031 }, {860,460,1853 ,1385,1999,3101 }, - {2508,2509,2592 ,2630,3329,2936 }, {840,2464,3450 ,68,3299,3330 }, - {2597,2673,2672 ,2769,2974,3331 }, {2311,2750,2754 ,3302,3332,3270 }, - {2750,890,2349 ,3332,3283,457 }, {2754,2750,2349 ,3270,3332,457 }, - {254,109,4036 ,2008,1654,1602 }, {687,1155,831 ,1665,3333,3334 }, - {546,2602,2560 ,1171,3234,3335 }, {2497,2132,2268 ,3336,655,3292 }, - {5264,5284,5239 ,1192,571,482 }, {8094,8088,8100 ,98,1998,1713 }, - {4041,2946,4040 ,1063,1062,460 }, {2456,2792,2626 ,2790,2718,2582 }, - {3207,3206,3164 ,2884,2886,3307 }, {3111,3165,3110 ,2829,3306,3337 }, - {486,437,529 ,1144,1149,1148 }, {2971,3019,1370 ,3338,2709,1500 }, - {2683,2736,2735 ,2645,2625,2887 }, {2704,2683,2735 ,3339,2645,2887 }, - {2730,741,827 ,2150,1221,1220 }, {220,131,8 ,3340,2965,3341 }, - {8093,8078,8076 ,2288,1744,617 }, {2801,2777,2822 ,3191,2824,2826 }, - {2675,2727,2674 ,3235,2760,2975 }, {3232,3233,78 ,3325,3316,3312 }, - {3233,2302,78 ,3316,3313,3312 }, {3203,3204,3233 ,3319,3314,3316 }, - {2445,2444,2368 ,1388,2941,2943 }, {6804,6805,6854 ,3342,3343,3344 }, - {5276,5330,5350 ,1525,3345,1526 }, {1294,108,874 ,635,1336,636 }, - {3684,3422,3664 ,1915,2055,1557 }, {554,90,2987 ,2588,2851,3346 }, - {90,2269,2987 ,2851,2881,3346 }, {3161,3160,3140 ,3320,3323,3327 }, - {3201,3200,3158 ,3328,3279,3193 }, {3159,3201,3158 ,3296,3328,3193 }, - {2630,2948,1482 ,3139,3347,2265 }, {2630,2577,2948 ,3139,3348,3347 }, - {2630,2853,2577 ,3139,806,3348 }, {2977,3028,3027 ,2997,3349,3350 }, - {2817,2881,2880 ,3174,2592,2591 }, {3105,3104,3064 ,3297,3136,3092 }, - {3065,3105,3064 ,3298,3297,3092 }, {2365,2441,2364 ,3351,3000,2513 }, - {3029,3066,3065 ,3311,3352,3298 }, {3028,3029,3065 ,3349,3311,3298 }, - {2978,3029,3028 ,2782,3311,3349 }, {2977,2978,3028 ,2997,2782,3349 }, - {2311,2419,2750 ,3302,3353,3332 }, {787,494,802 ,209,3041,400 }, - {2750,2419,890 ,3332,3353,3283 }, {1211,3636,939 ,2007,1060,3354 }, - {3636,1453,939 ,1060,3355,3354 }, {3487,3541,3540 ,3356,981,3357 }, - {221,2541,748 ,1296,1298,1677 }, {2532,1337,3226 ,3186,2148,3358 }, - {2263,2293,2262 ,3080,2561,3359 }, {3206,3236,3205 ,2886,2717,3360 }, - {3206,3205,3163 ,2886,3360,3361 }, {3165,3164,3110 ,3306,3307,3337 }, - {3036,3074,3035 ,3362,3363,3364 }, {2665,2664,2590 ,2677,2721,2648 }, - {1146,2921,1237 ,2467,2691,1467 }, {1237,2971,1370 ,1467,3338,1500 }, - {2930,2977,2928 ,3004,2997,2578 }, {3994,4036,4035 ,2806,1602,1601 }, - {3283,1556,2968 ,262,3365,263 }, {3182,2536,49 ,3366,2793,3367 }, - {2536,1015,3017 ,2793,3368,3369 }, {2533,2536,3182 ,3209,2793,3366 }, - {2535,2536,2533 ,3370,2793,3209 }, {2649,2536,2535 ,2791,2793,3370 }, - {2597,2672,2671 ,2769,3331,2705 }, {3227,3226,3198 ,1167,3358,1168 }, - {3067,3066,3029 ,3371,3352,3311 }, {3046,3067,3029 ,3372,3371,3311 }, - {3202,3203,3232 ,3324,3319,3325 }, {2781,2780,2756 ,3373,2830,2804 }, - {2729,2728,2676 ,3374,2759,2761 }, {2701,2729,2676 ,3375,3374,2761 }, - {2729,2756,2728 ,3374,2804,2759 }, {8087,8145,6797 ,385,3376,2246 }, - {2879,2923,2922 ,2621,2046,2045 }, {2878,2879,2922 ,2622,2621,2045 }, - {3209,3239,3238 ,3377,3378,3379 }, {199,270,271 ,2789,3130,1608 }, - {3066,3107,3106 ,3352,3326,3380 }, {3085,3066,3106 ,3381,3352,3380 }, - {1071,3661,2455 ,3382,789,475 }, {2822,2821,2776 ,2826,2915,3192 }, - {3027,3065,3026 ,3350,3298,3095 }, {3605,3604,3556 ,3383,3384,3385 }, - {1011,2864,1010 ,1285,3386,1286 }, {1688,451,134 ,1718,3387,1783 }, - {1592,1056,2309 ,2939,3388,3300 }, {1088,1592,2309 ,1604,2939,3300 }, - {2918,2734,2309 ,3389,3301,3300 }, {1056,2918,2309 ,3388,3389,3300 }, - {2918,2412,2311 ,3389,3390,3302 }, {2734,2918,2311 ,3301,3389,3302 }, - {2311,2412,2419 ,3302,3390,3353 }, {2840,2875,2687 ,2594,2596,2767 }, - {2629,2494,794 ,2673,3391,3392 }, {2637,1373,165 ,2720,1501,1869 }, - {3061,3060,3022 ,2953,3214,3393 }, {3236,3235,3205 ,2717,3394,3360 }, - {2565,2612,2524 ,2918,2646,3395 }, {2424,2870,2367 ,2778,2762,2764 }, - {3744,3746,3796 ,2390,2708,2969 }, {2905,2424,2367 ,399,2778,2764 }, - {2931,2930,2885 ,2780,3004,2914 }, {3169,3168,3127 ,2810,3396,2811 }, - {5363,6260,5346 ,3269,3397,3267 }, {2601,2600,2517 ,3398,2973,3399 }, - {2727,2778,2755 ,2760,3215,3400 }, {2597,2598,2673 ,2769,2602,2974 }, - {819,2769,2846 ,2244,2679,2623 }, {2436,2435,2358 ,2577,3401,2562 }, - {2729,2781,2756 ,3374,3373,2804 }, {2698,3187,4248 ,105,3402,3256 }, - {3065,3066,3085 ,3298,3352,3381 }, {2297,2362,2361 ,2663,2979,3177 }, - {3072,3125,3071 ,3403,3404,3405 }, {2999,2977,3027 ,2998,2997,3350 }, - {2359,2437,2358 ,3406,2576,2562 }, {2427,3117,2379 ,3237,3407,3238 }, - {6748,6757,1990 ,3408,3409,1305 }, {2581,2687,2857 ,2598,2767,2575 }, - {455,410,1665 ,2205,2204,3077 }, {2155,5444,2157 ,580,3410,3411 }, - {2443,2515,2442 ,2942,2600,2742 }, {2987,2497,2268 ,3346,3336,3292 }, - {2125,2193,2124 ,892,1007,1006 }, {3129,3086,3130 ,3412,3413,3414 }, - {1346,1806,2465 ,484,3124,3181 }, {2525,2565,2524 ,2937,2918,3395 }, - {2801,2822,2776 ,3191,2826,3192 }, {1740,1337,1782 ,3213,2148,1105 }, - {3405,2949,2920 ,1798,3239,1799 }, {2629,2583,2461 ,2673,2672,2899 }, - {2675,2674,2600 ,3235,2975,2973 }, {3108,3107,3066 ,3415,3326,3352 }, - {3067,3108,3066 ,3371,3415,3352 }, {2488,2693,2542 ,2877,3416,2879 }, - {2673,2725,2724 ,2974,2666,2668 }, {3256,90,554 ,3308,2851,2588 }, - {3011,2914,2965 ,3131,2573,3417 }, {7250,7233,3288 ,3418,2125,2058 }, - {1479,1480,3151 ,1270,2695,2585 }, {1480,3152,3151 ,2695,2580,2585 }, - {2884,2929,2928 ,2940,3003,2578 }, {2296,2297,2361 ,2664,2663,3177 }, - {2672,2673,2671 ,3331,2974,2705 }, {3742,3793,3792 ,3419,3420,3421 }, - {6742,4784,3192 ,3422,3423,3063 }, {3228,3227,1656 ,3042,1167,1166 }, - {2694,145,60 ,811,810,675 }, {2652,2918,1056 ,3424,3389,3388 }, - {2652,2745,2412 ,3424,3425,3390 }, {2918,2652,2412 ,3389,3424,3390 }, - {2412,2420,2419 ,3390,273,3353 }, {2513,2512,2440 ,3426,2981,2572 }, - {5166,6257,5473 ,3427,3428,3429 }, {219,3195,1855 ,3087,3430,3088 }, - {199,369,2534 ,2789,1521,2846 }, {2823,2863,2822 ,2825,3242,2826 }, - {2825,2824,2778 ,3431,3432,3215 }, {4256,4275,4255 ,3433,699,165 }, - {2555,2807,2416 ,1066,3289,808 }, {2915,2631,2711 ,3434,2971,3435 }, - {2525,2524,2485 ,2937,3395,2815 }, {4216,4256,4255 ,1241,3433,165 }, - {4215,4216,4255 ,370,1241,165 }, {2612,2611,2524 ,2646,3436,3395 }, - {4548,3247,2252 ,589,1494,590 }, {2779,2825,2778 ,2831,3431,3215 }, - {2294,2359,2358 ,3081,3406,2562 }, {2925,2924,2881 ,3115,2593,2592 }, - {3162,3161,3107 ,3318,3320,3326 }, {3108,3162,3107 ,3415,3318,3326 }, - {2693,2494,2542 ,3416,3391,2879 }, {6349,7252,6321 ,705,3437,706 }, - {2577,2416,2710 ,3348,808,3438 }, {4190,1265,1271 ,3439,1273,588 }, - {6416,6418,5089 ,3440,3441,3442 }, {2841,2521,2506 ,3443,3208,2882 }, - {2597,2671,2640 ,2769,2705,2770 }, {818,819,2800 ,1018,2244,2996 }, - {2450,3192,6689 ,3444,3063,3065 }, {2802,2827,2781 ,3022,3445,3373 }, - {2802,2848,2827 ,3022,2605,3445 }, {2497,2506,2344 ,3336,2882,2852 }, - {2702,2427,3149 ,2854,3237,3134 }, {2998,3025,2975 ,3446,3094,3157 }, - {3741,3742,3792 ,3447,3419,3421 }, {3744,3795,3794 ,2390,3448,3449 }, - {2782,2802,2781 ,3072,3022,3373 }, {31,2859,2652 ,3450,3451,3424 }, - {1056,1592,1004 ,3388,2939,3452 }, {2859,2695,2745 ,3451,3453,3425 }, - {2652,2859,2745 ,3424,3451,3425 }, {2745,2695,2412 ,3425,3453,3390 }, - {2695,2421,2420 ,3453,2568,273 }, {2412,2695,2420 ,3390,3453,273 }, - {3742,3743,3793 ,3419,2225,3420 }, {3324,3357,3323 ,881,883,1051 }, - {3743,3794,3793 ,2225,3449,3420 }, {1549,1550,1619 ,903,2586,1003 }, - {287,288,332 ,2470,2723,2471 }, {3269,2792,2456 ,2719,2718,2790 }, - {8114,5950,8108 ,2286,2873,2287 }, {2710,2416,2631 ,3438,808,2971 }, - {3166,3209,3208 ,3454,3377,3455 }, {2740,2739,2705 ,3456,2801,2802 }, - {2686,2740,2705 ,3071,3456,2802 }, {503,1633,466 ,374,373,347 }, - {1032,193,867 ,1581,1448,1121 }, {2985,3036,2984 ,3457,3362,3458 }, - {2740,2788,2739 ,3456,2652,2801 }, {7287,7286,7239 ,3459,3460,1527 }, - {2506,2805,2554 ,2882,3230,2853 }, {2437,2510,2509 ,2576,3162,3329 }, - {2436,2437,2509 ,2577,2576,3329 }, {2567,2370,227 ,2842,3216,2157 }, - {2413,2387,2900 ,3246,3461,2649 }, {2367,3134,3116 ,2764,3284,3029 }, - {2387,2633,2900 ,3461,2650,2649 }, {3824,8131,8133 ,3462,2307,2131 }, - {3954,8062,4957 ,3463,3272,2733 }, {2677,2379,886 ,3033,3238,3464 }, - {2445,2517,2481 ,1388,3399,3273 }, {2681,2464,2465 ,3254,3299,3181 }, - {2505,2506,2497 ,2883,2882,3336 }, {2202,650,2841 ,2875,3074,3443 }, - {6809,6808,6754 ,712,3465,713 }, {2514,2515,2513 ,2741,2600,3426 }, - {3743,3744,3794 ,2225,2390,3449 }, {2560,2518,546 ,3335,2388,1171 }, - {2782,2781,2729 ,3072,3373,3374 }, {2730,2782,2729 ,2150,3072,3374 }, - {6353,8121,6686 ,749,3466,750 }, {3460,3488,3487 ,3240,1980,3356 }, - {3478,3900,8141 ,885,884,3295 }, {4377,1255,1304 ,3090,3248,2639 }, - {3697,3743,3742 ,2224,2225,3419 }, {2774,2820,2773 ,2916,2869,2871 }, - {3025,3063,3062 ,3094,3093,3096 }, {3135,3183,1855 ,2833,2832,3088 }, - {7273,7251,7252 ,3467,2671,3437 }, {2567,3277,2901 ,2842,961,3468 }, - {2867,2868,2940 ,2844,1577,110 }, {1387,3372,1908 ,2951,2143,1058 }, - {147,4027,146 ,3469,3470,504 }, {5103,5418,5412 ,77,3471,360 }, - {1318,1032,867 ,164,1581,1121 }, {1787,1098,1908 ,2897,2725,1058 }, - {6735,6711,6308 ,3472,3473,3474 }, {8123,8108,8128 ,2247,2287,959 }, - {3959,3958,3904 ,982,294,1264 }, {2141,6707,8101 ,2015,2704,383 }, - {2816,2880,2879 ,3475,2591,2621 }, {3345,4106,8138 ,3476,1742,2018 }, - {2774,2776,2820 ,2916,3192,2869 }, {2775,2776,2774 ,3477,3192,2916 }, - {2776,2821,2820 ,3192,2915,2869 }, {3216,1657,3228 ,3478,1781,3042 }, - {2610,2682,790 ,3479,3480,1339 }, {2577,2710,2915 ,3348,3438,3434 }, - {220,4522,95 ,3340,3481,3482 }, {2545,2791,1789 ,3008,3483,3007 }, - {3227,2532,3226 ,1167,3186,3358 }, {2923,2973,2972 ,2046,3108,2047 }, - {2885,2930,2884 ,2914,3004,2940 }, {1590,1656,3198 ,3028,1166,1168 }, - {3212,2366,2495 ,3027,2589,2920 }, {3408,3383,3357 ,3484,882,883 }, - {3129,3128,3074 ,3412,2809,3363 }, {3086,3129,3074 ,3413,3412,3363 }, - {2506,2554,2344 ,2882,2853,2852 }, {2901,8098,2659 ,3468,907,3485 }, - {1917,2988,2862 ,833,3045,3031 }, {2353,2952,2570 ,3170,2610,2612 }, - {1235,1237,1370 ,1179,1467,1500 }, {1249,2693,2488 ,2876,3416,2877 }, - {2841,3213,2521 ,3443,3486,3208 }, {8096,8115,8140 ,1796,2448,2269 }, - {1847,178,1784 ,1519,3043,1662 }, {355,51,2939 ,1876,1856,1870 }, - {4248,3187,3972 ,3256,3402,3487 }, {4236,1334,4371 ,3488,1935,3489 }, - {3031,3030,2980 ,2857,3490,3305 }, {2724,2776,2723 ,2668,3192,2706 }, - {3031,3069,3030 ,2857,2827,3490 }, {2416,2807,1789 ,808,3289,3007 }, - {3890,3889,3854 ,1913,3126,1512 }, {3012,1332,2380 ,3491,3492,2597 }, - {1218,6891,8126 ,3255,813,812 }, {3275,2962,74 ,3493,3494,2053 }, - {2791,3275,2588 ,3483,3493,2072 }, {2278,2323,2322 ,3495,3496,3497 }, - {2323,282,281 ,3496,3498,2227 }, {2322,2323,281 ,3497,3496,2227 }, - {2484,2522,2451 ,3499,2678,2102 }, {3111,3110,3069 ,2829,3337,2827 }, - {3275,74,2588 ,3493,2053,2072 }, {2880,2924,2923 ,2591,2593,2046 }, - {2508,2592,2591 ,2630,2936,3303 }, {2493,2388,2840 ,3005,2608,2594 }, - {2986,3037,2985 ,3500,3501,3457 }, {5353,5352,4022 ,851,3502,3503 }, - {3037,3036,2985 ,3501,3362,3457 }, {2631,2467,2840 ,2971,3006,2594 }, - {3239,3241,2411 ,3378,3504,2777 }, {100,7960,162 ,2823,3505,2822 }, - {3213,2533,2521 ,3486,3209,3208 }, {2587,3142,90 ,3274,2849,2851 }, - {2515,2598,2597 ,2600,2602,2769 }, {2513,2515,2597 ,3426,2600,2769 }, - {7747,7797,7745 ,331,3506,650 }, {55,300,1155 ,3507,3508,3333 }, - {3855,3869,3854 ,1511,1912,1512 }, {2656,2461,2331 ,2893,2899,2895 }, - {3364,3390,3363 ,1184,3509,1911 }, {3129,3170,3128 ,3412,3510,2809 }, - {5556,5535,5536 ,3511,3512,754 }, {7702,7752,7701 ,3513,3514,3515 }, - {2835,2385,3173 ,3079,2702,3114 }, {3074,3128,3073 ,3363,2809,3516 }, - {2586,2840,2687 ,2972,2594,2767 }, {4371,1334,3864 ,3489,1935,3517 }, - {5970,2504,4104 ,3518,2898,3519 }, {5445,6577,4293 ,3520,97,2732 }, - {7874,7873,7847 ,344,817,3521 }, {2322,2277,2278 ,3497,3522,3495 }, - {1168,6643,4985 ,1098,3133,2703 }, {2948,2915,1482 ,3347,3434,2265 }, - {2915,2710,2631 ,3434,3438,2971 }, {1201,2986,1200 ,1477,3500,2629 }, - {2986,2985,1200 ,3500,3457,2629 }, {2449,2588,91 ,3259,2072,1984 }, - {1553,102,347 ,1669,432,431 }, {2879,2880,2923 ,2621,2591,2046 }, - {3103,3157,3156 ,3137,3194,3164 }, {2649,2535,2533 ,2791,3370,3209 }, - {2949,3011,2965 ,3239,3131,3417 }, {3213,2649,2533 ,3486,2791,3209 }, - {2875,3250,2635 ,2596,2595,2607 }, {817,906,905 ,1020,1019,1729 }, - {2789,2788,2740 ,637,2652,3456 }, {2948,2577,2915 ,3347,3348,3434 }, - {2435,2436,2508 ,3401,2577,2630 }, {2831,2830,2788 ,639,2654,2652 }, - {2789,2831,2788 ,637,639,2652 }, {6796,8136,8096 ,2617,2449,1796 }, - {2683,2682,2611 ,2645,3480,3436 }, {2807,2545,1789 ,3289,3008,3007 }, - {2533,2709,2805 ,3209,3523,3230 }, {2542,2494,2656 ,2879,3391,2893 }, - {3232,78,411 ,3325,3312,2919 }, {3231,3232,411 ,3293,3325,2919 }, - {411,78,2495 ,2919,3312,2920 }, {3027,3028,3065 ,3350,3349,3298 }, - {3485,3539,2686 ,3524,3525,3071 }, {1787,2620,1098 ,2897,3526,2725 }, - {2831,2868,2867 ,639,1577,2844 }, {2830,2831,2867 ,2654,639,2844 }, - {4240,6139,4260 ,2060,2059,3527 }, {3024,3025,3062 ,3098,3094,3096 }, - {2875,2635,2687 ,2596,2607,2767 }, {2923,2924,2973 ,2046,2593,3108 }, - {1204,1106,1205 ,338,1664,3187 }, {347,2423,2428 ,431,433,371 }, - {5308,5309,564 ,3528,987,3529 }, {2812,3349,3684 ,1914,2054,1915 }, - {3349,296,3422 ,2054,1558,2055 }, {2835,3180,2385 ,3079,2700,2702 }, - {821,1512,2136 ,2872,3199,1546 }, {6707,4985,6643 ,2704,2703,3133 }, - {2650,8096,8140 ,914,1796,2269 }, {8078,8142,8084 ,1744,1743,1139 }, - {2426,3190,2853 ,3113,807,806 }, {6351,8112,6322 ,1568,3530,3531 }, - {6914,6913,6861 ,3532,2634,3533 }, {1804,3317,296 ,2902,1882,1558 }, - {2436,2509,2508 ,2577,3329,2630 }, {2846,2877,1047 ,2623,3026,1328 }, - {2354,2555,3190 ,2968,1066,807 }, {1194,539,1599 ,393,2067,391 }, - {2426,2354,3190 ,3113,2968,807 }, {2741,2332,3212 ,3317,3322,3027 }, - {2609,2610,790 ,1130,3479,1339 }, {2443,2442,2365 ,2942,2742,3351 }, - {2612,2683,2611 ,2646,2645,3436 }, {1255,4377,795 ,3248,3090,302 }, - {2141,8091,8085 ,2015,2167,2016 }, {2400,111,2962 ,1117,1154,3494 }, - {3238,2424,2792 ,3379,2778,2718 }, {2691,2851,2943 ,3169,3051,3055 }, - {2555,2354,2434 ,1066,2968,1118 }, {3278,2851,2691 ,1158,3051,3169 }, - {382,1005,3732 ,3534,3535,3536 }, {2369,2368,2333 ,3257,2943,3537 }, - {3182,49,2709 ,3366,3367,3523 }, {4039,2680,2662 ,382,459,805 }, - {3746,3797,3796 ,2708,2061,2969 }, {2400,3275,2791 ,1117,3493,3483 }, - {2545,2400,2791 ,3008,1117,3483 }, {3915,3956,3955 ,1575,1383,3538 }, - {3939,3915,3955 ,3539,1575,3538 }, {3956,3996,3995 ,1383,1928,3540 }, - {3955,3956,3995 ,3538,1383,3540 }, {4038,4037,3995 ,1927,3541,3540 }, - {3996,4038,3995 ,1928,1927,3540 }, {238,237,100 ,868,869,2823 }, - {1853,1876,860 ,3101,1386,1385 }, {2760,2906,2428 ,2726,3542,371 }, - {2749,2760,2428 ,3286,2726,371 }, {2906,3270,2428 ,3542,3543,371 }, - {3270,347,2428 ,3543,431,371 }, {964,584,1294 ,535,2512,635 }, - {2719,2771,2770 ,2766,3197,2681 }, {2400,2962,3275 ,1117,3494,3493 }, - {4211,1113,4212 ,215,10,895 }, {8141,8069,3478 ,3295,1095,885 }, - {2242,2324,2323 ,2551,1995,3496 }, {2278,2242,2323 ,3495,2551,3496 }, - {2324,2391,282 ,1995,3544,3498 }, {2323,2324,282 ,3496,1995,3498 }, - {3226,1337,1740 ,3358,2148,3213 }, {2742,2031,2029 ,2558,1560,1748 }, - {2523,2564,2522 ,3545,3546,2678 }, {2610,2609,2522 ,3479,1130,2678 }, - {2564,2610,2522 ,3546,3479,2678 }, {5383,5396,6506 ,3547,3548,3549 }, - {2981,3032,3000 ,3550,2858,3304 }, {1103,1200,2890 ,1474,2629,2603 }, - {2353,2462,2489 ,3170,3245,2611 }, {872,836,3264 ,2962,3551,2468 }, - {3086,3074,3036 ,3413,3363,3362 }, {3037,3086,3036 ,3501,3413,3362 }, - {2682,2683,2704 ,3480,2645,3339 }, {5570,5641,5599 ,508,510,2213 }, - {2578,2635,2389 ,3552,2607,2609 }, {3238,3237,3207 ,3379,2885,2884 }, - {3208,3238,3207 ,3455,3379,2884 }, {3109,3108,3067 ,3553,3415,3371 }, - {2516,2600,2515 ,3554,2973,2600 }, {3068,3109,3067 ,3555,3553,3371 }, - {2933,2980,2979 ,3556,3305,2781 }, {2932,2933,2979 ,3243,3556,2781 }, - {3030,3029,2979 ,3490,3311,2781 }, {3030,3046,3029 ,3490,3372,3311 }, - {2980,3030,2979 ,3305,3490,2781 }, {3068,3067,3046 ,3555,3371,3372 }, - {3030,3068,3046 ,3490,3555,3372 }, {3235,2456,3268 ,3394,2790,3321 }, - {2533,3182,2709 ,3209,3366,3523 }, {2388,2430,2389 ,2608,2613,2609 }, - {2497,2865,2132 ,3336,3107,655 }, {8102,8082,8089 ,253,454,254 }, - {3594,3646,3592 ,2522,2123,2084 }, {3544,3594,3543 ,1153,2522,3075 }, - {3646,3645,3592 ,2123,1650,2084 }, {3594,3592,3593 ,2522,2084,3557 }, - {3594,3593,3543 ,2522,3557,3075 }, {4038,2662,4037 ,1927,805,3541 }, - {2662,254,4037 ,805,2008,3541 }, {5371,5404,5350 ,3558,3559,1526 }, - {562,885,708 ,1936,216,401 }, {80,2939,51 ,140,1870,1856 }, {2013,2711,2586 ,2242,3435,2972 }, - {2380,2013,2586 ,2597,2242,2972 }, {1011,1103,2864 ,1285,1474,3386 }, - {1103,2891,2864 ,1474,3560,3386 }, {2620,2760,1098 ,3526,2726,2725 }, - {2620,2305,2906 ,3526,3561,3542 }, {2760,2620,2906 ,2726,3526,3542 }, - {2305,3270,2906 ,3561,3543,3542 }, {1553,347,3270 ,1669,431,3543 }, - {2305,1553,3270 ,3561,1669,3543 }, {7704,7727,7682 ,3562,3563,3564 }, - {124,1423,5482 ,2206,151,3565 }, {2325,2392,2391 ,1994,3566,3544 }, - {2324,2325,2391 ,1995,1994,3544 }, {1929,1950,1884 ,899,786,3567 }, - {2392,328,2391 ,3566,2289,3544 }, {3166,3208,3207 ,3454,3455,2884 }, - {3165,3166,3207 ,3306,3454,2884 }, {3239,2411,2424 ,3378,2777,2778 }, - {2523,2522,2484 ,3545,2678,3499 }, {2967,111,2433 ,2701,1154,1116 }, - {2385,2967,2433 ,2702,2701,1116 }, {2967,3014,111 ,2701,3568,1154 }, - {511,2003,1972 ,3569,2542,3034 }, {1200,2985,2937 ,2629,3457,3570 }, - {2555,2434,2545 ,1066,1118,3008 }, {3237,3238,2792 ,2885,3379,2718 }, - {3048,3088,3047 ,2165,3290,3571 }, {2592,2667,2666 ,2936,2949,2765 }, - {2967,259,3014 ,2701,2203,3568 }, {2517,2516,2444 ,3399,3554,2941 }, - {2481,2517,2444 ,3273,3399,2941 }, {3234,3235,3268 ,3315,3394,3321 }, - {3205,3235,3204 ,3360,3394,3314 }, {3938,3939,3090 ,3572,3539,2805 }, - {3166,3165,3111 ,3454,3306,2829 }, {3088,3087,3047 ,3290,3573,3571 }, - {2586,2687,2581 ,2972,2767,2598 }, {70,1319,822 ,323,322,2000 }, - {3595,3648,3594 ,927,734,2522 }, {3544,3543,3489 ,1153,3075,1977 }, - {3490,3544,3489 ,2502,1153,1977 }, {2846,2844,2845 ,2623,1329,2995 }, - {2846,2845,819 ,2623,2995,2244 }, {3134,1855,2267 ,3284,3088,3574 }, - {3314,2333,2368 ,3575,3537,2943 }, {2150,3016,964 ,534,1028,535 }, - {3048,3047,1224 ,2165,3571,1508 }, {2790,2789,2740 ,3576,637,3456 }, - {2790,2832,2789 ,3576,638,637 }, {2883,2927,2882 ,2579,516,518 }, - {3399,6659,4550 ,715,3577,716 }, {6353,4544,5444 ,749,748,3410 }, - {1908,3453,1787 ,1058,917,2897 }, {2977,2976,2928 ,2997,2999,2578 }, - {3998,3999,4040 ,381,1023,460 }, {113,697,4539 ,677,3578,633 }, - {3134,2267,650 ,3284,3574,3074 }, {2732,2367,3116 ,2583,2764,3029 }, - {3116,3134,3219 ,3029,3284,3044 }, {3219,3134,650 ,3044,3284,3074 }, - {3033,3032,2981 ,3579,2858,3550 }, {2852,2387,2647 ,3275,3461,3276 }, - {906,1047,1046 ,1019,1328,1929 }, {3058,3123,1407 ,2710,3023,1269 }, - {3069,3110,3068 ,2827,3337,3555 }, {4298,141,4186 ,3580,681,683 }, - {1688,242,451 ,1718,1717,3387 }, {3125,3166,3111 ,3404,3454,2829 }, - {3644,3643,3590 ,1651,2344,1867 }, {28,88,3636 ,1059,3581,1060 }, - {4037,254,4036 ,3541,2008,1602 }, {3381,3407,3380 ,871,3012,1514 }, - {3407,3431,3380 ,3012,3021,1514 }, {1200,2937,2890 ,2629,3570,2603 }, - {2614,2684,2612 ,2638,2644,2646 }, {7910,2164,6483 ,3582,3583,3584 }, - {2298,2475,2793 ,3013,3047,3048 }, {7252,7251,6321 ,3437,2671,706 }, - {3016,584,964 ,1028,2512,535 }, {3227,3228,2532 ,1167,3042,3186 }, - {3241,3081,2411 ,3504,1017,2777 }, {2496,492,171 ,2961,1580,921 }, - {3349,1804,296 ,2054,2902,1558 }, {7067,7066,7028 ,3585,3586,3587 }, - {2326,2393,2392 ,3588,2445,3566 }, {2325,2326,2392 ,1994,3588,3566 }, - {2393,329,2392 ,2445,2348,3566 }, {329,328,2392 ,2348,2289,3566 }, - {3434,3382,3408 ,737,870,3484 }, {2936,2983,2935 ,2843,3589,3310 }, - {3240,3241,3239 ,3590,3504,3378 }, {3198,3226,3197 ,1168,3358,2599 }, - {2441,2513,2440 ,3000,3426,2572 }, {2853,2416,2577 ,806,808,3348 }, - {2805,2709,651 ,3230,3523,3591 }, {2764,3142,2587 ,2847,2849,3274 }, - {2702,3149,2351 ,2854,3134,3258 }, {2344,2702,2351 ,2852,2854,3258 }, - {2848,2826,2827 ,2605,3592,3445 }, {2682,830,829 ,3480,1996,1368 }, - {493,492,2496 ,1785,1580,2961 }, {2663,921,688 ,3593,1922,765 }, - {3198,3197,3154 ,1168,2599,2692 }, {3226,1740,3197 ,3358,3213,2599 }, - {2837,930,107 ,2511,3594,2548 }, {2726,2755,2777 ,2667,3400,2824 }, - {2517,2600,2516 ,3399,2973,3554 }, {2602,2600,2601 ,3234,2973,3398 }, - {3072,3071,3033 ,3403,3405,3579 }, {3034,3072,3033 ,3595,3403,3579 }, - {3087,3086,3037 ,3573,3413,3501 }, {2704,2735,830 ,3339,2887,1996 }, - {2682,2704,830 ,3480,3339,1996 }, {1168,8122,6643 ,1098,384,3133 }, - {4901,5591,6162 ,3596,3597,3598 }, {688,921,1065 ,765,1922,821 }, - {921,1090,1340 ,1922,2687,822 }, {2590,529,2589 ,2648,1148,3204 }, - {2097,7910,2096 ,846,3582,788 }, {2942,2587,554 ,2587,3274,2588 }, - {2590,2664,529 ,2648,2721,1148 }, {2763,2620,1787 ,2896,3526,2897 }, - {2763,3120,2620 ,2896,3599,3526 }, {2500,2305,2620 ,3600,3561,3526 }, - {3120,2500,2620 ,3599,3600,3526 }, {2500,2768,1553 ,3600,1297,1669 }, - {2305,2500,1553 ,3561,3600,1669 }, {2982,2981,2934 ,3601,3550,3602 }, - {2935,2982,2934 ,3310,3601,3602 }, {3047,3087,3037 ,3571,3573,3501 }, - {2610,2611,2682 ,3479,3436,3480 }, {2039,1626,349 ,1655,564,566 }, - {2730,2729,2701 ,2150,3374,3375 }, {2770,2816,2847 ,2681,3475,2624 }, - {3059,3099,3058 ,2892,3603,2710 }, {3099,3123,3058 ,3603,3023,2710 }, - {3019,3020,3058 ,2709,2890,2710 }, {2135,2478,631 ,1425,3143,1426 }, - {2770,2771,2816 ,2681,3197,3475 }, {2424,2905,2367 ,2778,399,2764 }, - {2267,1855,2649 ,3574,3088,2791 }, {3648,3647,3594 ,734,2521,2522 }, - {3574,3595,3544 ,928,927,1153 }, {3107,3105,3106 ,3326,3297,3380 }, - {3107,3140,3105 ,3326,3327,3297 }, {2933,2932,2863 ,3556,3243,3242 }, - {1590,3154,3123 ,3028,2692,3023 }, {3124,1590,3123 ,3604,3028,3023 }, - {2084,460,860 ,1361,1999,1385 }, {2674,2726,2700 ,2975,2667,3009 }, - {2727,2755,2726 ,2760,3400,2667 }, {3169,3211,3168 ,2810,3605,3396 }, - {2677,213,123 ,3033,3606,3607 }, {3211,3210,3168 ,3605,3608,3396 }, - {2796,3080,2472 ,2462,908,2009 }, {2255,1211,939 ,3229,2007,3354 }, - {3866,35,783 ,1479,1338,679 }, {2846,2878,2877 ,2623,2622,3026 }, - {2435,388,292 ,3401,2202,2563 }, {2358,2435,292 ,2562,3401,2563 }, - {2833,2763,3054 ,2065,2896,916 }, {2833,3120,2763 ,2065,3599,2896 }, - {2550,2500,3120 ,3609,3600,3599 }, {2833,2550,3120 ,2065,3609,3599 }, - {2550,2768,2500 ,3609,1297,3600 }, {2167,2089,2947 ,1323,1325,2988 }, - {2167,2947,2166 ,1323,2988,1040 }, {388,2435,2508 ,2202,3401,2630 }, - {3069,3068,3030 ,2827,3555,3490 }, {2591,2590,2508 ,3303,2648,2630 }, - {7514,7513,7484 ,3610,3611,3612 }, {2280,2325,2243 ,3613,1994,1993 }, - {2662,2796,2472 ,805,2462,2009 }, {2997,3023,3022 ,3158,3099,3393 }, - {2591,2638,2590 ,3303,2935,2648 }, {2666,2665,2590 ,2765,2677,2648 }, - {2563,2194,2193 ,2707,1853,1007 }, {2983,2982,2935 ,3589,3601,3310 }, - {1098,1096,28 ,2725,2727,1059 }, {1908,1098,28 ,1058,2725,1059 }, - {1197,1987,2808 ,478,3614,522 }, {401,2445,2369 ,1387,1388,3257 }, - {3322,3321,7387 ,1182,2461,3615 }, {2792,2424,2367 ,2718,2778,2764 }, - {3593,3592,3543 ,3557,2084,3075 }, {3090,3955,3994 ,2805,3538,2806 }, - {600,131,1390 ,978,2965,2964 }, {3163,3205,3162 ,3361,3360,3318 }, - {788,600,1390 ,976,978,2964 }, {2626,2764,2587 ,2582,2847,3274 }, - {2332,2626,2587 ,3322,2582,3274 }, {2442,2441,2365 ,2742,3000,3351 }, - {2441,2442,2513 ,3000,2742,3426 }, {2638,2666,2590 ,2935,2765,2648 }, - {2617,2371,2387 ,2878,2880,3461 }, {2887,2933,2863 ,3616,3556,3242 }, - {2827,2826,2780 ,3445,3592,2830 }, {2791,2588,2429 ,3483,2072,3617 }, - {818,2800,2844 ,1018,2996,1329 }, {2593,2668,2667 ,3111,3138,2949 }, - {740,2730,2701 ,1595,2150,3375 }, {2755,2778,2777 ,3400,3215,2824 }, - {2727,2726,2674 ,2760,2667,2975 }, {2885,2884,2820 ,2914,2940,2869 }, - {2332,2587,3212 ,3322,3274,3027 }, {3123,1480,1407 ,3023,2695,1269 }, - {2364,2363,177 ,2513,2571,3618 }, {3357,3382,3356 ,883,870,872 }, - {2768,2460,2541 ,1297,3619,1298 }, {3654,3653,3604 ,1438,1440,3384 }, - {492,444,171 ,1580,922,921 }, {2557,3566,3456 ,3620,1146,1145 }, - {5837,4771,5804 ,3621,3622,3623 }, {2665,2666,2718 ,2677,2765,2675 }, - {2878,2922,2921 ,2622,2045,2691 }, {2550,2460,2768 ,3609,3619,1297 }, - {108,36,874 ,1336,1478,636 }, {2857,2761,2765 ,2575,2768,2590 }, - {7923,7973,7948 ,816,3624,3625 }, {2281,2327,2326 ,3626,3627,3588 }, - {2327,286,2393 ,3627,2446,2445 }, {2326,2327,2393 ,3588,3627,2445 }, - {5038,4743,5702 ,3628,3629,3630 }, {2156,6783,6464 ,3631,3632,3633 }, - {2528,924,3183 ,2763,3634,2832 }, {3195,3114,2649 ,3430,2792,2791 }, - {3494,3546,3492 ,736,2557,930 }, {2143,2099,2166 ,953,1093,1040 }, - {2611,2610,2524 ,3436,3479,3395 }, {3845,6215,1949 ,3635,3636,3637 }, - {3130,4032,3129 ,3414,3638,3412 }, {2982,3033,2981 ,3601,3579,3550 }, - {3071,3125,3111 ,3405,3404,2829 }, {489,3761,2955 ,3639,218,3640 }, - {3163,3162,3108 ,3361,3318,3415 }, {3109,3163,3108 ,3553,3361,3415 }, - {3205,3204,3162 ,3360,3314,3318 }, {3235,3234,3204 ,3394,3315,3314 }, - {5337,4866,4830 ,572,3641,3642 }, {2673,2724,2671 ,2974,2668,2705 }, - {3201,3231,3230 ,3328,3293,3294 }, {7704,7753,7727 ,3562,3643,3563 }, - {2815,2770,2847 ,2696,2681,2624 }, {2781,2827,2780 ,3373,3445,2830 }, - {1984,2530,2038 ,831,2389,1551 }, {1004,2652,1056 ,3452,3424,3388 }, - {7910,2097,2164 ,3582,846,3583 }, {2974,2997,3022 ,3116,3158,3393 }, - {2494,2629,2656 ,3391,2673,2893 }, {2973,2974,3022 ,3108,3116,3393 }, - {2869,2868,2832 ,1576,1577,638 }, {2194,2563,2146 ,1853,2707,1732 }, - {8135,4835,3148 ,2439,2130,814 }, {2952,2353,2489 ,2610,3170,2611 }, - {3216,1656,1657 ,3478,1166,1781 }, {2869,2832,2907 ,1576,638,111 }, - {5653,350,6115 ,3644,1114,3645 }, {2877,2878,2921 ,3026,2622,2691 }, - {2793,2426,2853 ,3048,3113,806 }, {1845,1337,1874 ,912,2148,962 }, - {1519,2990,79 ,2450,3646,1510 }, {6554,6619,4190 ,3647,1272,3439 }, - {777,2137,776 ,421,147,1120 }, {4445,2871,712 ,3648,3649,3650 }, - {2572,3138,1181 ,3050,2984,1981 }, {2868,2907,2940 ,1577,111,110 }, - {3331,3330,3291 ,1183,1185,1203 }, {2497,2201,2865 ,3336,2455,3107 }, - {3032,3031,3000 ,2858,2857,3304 }, {2889,2888,2826 ,2604,3651,3592 }, - {2626,2625,2839 ,2582,2584,2848 }, {2848,2889,2826 ,2605,2604,3592 }, - {3071,3111,3070 ,3405,2829,2828 }, {3071,3070,3032 ,3405,2828,2858 }, - {3033,3071,3032 ,3579,3405,2858 }, {2351,2201,2497 ,3258,2455,3336 }, - {2516,2515,2443 ,3554,2600,2942 }, {2444,2516,2443 ,2941,3554,2942 }, - {2442,2514,2513 ,2742,2741,3426 }, {3545,3574,3544 ,926,928,1153 }, - {3931,3944,3684 ,1868,729,1915 }, {3541,3572,3571 ,981,1931,3652 }, - {3195,219,3114 ,3430,3087,2792 }, {2711,2013,877 ,3435,2242,2243 }, - {689,3828,4206 ,1363,1364,682 }, {5488,6257,6039 ,2730,3428,2728 }, - {3114,2471,2536 ,2792,3653,2793 }, {1238,165,155 ,1569,1869,1497 }, - {2973,3022,3021 ,3108,3393,2891 }, {2930,2929,2884 ,3004,3003,2940 }, - {2387,2531,2647 ,3461,3654,3276 }, {3194,1487,2338 ,1065,531,458 }, - {3022,3060,3059 ,3393,3214,2892 }, {3021,3022,3059 ,2891,3393,2892 }, - {3075,2803,2833 ,2670,3655,2065 }, {2608,2550,2833 ,3656,3609,2065 }, - {2850,2608,2833 ,3657,3656,2065 }, {2608,2470,2460 ,3656,3658,3619 }, - {2550,2608,2460 ,3609,3656,3619 }, {2470,2541,2460 ,3658,1298,3619 }, - {3422,296,3664 ,2055,1558,1557 }, {3060,3100,3099 ,3214,2955,3603 }, - {3194,3189,2150 ,1065,1027,534 }, {3400,2410,3402 ,3659,1,3660 }, - {1218,1742,3008 ,3255,707,546 }, {3170,4032,1160 ,3510,3638,3661 }, - {2099,2100,2166 ,1093,1218,1040 }, {3059,3060,3099 ,2892,3214,3603 }, - {3100,3124,3099 ,2955,3604,3603 }, {2972,3020,3019 ,2047,2890,2709 }, - {3183,924,219 ,2832,3634,3087 }, {2889,2935,2888 ,2604,3310,3651 }, - {2935,2934,2888 ,3310,3602,3651 }, {740,2676,643 ,1595,2761,3285 }, - {2006,2032,2031 ,1181,1127,1560 }, {740,2701,2676 ,1595,3375,2761 }, - {2852,2617,2387 ,3275,2878,3461 }, {2360,2437,2359 ,3161,2576,3406 }, - {2294,2295,2359 ,3081,3160,3406 }, {2971,2972,3019 ,3338,2047,2709 }, - {1224,3047,3037 ,1508,3571,3501 }, {2922,2972,2971 ,2045,2047,3338 }, - {2793,2853,2630 ,3048,806,3139 }, {2592,2593,2667 ,2936,3111,2949 }, - {628,2717,728 ,1177,2676,1266 }, {2921,2922,2971 ,2691,2045,3338 }, - {3100,1590,3124 ,2955,3028,3604 }, {3211,3241,3240 ,3605,3504,3590 }, - {3210,3211,3240 ,3608,3605,3590 }, {2983,3034,2982 ,3589,3595,3601 }, - {3463,3432,3433 ,1271,929,3049 }, {2651,2803,3075 ,1516,3655,2670 }, - {2651,2850,2803 ,1516,3657,3655 }, {2651,2608,2850 ,1516,3656,3657 }, - {2418,2541,2470 ,3662,1298,3658 }, {451,242,2541 ,3387,1717,1298 }, - {2418,451,2541 ,3662,3387,1298 }, {2931,2978,2977 ,2780,2782,2997 }, - {2841,2649,3213 ,3443,2791,3486 }, {596,549,647 ,1462,1129,973 }, - {3034,3033,2982 ,3595,3579,3601 }, {875,6836,6353 ,579,852,749 }, - {2282,2328,2327 ,3663,3664,3627 }, {2328,287,286 ,3664,2470,2446 }, - {2327,2328,286 ,3627,3664,2446 }, {4246,1729,3837 ,3665,3666,3667 }, - {4377,2431,967 ,3090,2977,3668 }, {3020,3059,3058 ,2890,2892,2710 }, - {650,2267,2841 ,3074,3574,3443 }, {219,2471,3114 ,3087,3653,2792 }, - {2823,2824,2863 ,2825,3432,3242 }, {3065,3085,3105 ,3298,3381,3297 }, - {3085,3106,3105 ,3381,3380,3297 }, {2596,2640,2595 ,2771,2770,2983 }, - {2723,2775,2774 ,2706,3477,2916 }, {2722,2723,2774 ,3014,2706,2916 }, - {2509,2510,2592 ,3329,3162,2936 }, {2295,2360,2359 ,3160,3161,3406 }, - {3229,1726,3199 ,3266,1746,3244 }, {2300,1304,2551 ,2640,2639,3247 }, - {1336,2085,2604 ,2924,657,3046 }, {3566,2397,3768 ,1146,3669,1518 }, - {2267,2649,2841 ,3574,2791,3443 }, {1920,2155,2157 ,581,580,3411 }, - {3459,3460,3487 ,3241,3240,3356 }, {1789,2791,2429 ,3007,3483,3617 }, - {2656,2629,2461 ,2893,2673,2899 }, {3110,3164,3109 ,3337,3307,3553 }, - {1224,3037,2986 ,1508,3501,3500 }, {6071,6082,6088 ,1346,1348,3670 }, - {155,1183,1238 ,1497,1238,1569 }, {4978,5592,2206 ,178,3671,176 }, - {3546,3545,3492 ,2557,926,930 }, {2381,2608,2651 ,3672,3656,1516 }, - {2381,2470,2608 ,3672,3658,3656 }, {2468,2418,2470 ,3673,3662,3658 }, - {1313,5085,2251 ,3674,3675,3676 }, {1606,4055,4054 ,3677,3678,3679 }, - {689,1586,3315 ,1363,943,944 }, {2741,3212,2495 ,3317,3027,2920 }, - {2329,288,287 ,3680,2723,2470 }, {2328,2329,287 ,3664,3680,2470 }, - {3345,8138,5352 ,3476,2018,3502 }, {2647,2531,2413 ,3276,3654,3246 }, - {2778,2824,2823 ,3215,3432,2825 }, {2777,2778,2823 ,2824,3215,2825 }, - {2824,2887,2863 ,3432,3616,3242 }, {2387,2371,2633 ,3461,2880,2650 }, - {3202,3201,3159 ,3324,3328,3296 }, {3202,3232,3201 ,3324,3325,3328 }, - {2640,2671,2595 ,2770,2705,2983 }, {2900,2300,2551 ,2649,2640,3247 }, - {2604,2085,2964 ,3046,657,3011 }, {2964,2085,2131 ,3011,657,656 }, - {2677,886,213 ,3033,3464,3606 }, {3268,2456,2302 ,3321,2790,3313 }, - {6436,5255,6480 ,3681,3682,1524 }, {3916,3904,3879 ,969,1264,2549 }, - {2524,2610,2564 ,3395,3479,3546 }, {282,327,281 ,3498,2226,2227 }, - {3421,3278,2851 ,1156,1158,3051 }, {2792,2367,2732 ,2718,2764,2583 }, - {5191,5170,5914 ,2900,3683,1566 }, {2547,2470,2381 ,3684,3658,3672 }, - {2397,2547,2381 ,3669,3684,3672 }, {2547,3053,2470 ,3684,3685,3658 }, - {2655,2468,2470 ,3686,3673,3658 }, {3053,2655,2470 ,3685,3686,3658 }, - {2655,2418,2468 ,3686,3662,3673 }, {2467,2493,2840 ,3006,3005,2594 }, - {2524,2564,2523 ,3395,3546,3545 }, {2946,3194,2338 ,1062,1065,458 }, - {178,1847,1874 ,3043,1519,962 }, {2924,2974,2973 ,2593,3116,3108 }, - {2328,2283,2329 ,3664,3687,3680 }, {2909,1849,1785 ,3189,1660,1659 }, - {3148,1378,8135 ,814,925,2439 }, {6098,6388,5191 ,1565,3184,2900 }, - {2524,2523,2484 ,3395,3545,3499 }, {6970,3340,4831 ,3688,114,3154 }, - {3126,3125,3072 ,3689,3404,3403 }, {2664,628,529 ,2721,1177,1148 }, - {2584,2310,2476 ,2894,2442,2441 }, {2552,2584,2476 ,2657,2894,2441 }, - {2633,2584,2552 ,2650,2894,2657 }, {3160,3202,3159 ,3323,3324,3296 }, - {3647,3700,3646 ,2521,2545,2123 }, {2928,2926,2927 ,2578,517,516 }, - {2976,2998,2975 ,2999,3446,3157 }, {2362,2439,2438 ,2979,2980,3163 }, - {2361,2362,2438 ,3177,2979,3163 }, {2456,2626,2332 ,2790,2582,3322 }, - {2302,2456,2332 ,3313,2790,3322 }, {3164,3163,3109 ,3307,3361,3553 }, - {2532,178,1874 ,3186,3043,962 }, {2670,2669,2595 ,2982,3015,2983 }, - {3873,3931,3475 ,1809,1868,1559 }, {6309,6735,6308 ,3690,3472,3474 }, - {6088,5255,6436 ,3670,3682,3681 }, {1265,4548,1271 ,1273,589,588 }, - {2195,165,341 ,1851,1869,1852 }, {2547,2469,2655 ,3684,3691,3686 }, - {3053,2547,2655 ,3685,3684,3686 }, {2469,2418,2655 ,3691,3662,3686 }, - {2469,451,2418 ,3691,3387,3662 }, {2939,134,451 ,1870,1783,3387 }, - {3167,3209,3166 ,3692,3377,3454 }, {8078,8084,8124 ,1744,1139,1141 }, - {3216,3228,1656 ,3478,3042,1166 }, {2330,289,288 ,3693,1044,2723 }, - {2329,2330,288 ,3680,3693,2723 }, {925,2730,827 ,1287,2150,1220 }, - {1010,925,926 ,1286,1287,1284 }, {1146,1237,1236 ,2467,1467,1434 }, - {2844,2846,1047 ,1329,2623,1328 }, {3022,3023,3061 ,3393,3099,2953 }, - {2937,2936,2890 ,3570,2843,2603 }, {3105,3140,3159 ,3297,3327,3296 }, - {3140,3160,3159 ,3327,3323,3296 }, {3232,3231,3201 ,3325,3293,3328 }, - {3021,3020,2972 ,2891,2890,2047 }, {2976,2975,2926 ,2999,3157,517 }, - {2928,2976,2926 ,2578,2999,517 }, {2595,2639,2594 ,2983,3694,3110 }, - {2595,2594,2511 ,2983,3110,3112 }, {2356,3267,3229 ,3264,3265,3266 }, - {2909,3223,2911 ,3189,2922,2923 }, {2891,2890,1010 ,3560,2603,1286 }, - {3238,3239,2424 ,3379,3378,2778 }, {1103,2890,2891 ,1474,2603,3560 }, - {3167,3166,3125 ,3692,3454,3404 }, {3306,7276,3326 ,3695,3696,826 }, - {2497,2344,2351 ,3336,2852,3258 }, {1482,2915,2711 ,2265,3434,3435 }, - {3434,3433,3382 ,737,3049,870 }, {5219,5218,5198 ,1860,483,423 }, - {8101,8087,8099 ,383,385,2867 }, {3208,3209,3238 ,3455,3377,3379 }, - {2397,2651,3768 ,3669,1516,1518 }, {2688,451,2469 ,3697,3387,3691 }, - {2658,2939,451 ,3698,1870,3387 }, {2688,2658,451 ,3697,3698,3387 }, - {3645,3698,3644 ,1650,2122,1651 }, {6639,1213,4983 ,3699,3700,480 }, - {2985,2984,2937 ,3457,3458,3570 }, {3546,3597,3545 ,2557,1810,926 }, - {6257,5166,6039 ,3428,3427,2728 }, {2283,2285,2329 ,3687,3701,3680 }, - {2129,2130,2069 ,1469,1505,1711 }, {1855,3195,2649 ,3088,3430,2791 }, - {5327,6525,6524 ,3702,3703,3704 }, {2702,2254,2427 ,2854,3705,3237 }, - {2475,2715,3013 ,3047,2944,2946 }, {3095,2715,2835 ,2945,2944,3079 }, - {2512,2596,2595 ,2981,2771,2983 }, {2563,2125,2146 ,2707,892,1732 }, - {2691,2570,2544 ,3169,2612,2451 }, {2639,2669,2593 ,3694,3015,3111 }, - {2595,2669,2639 ,2983,3015,3694 }, {3026,3025,2998 ,3095,3094,3446 }, - {2899,3223,2909 ,2921,2922,3189 }, {3700,3699,3646 ,2545,2121,2123 }, - {3073,3127,3072 ,3516,2811,3403 }, {2429,2588,2556 ,3617,2072,2779 }, - {1789,2429,2556 ,3007,3617,2779 }, {2864,2891,1010 ,3386,3560,1286 }, - {3126,3167,3125 ,3689,3692,3404 }, {1482,2711,877 ,2265,3435,2243 }, - {2518,2517,2445 ,2388,3399,1388 }, {2984,2983,2936 ,3458,3589,2843 }, - {1740,1687,3196 ,3213,1104,2581 }, {3197,1740,3196 ,2599,3213,2581 }, - {2493,1789,2556 ,3005,3007,2779 }, {554,2987,2268 ,2588,3346,3292 }, - {2876,2706,2469 ,3706,3707,3691 }, {2547,2876,2469 ,3684,3706,3691 }, - {2706,2688,2469 ,3707,3697,3691 }, {2658,355,2939 ,3698,1876,1870 }, - {3035,3034,2983 ,3364,3595,3589 }, {2388,2493,2556 ,2608,3005,2779 }, - {685,3371,1309 ,2674,3708,3709 }, {3187,4236,4371 ,3402,3488,3489 }, - {157,2133,91 ,1877,2614,1984 }, {5455,1263,6509 ,686,1199,1452 }, - {4452,4795,4820 ,2398,3710,2399 }, {3073,3072,3034 ,3516,3403,3595 }, - {3035,3073,3034 ,3364,3516,3595 }, {6173,6088,6388 ,3711,3670,3184 }, - {110,74,2962 ,743,2053,3494 }, {2715,2386,2835 ,2944,3196,3079 }, - {369,418,470 ,1521,1610,1522 }, {2549,2622,2894 ,2841,2840,3140 }, - {2639,2593,2594 ,3694,3111,3110 }, {2773,2772,2720 ,2871,3198,3173 }, - {2721,2773,2720 ,2917,2871,3173 }, {2676,2675,2602 ,2761,3235,3234 }, - {3168,3167,3126 ,3396,3692,3689 }, {1452,1004,1592 ,1606,3452,2939 }, - {1936,5140,5359 ,848,3712,3713 }, {5326,5327,6524 ,3714,3702,3704 }, - {2201,968,2200 ,2455,1661,1570 }, {2401,651,2995 ,2855,3591,2856 }, - {2531,2387,2413 ,3654,3461,3246 }, {2937,2984,2936 ,3570,3458,2843 }, - {3168,3210,3167 ,3396,3608,3692 }, {2194,2146,2147 ,1853,1732,1562 }, - {2837,107,2501 ,2511,2548,2547 }, {2652,1004,31 ,3424,3452,3450 }, - {3054,3981,3076 ,916,915,2066 }, {2557,2397,3052 ,3620,3669,3715 }, - {2876,2547,2397 ,3706,3684,3669 }, {2557,2876,2397 ,3620,3706,3669 }, - {2984,3035,2983 ,3458,3364,3589 }, {2250,2286,2330 ,3716,3717,3693 }, - {2285,2250,2330 ,3701,3716,3693 }, {2286,290,289 ,3717,1042,1044 }, - {2330,2286,289 ,3693,3717,1044 }, {8067,8083,8097 ,2168,2103,1710 }, - {2000,2025,2024 ,2428,2430,2370 }, {3127,3126,3072 ,2811,3689,3403 }, - {3179,1249,2617 ,2947,2876,2878 }, {3472,3446,1165 ,3155,3718,3153 }, - {2647,2413,2462 ,3276,3246,3245 }, {3230,2356,3229 ,3294,3264,3266 }, - {2370,2630,1482 ,3216,3139,2265 }, {2637,1238,1373 ,2720,1569,1501 }, - {2131,2129,2653 ,656,1469,3010 }, {411,2899,2909 ,2919,2921,3189 }, - {2356,411,2909 ,3264,2919,3189 }, {3534,8072,3668 ,942,511,513 }, - {3127,3168,3126 ,2811,3396,3689 }, {2513,2597,2512 ,3426,2769,2981 }, - {6170,3537,3451 ,3719,1735,1720 }, {3210,3209,3167 ,3608,3377,3692 }, - {2587,90,3256 ,3274,2851,3308 }, {2717,2769,819 ,2676,2679,2244 }, - {2805,651,2401 ,3230,3591,2855 }, {2528,56,924 ,2763,2606,3634 }, - {2416,1789,2467 ,808,3007,3006 }, {2711,2631,2586 ,3435,2971,2972 }, - {2748,2876,2557 ,3720,3706,3620 }, {2580,2688,2706 ,3721,3697,3707 }, - {2688,2580,2658 ,3697,3721,3698 }, {2459,355,2658 ,3722,1876,3698 }, - {355,2459,250 ,1876,3722,1916 }, {3210,3240,3239 ,3608,3590,3378 }, - {3209,3210,3239 ,3377,3608,3378 }, {437,2589,529 ,1149,3204,1148 }, - {2151,109,349 ,1603,1654,566 }, {790,2682,829 ,1339,3480,1368 }, - {2003,2029,2028 ,2542,1748,1811 }, {2518,2560,2517 ,2388,3335,3399 }, - {2269,2505,2497 ,2881,2883,3336 }, {2987,2269,2497 ,3346,2881,3336 }, - {2819,2818,2772 ,2870,3156,3198 }, {3095,2835,3173 ,2945,3079,3114 }, - {162,2290,238 ,2822,1982,868 }, {2724,2726,2776 ,2668,2667,3192 }, - {2723,2776,2775 ,2706,3192,3477 }, {4086,284,557 ,3723,139,3724 }, - {1255,795,2576 ,3248,302,3076 }, {3025,3024,2975 ,3094,3098,3157 }, - {2884,2928,2883 ,2940,2578,2579 }, {2537,2427,2677 ,2985,3237,3033 }, - {2598,2599,2673 ,2602,2601,2974 }, {2269,2841,2506 ,2881,3443,2882 }, - {2552,2476,2300 ,2657,2441,2640 }, {8065,8094,8080 ,96,98,1094 }, - {1238,1515,1373 ,1569,1502,1501 }, {2489,2475,2298 ,2611,3047,3013 }, - {3429,3428,3378 ,1978,3261,1442 }, {3143,2537,968 ,3135,2985,1661 }, - {3699,3745,3744 ,2121,2546,2390 }, {2554,2805,2401 ,2853,3230,2855 }, - {1969,4644,1970 ,2369,2425,2427 }, {1482,877,58 ,2265,2243,2156 }, - {2748,2579,2706 ,3720,3725,3707 }, {2876,2748,2706 ,3706,3720,3707 }, - {2579,2580,2706 ,3725,3721,3707 }, {2580,2578,2658 ,3721,3552,3698 }, - {2578,2459,2658 ,3552,3722,3698 }, {2614,2613,2526 ,2638,2665,2636 }, - {3124,3123,3099 ,3604,3023,3603 }, {3597,3596,3545 ,1810,1792,926 }, - {2544,79,2691 ,2451,1510,3169 }, {6155,6413,6970 ,3726,112,3688 }, - {4302,4303,166 ,3727,3728,3729 }, {2685,2737,2684 ,2642,2626,2644 }, - {2692,56,2528 ,2845,2606,2763 }, {2411,2692,2528 ,2777,2845,2763 }, - {2602,2601,2517 ,3234,3398,3399 }, {2569,554,1336 ,3291,2588,2924 }, - {2647,2462,2353 ,3276,3245,3170 }, {2773,2819,2772 ,2871,2870,3198 }, - {2438,2510,2437 ,3163,3162,2576 }, {2771,2817,2816 ,3197,3174,3475 }, - {2816,2817,2880 ,3475,3174,2591 }, {3359,6393,3305 ,824,1556,879 }, - {4146,4170,3844 ,3730,3731,2443 }, {2819,2820,2883 ,2870,2869,2579 }, - {2202,2841,2269 ,2875,3443,2881 }, {2888,2887,2824 ,3651,3616,3432 }, - {2826,2888,2824 ,3592,3651,3432 }, {2676,2727,2675 ,2761,2760,3235 }, - {3379,3429,3378 ,1979,1978,1442 }, {554,785,1336 ,2588,2952,2924 }, - {2569,1336,2366 ,3291,2924,2589 }, {3087,3088,3131 ,3573,3290,3732 }, - {4068,4091,4077 ,408,123,126 }, {2826,2825,2779 ,3592,3431,2831 }, - {2780,2826,2779 ,2830,3592,2831 }, {1849,2909,1875 ,1660,3189,1663 }, - {2685,2684,2614 ,2642,2644,2638 }, {1205,1106,1107 ,3187,1664,2656 }, - {2949,2748,2557 ,3239,3720,3620 }, {2949,2965,2579 ,3239,3417,3725 }, - {2748,2949,2579 ,3720,3239,3725 }, {2765,2580,2579 ,2590,3721,3725 }, - {2389,2459,2578 ,2609,3722,3552 }, {2389,250,2459 ,2609,1916,3722 }, - {3164,3206,3163 ,3307,2886,3361 }, {725,818,817 ,721,1018,1020 }, - {4172,4173,4215 ,135,134,370 }, {6525,6573,6572 ,3703,3733,3734 }, - {291,290,2286 ,1012,1042,3717 }, {2287,291,2286 ,3735,1012,3717 }, - {6524,6525,6572 ,3704,3703,3734 }, {2371,2542,2656 ,2880,2879,2893 }, - {554,2085,785 ,2588,657,2952 }, {2572,1181,293 ,3050,1981,1430 }, - {2678,1917,1875 ,3190,833,1663 }, {2909,2678,1875 ,3189,3190,1663 }, - {1665,410,2967 ,3077,2204,2701 }, {2847,2816,2879 ,2624,3475,2621 }, - {149,2471,219 ,3736,3653,3087 }, {3175,2576,2628 ,3249,3076,3078 }, - {2427,2537,3149 ,3237,2985,3134 }, {3074,3073,3035 ,3363,3516,3364 }, - {3154,3153,3123 ,2692,2693,3023 }, {2742,2003,1565 ,2558,2542,3737 }, - {511,1565,2003 ,3569,3737,2542 }, {3494,3492,3493 ,736,930,3738 }, - {13,1303,1626 ,910,1933,564 }, {3792,2941,3791 ,3421,109,3739 }, - {2504,2310,2502 ,2898,2442,3740 }, {2331,2502,2310 ,2895,3740,2442 }, - {2584,2331,2310 ,2894,2895,2442 }, {90,2202,2269 ,2851,2875,2881 }, - {2826,2824,2825 ,3592,3432,3431 }, {3110,3109,3068 ,3337,3553,3555 }, - {2965,2765,2579 ,3417,2590,3725 }, {2761,2580,2765 ,2768,3721,2590 }, - {2761,2578,2580 ,2768,3552,3721 }, {2133,250,2389 ,2614,1916,2609 }, - {253,247,2663 ,2564,2569,3593 }, {3494,3493,3432 ,736,3738,929 }, - {2921,2971,1237 ,2691,3338,1467 }, {2886,2931,2885 ,2913,2780,2914 }, - {3434,3463,3433 ,737,1271,3049 }, {972,1073,2314 ,3277,3741,3742 }, - {2286,2250,2287 ,3717,3716,3735 }, {236,291,2287 ,2444,1012,3735 }, - {99,236,2287 ,3743,2444,3735 }, {1172,2993,1081 ,1758,3744,3745 }, - {819,2845,2800 ,2244,2995,2996 }, {2560,2602,2517 ,3335,3234,3399 }, - {3993,3994,4035 ,2807,2806,1601 }, {2999,3027,3026 ,2998,3350,3095 }, - {968,2572,293 ,1661,3050,1430 }, {2976,3026,2998 ,2999,3095,3446 }, - {2510,2511,2593 ,3162,3112,3111 }, {2166,2100,2167 ,1040,1218,1323 }, - {292,239,2292 ,2563,1103,1983 }, {2874,3175,2628 ,3195,3249,3078 }, - {3063,3103,3102 ,3093,3137,3097 }, {3062,3063,3102 ,3096,3093,3097 }, - {2722,2774,2721 ,3014,2916,2917 }, {2503,2353,2691 ,2948,3170,3169 }, - {3149,3260,2201 ,3134,3188,2455 }, {2981,2980,2933 ,3550,3305,3556 }, - {2981,3000,2980 ,3550,3304,3305 }, {6388,6088,6436 ,3184,3670,3681 }, - {2416,2467,2631 ,808,3006,2971 }, {3128,3127,3073 ,2809,2811,3516 }, - {4426,6837,6577 ,1056,1057,97 }, {2908,1249,3179 ,3052,2876,2947 }, - {3138,1050,1181 ,2984,2282,1981 }, {1406,1329,1370 ,1135,1134,1500 }, - {2700,2726,2725 ,3009,2667,2666 }, {2673,2700,2725 ,2974,3009,2666 }, - {2888,2934,2933 ,3651,3602,3556 }, {2976,2999,3026 ,2999,2998,3095 }, - {2887,2888,2933 ,3616,3651,3556 }, {6573,6613,6612 ,3733,3746,1389 }, - {2722,2721,2669 ,3014,2917,3015 }, {2949,2557,2920 ,3239,3620,1799 }, - {2914,2765,2965 ,2573,2590,3417 }, {2635,2578,2761 ,2607,3552,2768 }, - {2430,2133,2389 ,2613,2614,2609 }, {2131,2132,2865 ,656,655,3107 }, - {3036,3035,2984 ,3362,3364,3458 }, {6572,6573,6612 ,3734,3733,1389 }, - {2735,929,830 ,2887,1234,1996 }, {5702,3009,5038 ,3630,3747,3628 }, - {3482,3478,8069 ,2494,885,1095 }, {2556,2449,2430 ,2779,3259,2613 }, - {2449,91,2430 ,3259,1984,2613 }, {2934,2981,2933 ,3602,3550,3556 }, - {3235,3236,2456 ,3394,2717,2790 }, {2863,2886,2822 ,3242,2913,2826 }, - {7371,7423,7370 ,3748,3749,3750 }, {2249,6897,6531 ,3751,54,2280 }, - {3258,3484,1009 ,542,283,3752 }, {7797,7848,7823 ,3506,343,345 }, - {3763,2707,1305 ,3753,235,466 }, {4083,4157,5458 ,3754,3755,3756 }, - {1376,4610,4788 ,1937,3757,3758 }, {7826,7876,7853 ,1616,1725,1617 }, - {5352,8138,4022 ,3502,2018,3503 }, {6649,6648,6612 ,3759,1390,1389 }, - {1156,106,5138 ,3100,1073,1940 }, {2622,2567,2901 ,2840,2842,3468 }, - {3648,3701,3647 ,734,733,2521 }, {4248,3972,1159 ,3256,3487,3760 }, - {836,1068,1385 ,3551,3761,3762 }, {3819,1159,5414 ,3763,3760,3764 }, - {7412,7410,7411 ,3765,3766,3767 }, {3306,3326,3325 ,3695,826,825 }, - {1668,689,4206 ,3212,1363,682 }, {1668,4206,141 ,3212,682,681 }, - {7825,7850,7824 ,3768,3769,3770 }, {1009,2607,1250 ,3752,2452,3771 }, - {7246,7268,7291 ,3772,3773,3774 }, {7979,8003,8002 ,3775,3776,3777 }, - {3690,958,872 ,3778,3779,2962 }, {5414,1159,3673 ,3764,3760,648 }, - {2731,3690,872 ,659,3778,2962 }, {3690,2394,958 ,3778,3780,3779 }, - {1594,3312,958 ,3781,3782,3779 }, {2394,1594,958 ,3780,3781,3779 }, - {5731,3615,5682 ,934,201,3783 }, {1594,3098,3420 ,3781,3784,3785 }, - {3312,1594,3420 ,3782,3781,3785 }, {3098,3254,3299 ,3784,3233,3232 }, - {3420,3098,3299 ,3785,3784,3232 }, {6355,6356,5151 ,3786,3787,1803 }, - {6311,8114,8122 ,1097,2286,384 }, {3169,3128,3170 ,2810,2809,3510 }, - {1510,3893,5862 ,3788,3789,3790 }, {8075,8105,8107 ,2308,3791,658 }, - {3454,3340,4424 ,710,114,3792 }, {4424,3340,6414 ,3792,114,113 }, - {5327,5300,6525 ,3702,3793,3703 }, {1942,1668,141 ,2337,3212,681 }, - {6415,4424,6414 ,386,3792,113 }, {8089,8097,3861 ,254,1710,1147 }, - {5259,4827,5282 ,3794,2734,1194 }, {5302,5706,6008 ,3795,3796,3797 }, - {2892,1669,771 ,918,3798,3799 }, {2582,3513,1017 ,873,441,1793 }, - {5299,6041,5251 ,3800,3801,3802 }, {7796,7823,7795 ,3803,345,3804 }, - {3371,685,794 ,3708,2674,3392 }, {3306,3325,3305 ,3695,825,879 }, - {3932,652,55 ,1096,334,3507 }, {3998,4040,4039 ,381,460,382 }, - {637,3585,3736 ,3805,3806,3807 }, {3474,637,3736 ,3808,3805,3807 }, - {1068,3685,3057 ,3761,3809,3810 }, {3685,3474,3736 ,3809,3808,3807 }, - {1385,1068,3057 ,3762,3761,3810 }, {3685,3736,3057 ,3809,3807,3810 }, - {541,1385,3057 ,3811,3762,3810 }, {3147,3477,4162 ,2285,1736,1738 }, - {8096,8136,8115 ,1796,2449,2448 }, {3477,4129,5785 ,1736,3812,1737 }, - {5680,5698,5679 ,50,44,2553 }, {836,1385,541 ,3551,3762,3811 }, - {2746,8089,3861 ,1517,254,1147 }, {3113,1734,269 ,410,2219,3813 }, - {3413,3441,3391 ,3814,2987,3815 }, {3701,3700,3647 ,733,2545,2521 }, - {296,3317,630 ,1558,1882,2491 }, {3493,3492,3432 ,3738,930,929 }, - {8124,8110,3271 ,1141,1140,1727 }, {3939,3955,3090 ,3539,3538,2805 }, - {4027,4712,1020 ,3470,3816,3817 }, {5231,922,5000 ,115,117,286 }, - {6041,5298,5273 ,3801,3818,3819 }, {3891,3890,3869 ,3820,1913,1912 }, - {7823,7847,7795 ,345,3521,3804 }, {2856,588,3549 ,3821,3822,3823 }, - {3690,1594,2394 ,3778,3781,3780 }, {5698,5726,5725 ,44,515,3824 }, - {4178,5297,4023 ,3825,3826,3827 }, {1694,836,541 ,3828,3551,3811 }, - {3453,3054,1787 ,917,916,2897 }, {531,34,62 ,3829,3830,3831 }, - {3441,3468,3440 ,2987,2688,2690 }, {3187,4371,3972 ,3402,3489,3487 }, - {3491,3544,3490 ,955,1153,2502 }, {7074,7073,7038 ,2747,662,3832 }, - {6613,6649,6612 ,3746,3759,1389 }, {6650,6680,6648 ,3833,3834,1390 }, - {3855,3891,3869 ,1511,3820,1912 }, {3504,3503,3468 ,2986,3835,2688 }, - {3441,3440,3391 ,2987,2690,3815 }, {3549,3369,62 ,3823,3836,3831 }, - {3529,3371,794 ,3837,3708,3392 }, {3369,531,62 ,3836,3829,3831 }, - {3529,794,3476 ,3837,3392,3838 }, {3568,3529,3476 ,3839,3837,3838 }, - {1783,543,1341 ,2251,3840,3841 }, {3891,3926,3890 ,3820,3125,1913 }, - {3612,3819,3473 ,3842,3763,649 }, {5785,4129,4162 ,1737,3812,1738 }, - {5242,5241,5220 ,3843,2120,2119 }, {7253,7252,6349 ,1855,3437,705 }, - {3764,3718,3316 ,3844,3845,2149 }, {3948,915,4326 ,280,443,219 }, - {3264,836,1694 ,2468,3551,3828 }, {882,3264,1694 ,3846,2468,3828 }, - {5258,5259,5282 ,1193,3794,1194 }, {3924,3968,3923 ,3847,3848,3849 }, - {5224,5244,5223 ,3850,79,3851 }, {5224,5223,4679 ,3850,3851,3852 }, - {7982,8007,7958 ,3853,3854,3855 }, {3339,3690,2731 ,3856,3778,659 }, - {3006,3339,2731 ,414,3856,659 }, {3279,1594,3690 ,3857,3781,3778 }, - {3339,3279,3690 ,3856,3857,3778 }, {1415,3098,1594 ,3858,3784,3781 }, - {3279,1415,1594 ,3857,3858,3781 }, {5286,5285,5241 ,3859,2169,2120 }, - {3392,3413,3391 ,3860,3814,3815 }, {5640,5679,5678 ,2214,2553,3861 }, - {1974,3549,62 ,3862,3823,3831 }, {4157,5481,5304 ,3755,3863,654 }, - {5639,5640,5656 ,2215,2214,3864 }, {5656,5640,5678 ,3864,2214,3861 }, - {5380,5385,5304 ,653,3865,654 }, {6649,6650,6648 ,3759,3833,1390 }, - {3511,3254,3098 ,539,3233,3784 }, {1415,3511,3098 ,3858,539,3784 }, - {3511,4019,3254 ,539,227,3233 }, {5013,8074,5950 ,684,2460,2873 }, - {6698,6697,6680 ,629,3866,3834 }, {3396,3764,3316 ,3867,3844,2149 }, - {2256,6390,2162 ,2323,3868,3278 }, {3828,4199,4186 ,1364,3869,683 }, - {4206,3828,4186 ,682,1364,683 }, {3864,138,1206 ,3517,434,2218 }, - {3972,3864,1206 ,3487,3517,2218 }, {6650,6698,6680 ,3833,629,3834 }, - {4009,4008,3967 ,3870,3871,3872 }, {6073,6055,3659 ,3873,3874,3875 }, - {834,96,1539 ,3876,836,835 }, {4861,4896,4669 ,3877,3878,3879 }, - {1724,3425,1249 ,3880,1859,2876 }, {834,1588,2856 ,3876,3881,3821 }, - {5354,4866,3907 ,3882,3641,3883 }, {3266,543,1783 ,3884,3840,2251 }, - {7796,7795,7745 ,3803,3804,650 }, {2785,1907,3878 ,1246,1245,1248 }, - {6574,6614,6613 ,3885,3886,3746 }, {3652,3651,3602 ,3887,3888,3889 }, - {3603,3652,3602 ,3890,3887,3889 }, {3392,3391,3332 ,3860,3815,3891 }, - {3309,3333,3308 ,3892,3893,3894 }, {3333,3392,3332 ,3893,3860,3891 }, - {3309,3308,3294 ,3892,3894,3895 }, {3333,3332,3308 ,3893,3891,3894 }, - {1415,3279,3339 ,3858,3857,3856 }, {3476,794,3252 ,3838,3392,781 }, - {1207,5153,5146 ,1074,3896,1075 }, {4291,920,4926 ,58,57,3897 }, - {3681,3258,1250 ,691,542,3771 }, {2813,3911,3738 ,3898,3899,3900 }, - {3294,3308,7110 ,3895,3894,1990 }, {3810,3853,3809 ,3901,3902,3903 }, - {5816,5833,5832 ,545,544,3904 }, {3968,4009,3967 ,3848,3870,3872 }, - {2472,13,1626 ,2009,910,564 }, {2659,3286,601 ,3485,56,3905 }, - {4005,2422,4004 ,3906,3907,3908 }, {4007,2798,4005 ,3909,420,3906 }, - {789,3878,1156 ,3910,1248,3100 }, {1510,1410,3893 ,3788,3911,3789 }, - {28,1096,88 ,1059,2727,3581 }, {3439,3438,3389 ,2689,3912,3913 }, - {3604,3603,3554 ,3384,3890,3914 }, {3861,8083,3456 ,1147,2103,1145 }, - {109,254,2039 ,1654,2008,1655 }, {3286,79,2990 ,56,1510,3646 }, - {2832,3694,2907 ,638,3915,111 }, {1882,4543,4980 ,894,3002,241 }, - {4714,4778,4777 ,1031,3916,3917 }, {6310,6742,6735 ,3918,3422,3472 }, - {3243,1111,4845 ,3919,3920,3921 }, {3336,6285,7095 ,3922,3923,3924 }, - {3911,1940,3738 ,3899,3925,3900 }, {3764,1412,3841 ,3844,3926,3927 }, - {3718,3764,3841 ,3845,3844,3927 }, {1412,2813,3738 ,3926,3898,3900 }, - {3841,1412,3738 ,3927,3926,3900 }, {3911,3447,1940 ,3899,3928,3925 }, - {552,3900,2945 ,3929,884,1239 }, {978,1029,977 ,1620,3930,3931 }, - {3373,206,1804 ,693,3932,2902 }, {3425,1693,1249 ,1859,264,2876 }, - {2357,2912,2422 ,3933,3934,3907 }, {3981,8140,3263 ,915,2269,252 }, - {3713,3712,3654 ,3935,1439,1438 }, {3655,3713,3654 ,3936,3935,1438 }, - {1215,6950,5006 ,3937,1349,1351 }, {5701,5682,5683 ,3938,3783,3939 }, - {8112,6351,8079 ,3530,1568,55 }, {5732,5731,5682 ,3940,934,3783 }, - {5701,5732,5682 ,3938,3940,3783 }, {4186,4199,1607 ,683,3869,3941 }, - {5832,3727,5831 ,3904,3942,3943 }, {931,1256,2910 ,3944,3945,3946 }, - {3224,931,2910 ,3947,3944,3946 }, {3653,3678,3652 ,1440,3948,3887 }, - {5732,5770,5769 ,3940,3949,932 }, {5731,5732,5769 ,934,3940,932 }, - {5770,5768,5769 ,3949,933,932 }, {7649,7699,7648 ,3950,3951,3952 }, - {5092,3766,4585 ,3953,3954,3955 }, {3619,2790,2740 ,3956,3576,3456 }, - {3554,3553,3502 ,3914,3957,3958 }, {3503,3554,3502 ,3835,3914,3958 }, - {592,1018,3368 ,1863,3959,3960 }, {20,348,75 ,3961,643,3962 }, - {7452,7451,7424 ,1875,3963,1188 }, {4005,4004,3964 ,3906,3908,3964 }, - {2422,731,4004 ,3907,3965,3908 }, {2798,2357,2422 ,420,3933,3907 }, - {6279,6278,7092 ,3966,3967,3968 }, {6870,6898,6796 ,2616,1946,2617 }, - {5308,564,4868 ,3528,3529,3969 }, {3712,3713,3714 ,1439,3935,3970 }, - {8138,3534,4022 ,2018,942,3503 }, {3537,3725,2072 ,1735,3971,1812 }, - {1556,3283,3252 ,3365,262,781 }, {406,746,2958 ,2566,2661,1374 }, - {3329,3362,3328 ,3972,1200,3973 }, {3616,4186,2795 ,3974,683,3975 }, - {3218,5480,4162 ,3976,1739,1738 }, {3962,3961,3918 ,3977,2346,2335 }, - {1256,931,1518 ,3945,3944,3978 }, {3652,3677,3651 ,3887,3979,3888 }, - {3708,3707,3651 ,3980,3981,3888 }, {4764,4765,4797 ,3982,3983,3984 }, - {6603,6744,1116 ,3985,3986,1401 }, {5838,6015,6083 ,3987,3988,3989 }, - {4837,1375,955 ,3990,325,3991 }, {3899,3760,5146 ,1938,2250,1075 }, - {3503,3502,3467 ,3835,3958,3992 }, {3440,3439,3390 ,2690,2689,3509 }, - {3391,3440,3390 ,3815,2690,3509 }, {635,20,75 ,2183,3961,3962 }, - {8134,6897,8120 ,3263,54,906 }, {5266,5288,5243 ,81,3993,3994 }, - {5306,5305,5335 ,462,2283,2523 }, {7425,7454,7397 ,3995,3996,1186 }, - {4868,4888,5379 ,3969,3997,3998 }, {5805,4129,5385 ,3999,3812,3865 }, - {6467,2407,6484 ,4000,4001,685 }, {4868,5340,5287 ,3969,4002,4003 }, - {6847,6899,3891 ,4004,4005,3820 }, {4129,5458,5385 ,3812,3756,3865 }, - {4199,694,4487 ,3869,1987,4006 }, {1333,2812,8064 ,849,1914,3141 }, - {3601,3650,3624 ,4007,4008,4009 }, {3257,5855,1974 ,4010,4011,3862 }, - {1584,694,4199 ,4012,1987,3869 }, {3809,3808,3753 ,3903,4013,4014 }, - {3252,1941,3283 ,781,1034,262 }, {4713,5768,5770 ,4015,933,3949 }, - {3907,4914,917 ,3883,65,4016 }, {1561,1459,407 ,1865,4017,4018 }, - {7595,7649,7594 ,4019,3950,4020 }, {4252,4157,4083 ,4021,3755,3754 }, - {2945,3339,3006 ,1239,3856,414 }, {7874,7924,7873 ,344,815,817 }, - {5392,5717,5122 ,4022,4023,4024 }, {3755,3809,3753 ,4025,3903,4014 }, - {5217,4797,5258 ,4026,3984,1193 }, {1018,3681,1486 ,3959,691,4027 }, - {3750,3681,1018 ,690,691,3959 }, {3681,1250,1486 ,691,3771,4027 }, - {4221,2955,592 ,1862,3640,1863 }, {592,3750,1018 ,1863,690,3959 }, - {4516,1152,1692 ,4028,4029,4030 }, {588,592,3368 ,3822,1863,3960 }, - {3274,3819,3612 ,3172,3763,3842 }, {834,1670,1588 ,3876,2327,3881 }, - {2314,6467,2223 ,3742,4000,2336 }, {530,2044,959 ,512,4031,1358 }, - {3747,3701,6249 ,2925,733,4032 }, {2203,1415,3339 ,4033,3858,3856 }, - {2945,2203,3339 ,1239,4033,3856 }, {3534,8121,6353 ,942,3466,749 }, - {3603,3602,3553 ,3890,3889,3957 }, {3554,3603,3553 ,3914,3890,3957 }, - {3748,3218,5385 ,4034,3976,3865 }, {1444,382,3318 ,4035,3534,4036 }, - {6030,5475,5576 ,4037,4038,4039 }, {3663,3511,1415 ,4040,539,3858 }, - {2203,3663,1415 ,4033,4040,3858 }, {5858,5254,3436 ,4041,4042,2145 }, - {556,5295,4471 ,4043,2188,2187 }, {4394,4323,4273 ,4044,4045,120 }, - {4043,2798,4007 ,419,420,3909 }, {6750,6749,6697 ,631,4046,3866 }, - {530,8072,3040 ,512,511,619 }, {4199,4487,1607 ,3869,4006,3941 }, - {2246,7067,5755 ,591,3585,4047 }, {4323,1297,2042 ,4045,4048,121 }, - {3809,3833,3808 ,3903,4049,4013 }, {4273,4323,2042 ,120,4045,121 }, - {803,6204,6203 ,144,4050,1249 }, {7797,7796,7745 ,3506,3803,650 }, - {3663,4029,3511 ,4040,225,539 }, {8092,8130,8098 ,2267,905,907 }, - {733,184,5006 ,4051,4052,1351 }, {4105,5392,5122 ,4053,4022,4024 }, - {1152,3274,3612 ,4029,3172,3842 }, {3819,4468,3473 ,3763,647,649 }, - {3001,1159,3819 ,3210,3760,3763 }, {3819,5414,4468 ,3763,3764,647 }, - {5957,4105,5122 ,4054,4053,4024 }, {3886,3887,3885 ,4055,4056,4057 }, - {8105,3006,8137 ,3791,414,2174 }, {3612,3473,2843 ,3842,649,4058 }, - {3577,2843,4469 ,4059,4058,4060 }, {1102,1733,4165 ,2317,2328,640 }, - {1692,2843,3577 ,4030,4058,4059 }, {1692,3612,2843 ,4030,3842,4058 }, - {3368,1018,3577 ,3960,3959,4059 }, {1486,1692,3577 ,4027,4030,4059 }, - {3344,592,588 ,1861,1863,3822 }, {1211,3327,3636 ,2007,439,1060 }, - {3844,1283,2476 ,2443,2978,2441 }, {3668,530,959 ,513,512,1358 }, - {3807,3806,3752 ,4061,4062,4063 }, {5799,5800,5815 ,4064,702,4065 }, - {3828,1454,4199 ,1364,2318,3869 }, {22,1468,2042 ,4066,174,121 }, - {2013,2380,1332 ,2242,2597,3492 }, {3292,3331,3291 ,4067,1183,1203 }, - {2607,4516,1250 ,2452,4028,3771 }, {3922,3965,3921 ,4068,4069,4070 }, - {3755,3754,3707 ,4025,4071,3981 }, {3677,3708,3651 ,3979,3980,3888 }, - {3708,3755,3707 ,3980,4025,3981 }, {3755,3753,3754 ,4025,4014,4071 }, - {1297,22,2042 ,4048,4066,121 }, {22,1451,1468 ,4066,175,174 }, - {3289,3288,7196 ,2056,2058,2401 }, {5928,3903,3915 ,4072,296,1575 }, - {7102,7129,7101 ,1240,1026,4073 }, {22,3786,1451 ,4066,4074,175 }, - {5693,5715,5714 ,4075,4076,4077 }, {3484,1300,1009 ,283,285,3752 }, - {4009,4007,4008 ,3870,3909,3871 }, {1018,1486,3577 ,3959,4027,4059 }, - {6281,6280,7090 ,4078,4079,4080 }, {6698,6750,6697 ,629,631,3866 }, - {3981,2833,3076 ,915,2065,2066 }, {2155,6353,5444 ,580,749,3410 }, - {2681,3119,2346 ,3254,3223,3252 }, {4856,4570,3834 ,4081,4082,4083 }, - {3006,552,2945 ,414,3929,1239 }, {2265,177,2297 ,2662,3618,2663 }, - {1483,834,1539 ,4084,3876,835 }, {3887,3923,3922 ,4056,3849,4068 }, - {3885,3887,3922 ,4057,4056,4068 }, {6386,4856,3834 ,4085,4081,4083 }, - {303,915,3948 ,4086,443,280 }, {68,4813,1655 ,4087,4088,4089 }, - {8100,4021,3482 ,1713,1712,2494 }, {1335,1443,1152 ,236,182,4029 }, - {3753,3807,3752 ,4014,4061,4063 }, {3871,1334,3748 ,397,1935,4034 }, - {1250,4516,1486 ,3771,4028,4027 }, {3555,3554,3503 ,4090,3914,3835 }, - {3972,1206,5389 ,3487,2218,4091 }, {6096,3765,6050 ,4092,4093,4094 }, - {3258,1009,1250 ,542,3752,3771 }, {4527,5305,3147 ,1740,2283,2285 }, - {4915,917,4914 ,64,4016,65 }, {3965,3964,3921 ,4069,3964,4070 }, - {5228,5247,5227 ,4095,4096,4097 }, {1935,913,915 ,4098,444,443 }, - {4866,5337,5354 ,3641,572,3882 }, {3968,3967,3923 ,3848,3872,3849 }, - {303,1935,915 ,4086,4098,443 }, {3263,2651,3075 ,252,1516,2670 }, - {3747,3700,3701 ,2925,2545,733 }, {3278,2691,79 ,1158,3169,1510 }, - {177,2363,2297 ,3618,2571,2663 }, {106,5146,5139 ,1073,1075,1939 }, - {3457,3348,2043 ,4099,2255,4100 }, {3348,684,2043 ,2255,4101,4100 }, - {2910,1256,3004 ,3946,3945,2437 }, {1219,1170,3471 ,4102,4103,4104 }, - {2795,4186,1607 ,3975,683,3941 }, {202,5957,5122 ,4105,4054,4024 }, - {1935,3622,913 ,4098,4106,444 }, {3850,3851,3885 ,4107,4108,4057 }, - {3851,3886,3885 ,4108,4055,4057 }, {3851,3850,3808 ,4108,4107,4013 }, - {3833,3851,3808 ,4049,4108,4013 }, {3966,4007,3965 ,4109,3909,4069 }, - {4008,4007,3967 ,3871,3909,3872 }, {4007,4005,4006 ,3909,3906,4110 }, - {7108,7095,7109 ,4111,3924,4112 }, {2607,1335,4516 ,2452,236,4028 }, - {5806,248,3350 ,4113,4114,2541 }, {3330,3329,3290 ,1185,3972,1204 }, - {3291,3330,3290 ,1203,1185,1204 }, {3330,3363,3329 ,1185,1911,3972 }, - {3389,3388,3329 ,3913,4115,3972 }, {3363,3389,3329 ,1911,3913,3972 }, - {7899,7898,7875 ,4116,4117,4118 }, {3931,3664,3475 ,1868,1557,1559 }, - {8125,8068,8117 ,752,1784,4119 }, {4516,1692,1486 ,4028,4030,4027 }, - {3972,5389,4185 ,3487,4091,409 }, {3001,4248,1159 ,3210,3256,3760 }, - {3709,3708,3652 ,4120,3980,3887 }, {8038,8052,8022 ,4121,4122,4123 }, - {5871,3337,5116 ,4124,4125,4126 }, {3294,7110,3309 ,3895,1990,3892 }, - {2380,2624,3012 ,2597,3132,3491 }, {3732,3818,2785 ,3536,1244,1246 }, - {5592,5705,1608 ,3671,4127,4128 }, {2813,3447,3911 ,3898,3928,3899 }, - {684,11,2043 ,4101,299,4100 }, {3926,3925,3889 ,3125,3227,3126 }, - {2968,2693,1693 ,263,3416,264 }, {1295,3348,3040 ,618,2255,619 }, - {2039,2472,1626 ,1655,2009,564 }, {3727,3616,1794 ,3942,3974,4129 }, - {3808,3850,3806 ,4013,4107,4062 }, {3849,3850,3884 ,4130,4107,4131 }, - {3724,5644,5495 ,4132,4133,4134 }, {3348,3457,3040 ,2255,4099,619 }, - {3556,3554,3555 ,3385,3914,4090 }, {7995,8020,7973 ,4135,1405,3624 }, - {273,272,205 ,1749,1680,1741 }, {5198,4710,5177 ,423,4136,424 }, - {2912,1379,931 ,3934,4137,3944 }, {4007,4006,3965 ,3909,4110,4069 }, - {3966,3965,3922 ,4109,4069,4068 }, {3923,3966,3922 ,3849,4109,4068 }, - {2912,3178,731 ,3934,4138,3965 }, {2798,2422,4005 ,420,3907,3906 }, - {6786,6785,6749 ,4139,4140,4046 }, {6750,6786,6749 ,631,4139,4046 }, - {3412,3387,3388 ,4141,1787,4115 }, {3389,3412,3388 ,3913,4141,4115 }, - {3438,3437,3387 ,3912,4142,1787 }, {1670,834,1483 ,2327,3876,4084 }, - {2698,5335,4236 ,105,2523,3488 }, {3706,3752,3705 ,4143,4063,4144 }, - {3624,3650,3601 ,4009,4008,4007 }, {6611,5401,6571 ,1391,4145,4146 }, - {5800,5816,5815 ,702,545,4065 }, {1588,2856,5855 ,3881,3821,4011 }, - {1005,3338,4368 ,3535,4147,4148 }, {3883,3922,3921 ,4149,4068,4070 }, - {3849,3884,3883 ,4130,4131,4149 }, {918,558,2044 ,4150,4151,4031 }, - {530,918,2044 ,512,4150,4031 }, {2868,2832,2869 ,1577,638,1576 }, - {3447,3736,3585 ,3928,3807,3806 }, {915,3341,4326 ,443,541,219 }, - {3622,4289,2892 ,4106,4152,918 }, {7090,6280,7091 ,4080,4079,4153 }, - {3283,3476,3252 ,262,3838,781 }, {913,3622,2892 ,444,4106,918 }, - {3880,3881,3918 ,2320,4154,2335 }, {4289,3172,2892 ,4152,4155,918 }, - {3850,3885,3884 ,4107,4057,4131 }, {694,2477,1607 ,1987,642,3941 }, - {5438,5439,6705 ,4156,4157,4158 }, {3296,3313,177 ,4159,4160,3618 }, - {3313,3343,3342 ,4160,2515,2514 }, {177,3313,3342 ,3618,4160,2514 }, - {166,85,4250 ,3729,2504,4161 }, {3178,2912,931 ,4138,3934,3944 }, - {7847,7846,7795 ,3521,4162,3804 }, {5831,1794,5830 ,3943,4129,4163 }, - {3257,1588,5855 ,4010,3881,4011 }, {2856,3549,1974 ,3821,3823,3862 }, - {5458,4157,5385 ,3756,3755,3865 }, {1152,3612,1692 ,4029,3842,4030 }, - {531,3577,4469 ,3829,4059,4060 }, {96,834,3909 ,836,3876,4164 }, - {3412,3438,3387 ,4141,3912,1787 }, {1733,1483,4165 ,2328,4084,640 }, - {1733,1670,1483 ,2328,2327,4084 }, {5305,4527,4236 ,2283,1740,3488 }, - {2405,4900,5578 ,4165,585,587 }, {2296,2264,2265 ,2664,4166,2662 }, - {3553,3601,3552 ,3957,4007,4167 }, {3849,3848,3806 ,4130,4168,4062 }, - {3848,3849,3883 ,4168,4130,4149 }, {1335,1152,4516 ,236,4029,4028 }, - {4162,5805,5385 ,1738,3999,3865 }, {3696,3697,3742 ,2325,2224,3419 }, - {3540,3541,3571 ,3357,981,3652 }, {6804,6803,6785 ,3342,4169,4140 }, - {5354,3907,4866 ,3882,3883,3641 }, {3285,4900,2405 ,3064,585,4165 }, - {6116,3336,7095 ,4170,3922,3924 }, {7974,7995,7973 ,4171,4135,3624 }, - {2088,3932,55 ,1667,1096,3507 }, {5764,5800,5799 ,537,702,4064 }, - {36,35,3866 ,1478,1338,1479 }, {3967,4007,3966 ,3872,3909,4109 }, - {3172,3093,1669 ,4155,4172,3798 }, {1538,3402,2410 ,0,3660,1 }, - {882,3271,8117 ,3846,1727,4119 }, {3613,2203,2945 ,4173,4033,1239 }, - {3455,3613,2945 ,886,4173,1239 }, {2892,3172,1669 ,918,4155,3798 }, - {3652,3708,3677 ,3887,3980,3979 }, {3468,3503,3467 ,2688,3835,3992 }, - {1248,1207,3878 ,2850,1074,1248 }, {5138,5139,5260 ,1940,1939,4174 }, - {834,1588,3257 ,3876,3881,4010 }, {635,2657,20 ,2183,2185,3961 }, - {3438,3466,3437 ,3912,4175,4142 }, {3501,3500,3437 ,4176,4177,4142 }, - {3466,3501,3437 ,4175,4176,4142 }, {3579,3550,3551 ,4178,4179,4180 }, - {3552,3579,3551 ,4167,4178,4180 }, {1454,1102,694 ,2318,2317,1987 }, - {1102,4165,694 ,2317,640,1987 }, {3473,3673,2153 ,649,648,4181 }, - {5109,5107,5108 ,4182,4183,4184 }, {874,3866,783 ,636,1479,679 }, - {4913,4866,3907 ,4185,3641,3883 }, {2368,2443,2365 ,2943,2942,3351 }, - {4003,1003,4002 ,4186,4187,4188 }, {1909,3663,2203 ,4189,4040,4033 }, - {3613,1909,2203 ,4173,4189,4033 }, {1338,4029,3663 ,1077,225,4040 }, - {1909,1338,3663 ,4189,1077,4040 }, {3093,5119,4942 ,4172,4190,4191 }, - {1669,3093,4942 ,3798,4172,4191 }, {6226,5562,3905 ,4192,4193,4194 }, - {3656,3655,3626 ,4195,3936,4196 }, {5705,5992,1608 ,4127,4197,4128 }, - {1022,969,793 ,4198,4199,4200 }, {4855,4809,1469 ,4201,4202,2361 }, - {3297,3314,3313 ,4203,3575,4160 }, {3313,3314,3343 ,4160,3575,2515 }, - {6786,6804,6785 ,4139,3342,4140 }, {3653,3652,3603 ,1440,3887,3890 }, - {5573,1525,407 ,2217,1866,4018 }, {3888,3924,3887 ,4204,3847,4056 }, - {3391,3390,3331 ,3815,3509,1183 }, {3332,3391,3331 ,3891,3815,1183 }, - {6803,6853,5587 ,4169,2505,1459 }, {3579,3601,3550 ,4178,4007,4179 }, - {3601,3578,3550 ,4007,4205,4179 }, {3624,3623,3578 ,4009,4206,4205 }, - {3601,3624,3578 ,4007,4009,4205 }, {3727,1794,5831 ,3942,4129,3943 }, - {3341,915,863 ,541,443,284 }, {3389,3438,3412 ,3913,3912,4141 }, - {931,1629,1518 ,3944,4207,3978 }, {1588,3344,2856 ,3881,1861,3821 }, - {3552,3601,3579 ,4167,4007,4178 }, {4003,4002,3963 ,4186,4188,4208 }, - {4393,5306,5335 ,463,462,2523 }, {3573,3591,3590 ,980,1652,1867 }, - {5572,407,3147 ,2284,4018,2285 }, {3290,3329,3289 ,1204,3972,2056 }, - {3218,4162,5385 ,3976,1738,3865 }, {8103,3283,3425 ,1291,262,1859 }, - {4558,4599,4631 ,4209,4210,1816 }, {1454,1584,4199 ,2318,4012,3869 }, - {5335,5305,4236 ,2523,2283,3488 }, {3314,2368,2365 ,3575,2943,3351 }, - {3343,3314,2365 ,2515,3575,3351 }, {2428,2872,784 ,371,311,4211 }, - {687,2088,1155 ,1665,1667,3333 }, {5697,5698,5725 ,2552,44,3824 }, - {5725,5726,5763 ,3824,515,4212 }, {3628,5045,6235 ,4213,4214,4215 }, - {5726,5764,5763 ,515,537,4212 }, {3248,6076,5982 ,4216,4217,4218 }, - {5045,6165,6235 ,4214,4219,4215 }, {6103,5988,6123 ,4220,2516,4221 }, - {4713,5837,5804 ,4015,3621,3623 }, {1333,3300,2812 ,849,694,1914 }, - {3300,3349,2812 ,694,2054,1914 }, {3336,3335,3309 ,3922,4222,3892 }, - {5471,5435,6640 ,2051,4223,4224 }, {5026,5165,4881 ,4225,4226,2731 }, - {8106,4835,8135 ,2438,2130,2439 }, {3998,3997,3957 ,381,380,295 }, - {3224,3178,931 ,3947,4138,3944 }, {3624,3650,3623 ,4009,4008,4206 }, - {75,348,268 ,3962,643,645 }, {5119,5424,5420 ,4190,4227,4228 }, - {1300,2607,1009 ,285,2452,3752 }, {4866,4913,4865 ,3641,4185,4229 }, - {4942,5119,5420 ,4191,4190,4228 }, {5424,201,84 ,4227,4230,4231 }, - {5420,5424,84 ,4228,4227,4231 }, {201,2643,3759 ,4230,4232,4233 }, - {84,201,3759 ,4231,4230,4233 }, {1160,3096,3241 ,3661,4234,3504 }, - {2316,8059,2275 ,3067,4235,3152 }, {3573,3572,3541 ,980,1931,981 }, - {3678,3709,3652 ,3948,4120,3887 }, {3710,3708,3709 ,4236,3980,4120 }, - {4846,1622,1517 ,4237,4238,600 }, {6324,3365,3335 ,4239,4240,4222 }, - {1851,1733,1102 ,4241,2328,2317 }, {3744,3796,3795 ,2390,2969,3448 }, - {4373,1851,1102 ,3288,4241,2317 }, {5763,5764,5799 ,4212,537,4064 }, - {2955,3750,592 ,3640,690,1863 }, {3850,3849,3806 ,4107,4130,4062 }, - {5877,5474,5489 ,4242,4243,4244 }, {5414,3673,4468 ,3764,648,647 }, - {7436,7464,7412 ,4245,4246,3765 }, {3338,1005,4207 ,4147,3535,4247 }, - {3852,3887,3886 ,4248,4056,4055 }, {3729,3772,3728 ,4249,4250,4251 }, - {3718,684,3348 ,3845,4101,2255 }, {1940,11,684 ,3925,299,4101 }, - {6257,5488,5473 ,3428,2730,3429 }, {7981,7980,7955 ,4252,4253,4254 }, - {6804,6854,6853 ,3342,3344,2505 }, {3756,3755,3708 ,4255,4025,3980 }, - {3656,3627,6534 ,4195,4256,4257 }, {3627,3656,3626 ,4256,4195,4196 }, - {5356,5663,5909 ,4258,4259,4260 }, {4928,3907,917 ,4261,3883,4016 }, - {1334,4236,5480 ,1935,3488,1739 }, {1851,1691,1733 ,4241,2326,2328 }, - {3373,3897,3349 ,693,4262,2054 }, {3751,3806,3729 ,4263,4062,4249 }, - {3806,3805,3729 ,4062,4264,4249 }, {3473,2153,4469 ,649,4181,4060 }, - {2843,3473,4469 ,4058,649,4060 }, {3324,3323,3304 ,881,1051,1050 }, - {976,7335,975 ,782,4265,4266 }, {3881,3920,3919 ,4154,4267,4268 }, - {3920,3963,3962 ,4267,4208,3977 }, {3919,3920,3962 ,4268,4267,3977 }, - {3963,4002,3962 ,4208,4188,3977 }, {2634,21,3759 ,4269,1231,4233 }, - {2643,2634,3759 ,4232,4269,4233 }, {3752,3751,3730 ,4063,4263,4270 }, - {3705,3752,3730 ,4144,4063,4270 }, {3919,3962,3918 ,4268,3977,2335 }, - {7720,7770,7747 ,4271,4272,331 }, {3547,6556,6598 ,803,4273,804 }, - {3372,3453,1908 ,2143,917,1058 }, {5923,6162,5591 ,2073,3598,3597 }, - {3837,1729,4286 ,3667,3666,4274 }, {3883,3885,3922 ,4149,4057,4068 }, - {3332,3331,3292 ,3891,1183,4067 }, {3714,3713,3655 ,3970,3935,3936 }, - {5870,3248,5939 ,4275,4216,4276 }, {3851,3852,3886 ,4108,4248,4055 }, - {3884,3885,3883 ,4131,4057,4149 }, {5283,5337,4830 ,570,572,3642 }, - {3316,3718,3348 ,2149,3845,2255 }, {3718,3841,684 ,3845,3927,4101 }, - {3841,1940,684 ,3927,3925,4101 }, {2634,3672,3987 ,4269,4277,1229 }, - {7930,7981,7955 ,4278,4252,4254 }, {6803,6804,6853 ,4169,3342,2505 }, - {3757,3758,3773 ,2281,1789,4279 }, {4105,3411,5392 ,4053,4280,4022 }, - {3398,3613,3455 ,4281,4173,886 }, {3478,3398,3455 ,885,4281,886 }, - {21,2634,3987 ,1231,4269,1229 }, {3300,3373,3349 ,694,693,2054 }, - {3848,3847,3805 ,4168,4282,4264 }, {3806,3848,3805 ,4062,4168,4264 }, - {7310,7344,7286 ,4283,2749,3460 }, {7698,7697,7648 ,333,651,3952 }, - {5411,5410,5376 ,4284,4285,2758 }, {3729,3728,3674 ,4249,4251,4286 }, - {3847,3846,3805 ,4282,4287,4264 }, {5307,5573,5572 ,461,2217,2284 }, - {6154,6141,3725 ,2387,2386,3971 }, {5306,5307,5572 ,462,461,2284 }, - {3881,3882,3920 ,4154,4288,4267 }, {3672,4423,3987 ,4277,4289,1229 }, - {3808,3806,3807 ,4013,4062,4061 }, {5239,5283,4830 ,482,570,3642 }, - {3252,794,2693 ,781,3392,3416 }, {3146,1909,3613 ,4290,4189,4173 }, - {3398,3146,3613 ,4281,4290,4173 }, {3735,1338,1909 ,4291,1077,4189 }, - {3146,3735,1909 ,4290,4291,4189 }, {3397,1257,1338 ,136,208,1077 }, - {3735,3397,1338 ,4291,136,1077 }, {3308,3332,3292 ,3894,3891,4067 }, - {3809,3852,3833 ,3903,4248,4049 }, {3852,3851,3833 ,4248,4108,4049 }, - {6279,7091,6280 ,3966,4153,4079 }, {5922,3445,5908 ,4292,1791,4293 }, - {3327,3404,1387 ,439,2170,2951 }, {6906,6905,6853 ,4294,2506,2505 }, - {6854,6906,6853 ,3344,4294,2505 }, {5194,4373,1102 ,727,3288,2317 }, - {1412,541,2813 ,3926,3811,3898 }, {3736,3447,2813 ,3807,3928,3898 }, - {3925,3924,3889 ,3227,3847,3126 }, {4423,160,4227 ,4289,4295,1564 }, - {3808,3807,3753 ,4013,4061,4014 }, {3883,3882,3847 ,4149,4288,4282 }, - {132,1518,2837 ,2503,3978,2511 }, {5296,5322,4023 ,4296,4297,3827 }, - {6283,7102,7088 ,4298,1240,4299 }, {3315,5194,1454 ,944,727,2318 }, - {3388,3387,3329 ,4115,1787,3972 }, {3675,3729,3674 ,4300,4249,4286 }, - {3729,3805,3772 ,4249,4264,4250 }, {3315,1454,3828 ,944,2318,1364 }, - {3987,4423,4227 ,1229,4289,1564 }, {7091,6279,7092 ,4153,3966,3968 }, - {160,5236,5237 ,4295,4301,1800 }, {4227,160,5237 ,1564,4295,1800 }, - {5263,5239,4830 ,4302,482,3642 }, {3711,3710,3653 ,1790,4236,1440 }, - {1670,3344,1588 ,2327,1861,3881 }, {541,3057,2813 ,3811,3810,3898 }, - {794,2494,2693 ,3392,3391,3416 }, {3764,1694,1412 ,3844,3828,3926 }, - {3309,3333,3334 ,3892,3893,399 }, {1606,1253,4055 ,3677,2353,3678 }, - {3178,3224,731 ,4138,3947,3965 }, {3967,3966,3923 ,3872,4109,3849 }, - {3344,2473,4221 ,1861,4303,1862 }, {5236,4834,5600 ,4301,4304,1801 }, - {3252,2693,2968 ,781,3416,263 }, {1556,3252,2968 ,3365,781,263 }, - {3864,1334,138 ,3517,1935,434 }, {1694,541,1412 ,3828,3811,3926 }, - {3404,3372,1387 ,2170,2143,2951 }, {882,1694,3396 ,3846,3828,3867 }, - {5237,5236,5600 ,1800,4301,1801 }, {4834,5787,2086 ,4304,4305,1802 }, - {7171,7208,7194 ,4306,1671,1670 }, {3556,3604,3554 ,3385,3384,3914 }, - {3521,3556,3503 ,4307,3385,3835 }, {3504,3521,3503 ,2986,4307,3835 }, - {3556,3555,3503 ,3385,4090,3835 }, {3542,3541,3487 ,979,981,3356 }, - {7495,7494,7462 ,4308,4309,4310 }, {4293,8065,3954 ,2732,96,3463 }, - {3969,3968,3924 ,3226,3848,3847 }, {3889,3924,3888 ,3126,3847,4204 }, - {3329,3328,3289 ,3972,3973,2056 }, {3848,3883,3847 ,4168,4149,4282 }, - {253,1158,966 ,2564,2744,2565 }, {5600,4834,2086 ,1801,4304,1802 }, - {2086,5787,377 ,1802,4305,4311 }, {7240,7287,7239 ,4312,3459,1527 }, - {3846,3881,3832 ,4287,4154,2322 }, {8120,2659,8098 ,906,3485,907 }, - {959,2044,501 ,1358,4031,4313 }, {3719,959,501 ,1356,1358,4313 }, - {6958,6957,6905 ,4314,4315,2506 }, {6276,7094,7093 ,4316,4317,4318 }, - {3882,3881,3846 ,4288,4154,4287 }, {3752,3806,3751 ,4063,4062,4263 }, - {141,4298,5832 ,681,3580,3904 }, {3925,3969,3924 ,3227,3226,3847 }, - {5181,5180,4631 ,4319,1878,1816 }, {2473,2955,4221 ,4303,3640,1862 }, - {3965,4005,3964 ,4069,3906,3964 }, {2422,2912,731 ,3907,3934,3965 }, - {3457,918,530 ,4099,4150,512 }, {75,2938,635 ,3962,4320,2183 }, - {3040,3457,530 ,619,4099,512 }, {3711,3757,3710 ,1790,2281,4236 }, - {6261,4044,4570 ,4321,4322,4082 }, {3057,3736,2813 ,3810,3807,3898 }, - {1694,3764,3396 ,3828,3844,3867 }, {1159,3972,4185 ,3760,3487,409 }, - {3274,3001,3819 ,3172,3210,3763 }, {5289,5288,5266 ,80,3993,81 }, - {3810,3811,3853 ,3901,4323,3902 }, {3889,3888,3853 ,3126,4204,3902 }, - {75,268,107 ,3962,645,2548 }, {6906,6958,6905 ,4294,4314,2506 }, - {1256,1518,132 ,3945,3978,2503 }, {2087,1256,132 ,2465,3945,2503 }, - {3846,3832,3804 ,4287,2322,4324 }, {3897,1804,3349 ,4262,2902,2054 }, - {5180,5242,5220 ,1878,3843,2119 }, {3800,1524,4697 ,4325,4326,4327 }, - {3676,3675,3623 ,4328,4300,4206 }, {6680,5471,6647 ,3834,2051,2050 }, - {3847,3882,3846 ,4282,4288,4287 }, {3650,3676,3623 ,4008,4328,4206 }, - {3964,3963,3920 ,3964,4208,4267 }, {3651,3706,3650 ,3888,4143,4008 }, - {3748,5385,5380 ,4034,3865,653 }, {5022,4669,1196 ,4329,3879,4330 }, - {3854,3889,3853 ,1512,3126,3902 }, {6116,7095,7094 ,4170,3924,4317 }, - {1159,4185,3673 ,3760,409,648 }, {7225,7246,7267 ,1672,3772,4331 }, - {7752,7751,7701 ,3514,4332,3515 }, {4797,5259,5258 ,3984,3794,1193 }, - {834,2856,1588 ,3876,3821,3881 }, {3909,834,3257 ,4164,3876,4010 }, - {5855,2856,1974 ,4011,3821,3862 }, {7312,7311,7262 ,4333,4334,4335 }, - {7797,7823,7796 ,3506,345,3803 }, {7825,7824,7771 ,3768,3770,4336 }, - {5286,5339,5285 ,3859,2223,2169 }, {107,268,751 ,2548,645,1365 }, - {7096,5473,3518 ,4337,3429,4338 }, {4369,4393,5335 ,104,463,2523 }, - {1339,3398,3478 ,4339,4281,885 }, {3449,1339,3478 ,2440,4339,885 }, - {3811,3810,3756 ,4323,3901,4255 }, {3757,3811,3756 ,2281,4323,4255 }, - {3757,3773,3811 ,2281,4279,4323 }, {3812,3854,3811 ,1513,1512,4323 }, - {407,3477,3147 ,4018,1736,2285 }, {1339,3564,3146 ,4339,4340,4290 }, - {3398,1339,3146 ,4281,4339,4290 }, {1334,5480,3218 ,1935,1739,3976 }, - {1670,1691,3344 ,2327,2326,1861 }, {1691,2473,3344 ,2326,4303,1861 }, - {3710,3709,3678 ,4236,4120,3948 }, {3653,3710,3678 ,1440,4236,3948 }, - {3962,4002,3961 ,3977,4188,2346 }, {3602,3601,3553 ,3889,4007,3957 }, - {4002,4001,3961 ,4188,2380,2346 }, {731,1003,4003 ,3965,4187,4186 }, - {3805,3846,3804 ,4264,4287,4324 }, {2219,5231,5000 ,177,115,286 }, - {8064,3975,3008 ,3141,728,546 }, {4078,1004,1919 ,4341,3452,1779 }, - {3368,3577,3369 ,3960,4059,3836 }, {3542,3591,3573 ,979,1652,980 }, - {7424,7423,7371 ,1188,3749,3748 }, {3038,1783,1341 ,4342,2251,3841 }, - {5288,4868,5287 ,3993,3969,4003 }, {4487,694,1607 ,4006,1987,3941 }, - {3969,4010,3968 ,3226,4343,3848 }, {3564,3782,3735 ,4340,4344,4291 }, - {3146,3564,3735 ,4290,4340,4291 }, {3370,3397,3735 ,2303,136,4291 }, - {3782,3370,3735 ,4344,2303,4291 }, {1454,694,1584 ,2318,1987,4012 }, - {4710,5218,4770 ,4136,483,4345 }, {1443,3274,1152 ,182,3172,4029 }, - {4236,4527,5480 ,3488,1740,1739 }, {4527,3147,4162 ,1740,2285,1738 }, - {4006,4005,3965 ,4110,3906,4069 }, {5572,5573,407 ,2284,2217,4018 }, - {1256,2087,3004 ,3945,2465,2437 }, {1309,3935,685 ,3709,4346,2674 }, - {1483,1539,87 ,4084,835,641 }, {3871,3748,5850 ,397,4034,394 }, - {3329,3387,3362 ,3972,1787,1200 }, {3566,2557,3052 ,1146,3620,3715 }, - {4653,4654,4679 ,4347,4348,3852 }, {6116,6276,6286 ,4170,4316,4349 }, - {6853,6892,5626 ,2505,2507,1460 }, {147,4712,4027 ,3469,3816,3470 }, - {3243,1099,1111 ,3919,4350,3920 }, {3187,2698,4236 ,3402,105,3488 }, - {5218,5238,4770 ,483,4351,4345 }, {3748,3749,5850 ,4034,652,394 }, - {2713,4091,3259 ,4352,123,407 }, {3304,3303,7333 ,1050,1049,4353 }, - {7948,7947,7896 ,3625,4354,4355 }, {3446,3472,5608 ,3718,3155,4356 }, - {6276,6116,7094 ,4316,4170,4317 }, {3309,7110,7109 ,3892,1990,4112 }, - {3707,3706,3651 ,3981,4143,3888 }, {3553,3552,3519 ,3957,4167,4357 }, - {3520,3553,3519 ,4358,3957,4357 }, {4004,731,4003 ,3908,3965,4186 }, - {3773,3758,3812 ,4279,1789,1513 }, {1003,2910,393 ,4187,3946,2400 }, - {3881,3880,3832 ,4154,2320,2322 }, {3271,3396,3316 ,1727,3867,2149 }, - {3289,3328,3307 ,2056,3973,2057 }, {4022,8111,5353 ,3503,4359,851 }, - {1630,924,56 ,1016,3634,2606 }, {3451,3537,3516 ,1720,1735,1721 }, - {4600,4653,4599 ,4360,4347,4210 }, {3566,3052,2397 ,1146,3715,3669 }, - {5385,4157,5304 ,3865,3755,654 }, {3883,3921,3920 ,4149,4070,4267 }, - {3753,3752,3706 ,4014,4063,4143 }, {7983,8095,8105 ,4361,4362,3791 }, - {4002,1003,4001 ,4188,4187,2380 }, {2544,2622,601 ,2451,2840,3905 }, - {149,454,2471 ,3736,4363,3653 }, {3456,2920,2557 ,1145,1799,3620 }, - {4165,1483,87 ,640,4084,641 }, {3502,3553,3520 ,3958,3957,4358 }, - {3331,3390,3364 ,1183,3509,1184 }, {2043,143,918 ,4100,157,4150 }, - {4056,2421,4161 ,1123,2568,4364 }, {3457,2043,918 ,4099,4100,4150 }, - {5833,141,5832 ,544,681,3904 }, {3757,3756,3710 ,2281,4255,4236 }, - {6186,3518,5488 ,4365,4338,2730 }, {7203,7240,7220 ,4366,4312,4367 }, - {3130,3086,3087 ,3414,3413,3573 }, {6997,6996,6957 ,4368,4369,4315 }, - {507,5309,505 ,1523,987,986 }, {3812,3758,3813 ,1513,1789,1515 }, - {115,3081,3241 ,2901,1017,3504 }, {3096,115,3241 ,4234,2901,3504 }, - {1657,1552,3156 ,1781,2266,3164 }, {923,924,1630 ,4370,3634,1016 }, - {1663,923,1630 ,1015,4370,1016 }, {923,3640,924 ,4370,4371,3634 }, - {149,219,924 ,3736,3087,3634 }, {3640,149,924 ,4371,3736,3634 }, - {1383,1296,788 ,336,335,976 }, {53,2346,2573 ,4372,3252,3253 }, - {3882,3883,3920 ,4288,4149,4267 }, {6958,6997,6957 ,4314,4368,4315 }, - {7397,7371,7372 ,1186,3748,4373 }, {3513,3404,3327 ,441,2170,439 }, - {268,1664,751 ,645,2268,1365 }, {3811,3854,3853 ,4323,1512,3902 }, - {2651,2397,2381 ,1516,3669,3672 }, {6997,5752,6996 ,4368,4374,4369 }, - {3112,3993,3132 ,2795,2807,2796 }, {6285,7109,7095 ,3923,4112,3924 }, - {3170,1160,3211 ,3510,3661,3605 }, {869,2681,2346 ,4375,3254,3252 }, - {53,869,2346 ,4372,4375,3252 }, {3450,2464,2681 ,3330,3299,3254 }, - {869,3450,2681 ,4375,3330,3254 }, {7039,7038,5752 ,4376,3832,4374 }, - {7109,6285,3309 ,4112,3923,3892 }, {3651,3650,3624 ,3888,4008,4009 }, - {3749,3748,5380 ,652,4034,653 }, {3373,1804,3897 ,693,2902,4262 }, - {6276,6287,6286 ,4316,4377,4349 }, {4770,5238,5263 ,4345,4351,4302 }, - {3009,5701,5683 ,3747,3938,3939 }, {2624,3405,1153 ,3132,1798,2245 }, - {1794,3616,2795 ,4129,3974,3975 }, {3377,3378,3427 ,1443,1442,4378 }, - {6509,8092,8074 ,1452,2267,2460 }, {4032,3170,3129 ,3638,3510,3412 }, - {2829,1107,1106 ,2620,2656,1664 }, {6997,7039,5752 ,4368,4376,4374 }, - {1066,2558,105 ,680,977,249 }, {2850,2833,2803 ,3657,2065,3655 }, - {2910,3004,393 ,3946,2437,2400 }, {5238,5239,5263 ,4351,482,4302 }, - {5198,5218,4710 ,423,483,4136 }, {3486,3487,3540 ,4379,3356,3357 }, - {6390,2256,5950 ,3868,2323,2873 }, {538,53,3569 ,4380,4372,4381 }, - {3468,3467,3439 ,2688,3992,2689 }, {1387,1908,3636 ,2951,1058,1060 }, - {1378,1339,3449 ,925,4339,2440 }, {3334,3333,3309 ,399,3893,3892 }, - {3502,3520,3501 ,3958,4358,4176 }, {3921,3964,3920 ,4070,3964,4267 }, - {3706,3705,3650 ,4143,4144,4008 }, {3501,3519,3500 ,4176,4357,4177 }, - {3519,3552,3500 ,4357,4167,4177 }, {3552,3551,3500 ,4167,4180,4177 }, - {3602,3625,3601 ,3889,4382,4007 }, {3625,3651,3601 ,4382,3888,4007 }, - {1693,2693,1249 ,264,3416,2876 }, {1524,1907,3818 ,4326,1245,1244 }, - {1109,1160,4032 ,4383,3661,3638 }, {1109,3096,1160 ,4383,4234,3661 }, - {149,3640,923 ,3736,4371,4370 }, {2218,409,454 ,4384,1480,4363 }, - {1303,687,831 ,1933,1665,3334 }, {3428,3459,3458 ,3261,3241,4385 }, - {3378,3428,3427 ,1442,3261,4378 }, {88,3637,1453 ,3581,4386,3355 }, - {3636,88,1453 ,1060,3581,3355 }, {3378,3428,3406 ,1442,3261,399 }, - {6277,6287,6276 ,4387,4377,4316 }, {3266,4775,495 ,3884,4388,4389 }, - {3913,2698,4248 ,841,105,3256 }, {6133,3518,7084 ,4390,4338,4391 }, - {1487,3194,2150 ,531,1065,534 }, {3571,3572,3589 ,3652,1931,1932 }, - {3531,53,538 ,4392,4372,4380 }, {5936,6143,6125 ,4393,4394,4395 }, - {382,3732,3318 ,3534,3536,4036 }, {4630,4710,4709 ,827,4136,4396 }, - {5815,5816,5832 ,4065,545,3904 }, {3439,3467,3438 ,2689,3992,3912 }, - {3502,3501,3466 ,3958,4176,4175 }, {3650,3705,3676 ,4008,4144,4328 }, - {3705,3730,3676 ,4144,4270,4328 }, {3730,3675,3676 ,4270,4300,4328 }, - {3730,3751,3675 ,4270,4263,4300 }, {3751,3729,3675 ,4263,4249,4300 }, - {4004,4003,3963 ,3908,4186,4208 }, {3964,4004,3963 ,3964,3908,4208 }, - {3651,3624,3601 ,3888,4009,4007 }, {3602,3651,3625 ,3889,3888,4382 }, - {6186,7084,3518 ,4365,4391,4338 }, {1334,3218,3748 ,1935,3976,4034 }, - {1003,393,4001 ,4187,2400,2380 }, {3321,3322,3354 ,2461,1182,1293 }, - {3853,3888,3852 ,3902,4204,4248 }, {3756,3810,3755 ,4255,3901,4025 }, - {3772,5882,3728 ,4250,4397,4251 }, {3131,4033,4032 ,3732,4398,3638 }, - {3130,3131,4032 ,3414,3732,3638 }, {225,1109,4032 ,4399,4383,3638 }, - {4033,225,4032 ,4398,4399,3638 }, {3901,3096,1109 ,4400,4234,4383 }, - {225,3901,1109 ,4399,4400,4383 }, {3901,3949,3096 ,4400,4401,4234 }, - {2010,115,3096 ,4402,2901,4234 }, {3949,2010,3096 ,4401,4402,4234 }, - {2415,1663,115 ,4403,1015,2901 }, {2010,2415,115 ,4402,4403,2901 }, - {1301,923,1663 ,4404,4370,1015 }, {2415,1301,1663 ,4403,4404,1015 }, - {182,149,923 ,4405,3736,4370 }, {1301,182,923 ,4404,4405,4370 }, - {182,230,149 ,4405,4406,3736 }, {12,454,149 ,4407,4363,3736 }, - {230,12,149 ,4406,4407,3736 }, {130,2218,454 ,2341,4384,4363 }, - {12,130,454 ,4407,2341,4363 }, {150,409,2218 ,1481,1480,4384 }, - {6509,8074,5013 ,1452,2460,684 }, {2218,130,150 ,4384,2341,1481 }, - {3427,3428,3458 ,4378,3261,4385 }, {3459,3487,3486 ,3241,3356,4379 }, - {3958,3998,3957 ,294,381,295 }, {1946,131,4384 ,4408,2965,4409 }, - {2522,549,2451 ,2678,1129,2102 }, {495,3788,1341 ,4389,4410,3841 }, - {7777,7776,7730 ,4411,4412,4413 }, {1378,3687,3564 ,925,924,4340 }, - {1339,1378,3564 ,4339,925,4340 }, {3382,3407,3381 ,870,3012,871 }, - {3050,538,3532 ,4414,4380,4415 }, {3479,3531,538 ,4416,4392,4380 }, - {3050,3479,538 ,4414,4416,4380 }, {3479,53,3531 ,4416,4372,4392 }, - {295,869,53 ,4417,4375,4372 }, {3479,295,53 ,4416,4417,4372 }, - {295,2645,3450 ,4417,4418,3330 }, {869,295,3450 ,4375,4417,3330 }, - {3535,840,3450 ,442,68,3330 }, {2645,3535,3450 ,4418,442,3330 }, - {3687,3873,3782 ,924,1809,4344 }, {3488,3542,3487 ,1980,979,3356 }, - {3888,3887,3852 ,4204,4056,4248 }, {3853,3852,3809 ,3902,4248,3903 }, - {3707,3754,3706 ,3981,4071,4143 }, {3754,3753,3706 ,4071,4014,4143 }, - {3520,3519,3501 ,4358,4357,4176 }, {3467,3502,3466 ,3992,3958,4175 }, - {731,3224,1003 ,3965,3947,4187 }, {3224,2910,1003 ,3947,3946,4187 }, - {7691,450,449 ,4419,1653,1169 }, {3390,3439,3389 ,3509,2689,3913 }, - {3467,3466,3438 ,3992,4175,3912 }, {3738,1940,3841 ,3900,3925,3927 }, - {3710,3756,3708 ,4236,4255,3980 }, {3564,3687,3782 ,4340,924,4344 }, - {3873,3475,3370 ,1809,1559,2303 }, {3782,3873,3370 ,4344,1809,2303 }, - {4162,4129,5805 ,1738,3812,3999 }, {7075,7074,7038 ,4420,2747,3832 }, - {498,2010,3949 ,4421,4402,4401 }, {3901,498,3949 ,4400,4421,4401 }, - {2088,55,1155 ,1667,3507,3333 }, {3458,3459,3486 ,4385,3241,4379 }, - {1096,1975,3637 ,2727,4422,4386 }, {88,1096,3637 ,3581,2727,4386 }, - {7039,7075,7038 ,4376,4420,3832 }, {4600,4653,4679 ,4360,4347,3852 }, - {7451,7479,7423 ,3963,4423,3749 }, {630,879,3370 ,2491,1155,2303 }, - {3475,630,3370 ,1559,2491,2303 }, {543,495,1341 ,3840,4389,3841 }, - {7983,4744,5026 ,4361,4424,4225 }, {3617,3532,3375 ,4425,4415,4426 }, - {1208,1586,689 ,3211,943,1363 }, {2527,2486,2487 ,2637,2814,4427 }, - {2831,2832,2868 ,639,638,1577 }, {5845,6174,5113 ,143,2381,141 }, - {3604,3653,3603 ,3384,1440,3890 }, {3810,3809,3755 ,3901,3903,4025 }, - {1975,1096,784 ,4422,2727,4211 }, {498,2415,2010 ,4421,4403,4402 }, - {3696,3742,3741 ,2325,3419,3447 }, {3695,3696,3741 ,4428,2325,3447 }, - {300,183,499 ,3508,4429,4430 }, {3091,1390,300 ,3262,2964,3508 }, - {4617,4924,7074 ,4431,2745,2747 }, {7075,4617,7074 ,4420,4431,2747 }, - {5337,4866,5354 ,572,3641,3882 }, {1922,3244,4397 ,4432,4433,4434 }, - {2407,2258,6484 ,4001,1198,685 }, {3924,3923,3887 ,3847,3849,4056 }, - {6316,2162,6390 ,4435,3278,3868 }, {1299,295,3479 ,4436,4417,4416 }, - {295,3041,2645 ,4417,3182,4418 }, {3902,5970,4104 ,4437,3518,3519 }, - {2314,2223,2256 ,3742,2336,2323 }, {3426,3485,2615 ,4438,3524,2788 }, - {2566,3426,2615 ,2635,4438,2788 }, {2615,3485,2646 ,2788,3524,3070 }, - {2646,3485,2686 ,3070,3524,3071 }, {3539,3588,2740 ,3525,4439,3456 }, - {2686,3539,2740 ,3071,3525,3456 }, {2740,3588,3619 ,3456,4439,3956 }, - {3588,3641,2790 ,4439,4440,3576 }, {3619,3588,2790 ,3956,4439,3576 }, - {3641,3694,2832 ,4440,3915,638 }, {2790,3641,2832 ,3576,4440,638 }, - {3393,3392,3333 ,4441,3860,3893 }, {6467,6484,2223 ,4000,685,2336 }, - {3365,3333,3309 ,4240,3893,3892 }, {3132,4034,4033 ,2796,4442,4398 }, - {3131,3132,4033 ,3732,2796,4398 }, {4034,97,225 ,4442,4443,4399 }, - {4033,4034,225 ,4398,4442,4399 }, {1110,3901,225 ,4444,4400,4399 }, - {97,1110,225 ,4443,4444,4399 }, {585,498,3901 ,4445,4421,4400 }, - {1110,585,3901 ,4444,4445,4400 }, {919,2415,498 ,4446,4403,4421 }, - {585,919,498 ,4445,4446,4421 }, {54,1301,2415 ,4447,4404,4403 }, - {919,54,2415 ,4446,4447,4403 }, {224,182,1301 ,4448,4405,4404 }, - {54,224,1301 ,4447,4448,4404 }, {3056,230,182 ,4449,4406,4405 }, - {224,3056,182 ,4448,4449,4405 }, {400,12,230 ,4450,4407,4406 }, - {3056,400,230 ,4449,4450,4406 }, {95,130,12 ,3482,2341,4407 }, - {400,95,12 ,4450,3482,4407 }, {1798,89,130 ,4451,1482,2341 }, - {1155,300,499 ,3333,3508,4430 }, {1096,2749,784 ,2727,3286,4211 }, - {784,2749,2428 ,4211,3286,371 }, {1155,499,957 ,3333,4430,4452 }, - {1626,831,3253 ,564,3334,565 }, {3184,2618,3895 ,4453,4454,4455 }, - {831,1155,957 ,3334,3333,4452 }, {1004,4078,31 ,3452,4341,3450 }, - {8108,8066,8128 ,2287,960,959 }, {3770,3050,3617 ,4456,4414,4425 }, - {3346,3770,3617 ,4457,4456,4425 }, {3424,3479,3050 ,4458,4416,4414 }, - {3770,3424,3050 ,4456,4458,4414 }, {3635,1299,3479 ,4459,4436,4416 }, - {3424,3635,3479 ,4458,4459,4416 }, {3635,295,1299 ,4459,4417,4436 }, - {3635,1860,3041 ,4459,3183,3182 }, {295,3635,3041 ,4417,4459,3182 }, - {3618,1860,4204 ,37,3183,38 }, {832,3094,1059 ,613,634,678 }, - {3955,3995,3994 ,3538,3540,2806 }, {4617,1166,4924 ,4431,2746,2745 }, - {3365,3393,3333 ,4240,4441,3893 }, {2471,454,2536 ,3653,4363,2793 }, - {55,652,1383 ,3507,334,336 }, {1296,2558,788 ,335,977,976 }, - {3442,3441,3413 ,4460,2987,3814 }, {3393,3413,3392 ,4441,3814,3860 }, - {3342,2364,177 ,2514,2513,3618 }, {1558,919,585 ,4461,4446,4445 }, - {3643,3696,3695 ,2344,2325,4428 }, {3642,3643,3695 ,4462,2344,4428 }, - {3378,3406,3428 ,1442,399,3261 }, {6260,6053,5320 ,3397,4463,4464 }, - {7241,7240,7203 ,4465,4312,4366 }, {6996,5752,6957 ,4369,4374,4315 }, - {3335,3365,3309 ,4222,4240,3892 }, {2536,454,409 ,2793,4363,1480 }, - {3346,3375,4098 ,4457,4426,353 }, {1303,831,1626 ,1933,3334,564 }, - {2743,3944,3931 ,923,729,1868 }, {53,2573,3569 ,4372,3253,4381 }, - {3895,2618,3376 ,4455,4454,4466 }, {3539,3570,3588 ,3525,4467,4439 }, - {730,960,1985 ,1549,1326,1547 }, {1378,3449,8135 ,925,2440,2439 }, - {3420,3299,3474 ,3785,3232,3808 }, {58,2013,1332 ,2156,2242,3492 }, - {224,400,3056 ,4448,4450,4449 }, {8,4522,220 ,3341,3481,3340 }, - {1390,151,183 ,2964,2966,4429 }, {300,1390,183 ,3508,2964,4429 }, - {8131,7714,7983 ,2307,4468,4361 }, {1383,3091,300 ,336,3262,3508 }, - {55,1383,300 ,3507,336,3508 }, {3390,3389,3363 ,3509,3913,1911 }, - {3119,2681,2747 ,3223,3254,3224 }, {3805,3804,3772 ,4264,4324,4250 }, - {831,957,3253 ,3334,4452,565 }, {3299,637,3474 ,3232,3805,3808 }, - {366,1202,1204 ,4469,339,338 }, {3826,3770,3346 ,4470,4456,4457 }, - {784,4204,1860 ,4211,38,3183 }, {912,309,1166 ,4471,4472,2746 }, - {2162,2314,2256 ,3278,3742,2323 }, {3211,1160,3241 ,3605,3661,3504 }, - {4617,912,1166 ,4431,4471,2746 }, {79,3421,3278 ,1510,1156,1158 }, - {1537,4191,3802 ,4473,4474,4475 }, {3377,3427,3426 ,1443,4378,4438 }, - {2487,3377,3426 ,4427,1443,4438 }, {3427,3486,3485 ,4378,4379,3524 }, - {3426,3427,3485 ,4438,4378,3524 }, {3486,3540,3539 ,4379,3357,3525 }, - {3485,3486,3539 ,3524,4379,3525 }, {3540,3571,3570 ,3357,3652,4467 }, - {3539,3540,3570 ,3525,3357,4467 }, {3571,3589,3588 ,3652,1932,4439 }, - {3570,3571,3588 ,4467,3652,4439 }, {3589,3642,3641 ,1932,4462,4440 }, - {3588,3589,3641 ,4439,1932,4440 }, {3642,3695,3694 ,4462,4428,3915 }, - {3641,3642,3694 ,4440,4462,3915 }, {3695,3741,2907 ,4428,3447,111 }, - {3694,3695,2907 ,3915,4428,111 }, {3792,3791,2907 ,3421,3739,111 }, - {3741,3792,2907 ,3447,3421,111 }, {3881,3919,3918 ,4154,4268,2335 }, - {3420,3474,3685 ,3785,3808,3809 }, {3312,3420,3685 ,3782,3785,3809 }, - {4035,4034,3132 ,1601,4442,2796 }, {3993,4035,3132 ,2807,1601,2796 }, - {4035,2151,97 ,1601,1603,4443 }, {4034,4035,97 ,4442,1601,4443 }, - {2151,349,1110 ,1603,566,4444 }, {97,2151,1110 ,4443,1603,4444 }, - {3253,585,1110 ,565,4445,4444 }, {349,3253,1110 ,566,565,4444 }, - {957,1558,585 ,4452,4461,4445 }, {3253,957,585 ,565,4452,4445 }, - {2071,919,1558 ,4476,4446,4461 }, {957,2071,1558 ,4452,4476,4461 }, - {499,54,919 ,4430,4447,4446 }, {2071,499,919 ,4476,4430,4446 }, - {183,224,54 ,4429,4448,4447 }, {499,183,54 ,4430,4429,4447 }, - {151,400,224 ,2966,4450,4448 }, {183,151,224 ,4429,2966,4448 }, - {220,95,400 ,3340,3482,4450 }, {151,220,400 ,2966,3340,4450 }, - {3590,3643,3642 ,1867,2344,4462 }, {3589,3590,3642 ,1932,1867,4462 }, - {4037,4036,3994 ,3541,1602,2806 }, {958,3312,3685 ,3779,3782,3809 }, - {3393,3442,3413 ,4441,4460,3814 }, {1068,958,3685 ,3761,3779,3809 }, - {939,3346,4350 ,3354,4457,740 }, {1453,3826,3346 ,3355,4470,4457 }, - {939,1453,3346 ,3354,3355,4457 }, {3992,3770,3826 ,4477,4456,4470 }, - {1453,3992,3826 ,3355,4477,4470 }, {3637,3424,3770 ,4386,4458,4456 }, - {3992,3637,3770 ,4477,4386,4456 }, {1975,3635,3424 ,4422,4459,4458 }, - {3637,1975,3424 ,4386,4422,4458 }, {1975,784,1860 ,4422,4211,3183 }, - {3635,1975,1860 ,4459,4422,3183 }, {2829,1106,1062 ,2620,1664,1232 }, - {4031,2585,2474 ,3069,875,874 }, {8007,2287,2250 ,3854,3735,3716 }, - {8077,8113,3421 ,2078,1157,1156 }, {8096,5057,6796 ,1796,1795,2617 }, - {957,499,2071 ,4452,4430,4476 }, {131,220,151 ,2965,3340,2966 }, - {912,2240,2375 ,4471,4478,4479 }, {254,2472,2039 ,2008,2009,1655 }, - {3995,4037,3994 ,3540,3541,2806 }, {3278,1724,2851 ,1158,3880,3051 }, - {1668,1208,689 ,3212,3211,1363 }, {872,958,1068 ,2962,3779,3761 }, - {836,872,1068 ,3551,2962,3761 }, {1453,3637,3992 ,3355,4386,4477 }, - {3211,3169,3170 ,3605,2810,3510 }, {309,912,2375 ,4472,4471,4479 }, - {3427,3458,3486 ,4378,4385,4379 }, {1564,1922,1085 ,368,4432,369 }, - {1085,1859,703 ,369,4480,100 }, {1859,1625,703 ,4480,388,100 }, - {1859,4222,1625 ,4480,530,388 }, {4181,23,4100 ,2273,2272,4481 }, - {1933,4181,1695 ,470,2273,471 }, {4131,1087,2026 ,1656,4482,130 }, - {2026,1087,870 ,130,4482,2274 }, {1063,1092,1366 ,4483,4484,4485 }, - {1092,1424,1366 ,4484,2298,4485 }, {5728,5766,5727 ,4486,4487,514 }, - {4181,4100,1695 ,2273,4481,471 }, {245,1458,1148 ,4488,4489,160 }, - {1148,1458,1925 ,160,4489,161 }, {4061,4064,4060 ,4490,1880,2099 }, - {5766,5765,5727 ,4487,540,514 }, {4107,1932,770 ,4491,2141,275 }, - {1396,4107,770 ,34,4491,275 }, {4262,2,702 ,4492,4493,2699 }, - {4090,4148,4108 ,1487,2305,2304 }, {416,2091,604 ,556,4494,486 }, - {4090,4108,4089 ,1487,2304,4495 }, {2,265,443 ,4493,4496,920 }, - {1932,1792,2352 ,2141,324,390 }, {4090,4089,4070 ,1487,4495,4497 }, - {1600,311,209 ,4498,246,4499 }, {4257,4276,4275 ,698,1446,699 }, - {461,2,443 ,399,4493,920 }, {1792,955,1375 ,324,3991,325 }, {4109,4126,4175 ,576,124,4500 }, - {5700,5728,5727 ,4501,4486,514 }, {4061,4060,3347 ,4490,2099,2098 }, - {494,787,175 ,3041,209,211 }, {1872,1836,1808 ,2459,2490,4502 }, - {443,697,59 ,920,3578,967 }, {493,494,175 ,1785,3041,211 }, {8119,5261,8116 ,184,173,172 }, - {4175,3784,4218 ,4500,2252,4503 }, {5699,5700,5727 ,43,4501,514 }, - {6311,6390,8114 ,1097,3868,2286 }, {121,4155,1730 ,4504,4505,4506 }, - {665,761,760 ,1754,1826,3106 }, {1310,229,737 ,149,221,150 }, - {3867,1101,1851 ,4507,2,4241 }, {251,491,1517 ,603,487,600 }, - {3794,3795,3796 ,3449,3448,2969 }, {2423,1225,2428 ,433,327,371 }, - {2427,2254,3117 ,3237,3705,3407 }, {4357,4405,2941 ,4508,4509,109 }, - {3792,4357,2941 ,3421,4508,109 }, {4405,2940,2941 ,4509,110,109 }, - {4499,1205,2940 ,4510,3187,110 }, {5282,4827,5334 ,1194,2734,2736 }, - {2310,2504,4146 ,2442,2898,3730 }, {3794,3796,4259 ,3449,2969,4511 }, - {3794,4305,3793 ,3449,4512,3420 }, {3793,4357,3792 ,3420,4508,3421 }, - {4474,2940,4405 ,4513,110,4509 }, {4474,4499,2940 ,4513,4510,110 }, - {4474,1205,4499 ,4513,3187,4510 }, {596,548,549 ,1462,938,1129 }, - {23,1441,1063 ,2272,1485,4483 }, {4070,4069,4064 ,4497,1881,1880 }, - {4146,3844,2310 ,3730,2443,2442 }, {3585,4493,3447 ,3806,4514,3928 }, - {4533,64,3447 ,4515,297,3928 }, {4493,4533,3447 ,4514,4515,3928 }, - {4533,207,64 ,4515,4516,297 }, {4472,4491,1653 ,2189,107,106 }, - {237,236,99 ,869,2444,3743 }, {3794,4259,4305 ,3449,4511,4512 }, - {4434,4405,4357 ,4517,4509,4508 }, {4434,4474,4405 ,4517,4513,4509 }, - {4499,1205,4474 ,4510,3187,4513 }, {549,548,450 ,1129,938,1653 }, - {1425,121,1730 ,4518,4504,4506 }, {477,478,64 ,4519,2077,297 }, - {207,477,64 ,4516,4519,297 }, {1212,1178,4075 ,4520,206,4521 }, - {8109,5521,8110 ,2938,751,1140 }, {4357,4381,4434 ,4508,4522,4517 }, - {4474,4500,4499 ,4513,4523,4510 }, {1529,277,478 ,4524,85,2077 }, - {477,1529,478 ,4519,4524,2077 }, {1529,276,277 ,4524,4525,85 }, - {5641,5657,5640 ,510,48,2214 }, {4381,4357,4305 ,4522,4508,4512 }, - {4332,4381,4305 ,4526,4522,4512 }, {4434,4453,4474 ,4517,4527,4513 }, - {4453,4500,4474 ,4527,4523,4513 }, {4371,3864,3972 ,3489,3517,3487 }, - {276,463,277 ,4525,1941,85 }, {38,74,110 ,742,2053,743 }, {5680,5679,5640 ,50,2553,2214 }, - {8037,8050,4589 ,4528,4529,776 }, {3950,5352,3799 ,4530,3502,4531 }, - {4277,4259,4240 ,4532,4511,2060 }, {4332,4305,4259 ,4526,4512,4511 }, - {4277,4332,4259 ,4532,4526,4511 }, {4406,4381,4434 ,399,4522,4517 }, - {4381,4406,4434 ,4522,399,4517 }, {4381,4453,4434 ,4522,4527,4517 }, - {4500,366,1204 ,4523,4469,338 }, {4099,4327,561 ,4533,2377,2379 }, - {122,1467,755 ,4534,4535,1615 }, {4113,1178,1212 ,207,206,4520 }, - {504,4330,513 ,4536,399,377 }, {467,504,513 ,346,4536,377 }, - {4161,2421,2695 ,4364,2568,3453 }, {4259,3796,4240 ,4511,2969,2060 }, - {5641,5640,5599 ,510,2214,2213 }, {4260,4277,4240 ,3527,4532,2060 }, - {4358,4381,4332 ,4537,4522,4526 }, {4407,4406,4381 ,4538,399,4522 }, - {4358,4407,4381 ,4537,4538,4522 }, {4407,4435,4381 ,4538,4539,4522 }, - {4406,4407,4381 ,399,4538,4522 }, {4381,4435,4453 ,4522,4539,4527 }, - {4453,4501,4500 ,4527,4540,4523 }, {4501,4538,366 ,4540,4541,4469 }, - {4500,4501,366 ,4523,4540,4469 }, {342,343,1993 ,4542,4543,4544 }, - {4471,4472,1653 ,2187,2189,106 }, {513,1154,1343 ,377,4545,378 }, - {1343,1154,4399 ,378,4545,4546 }, {8111,4022,6836 ,4359,3503,852 }, - {2697,3977,3327 ,2006,440,439 }, {6892,6905,5626 ,2507,2506,1460 }, - {4359,4358,4332 ,4547,4537,4526 }, {4277,4359,4332 ,4532,4547,4526 }, - {4359,4407,4358 ,4547,4538,4537 }, {4453,4475,4501 ,4527,4548,4540 }, - {4538,1040,366 ,4541,1476,4469 }, {2240,3978,3945 ,4478,4549,4550 }, - {2313,321,1222 ,438,60,62 }, {378,1467,122 ,4551,4535,4534 }, - {4397,1859,1085 ,4434,4480,369 }, {8095,8090,8105 ,4362,412,3791 }, - {8131,7983,8105 ,2307,4361,3791 }, {2955,3761,3750 ,3640,218,690 }, - {5569,5570,5599 ,2075,508,2213 }, {4306,4359,4277 ,4552,4547,4532 }, - {4407,4408,4435 ,4538,4553,4539 }, {4435,4408,4453 ,4539,4553,4527 }, - {4453,4408,4475 ,4527,4553,4548 }, {4475,1904,4501 ,4548,2164,4540 }, - {1904,1906,4538 ,2164,1509,4541 }, {4501,1904,4538 ,4540,2164,4541 }, - {1906,1040,4538 ,1509,1476,4541 }, {1224,1201,1040 ,1508,1477,1476 }, - {887,4131,4222 ,4554,1656,530 }, {4156,887,4222 ,4555,4554,530 }, - {887,1087,4131 ,4554,4482,1656 }, {8113,8077,1724 ,1157,2078,3880 }, - {4360,4359,4306 ,4556,4547,4552 }, {4360,4382,4359 ,4556,4557,4547 }, - {4408,4407,4359 ,4553,4538,4547 }, {4382,4408,4359 ,4557,4553,4547 }, - {4476,1904,4475 ,4558,2164,4548 }, {1904,1224,1906 ,2164,1508,1509 }, - {649,492,175 ,1579,1580,211 }, {365,4521,606 ,402,179,180 }, - {1424,1528,1807 ,2298,4559,4560 }, {1807,1528,608 ,4560,4559,2300 }, - {1366,1424,1807 ,4485,2298,4560 }, {1087,1531,870 ,4482,4561,2274 }, - {1531,4107,1396 ,4561,4491,34 }, {3761,1463,3750 ,218,220,690 }, - {4307,4360,4306 ,4562,4556,4552 }, {4307,4382,4360 ,4562,4557,4556 }, - {4476,4475,4408 ,4558,4548,4553 }, {3977,2697,66 ,440,2006,4563 }, - {1222,4521,365 ,62,179,402 }, {4515,1651,1609 ,30,188,31 }, {260,4311,4312 ,1573,468,467 }, - {870,1531,1396 ,2274,4561,34 }, {1284,23,1063 ,4564,2272,4483 }, - {1509,377,1792 ,4565,4311,324 }, {1932,1509,1792 ,2141,4565,324 }, - {5223,5222,5182 ,3851,4566,4567 }, {1268,5301,2238 ,4568,2698,2697 }, - {1691,1101,2473 ,2326,2,4303 }, {6735,6742,3192 ,3472,3422,3063 }, - {489,2473,1101 ,3639,4303,2 }, {1868,1783,2217 ,2249,2251,4569 }, - {1783,3038,2217 ,2251,4342,4569 }, {4409,4436,4408 ,4570,4571,4553 }, - {4382,4409,4408 ,4557,4570,4553 }, {4436,4437,4408 ,4571,4572,4553 }, - {4437,3049,4476 ,4572,2803,4558 }, {4408,4437,4476 ,4553,4572,4558 }, - {4476,3049,1904 ,4558,2803,2164 }, {5834,5835,1942 ,543,4573,2337 }, - {4280,4159,413 ,1242,1299,351 }, {1837,363,380 ,997,427,2540 }, - {102,4312,4313 ,432,467,4574 }, {1079,987,1832 ,4575,2950,4576 }, - {4103,1731,604 ,4577,485,486 }, {4104,2504,3902 ,3519,2898,4437 }, - {23,1284,4100 ,2272,4564,4481 }, {2588,14,91 ,2072,1986,1984 }, - {1792,377,955 ,324,4311,3991 }, {3784,4239,4217 ,2252,2254,83 }, - {465,604,4304 ,4578,486,4579 }, {4888,4932,5379 ,3997,4580,3998 }, - {4653,4679,5182 ,4347,3852,4567 }, {8000,8023,7999 ,4581,4582,4583 }, - {4361,3938,4382 ,4584,3572,4557 }, {4307,4361,4382 ,4562,4584,4557 }, - {4382,3938,4409 ,4557,3572,4570 }, {3938,4437,4436 ,3572,4572,4571 }, - {4409,3938,4436 ,4570,3572,4571 }, {659,1730,802 ,71,4506,400 }, - {6949,6989,6947 ,4585,4586,4587 }, {2834,4961,2403 ,2860,2905,2904 }, - {1051,6,1858 ,4588,4589,4590 }, {1305,2892,3763 ,466,918,3753 }, - {1940,64,11 ,3925,297,299 }, {4356,4433,4432 ,155,4591,156 }, - {2504,2583,3902 ,2898,2672,4437 }, {4383,4311,490 ,399,468,469 }, - {210,1695,194 ,4592,471,4593 }, {4218,3784,4217 ,4503,2252,83 }, - {1695,4100,1933 ,471,4481,470 }, {4890,4918,4933 ,4594,4595,4596 }, - {3088,3132,3131 ,3290,2796,3732 }, {1091,5236,160 ,4597,4301,4295 }, - {1101,1691,1851 ,2,2326,4241 }, {4361,3939,3938 ,4584,3539,3572 }, - {3938,3112,4437 ,3572,2795,4572 }, {4437,3112,3049 ,4572,2795,2803 }, - {4253,4398,4048 ,4598,4599,4600 }, {1625,4251,4192 ,388,132,4601 }, - {4380,4452,4451 ,4602,2398,271 }, {2375,2240,3945 ,4479,4478,4550 }, - {771,1669,2707 ,3799,3798,235 }, {5986,1251,5563 ,4603,4604,4605 }, - {1976,4351,2390 ,1180,4606,1594 }, {4074,890,2959 ,4607,3283,4608 }, - {1421,1223,490 ,4609,2182,469 }, {207,4533,4493 ,4516,4515,4514 }, - {4494,207,4493 ,4610,4516,4514 }, {50,651,2709 ,4611,3591,3523 }, - {2856,3344,588 ,3821,1861,3822 }, {3938,3090,3112 ,3572,2805,2795 }, - {3112,3089,3049 ,2795,2794,2803 }, {0,4253,4048 ,4612,4598,4600 }, - {376,1564,842 ,379,368,4613 }, {5835,212,1942 ,4573,4614,2337 }, - {1633,4103,466 ,373,4577,347 }, {1051,793,6 ,4588,4200,4589 }, - {4889,4890,4933 ,4615,4594,4596 }, {5571,5643,1350 ,4616,4617,509 }, - {302,683,223 ,622,621,1985 }, {987,66,1832 ,2950,4563,4576 }, - {1748,1421,490 ,4618,4609,469 }, {1225,1223,57 ,327,2182,222 }, - {3786,1418,1934 ,4074,4619,4620 }, {275,1529,477 ,4621,4524,4519 }, - {1,4062,969 ,4622,4623,4199 }, {4080,1883,4253 ,4624,4625,4598 }, - {735,2012,1345 ,224,2212,203 }, {1462,1927,710 ,2338,1196,33 }, - {4829,4799,4770 ,4626,4627,4345 }, {5194,4395,4373 ,727,726,3288 }, - {2892,771,3763 ,918,3799,3753 }, {4192,4182,118 ,4601,435,1124 }, - {7008,7007,6967 ,3176,4628,4629 }, {275,276,1529 ,4621,4525,4524 }, - {1162,463,276 ,4630,1941,4525 }, {275,1162,276 ,4621,4630,4525 }, - {1631,504,467 ,4631,4536,346 }, {2044,558,465 ,4031,4151,4578 }, - {463,1631,467 ,1941,4631,346 }, {504,513,4330 ,4536,377,399 }, - {1070,1560,245 ,4632,4633,4488 }, {7804,7856,7803 ,4634,4635,4636 }, - {4062,500,969 ,4623,4637,4199 }, {802,439,1346 ,400,4638,484 }, - {4080,4253,316 ,4624,4598,4639 }, {5817,5818,5834 ,701,4640,543 }, - {4398,759,4242 ,4599,4641,4642 }, {1949,217,216 ,3637,4643,4644 }, - {1976,2390,1977 ,1180,1594,1503 }, {1747,1731,979 ,266,485,267 }, - {1154,513,4331 ,4545,377,4645 }, {4075,1993,344 ,4521,4544,562 }, - {4933,4918,4966 ,4596,4595,4646 }, {4239,85,4217 ,2254,2504,83 }, - {4714,4777,5267 ,1031,3917,4647 }, {1426,4062,500 ,4648,4623,4637 }, - {1377,4069,630 ,2651,1881,2491 }, {4329,4355,4380 ,1247,1447,4602 }, - {1742,1562,7051 ,707,4649,547 }, {1153,8087,6797 ,2245,385,2246 }, - {4149,4174,4148 ,1486,769,2305 }, {379,1114,1467 ,2299,2256,4535 }, - {1070,245,1163 ,4632,4488,2211 }, {4156,4222,1859 ,4555,530,4480 }, - {4502,4156,1859 ,4650,4555,4480 }, {1212,4075,447 ,4520,4521,4651 }, - {122,755,4113 ,4534,1615,207 }, {245,1560,1458 ,4488,4633,4489 }, - {1441,1092,1063 ,1485,4484,4483 }, {1308,1306,4245 ,4652,4653,4654 }, - {510,2092,191 ,4655,4656,4657 }, {759,4265,4242 ,4641,4658,4642 }, - {1858,6,217 ,4590,4589,4643 }, {4088,4124,4087 ,4659,2306,4660 }, - {4355,4356,4380 ,1447,155,4602 }, {196,607,94 ,152,2207,1858 }, - {2261,608,672 ,4661,2300,4662 }, {887,4050,1087 ,4554,4663,4482 }, - {1087,4050,1531 ,4482,4663,4561 }, {4107,4396,1932 ,4491,4664,2141 }, - {4145,660,67 ,101,387,1125 }, {4175,4218,4217 ,4500,4503,83 }, - {2660,890,4074 ,455,3283,4607 }, {512,1308,4245 ,4665,4652,4654 }, - {4279,1489,4139 ,4666,4667,4668 }, {362,4048,192 ,4669,4600,4670 }, - {215,71,81 ,4671,4672,4673 }, {5818,5835,5834 ,4640,4573,543 }, - {4124,4123,4087 ,2306,133,4660 }, {4109,4175,4149 ,576,4500,1486 }, - {1925,698,1425 ,161,4674,4518 }, {4396,377,1509 ,4664,4311,4565 }, - {1932,4396,1509 ,2141,4664,4565 }, {608,378,672 ,2300,4551,4662 }, - {754,1063,800 ,4675,4483,4676 }, {1063,1366,800 ,4483,4485,4676 }, - {1807,608,2261 ,4560,2300,4661 }, {4262,699,2 ,4492,4677,4493 }, - {4197,4258,4238 ,82,84,4678 }, {3532,4074,4057 ,4415,4607,272 }, - {209,311,648 ,4499,246,4679 }, {4141,1489,4279 ,4680,4667,4666 }, - {4398,4242,192 ,4599,4642,4670 }, {5802,5801,5765 ,4681,700,540 }, - {783,2663,688 ,679,3593,765 }, {4253,4049,4398 ,4598,4682,4599 }, - {4088,4087,630 ,4659,4660,2491 }, {4069,4088,630 ,1881,4659,2491 }, - {1308,512,1883 ,4652,4665,4625 }, {5684,4106,3345 ,4683,1742,3476 }, - {1595,4145,67 ,99,101,1125 }, {4356,4403,4380 ,155,154,4602 }, - {376,979,1731 ,379,267,485 }, {3532,2660,4074 ,4415,455,4607 }, - {4137,4095,1259 ,399,349,348 }, {124,5482,2661 ,2206,3565,4684 }, - {1284,1063,754 ,4564,4483,4675 }, {229,457,116 ,221,214,217 }, - {1695,1933,701 ,471,470,4685 }, {1933,4100,4274 ,470,4481,4686 }, - {1933,4274,701 ,470,4686,4685 }, {4257,4238,4276 ,698,4678,1446 }, - {4111,1871,1872 ,4687,2458,2459 }, {889,770,844 ,35,275,4688 }, - {4048,4398,192 ,4600,4599,4670 }, {215,81,422 ,4671,4673,4689 }, - {216,215,422 ,4644,4671,4689 }, {5766,5802,5765 ,4487,4681,540 }, - {4087,879,630 ,4660,1155,2491 }, {65,71,215 ,4690,4672,4671 }, - {1926,1308,1883 ,4691,4652,4625 }, {1462,710,4177 ,2338,33,2339 }, - {4160,2012,735 ,223,2212,224 }, {3375,3532,4057 ,4426,4415,272 }, - {1425,698,121 ,4518,4674,4504 }, {4350,3346,4098 ,740,4457,353 }, - {3585,4494,4493 ,3806,4610,4514 }, {5199,5240,5219 ,422,481,1860 }, - {476,275,477 ,4692,4621,4519 }, {2,658,265 ,4493,4693,4496 }, - {4137,1259,4095 ,399,348,349 }, {4251,4470,4182 ,132,4694,435 }, - {4352,3299,3990 ,4695,3232,3231 }, {345,1489,4119 ,4696,4667,4697 }, - {362,192,71 ,4669,4670,4672 }, {1306,655,345 ,4653,1122,4696 }, - {3324,3304,3305 ,881,1050,879 }, {4098,3375,4057 ,353,4426,272 }, - {1935,3172,4289 ,4098,4155,4152 }, {275,318,463 ,4621,4698,1941 }, - {1162,275,463 ,4630,4621,1941 }, {1695,701,194 ,471,4685,4593 }, - {463,318,1631 ,1941,4698,4631 }, {504,4118,513 ,4536,4699,377 }, - {1113,117,4213 ,10,9,1070 }, {4117,4156,4502 ,4700,4555,4650 }, - {4117,4157,4156 ,4700,3755,4555 }, {4252,887,4156 ,4021,4554,4555 }, - {3141,4127,4176 ,4701,4702,4703 }, {4227,5237,5307 ,1564,1800,461 }, - {443,444,702 ,920,922,2699 }, {2255,939,4350 ,3229,3354,740 }, - {683,196,223 ,621,152,1985 }, {345,4161,1489 ,4696,4364,4667 }, - {4161,4210,1489 ,4364,4704,4667 }, {5801,5802,5817 ,700,4681,701 }, - {65,362,71 ,4690,4669,4672 }, {2859,31,4139 ,3451,3450,4668 }, - {824,4245,2092 ,4705,4654,4656 }, {4245,1306,345 ,4654,4653,4696 }, - {4177,889,379 ,2339,35,2299 }, {265,4539,697 ,4496,633,3578 }, - {4157,4252,4156 ,3755,4021,4555 }, {4252,4102,887 ,4021,4706,4554 }, - {4155,699,4262 ,4505,4677,4492 }, {5237,2086,1525 ,1800,1802,1866 }, - {4102,4050,887 ,4706,4663,4554 }, {1531,1530,4107 ,4561,4707,4491 }, - {1530,4314,4396 ,4707,1864,4664 }, {4107,1530,4396 ,4491,4707,4664 }, - {4058,4061,3347 ,4708,4490,2098 }, {4081,1079,1926 ,4709,4575,4691 }, - {759,824,2092 ,4641,4705,4656 }, {4279,4139,563 ,4666,4668,4710 }, - {0,4048,362 ,4612,4600,4669 }, {4062,1426,500 ,4623,4648,4637 }, - {3317,4069,1377 ,1882,1881,2651 }, {3452,987,1079 ,1794,2950,4575 }, - {416,466,2091 ,556,347,4494 }, {466,604,2091 ,347,486,4494 }, - {3622,1935,4289 ,4106,4098,4152 }, {4201,4181,4051 ,436,2273,437 }, - {4314,2086,377 ,1864,1802,4311 }, {4396,4314,377 ,4664,1864,4311 }, - {447,4075,344 ,4651,4521,562 }, {4977,4999,4998 ,4711,4712,4713 }, - {4276,4302,4356 ,1446,3727,155 }, {6563,2343,8061 ,464,2685,453 }, - {5573,5237,1525 ,2217,1800,1866 }, {558,416,465 ,4151,556,4578 }, - {4109,4090,4076 ,576,1487,577 }, {1467,4084,755 ,4535,205,1615 }, - {66,2697,45 ,4563,2006,4714 }, {3532,3617,3050 ,4415,4425,4414 }, - {21,3946,1089 ,1231,1230,103 }, {133,284,1241 ,1719,139,313 }, - {2695,2859,4210 ,3453,3451,4704 }, {4161,2695,4210 ,4364,3453,4704 }, - {759,2092,510 ,4641,4656,4655 }, {5802,5818,5817 ,4681,4640,701 }, - {510,191,263 ,4655,4657,4715 }, {71,27,81 ,4672,303,4673 }, {4087,4123,879 ,4660,133,1155 }, - {4062,1426,4082 ,4623,4648,4716 }, {502,604,465 ,2301,486,4578 }, - {4058,4065,4061 ,4708,4717,4490 }, {279,325,4691 ,2176,2088,2089 }, - {1163,4160,1223 ,2211,223,2182 }, {4151,510,263 ,839,4655,4715 }, - {175,4262,702 ,211,4492,2699 }, {1619,3196,1686 ,1003,2581,1004 }, - {192,4242,43 ,4670,4642,304 }, {4312,490,1225 ,467,469,327 }, - {413,4159,4235 ,351,1299,985 }, {4082,3452,1079 ,4716,1794,4575 }, - {4238,4258,4302 ,4678,84,3727 }, {193,864,867 ,1448,840,1121 }, - {4079,4113,1212 ,4718,207,4520 }, {672,378,122 ,4662,4551,4534 }, - {4824,1602,4886 ,4719,2329,4720 }, {84,3759,2030 ,4231,4233,770 }, - {2995,2254,2702 ,2856,3705,2854 }, {4400,4450,3585 ,4721,4722,3806 }, - {637,4400,3585 ,3805,4721,3806 }, {4495,4494,3585 ,4723,4610,3806 }, - {4450,4495,3585 ,4722,4723,3806 }, {4238,4302,4276 ,4678,3727,1446 }, - {4257,4238,4276 ,698,4678,1446 }, {4258,4303,4302 ,84,3728,3727 }, - {1150,683,681 ,741,621,620 }, {2697,2255,45 ,2006,3229,4714 }, - {1006,962,1100 ,234,233,2466 }, {4101,1600,209 ,4724,4498,4499 }, - {192,43,27 ,4670,304,303 }, {4059,4193,609 ,363,4725,4726 }, - {1195,4347,4395 ,725,3287,726 }, {1150,774,683 ,741,767,621 }, - {4139,31,4078 ,4668,3450,4341 }, {7799,7825,7798 ,4727,3768,4728 }, - {4779,4778,4716 ,4729,3916,1030 }, {122,4113,4079 ,4534,207,4718 }, - {841,122,4079 ,4730,4534,4718 }, {4065,1253,4067 ,4717,2353,185 }, - {756,1748,490 ,4731,4618,469 }, {4311,756,490 ,468,4731,469 }, - {4494,4495,207 ,4610,4723,4516 }, {1366,1807,4133 ,4485,4560,4732 }, - {4263,2420,4056 ,274,273,1123 }, {38,1150,681 ,742,741,620 }, - {4062,1,4063 ,4623,4622,4733 }, {808,4472,5295 ,4734,2189,2188 }, - {71,192,27 ,4672,4670,303 }, {4051,1695,210 ,437,471,4592 }, - {5681,5700,5699 ,2934,4501,43 }, {4071,4070,4064 ,186,4497,1880 }, - {4335,4265,4151 ,838,4658,839 }, {4242,642,43 ,4642,1243,304 }, - {3347,206,4058 ,2098,3932,4708 }, {4121,4139,4078 ,4735,4668,4341 }, - {0,362,65 ,4612,4669,4690 }, {3271,8125,8117 ,1727,752,4119 }, - {216,65,215 ,4644,4690,4671 }, {5618,5420,103 ,127,4228,128 }, - {1931,4644,5019 ,4736,2425,2384 }, {4067,4071,4065 ,185,186,4717 }, - {240,4724,1790 ,1571,4737,4738 }, {260,1210,4311 ,1573,4739,468 }, - {4183,1210,260 ,1572,4739,1573 }, {1070,4073,1560 ,4632,4740,4633 }, - {1730,4262,175 ,4506,4492,211 }, {476,477,207 ,4692,4519,4516 }, - {1134,476,207 ,4741,4692,4516 }, {800,1366,4133 ,4676,4485,4732 }, - {1441,4093,1092 ,1485,1484,4484 }, {4182,661,4201 ,435,2297,436 }, - {4470,880,661 ,4694,1483,2297 }, {4242,4265,642 ,4642,4658,1243 }, - {770,805,844 ,275,389,4688 }, {208,4114,1259 ,4742,362,348 }, - {143,2043,781 ,157,4100,158 }, {1421,1163,1223 ,4609,2211,2182 }, - {4313,2423,102 ,4574,433,432 }, {4265,4335,642 ,4658,838,1243 }, - {1481,654,231 ,194,93,92 }, {759,510,4151 ,4641,4655,839 }, {5986,5563,4550 ,4603,4605,716 }, - {4148,4123,4124 ,2305,133,2306 }, {322,46,5 ,1250,2232,350 }, - {4942,5420,5618 ,4191,4228,127 }, {1092,1462,4116 ,4484,2338,2340 }, - {4376,4073,1070 ,4743,4740,4632 }, {1560,2001,1458 ,4633,4744,4489 }, - {4116,4177,379 ,2340,2339,2299 }, {4376,1421,1748 ,4743,4609,4618 }, - {275,129,318 ,4621,730,4698 }, {4274,4100,72 ,4686,4481,4745 }, - {4376,1163,1421 ,4743,2211,4609 }, {4093,1462,1092 ,1484,2338,4484 }, - {5420,84,103 ,4228,4231,128 }, {5162,4942,5618 ,181,4191,127 }, - {4491,4519,226 ,107,4746,108 }, {1926,1832,1308 ,4691,4576,4652 }, - {4049,512,824 ,4682,4665,4705 }, {1992,214,781 ,834,87,158 }, - {3132,3088,3089 ,2796,3290,2794 }, {21,1089,5203 ,1231,103,102 }, - {614,609,1367 ,1397,4726,574 }, {4059,609,614 ,363,4726,1397 }, - {4265,759,4151 ,4658,4641,839 }, {802,1730,4130 ,400,4506,210 }, - {608,379,378 ,2300,2299,4551 }, {824,512,4245 ,4705,4665,4654 }, - {3577,531,3369 ,4059,3829,3836 }, {4403,4432,4452 ,154,156,2398 }, - {4148,4173,4123 ,2305,134,133 }, {4196,4195,4173 ,4747,4748,134 }, - {4540,891,4526 ,19,189,187 }, {1163,4376,1070 ,2211,4743,4632 }, - {4274,72,701 ,4686,4745,4685 }, {1631,753,504 ,4631,4749,4536 }, - {504,753,4118 ,4536,4749,4699 }, {4134,658,2 ,4750,4693,4493 }, - {4100,1284,1368 ,4481,4564,4751 }, {1458,825,1925 ,4489,4752,161 }, - {3562,5944,3943 ,4753,4754,4755 }, {4394,4273,4796 ,4044,120,4756 }, - {1445,1469,1397 ,2359,2361,2358 }, {825,698,1925 ,4752,4674,161 }, - {4100,1368,72 ,4481,4751,4745 }, {4081,1926,4080 ,4709,4691,4624 }, - {844,805,1115 ,4688,389,2257 }, {1345,2012,1846 ,203,2212,204 }, - {705,4132,659 ,70,162,71 }, {4151,263,208 ,839,4715,4742 }, {4504,4151,208 ,4757,839,4742 }, - {1833,4151,4504 ,317,839,4757 }, {111,3014,258 ,1154,3568,3001 }, - {1625,4192,660 ,388,4601,387 }, {316,4253,0 ,4639,4598,4612 }, - {4380,4403,4452 ,4602,154,2398 }, {1308,1832,1306 ,4652,4576,4653 }, - {4148,4196,4173 ,2305,4747,134 }, {3946,2893,1089 ,1230,1679,103 }, - {214,466,142 ,87,347,159 }, {4076,4090,4070 ,577,1487,4497 }, - {262,194,312 ,4758,4593,4759 }, {4083,4102,4252 ,3754,4706,4021 }, - {670,4144,5043 ,4760,4761,4762 }, {1459,4050,4102 ,4017,4663,4706 }, - {4083,1459,4102 ,3754,4017,4706 }, {1459,4066,4050 ,4017,4763,4663 }, - {658,4243,265 ,4693,4764,4496 }, {376,1731,4103 ,379,485,4577 }, - {1926,1883,4080 ,4691,4625,4624 }, {4051,4181,1933 ,437,2273,470 }, - {2092,4245,4141 ,4656,4654,4680 }, {4093,4234,1462 ,1484,1197,2338 }, - {4234,1927,1462 ,1197,1196,2338 }, {563,4139,4121 ,4710,4668,4735 }, - {2031,2122,2067 ,1560,1128,1592 }, {81,27,545 ,4673,303,305 }, - {104,774,1150 ,4765,767,741 }, {4280,4335,1833 ,1242,838,317 }, - {4195,4216,4173 ,4748,1241,134 }, {979,1595,67 ,267,99,1125 }, - {702,444,649 ,2699,922,1579 }, {4243,317,265 ,4764,561,4496 }, - {4066,1459,1531 ,4763,4017,4561 }, {4050,4066,1531 ,4663,4763,4561 }, - {1459,1561,1530 ,4017,1865,4707 }, {1531,1459,1530 ,4561,4017,4707 }, - {266,699,4155 ,4766,4677,4505 }, {121,266,4155 ,4504,4766,4505 }, - {21,5203,2030 ,1231,102,770 }, {3476,1941,3568 ,3838,1034,3839 }, - {1530,1561,4314 ,4707,1865,1864 }, {4174,4175,4217 ,769,4500,83 }, - {3532,538,2660 ,4415,4380,455 }, {5643,5642,1350 ,4617,526,509 }, - {655,4056,345 ,1122,1123,4696 }, {4141,4119,1489 ,4680,4697,4667 }, - {81,545,323 ,4673,305,4767 }, {4065,4071,4064 ,4717,186,1880 }, - {4081,4080,500 ,4709,4624,4637 }, {963,786,962 ,1341,2071,233 }, - {2536,3017,49 ,2793,3369,3367 }, {191,4279,4114 ,4657,4666,362 }, - {263,191,4114 ,4715,4657,362 }, {793,316,0 ,4200,4639,4612 }, - {4978,6414,6413 ,178,113,112 }, {2502,2331,2504 ,3740,2895,2898 }, - {4067,4068,4077 ,185,408,126 }, {702,2,443 ,2699,4493,920 }, - {699,4134,2 ,4677,4750,4493 }, {6351,8103,8077 ,1568,1291,2078 }, - {416,502,465 ,556,2301,4578 }, {604,1747,4304 ,486,266,4579 }, - {885,321,2313 ,216,60,438 }, {1591,1032,1318 ,1072,1581,164 }, - {4201,661,4181 ,436,2297,2273 }, {2707,1669,5162 ,235,3798,181 }, - {1489,4210,4139 ,4667,4704,4668 }, {4234,4093,880 ,1197,1484,1483 }, - {191,4141,4279 ,4657,4680,4666 }, {208,263,4114 ,4742,4715,362 }, - {1331,1411,2041 ,790,850,2737 }, {5839,5807,5840 ,4768,4769,4770 }, - {1333,1936,3300 ,849,848,694 }, {5288,5308,4868 ,3993,3528,3969 }, - {5970,4146,2504 ,3518,3730,2898 }, {609,1919,1367 ,4726,1779,574 }, - {609,4078,1919 ,4726,4341,1779 }, {4195,4237,4216 ,4748,697,1241 }, - {4347,3867,1851 ,3287,4507,4241 }, {793,0,6 ,4200,4612,4589 }, - {4237,4275,4256 ,697,699,3433 }, {500,4080,316 ,4637,4624,4639 }, - {45,195,655 ,4714,739,1122 }, {8096,4031,2474 ,1796,3069,874 }, - {8110,8125,3271 ,1140,752,1727 }, {2558,1066,19 ,977,680,1067 }, - {318,129,695 ,4698,730,731 }, {447,344,317 ,4651,562,561 }, {265,317,4539 ,4496,561,633 }, - {1186,1623,1624 ,1605,1709,1708 }, {658,1212,447 ,4693,4520,4651 }, - {4079,1212,658 ,4718,4520,4693 }, {94,607,82 ,1858,2207,138 }, - {315,413,4235 ,352,351,985 }, {1669,4942,5162 ,3798,4191,181 }, - {4056,4161,345 ,1123,4364,4696 }, {43,642,739 ,304,1243,2233 }, - {934,880,4470 ,131,1483,4694 }, {653,1259,4053 ,318,348,1371 }, - {49,3017,1015 ,3367,3369,3368 }, {805,24,1115 ,389,247,2257 }, - {5240,5284,5264 ,481,571,1192 }, {1532,217,1949 ,4771,4643,3637 }, - {500,316,793 ,4637,4639,4200 }, {969,500,793 ,4199,4637,4200 }, - {147,792,4712 ,3469,95,3816 }, {979,67,970 ,267,1125,268 }, {4251,4182,4192 ,132,435,4601 }, - {4375,1591,981 ,312,1072,2128 }, {4400,4401,4450 ,4721,4772,4722 }, - {1730,4155,4262 ,4506,4505,4492 }, {1633,376,4103 ,373,379,4577 }, - {664,760,663 ,1755,3106,4773 }, {672,122,266 ,4662,4534,4766 }, - {1179,207,4495 ,4774,4516,4723 }, {1210,261,4311 ,4739,4775,468 }, - {294,4375,1132 ,945,312,61 }, {4779,4803,4778 ,4729,4776,3916 }, - {237,99,100 ,869,3743,2823 }, {4095,614,1805 ,349,1397,1071 }, - {6122,8096,2072 ,1797,1796,1812 }, {180,1946,4385 ,4777,4408,4778 }, - {3299,4352,4400 ,3232,4695,4721 }, {1131,1728,1015 ,4779,4780,3368 }, - {89,1131,1015 ,1482,4779,3368 }, {1728,408,1015 ,4780,4781,3368 }, - {7,49,1015 ,4782,3367,3368 }, {408,7,1015 ,4781,4782,3368 }, - {4114,4279,563 ,362,4666,4710 }, {2709,7,50 ,3523,4782,4611 }, - {842,1564,1595 ,4613,368,99 }, {4049,759,4398 ,4682,4641,4599 }, - {1665,456,455 ,3077,301,2205 }, {2866,2867,1205 ,2655,2844,3187 }, - {806,706,843 ,4783,4784,2423 }, {1132,4375,981 ,61,312,2128 }, - {266,122,841 ,4766,4534,4730 }, {1179,1134,207 ,4774,4741,4516 }, - {4133,1807,672 ,4732,4560,4662 }, {1601,476,1134 ,4785,4692,4741 }, - {1179,1601,1134 ,4774,4785,4741 }, {6643,8122,8101 ,3133,384,383 }, - {3452,3513,3977 ,1794,441,440 }, {1284,754,4073 ,4564,4675,4740 }, - {476,129,275 ,4692,730,4621 }, {1368,1284,4073 ,4751,4564,4740 }, - {4706,4763,4704 ,4786,4787,4788 }, {4431,4380,4451 ,568,4602,271 }, - {1066,4336,19 ,680,4789,1067 }, {4336,4384,600 ,4789,4409,978 }, - {19,4336,600 ,1067,4789,978 }, {4063,1426,4062 ,4733,4648,4623 }, - {260,4312,102 ,1573,467,432 }, {8,4505,1798 ,3341,4790,4451 }, - {4505,1799,1798 ,4790,4791,4451 }, {1799,1131,89 ,4791,4779,1482 }, - {1798,1799,89 ,4451,4791,1482 }, {176,408,1728 ,4792,4781,4780 }, - {1131,176,1728 ,4779,4792,4780 }, {4114,563,4059 ,362,4710,363 }, - {3308,3294,7110 ,3894,3895,1990 }, {4059,563,4193 ,363,4710,4725 }, - {563,4121,4193 ,4710,4735,4725 }, {1595,1564,4145 ,99,368,101 }, - {1092,4116,1424 ,4484,2340,2298 }, {318,753,1631 ,4698,4749,4631 }, - {1807,2261,672 ,4560,4661,4662 }, {261,4333,4311 ,4775,4793,468 }, - {1368,4073,4376 ,4751,4740,4743 }, {5458,4129,4083 ,3756,3812,3754 }, - {226,4519,1150 ,108,4746,741 }, {4177,710,889 ,2339,33,35 }, - {4315,4337,4336 ,4794,4795,4789 }, {1066,4315,4336 ,680,4794,4789 }, - {4337,4385,4384 ,4795,4778,4409 }, {4336,4337,4384 ,4789,4795,4409 }, - {180,4455,8 ,4777,4796,3341 }, {4455,4505,8 ,4796,4790,3341 }, - {1797,176,1131 ,4797,4792,4779 }, {1799,1797,1131 ,4791,4797,4779 }, - {782,786,777 ,1648,2071,421 }, {4121,4078,609 ,4735,4341,4726 }, - {759,4049,824 ,4641,4682,4705 }, {4311,4333,756 ,468,4793,4731 }, - {379,844,1114 ,2299,4688,2256 }, {2070,1095,614 ,573,1910,1397 }, - {4238,4257,4276 ,4678,698,1446 }, {4083,4129,1459 ,3754,3812,4017 }, - {4129,3477,1459 ,3812,1736,4017 }, {800,4133,1458 ,4676,4732,4489 }, - {1560,800,2001 ,4633,4676,4744 }, {69,25,1319 ,321,2186,322 }, - {118,4051,210 ,1124,437,4592 }, {4315,1450,4337 ,4794,823,4795 }, - {4385,4413,180 ,4778,4798,4777 }, {4413,4455,180 ,4798,4796,4777 }, - {4506,1797,1799 ,4799,4797,4791 }, {4505,4506,1799 ,4790,4799,4791 }, - {573,823,408 ,4800,4801,4781 }, {176,573,408 ,4792,4800,4781 }, - {50,7,408 ,4611,4782,4781 }, {823,50,408 ,4801,4611,4781 }, {2092,4141,191 ,4656,4680,4657 }, - {4193,4121,609 ,4725,4735,4726 }, {934,4234,880 ,131,1197,1483 }, - {4245,4119,4141 ,4654,4697,4680 }, {1926,1079,1832 ,4691,4575,4576 }, - {3985,789,3858 ,4802,3910,4803 }, {66,1211,2697 ,4563,2007,2006 }, - {4085,1368,4376 ,4804,4751,4743 }, {4149,4148,4090 ,1486,2305,1487 }, - {4085,4376,1748 ,4804,4743,4618 }, {4237,4238,4257 ,697,4678,698 }, - {2001,800,1458 ,4744,4676,4489 }, {4276,4356,4355 ,1446,155,1447 }, - {4108,4124,4088 ,2304,2306,4659 }, {7029,2341,3008 ,548,845,546 }, - {3763,771,2707 ,3753,3799,235 }, {1808,1836,380 ,4502,2490,2540 }, - {4337,4338,4385 ,4795,4805,4778 }, {4385,4338,4413 ,4778,4805,4798 }, - {4456,4506,4505 ,4806,4799,4790 }, {4455,4456,4505 ,4796,4806,4790 }, - {4506,895,1797 ,4799,4807,4797 }, {895,573,176 ,4807,4800,4792 }, - {1797,895,176 ,4797,4807,4792 }, {73,50,823 ,4808,4611,4801 }, - {1621,73,823 ,4809,4808,4801 }, {148,651,50 ,4810,3591,4611 }, - {73,148,50 ,4808,4810,4611 }, {212,1208,1942 ,4614,3211,2337 }, - {4251,934,4470 ,132,131,4694 }, {6,0,65 ,4589,4612,4690 }, {884,4059,614 ,2160,363,1397 }, - {1259,4059,4095 ,348,363,349 }, {1306,45,655 ,4653,4714,1122 }, - {1467,1114,4084 ,4535,2256,205 }, {3872,5188,5786 ,4811,4812,4813 }, - {4197,4238,4196 ,82,4678,4747 }, {379,889,844 ,2299,35,4688 }, - {842,1595,979 ,4613,99,267 }, {261,312,756 ,4775,4759,4731 }, - {312,194,756 ,4759,4593,4731 }, {1458,4133,825 ,4489,4732,4752 }, - {4238,4237,4196 ,4678,697,4747 }, {43,739,545 ,304,2233,305 }, - {2650,4031,8096 ,914,3069,1796 }, {4316,4338,4337 ,4814,4805,4795 }, - {1450,4316,4337 ,823,4814,4795 }, {4414,4456,4455 ,4815,4806,4796 }, - {4413,4414,4455 ,4798,4815,4796 }, {4456,4507,4506 ,4806,4816,4799 }, - {4506,4507,895 ,4799,4816,4807 }, {452,1621,823 ,4817,4809,4801 }, - {573,452,823 ,4800,4817,4801 }, {73,2254,148 ,4808,3705,4810 }, - {4373,4347,1851 ,3288,3287,4241 }, {217,6,216 ,4643,4589,4644 }, - {6,65,216 ,4589,4690,4644 }, {4210,2859,4139 ,4704,3451,4668 }, - {4071,4076,4070 ,186,577,4497 }, {4084,2047,1600 ,205,2258,4498 }, - {455,4471,259 ,2205,2187,2203 }, {194,701,756 ,4593,4685,4731 }, - {698,266,121 ,4674,4766,4504 }, {918,416,558 ,4150,556,4151 }, - {4077,4125,4109 ,126,125,576 }, {4144,670,261 ,4761,4760,4775 }, - {670,312,261 ,4760,4759,4775 }, {1114,2047,4084 ,2256,2258,205 }, - {4313,4312,1225 ,4574,467,327 }, {1148,4132,705 ,160,162,70 }, - {259,4471,258 ,2203,2187,3001 }, {1340,4266,1450 ,822,4818,823 }, - {1450,4266,4316 ,823,4818,4814 }, {2254,2995,148 ,3705,2856,4810 }, - {4363,4414,4413 ,4819,4815,4798 }, {4338,4363,4413 ,4805,4819,4798 }, - {4415,4457,4456 ,4820,4821,4806 }, {4414,4415,4456 ,4815,4820,4806 }, - {4456,4457,4507 ,4806,4821,4816 }, {895,452,573 ,4807,4817,4800 }, - {752,2254,73 ,4822,3705,4808 }, {8086,8087,1153 ,4823,385,2245 }, - {4171,659,802 ,399,71,400 }, {4245,345,4119 ,4654,4696,4697 }, - {4135,1259,4179 ,1069,348,251 }, {4159,4280,1833 ,1299,1242,317 }, - {3793,4305,4357 ,3420,4512,4508 }, {664,663,568 ,1755,4773,1723 }, - {4149,4175,4174 ,1486,4500,769 }, {5136,5178,4630 ,1817,524,827 }, - {4089,4088,4069 ,4495,4659,1881 }, {4070,4089,4069 ,4497,4495,1881 }, - {3978,6679,6418 ,4549,4824,3441 }, {4084,4101,1178 ,205,4724,206 }, - {4084,1600,4101 ,205,4498,4724 }, {2,461,443 ,4493,399,920 }, - {660,4192,118 ,387,4601,1124 }, {3615,5681,5660 ,201,2934,3054 }, - {1090,4266,1340 ,2687,4818,822 }, {4266,4317,4316 ,4818,4825,4814 }, - {4317,4363,4338 ,4825,4819,4805 }, {4316,4317,4338 ,4814,4825,4805 }, - {4364,4415,4414 ,4826,4820,4815 }, {4363,4364,4414 ,4819,4826,4815 }, - {4507,1769,895 ,4816,4827,4807 }, {1769,1084,895 ,4827,4828,4807 }, - {1084,246,452 ,4828,4829,4817 }, {895,1084,452 ,4807,4828,4817 }, - {246,1654,1621 ,4829,4830,4809 }, {452,246,1621 ,4817,4829,4809 }, - {1654,73,1621 ,4830,4808,4809 }, {1654,167,73 ,4830,4831,4808 }, - {167,752,73 ,4831,4822,4808 }, {2659,8120,3286 ,3485,906,56 }, - {5182,5181,4631 ,4567,4319,1816 }, {1883,512,4049 ,4625,4665,4682 }, - {4446,2607,1300 ,237,2452,285 }, {4089,4108,4088 ,4495,2304,4659 }, - {5158,5182,4631 ,4832,4567,1816 }, {7,2709,49 ,4782,3523,3367 }, - {3039,2040,1090 ,2686,4833,2687 }, {323,545,46 ,4767,305,2232 }, - {4352,4401,4400 ,4695,4772,4721 }, {2043,11,781 ,4100,299,158 }, - {2040,537,1090 ,4833,4834,2687 }, {537,4267,4266 ,4834,4835,4818 }, - {1090,537,4266 ,2687,4834,4818 }, {4266,4267,4317 ,4818,4835,4825 }, - {4318,4364,4363 ,4836,4826,4819 }, {4317,4318,4363 ,4825,4836,4819 }, - {4457,4523,4507 ,4821,4837,4816 }, {4523,1084,1769 ,4837,4828,4827 }, - {4507,4523,1769 ,4816,4837,4827 }, {1589,167,1654 ,4838,4831,4830 }, - {2276,2254,752 ,4839,3705,4822 }, {167,2276,752 ,4831,4839,4822 }, - {123,48,1049 ,3607,4840,1362 }, {2276,3117,2254 ,4839,3407,3705 }, - {1883,4049,4253 ,4625,4682,4598 }, {8063,8061,8102 ,465,453,253 }, - {130,95,1798 ,2341,3482,4451 }, {642,4280,413 ,1243,1242,351 }, - {699,841,4079 ,4677,4730,4718 }, {825,4133,698 ,4752,4732,4674 }, - {67,118,359 ,1125,1124,4841 }, {67,359,970 ,1125,4841,268 }, - {4133,672,698 ,4732,4662,4674 }, {136,1179,4495 ,1685,4774,4723 }, - {5182,5180,5181 ,4567,1878,4319 }, {5197,1928,4518 ,4842,1282,1281 }, - {4267,4318,4317 ,4835,4836,4825 }, {4415,4458,4457 ,4820,4843,4821 }, - {4458,4477,4457 ,4843,4844,4821 }, {4457,4477,4523 ,4821,4844,4837 }, - {246,641,1654 ,4829,4845,4830 }, {641,1589,1654 ,4845,4838,4830 }, - {354,2276,167 ,4846,4839,4831 }, {2379,3117,2276 ,3238,3407,4839 }, - {354,2379,2276 ,4846,3238,4839 }, {4275,4276,4329 ,699,1446,1247 }, - {1600,24,311 ,4498,247,246 }, {1115,24,1600 ,2257,247,4498 }, - {2314,2407,6467 ,3742,4001,4000 }, {659,1425,1730 ,71,4518,4506 }, - {118,210,359 ,1124,4592,4841 }, {266,841,699 ,4766,4730,4677 }, - {1601,76,476 ,4785,689,4692 }, {1925,1425,659 ,161,4518,71 }, - {76,1632,476 ,689,2241,4692 }, {476,1632,129 ,4692,2241,730 }, - {462,318,695 ,4847,4698,731 }, {4470,661,4182 ,4694,2297,435 }, - {7479,7478,7422 ,4423,1402,1404 }, {537,4268,4267 ,4834,4848,4835 }, - {4267,4268,4318 ,4835,4848,4836 }, {4364,4386,4415 ,4826,4849,4820 }, - {4386,4438,4415 ,4849,4850,4820 }, {4415,4438,4458 ,4820,4850,4843 }, - {4458,4438,4477 ,4843,4850,4844 }, {1084,854,246 ,4828,4851,4829 }, - {854,641,246 ,4851,4845,4829 }, {4451,4452,4498 ,271,2398,88 }, - {4196,4237,4195 ,4747,697,4748 }, {376,842,979 ,379,4613,267 }, - {4216,4237,4256 ,1241,697,3433 }, {4504,208,653 ,4757,4742,318 }, - {323,46,706 ,4767,2232,4784 }, {698,672,266 ,4674,4662,4766 }, - {4073,754,1560 ,4740,4675,4633 }, {8095,7983,8062 ,4362,4361,3272 }, - {753,462,695 ,4749,4847,731 }, {318,462,753 ,4698,4847,4749 }, - {3618,735,3535 ,37,224,442 }, {1245,735,3618 ,36,224,37 }, {4130,1730,175 ,210,4506,211 }, - {4339,4340,4364 ,4852,4853,4826 }, {4318,4339,4364 ,4836,4852,4826 }, - {4340,4386,4364 ,4853,4849,4826 }, {4523,1770,1084 ,4837,4854,4828 }, - {1770,854,1084 ,4854,4851,4828 }, {894,10,641 ,4855,4856,4845 }, - {854,894,641 ,4851,4855,4845 }, {1878,1589,641 ,4857,4838,4845 }, - {10,1878,641 ,4856,4857,4845 }, {1392,167,1589 ,4858,4831,4838 }, - {1878,1392,1589 ,4857,4858,4838 }, {1392,354,167 ,4858,4846,4831 }, - {537,6199,4268 ,4834,4859,4848 }, {214,142,781 ,87,159,158 }, - {8089,8082,8097 ,254,454,1710 }, {46,322,843 ,2232,1250,2423 }, - {4053,1259,4135 ,1371,348,1069 }, {1833,4504,653 ,317,4757,318 }, - {1163,245,2012 ,2211,4488,2212 }, {1424,4116,379 ,2298,2340,2299 }, - {615,614,1095 ,766,1397,1910 }, {4065,4064,4061 ,4717,1880,4490 }, - {148,2995,651 ,4810,2856,3591 }, {4132,1925,659 ,162,161,71 }, - {754,800,1560 ,4675,4676,4633 }, {1808,4111,1872 ,4502,4687,2459 }, - {4268,4269,4318 ,4848,4860,4836 }, {4269,4340,4339 ,4860,4853,4852 }, - {4318,4269,4339 ,4836,4860,4852 }, {4477,1770,4523 ,4844,4854,4837 }, - {1770,1737,4523 ,4854,4861,4837 }, {4523,1737,1770 ,4837,4861,4854 }, - {1737,894,854 ,4861,4855,4851 }, {1770,1737,854 ,4854,4861,4851 }, - {1086,354,1392 ,4862,4846,4858 }, {4163,2379,354 ,4863,3238,4846 }, - {1086,4163,354 ,4862,4863,4846 }, {5334,4887,5376 ,2736,2735,2758 }, - {5950,8074,8066 ,2873,2460,960 }, {4765,4764,4706 ,3983,3982,4786 }, - {4095,4059,884 ,349,363,2160 }, {4182,4051,118 ,435,437,1124 }, - {5182,5221,5180 ,4567,4864,1878 }, {709,653,544 ,316,318,195 }, - {341,632,2193 ,1852,1380,1007 }, {735,1345,3535 ,224,203,442 }, - {72,1368,4085 ,4745,4751,4804 }, {3477,407,1459 ,1736,4018,4017 }, - {4773,4844,4804 ,4865,4866,4867 }, {1835,1871,1870 ,2421,2458,4868 }, - {5377,5411,5376 ,361,4284,2758 }, {4269,4284,4340 ,4860,4869,4853 }, - {4438,4478,4477 ,4850,4870,4844 }, {4477,4478,1770 ,4844,4870,4854 }, - {1770,4478,1737 ,4854,4870,4861 }, {693,1878,10 ,4871,4857,4856 }, - {693,1392,1878 ,4871,4858,4857 }, {213,4163,1086 ,3606,4863,4862 }, - {102,240,260 ,432,1571,1573 }, {4074,2959,4057 ,4607,4608,272 }, - {2012,245,1846 ,2212,4488,204 }, {683,396,196 ,621,153,152 }, - {245,1148,1846 ,4488,160,204 }, {226,111,1653 ,108,1154,106 }, - {379,1467,378 ,2299,4535,4551 }, {4315,1066,1450 ,4794,680,823 }, - {5243,5242,5180 ,3994,3843,1878 }, {6225,4321,5630 ,4872,4873,4874 }, - {701,72,1748 ,4685,4745,4618 }, {72,4085,1748 ,4745,4804,4618 }, - {5814,5815,5831 ,4875,4065,3943 }, {5799,5815,5814 ,4064,4065,4875 }, - {5798,5799,5814 ,4876,4064,4875 }, {5815,5832,5831 ,4065,3904,3943 }, - {3945,3978,6418 ,4550,4549,3441 }, {4229,4284,4269 ,4877,4869,4860 }, - {4387,4439,4438 ,4878,4879,4850 }, {4386,4387,4438 ,4849,4878,4850 }, - {4438,4439,4478 ,4850,4879,4870 }, {942,693,10 ,4880,4871,4856 }, - {894,942,10 ,4855,4880,4856 }, {228,1086,1392 ,4881,4862,4858 }, - {228,213,1086 ,4881,3606,4862 }, {1806,439,802 ,3124,4638,400 }, - {2431,1304,2476 ,2977,2639,2441 }, {5221,5243,5180 ,4864,3994,1878 }, - {4379,4380,4431 ,567,4602,568 }, {5265,5241,5242 ,4882,2120,3843 }, - {4158,802,1346 ,72,400,484 }, {2041,553,1331 ,2737,792,790 }, - {6346,7254,7860 ,4883,450,452 }, {697,113,59 ,3578,677,967 }, - {5243,5265,5242 ,3994,4882,3843 }, {600,4384,131 ,978,4409,2965 }, - {705,4537,1093 ,70,67,66 }, {1846,705,1093 ,204,70,66 }, {4174,4197,4148 ,769,82,2305 }, - {261,756,4333 ,4775,4731,4793 }, {5763,5799,5798 ,4212,4064,4876 }, - {5762,5763,5798 ,4884,4212,4876 }, {1220,875,1920 ,4885,579,581 }, - {7076,1076,7075 ,4886,4887,4420 }, {4284,4341,4340 ,4869,4888,4853 }, - {4341,4387,4386 ,4888,4878,4849 }, {4340,4341,4386 ,4853,4888,4849 }, - {4387,4416,4439 ,4878,4889,4879 }, {1738,942,894 ,4890,4880,4855 }, - {1737,1738,894 ,4861,4890,4855 }, {942,33,693 ,4880,4891,4871 }, - {1856,1392,693 ,4892,4858,4871 }, {33,1856,693 ,4891,4892,4871 }, - {1856,1457,1392 ,4892,4893,4858 }, {1457,228,1392 ,4893,4881,4858 }, - {3447,64,1940 ,3928,297,3925 }, {48,123,213 ,4840,3607,3606 }, - {5287,5286,5241 ,4003,3859,2120 }, {4144,261,1210 ,4761,4775,4739 }, - {1973,4685,4906 ,551,4894,4895 }, {5265,5287,5241 ,4882,4003,2120 }, - {5874,6090,6066 ,4896,4897,1455 }, {2390,1869,940 ,1594,2808,197 }, - {4158,1346,705 ,72,484,70 }, {701,1748,756 ,4685,4618,4731 }, - {3990,3254,4019 ,3231,3233,227 }, {1015,2536,409 ,3368,2793,1480 }, - {3549,588,3369 ,3823,3822,3836 }, {5724,5725,5762 ,4898,3824,4884 }, - {4246,4285,4284 ,3665,4899,4869 }, {4270,4246,4284 ,4900,3665,4869 }, - {4284,4285,4341 ,4869,4899,4888 }, {4341,4365,4387 ,4888,4901,4878 }, - {4387,4365,4416 ,4878,4901,4889 }, {4508,1738,1737 ,4902,4890,4861 }, - {4478,4508,1737 ,4870,4902,4861 }, {1738,986,942 ,4890,4903,4880 }, - {986,1493,942 ,4903,4904,4880 }, {942,1493,33 ,4880,4904,4891 }, - {285,1856,33 ,4905,4892,4891 }, {285,1457,1856 ,4905,4893,4892 }, - {285,228,1457 ,4905,4881,4893 }, {48,213,228 ,4840,3606,4881 }, - {2677,123,1050 ,3033,3607,2282 }, {642,413,739 ,1243,351,2233 }, - {3990,4353,4352 ,3231,4906,4695 }, {4101,209,1178 ,4724,4499,206 }, - {4313,1225,2423 ,4574,327,433 }, {653,208,1259 ,318,4742,348 }, - {1976,4351,2390 ,1180,4606,1594 }, {4351,1976,2390 ,4606,1180,1594 }, - {4351,801,2390 ,4606,4907,1594 }, {2390,801,1869 ,1594,4907,2808 }, - {4352,4353,4401 ,4695,4906,4772 }, {1946,180,131 ,4408,4777,2965 }, - {4539,93,113 ,633,563,677 }, {4835,8126,3148 ,2130,812,814 }, - {588,3368,3369 ,3822,3960,3836 }, {4285,4319,4341 ,4899,4908,4888 }, - {4341,4319,4365 ,4888,4908,4901 }, {4459,4508,4478 ,4909,4902,4870 }, - {4439,4459,4478 ,4879,4909,4870 }, {4508,4509,1738 ,4902,4910,4890 }, - {1738,4509,986 ,4890,4910,4903 }, {1493,598,33 ,4904,4911,4891 }, - {598,285,33 ,4911,4905,4891 }, {285,4143,228 ,4905,4912,4881 }, - {4143,4092,228 ,4912,4913,4881 }, {228,4092,48 ,4881,4913,4840 }, - {47,941,1049 ,4914,2277,1362 }, {48,47,1049 ,4840,4914,1362 }, - {45,2255,195 ,4714,3229,739 }, {1088,1623,1186 ,1604,1709,1605 }, - {142,466,143 ,159,347,157 }, {4367,4343,6225 ,228,1818,4872 }, - {1976,2006,4351 ,1180,1181,4606 }, {1869,363,4427 ,2808,427,426 }, - {2364,3343,2365 ,2513,2515,3351 }, {4243,447,317 ,4764,4651,561 }, - {340,960,730 ,1107,1326,1549 }, {5725,5763,5762 ,3824,4212,4884 }, - {5678,5679,5724 ,3861,2553,4898 }, {5522,5558,5557 ,755,2302,756 }, - {4246,4319,4285 ,3665,4908,4899 }, {4365,4417,4416 ,4901,4915,4889 }, - {4417,4459,4439 ,4915,4909,4879 }, {4416,4417,4439 ,4889,4915,4879 }, - {4460,4509,4508 ,4916,4910,4902 }, {4459,4460,4508 ,4909,4916,4902 }, - {986,599,1493 ,4903,4917,4904 }, {1493,599,598 ,4904,4917,4911 }, - {397,4092,4143 ,4918,4913,4912 }, {285,397,4143 ,4905,4918,4912 }, - {4092,4428,48 ,4913,4919,4840 }, {4428,47,48 ,4919,4914,4840 }, - {1834,1835,1870 ,2422,2421,4868 }, {4082,4081,500 ,4716,4709,4637 }, - {5418,5103,7917 ,3471,77,4920 }, {2255,4350,195 ,3229,740,739 }, - {987,3977,66 ,2950,440,4563 }, {2431,1511,967 ,2977,4921,3668 }, - {136,4495,4450 ,1685,4723,4722 }, {4496,136,4450 ,2220,1685,4722 }, - {4134,4079,658 ,4750,4718,4693 }, {1005,3818,3732 ,3535,1244,3536 }, - {3075,3981,3263 ,2670,915,252 }, {801,415,1869 ,4907,4922,2808 }, - {1869,415,363 ,2808,4922,427 }, {4091,3141,4126 ,123,4701,124 }, - {699,4079,4134 ,4677,4718,4750 }, {913,536,863 ,444,523,284 }, - {1343,4399,1564 ,378,4546,368 }, {443,265,697 ,920,4496,3578 }, - {5217,4763,4797 ,4026,4787,3984 }, {5679,5725,5724 ,2553,3824,4898 }, - {5308,5288,5289 ,3528,3993,80 }, {3837,4320,4319 ,3667,4923,4908 }, - {4246,3837,4319 ,3665,3667,4908 }, {4320,4366,4365 ,4923,4924,4901 }, - {4319,4320,4365 ,4908,4923,4901 }, {4365,4366,4417 ,4901,4924,4915 }, - {4418,4460,4459 ,4925,4916,4909 }, {4417,4418,4459 ,4915,4925,4909 }, - {4509,853,986 ,4910,4926,4903 }, {986,853,599 ,4903,4926,4917 }, - {598,397,285 ,4911,4918,4905 }, {4092,4168,4428 ,4913,4927,4919 }, - {4428,4168,47 ,4919,4927,4914 }, {4062,4082,500 ,4623,4716,4637 }, - {4159,1833,709 ,1299,317,316 }, {1117,5090,6418 ,4928,4929,3441 }, - {658,447,4243 ,4693,4651,4764 }, {223,196,157 ,1985,152,1877 }, - {1179,883,1601 ,4774,119,4785 }, {4399,1922,1564 ,4546,4432,368 }, - {1601,883,76 ,4785,119,689 }, {1307,4203,801 ,4930,4931,4907 }, - {801,4203,415 ,4907,4931,4922 }, {5340,5339,5286 ,4002,2223,3859 }, - {4302,166,4356 ,3727,3729,155 }, {4384,4385,1946 ,4409,4778,4408 }, - {545,739,46 ,305,2233,2232 }, {4110,467,1633 ,372,346,373 }, - {467,513,1633 ,346,377,373 }, {2153,164,3980 ,4181,4932,4933 }, - {3414,4528,2142 ,4934,4935,4936 }, {3837,4286,4320 ,3667,4274,4923 }, - {4366,4418,4417 ,4924,4925,4915 }, {4479,4524,4509 ,4937,4938,4910 }, - {4460,4479,4509 ,4916,4937,4910 }, {4509,4524,853 ,4910,4938,4926 }, - {599,750,598 ,4917,4939,4911 }, {598,750,397 ,4911,4939,4918 }, - {397,4168,4092 ,4918,4927,4913 }, {610,47,4168 ,4940,4914,4927 }, - {4282,941,47 ,4941,2277,4914 }, {610,4282,47 ,4940,4941,4914 }, - {4282,119,941 ,4941,2278,2277 }, {4054,4055,4058 ,3679,3678,4708 }, - {1528,1424,608 ,4559,2298,2300 }, {258,3014,259 ,3001,3568,2203 }, - {2006,1565,1307 ,1181,3737,4930 }, {1307,1565,4203 ,4930,3737,4931 }, - {415,380,363 ,4922,2540,427 }, {5287,5340,5286 ,4003,4002,3859 }, - {4109,4125,4126 ,576,125,124 }, {4058,1253,4065 ,4708,2353,4717 }, - {5103,5412,5378 ,77,360,359 }, {4298,4186,3616 ,3580,683,3974 }, - {4469,2153,3980 ,4060,4181,4933 }, {3009,5702,5701 ,3747,3630,3938 }, - {4366,4388,4418 ,4924,4942,4925 }, {4440,4479,4460 ,4943,4937,4916 }, - {4418,4440,4460 ,4925,4943,4916 }, {853,1563,599 ,4926,4944,4917 }, - {1563,1149,599 ,4944,4945,4917 }, {1149,1924,750 ,4945,4946,4939 }, - {599,1149,750 ,4917,4945,4939 }, {4219,4168,397 ,4947,4927,4918 }, - {657,4282,610 ,4948,4941,4940 }, {657,119,4282 ,4948,2278,4941 }, - {4081,4082,1079 ,4709,4716,4575 }, {4057,2959,2420 ,272,4608,273 }, - {5643,5659,5642 ,4617,3053,526 }, {4471,1653,258 ,2187,106,3001 }, - {1651,200,1609 ,188,244,31 }, {2742,1565,2006 ,2558,3737,1181 }, - {4203,796,415 ,4931,4949,4922 }, {415,796,380 ,4922,4949,2540 }, - {95,4522,1798 ,3482,3481,4451 }, {3604,3626,3654 ,3384,4196,1438 }, - {4451,4498,4497 ,271,88,91 }, {3727,4298,3616 ,3942,3580,3974 }, - {531,4469,3980 ,3829,4060,4933 }, {4320,4342,4366 ,4923,4950,4924 }, - {4342,4389,4388 ,4950,4951,4942 }, {4366,4342,4388 ,4924,4950,4942 }, - {4389,4440,4418 ,4951,4943,4925 }, {4388,4389,4418 ,4942,4951,4925 }, - {4524,1736,853 ,4938,4952,4926 }, {1736,773,853 ,4952,4953,4926 }, - {773,1563,853 ,4953,4944,4926 }, {773,1149,1563 ,4953,4945,4944 }, - {1924,98,397 ,4946,4954,4918 }, {750,1924,397 ,4939,4946,4918 }, - {98,769,397 ,4954,4955,4918 }, {769,4219,397 ,4955,4947,4918 }, - {769,4168,4219 ,4955,4927,4947 }, {656,610,4168 ,4956,4940,4927 }, - {656,657,610 ,4956,4948,4940 }, {2959,2419,2420 ,4608,3353,273 }, - {918,143,416 ,4150,157,556 }, {4019,4300,3990 ,227,231,3231 }, - {3626,3655,3654 ,4196,3936,1438 }, {4300,4301,3990 ,231,167,3231 }, - {1565,1972,4203 ,3737,3034,4931 }, {4203,1972,796 ,4931,3034,4949 }, - {796,1808,380 ,4949,4502,2540 }, {439,1806,1346 ,4638,3124,484 }, - {8088,6837,8100 ,1998,1057,1713 }, {463,360,277 ,1941,86,85 }, - {4301,4354,4353 ,167,376,4906 }, {3990,4301,4353 ,3231,167,4906 }, - {4354,4402,4401 ,376,2190,4772 }, {1869,4427,1839 ,2808,426,1138 }, - {792,4536,4712 ,95,89,3816 }, {4655,4681,4654 ,4957,4958,4348 }, - {5377,4904,5378 ,361,3309,359 }, {34,531,3980 ,3830,3829,4933 }, - {4342,4320,4286 ,4950,4923,4274 }, {3371,1941,3265 ,3708,1034,1033 }, - {4510,4524,4479 ,4959,4938,4937 }, {4510,1719,1736 ,4959,4960,4952 }, - {4524,4510,1736 ,4938,4959,4952 }, {1719,773,1736 ,4960,4953,4952 }, - {1149,98,1924 ,4945,4954,4946 }, {4169,4168,769 ,4961,4927,4955 }, - {4169,605,4168 ,4961,4962,4927 }, {605,656,4168 ,4962,4956,4927 }, - {657,26,119 ,4948,2126,2278 }, {26,460,119 ,2126,1999,2278 }, - {643,2602,546 ,3285,3234,1171 }, {466,4103,604 ,347,4577,486 }, - {66,2697,1211 ,4563,2006,2007 }, {478,613,64 ,2077,298,297 }, - {4353,4354,4401 ,4906,376,4772 }, {1565,511,1972 ,3737,3569,3034 }, - {453,301,82 ,1814,1857,138 }, {1972,1948,796 ,3034,2543,4949 }, - {796,1948,1808 ,4949,2543,4502 }, {915,916,863 ,443,445,284 }, - {4473,4450,4401 ,270,4722,4772 }, {4402,4473,4401 ,2190,270,4772 }, - {6679,1117,6418 ,4824,4928,3441 }, {4298,3727,5832 ,3580,3942,3904 }, - {3673,52,164 ,648,411,4932 }, {3294,3292,3293 ,3895,4067,1988 }, - {4461,4479,4440 ,4963,4937,4943 }, {4461,4511,4510 ,4963,4964,4959 }, - {4479,4461,4510 ,4937,4963,4959 }, {4511,1719,4510 ,4964,4960,4959 }, - {560,98,1149 ,4965,4954,4945 }, {769,1209,4169 ,4955,279,4961 }, - {4169,1209,605 ,4961,279,4962 }, {656,605,657 ,4956,4962,4948 }, - {1347,26,657 ,4966,2126,4948 }, {1427,1971,4740 ,2378,2520,2426 }, - {2504,2461,2583 ,2898,2899,2672 }, {4473,4496,4450 ,270,2220,4722 }, - {1972,1349,1948 ,3034,2519,2543 }, {103,84,2030 ,128,4231,770 }, - {1840,1869,1839 ,198,2808,1138 }, {136,883,1179 ,1685,119,4774 }, - {5679,5697,5725 ,2553,2552,3824 }, {2153,3673,164 ,4181,648,4932 }, - {7648,7697,7647 ,3952,651,4967 }, {4420,4440,4389 ,276,4943,4951 }, - {4419,4420,4389 ,4968,276,4951 }, {4420,4461,4440 ,276,4963,4943 }, - {594,1149,773 ,2453,4945,4953 }, {1149,594,560 ,4945,2453,4965 }, - {1209,769,98 ,279,4955,4954 }, {605,1347,657 ,4962,4966,4948 }, - {1919,1004,1452 ,1779,3452,1606 }, {1832,66,1306 ,4576,4563,4653 }, - {3569,2660,538 ,4381,455,4380 }, {3759,21,2030 ,4233,1231,770 }, - {1948,1427,1808 ,2543,2378,4502 }, {1808,1427,4111 ,4502,2378,4687 }, - {643,546,644 ,3285,1171,1176 }, {4111,1870,1871 ,4687,4868,2458 }, - {4126,4198,4175 ,124,4969,4500 }, {5389,1206,1734 ,4091,2218,2219 }, - {5784,4011,878 ,396,395,398 }, {5814,5831,5830 ,4875,3943,4163 }, - {3892,3905,6166 ,4970,4194,4971 }, {5798,5814,5830 ,4876,4875,4163 }, - {4343,4389,4342 ,1818,4951,4950 }, {4343,4420,4419 ,1818,276,4968 }, - {4389,4343,4419 ,4951,1818,4968 }, {893,773,1719 ,2454,4953,4960 }, - {893,594,773 ,2454,2453,4953 }, {98,1069,1209 ,4954,277,279 }, - {605,44,1347 ,4962,357,4966 }, {1347,70,26 ,4966,323,2126 }, - {66,45,1306 ,4563,4714,4653 }, {3668,959,8070 ,513,1358,941 }, - {4723,1676,4895 ,4972,2343,4973 }, {1049,1050,123 ,1362,2282,3607 }, - {1015,409,89 ,3368,1480,1482 }, {4111,4327,1870 ,4687,2377,4868 }, - {4175,4198,3784 ,4500,4969,2252 }, {2208,3801,5090 ,1257,4974,4929 }, - {1117,2208,5090 ,4928,1257,4929 }, {5389,1734,3113 ,4091,2219,410 }, - {4185,5389,3113 ,409,4091,410 }, {2960,5941,808 ,4975,4976,4734 }, - {4390,4420,4343 ,230,276,1818 }, {4512,4511,4461 ,2279,4964,4963 }, - {4511,4512,1719 ,4964,2279,4960 }, {4512,893,1719 ,2279,2454,4960 }, - {560,971,98 ,4965,356,4954 }, {98,971,1069 ,4954,356,277 }, {4138,44,605 ,278,357,4962 }, - {1209,4138,605 ,279,278,4962 }, {44,1793,1347 ,357,319,4966 }, - {1793,69,1347 ,319,321,4966 }, {1347,69,70 ,4966,321,323 }, {6836,3534,6353 ,852,942,749 }, - {5657,5680,5640 ,48,50,2214 }, {4056,4098,4263 ,1123,353,274 }, - {1846,1148,705 ,204,160,70 }, {844,1115,1114 ,4688,2257,2256 }, - {1115,1600,2047 ,2257,4498,2258 }, {4969,4992,1030 ,4977,4978,1619 }, - {356,1112,4194 ,310,292,293 }, {3346,3617,3375 ,4457,4425,4426 }, - {4329,4380,4379 ,1247,4602,567 }, {1427,4327,4111 ,2378,2377,4687 }, - {4327,4099,1870 ,2377,4533,4868 }, {4197,4196,4148 ,82,4747,2305 }, - {5412,5411,5377 ,360,4284,361 }, {4462,4461,4420 ,1076,4963,276 }, - {4462,4512,4461 ,1076,2279,4963 }, {1057,560,594 ,354,4965,2453 }, - {871,1057,594 ,2106,354,2453 }, {1057,971,560 ,354,356,4965 }, - {4836,137,4783 ,4979,4980,4981 }, {4833,4848,4822 ,4982,4983,4984 }, - {4687,274,4637 ,4985,1819,4986 }, {4176,4127,5163 ,4703,4702,4987 }, - {7374,7373,7348 ,4988,4989,4990 }, {4857,4873,509 ,4991,4992,4993 }, - {4886,1602,361 ,4720,2329,4994 }, {5030,5031,5047 ,2134,4995,4996 }, - {4688,4748,4687 ,4997,4998,4985 }, {574,616,4874 ,2181,4999,5000 }, - {4657,4716,4656 ,5001,1030,5002 }, {3801,3660,7052 ,4974,709,708 }, - {996,3784,4176 ,5003,2252,4703 }, {4614,4692,279 ,3109,2177,2176 }, - {2208,5088,5393 ,1257,5004,5005 }, {4716,4715,4656 ,1030,1029,5002 }, - {4657,4656,4605 ,5001,5002,5006 }, {4751,4880,417 ,5007,5008,5009 }, - {5016,561,4644 ,5010,2379,2425 }, {4644,561,4740 ,2425,2379,2426 }, - {8122,8114,8145 ,384,2286,3376 }, {3801,2208,5393 ,4974,1257,5005 }, - {4614,2322,4692 ,3109,3497,2177 }, {707,669,612 ,2091,2020,5011 }, - {1750,1751,1811 ,745,773,777 }, {1511,4377,967 ,4921,3090,3668 }, - {4935,4936,4973 ,5012,5013,5014 }, {3261,6554,4190 ,5015,3647,3439 }, - {8081,8078,8093 ,1445,1744,2288 }, {4604,4603,4562 ,5016,5017,5018 }, - {811,718,4954 ,557,2407,558 }, {4804,4833,4822 ,4867,4982,4984 }, - {4974,4996,4995 ,5019,5020,5021 }, {1264,1252,3929 ,5022,5023,5024 }, - {8091,8101,8099 ,2167,383,2867 }, {4817,1771,1834 ,2368,2367,2422 }, - {5350,6529,6507 ,1526,5025,2680 }, {4848,4886,417 ,4983,4720,5009 }, - {618,717,677 ,2391,2408,2296 }, {2301,8107,872 ,2132,658,2962 }, - {4749,4810,4785 ,5026,5027,5028 }, {4809,4855,4782 ,4202,4201,5029 }, - {4656,4715,4681 ,5002,1029,4958 }, {4686,4492,4773 ,5030,5031,4865 }, - {7593,7620,7645 ,5032,5033,5034 }, {4683,4647,4643 ,5035,5036,5037 }, - {5049,1928,4859 ,5038,1282,5039 }, {6942,6943,6962 ,521,520,3260 }, - {4871,1834,1870 ,5040,2422,4868 }, {4907,4935,4934 ,2209,5012,5041 }, - {8064,2341,1333 ,3141,845,849 }, {4954,720,812 ,558,2475,559 }, - {8089,2746,8139 ,254,1517,1923 }, {5063,5064,5062 ,5042,5043,5044 }, - {4607,4659,4658 ,5045,5046,5047 }, {4976,4998,4997 ,5048,4713,5049 }, - {4659,319,4658 ,5046,5050,5047 }, {319,367,4717 ,5050,1370,1449 }, - {4658,319,4717 ,5047,5050,1449 }, {8071,8144,3264 ,738,2469,2468 }, - {8127,8142,4106 ,2970,1743,1742 }, {4690,4721,383 ,5051,5052,2087 }, - {4907,576,674 ,2209,2208,2271 }, {717,4938,677 ,2408,5053,2296 }, - {2476,1283,2431 ,2441,2978,2977 }, {5966,1455,4987 ,5054,1917,1491 }, - {1346,2464,1697 ,484,3299,69 }, {1610,1676,749 ,2330,2343,2331 }, - {4934,4972,851 ,5041,2158,2155 }, {1610,1602,4855 ,2330,2329,4201 }, - {6755,6792,6738 ,5055,5056,5057 }, {1536,1581,1508 ,2080,2030,2029 }, - {4951,717,4977 ,5058,2408,4711 }, {6390,6311,6316 ,3868,1097,4435 }, - {565,4869,564 ,1614,5059,3529 }, {4890,4889,564 ,4594,4615,3529 }, - {4869,4890,564 ,5059,4594,3529 }, {417,137,4836 ,5009,4980,4979 }, - {1077,1030,1078 ,2694,1619,5060 }, {4286,3528,4342 ,4274,5061,4950 }, - {361,4751,417 ,4994,5007,5009 }, {5098,5097,5065 ,5062,5063,5064 }, - {1870,4099,4871 ,4868,4533,5040 }, {4918,4967,4966 ,4595,5065,4646 }, - {4931,4964,937 ,5066,783,5067 }, {4930,4931,937 ,5068,5066,5067 }, - {1320,4667,4618 ,2356,5069,5070 }, {2271,6309,6308 ,5071,3690,3474 }, - {4966,4967,978 ,4646,5065,1620 }, {4433,4356,4404 ,4591,155,5072 }, - {1831,1830,1768 ,2221,2110,2109 }, {7185,7184,7159 ,5073,5074,5075 }, - {4795,4432,4433 ,3710,156,4591 }, {1879,1928,5049 ,1283,1282,5038 }, - {4972,4994,949 ,2158,5076,2133 }, {468,469,505 ,1369,988,986 }, - {1024,4686,4647 ,5077,5030,5036 }, {8105,8090,3006 ,3791,412,414 }, - {4967,4991,978 ,5065,1621,1620 }, {446,640,4959 ,501,5078,5079 }, - {7565,7620,7593 ,5080,5033,5032 }, {4951,4977,4976 ,5058,4711,5048 }, - {3609,6159,6144 ,5081,5082,5083 }, {761,845,760 ,1826,1828,3106 }, - {6484,2258,5455 ,685,1198,686 }, {561,4871,4099 ,2379,5040,4533 }, - {4018,2271,6308 ,5084,5071,3474 }, {2808,6606,2454 ,522,5085,1345 }, - {567,568,663 ,1684,1723,4773 }, {2089,2090,2619 ,1325,1324,3035 }, - {7253,7254,6346 ,1855,450,4883 }, {5949,404,5967 ,5086,761,1352 }, - {1164,6386,1298 ,5087,4085,5088 }, {1635,1636,1702 ,989,1036,1039 }, - {5094,4683,1365 ,5089,5035,2085 }, {1699,1752,1751 ,772,946,773 }, - {4615,1566,1635 ,5090,1037,989 }, {1494,4609,1973 ,1083,5091,551 }, - {5017,5292,4144 ,5092,5093,4761 }, {1442,4615,1635 ,990,5090,989 }, - {6322,6897,2249 ,3531,54,3751 }, {4536,4820,4712 ,89,2399,3816 }, - {4452,4536,4498 ,2398,89,88 }, {4989,307,4986 ,5094,5095,5096 }, - {4606,4607,4658 ,5097,5045,5047 }, {2067,2031,2005 ,1592,1560,1593 }, - {1676,4817,4895 ,2343,2368,4973 }, {4782,4667,1397 ,5029,5069,2358 }, - {1028,406,2958 ,1373,2566,1374 }, {6644,5056,4950 ,5098,1300,1302 }, - {1365,1282,1317 ,2085,2097,5099 }, {3534,6686,8121 ,942,750,3466 }, - {1169,4607,4606 ,5100,5045,5097 }, {4647,4773,4661 ,5036,4865,5101 }, - {2015,1994,1995 ,759,5102,757 }, {7287,7310,7286 ,3459,4283,3460 }, - {951,5033,4999 ,2394,5103,4712 }, {2155,875,6353 ,580,579,749 }, - {4874,616,673 ,5000,4999,2210 }, {1602,749,4723 ,2329,2331,4972 }, - {1365,4757,4666 ,2085,5104,5105 }, {7613,7612,7551 ,5106,5107,5108 }, - {2650,8140,3981 ,914,2269,915 }, {8120,6897,3286 ,906,54,56 }, - {4594,4027,5028 ,688,3470,5109 }, {4844,4848,4833 ,4866,4983,4982 }, - {4594,146,4027 ,688,504,3470 }, {4996,4997,5030 ,5020,5049,2134 }, - {5030,5047,5061 ,2134,4996,2135 }, {4594,5028,3249 ,688,5109,5110 }, - {792,147,146 ,95,3469,504 }, {4688,4689,4749 ,4997,5111,5026 }, - {128,4594,4905 ,687,688,5112 }, {5036,128,4905 ,399,687,5112 }, - {1202,366,1040 ,339,4469,1476 }, {4615,1442,4806 ,5090,990,5113 }, - {5127,4801,124 ,5114,5115,2206 }, {128,5036,4905 ,687,399,5112 }, - {3243,128,4905 ,3919,687,5112 }, {616,574,575 ,4999,2181,2180 }, - {7556,6760,4636 ,5116,5117,5118 }, {4997,4998,5018 ,5049,4713,5119 }, - {1105,1202,1040 ,2049,339,1476 }, {7372,7371,7310 ,4373,3748,4283 }, - {1649,1581,1582 ,2031,2030,2081 }, {7204,7241,7203 ,5120,4465,4366 }, - {5910,5838,6343 ,5121,3987,5122 }, {1442,4898,4806 ,990,5123,5113 }, - {1099,3243,4905 ,4350,3919,5112 }, {695,128,3243 ,731,687,3919 }, - {7572,7599,7571 ,5124,5125,5126 }, {4734,4661,4902 ,5127,5101,5128 }, - {135,4637,274 ,2653,4986,1819 }, {4817,1834,4871 ,2368,2422,5040 }, - {7823,7874,7847 ,345,344,3521 }, {7452,7509,7451 ,1875,1874,3963 }, - {137,4762,4843 ,4980,5129,5130 }, {4615,4806,4670 ,5090,5113,5131 }, - {1659,4615,4670 ,5132,5090,5131 }, {8103,1724,8077 ,1291,3880,2078 }, - {5091,5090,7052 ,5133,4929,708 }, {126,197,4659 ,1394,1393,5046 }, - {4607,126,4659 ,5045,1394,5046 }, {4659,197,319 ,5046,1393,5050 }, - {2006,1307,4351 ,1181,4930,4606 }, {4751,4821,4880 ,5007,5134,5008 }, - {4870,4871,5016 ,5135,5040,5010 }, {5061,5093,1317 ,2135,5136,5099 }, - {4838,4808,311 ,245,5137,246 }, {4880,4762,137 ,5008,5129,4980 }, - {760,4970,4920 ,3106,3073,5138 }, {5021,4644,1969 ,2383,2425,2369 }, - {856,811,855 ,5139,557,2392 }, {1768,4735,1831 ,2109,5140,2221 }, - {4869,565,566 ,5059,1614,1613 }, {4843,1718,4783 ,5130,2108,4981 }, - {662,4890,4869 ,5141,4594,5059 }, {4683,4643,4757 ,5035,5037,5104 }, - {324,325,383 ,2090,2088,2087 }, {917,4915,4929 ,4016,64,5142 }, - {4667,4782,4492 ,5069,5029,5031 }, {753,3243,4845 ,4749,3919,3921 }, - {753,462,3243 ,4749,4847,3919 }, {753,695,462 ,4749,731,4847 }, - {280,326,325 ,2175,2152,2088 }, {4976,4977,4998 ,5048,4711,4713 }, - {4566,4571,1168 ,5143,1342,1098 }, {2583,685,3902 ,2672,2674,4437 }, - {5187,4845,1111 ,5144,3921,3920 }, {6689,3285,2405 ,3065,3064,4165 }, - {1790,4183,240 ,4738,1572,1571 }, {1040,1104,1105 ,1476,1473,2049 }, - {4985,4566,1168 ,2703,5143,1098 }, {4643,4818,4734 ,5037,5145,5127 }, - {7815,5439,5440 ,5146,4157,5147 }, {5093,1365,1317 ,5136,2085,5099 }, - {1177,1130,5061 ,5148,2136,2135 }, {4994,5030,5029 ,5076,2134,2159 }, - {4751,4862,4821 ,5007,5149,5134 }, {1517,4780,4846 ,600,5150,4237 }, - {4989,972,4571 ,5094,3277,1342 }, {4984,753,4845 ,5151,4749,3921 }, - {4750,4786,4749 ,5152,5153,5026 }, {4818,4661,4734 ,5145,5101,5127 }, - {4638,4663,4637 ,5154,5155,4986 }, {5750,5739,5775 ,5156,5157,5158 }, - {6463,4989,4566 ,5159,5094,5143 }, {6704,6736,6703 ,5160,1622,5161 }, - {5629,5938,6605 ,5162,5163,5164 }, {383,4811,4786 ,2087,5165,5153 }, - {4690,4689,4639 ,5051,5111,5166 }, {4740,561,1427 ,2426,2379,2378 }, - {4566,4989,4571 ,5143,5094,1342 }, {1024,5071,4686 ,5077,5167,5030 }, - {4997,5031,5030 ,5049,4995,2134 }, {5031,5032,5064 ,4995,5168,5043 }, - {557,82,4801 ,3724,138,5115 }, {7693,3824,8133 ,724,3462,2131 }, - {4824,4812,1602 ,4719,5169,2329 }, {760,4920,663 ,3106,5138,4773 }, - {4973,4974,4995 ,5014,5019,5021 }, {4690,4749,4689 ,5051,5026,5111 }, - {4751,5015,4862 ,5007,5170,5149 }, {1282,1176,1317 ,2097,2096,5099 }, - {4855,4812,4782 ,4201,5169,5029 }, {4850,1931,1903 ,5171,4736,406 }, - {8127,8084,8142 ,2970,1139,1743 }, {2527,2487,3426 ,2637,4427,4438 }, - {4605,4606,4658 ,5006,5097,5047 }, {3762,5044,5442 ,5172,5173,5174 }, - {674,675,4936 ,2271,2294,5013 }, {4331,4984,4845 ,4645,5151,3921 }, - {5083,4331,4845 ,5175,4645,3921 }, {4331,753,4984 ,4645,4749,5151 }, - {5246,6039,5227 ,5176,2728,4097 }, {4862,4850,1831 ,5149,5171,2221 }, - {417,4880,137 ,5009,5008,4980 }, {4811,514,4858 ,5165,2154,5177 }, - {4929,4930,4963 ,5142,5068,5178 }, {5844,1119,1028 ,5179,5180,1373 }, - {7064,7065,2073 ,5181,5182,5183 }, {5056,3319,5005 ,1300,1259,1301 }, - {6570,4554,1263 ,1252,1307,1199 }, {4870,1931,4862 ,5135,4736,5149 }, - {4643,4734,1508 ,5037,5127,2029 }, {1884,1885,1929 ,3567,778,899 }, - {855,951,4977 ,2392,2394,4711 }, {4986,1073,972 ,5096,3741,3277 }, - {1169,4606,4605 ,5100,5097,5006 }, {4689,4688,4638 ,5111,4997,5154 }, - {1252,5999,3929 ,5023,5184,5024 }, {4989,4986,972 ,5094,5096,3277 }, - {4889,4933,4932 ,4615,4596,4580 }, {4688,4687,4638 ,4997,4985,5154 }, - {5096,5097,4912 ,5185,5063,5186 }, {5071,4912,4686 ,5167,5186,5030 }, - {564,4889,4868 ,3529,4615,3969 }, {675,677,4937 ,2294,2296,5187 }, - {5033,5066,5065 ,5103,5188,5064 }, {5034,5033,951 ,2396,5103,2394 }, - {897,898,952 ,2393,2411,2395 }, {1672,1650,1582 ,2083,2082,2081 }, - {4845,5052,5083 ,3921,5189,5175 }, {4118,753,4331 ,4699,4749,4645 }, - {5702,4743,5701 ,3630,3629,3938 }, {4812,4824,4492 ,5169,4719,5031 }, - {673,4934,767 ,2210,5041,2093 }, {1536,4734,1582 ,2080,5127,2081 }, - {17,961,4959 ,5190,500,5079 }, {1659,4670,4685 ,5132,5131,4894 }, - {4644,5021,5019 ,2425,2383,2384 }, {4783,1672,4902 ,4981,2083,5128 }, - {7646,7675,7645 ,5191,5192,5034 }, {1381,1169,4605 ,5193,5100,5006 }, - {5061,1317,1177 ,2135,5099,5148 }, {4933,4966,4965 ,4596,4646,784 }, - {206,3347,1804 ,3932,2098,2902 }, {4888,4889,4932 ,3997,4615,4580 }, - {4932,4933,4965 ,4580,4596,784 }, {4889,4888,4868 ,4615,3997,3969 }, - {4986,5441,42 ,5096,5194,5195 }, {1285,4618,5097 ,1944,5070,5063 }, - {4716,4717,468 ,1030,1449,1369 }, {4997,5018,5031 ,5049,5119,4995 }, - {1073,4986,42 ,3741,5096,5195 }, {1297,4752,1488 ,4048,5196,5197 }, - {280,325,279 ,2175,2088,2176 }, {4817,1676,1720 ,2368,2343,2366 }, - {1213,1197,4983 ,3700,478,480 }, {4331,5083,5052 ,4645,5175,5189 }, - {8132,8141,3900 ,413,3295,884 }, {943,1034,1081 ,1807,1756,3745 }, - {1931,1967,1903 ,4736,404,406 }, {4563,126,4607 ,3024,1394,5045 }, - {1169,4563,4607 ,5100,3024,5045 }, {4969,4970,4992 ,4977,3073,4978 }, - {6686,8070,959 ,750,941,1358 }, {4898,4867,4806 ,5123,5198,5113 }, - {1365,4683,4757 ,2085,5035,5104 }, {3482,4021,8106 ,2494,1712,2438 }, - {977,976,4965 ,3931,782,784 }, {4618,4667,4912 ,5070,5069,5186 }, - {4695,4725,4772 ,5199,5200,5201 }, {4935,4973,4972 ,5012,5014,2158 }, - {509,572,475 ,4993,1947,1884 }, {4938,4976,4975 ,5053,5048,5202 }, - {4848,417,4836 ,4983,5009,4979 }, {4749,4811,4810 ,5026,5165,5027 }, - {4974,4975,4996 ,5019,5202,5020 }, {5033,5065,5064 ,5103,5064,5043 }, - {5032,5033,5064 ,5168,5103,5043 }, {3316,2810,3271 ,2149,1728,1727 }, - {7512,7570,7536 ,5203,5204,5205 }, {4018,6308,6307 ,5084,3474,5206 }, - {1381,4604,4562 ,5193,5016,5018 }, {375,374,320 ,1883,1821,1871 }, - {3824,7714,8131 ,3462,4468,2307 }, {4661,4822,4902 ,5101,4984,5128 }, - {4972,4973,4994 ,2158,5014,5076 }, {4973,4995,4994 ,5014,5021,5076 }, - {7693,8133,8071 ,724,2131,738 }, {4811,4858,4840 ,5165,5177,5207 }, - {2322,281,4692 ,3497,2227,2177 }, {4118,4331,513 ,4699,4645,377 }, - {4810,509,475 ,5027,4993,1884 }, {4640,4691,4690 ,5208,2089,5051 }, - {442,357,589 ,673,672,671 }, {1442,1699,4898 ,990,772,5123 }, - {2219,5000,4982 ,177,286,364 }, {4611,4634,4754 ,5209,5210,5211 }, - {6299,5012,1989 ,760,5212,3016 }, {4782,1469,4809 ,5029,2361,4202 }, - {4619,39,344 ,5213,529,562 }, {4754,4959,4903 ,5211,5079,5214 }, - {4903,4959,640 ,5214,5079,5078 }, {4780,1291,4819 ,5150,5215,5216 }, - {4684,5082,6603 ,5217,5218,3985 }, {5166,5205,5227 ,3427,5219,4097 }, - {1154,1922,4399 ,4545,4432,4546 }, {4858,514,574 ,5177,2154,2181 }, - {3243,462,695 ,3919,4847,731 }, {1635,1702,1699 ,989,1039,772 }, - {4780,1517,5079 ,5150,600,5220 }, {5079,1517,833 ,5220,600,489 }, - {2464,1346,2465 ,3299,484,3181 }, {662,4919,4918 ,5141,5221,4595 }, - {4890,662,4918 ,4594,5141,4595 }, {4919,4968,4967 ,5221,5222,5065 }, - {4918,4919,4967 ,4595,5221,5065 }, {4968,1030,4991 ,5222,1619,1621 }, - {4840,574,4874 ,5207,2181,5000 }, {1136,1042,1043 ,2412,2409,2478 }, - {4967,4968,4991 ,5065,5222,1621 }, {4806,4867,4621 ,5113,5198,5223 }, - {4867,797,4621 ,5198,5224,5223 }, {4749,4785,4748 ,5026,5028,4998 }, - {273,135,274 ,1749,2653,1819 }, {559,4958,4955 ,5225,5226,5227 }, - {4693,559,4955 ,5228,5225,5227 }, {5012,6299,6417 ,5212,760,762 }, - {4840,4858,574 ,5207,5177,2181 }, {4725,4726,4729 ,5200,5229,5230 }, - {4772,4725,4729 ,5201,5200,5230 }, {1291,4772,4819 ,5215,5201,5216 }, - {4812,4855,1602 ,5169,4201,2329 }, {307,6659,4986 ,5095,3577,5096 }, - {17,438,961 ,5190,498,500 }, {4492,4824,4844 ,5031,4719,4866 }, - {1971,1427,1948 ,2520,2378,2543 }, {4898,1698,4867 ,5123,747,5198 }, - {1698,5084,4867 ,747,5231,5198 }, {1810,4698,4894 ,779,1578,746 }, - {1884,1950,1995 ,3567,786,757 }, {4609,4615,1659 ,5091,5090,5132 }, - {2258,2376,1263 ,1198,1251,1199 }, {5093,5094,1365 ,5136,5089,2085 }, - {4785,4810,4748 ,5028,5027,4998 }, {4750,4749,4690 ,5152,5026,5051 }, - {1081,5046,1078 ,3745,5232,5060 }, {4938,4951,4976 ,5053,5058,5048 }, - {5050,1922,1154 ,5233,4432,4545 }, {936,4963,937 ,5234,5178,5067 }, - {4748,375,4687 ,4998,1883,4985 }, {2212,2271,5001 ,476,5071,477 }, - {5944,3562,6037 ,4754,4753,5235 }, {1041,5067,5033 ,2410,1945,5103 }, - {4560,4600,4559 ,5236,4360,5237 }, {579,677,676 ,2324,2296,2295 }, - {7543,7605,7542 ,5238,5239,5240 }, {5412,5418,5411 ,360,3471,4284 }, - {4819,4772,4701 ,5216,5201,5241 }, {2808,1920,6606 ,522,581,5085 }, - {6699,6751,6750 ,630,5242,631 }, {5442,4672,3762 ,5174,2682,5172 }, - {5004,4817,4871 ,5243,2368,5040 }, {197,367,319 ,1393,1370,5050 }, - {5096,4912,1024 ,5185,5186,5077 }, {4723,4895,4751 ,4972,4973,5007 }, - {5016,1931,4870 ,5010,4736,5135 }, {4611,17,4634 ,5209,5190,5210 }, - {4910,4923,4611 ,5244,5245,5209 }, {4923,4945,4611 ,5245,5246,5209 }, - {975,937,976 ,4266,5067,782 }, {833,1517,491 ,489,600,487 }, - {1081,1078,1030 ,3745,5060,1619 }, {4843,4735,1718 ,5130,5140,2108 }, - {4239,4884,85 ,2254,2253,2504 }, {505,5308,5290 ,986,3528,5247 }, - {2322,4614,4590 ,3497,3109,1490 }, {2277,2322,4590 ,3522,3497,1490 }, - {4641,4640,4612 ,5248,5208,5249 }, {4176,4198,4126 ,4703,4969,124 }, - {357,1198,589 ,672,608,671 }, {6570,2376,6562 ,1252,1251,1306 }, - {4670,4621,5073 ,5131,5223,5250 }, {4761,640,4847 ,5251,5078,5252 }, - {5709,5710,6957 ,5253,5254,4315 }, {2224,406,2206 ,2660,2566,176 }, - {2219,4982,4978 ,177,364,178 }, {2814,4922,4636 ,5255,5256,5118 }, - {5067,5066,5033 ,1945,5188,5103 }, {4772,4729,4701 ,5201,5230,5241 }, - {2163,4586,158 ,243,5257,5258 }, {4692,281,280 ,2177,2227,2175 }, - {4727,4667,4492 ,5259,5069,5031 }, {4746,1264,5078 ,5260,5022,5261 }, - {1285,5098,5066 ,1944,5062,5188 }, {4686,4727,4492 ,5030,5259,5031 }, - {951,4999,4977 ,2394,4712,4711 }, {4932,4965,4964 ,4580,784,783 }, - {4948,4962,935 ,5262,5263,5264 }, {1541,1610,4855 ,2363,2330,4201 }, - {2252,3247,144 ,590,1494,853 }, {6342,1749,1492 ,5265,555,1585 }, - {1028,1119,406 ,1373,5180,2566 }, {4923,638,4945 ,5245,495,5246 }, - {7603,7683,7655 ,5266,5267,5268 }, {4647,4686,4773 ,5036,5030,4865 }, - {898,897,811 ,2411,2393,557 }, {4773,4492,4844 ,4865,5031,4866 }, - {4850,4862,1931 ,5171,5149,4736 }, {4661,4804,4822 ,5101,4867,4984 }, - {4762,4735,4843 ,5129,5140,5130 }, {6751,6787,6786 ,5242,5269,4139 }, - {4640,4641,4691 ,5208,5248,2089 }, {281,326,280 ,2227,2152,2175 }, - {2376,3898,6562 ,1251,1400,1306 }, {6349,6358,6357 ,705,5270,5271 }, - {1469,1541,4855 ,2361,2363,4201 }, {4792,4726,5002 ,5272,5229,5273 }, - {5047,5062,5061 ,4996,5044,2135 }, {2093,603,1551 ,553,5274,5275 }, - {897,855,811 ,2393,2392,557 }, {4620,4619,4823 ,5276,5213,5277 }, - {4621,797,4925 ,5223,5224,5278 }, {4749,4748,4688 ,5026,4998,4997 }, - {1990,5520,5059 ,1305,1304,1253 }, {7105,7104,7092 ,5279,5280,3968 }, - {5067,1285,5066 ,1945,1944,5188 }, {4821,4862,4762 ,5134,5149,5129 }, - {4869,566,662 ,5059,1613,5141 }, {4879,4910,4941 ,5281,5244,5282 }, - {4910,4611,4941 ,5244,5209,5282 }, {4726,4725,4700 ,5229,5200,5283 }, - {4867,4789,797 ,5198,5284,5224 }, {4894,4698,4532 ,746,1578,5285 }, - {4945,638,4611 ,5246,495,5209 }, {4844,4833,4804 ,4866,4982,4867 }, - {1566,4615,4609 ,1037,5090,5091 }, {4934,4935,4972 ,5041,5012,2158 }, - {1717,1672,1718 ,2033,2083,2108 }, {4293,6577,8065 ,2732,97,96 }, - {612,4893,707 ,5011,5286,2091 }, {4650,4649,4595 ,5287,5288,5289 }, - {4596,4650,4595 ,1629,5287,5289 }, {4920,4969,4968 ,5138,4977,5222 }, - {4703,4702,4649 ,5290,5291,5288 }, {4690,383,4750 ,5051,2087,5152 }, - {4992,4993,1030 ,4978,5292,1619 }, {7311,7372,7310 ,4334,4373,4283 }, - {4685,4670,4906 ,4894,5131,4895 }, {4787,4729,4792 ,5293,5230,5272 }, - {4608,4925,4585 ,5294,5278,3955 }, {4726,4700,5002 ,5229,5283,5273 }, - {4925,4593,4585 ,5278,5295,3955 }, {4592,4927,4732 ,5296,5297,5298 }, - {4925,4732,4593 ,5278,5298,5295 }, {4761,4619,4620 ,5251,5213,5276 }, - {4789,4592,4732 ,5284,5296,5298 }, {6308,6711,6307 ,3474,3473,5206 }, - {7185,7220,7184 ,5073,4367,5074 }, {833,4695,4772 ,489,5199,5201 }, - {1025,6714,6389 ,5299,5300,5301 }, {4806,4621,4670 ,5113,5223,5131 }, - {5950,8066,8108 ,2873,960,2287 }, {5866,3311,5864 ,5302,5303,5304 }, - {4609,1659,1973 ,5091,5132,551 }, {4611,638,17 ,5209,495,5190 }, - {1494,1566,4609 ,1083,1037,5091 }, {4667,4727,5068 ,5069,5259,5305 }, - {7524,4596,4595 ,1630,1629,5289 }, {4674,4649,4650 ,5306,5288,5287 }, - {4674,4703,4649 ,5306,5290,5288 }, {4848,4783,4822 ,4983,4981,4984 }, - {5062,5064,5094 ,5044,5043,5089 }, {4930,937,4963 ,5068,5067,5178 }, - {3621,4980,158 ,625,241,5258 }, {5082,6744,6603 ,5218,3986,3985 }, - {4670,5073,4608 ,5131,5250,5294 }, {4729,4787,5025 ,5230,5293,5307 }, - {4584,7818,4557 ,5308,5309,5310 }, {3783,4639,4638 ,5311,5166,5154 }, - {616,575,673 ,4999,2180,2210 }, {797,4789,4732 ,5224,5284,5298 }, - {4678,4903,4794 ,5312,5214,5313 }, {4789,4927,4592 ,5284,5297,5296 }, - {4819,4701,4573 ,5216,5241,5314 }, {5068,4727,4686 ,5305,5259,5030 }, - {4800,4700,4725 ,5315,5283,5200 }, {4695,4800,4725 ,5199,5315,5200 }, - {6028,6040,5927 ,5316,5317,5318 }, {4879,4923,4910 ,5281,5245,5244 }, - {6523,5349,5326 ,5319,5320,3714 }, {4800,4923,4879 ,5315,5245,5281 }, - {5090,3801,7052 ,4929,4974,708 }, {4698,559,4532 ,1578,5225,5285 }, - {1698,4894,4789 ,747,746,5284 }, {2154,5474,2273 ,5321,4243,5322 }, - {7698,7746,7697 ,333,332,651 }, {717,4951,4938 ,2408,5058,5053 }, - {4651,4650,4596 ,5323,5287,1629 }, {4576,4651,4596 ,1628,5323,1629 }, - {1519,2544,601 ,2450,2451,3905 }, {4650,4703,4674 ,5287,5290,5306 }, - {674,4936,4935 ,2271,5013,5012 }, {4848,4836,4783 ,4983,4979,4981 }, - {8071,8133,8144 ,738,2131,2469 }, {4663,4687,4637 ,5155,4985,4986 }, - {4643,1508,4666 ,5037,2029,5105 }, {5672,7009,5694 ,5324,5325,5326 }, - {4670,4608,4906 ,5131,5294,4895 }, {4754,4633,4694 ,5211,5327,5328 }, - {4701,4729,5025 ,5241,5230,5307 }, {4633,4632,4694 ,5327,5329,5328 }, - {8091,8099,8083 ,2167,2867,2103 }, {4250,4404,166 ,4161,5072,3729 }, - {4837,4834,4720 ,3990,4304,5330 }, {3600,3578,3623 ,5331,4205,4206 }, - {4929,4963,4948 ,5142,5178,5262 }, {4963,936,4962 ,5178,5234,5263 }, - {917,4929,4948 ,4016,5142,5262 }, {4963,4962,4948 ,5178,5263,5262 }, - {4283,4086,4774 ,1678,3723,5332 }, {4700,4800,4879 ,5283,5315,5281 }, - {682,346,4923 ,448,449,5245 }, {797,4732,4925 ,5224,5298,5278 }, - {5647,6890,6852 ,5333,5334,5335 }, {4958,1995,1994 ,5226,757,5102 }, - {5084,1698,4789 ,5231,747,5284 }, {4894,4532,4789 ,746,5285,5284 }, - {5084,4789,4867 ,5231,5284,5198 }, {2391,328,327 ,3544,2289,2226 }, - {1317,1176,1177 ,5099,2096,5148 }, {5061,5094,5093 ,2135,5089,5136 }, - {137,4843,4783 ,4980,5130,4981 }, {1831,1902,1866 ,2221,2193,2191 }, - {4704,4703,4650 ,4788,5290,5287 }, {4651,4704,4650 ,5323,4788,5287 }, - {665,760,664 ,1754,3106,1755 }, {4936,4974,4973 ,5013,5019,5014 }, - {4907,674,4935 ,2209,2271,5012 }, {4587,4637,135 ,774,4986,2653 }, - {4879,4941,4790 ,5281,5282,5336 }, {4995,4996,5030 ,5021,5020,2134 }, - {4729,4726,4792 ,5230,5229,5272 }, {4810,475,4748 ,5027,1884,4998 }, - {4998,4999,5033 ,4713,4712,5103 }, {4620,4823,343 ,5276,5277,4543 }, - {8079,8104,3286 ,55,1343,56 }, {6613,6650,6649 ,3746,3833,3759 }, - {4880,4821,4762 ,5008,5134,5129 }, {2279,2251,7042 ,5337,3676,5338 }, - {5061,5062,5094 ,2135,5044,5089 }, {1120,6342,1492 ,5339,5265,1585 }, - {4824,4886,4848 ,4719,4720,4983 }, {876,1167,4017 ,5340,5341,5342 }, - {1330,615,654 ,53,766,93 }, {4558,4598,4584 ,4209,5343,5308 }, - {4800,682,4923 ,5315,448,5245 }, {4948,935,4947 ,5262,5264,5344 }, - {82,557,284 ,138,3724,139 }, {4879,4790,4700 ,5281,5336,5283 }, - {4789,4532,838 ,5284,5285,5345 }, {4955,4958,1994 ,5227,5226,5102 }, - {1210,4183,5017 ,4739,1572,5092 }, {4675,4704,4651 ,5346,4788,5323 }, - {4590,3693,2277 ,1490,5347,3522 }, {833,1291,4780 ,489,5215,5150 }, - {1119,2408,406 ,5180,1399,2566 }, {4912,5071,1024 ,5186,5167,5077 }, - {683,1634,396 ,621,768,153 }, {718,620,719 ,2407,2474,2476 }, - {2908,2851,1249 ,3052,3051,2876 }, {4796,1937,4291 ,4756,59,58 }, - {5089,5091,7052 ,3442,5133,708 }, {4621,4925,4608 ,5223,5278,5294 }, - {5073,4621,4608 ,5250,5223,5294 }, {5396,5395,6506 ,3548,5348,3549 }, - {4634,17,4754 ,5210,5190,5211 }, {402,401,7712 ,5349,1387,5350 }, - {7640,4577,4575 ,5351,5352,3128 }, {682,4800,4695 ,448,5315,5199 }, - {4902,1672,1582 ,5128,2083,2081 }, {5326,6524,6523 ,3714,3704,5319 }, - {4927,4789,838 ,5297,5284,5345 }, {4611,4694,4941 ,5209,5328,5282 }, - {559,4698,4958 ,5225,1578,5226 }, {4698,1884,4958 ,1578,3567,5226 }, - {4958,1884,1995 ,5226,3567,757 }, {833,4772,1291 ,489,5201,5215 }, - {1968,1967,1931 ,2309,404,4736 }, {4577,4578,4576 ,5352,5353,1628 }, - {4578,4624,4651 ,5353,5354,5323 }, {4576,4578,4651 ,1628,5353,5323 }, - {4651,4624,4675 ,5323,5354,5346 }, {4579,4578,7641 ,5355,5353,5356 }, - {4757,4643,4666 ,5104,5037,5105 }, {4639,4689,4638 ,5166,5111,5154 }, - {5081,6605,5938 ,1258,5164,5163 }, {4874,673,707 ,5000,2210,2091 }, - {4941,4694,4790 ,5282,5328,5336 }, {6711,6735,7031 ,3473,3472,5357 }, - {4711,4771,5837 ,5358,3622,3621 }, {4926,4796,4291 ,3897,4756,58 }, - {4601,4654,4653 ,5359,4348,4347 }, {4803,4777,4778 ,4776,3917,3916 }, - {3861,3566,2746 ,1147,1146,1517 }, {1602,4723,361 ,2329,4972,4994 }, - {798,4913,5413 ,78,4185,76 }, {4735,1768,1718 ,5140,2109,2108 }, - {5771,8099,8086 ,2868,2867,4823 }, {2000,4740,1971 ,2428,2426,2520 }, - {4562,4602,4561 ,5018,5360,5361 }, {943,4993,4970 ,1807,5292,3073 }, - {943,1081,4993 ,1807,3745,5292 }, {4600,4599,4558 ,4360,4210,4209 }, - {4559,4600,4558 ,5237,4360,4209 }, {559,4693,267 ,5225,5228,5362 }, - {1091,160,4834 ,4597,4295,4304 }, {4638,4687,4663 ,5154,4985,5155 }, - {4624,4704,4675 ,5354,4788,5346 }, {5082,4684,4979 ,5218,5217,5363 }, - {320,4687,375 ,1871,4985,1883 }, {4656,4681,4655 ,5002,4958,4957 }, - {4923,346,638 ,5245,449,495 }, {4667,5068,4912 ,5069,5305,5186 }, - {4829,4852,4865 ,4626,5364,4229 }, {5043,4144,5985 ,4762,4761,5365 }, - {974,975,7335 ,5366,4266,4265 }, {5837,4713,4711 ,3621,4015,5358 }, - {1699,1698,4898 ,772,747,5123 }, {4754,17,4959 ,5211,5190,5079 }, - {4656,4655,4602 ,5002,4957,5360 }, {4604,4656,4602 ,5016,5002,5360 }, - {4603,4604,4602 ,5017,5016,5360 }, {4602,4601,4560 ,5360,5359,5236 }, - {4573,4701,2733 ,5314,5241,5367 }, {4694,4611,4754 ,5328,5209,5211 }, - {4903,640,4761 ,5214,5078,5251 }, {3788,4680,3672 ,4410,5368,4277 }, - {4834,160,1091 ,4304,4295,4597 }, {4720,4834,1091 ,5330,4304,4597 }, - {4680,4720,1091 ,5368,5330,4597 }, {6141,2072,3725 ,2386,1812,3971 }, - {883,146,4594 ,119,504,688 }, {1176,1130,1177 ,2096,2136,5148 }, - {2315,2382,4563 ,3068,3025,3024 }, {2407,1073,2156 ,4001,3741,3631 }, - {4624,4705,4704 ,5354,5369,4788 }, {4705,4706,4704 ,5369,4786,4788 }, - {7186,7185,7168 ,5370,5073,5371 }, {124,196,1423 ,2206,152,151 }, - {3954,8065,8062 ,3463,96,3272 }, {4603,4602,4562 ,5017,5360,5018 }, - {1968,1931,5019 ,2309,4736,2384 }, {640,446,4847 ,5078,501,5252 }, - {678,682,4695 ,446,448,5199 }, {3786,22,1418 ,4074,4066,4619 }, - {4921,4979,2257 ,2079,5363,554 }, {1941,3476,3283 ,1034,3838,262 }, - {4787,4739,4815 ,5293,5372,5373 }, {5025,4787,4815 ,5307,5293,5373 }, - {5768,4713,5804 ,933,4015,3623 }, {4449,4564,4805 ,5374,5375,5376 }, - {4711,4449,4805 ,5358,5374,5376 }, {4564,4646,4645 ,5375,5377,5378 }, - {4805,4564,4645 ,5376,5375,5378 }, {4646,4926,4645 ,5377,3897,5378 }, - {1724,1249,2851 ,3880,2876,3051 }, {4912,5068,4686 ,5186,5305,5030 }, - {976,937,4964 ,782,5067,783 }, {4655,4654,4602 ,4957,4348,5360 }, - {4561,4602,4560 ,5361,5360,5236 }, {2391,327,282 ,3544,2226,3498 }, - {4601,4653,4600 ,5359,4347,4360 }, {675,4937,4936 ,2294,5187,5013 }, - {5095,5096,1024 ,5379,5185,5077 }, {4823,4619,343 ,5277,5213,4543 }, - {1365,4666,1440 ,2085,5105,2063 }, {4619,344,343 ,5213,562,4543 }, - {4761,4847,4619 ,5251,5252,5213 }, {4794,4903,4761 ,5313,5214,5251 }, - {509,4873,572 ,4993,4992,1947 }, {4579,4624,4578 ,5355,5354,5353 }, - {4676,4706,4705 ,5380,4786,5369 }, {4624,4676,4705 ,5354,5380,5369 }, - {4569,4983,2808 ,1344,480,522 }, {2382,127,126 ,3025,1395,1394 }, - {1172,1081,1034 ,1758,3745,1756 }, {1672,1649,1650 ,2083,2031,2082 }, - {4834,5236,1091 ,4304,4301,4597 }, {4847,446,4619 ,5252,501,5213 }, - {446,39,4619 ,501,529,5213 }, {111,110,2962 ,1154,743,3494 }, - {4589,4638,4637 ,776,5154,4986 }, {4571,2162,6311 ,1342,3278,1097 }, - {4176,3784,4198 ,4703,2252,4969 }, {4701,4815,4956 ,5241,5373,5381 }, - {4696,4753,4713 ,5382,5383,4015 }, {5770,4696,4713 ,3949,5382,4015 }, - {4753,4449,4711 ,5383,5374,5358 }, {4713,4753,4711 ,4015,5383,5358 }, - {4939,4926,4646 ,5384,3897,5377 }, {4876,4796,4926 ,5385,4756,3897 }, - {4939,4876,4926 ,5384,5385,3897 }, {4323,4394,4796 ,4045,4044,4756 }, - {4876,4323,4796 ,5385,4045,4756 }, {3975,8064,2812 ,728,3141,1914 }, - {4916,4930,4929 ,2248,5068,5142 }, {4892,343,342 ,5386,4543,4542 }, - {4691,4721,4690 ,2089,5052,5051 }, {468,4803,4779 ,1369,4776,4729 }, - {4822,4783,4902 ,4984,4981,5128 }, {468,4779,4716 ,1369,4729,1030 }, - {5479,6741,6706 ,5387,5388,5389 }, {4601,4600,4560 ,5359,4360,5236 }, - {4994,4995,5030 ,5076,5021,2134 }, {4532,559,838 ,5285,5225,5345 }, - {4754,4903,4678 ,5211,5214,5312 }, {559,267,838 ,5225,5362,5345 }, - {4776,4680,3788 ,5390,5368,4410 }, {4452,4432,4795 ,2398,156,3710 }, - {7101,7128,7114 ,4073,5391,5392 }, {8049,2318,2317 ,5393,3185,3129 }, - {4625,4624,4579 ,5394,5354,5355 }, {4625,4626,4624 ,5394,5395,5354 }, - {4626,4676,4624 ,5395,5380,5354 }, {4998,5033,5032 ,4713,5103,5168 }, - {4717,4716,4657 ,1449,1030,5001 }, {8116,8071,5521 ,172,738,751 }, - {4612,278,4641 ,5249,1489,5248 }, {5031,5064,5063 ,4995,5043,5042 }, - {4721,4691,383 ,5052,2089,2087 }, {4639,4640,4690 ,5166,5208,5051 }, - {1073,42,2156 ,3741,5195,3631 }, {7029,7051,2341 ,548,547,845 }, - {6524,6572,6571 ,3704,3734,4146 }, {1902,1901,1866 ,2193,2192,2191 }, - {6989,5693,6947 ,4586,4075,4587 }, {6968,6967,6916 ,595,4629,596 }, - {6735,3192,2450 ,3472,3063,3444 }, {2733,4701,4956 ,5367,5241,5381 }, - {2407,2156,2258 ,4001,3631,1198 }, {4753,4854,4449 ,5383,5396,5374 }, - {4877,4876,4939 ,5397,5385,5384 }, {4940,4877,4939 ,5398,5397,5384 }, - {4752,4323,4876 ,5196,4045,5385 }, {4877,4752,4876 ,5397,5196,5385 }, - {717,855,4977 ,2408,2392,4711 }, {6711,7031,2450 ,3473,5357,3444 }, - {5520,1516,5059 ,1304,1254,1253 }, {4931,4930,4916 ,5066,5068,2248 }, - {5079,833,4780 ,5220,489,5150 }, {4980,2163,158 ,241,243,5258 }, - {5477,4672,5442 ,5399,2682,5174 }, {1307,801,4351 ,4930,4907,4606 }, - {4846,4780,3139 ,4237,5150,5400 }, {5659,2453,5658 ,3053,42,49 }, - {4633,4754,4678 ,5327,5211,5312 }, {4700,4790,5002 ,5283,5336,5273 }, - {5060,4720,4680 ,5401,5330,5368 }, {4776,5060,4680 ,5390,5401,5368 }, - {5060,4837,4720 ,5401,3990,5330 }, {4937,4975,4974 ,5187,5202,5019 }, - {4626,4625,4579 ,5395,5394,5355 }, {4626,4706,4676 ,5395,4786,5380 }, - {4612,4640,4639 ,5249,5208,5166 }, {4658,4717,4657 ,5047,1449,5001 }, - {677,4938,4937 ,2296,5053,5187 }, {3783,4612,4639 ,5311,5249,5166 }, - {4750,383,4786 ,5152,2087,5153 }, {577,675,674 ,2270,2294,2271 }, - {4893,4874,707 ,5286,5000,2091 }, {4602,4654,4601 ,5360,4348,5359 }, - {5943,6292,6283 ,5402,5403,4298 }, {6523,6524,6571 ,5319,3704,4146 }, - {1167,1120,1492 ,5341,5339,1585 }, {5672,5694,6949 ,5324,5326,4585 }, - {4774,4086,557 ,5332,3723,3724 }, {2156,6464,3898 ,3631,3633,1400 }, - {4701,5025,4815 ,5241,5307,5373 }, {4854,4616,4564 ,5396,5404,5375 }, - {4449,4854,4564 ,5374,5396,5375 }, {4616,4662,4646 ,5404,5405,5377 }, - {4564,4616,4646 ,5375,5404,5377 }, {4940,4939,4646 ,5398,5384,5377 }, - {4662,4940,4646 ,5405,5398,5377 }, {406,2219,2206 ,2566,177,176 }, - {4613,4614,278 ,1488,3109,1489 }, {617,618,677 ,2352,2391,2296 }, - {5064,5096,5095 ,5043,5185,5379 }, {4824,4848,4844 ,4719,4983,4866 }, - {4937,4938,4975 ,5187,5053,5202 }, {5018,5032,5031 ,5119,5168,4995 }, - {4840,4874,4873 ,5207,5000,4992 }, {972,2314,2162 ,3277,3742,3278 }, - {6681,6704,6655 ,5406,5160,5407 }, {4948,4947,917 ,5262,5344,4016 }, - {3358,6393,3357 ,880,1556,883 }, {6342,6555,1749 ,5265,597,555 }, - {4693,4955,4953 ,5228,5227,5408 }, {267,4693,4953 ,5362,5228,5408 }, - {4955,1994,4971 ,5227,5102,5409 }, {4953,4955,4971 ,5408,5227,5409 }, - {4838,5060,4776 ,245,5401,5390 }, {4838,4837,5060 ,245,3990,5401 }, - {4818,4647,4661 ,5145,5036,5101 }, {4683,1024,4647 ,5035,5077,5036 }, - {4580,4626,4579 ,5410,5395,5355 }, {4707,4706,4626 ,5411,4786,5395 }, - {4707,4737,4706 ,5411,5412,4786 }, {4706,4737,4765 ,4786,5412,3983 }, - {4826,5259,4765 ,5413,3794,3983 }, {4658,4657,4605 ,5047,5001,5006 }, - {361,4723,4751 ,4994,4972,5007 }, {4857,509,4810 ,4991,4993,5027 }, - {4873,4874,4893 ,4992,5000,5286 }, {4840,4857,4810 ,5207,4991,5027 }, - {375,4748,475 ,1883,4998,1884 }, {1320,4618,1285 ,2356,5070,1944 }, - {4664,4614,279 ,5414,3109,2176 }, {1583,2621,3683 ,5415,5416,5417 }, - {922,6416,6415 ,117,3440,386 }, {5000,922,6415 ,286,117,386 }, - {6795,5491,5516 ,5418,5419,5420 }, {4922,4608,4585 ,5256,5294,3955 }, - {1073,2407,2314 ,3741,4001,3742 }, {4793,4718,4753 ,5421,5422,5383 }, - {4718,4814,4854 ,5422,5423,5396 }, {4753,4718,4854 ,5383,5422,5396 }, - {4814,4722,4616 ,5423,5424,5404 }, {4854,4814,4616 ,5396,5423,5404 }, - {4722,4665,4662 ,5424,5425,5405 }, {4616,4722,4662 ,5404,5424,5405 }, - {4943,4940,4662 ,5426,5398,5405 }, {4665,4943,4662 ,5425,5426,5405 }, - {1655,2432,68 ,4089,5427,4087 }, {4614,4641,278 ,3109,5248,1489 }, - {4895,4817,5004 ,4973,2368,5243 }, {4975,4976,4997 ,5202,5048,5049 }, - {6417,404,5388 ,762,761,5428 }, {5016,4644,1931 ,5010,2425,4736 }, - {1699,1702,1752 ,772,1039,946 }, {4975,4997,4996 ,5202,5049,5020 }, - {4936,4937,4974 ,5013,5187,5019 }, {4810,4811,4840 ,5027,5165,5207 }, - {6957,5752,5709 ,4315,4374,5253 }, {4866,4865,4852 ,3641,4229,5364 }, - {4734,4902,1582 ,5127,5128,2081 }, {4829,4830,4852 ,4626,3642,5364 }, - {961,446,4959 ,500,501,5079 }, {5015,4870,4862 ,5170,5135,5149 }, - {1702,1636,1703 ,1039,1036,1038 }, {638,15,17 ,495,497,5190 }, - {4842,4838,4776 ,5429,245,5390 }, {24,1375,4837 ,247,325,3990 }, - {4838,24,4837 ,245,247,3990 }, {4919,4920,4968 ,5221,5138,5222 }, - {4581,4627,4626 ,5430,5431,5395 }, {4580,4581,4626 ,5410,5430,5395 }, - {4626,4708,4707 ,5395,5432,5411 }, {4707,4708,4737 ,5411,5432,5412 }, - {4737,4708,4765 ,5412,5432,3983 }, {384,327,328 ,2228,2226,2289 }, - {4751,5004,4871 ,5007,5243,5040 }, {4871,561,5016 ,5040,2379,5010 }, - {4895,5004,4751 ,4973,5243,5007 }, {4857,4840,4873 ,4991,5207,4992 }, - {4782,4812,4492 ,5029,5169,5031 }, {6842,6895,6865 ,5433,5434,5435 }, - {4820,4433,4849 ,2399,4591,5436 }, {4801,82,607 ,5115,138,2207 }, - {935,4962,936 ,5264,5263,5234 }, {7138,7161,7169 ,2560,2559,5437 }, - {1265,6344,4548 ,1273,1450,589 }, {4730,4885,4743 ,5438,5439,3629 }, - {4885,4747,4793 ,5439,5440,5421 }, {4743,4885,4793 ,3629,5439,5421 }, - {4793,4747,4718 ,5421,5440,5422 }, {4742,4877,4940 ,5441,5397,5398 }, - {4943,4742,4940 ,5426,5441,5398 }, {4742,4752,4877 ,5441,5196,5397 }, - {4897,1488,4752 ,5442,5197,5196 }, {410,259,2967 ,2204,2203,2701 }, - {4920,4919,662 ,5138,5221,5141 }, {1041,5034,952 ,2410,2396,2395 }, - {5034,1041,5033 ,2396,2410,5103 }, {2156,42,6783 ,3631,5195,3632 }, - {344,1993,343 ,562,4544,4543 }, {4587,4589,4637 ,774,776,4986 }, - {4787,4792,4816 ,5293,5272,5443 }, {4633,4678,4632 ,5327,5312,5329 }, - {4678,4794,4761 ,5312,5313,5251 }, {4758,4761,4620 ,5444,5251,5276 }, - {4841,4842,4745 ,5445,5429,5446 }, {4745,4776,495 ,5446,5390,4389 }, - {4917,4838,4842 ,5447,245,5429 }, {4841,4917,4842 ,5445,5447,5429 }, - {4258,85,4303 ,84,2504,3728 }, {5095,1024,4683 ,5379,5077,5035 }, - {5094,5095,4683 ,5089,5379,5035 }, {4627,4677,4626 ,5431,5448,5395 }, - {4626,4677,4708 ,5395,5448,5432 }, {4708,4766,4765 ,5432,5449,3983 }, - {4766,4827,4826 ,5449,2734,5413 }, {949,850,851 ,2133,2092,2155 }, - {4886,361,417 ,4720,4994,5009 }, {4580,7672,4581 ,5410,5450,5430 }, - {1397,4667,1320 ,2358,5069,2356 }, {6172,6147,5920 ,5451,5452,5453 }, - {8144,8133,2301 ,2469,2131,2132 }, {7027,7049,7007 ,3175,5454,4628 }, - {3320,6639,2158 ,5455,3699,1160 }, {911,3320,2158 ,1159,5455,1160 }, - {6309,6310,6735 ,3690,3918,3472 }, {1920,2157,6606 ,581,3411,5085 }, - {6686,5105,4544 ,750,5456,748 }, {1332,8123,8143 ,3492,2247,2570 }, - {4730,4731,4747 ,5438,5457,5440 }, {4885,4730,4747 ,5439,5438,5440 }, - {4899,4752,4742 ,5458,5196,5441 }, {4882,4899,4742 ,5459,5458,5441 }, - {3895,4897,4752 ,4455,5442,5196 }, {4899,3895,4752 ,5458,4455,5196 }, - {5065,5097,5096 ,5064,5063,5185 }, {5064,5065,5096 ,5043,5064,5185 }, - {663,4920,662 ,4773,5138,5141 }, {7163,7171,7194 ,5460,4306,1670 }, - {4773,4804,4661 ,4865,4867,5101 }, {4605,4604,1381 ,5006,5016,5193 }, - {1831,4850,1902 ,2221,5171,2193 }, {4968,4969,1030 ,5222,4977,1619 }, - {4906,4608,4922 ,4895,5294,5256 }, {4906,4922,2814 ,4895,5256,5255 }, - {4830,4866,4852 ,3642,3641,5364 }, {4739,4787,4816 ,5372,5293,5443 }, - {1994,2048,3184 ,5102,5461,4453 }, {4971,1994,1460 ,5409,5102,5462 }, - {1460,1994,3184 ,5462,5102,4453 }, {4745,4775,4841 ,5446,4388,5445 }, - {4917,4808,4838 ,5447,5137,245 }, {241,4724,240 ,314,4737,1571 }, - {4581,4628,4627 ,5430,5463,5431 }, {4627,4628,4677 ,5431,5463,5448 }, - {4677,4628,4708 ,5448,5463,5432 }, {4738,4767,4766 ,5464,5465,5449 }, - {4708,4738,4766 ,5432,5464,5449 }, {4766,4828,4827 ,5449,5466,2734 }, - {423,4811,383 ,2086,5165,2087 }, {6744,3350,1116 ,3986,2541,1401 }, - {6415,6416,4424 ,386,3440,3792 }, {6416,5089,4424 ,3440,3442,3792 }, - {5171,5125,5172 ,1804,5467,5468 }, {5105,5040,4544 ,5456,5469,748 }, - {5949,5388,404 ,5086,5428,761 }, {4731,4719,4718 ,5457,5470,5422 }, - {4747,4731,4718 ,5440,5457,5422 }, {4719,4673,4814 ,5470,5471,5423 }, - {4718,4719,4814 ,5422,5470,5423 }, {4673,4781,4722 ,5471,5472,5424 }, - {4814,4673,4722 ,5423,5471,5424 }, {4944,4943,4665 ,5473,5426,5425 }, - {4671,4944,4665 ,5474,5473,5425 }, {4944,4882,4742 ,5473,5459,5441 }, - {4943,4944,4742 ,5426,5473,5441 }, {3247,3777,6430 ,1494,1582,2748 }, - {423,514,4811 ,2086,2154,5165 }, {5018,4998,5032 ,5119,4713,5168 }, - {7927,7976,7952 ,1716,1715,5475 }, {718,811,856 ,2407,557,5139 }, - {8076,8124,2810 ,617,1141,1728 }, {4931,4932,4964 ,5066,4580,783 }, - {1042,898,1043 ,2409,2411,2478 }, {955,4834,4837 ,3991,4304,3990 }, - {4792,5002,4699 ,5272,5273,5476 }, {4802,4841,4775 ,5477,5445,4388 }, - {4917,648,4808 ,5447,4679,5137 }, {4716,4778,4714 ,1030,3916,1031 }, - {4734,1536,1508 ,5127,2080,2029 }, {5047,5031,5062 ,4996,4995,5044 }, - {4993,1081,1030 ,5292,3745,1619 }, {4762,1831,4735 ,5129,2221,5140 }, - {4743,5038,4730 ,3629,3628,5438 }, {4581,4582,4628 ,5430,5478,5463 }, - {4767,4798,4766 ,5465,5479,5449 }, {4766,4798,4828 ,5449,5479,5466 }, - {4828,4887,4827 ,5466,2735,2734 }, {5064,5095,5094 ,5043,5379,5089 }, - {4863,4904,4887 ,5480,3309,2735 }, {8145,8114,6797 ,3376,2286,2246 }, - {1167,1492,4017 ,5341,1585,5342 }, {5946,5991,7083 ,5481,5482,5483 }, - {4445,712,6757 ,3648,3650,3409 }, {5092,4730,4839 ,3953,5438,5484 }, - {7055,1219,7079 ,5485,4102,5486 }, {4635,4591,4731 ,5487,5488,5457 }, - {4730,4635,4731 ,5438,5487,5457 }, {4591,4825,4719 ,5488,5489,5470 }, - {4731,4591,4719 ,5457,5488,5470 }, {4825,4791,4673 ,5489,5490,5471 }, - {4719,4825,4673 ,5470,5489,5471 }, {4791,4728,4781 ,5490,5491,5472 }, - {4673,4791,4781 ,5471,5490,5472 }, {4781,4728,4722 ,5472,5491,5424 }, - {4728,837,4665 ,5491,5492,5425 }, {4722,4728,4665 ,5424,5491,5425 }, - {4665,837,4671 ,5425,5492,5474 }, {837,4946,4944 ,5492,5493,5473 }, - {4671,837,4944 ,5474,5492,5473 }, {4922,3766,4636 ,5256,3954,5118 }, - {4756,3817,5099 ,1190,1189,5494 }, {3817,3800,5099 ,1189,4325,5494 }, - {4682,4756,4736 ,1191,1190,5495 }, {7031,6735,2450 ,5357,3472,3444 }, - {4873,4893,612 ,4992,5286,5011 }, {6704,6703,6655 ,5160,5161,5407 }, - {4816,4792,4699 ,5443,5272,5476 }, {3945,6418,6416 ,4550,3441,3440 }, - {4775,4861,4802 ,4388,3877,5477 }, {4802,4917,4841 ,5477,5447,5445 }, - {4751,4871,5015 ,5007,5040,5170 }, {5031,5063,5062 ,4995,5042,5044 }, - {1790,5017,4183 ,4738,5092,1572 }, {4970,4993,4992 ,3073,5292,4978 }, - {4783,1718,1672 ,4981,2108,2083 }, {4555,4556,4582 ,5496,5497,5478 }, - {79,8104,3421 ,1510,1343,1156 }, {4583,4628,4582 ,5498,5463,5478 }, - {4652,4709,4708 ,5499,4396,5432 }, {4628,4652,4708 ,5463,5499,5432 }, - {4708,4709,4738 ,5432,4396,5464 }, {4767,4768,4798 ,5465,5500,5479 }, - {4798,4768,4828 ,5479,5500,5466 }, {4863,4887,4828 ,5480,2735,5466 }, - {4851,4863,4828 ,5501,5480,5466 }, {311,4808,648 ,246,5137,4679 }, - {673,4907,4934 ,2210,2209,5041 }, {6808,6809,6859 ,3465,712,472 }, - {1583,1606,2621 ,5415,3677,5416 }, {7004,7045,7026 ,5502,5503,5504 }, - {5089,3454,4424 ,3442,710,3792 }, {5844,5812,7073 ,5179,660,662 }, - {1666,4882,4944 ,5505,5459,5473 }, {4946,1666,4944 ,5493,5505,5473 }, - {1666,4899,4882 ,5505,5458,5459 }, {4921,5082,4979 ,2079,5218,5363 }, - {5066,5098,5065 ,5188,5062,5064 }, {4733,5099,4697 ,5506,5494,4327 }, - {4733,4756,5099 ,5506,1190,5494 }, {3634,4741,4813 ,5507,5508,4088 }, - {4733,4736,4756 ,5506,5495,1190 }, {5026,4881,4957 ,4225,2731,2733 }, - {8122,8145,8087 ,384,3376,385 }, {5022,1196,4788 ,4329,4330,3758 }, - {4760,4669,5022 ,5509,3879,4329 }, {4759,4861,4669 ,5510,3877,3879 }, - {4760,4759,4669 ,5509,5510,3879 }, {4892,4917,4802 ,5386,5447,5477 }, - {4892,648,4917 ,5386,4679,5447 }, {675,578,676 ,2294,2293,2295 }, - {718,856,855 ,2407,5139,2392 }, {6348,6349,6357 ,1607,705,5271 }, - {663,662,566 ,4773,5141,1613 }, {668,611,669 ,1949,1948,2020 }, - {4605,4656,4604 ,5006,5002,5016 }, {5217,4703,4704 ,4026,5290,4788 }, - {5067,1041,1135 ,1945,2410,1943 }, {3855,3813,3814 ,1511,1515,5511 }, - {3303,7387,7386 ,1049,3615,5512 }, {4583,4629,4628 ,5498,5513,5463 }, - {4629,4652,4628 ,5513,5499,5463 }, {4709,4768,4767 ,4396,5500,5465 }, - {4738,4709,4767 ,5464,4396,5465 }, {851,767,4934 ,2155,2093,5041 }, - {6153,5040,5105 ,5514,5469,5456 }, {3898,6464,4987 ,1400,3633,1491 }, - {6917,6916,6895 ,5515,596,5434 }, {6757,3702,1990 ,3409,1303,1305 }, - {911,5027,158 ,1159,5516,5258 }, {5027,911,6684 ,5516,1159,1161 }, - {6681,6655,6656 ,5406,5407,5517 }, {5405,6681,6656 ,5518,5406,5517 }, - {5688,5738,6890 ,5519,5520,5334 }, {5738,5750,6890 ,5520,5156,5334 }, - {1460,3184,4899 ,5462,4453,5458 }, {1666,1460,4899 ,5505,5462,5458 }, - {4899,3184,3895 ,5458,4453,4455 }, {4618,4912,5097 ,5070,5186,5063 }, - {8094,8100,8069 ,98,1713,1095 }, {6853,5626,5587 ,2505,1460,1459 }, - {6098,6173,6388 ,1565,3711,3184 }, {2164,2165,3191 ,3583,1041,2903 }, - {7169,7186,7168 ,5437,5370,5371 }, {4741,4815,1655 ,5508,5373,4089 }, - {4813,4741,1655 ,4088,5508,4089 }, {4815,4878,4872 ,5373,5521,5522 }, - {1655,4815,4872 ,4089,5373,5522 }, {4878,4739,4872 ,5521,5372,5522 }, - {4668,4697,4872 ,5523,4327,5522 }, {4739,4668,4872 ,5372,5523,5522 }, - {4668,4733,4697 ,5523,5506,4327 }, {4610,4682,4736 ,3757,1191,5495 }, - {4610,1376,4682 ,3757,1937,1191 }, {5002,4790,4699 ,5273,5336,5476 }, - {4760,5022,4788 ,5509,4329,3758 }, {4660,4760,4788 ,5524,5509,3758 }, - {4759,4802,4861 ,5510,5477,3877 }, {4802,4875,4892 ,5477,5525,5386 }, - {1469,4782,1397 ,2361,5029,2358 }, {707,673,767 ,2091,2210,2093 }, - {4647,4818,4643 ,5036,5145,5037 }, {749,1676,4723 ,2331,2343,4972 }, - {663,566,567 ,4773,1613,1684 }, {4724,4949,1790 ,4737,5526,4738 }, - {1440,4666,1508 ,2063,5105,2029 }, {611,612,669 ,1948,5011,2020 }, - {5098,1285,5097 ,5062,1944,5063 }, {4630,4629,4583 ,827,5513,5498 }, - {4629,4630,4652 ,5513,827,5499 }, {4709,4710,4768 ,4396,4136,5500 }, - {4829,4828,4768 ,4626,5466,5500 }, {4829,4851,4828 ,4626,5501,5466 }, - {2290,7935,7936 ,1982,5527,5528 }, {2376,2156,3898 ,1251,3631,1400 }, - {3609,6195,5920 ,5081,5529,5453 }, {3319,2336,5005 ,1259,1261,1301 }, - {8081,8138,8078 ,1445,2018,1744 }, {5003,4591,4635 ,5530,5488,5487 }, - {4860,5003,4635 ,5531,5530,5487 }, {4591,4909,4825 ,5488,5532,5489 }, - {4909,4791,4825 ,5532,5490,5489 }, {837,4952,4946 ,5492,5533,5493 }, - {4952,1460,1666 ,5533,5462,5505 }, {4946,4952,1666 ,5493,5533,5505 }, - {4698,1885,1884 ,1578,778,3567 }, {4635,4730,5092 ,5487,5438,3953 }, - {7743,7769,7768 ,5534,5535,5536 }, {4741,4956,4815 ,5508,5381,5373 }, - {4815,4739,4878 ,5373,5372,5521 }, {4816,4668,4739 ,5443,5523,5372 }, - {4699,4733,4668 ,5476,5506,5523 }, {4816,4699,4668 ,5443,5476,5523 }, - {4632,4788,4610 ,5329,3758,3757 }, {4891,4632,4610 ,5537,5329,3757 }, - {4632,4660,4788 ,5329,5524,3758 }, {4678,4760,4660 ,5312,5509,5524 }, - {4632,4678,4660 ,5329,5312,5524 }, {4620,4802,4759 ,5276,5477,5510 }, - {4802,4620,4875 ,5477,5276,5525 }, {5015,4871,4870 ,5170,5040,5135 }, - {678,4695,833 ,446,5199,489 }, {4862,1831,4762 ,5149,2221,5129 }, - {4970,4969,4920 ,3073,4977,5138 }, {4749,4786,4811 ,5026,5153,5165 }, - {4641,4664,279 ,5248,5414,2176 }, {557,4883,4774 ,3724,5538,5332 }, - {156,1297,1488 ,5539,4048,5197 }, {557,4801,4883 ,3724,5115,5538 }, - {4264,1488,4897 ,5540,5197,5442 }, {4641,279,4691 ,5248,2176,2089 }, - {4597,4630,4583 ,5541,827,5498 }, {4652,4630,4709 ,5499,827,4396 }, - {4710,4630,5177 ,4136,827,424 }, {4710,4769,4768 ,4136,5542,5500 }, - {4769,4799,4768 ,5542,4627,5500 }, {4768,4799,4829 ,5500,4627,4626 }, - {4829,4864,4863 ,4626,5543,5480 }, {4851,4829,4863 ,5501,4626,5480 }, - {3006,8132,552 ,414,413,3929 }, {6611,5471,6640 ,1391,2051,4224 }, - {1562,4392,7051 ,4649,844,547 }, {4586,911,158 ,5257,1159,5258 }, - {7568,7594,7567 ,5544,4020,5545 }, {7552,7613,7551 ,5546,5106,5108 }, - {4585,4593,5003 ,3955,5295,5530 }, {4860,4585,5003 ,5531,3955,5530 }, - {5003,4593,4591 ,5530,5295,5488 }, {4593,4732,4909 ,5295,5298,5532 }, - {4591,4593,4909 ,5488,5295,5532 }, {4732,4927,4791 ,5298,5297,5490 }, - {4909,4732,4791 ,5532,5298,5490 }, {4927,838,4728 ,5297,5345,5491 }, - {4791,4927,4728 ,5490,5297,5491 }, {267,837,4728 ,5362,5492,5491 }, - {838,267,4728 ,5345,5362,5491 }, {267,4953,4952 ,5362,5408,5533 }, - {837,267,4952 ,5492,5362,5533 }, {7647,7697,7675 ,4967,651,5192 }, - {2000,1971,2002 ,2428,2520,2493 }, {3276,2375,922 ,116,4479,117 }, - {403,1264,3929 ,5547,5022,5024 }, {4832,4736,4733 ,5548,5495,5506 }, - {4699,4832,4733 ,5476,5548,5506 }, {4891,4610,4736 ,5537,3757,5495 }, - {4678,4759,4760 ,5312,5510,5509 }, {4759,4758,4620 ,5510,5444,5276 }, - {4875,4620,4892 ,5525,5276,5386 }, {4850,1903,1902 ,5171,406,2193 }, - {4614,4664,4641 ,3109,5414,5248 }, {7672,7716,4581 ,5450,5549,5430 }, - {1970,4740,2000 ,2427,2426,2428 }, {4724,241,1241 ,4737,314,313 }, - {1241,4283,4774 ,313,1678,5332 }, {4724,1241,4774 ,4737,313,5332 }, - {607,124,4801 ,2207,2206,5115 }, {4491,808,5202 ,107,4734,5550 }, - {6039,5166,5227 ,2728,3427,4097 }, {4331,5052,1154 ,4645,5189,4545 }, - {1154,5052,993 ,4545,5189,5551 }, {4769,4770,4799 ,5542,4345,4627 }, - {4829,4865,4864 ,4626,4229,5543 }, {4864,4865,4863 ,5543,4229,5480 }, - {4865,4913,798 ,4229,4185,78 }, {4863,4865,798 ,5480,4229,78 }, - {3264,8117,8068 ,2468,4119,1784 }, {3626,6534,3627 ,4196,4257,4256 }, - {6418,5091,5089 ,3441,5133,3442 }, {4549,6621,6605 ,877,5552,5164 }, - {5669,5710,5709 ,5553,5254,5253 }, {6389,6714,6868 ,5301,5300,5554 }, - {5092,4585,4860 ,3953,3955,5531 }, {4953,4971,1460 ,5408,5409,5462 }, - {4952,4953,1460 ,5533,5408,5462 }, {185,3777,3247 ,1493,1582,1494 }, - {1166,309,2408 ,2746,4472,1399 }, {4560,4559,7918 ,5236,5237,5555 }, - {922,3945,6416 ,117,4550,3440 }, {2156,2376,2258 ,3631,1251,1198 }, - {4790,4832,4699 ,5336,5548,5476 }, {4790,4736,4832 ,5336,5495,5548 }, - {4694,4891,4736 ,5328,5537,5495 }, {4790,4694,4736 ,5336,5328,5495 }, - {4694,4632,4891 ,5328,5329,5537 }, {4761,4759,4678 ,5251,5510,5312 }, - {4759,4761,4758 ,5510,5251,5444 }, {4620,343,4892 ,5276,4543,5386 }, - {4117,5481,4157 ,4700,3863,3755 }, {206,3815,1606 ,3932,5556,3677 }, - {247,921,2663 ,2569,1922,3593 }, {6044,5934,5980 ,614,616,5557 }, - {3797,3746,3747 ,2061,2708,2925 }, {6211,6246,6245 ,5558,5559,5560 }, - {4443,4422,6212 ,703,5561,5562 }, {1344,1511,3941 ,300,4921,5563 }, - {1268,5995,6105 ,4568,5564,5565 }, {6210,6211,6245 ,5566,5558,5560 }, - {5515,5960,6045 ,5567,5568,935 }, {6157,1017,1426 ,5569,1793,4648 }, - {5140,2094,5130 ,3712,5570,5571 }, {5130,5499,5140 ,5571,5572,3712 }, - {5499,2621,5140 ,5572,5416,3712 }, {1627,3688,1729 ,5573,5574,3666 }, - {6247,6275,1557 ,5575,5576,5577 }, {5340,5379,4916 ,4002,3998,2248 }, - {5339,5340,4916 ,2223,4002,2248 }, {2256,5013,5950 ,2323,684,2873 }, - {6123,5988,4147 ,4221,2516,2517 }, {4392,5130,2094 ,844,5571,5570 }, - {5204,3683,5704 ,5578,5417,5579 }, {5499,5204,5704 ,5572,5578,5579 }, - {6377,6079,5978 ,1347,5580,5581 }, {5978,6082,6377 ,5581,1348,1347 }, - {7948,7994,7947 ,3625,5582,4354 }, {4306,4278,4307 ,4552,5583,4562 }, - {4489,3778,1583 ,5584,5585,5415 }, {6744,5082,5786 ,3986,5218,4813 }, - {5131,5204,5499 ,5586,5578,5572 }, {5130,5131,5499 ,5571,5586,5572 }, - {6390,5950,8114 ,3868,2873,2286 }, {1557,3310,5989 ,5577,5587,5588 }, - {778,1742,5498 ,5589,707,5590 }, {3820,778,5498 ,5591,5589,5590 }, - {313,1562,1742 ,5592,4649,707 }, {778,313,1742 ,5589,5592,707 }, - {313,5132,5130 ,5592,5593,5571 }, {1562,313,5130 ,4649,5592,5571 }, - {5130,5132,5131 ,5571,5593,5586 }, {5132,5141,5204 ,5593,5594,5578 }, - {5131,5132,5204 ,5586,5593,5578 }, {5141,5145,3683 ,5594,5595,5417 }, - {5204,5141,3683 ,5578,5594,5417 }, {5145,1289,1583 ,5595,5596,5415 }, - {3683,5145,1583 ,5417,5595,5415 }, {1289,3952,4489 ,5596,5597,5584 }, - {1583,1289,4489 ,5415,5596,5584 }, {4342,3528,4321 ,4950,5061,4873 }, - {1289,2713,3952 ,5596,4352,5597 }, {5141,1289,5145 ,5594,5596,5595 }, - {1289,3141,2713 ,5596,4701,4352 }, {5231,2375,3276 ,115,4479,116 }, - {5419,3820,3936 ,5598,5591,5599 }, {4024,5419,3936 ,5600,5598,5599 }, - {3798,778,3820 ,5601,5589,5591 }, {5419,3798,3820 ,5598,5601,5591 }, - {3798,4325,313 ,5601,5602,5592 }, {778,3798,313 ,5589,5601,5592 }, - {4325,5133,5132 ,5602,5603,5593 }, {313,4325,5132 ,5592,5602,5593 }, - {5133,533,5141 ,5603,5604,5594 }, {5132,5133,5141 ,5593,5603,5594 }, - {533,1944,5141 ,5604,5605,5594 }, {1944,2270,1289 ,5605,5606,5596 }, - {5141,1944,1289 ,5594,5605,5596 }, {2270,3222,1289 ,5606,5607,5596 }, - {1289,3222,3141 ,5596,5607,4701 }, {5133,1944,533 ,5603,5605,5604 }, - {5163,4127,3141 ,4987,4702,4701 }, {3222,5163,3141 ,5607,4987,4701 }, - {7090,7089,6281 ,4080,5608,4078 }, {4426,1414,4024 ,1056,5609,5600 }, - {3733,4426,4024 ,5610,1056,5600 }, {1414,5422,5419 ,5609,5611,5598 }, - {4024,1414,5419 ,5600,5609,5598 }, {4014,3798,5419 ,5612,5601,5598 }, - {5422,4014,5419 ,5611,5612,5598 }, {4014,3947,4325 ,5612,5613,5602 }, - {3798,4014,4325 ,5601,5612,5602 }, {5133,5561,1944 ,5603,5614,5605 }, - {2270,3002,3222 ,5606,5615,5607 }, {3222,3002,5163 ,5607,5615,4987 }, - {4545,4980,3621 ,586,241,625 }, {3947,5134,5133 ,5613,5616,5603 }, - {4325,3947,5133 ,5602,5613,5603 }, {5133,5134,5561 ,5603,5616,5614 }, - {5134,1786,1944 ,5616,5617,5605 }, {5561,5134,1944 ,5614,5616,5605 }, - {1786,5144,2270 ,5617,5618,5606 }, {1944,1786,2270 ,5605,5617,5606 }, - {5144,996,3002 ,5618,5003,5615 }, {2270,5144,3002 ,5606,5618,5615 }, - {3002,996,5163 ,5615,5003,4987 }, {5163,996,4176 ,4987,5003,4703 }, - {4014,159,3947 ,5612,5619,5613 }, {3947,159,5134 ,5613,5619,5616 }, - {6033,5987,5982 ,5620,5621,4218 }, {4012,1414,4426 ,5622,5609,1056 }, - {3816,4012,4426 ,5623,5622,1056 }, {5268,5422,1414 ,5624,5611,5609 }, - {4012,5268,1414 ,5622,5624,5609 }, {5268,5773,4014 ,5624,5625,5612 }, - {5422,5268,4014 ,5611,5624,5612 }, {5773,3971,4014 ,5625,5626,5612 }, - {3971,2961,159 ,5626,5627,5619 }, {4014,3971,159 ,5612,5626,5619 }, - {2961,5135,5134 ,5627,5628,5616 }, {159,2961,5134 ,5619,5627,5616 }, - {5135,5142,1786 ,5628,5629,5617 }, {5134,5135,1786 ,5616,5628,5617 }, - {1786,5142,5144 ,5617,5629,5618 }, {5142,5311,996 ,5629,5630,5003 }, - {5144,5142,996 ,5618,5629,5003 }, {5311,3784,996 ,5630,2252,5003 }, - {5336,4012,3816 ,5631,5622,5623 }, {5157,3971,5773 ,5632,5626,5625 }, - {5268,5157,5773 ,5624,5632,5625 }, {5447,5426,2140 ,5633,5634,5635 }, - {5157,5268,4012 ,5632,5624,5622 }, {5336,5157,4012 ,5631,5632,5622 }, - {5135,5456,5142 ,5628,5636,5629 }, {6905,5669,5626 ,2506,5553,1460 }, - {5251,5300,5299 ,3802,3793,3800 }, {4097,3816,5445 ,5637,5623,3520 }, - {4293,4097,5445 ,2732,5637,3520 }, {4294,5336,3816 ,5638,5631,5623 }, - {4097,4294,3816 ,5637,5638,5623 }, {4294,3055,5157 ,5638,5639,5632 }, - {5336,4294,5157 ,5631,5638,5632 }, {3055,1391,3971 ,5639,5640,5626 }, - {5157,3055,3971 ,5632,5639,5626 }, {1391,5343,2961 ,5640,5641,5627 }, - {3971,1391,2961 ,5626,5640,5627 }, {5343,5269,5135 ,5641,5642,5628 }, - {2961,5343,5135 ,5627,5641,5628 }, {5269,5310,5456 ,5642,5643,5636 }, - {5135,5269,5456 ,5628,5642,5636 }, {5310,4884,5142 ,5643,2253,5629 }, - {5456,5310,5142 ,5636,5643,5629 }, {5142,4884,5311 ,5629,2253,5630 }, - {5311,4884,3784 ,5630,2253,2252 }, {5343,5310,5269 ,5641,5643,5642 }, - {3100,1552,1590 ,2955,2266,3028 }, {4881,3536,4293 ,2731,5644,2732 }, - {5343,5360,5310 ,5641,5645,5643 }, {5310,5574,4884 ,5643,5646,2253 }, - {5058,3320,2163 ,5647,5455,243 }, {3536,4097,4293 ,5644,5637,2732 }, - {3403,4294,4097 ,5648,5638,5637 }, {3536,3403,4097 ,5644,5648,5637 }, - {3403,1690,3055 ,5648,5649,5639 }, {4294,3403,3055 ,5638,5648,5639 }, - {1690,5164,1391 ,5649,5650,5640 }, {3055,1690,1391 ,5639,5649,5640 }, - {5164,5312,5343 ,5650,5651,5641 }, {1391,5164,5343 ,5640,5650,5641 }, - {5312,5226,5360 ,5651,5652,5645 }, {5343,5312,5360 ,5641,5651,5645 }, - {5226,5601,5310 ,5652,5653,5643 }, {5360,5226,5310 ,5645,5652,5643 }, - {5601,711,5574 ,5653,5654,5646 }, {5310,5601,5574 ,5643,5653,5646 }, - {711,85,4884 ,5654,2504,2253 }, {5574,711,4884 ,5646,5654,2253 }, - {125,5127,124 ,399,5114,2206 }, {5164,5226,5312 ,5650,5652,5651 }, - {5226,711,5601 ,5652,5654,5653 }, {5308,505,5309 ,3528,986,987 }, - {5325,5297,5298 ,5655,3826,3818 }, {5379,4931,4916 ,3998,5066,2248 }, - {5200,5224,4679 ,5656,3850,3852 }, {4558,4631,4598 ,4209,1816,5343 }, - {8090,8080,8132 ,412,1094,413 }, {2320,2319,8049 ,1406,3228,5393 }, - {6256,5609,5953 ,5657,5658,5659 }, {5394,3499,405 ,5660,5661,5662 }, - {758,5361,3525 ,5663,5664,5665 }, {6243,6244,5356 ,5666,5667,4258 }, - {1158,253,249 ,2744,2564,644 }, {5322,5321,5320 ,4297,5668,4464 }, - {6306,5943,6336 ,5669,5402,5670 }, {5122,5717,3507 ,4024,4023,5671 }, - {1661,3611,6111 ,5672,5673,5674 }, {4805,1791,4711 ,5376,5675,5358 }, - {3320,911,2163 ,5455,1159,243 }, {5186,3536,4881 ,5676,5644,2731 }, - {5165,5186,4881 ,4226,5676,2731 }, {4370,3403,3536 ,5677,5648,5644 }, - {5186,4370,3536 ,5676,5677,5644 }, {4370,5156,1690 ,5677,5678,5649 }, - {3403,4370,1690 ,5648,5677,5649 }, {5156,5529,5164 ,5678,5679,5650 }, - {1690,5156,5164 ,5649,5678,5650 }, {5529,1386,5164 ,5679,5680,5650 }, - {1386,5159,5226 ,5680,5681,5652 }, {5164,1386,5226 ,5650,5680,5652 }, - {5159,3840,5226 ,5681,5682,5652 }, {3840,4250,711 ,5682,4161,5654 }, - {5226,3840,711 ,5652,5682,5654 }, {711,4250,85 ,5654,4161,2504 }, - {3499,5394,405 ,5661,5660,5662 }, {6245,6246,6273 ,5560,5559,5683 }, - {6246,6274,6273 ,5559,5684,5683 }, {6111,6063,5575 ,5674,5685,5686 }, - {1451,1538,2954 ,175,0,5687 }, {4490,4273,2954 ,5688,120,5687 }, - {542,1451,2954 ,122,175,5687 }, {5114,2291,2342 ,5689,1115,5690 }, - {1561,407,1525 ,1865,4018,1866 }, {4860,4635,5092 ,5531,5487,3953 }, - {3817,4682,5153 ,1189,1191,3896 }, {5960,5515,6005 ,5568,5567,5691 }, - {747,5782,5781 ,5692,5693,5694 }, {1741,6104,6230 ,5695,5696,2144 }, - {6055,3906,5129 ,3874,5697,5698 }, {2534,368,198 ,2846,1396,1392 }, - {6274,3666,5880 ,5684,5699,5700 }, {5296,5323,5322 ,4296,5701,4297 }, - {5575,202,5958 ,5686,4105,5702 }, {6042,5230,5921 ,5703,5704,5705 }, - {202,5122,5958 ,4105,4024,5702 }, {5898,5653,6115 ,5706,3644,3645 }, - {7487,7516,7540 ,5707,5708,5709 }, {5156,1386,5529 ,5678,5680,5679 }, - {1386,3840,5159 ,5680,5682,5681 }, {5139,5146,5260 ,1939,1075,4174 }, - {6053,5874,5925 ,4463,4896,5710 }, {4153,4374,3953 ,5711,5712,5713 }, - {5891,3822,5602 ,5714,5715,5716 }, {6052,5486,3311 ,5717,5718,5303 }, - {5948,5602,3507 ,5719,5716,5671 }, {5717,5948,3507 ,4023,5719,5671 }, - {5774,5750,5775 ,5720,5156,5158 }, {5808,5807,5774 ,5721,4769,5720 }, - {5775,5808,5774 ,5158,5721,5720 }, {5808,5840,5807 ,5721,4770,4769 }, - {4273,542,2954 ,120,122,5687 }, {4648,1125,1269 ,5722,5723,5724 }, - {7846,7845,7793 ,4162,5725,5726 }, {4859,5501,5165 ,5039,5727,4226 }, - {5026,4859,5165 ,4225,5039,4226 }, {5501,5537,5165 ,5727,5728,4226 }, - {2799,5186,5165 ,5729,5676,4226 }, {5537,2799,5165 ,5728,5729,4226 }, - {2799,5313,4370 ,5729,5730,5677 }, {5186,2799,4370 ,5676,5729,5677 }, - {5313,5358,5156 ,5730,5731,5678 }, {4370,5313,5156 ,5677,5730,5678 }, - {5358,1533,5156 ,5731,5732,5678 }, {1533,3857,1386 ,5732,5733,5680 }, - {5156,1533,1386 ,5678,5732,5680 }, {3857,5270,3840 ,5733,5734,5682 }, - {1386,3857,3840 ,5680,5733,5682 }, {4404,4250,3840 ,5072,4161,5682 }, - {5270,4404,3840 ,5734,5072,5682 }, {5200,4681,5224 ,5656,4958,3850 }, - {4410,6119,5303 ,5735,5736,5737 }, {3845,3497,5319 ,3635,5738,5739 }, - {2238,1272,1268 ,2697,5740,4568 }, {5647,5689,5688 ,5333,5741,5519 }, - {5689,5739,5738 ,5741,5157,5520 }, {5739,5750,5738 ,5157,5156,5520 }, - {5776,5775,5739 ,5742,5158,5157 }, {5809,5808,5775 ,5743,5721,5158 }, - {5776,5809,5775 ,5742,5743,5158 }, {5809,5840,5808 ,5743,4770,5721 }, - {5461,5962,5252 ,5744,5745,5746 }, {2004,1937,4490 ,5747,59,5688 }, - {6235,6165,5703 ,4215,4219,5748 }, {4766,4826,4765 ,5449,5413,3983 }, - {7794,7846,7793 ,5749,4162,5726 }, {3857,4015,5270 ,5733,5750,5734 }, - {4753,4696,4793 ,5383,5382,5421 }, {6418,5090,5091 ,3441,4929,5133 }, - {4653,5182,4599 ,4347,4567,4210 }, {5368,5888,6077 ,5751,5752,5753 }, - {3562,3856,6037 ,4753,5754,5235 }, {6244,6272,5663 ,5667,5755,4259 }, - {6240,6268,6254 ,5756,5757,5758 }, {2048,2015,2049 ,5461,759,758 }, - {5566,5581,5605 ,5759,5760,5761 }, {5647,5664,5689 ,5333,5762,5741 }, - {5841,5840,5809 ,5763,4770,5743 }, {1293,932,532 ,5764,5765,5766 }, - {4271,932,1464 ,5767,5765,5768 }, {1937,4273,4490 ,59,120,5688 }, - {4711,1791,4771 ,5358,5675,3622 }, {1928,5501,4859 ,1282,5727,5039 }, - {5501,2799,5537 ,5727,5729,5728 }, {5313,1533,5358 ,5730,5732,5731 }, - {5260,1868,2217 ,4174,2249,4569 }, {5902,686,6093 ,5769,5770,5771 }, - {3583,5391,5652 ,5772,5773,5774 }, {6046,5713,5686 ,5775,5776,5777 }, - {5935,3560,6218 ,5778,5779,5780 }, {5504,5541,5566 ,5781,5782,5759 }, - {5531,5504,5566 ,5783,5781,5759 }, {5566,5541,5581 ,5759,5782,5760 }, - {5541,5582,5605 ,5782,5784,5761 }, {5581,5541,5605 ,5760,5782,5761 }, - {5582,5622,5647 ,5784,5785,5333 }, {5605,5582,5647 ,5761,5784,5333 }, - {5647,5622,5664 ,5333,5785,5762 }, {5622,5690,5689 ,5785,5786,5741 }, - {5664,5622,5689 ,5762,5785,5741 }, {5690,5740,5739 ,5786,5787,5157 }, - {5689,5690,5739 ,5741,5786,5157 }, {5777,5776,5739 ,5788,5742,5157 }, - {5740,5777,5739 ,5787,5788,5157 }, {5810,5809,5776 ,5789,5743,5742 }, - {5777,5810,5776 ,5788,5789,5742 }, {5842,5841,5809 ,5790,5763,5743 }, - {5810,5842,5809 ,5789,5790,5743 }, {5842,5840,5841 ,5790,4770,5763 }, - {1246,4271,5840 ,5791,5767,4770 }, {5842,1246,5840 ,5790,5791,4770 }, - {532,932,4271 ,5766,5765,5767 }, {1246,532,4271 ,5791,5766,5767 }, - {1689,1937,2004 ,5792,59,5747 }, {4743,5732,5701 ,3629,3940,3938 }, - {489,4326,3761 ,3639,219,218 }, {4744,5049,4859 ,4424,5038,5039 }, - {4096,2799,5501 ,5793,5729,5727 }, {1928,4096,5501 ,1282,5793,5727 }, - {4096,3876,5313 ,5793,5794,5730 }, {2799,4096,5313 ,5729,5793,5730 }, - {3876,5155,1533 ,5794,5795,5732 }, {5313,3876,1533 ,5730,5794,5732 }, - {5155,5620,3857 ,5795,5796,5733 }, {1533,5155,3857 ,5732,5795,5733 }, - {5620,5344,4015 ,5796,5797,5750 }, {3857,5620,4015 ,5733,5796,5750 }, - {5344,4849,5270 ,5797,5436,5734 }, {4015,5344,5270 ,5750,5797,5734 }, - {4849,4433,4404 ,5436,4591,5072 }, {5270,4849,4404 ,5734,5436,5072 }, - {1791,4805,3367 ,5675,5376,5798 }, {5391,3583,3608 ,5773,5772,5799 }, - {3498,5960,6005 ,5800,5568,5691 }, {5652,6046,5686 ,5774,5775,5777 }, - {5463,5505,5504 ,5801,5802,5781 }, {5505,5541,5504 ,5802,5782,5781 }, - {5582,5606,5622 ,5784,5803,5785 }, {5690,5741,5740 ,5786,5804,5787 }, - {5857,1246,5842 ,5805,5791,5790 }, {5155,5344,5620 ,5795,5797,5796 }, - {5290,5308,5289 ,5247,3528,80 }, {4793,5770,5732 ,5421,3949,3940 }, - {4743,4793,5732 ,3629,5421,3940 }, {4797,4765,5259 ,3984,3983,3794 }, - {4715,4714,4681 ,1029,1031,4958 }, {3498,5478,5960 ,5800,5806,5568 }, - {2568,5391,3608 ,5807,5773,5799 }, {5478,6056,6045 ,5806,5808,935 }, - {5880,6137,6136 ,5700,5809,5810 }, {5663,5880,6136 ,4259,5700,5810 }, - {5427,5428,5463 ,5811,5812,5801 }, {5463,5485,5505 ,5801,5813,5802 }, - {5741,5778,5777 ,5804,5814,5788 }, {5740,5741,5777 ,5787,5804,5788 }, - {5778,5810,5777 ,5814,5789,5788 }, {421,1293,746 ,5815,5764,2661 }, - {920,1191,3986 ,57,5816,5817 }, {495,4776,3788 ,4389,5390,4410 }, - {5188,6744,5786 ,4812,3986,4813 }, {3876,4205,5155 ,5794,5818,5795 }, - {3787,5918,5562 ,5819,5820,4193 }, {5224,5267,5244 ,3850,4647,79 }, - {1191,920,1689 ,5816,57,5792 }, {3367,920,3986 ,5798,57,5817 }, - {5960,5478,6045 ,5568,5806,935 }, {5894,2568,3608 ,5821,5807,5799 }, - {6137,3989,4028 ,5809,5822,5823 }, {5391,6046,5652 ,5773,5775,5774 }, - {6136,6137,4028 ,5810,5809,5823 }, {5429,5464,5463 ,5824,5825,5801 }, - {5428,5429,5463 ,5812,5824,5801 }, {5463,5464,5485 ,5801,5825,5813 }, - {5464,5506,5505 ,5825,5826,5802 }, {5485,5464,5505 ,5813,5825,5802 }, - {5506,5542,5541 ,5826,5827,5782 }, {5505,5506,5541 ,5802,5826,5782 }, - {5542,5543,5541 ,5827,5828,5782 }, {5543,5583,5582 ,5828,5829,5784 }, - {5541,5543,5582 ,5782,5828,5784 }, {5582,5583,5606 ,5784,5829,5803 }, - {5583,5623,5622 ,5829,5830,5785 }, {5606,5583,5622 ,5803,5829,5785 }, - {5623,5665,5622 ,5830,5831,5785 }, {5622,5665,5690 ,5785,5831,5786 }, - {5665,5707,5690 ,5831,5832,5786 }, {5690,5707,5741 ,5786,5832,5804 }, - {5707,5779,5778 ,5832,5833,5814 }, {5741,5707,5778 ,5804,5832,5814 }, - {5811,5810,5778 ,5834,5789,5814 }, {5779,5811,5778 ,5833,5834,5814 }, - {5811,5822,5810 ,5834,5835,5789 }, {5843,5842,5810 ,1372,5790,5789 }, - {5822,5843,5810 ,5835,1372,5789 }, {2958,5857,5842 ,1374,5805,5790 }, - {5843,2958,5842 ,1372,1374,5790 }, {1662,1246,5857 ,5836,5791,5805 }, - {2958,1662,5857 ,1374,5836,5805 }, {746,532,1246 ,2661,5766,5791 }, - {1662,746,1246 ,5836,2661,5791 }, {3367,3986,2953 ,5798,5817,5837 }, - {1791,3367,2953 ,5675,5798,5837 }, {556,455,1344 ,4043,2205,300 }, - {994,4096,1928 ,5838,5793,1282 }, {5197,994,1928 ,4842,5838,1282 }, - {994,5421,4096 ,5838,5839,5793 }, {5421,4529,3876 ,5839,5840,5794 }, - {4096,5421,3876 ,5793,5839,5794 }, {4529,3865,4205 ,5840,5841,5818 }, - {3876,4529,4205 ,5794,5840,5818 }, {3865,5357,5155 ,5841,5842,5795 }, - {4205,3865,5155 ,5818,5841,5795 }, {5357,5154,5155 ,5842,5843,5795 }, - {5154,4345,5344 ,5843,5844,5797 }, {5155,5154,5344 ,5795,5843,5797 }, - {4345,4820,4849 ,5844,2399,5436 }, {5344,4345,4849 ,5797,5844,5436 }, - {5337,4914,4866 ,572,65,3641 }, {5290,5289,5244 ,5247,80,79 }, - {3875,3184,2048 ,5845,4453,5461 }, {920,1937,1689 ,57,59,5792 }, - {4028,3989,5193 ,5823,5822,5846 }, {3989,5252,5193 ,5822,5746,5846 }, - {5395,5429,5428 ,5348,5824,5812 }, {5506,5543,5542 ,5826,5828,5827 }, - {5665,5666,5707 ,5831,5847,5832 }, {5811,5843,5822 ,5834,1372,5835 }, - {2958,746,1662 ,1374,2661,5836 }, {3446,5884,3781 ,3718,5848,5849 }, - {6344,4568,4548 ,1450,1492,589 }, {5421,3865,4529 ,5839,5841,5840 }, - {6749,6785,5548 ,4046,4140,1590 }, {5244,5221,5222 ,79,4864,4566 }, - {6056,5883,6080 ,5808,5850,936 }, {5904,1124,5849 ,5851,5852,5853 }, - {5252,5962,4466 ,5746,5745,5854 }, {5121,5167,5120 ,5855,5856,5857 }, - {5167,5190,5166 ,5856,5858,3427 }, {1791,2953,5836 ,5675,5837,5859 }, - {4679,5223,5182 ,3852,3851,4567 }, {5288,5287,5243 ,3993,4003,3994 }, - {4932,4931,5379 ,4580,5066,3998 }, {5515,5898,6115 ,5567,5706,3645 }, - {5117,5515,6115 ,1113,5567,3645 }, {4466,3469,6104 ,5854,5860,5696 }, - {5396,5430,5429 ,3548,5861,5824 }, {5395,5396,5429 ,5348,3548,5824 }, - {5430,5465,5464 ,5861,5862,5825 }, {5429,5430,5464 ,5824,5861,5825 }, - {5465,5507,5506 ,5862,5863,5826 }, {5464,5465,5506 ,5825,5862,5826 }, - {5507,5532,5506 ,5863,5864,5826 }, {5532,5544,5543 ,5864,5865,5828 }, - {5506,5532,5543 ,5826,5864,5828 }, {5544,5584,5583 ,5865,5866,5829 }, - {5543,5544,5583 ,5828,5865,5829 }, {5583,5584,5623 ,5829,5866,5830 }, - {5623,5666,5665 ,5830,5847,5831 }, {5592,746,2224 ,3671,2661,2660 }, - {7538,7573,7602 ,2014,2013,5867 }, {5421,994,3865 ,5839,5838,5841 }, - {4669,4896,4775 ,3879,3878,4388 }, {6045,6056,6080 ,935,5808,936 }, - {5883,5954,5453 ,5850,5868,937 }, {6080,5883,5453 ,936,5850,937 }, - {5954,3657,5923 ,5868,5869,2073 }, {5386,3905,3892 ,5870,4194,4970 }, - {4281,615,1330 ,51,766,53 }, {5167,5205,5190 ,5856,5219,5858 }, - {4771,1791,5836 ,3622,5675,5859 }, {5804,4771,5803 ,3623,3622,5871 }, - {5453,5954,5923 ,937,5868,2073 }, {5461,5984,6113 ,5744,5872,5873 }, - {5894,3608,6113 ,5821,5799,5873 }, {5584,5624,5623 ,5866,5874,5830 }, - {5624,5648,5623 ,5874,5875,5830 }, {5648,5667,5666 ,5875,5876,5847 }, - {5623,5648,5666 ,5830,5875,5847 }, {5667,5708,5707 ,5876,5877,5832 }, - {5666,5667,5707 ,5847,5876,5832 }, {5708,5751,5707 ,5877,5878,5832 }, - {5751,5779,5707 ,5878,5833,5832 }, {5751,5812,5811 ,5878,660,5834 }, - {5779,5751,5811 ,5833,5878,5834 }, {5844,5843,5811 ,5179,1372,5834 }, - {5812,5844,5811 ,660,5179,5834 }, {4724,4908,5245 ,4737,5879,5880 }, - {5984,5894,6113 ,5872,5821,5873 }, {3657,6178,5923 ,5869,5881,2073 }, - {3830,5196,5197 ,5882,5883,4842 }, {4518,3830,5197 ,1281,5882,4842 }, - {4372,994,5197 ,5884,5838,4842 }, {5196,4372,5197 ,5883,5884,4842 }, - {4372,5425,3865 ,5884,5885,5841 }, {994,4372,3865 ,5838,5884,5841 }, - {5425,3083,5357 ,5885,5886,5842 }, {3865,5425,5357 ,5841,5885,5842 }, - {5357,3083,5154 ,5842,5886,5843 }, {3083,1020,4345 ,5886,3817,5844 }, - {5154,3083,4345 ,5843,5886,5844 }, {1020,4712,4820 ,3817,3816,2399 }, - {4345,1020,4820 ,5844,3817,2399 }, {4775,4896,4861 ,4388,3878,3877 }, - {3575,3584,5293 ,5887,5888,5889 }, {3576,5294,5912 ,5890,5891,5892 }, - {2408,309,5231 ,1399,4472,115 }, {4687,320,274 ,4985,1871,1819 }, - {5768,5804,5767 ,933,3623,5893 }, {6178,4901,6162 ,5881,3596,3598 }, - {5923,6178,6162 ,2073,5881,3598 }, {3366,3310,5461 ,5894,5587,5744 }, - {3310,5984,5461 ,5587,5872,5744 }, {7043,2279,7042 ,5895,5337,5338 }, - {5050,3244,1922 ,5233,4433,4432 }, {1196,4669,3266 ,4330,3879,3884 }, - {5989,3310,3366 ,5588,5587,5894 }, {6068,5989,3366 ,5896,5588,5894 }, - {772,1605,713 ,18,17,3 }, {6132,3628,3632 ,5897,4213,5898 }, - {5109,5121,5107 ,4182,5855,4183 }, {5109,5147,5121 ,4182,5899,5855 }, - {5168,5167,5121 ,5900,5856,5855 }, {5147,5168,5121 ,5899,5900,5855 }, - {5206,5205,5167 ,5901,5219,5856 }, {5168,5206,5167 ,5900,5901,5856 }, - {5206,5229,5205 ,5901,5902,5219 }, {7310,7370,7344 ,4283,3750,2749 }, - {5229,4178,5205 ,5902,3825,5219 }, {5305,5306,5572 ,2283,462,2284 }, - {75,1629,2938 ,3962,4207,4320 }, {5976,6028,5927 ,5903,5316,5318 }, - {5364,5397,5396 ,3268,5904,3548 }, {5363,5364,5396 ,3269,3268,3548 }, - {5397,5431,5430 ,5904,5905,5861 }, {5396,5397,5430 ,3548,5904,5861 }, - {5431,5466,5465 ,5905,5906,5862 }, {5430,5431,5465 ,5861,5905,5862 }, - {5466,5467,5465 ,5906,5907,5862 }, {5467,5508,5507 ,5907,5908,5863 }, - {5465,5467,5507 ,5862,5907,5863 }, {5507,5508,5532 ,5863,5908,5864 }, - {5508,5545,5544 ,5908,5909,5865 }, {5532,5508,5544 ,5864,5908,5865 }, - {5545,5585,5584 ,5909,5910,5866 }, {5544,5545,5584 ,5865,5909,5866 }, - {5585,5625,5624 ,5910,5911,5874 }, {5584,5585,5624 ,5866,5910,5874 }, - {5624,5625,5648 ,5874,5911,5875 }, {5625,5668,5667 ,5911,5912,5876 }, - {5648,5625,5667 ,5875,5911,5876 }, {5668,5709,5708 ,5912,5253,5877 }, - {5667,5668,5708 ,5876,5912,5877 }, {5708,5709,5751 ,5877,5253,5878 }, - {1557,5989,6068 ,5577,5588,5896 }, {5812,7038,5823 ,660,3832,661 }, - {3666,1557,6068 ,5699,5577,5896 }, {5115,6060,6281 ,5913,5914,4078 }, - {2279,1313,2251 ,5337,3674,3676 }, {3943,5662,4045 ,4755,5915,5916 }, - {5255,5276,6480 ,3682,1525,1524 }, {3669,1196,3266 ,5917,4330,3884 }, - {6239,6055,6073 ,5918,3874,3873 }, {6238,6239,6073 ,5919,5918,3873 }, - {3469,5651,6104 ,5860,5920,5696 }, {5651,5254,6104 ,5920,4042,5696 }, - {5403,3631,5417 ,5921,5922,5923 }, {5489,5911,5932 ,4244,5924,5925 }, - {5474,5370,5932 ,4243,2171,5925 }, {5804,5803,5767 ,3623,5871,5893 }, - {5108,5107,5106 ,4184,4183,5926 }, {5169,5168,5147 ,5927,5900,5899 }, - {5109,5169,5147 ,4182,5927,5899 }, {5206,4178,5229 ,5901,3825,5902 }, - {2713,3141,4091 ,4352,4701,123 }, {4771,5836,5803 ,3622,5859,5871 }, - {5585,5607,5625 ,5910,5928,5911 }, {5709,5752,5751 ,5253,4374,5878 }, - {6268,1661,3906 ,5757,5672,5697 }, {5261,5538,3830 ,173,5929,5882 }, - {5538,3417,5196 ,5929,5930,5883 }, {3830,5538,5196 ,5882,5929,5883 }, - {5500,4372,5196 ,5931,5884,5883 }, {3417,5500,5196 ,5930,5931,5883 }, - {5500,5821,5425 ,5931,5932,5885 }, {4372,5500,5425 ,5884,5931,5885 }, - {5821,4072,5425 ,5932,5933,5885 }, {4072,1938,3083 ,5933,5934,5886 }, - {5425,4072,3083 ,5885,5933,5886 }, {1938,4027,1020 ,5934,3470,3817 }, - {3083,1938,1020 ,5886,5934,3817 }, {5593,5552,5594 ,5935,5936,5937 }, - {3321,7437,7386 ,2461,5938,5512 }, {3899,1196,3669 ,1938,4330,5917 }, - {5185,3395,6142 ,5939,5940,5941 }, {5579,5564,4565 ,5942,5943,5944 }, - {5169,5206,5168 ,5927,5901,5900 }, {5321,5364,5346 ,5668,3268,3267 }, - {5364,5398,5397 ,3268,5945,5904 }, {5398,5432,5431 ,5945,5946,5905 }, - {5397,5398,5431 ,5904,5945,5905 }, {5432,5467,5466 ,5946,5907,5906 }, - {5431,5432,5466 ,5905,5946,5906 }, {6905,6957,5710 ,2506,4315,5254 }, - {7268,7317,7316 ,3773,5947,5948 }, {8078,8124,8076 ,1744,1141,617 }, - {4572,3693,3638 ,5949,5347,5950 }, {5390,5579,5415 ,3205,5942,5951 }, - {4763,4764,4797 ,4787,3982,3984 }, {5148,5169,5109 ,5952,5927,4182 }, - {5110,5148,5109 ,5953,5952,4182 }, {5207,5206,5169 ,5954,5901,5927 }, - {5148,5207,5169 ,5952,5954,5927 }, {5249,4178,5206 ,5955,3825,5901 }, - {5207,5249,5206 ,5954,5955,5901 }, {5249,5272,4178 ,5955,5956,3825 }, - {5296,5297,5323 ,4296,3826,5701 }, {5347,5321,5322 ,5957,5668,4297 }, - {5323,5347,5322 ,5701,5957,4297 }, {5365,5364,5321 ,5958,3268,5668 }, - {5347,5365,5321 ,5957,5958,5668 }, {5365,5399,5398 ,5958,5959,5945 }, - {5364,5365,5398 ,3268,5958,5945 }, {5399,5433,5432 ,5959,5960,5946 }, - {5398,5399,5432 ,5945,5959,5946 }, {5433,5468,5467 ,5960,5961,5907 }, - {5432,5433,5467 ,5946,5960,5907 }, {5468,5509,5508 ,5961,5962,5908 }, - {5467,5468,5508 ,5907,5961,5908 }, {5509,5546,5545 ,5962,5963,5909 }, - {5508,5509,5545 ,5908,5962,5909 }, {5546,5586,5585 ,5963,1461,5910 }, - {5545,5546,5585 ,5909,5963,5910 }, {5585,5586,5607 ,5910,1461,5928 }, - {5586,5626,5625 ,1461,1460,5911 }, {5607,5586,5625 ,5928,1461,5911 }, - {5625,5626,5668 ,5911,1460,5912 }, {5668,5669,5709 ,5912,5553,5253 }, - {5293,3558,3629 ,5889,5964,5965 }, {5731,5768,5729 ,934,933,200 }, - {5768,5767,5729 ,933,5893,200 }, {5326,6041,5327 ,3714,3801,3702 }, - {5146,3760,1868 ,1075,2250,2249 }, {5731,5729,5730 ,934,200,199 }, - {5731,5730,3615 ,934,199,201 }, {3952,2713,3259 ,5597,4352,407 }, - {1156,5138,5128 ,3100,1940,5966 }, {5500,4072,5821 ,5931,5933,5932 }, - {7837,4897,3895 ,5967,5442,4455 }, {3760,3899,3669 ,2250,1938,5917 }, - {184,4295,4016 ,4052,5968,5969 }, {1064,5579,5390 ,5970,5942,3205 }, - {7166,5110,5109 ,5971,5953,4182 }, {5546,5547,5586 ,5963,5972,1461 }, - {6234,6167,6002 ,5973,5974,5975 }, {5243,5287,5265 ,3994,4003,4882 }, - {3221,5262,5261 ,183,5976,173 }, {5352,3950,3345 ,3502,4530,3476 }, - {5262,5527,5538 ,5976,5977,5929 }, {5261,5262,5538 ,173,5976,5929 }, - {5527,5746,3417 ,5977,5978,5930 }, {5538,5527,3417 ,5929,5977,5930 }, - {5619,5500,3417 ,5979,5931,5930 }, {5746,5619,3417 ,5978,5979,5930 }, - {5619,1671,4072 ,5979,5980,5933 }, {5500,5619,4072 ,5931,5979,5933 }, - {1671,3249,1938 ,5980,5110,5934 }, {4072,1671,1938 ,5933,5980,5934 }, - {572,4873,611 ,1947,4992,1948 }, {6128,1620,6129 ,5981,5982,5983 }, - {4299,1064,5390 ,5984,5970,3205 }, {5446,4299,5390 ,5985,5984,3205 }, - {4299,5579,1064 ,5984,5942,5970 }, {4299,1002,5579 ,5984,2976,5942 }, - {5323,5324,5348 ,5701,5986,5987 }, {5509,5547,5546 ,5962,5972,5963 }, - {4827,5259,4826 ,2734,3794,5413 }, {3249,4905,4594 ,5110,5112,688 }, - {5272,5298,5297 ,5956,3818,3826 }, {5619,5735,1671 ,5979,5988,5980 }, - {6803,5587,5548 ,4169,1459,1590 }, {4863,5378,4904 ,5480,359,3309 }, - {5260,5146,1868 ,4174,1075,2249 }, {5149,5148,5110 ,5989,5952,5953 }, - {5111,5149,5110 ,5990,5989,5953 }, {5208,5207,5148 ,5991,5954,5952 }, - {5149,5208,5148 ,5989,5991,5952 }, {5250,5249,5207 ,5992,5955,5954 }, - {5208,5250,5207 ,5991,5992,5954 }, {5273,5272,5249 ,3819,5956,5955 }, - {5250,5273,5249 ,5992,3819,5955 }, {7683,7704,7682 ,5267,3562,3564 }, - {5348,5347,5323 ,5987,5957,5701 }, {5325,5323,5297 ,5655,5701,3826 }, - {5366,5365,5347 ,5993,5958,5957 }, {5348,5366,5347 ,5987,5993,5957 }, - {5366,5400,5399 ,5993,5994,5959 }, {5365,5366,5399 ,5958,5993,5959 }, - {5400,5434,5433 ,5994,5995,5960 }, {5399,5400,5433 ,5959,5994,5960 }, - {5434,5469,5468 ,5995,5996,5961 }, {5433,5434,5468 ,5960,5995,5961 }, - {5469,5510,5509 ,5996,5997,5962 }, {5468,5469,5509 ,5961,5996,5962 }, - {5510,5511,5509 ,5997,1588,5962 }, {5511,5548,5547 ,1588,1590,5972 }, - {5509,5511,5547 ,5962,1588,5972 }, {5548,5587,5586 ,1590,1459,1461 }, - {5547,5548,5586 ,5972,1590,1461 }, {5860,2321,5951 ,5998,5999,6000 }, - {3620,5991,5946 ,6001,5482,5481 }, {6907,6959,6958 ,6002,6003,4314 }, - {4696,5770,4793 ,5382,3949,5421 }, {6163,5889,6175 ,1453,1587,6004 }, - {5128,2217,5443 ,5966,4569,6005 }, {5266,5243,5221 ,81,3994,4864 }, - {5340,4868,5379 ,4002,3969,3998 }, {5223,5244,5222 ,3851,79,4566 }, - {3221,5527,5262 ,183,5977,5976 }, {3278,8113,1724 ,1158,1157,3880 }, - {5010,2140,5426 ,6006,5635,5634 }, {1166,1119,5844 ,2746,5180,5179 }, - {2375,3945,922 ,4479,4550,117 }, {1116,3350,5497 ,1401,2541,1375 }, - {1488,4264,156 ,5197,5540,5539 }, {3973,4299,5446 ,6007,5984,5985 }, - {5209,5208,5149 ,6008,5991,5989 }, {7566,7621,7565 ,6009,6010,5080 }, - {7621,7647,7620 ,6010,4967,5033 }, {5324,5366,5348 ,5986,5993,5987 }, - {5469,5511,5510 ,5996,1588,5997 }, {6208,6209,6243 ,6011,6012,5666 }, - {5933,3548,5934 ,615,6013,616 }, {3548,5489,5934 ,6013,4244,616 }, - {4278,4260,6138 ,5583,3527,6014 }, {3878,789,3419 ,1248,3910,2012 }, - {1119,1166,2408 ,5180,2746,1399 }, {156,22,1297 ,5539,4066,4048 }, - {5325,5298,6041 ,5655,3818,3801 }, {156,1934,22 ,5539,4620,4066 }, - {1218,3820,5498 ,3255,5591,5590 }, {6736,6754,6703 ,1622,713,5161 }, - {7923,7896,7897 ,816,4355,6015 }, {5772,5746,5527 ,6016,5978,5977 }, - {3984,5772,5527 ,6017,6016,5977 }, {5772,5577,5619 ,6016,6018,5979 }, - {5746,5772,5619 ,5978,6016,5979 }, {5577,4228,5735 ,6018,6019,5988 }, - {5619,5577,5735 ,5979,6018,5988 }, {4228,1099,1671 ,6019,4350,5980 }, - {5735,4228,1671 ,5988,6019,5980 }, {1099,4905,3249 ,4350,5112,5110 }, - {1671,1099,3249 ,5980,4350,5110 }, {3766,7556,4636 ,3954,5116,5118 }, - {3249,5036,4905 ,5110,399,5112 }, {3941,4299,3973 ,5563,5984,6007 }, - {636,3941,3973 ,6020,5563,6007 }, {3941,1511,4299 ,5563,4921,5984 }, - {5158,4631,4599 ,4832,1816,4210 }, {5112,5149,5111 ,6021,5989,5990 }, - {5273,5250,5251 ,3819,5992,3802 }, {2690,303,4296 ,6022,4086,6023 }, - {4191,2690,4296 ,4474,6022,6023 }, {6151,5860,5951 ,6024,5998,6000 }, - {6035,5654,4045 ,6025,6026,5916 }, {1741,6230,5947 ,5695,2144,6027 }, - {5948,5891,5602 ,5719,5714,5716 }, {5979,5929,6132 ,6028,6029,5897 }, - {798,5378,4863 ,78,359,5480 }, {8,1798,4522 ,3341,4451,3481 }, - {5218,5239,5238 ,483,482,4351 }, {5179,5199,5178 ,1879,422,524 }, - {5179,5178,5136 ,1879,524,1817 }, {2797,1935,303 ,6030,4098,4086 }, - {2690,2797,303 ,6022,6030,4086 }, {3150,636,2960 ,6031,6020,4975 }, - {5388,2960,636 ,5428,4975,6020 }, {5150,5149,5112 ,6032,5989,6021 }, - {5113,5150,5112 ,141,6032,6021 }, {5150,5170,5149 ,6032,3683,5989 }, - {5210,5209,5149 ,6033,6008,5989 }, {5170,5210,5149 ,3683,6033,5989 }, - {5210,5208,5209 ,6033,5991,6008 }, {5251,5250,5208 ,3802,5992,5991 }, - {5210,5251,5208 ,6033,3802,5991 }, {3985,3172,1935 ,4802,4155,4098 }, - {2797,3985,1935 ,6030,4802,4098 }, {5349,5324,5325 ,5320,5986,5655 }, - {3482,8106,3449 ,2494,2438,2440 }, {5367,5366,5324 ,6034,5993,5986 }, - {5349,5367,5324 ,5320,6034,5986 }, {5367,5401,5400 ,6034,4145,5994 }, - {5366,5367,5400 ,5993,6034,5994 }, {5401,5435,5434 ,4145,4223,5995 }, - {5400,5401,5434 ,5994,4145,5995 }, {5435,5470,5469 ,4223,6035,5996 }, - {5434,5435,5469 ,5995,4223,5996 }, {5470,5471,5469 ,6035,2051,5996 }, - {5469,5471,5511 ,5996,2051,1588 }, {5652,5686,5651 ,5774,5777,5920 }, - {5651,5747,5254 ,5920,6036,4042 }, {3525,5361,5935 ,5665,5664,5778 }, - {6051,5979,6132 ,6037,6028,5897 }, {6040,6028,5246 ,5317,5316,5176 }, - {5853,5318,6023 ,6038,6039,6040 }, {3731,5922,6075 ,6041,4292,6042 }, - {4654,5200,4679 ,4348,5656,3852 }, {4866,4914,3907 ,3641,65,3883 }, - {4916,4929,4915 ,2248,5142,64 }, {1525,2086,4314 ,1866,1802,1864 }, - {3985,3858,3093 ,4802,4803,4172 }, {3172,3985,3093 ,4155,4802,4172 }, - {5772,4228,5577 ,6016,6019,6018 }, {495,4775,4745 ,4389,4388,5446 }, - {5052,4845,5070 ,5189,3921,6043 }, {789,1156,3858 ,3910,3100,4803 }, - {3858,5128,5119 ,4803,5966,4190 }, {3093,3858,5119 ,4172,4803,4190 }, - {59,145,2689 ,967,810,2821 }, {5435,5471,5470 ,4223,2051,6035 }, - {6246,6247,3666 ,5559,5575,5699 }, {6166,6152,7034 ,4971,6044,6045 }, - {3835,5878,5550 ,6046,2684,6047 }, {6044,5980,5294 ,614,5557,5891 }, - {5889,6339,6034 ,1587,6048,6049 }, {3249,5028,1938 ,5110,5109,5934 }, - {6131,6132,3632 ,6050,5897,5898 }, {6185,5386,3892 ,6051,5870,4970 }, - {5749,5895,3444 ,6052,6053,6054 }, {5490,5749,3444 ,6055,6052,6054 }, - {5325,5326,5349 ,5655,3714,5320 }, {5644,5643,5571 ,4133,4617,4616 }, - {5495,5644,5571 ,4134,4133,4616 }, {5128,5443,5424 ,5966,6005,4227 }, - {3345,4226,835 ,3476,6056,3030 }, {5684,3345,835 ,4683,3476,3030 }, - {4226,1986,3984 ,6056,6057,6017 }, {835,4226,3984 ,3030,6056,6017 }, - {1986,2345,5772 ,6057,6058,6016 }, {3984,1986,5772 ,6017,6057,6016 }, - {2345,5187,4228 ,6058,5144,6019 }, {5772,2345,4228 ,6016,6058,6019 }, - {1111,1099,4228 ,3920,4350,6019 }, {5187,1111,4228 ,5144,3920,6019 }, - {68,2432,3818 ,4087,5427,1244 }, {5119,5128,5424 ,4190,5966,4227 }, - {5443,2217,201 ,6005,4569,4230 }, {5424,5443,201 ,4227,6005,4230 }, - {556,3150,2960 ,4043,6031,4975 }, {556,636,3150 ,4043,6020,6031 }, - {1344,3941,636 ,300,5563,6020 }, {556,1344,636 ,4043,300,6020 }, - {8020,8019,7972 ,1405,1407,6059 }, {2217,3038,2643 ,4569,4342,4232 }, - {201,2217,2643 ,4230,4569,4232 }, {7897,7896,7845 ,6015,4355,5725 }, - {3856,3562,6013 ,5754,4753,6060 }, {6247,1557,3666 ,5575,5577,5699 }, - {6098,6174,6173 ,1565,2381,3711 }, {5790,5813,5104 ,6061,6062,6063 }, - {5755,5790,5104 ,4047,6061,6063 }, {5086,5104,5813 ,6064,6063,6062 }, - {5813,5846,5086 ,6062,6065,6064 }, {5846,4225,257 ,6065,6066,6067 }, - {5714,5715,7028 ,4077,4076,3587 }, {4225,2871,257 ,6066,3649,6067 }, - {5461,3608,5962 ,5744,5799,5745 }, {5644,5660,5643 ,4133,3054,4617 }, - {3038,1341,2634 ,4342,3841,4269 }, {3345,4052,4226 ,3476,6068,6056 }, - {4226,4052,1986 ,6056,6068,6057 }, {1224,2986,1201 ,1508,3500,1477 }, - {3858,1156,5128 ,4803,3100,5966 }, {2643,3038,2634 ,4232,4342,4269 }, - {3788,3672,2634 ,4410,4277,4269 }, {1341,3788,2634 ,3841,4410,4269 }, - {5729,5728,5700 ,200,4486,4501 }, {210,194,262 ,4592,4593,4758 }, - {145,59,113 ,810,967,677 }, {4681,5200,4654 ,4958,5656,4348 }, - {3626,3580,3557 ,4196,6069,6070 }, {4894,1750,1810 ,746,745,779 }, - {4764,4763,4706 ,3982,4787,4786 }, {284,4086,4283 ,139,3723,1678 }, - {5211,5210,5170 ,6071,6033,3683 }, {7873,7923,7897 ,817,816,6015 }, - {5957,202,6063 ,4054,4105,5685 }, {8109,8116,5521 ,2938,172,751 }, - {1700,807,3563 ,6072,6073,6074 }, {3568,1941,3529 ,3839,1034,3837 }, - {5694,5716,5715 ,5326,6075,4076 }, {5716,5756,5755 ,6075,6076,4047 }, - {5715,5716,5755 ,4076,6075,4047 }, {5755,5756,5790 ,4047,6076,6061 }, - {5756,5791,5813 ,6076,6077,6062 }, {5790,5756,5813 ,6061,6076,6062 }, - {5791,5847,5846 ,6077,6078,6065 }, {5813,5791,5846 ,6062,6077,6065 }, - {5847,32,4225 ,6078,6079,6066 }, {5846,5847,4225 ,6065,6078,6066 }, - {32,181,2871 ,6079,6080,3649 }, {4225,32,2871 ,6066,6079,3649 }, - {5151,5125,5171 ,1803,5467,1804 }, {181,712,2871 ,6080,3650,3649 }, - {6242,6270,6269 ,6081,6082,6083 }, {1191,1689,1195 ,5816,5792,725 }, - {1689,4490,4347 ,5792,5688,3287 }, {1586,1191,1195 ,943,5816,725 }, - {612,611,4873 ,5011,1948,4992 }, {3615,5729,5700 ,201,200,4501 }, - {5295,556,808 ,2188,4043,4734 }, {2953,212,5835 ,5837,4614,4573 }, - {5836,2953,5835 ,5859,5837,4573 }, {4599,5182,5158 ,4210,4567,4832 }, - {4166,1016,1251 ,6084,6085,4604 }, {5788,5579,1002 ,6086,5942,2976 }, - {5316,5588,5550 ,6087,6088,6047 }, {6184,1170,1620 ,6089,4103,5982 }, - {6989,5694,5693 ,4586,5326,4075 }, {5716,5744,5756 ,6075,6090,6076 }, - {4106,5684,835 ,1742,4683,3030 }, {5924,6042,5865 ,6091,5703,6092 }, - {5649,5924,5865 ,6093,6091,6092 }, {6000,5851,3870 ,6094,6095,6096 }, - {4490,2954,3867 ,5688,5687,4507 }, {4347,4490,3867 ,3287,5688,4507 }, - {6644,4950,4981 ,5098,1302,1350 }, {3915,3939,4307 ,1575,3539,4562 }, - {3950,3799,4052 ,4530,4531,6068 }, {3345,3950,4052 ,3476,4530,6068 }, - {3799,3282,1986 ,4531,6097,6057 }, {4052,3799,1986 ,6068,4531,6057 }, - {3282,3769,2345 ,6097,6098,6058 }, {1986,3282,2345 ,6057,6097,6058 }, - {3769,5070,5187 ,6098,6043,5144 }, {2345,3769,5187 ,6058,6098,5144 }, - {4681,4714,5224 ,4958,1031,3850 }, {1005,68,3818 ,3535,4087,1244 }, - {5138,5260,5128 ,1940,4174,5966 }, {5767,5766,5728 ,5893,4487,4486 }, - {5729,5767,5728 ,200,5893,4486 }, {5143,1416,297 ,6099,6100,1918 }, - {1416,1455,140 ,6100,1917,1919 }, {4519,5202,1455 ,4746,5550,1917 }, - {1416,4519,1455 ,6100,4746,1917 }, {4519,4491,5202 ,4746,107,5550 }, - {4491,4472,808 ,107,2189,4734 }, {4710,4770,4769 ,4136,4345,5542 }, - {3580,3626,3605 ,6069,4196,3383 }, {5966,297,1455 ,5054,1918,1917 }, - {7745,7795,7744 ,650,3804,6101 }, {6255,6256,6275 ,6102,5657,5576 }, - {6247,6255,6275 ,5575,6102,5576 }, {5782,3836,3385 ,5693,6103,6104 }, - {2990,1519,601 ,3646,2450,3905 }, {6256,5953,1557 ,5657,5659,5577 }, - {6275,6256,1557 ,5576,5657,5577 }, {5756,5792,5791 ,6076,6105,6077 }, - {5852,2213,32 ,6106,6107,6079 }, {5847,5852,32 ,6078,6106,6079 }, - {4322,181,32 ,6108,6080,6079 }, {2213,4322,32 ,6107,6108,6079 }, - {5851,5881,3870 ,6095,6109,6096 }, {1700,3563,2896 ,6072,6074,6110 }, - {4770,5263,4830 ,4345,4302,3642 }, {309,2375,5231 ,4472,4479,115 }, - {4697,5099,3800 ,4327,5494,4325 }, {882,8117,3264 ,3846,4119,2468 }, - {3769,5736,5070 ,6098,6111,6043 }, {1937,4796,4273 ,59,4756,120 }, - {5441,5143,297 ,5194,6099,1918 }, {4447,5143,3399 ,717,6099,715 }, - {670,262,312 ,4760,4758,4759 }, {6158,6284,6034 ,6112,6113,6049 }, - {543,3266,495 ,3840,3884,4389 }, {7073,1166,5844 ,662,2746,5179 }, - {1689,2004,4490 ,5792,5747,5688 }, {5767,5803,5766 ,5893,5871,4487 }, - {5803,5802,5766 ,5871,4681,4487 }, {5819,5818,5802 ,6114,4640,4681 }, - {5803,5819,5802 ,5871,6114,4681 }, {5819,5836,5835 ,6114,5859,4573 }, - {5818,5819,5835 ,4640,6114,4573 }, {2953,3986,1208 ,5837,5817,3211 }, - {212,2953,1208 ,4614,5837,3211 }, {1191,1586,1208 ,5816,943,3211 }, - {3986,1191,1208 ,5817,5816,3211 }, {5953,3870,3310 ,5659,6096,5587 }, - {1557,5953,3310 ,5577,5659,5587 }, {5946,3415,5907 ,5481,6115,6116 }, - {3217,5008,2342 ,6117,6118,5690 }, {3870,3686,5984 ,6096,6119,5872 }, - {3310,3870,5984 ,5587,6096,5872 }, {3686,5915,5894 ,6119,6120,5821 }, - {5984,3686,5894 ,5872,6119,5821 }, {6091,5328,5384 ,2173,2172,6121 }, - {6003,6004,5871 ,6122,6123,4124 }, {5328,5301,5384 ,2172,2698,6121 }, - {3715,3018,5851 ,6124,6125,6095 }, {5718,5757,5756 ,6126,6127,6076 }, - {5943,5514,6009 ,5402,6128,6129 }, {5757,5793,5792 ,6127,6130,6105 }, - {5756,5757,5792 ,6076,6127,6105 }, {5793,5791,5792 ,6130,6077,6105 }, - {5793,5825,5847 ,6130,6131,6078 }, {5791,5793,5847 ,6077,6130,6078 }, - {5847,5825,5852 ,6078,6131,6106 }, {5825,445,2213 ,6131,6132,6107 }, - {5852,5825,2213 ,6106,6131,6107 }, {1456,4322,2213 ,6133,6108,6107 }, - {445,1456,2213 ,6132,6133,6107 }, {1456,181,4322 ,6133,6080,6108 }, - {852,712,181 ,6134,3650,6080 }, {1456,852,181 ,6133,6134,6080 }, - {852,4230,712 ,6134,6135,3650 }, {4249,3702,712 ,6136,1303,3650 }, - {4230,4249,712 ,6135,6136,3650 }, {3606,3607,4164 ,6137,6138,6139 }, - {7873,7846,7847 ,817,4162,3521 }, {5477,5789,6036 ,5399,6140,6141 }, - {5905,5528,5820 ,6142,6143,6144 }, {1538,1101,3867 ,0,2,4507 }, - {4805,4645,3367 ,5376,5378,5798 }, {6950,6644,4981 ,1349,5098,1350 }, - {5274,5300,5251 ,6145,3793,3802 }, {2158,6639,4983 ,1160,3699,480 }, - {6300,6301,6708 ,6146,6147,6148 }, {5342,5143,4447 ,6149,6099,717 }, - {5685,1416,5143 ,6150,6100,6099 }, {5036,3249,4905 ,399,5110,5112 }, - {4830,4829,4770 ,3642,4626,4345 }, {5615,3001,3274 ,3171,3210,3172 }, - {6223,6254,6055 ,6151,5758,3874 }, {5898,5662,5653 ,5706,5915,3644 }, - {6243,6271,6270 ,5666,6152,6082 }, {5610,5609,6248 ,6153,5658,6154 }, - {1028,5843,5844 ,1373,1372,5179 }, {5915,6072,2568 ,6120,6155,5807 }, - {5894,5915,2568 ,5821,6120,5807 }, {6207,6208,6242 ,6156,6011,6081 }, - {7621,7620,7565 ,6010,5033,5080 }, {5718,5719,5757 ,6126,6157,6127 }, - {4231,4249,4230 ,6158,6136,6135 }, {852,4231,4230 ,6134,6158,6135 }, - {7648,7647,7621 ,3952,4967,6010 }, {4231,1185,4249 ,6158,6159,6136 }, - {5915,5905,5820 ,6120,6142,6144 }, {5528,5712,5975 ,6143,6160,6161 }, - {1700,6266,3563 ,6072,399,6074 }, {180,8,131 ,4777,3341,2965 }, - {4400,637,3299 ,4721,3805,3232 }, {5975,5391,2568 ,6161,5773,5807 }, - {1410,5540,3893 ,3911,6162,3789 }, {6072,5975,2568 ,6155,6161,5807 }, - {5253,6046,5391 ,6163,5775,5773 }, {5353,1342,3799 ,851,6164,4531 }, - {5352,5353,3799 ,3502,851,4531 }, {1342,3255,3282 ,6164,6165,6097 }, - {3799,1342,3282 ,4531,6164,6097 }, {3255,1527,3877 ,6165,6166,6167 }, - {3282,3255,3877 ,6097,6165,6167 }, {3255,3282,3877 ,6165,6097,6167 }, - {1527,3255,3877 ,6166,6165,6167 }, {3255,4013,3769 ,6165,6168,6098 }, - {3282,3255,3769 ,6097,6165,6098 }, {4013,4220,5736 ,6168,6169,6111 }, - {3769,4013,5736 ,6098,6168,6111 }, {4220,5052,5070 ,6169,5189,6043 }, - {5736,4220,5070 ,6111,6169,6043 }, {3669,3266,1783 ,5917,3884,2251 }, - {3800,1248,1907 ,4325,2850,1245 }, {1524,3800,1907 ,4326,4325,1245 }, - {5685,774,1416 ,6150,767,6100 }, {5222,5221,5182 ,4566,4864,4567 }, - {2432,1524,3818 ,5427,4326,1244 }, {5128,5260,2217 ,5966,4174,4569 }, - {4883,4801,5127 ,5538,5115,5114 }, {1195,1689,4347 ,725,5792,3287 }, - {4550,5563,4447 ,716,4605,717 }, {5482,5342,4447 ,3565,6149,717 }, - {5126,5482,4447 ,6170,3565,717 }, {5423,5143,5342 ,6171,6099,6149 }, - {5482,5423,5342 ,3565,6171,6149 }, {1634,5685,5143 ,768,6150,6099 }, - {5423,1634,5143 ,6171,768,6099 }, {1416,774,104 ,6100,767,4765 }, - {8052,3638,8051 ,4122,5950,6172 }, {2659,601,2622 ,3485,3905,2840 }, - {3800,3817,1248 ,4325,1189,2850 }, {4872,4697,1524 ,5522,4327,4326 }, - {2954,1538,3867 ,5687,0,4507 }, {5356,5909,4167 ,4258,4260,6173 }, - {5654,6188,5916 ,6026,6174,6175 }, {5975,5253,5391 ,6161,6163,5773 }, - {2342,5008,2273 ,5690,6118,5322 }, {5896,5713,6046 ,6176,5776,5775 }, - {5253,5896,6046 ,6163,6176,5775 }, {564,5309,507 ,3529,987,1523 }, - {5611,5634,5633 ,6177,6178,6179 }, {6949,5634,5672 ,4585,6178,5324 }, - {5633,5634,6949 ,6179,6178,4585 }, {6088,6082,6069 ,3670,1348,6180 }, - {5672,5719,5718 ,5324,6157,6126 }, {5848,2994,445 ,6181,6182,6132 }, - {5825,5848,445 ,6131,6181,6132 }, {2994,1456,445 ,6182,6133,6132 }, - {3703,1185,4231 ,6183,6159,6158 }, {5820,5528,5975 ,6144,6143,6161 }, - {6532,3547,3494 ,6184,803,736 }, {1516,4233,3177 ,1254,6185,1255 }, - {3494,3495,6532 ,736,735,6184 }, {7422,7478,7450 ,1404,1402,399 }, - {5160,3524,5969 ,6186,6187,6188 }, {3018,5530,5851 ,6125,6189,6095 }, - {5965,5329,6076 ,6190,6191,4217 }, {5886,6087,6084 ,6192,6193,6194 }, - {3217,2342,2273 ,6117,5690,5322 }, {3829,3217,2273 ,6195,6117,5322 }, - {5713,5896,4648 ,5776,6176,5722 }, {3562,3943,6013 ,4753,4755,6060 }, - {5353,5733,1342 ,851,1584,6164 }, {1342,1527,3255 ,6164,6166,6165 }, - {4013,3255,1527 ,6168,6165,6166 }, {5244,5266,5221 ,79,81,4864 }, - {3817,1207,1248 ,1189,1074,2850 }, {5685,1634,774 ,6150,768,767 }, - {7509,7508,7479 ,1874,6196,4423 }, {1251,5126,5563 ,4604,6170,4605 }, - {2661,5126,1251 ,4684,6170,4604 }, {5482,1634,5423 ,3565,768,6171 }, - {3816,6577,5445 ,5623,97,3520 }, {3244,5050,993 ,4433,5233,5551 }, - {1376,4788,1196 ,1937,3758,4330 }, {1700,81,323 ,6072,4673,4767 }, - {3939,4361,4307 ,3539,4584,4562 }, {4321,4343,4342 ,4873,1818,4950 }, - {5980,6091,6010 ,5557,2173,6197 }, {1426,1017,3452 ,4648,1793,1794 }, - {6175,3620,6163 ,6004,6001,1453 }, {5331,5371,5350 ,6198,3558,1526 }, - {5331,5372,5371 ,6198,6199,3558 }, {5372,5406,5405 ,6199,6200,5518 }, - {5371,5372,5405 ,3558,6199,5518 }, {5406,5436,5405 ,6200,6201,5518 }, - {5008,2342,2273 ,6118,5690,5322 }, {5594,5612,5611 ,5937,6202,6177 }, - {5593,5594,5611 ,5935,5937,6177 }, {5612,5635,5634 ,6202,6203,6178 }, - {5611,5612,5634 ,6177,6202,6178 }, {5635,5673,5672 ,6203,6204,5324 }, - {5634,5635,5672 ,6178,6203,5324 }, {5673,5674,5672 ,6204,6205,5324 }, - {5674,5720,5719 ,6205,6206,6157 }, {5672,5674,5719 ,5324,6205,6157 }, - {5720,5758,5757 ,6206,6207,6127 }, {5719,5720,5757 ,6157,6206,6127 }, - {5758,5794,5793 ,6207,6208,6130 }, {5757,5758,5793 ,6127,6207,6130 }, - {5794,5826,5825 ,6208,6209,6131 }, {5793,5794,5825 ,6130,6208,6131 }, - {5825,5826,5848 ,6131,6209,6181 }, {5826,3145,2994 ,6209,6210,6182 }, - {5848,5826,2994 ,6181,6209,6182 }, {3940,1456,2994 ,6211,6133,6182 }, - {3145,3940,2994 ,6210,6211,6182 }, {1598,852,1456 ,6212,6134,6133 }, - {3940,1598,1456 ,6211,6212,6133 }, {3928,4231,852 ,6213,6158,6134 }, - {1598,3928,852 ,6212,6213,6134 }, {3587,3703,4231 ,6214,6183,6158 }, - {3928,3587,4231 ,6213,6214,6158 }, {2861,1185,3703 ,6215,6159,6183 }, - {3587,2861,3703 ,6214,6215,6183 }, {1988,1516,1185 ,6216,1254,6159 }, - {2861,1988,1185 ,6215,6216,6159 }, {4295,4233,1516 ,5968,6185,1254 }, - {1988,4295,1516 ,6216,5968,1254 }, {4295,4324,4233 ,5968,6217,6185 }, - {733,1026,4233 ,4051,6218,6185 }, {4324,733,4233 ,6217,4051,6185 }, - {7220,7239,7219 ,4367,1527,1529 }, {7186,7203,7185 ,5370,4366,5073 }, - {3781,5884,5451 ,5849,5848,6219 }, {5879,5987,6033 ,6220,5621,5620 }, - {5969,5965,6076 ,6188,6190,4217 }, {6001,6095,6081 ,6221,6222,6223 }, - {5869,5886,6084 ,6224,6192,6194 }, {4094,4642,5670 ,6225,6226,6227 }, - {3497,747,5319 ,5738,5692,5739 }, {6010,6016,5692 ,6197,6228,6229 }, - {5734,4467,1342 ,6230,6231,6164 }, {5733,5734,1342 ,1584,6230,6164 }, - {4467,602,1527 ,6231,6232,6166 }, {1342,4467,1527 ,6164,6231,6166 }, - {4220,4013,1527 ,6169,6168,6166 }, {602,4220,1527 ,6232,6169,6166 }, - {1537,1745,4191 ,4473,6233,4474 }, {1251,306,4166 ,4604,6234,6084 }, - {2661,5482,5126 ,4684,3565,6170 }, {3266,4669,4775 ,3884,3879,4388 }, - {1745,1444,4191 ,6233,4035,4474 }, {6242,6243,6270 ,6081,5666,6082 }, - {5171,5195,6114 ,1804,6235,1805 }, {5278,5277,5232 ,6236,6237,6238 }, - {5533,4010,6172 ,6239,4343,5451 }, {4465,4444,6210 ,257,582,5566 }, - {5845,6234,6174 ,143,5973,2381 }, {3943,5860,5956 ,4755,5998,6240 }, - {5636,5674,5673 ,6241,6205,6204 }, {5635,5636,5673 ,6203,6241,6204 }, - {5720,5759,5758 ,6206,6242,6207 }, {5758,5759,5794 ,6207,6242,6208 }, - {3942,3928,1598 ,6243,6213,6212 }, {3942,3587,3928 ,6243,6214,6213 }, - {184,733,4324 ,4052,4051,6217 }, {4295,184,4324 ,5968,4052,6217 }, - {7462,7435,7409 ,4310,6244,6245 }, {5973,6110,6081 ,6246,6247,6223 }, - {5711,5879,6033 ,6248,6220,5620 }, {2837,75,930 ,2511,3962,3594 }, - {6001,5996,6156 ,6221,6249,6250 }, {6001,6181,5705 ,6221,6251,4127 }, - {6081,3395,5185 ,6223,5940,5939 }, {5927,6040,5227 ,5318,5317,4097 }, - {6197,6223,6222 ,6252,6151,6253 }, {4026,4467,5734 ,6254,6231,6230 }, - {4261,4026,5734 ,1583,6254,6230 }, {4467,4026,602 ,6231,6254,6232 }, - {5459,4220,602 ,6255,6169,6232 }, {5459,993,5052 ,6255,5551,5189 }, - {4220,5459,5052 ,6169,6255,5189 }, {4908,4724,4774 ,5879,4737,5332 }, - {252,5526,966 ,2743,6256,2565 }, {3838,1016,4166 ,6257,6085,6084 }, - {5127,2661,1251 ,5114,4684,4604 }, {1016,5127,1251 ,6085,5114,4604 }, - {5127,124,2661 ,5114,2206,4684 }, {125,124,5127 ,399,2206,5114 }, - {1790,4949,5245 ,4738,5526,5880 }, {1444,3318,2690 ,4035,4036,6022 }, - {4191,1444,2690 ,4474,4035,6022 }, {3318,3419,2797 ,4036,2012,6030 }, - {2659,2622,2901 ,3485,2840,3468 }, {5609,6135,5953 ,5658,6258,5659 }, - {5278,5331,5277 ,6236,6198,6237 }, {5406,5437,5436 ,6200,6259,6201 }, - {5516,5552,5551 ,5420,5936,6260 }, {5612,5636,5635 ,6202,6241,6203 }, - {5720,5745,5759 ,6206,6261,6242 }, {1151,3587,3942 ,6262,6214,6243 }, - {2861,4295,1988 ,6215,5968,6216 }, {4016,4295,4348 ,5969,5968,6263 }, - {5898,6038,6035 ,5706,6264,6025 }, {6001,5705,5996 ,6221,4127,6249 }, - {3395,5854,6142 ,5940,6265,5941 }, {3583,5651,6067 ,5772,5920,6266 }, - {75,3510,1629 ,3962,6267,4207 }, {5654,5916,4425 ,6026,6175,6268 }, - {5987,5451,5608 ,5621,6219,4356 }, {5662,3943,5944 ,5915,4755,4754 }, - {5653,5662,5944 ,3644,5915,4754 }, {4209,4290,3777 ,6269,6270,1582 }, - {789,3985,2797 ,3910,4802,6030 }, {2690,3318,2797 ,6022,4036,6030 }, - {5803,5836,5819 ,5871,5859,6114 }, {7128,7160,7159 ,5391,6271,5075 }, - {993,5050,1154 ,5551,5233,4545 }, {5214,5213,5171 ,6272,428,1804 }, - {5172,5214,5171 ,5468,6272,1804 }, {5234,5233,5213 ,6273,429,428 }, - {5256,5232,5233 ,6274,6238,429 }, {5234,5256,5233 ,6273,6274,429 }, - {5279,5278,5232 ,6275,6236,6238 }, {5256,5279,5232 ,6274,6275,6238 }, - {5332,5331,5278 ,6276,6198,6236 }, {5279,5332,5278 ,6275,6276,6236 }, - {5332,5373,5372 ,6276,6277,6199 }, {5331,5332,5372 ,6198,6276,6199 }, - {5373,5407,5406 ,6277,6278,6200 }, {5372,5373,5406 ,6199,6277,6200 }, - {5407,5408,5406 ,6278,6279,6200 }, {5408,5438,5437 ,6279,4156,6259 }, - {5406,5408,5437 ,6200,6279,6259 }, {5479,5517,5516 ,5387,6280,5420 }, - {5517,5553,5552 ,6280,6281,5936 }, {5516,5517,5552 ,5420,6280,5936 }, - {5553,5554,5552 ,6281,6282,5936 }, {5554,5595,5594 ,6282,6283,5937 }, - {5552,5554,5594 ,5936,6282,5937 }, {5594,5595,5612 ,5937,6283,6202 }, - {5595,5637,5636 ,6283,6284,6241 }, {5612,5595,5636 ,6202,6283,6241 }, - {5637,5675,5674 ,6284,6285,6205 }, {5636,5637,5674 ,6241,6284,6205 }, - {5675,5721,5720 ,6285,6286,6206 }, {5674,5675,5720 ,6205,6285,6206 }, - {5721,5722,5745 ,6286,6287,6261 }, {5720,5721,5745 ,6206,6286,6261 }, - {5722,5760,5759 ,6287,6288,6242 }, {5745,5722,5759 ,6261,6287,6242 }, - {5760,5795,5794 ,6288,6289,6208 }, {5759,5760,5794 ,6242,6288,6208 }, - {5795,5827,5826 ,6289,6290,6209 }, {5794,5795,5826 ,6208,6289,6209 }, - {5827,5828,3145 ,6290,6291,6210 }, {5826,5827,3145 ,6209,6290,6210 }, - {5828,4288,3145 ,6291,6292,6210 }, {4288,4287,3145 ,6292,6293,6210 }, - {3480,3940,3145 ,6294,6211,6210 }, {4287,3480,3145 ,6293,6294,6210 }, - {995,1598,3940 ,6295,6212,6211 }, {3480,995,3940 ,6294,6295,6211 }, - {3983,3942,1598 ,6296,6243,6212 }, {995,3983,1598 ,6295,6296,6212 }, - {1522,1151,3942 ,6297,6262,6243 }, {3983,1522,3942 ,6296,6297,6243 }, - {1522,3587,1151 ,6297,6214,6262 }, {1732,2861,3587 ,6298,6215,6214 }, - {1522,1732,3587 ,6297,6298,6214 }, {4232,4295,2861 ,6299,5968,6215 }, - {1732,4232,2861 ,6298,6299,6215 }, {4232,4348,4295 ,6299,6263,5968 }, - {4321,3528,5630 ,4873,5061,4874 }, {4017,4016,4232 ,5342,5969,6299 }, - {5982,5924,5649 ,4218,6091,6093 }, {5705,6181,5992 ,4127,6251,4197 }, - {6130,6132,6131 ,6300,5897,6050 }, {5472,3443,5869 ,6301,6302,6224 }, - {7168,7185,7160 ,5371,5073,6271 }, {1555,4290,4209 ,6303,6270,6269 }, - {1554,1555,4209 ,6304,6303,6269 }, {1555,4261,4290 ,6303,1583,6270 }, - {3771,4026,4261 ,6305,6254,1583 }, {1555,3771,4261 ,6303,6305,1583 }, - {3010,602,4026 ,6306,6232,6254 }, {3771,3010,4026 ,6305,6306,6254 }, - {3242,5459,602 ,6307,6255,6232 }, {3010,3242,602 ,6306,6307,6232 }, - {3242,3244,993 ,6307,4433,5551 }, {5459,3242,993 ,6255,6307,5551 }, - {1207,3817,5153 ,1074,1189,3896 }, {6749,5548,5512 ,4046,1590,1589 }, - {5457,5387,1252 ,6308,6309,5023 }, {5387,3838,3839 ,6309,6257,6310 }, - {1388,1016,3838 ,6311,6085,6257 }, {5387,1388,3838 ,6309,6311,6257 }, - {5617,5127,1016 ,6312,5114,6085 }, {2621,1606,3815 ,5416,3677,5556 }, - {5321,5346,5320 ,5668,3267,4464 }, {5172,5215,5214 ,5468,6313,6272 }, - {5234,5279,5256 ,6273,6275,6274 }, {5332,5374,5373 ,6276,6314,6277 }, - {5374,5408,5407 ,6314,6279,6278 }, {5373,5374,5407 ,6277,6314,6278 }, - {7371,7370,7310 ,3748,3750,4283 }, {5479,5492,5517 ,5387,6315,6280 }, - {5517,5518,5553 ,6280,6316,6281 }, {5796,5828,5827 ,6317,6291,6290 }, - {5795,5796,5827 ,6289,6317,6290 }, {3665,3480,4287 ,6318,6294,6293 }, - {4288,3665,4287 ,6292,6318,6293 }, {1491,1732,1522 ,6319,6298,6297 }, - {4348,4232,4016 ,6263,6299,5969 }, {4921,1551,5082 ,2079,5275,5218 }, - {5786,5082,3872 ,4813,5218,4811 }, {6081,5185,5992 ,6223,5939,4197 }, - {6181,6081,5992 ,6251,6223,4197 }, {3726,3771,1555 ,6320,6305,6303 }, - {3726,3010,3771 ,6320,6306,6305 }, {4834,955,5787 ,4304,3991,4305 }, - {5387,3839,1252 ,6309,6310,5023 }, {4241,5387,5457 ,6321,6309,6308 }, - {5225,1388,5387 ,6322,6311,6309 }, {4883,5617,1016 ,5538,6312,6085 }, - {1388,4883,1016 ,6311,5538,6085 }, {5617,4883,5127 ,6312,5538,5114 }, - {3760,3669,1783 ,2250,5917,2251 }, {5267,5290,5244 ,4647,5247,79 }, - {5215,5172,5216 ,6313,5468,6323 }, {4842,4776,4745 ,5429,5390,5446 }, - {5492,5518,5517 ,6315,6316,6280 }, {5518,5554,5553 ,6316,6282,6281 }, - {5696,5722,5721 ,6324,6287,6286 }, {5675,5696,5721 ,6285,6324,6286 }, - {5760,5796,5795 ,6288,6317,6289 }, {4188,995,3480 ,6325,6295,6294 }, - {3665,4188,3480 ,6318,6325,6294 }, {2957,603,2093 ,6326,5274,553 }, - {5189,5188,3872 ,6327,4812,4811 }, {803,101,6204 ,144,5,4050 }, - {248,1554,4530 ,4114,6304,1376 }, {3118,3010,3726 ,6328,6306,6320 }, - {3118,3242,3010 ,6328,6307,6306 }, {455,556,4471 ,2205,4043,2187 }, - {5205,5228,5227 ,5219,4095,4097 }, {4649,5152,5125 ,5288,6329,5467 }, - {4702,5173,5125 ,5291,6330,5467 }, {5152,4702,5125 ,6329,5291,5467 }, - {4702,5172,5173 ,5291,5468,6330 }, {4702,5216,5172 ,5291,6323,5468 }, - {5257,5234,5214 ,6331,6273,6272 }, {5280,5279,5234 ,6332,6275,6273 }, - {5257,5280,5234 ,6331,6332,6273 }, {5333,5332,5279 ,6333,6276,6275 }, - {5280,5333,5279 ,6332,6333,6275 }, {5375,5374,5332 ,6334,6314,6276 }, - {5333,5375,5332 ,6333,6334,6276 }, {5375,5409,5408 ,6334,6335,6279 }, - {5374,5375,5408 ,6314,6334,6279 }, {5409,5439,5438 ,6335,4157,4156 }, - {5408,5409,5438 ,6279,6335,4156 }, {7160,7138,7168 ,6271,2560,5371 }, - {5493,5519,5518 ,6336,6337,6316 }, {5479,5493,5492 ,5387,6336,6315 }, - {5519,5555,5554 ,6337,6338,6282 }, {5518,5519,5554 ,6316,6337,6282 }, - {5555,5596,5595 ,6338,6339,6283 }, {5554,5555,5595 ,6282,6338,6283 }, - {5596,5638,5637 ,6339,6340,6284 }, {5595,5596,5637 ,6283,6339,6284 }, - {5638,5676,5675 ,6340,6341,6285 }, {5637,5638,5675 ,6284,6340,6285 }, - {5676,5677,5696 ,6341,6342,6324 }, {5675,5676,5696 ,6285,6341,6324 }, - {5677,5723,5722 ,6342,6343,6287 }, {5696,5677,5722 ,6324,6342,6287 }, - {5723,5761,5760 ,6343,6344,6288 }, {5722,5723,5760 ,6287,6343,6288 }, - {5761,5783,5760 ,6344,6345,6288 }, {5783,5797,5796 ,6345,6346,6317 }, - {5760,5783,5796 ,6288,6345,6317 }, {5797,5829,5828 ,6346,6347,6291 }, - {5796,5797,5828 ,6317,6346,6291 }, {5829,1667,4288 ,6347,6348,6292 }, - {5828,5829,4288 ,6291,6347,6292 }, {1667,1019,3665 ,6348,6349,6318 }, - {4288,1667,3665 ,6292,6348,6318 }, {1540,4188,3665 ,6350,6325,6318 }, - {1019,1540,3665 ,6349,6350,6318 }, {1540,995,4188 ,6350,6295,6325 }, - {2499,3983,995 ,6351,6296,6295 }, {1540,2499,995 ,6350,6351,6295 }, - {2499,3988,3983 ,6351,6352,6296 }, {3974,1522,3983 ,6353,6297,6296 }, - {3988,3974,3983 ,6352,6353,6296 }, {218,1491,1522 ,6354,6319,6297 }, - {3974,218,1522 ,6353,6354,6297 }, {218,1732,1491 ,6354,6298,6319 }, - {2809,4232,1732 ,6355,6299,6298 }, {218,2809,1732 ,6354,6355,6298 }, - {2809,4208,4232 ,6355,6356,6299 }, {7129,7138,7160 ,1026,2560,6271 }, - {7138,7169,7168 ,2560,5437,5371 }, {876,5859,1120 ,5340,6357,5339 }, - {5859,37,1120 ,6357,6358,5339 }, {2957,1120,37 ,6326,5339,6358 }, - {37,555,2957 ,6358,6359,6326 }, {555,551,603 ,6359,6360,5274 }, - {2957,555,603 ,6326,6359,5274 }, {1523,1551,603 ,6361,5275,5274 }, - {551,1523,603 ,6360,6361,5274 }, {1108,3872,1551 ,6362,4811,5275 }, - {1523,1108,1551 ,6361,6362,5275 }, {3361,5189,3872 ,6363,6327,4811 }, - {1108,3361,3872 ,6362,6363,4811 }, {3361,5188,5189 ,6363,4812,6327 }, - {1861,3350,5188 ,6364,2541,4812 }, {3361,1861,5188 ,6363,6364,4812 }, - {1861,5856,3350 ,6364,6365,2541 }, {5856,5806,3350 ,6365,4113,2541 }, - {3418,248,5806 ,6366,4114,4113 }, {3418,5341,1554 ,6366,6367,6304 }, - {4025,1555,1554 ,6368,6303,6304 }, {5341,4025,1554 ,6367,6368,6304 }, - {3774,3726,1555 ,6369,6320,6303 }, {4025,3774,1555 ,6368,6369,6303 }, - {2991,3118,3726 ,6370,6328,6320 }, {3774,2991,3726 ,6369,6370,6320 }, - {5201,3242,3118 ,6371,6307,6328 }, {2991,5201,3118 ,6370,6371,6328 }, - {5201,5174,3242 ,6371,6372,6307 }, {4397,3244,3242 ,4434,4433,6307 }, - {5174,4397,3242 ,6372,4434,6307 }, {1253,3778,4068 ,2353,5585,408 }, - {5020,4272,4241 ,6373,6374,6321 }, {5245,5387,4241 ,5880,6309,6321 }, - {4272,5245,4241 ,6374,5880,6321 }, {5245,4908,5387 ,5880,5879,6309 }, - {4908,5161,5225 ,5879,6375,6322 }, {5387,4908,5225 ,6309,5879,6322 }, - {5161,4774,1388 ,6375,5332,6311 }, {5225,5161,1388 ,6322,6375,6311 }, - {5153,4682,3899 ,3896,1191,1938 }, {4702,5152,4649 ,5291,6329,5288 }, - {1922,4397,1085 ,4432,4434,369 }, {5519,5535,5555 ,6337,3512,6338 }, - {5596,5613,5638 ,6339,6376,6340 }, {5638,5677,5676 ,6340,6342,6341 }, - {2499,3974,3988 ,6351,6353,6352 }, {4208,2809,876 ,6356,6355,5340 }, - {876,37,5859 ,5340,6358,6357 }, {2913,414,551 ,6377,6378,6360 }, - {555,2913,551 ,6359,6377,6360 }, {671,1523,551 ,6379,6361,6360 }, - {414,671,551 ,6378,6379,6360 }, {4309,1108,1523 ,6380,6362,6361 }, - {671,4309,1523 ,6379,6380,6361 }, {3630,5806,5856 ,6381,4113,6365 }, - {1861,3630,5856 ,6364,6381,6365 }, {3287,4025,5341 ,6382,6368,6367 }, - {3418,3287,5341 ,6366,6382,6367 }, {4025,5661,3774 ,6368,6383,6369 }, - {5174,4117,4397 ,6372,4700,4434 }, {6785,6803,5548 ,4140,4169,1590 }, - {5292,4272,5175 ,5093,6374,6384 }, {4908,4774,5161 ,5879,5332,6375 }, - {5224,4714,5267 ,3850,1031,4647 }, {3732,2785,3419 ,3536,1246,2012 }, - {3318,3732,3419 ,4036,3536,2012 }, {5235,5280,5257 ,6385,6332,6331 }, - {5439,5410,5440 ,4157,4285,5147 }, {5104,2246,5755 ,6063,591,4047 }, - {6697,5512,5471 ,3866,1589,2051 }, {6680,6697,5471 ,3834,3866,2051 }, - {5590,6173,6002 ,6386,3711,5975 }, {5555,5568,5596 ,6338,6387,6339 }, - {5638,5655,5677 ,6340,6388,6342 }, {1585,3974,2499 ,6389,6353,6351 }, - {876,4346,37 ,5340,6390,6358 }, {37,2913,555 ,6358,6377,6359 }, - {4309,3361,1108 ,6380,6363,6362 }, {1384,3418,5806 ,6391,6366,4113 }, - {3630,1384,5806 ,6381,6391,4113 }, {4025,3287,5661 ,6368,6382,6383 }, - {3775,2991,3774 ,6392,6370,6369 }, {5661,3775,3774 ,6383,6392,6369 }, - {4397,4117,1859 ,4434,4700,4480 }, {4272,5020,5175 ,6374,6373,6384 }, - {6697,6749,5512 ,3866,4046,1589 }, {3367,4645,4926 ,5798,5378,3897 }, - {1416,104,4519 ,6100,4765,4746 }, {5217,5216,4702 ,4026,6323,5291 }, - {4703,5217,4702 ,5290,4026,5291 }, {5217,5235,5216 ,4026,6385,6323 }, - {5217,5258,5235 ,4026,1193,6385 }, {5281,5280,5235 ,1195,6332,6385 }, - {5258,5281,5235 ,1193,1195,6385 }, {5281,5282,5280 ,1195,1194,6332 }, - {5282,5334,5333 ,1194,2736,6333 }, {5280,5282,5333 ,6332,1194,6333 }, - {5334,5376,5375 ,2736,2758,6334 }, {5333,5334,5375 ,6333,2736,6334 }, - {5410,5409,5375 ,4285,6335,6334 }, {5376,5410,5375 ,2758,4285,6334 }, - {5409,5410,5439 ,6335,4285,4157 }, {5172,5125,5173 ,5468,5467,6330 }, - {1353,1395,1352 ,1205,1164,1262 }, {5556,5557,5568 ,3511,756,6387 }, - {5555,5556,5568 ,6338,3511,6387 }, {5557,5597,5596 ,756,6393,6339 }, - {5568,5557,5596 ,6387,756,6339 }, {5557,5598,5597 ,756,2216,6393 }, - {5596,5597,5613 ,6339,6393,6376 }, {5598,5639,5638 ,2216,2215,6340 }, - {5613,5598,5638 ,6376,2216,6340 }, {5639,5656,5655 ,2215,3864,6388 }, - {5638,5639,5655 ,6340,2215,6388 }, {5656,5678,5677 ,3864,3861,6342 }, - {5655,5656,5677 ,6388,3864,6342 }, {5678,5724,5723 ,3861,4898,6343 }, - {5677,5678,5723 ,6342,3861,6343 }, {5724,5762,5761 ,4898,4884,6344 }, - {5723,5724,5761 ,6343,4898,6344 }, {5761,5762,5783 ,6344,4884,6345 }, - {5762,5798,5797 ,4884,4876,6346 }, {5783,5762,5797 ,6345,4884,6346 }, - {5798,5830,5829 ,4876,4163,6347 }, {5797,5798,5829 ,6346,4876,6347 }, - {1794,1667,5829 ,4129,6348,6347 }, {5830,1794,5829 ,4163,4129,6347 }, - {1794,2795,1019 ,4129,3975,6349 }, {1667,1794,1019 ,6348,4129,6349 }, - {1607,1540,1019 ,3941,6350,6349 }, {2795,1607,1019 ,3975,3941,6349 }, - {2477,2499,1540 ,642,6351,6350 }, {1607,2477,1540 ,3941,642,6350 }, - {87,1585,2499 ,641,6389,6351 }, {2477,87,2499 ,642,641,6351 }, - {87,3974,1585 ,641,6353,6389 }, {1587,218,3974 ,837,6354,6353 }, - {87,1587,3974 ,641,837,6353 }, {96,2809,218 ,836,6355,6354 }, - {1587,96,218 ,837,836,6354 }, {96,3257,876 ,836,4010,5340 }, - {2809,96,876 ,6355,836,5340 }, {876,3257,4346 ,5340,4010,6390 }, - {3257,5737,37 ,4010,6394,6358 }, {4346,3257,37 ,6390,4010,6358 }, - {5737,1974,37 ,6394,3862,6358 }, {1974,62,2913 ,3862,3831,6377 }, - {37,1974,2913 ,6358,3862,6377 }, {34,414,2913 ,3830,6378,6377 }, - {62,34,2913 ,3831,3830,6377 }, {3980,671,414 ,4933,6379,6378 }, - {34,3980,414 ,3830,4933,6378 }, {164,4309,671 ,4932,6380,6379 }, - {3980,164,671 ,4933,4932,6379 }, {52,3361,4309 ,411,6363,6380 }, - {164,52,4309 ,4932,411,6380 }, {3113,1861,3361 ,410,6364,6363 }, - {52,3113,3361 ,411,410,6363 }, {3113,269,1861 ,410,3813,6364 }, - {1734,3630,1861 ,2219,6381,6364 }, {269,1734,1861 ,3813,2219,6364 }, - {878,1384,3630 ,398,6391,6381 }, {1734,878,3630 ,2219,398,6381 }, - {878,3418,1384 ,398,6366,6391 }, {4011,3287,3418 ,395,6382,6366 }, - {878,4011,3418 ,398,395,6366 }, {4011,3662,5661 ,395,1942,6383 }, - {3287,4011,5661 ,6382,395,6383 }, {5304,3775,5661 ,654,6392,6383 }, - {3662,5304,5661 ,1942,654,6383 }, {5304,2991,3775 ,654,6370,6392 }, - {5481,5201,2991 ,3863,6371,6370 }, {5304,5481,2991 ,654,3863,6370 }, - {5481,4117,5201 ,3863,4700,6371 }, {4117,5174,5201 ,4700,6372,6371 }, - {104,1150,4519 ,4765,741,4746 }, {3899,1376,1196 ,1938,1937,4330 }, - {7410,7462,7409 ,3766,4310,6245 }, {7033,6152,5945 ,6395,6044,6396 }, - {5562,5945,6152 ,4193,6396,6044 }, {1790,4272,5292 ,4738,6374,5093 }, - {5017,1790,5292 ,5092,4738,5093 }, {4272,1790,5245 ,6374,4738,5880 }, - {3419,789,2797 ,2012,3910,6030 }, {5153,3899,5146 ,3896,1938,1075 }, - {4502,1859,4117 ,4650,4480,4700 }, {6648,6680,6647 ,1390,3834,2050 }, - {6648,6647,6611 ,1390,2050,1391 }, {5557,8043,5598 ,756,6397,2216 }, - {6530,1990,1217 ,6398,1305,6399 }, {5613,5597,5598 ,6376,6393,2216 }, - {1539,1587,87 ,835,837,641 }, {96,3909,3257 ,836,4164,4010 }, - {3257,1974,5737 ,4010,3862,6394 }, {3688,4292,4153 ,5574,6400,5711 }, - {3470,6003,5917 ,6401,6122,6402 }, {350,5653,1273 ,1114,3644,6403 }, - {4286,5580,3528 ,4274,6404,5061 }, {5392,5947,5717 ,4022,6027,4023 }, - {2044,465,501 ,4031,4578,4313 }, {670,5043,5985 ,4760,4762,5365 }, - {7960,100,2306 ,3505,2823,6405 }, {5882,3674,3728 ,4397,4286,4251 }, - {5575,5958,6112 ,5686,5702,6406 }, {8023,8038,8022 ,4582,4121,4123 }, - {4284,4229,4270 ,4869,4877,4900 }, {3778,1253,1583 ,5585,2353,5415 }, - {6267,3607,3606 ,6407,6138,6137 }, {6012,5963,6004 ,6408,6409,6123 }, - {6241,6242,6269 ,6410,6081,6083 }, {5129,5293,5314 ,5698,5889,6411 }, - {3659,5129,5314 ,3875,5698,6411 }, {6141,6122,2072 ,2386,1797,1812 }, - {3559,5567,5897 ,6412,6413,6414 }, {4058,206,4054 ,4708,3932,3679 }, - {5650,5981,3905 ,2713,6415,4194 }, {6227,5906,5462 ,6416,6417,6418 }, - {3813,3758,5926 ,1515,1789,6419 }, {5662,6035,4045 ,5915,6025,5916 }, - {5687,5048,5101 ,6420,6421,6422 }, {5361,6227,5462 ,5664,6416,6418 }, - {206,4054,4055 ,3932,3679,3678 }, {5488,3518,5473 ,2730,4338,3429 }, - {3528,5580,5630 ,5061,6404,4874 }, {6113,3608,5461 ,5873,5799,5744 }, - {6057,5994,5893 ,6423,6424,6425 }, {5994,5524,1008 ,6424,6426,6427 }, - {3599,6057,5893 ,6428,6423,6425 }, {4153,3688,3953 ,5711,5574,5713 }, - {5893,5994,1008 ,6425,6424,6427 }, {6096,2357,4043 ,4092,3933,419 }, - {2142,6031,5706 ,4936,6429,3796 }, {3007,6070,3582 ,818,820,1378 }, - {7437,7436,7386 ,5938,4245,5512 }, {6168,5646,5316 ,6430,6431,6087 }, - {5524,5646,1008 ,6426,6431,6427 }, {5646,5743,5316 ,6431,6432,6087 }, - {5346,6260,5320 ,3267,3397,4464 }, {8095,8062,8090 ,4362,3272,412 }, - {6128,6129,5123 ,5981,5983,6433 }, {5646,6168,1008 ,6431,6430,6427 }, - {6023,5646,5524 ,6040,6431,6426 }, {5875,3797,5490 ,6434,2061,6055 }, - {5550,5878,5549 ,6047,2684,6435 }, {5452,5650,3905 ,2714,2713,4194 }, - {3803,3720,3804 ,6436,6437,4324 }, {6001,6003,6095 ,6221,6122,6222 }, - {5345,6057,3599 ,6438,6423,6428 }, {5392,1741,5947 ,4022,5695,6027 }, - {187,2159,1171 ,6439,6440,6441 }, {3804,3720,5882 ,4324,6437,4397 }, - {3720,6160,3674 ,6437,6442,4286 }, {5882,3720,3674 ,4397,6437,4286 }, - {5254,5576,3436 ,4042,4039,2145 }, {7688,7689,7712 ,6443,6444,5350 }, - {4528,5550,5549 ,4935,6047,6435 }, {2159,4528,5549 ,6440,4935,6435 }, - {6230,6104,3436 ,2144,5696,2145 }, {5303,2636,5315 ,5737,2712,6445 }, - {3688,4153,3953 ,5574,5711,5713 }, {5880,3666,6137 ,5700,5699,5809 }, - {6059,3679,5906 ,6446,6447,6417 }, {6094,5977,5742 ,692,809,6448 }, - {3373,6094,5742 ,693,692,6448 }, {4807,5426,5970 ,6449,5634,3518 }, - {381,3522,1627 ,6450,6451,5573 }, {2636,5303,5650 ,2712,5737,2713 }, - {5862,3893,4623 ,3790,3789,6452 }, {3893,5861,4623 ,3789,6453,6452 }, - {5609,5851,6135 ,5658,6095,6258 }, {5297,4178,5272 ,3826,3825,5956 }, - {5044,5192,5303 ,5173,6454,5737 }, {5922,6149,3445 ,4292,732,1791 }, - {7089,7103,5115 ,5608,6455,5913 }, {4433,4820,4795 ,4591,2399,3710 }, - {5462,5906,5961 ,6418,6417,6456 }, {5192,4410,6251 ,6454,5735,6457 }, - {6224,6248,6213 ,6458,6154,6459 }, {2636,5452,5386 ,2712,2714,5870 }, - {6010,5384,6016 ,6197,6121,6228 }, {6058,5345,5416 ,6460,6438,6461 }, - {5384,3631,5403 ,6121,5922,5921 }, {5235,5257,5214 ,6385,6331,6272 }, - {6254,3906,6055 ,5758,5697,3874 }, {3411,5193,1741 ,4280,5846,5695 }, - {6003,5973,6095 ,6122,6246,6222 }, {3415,7082,6186 ,6115,6462,4365 }, - {6205,6206,6240 ,6463,6464,5756 }, {5213,5195,5171 ,428,6235,1804 }, - {5888,5902,6093 ,5752,5769,5771 }, {7423,7479,7422 ,3749,4423,1404 }, - {5322,5320,5695 ,4297,4464,6465 }, {5874,5976,5925 ,4896,5903,5710 }, - {7690,449,448 ,6466,1169,1172 }, {5958,5122,3507 ,5702,4024,5671 }, - {3930,1627,4247 ,6467,5573,6468 }, {3628,6235,5703 ,4213,4215,5748 }, - {5567,5526,5897 ,6413,6256,6414 }, {5205,5248,5247 ,5219,6469,4096 }, - {6254,6268,3906 ,5758,5757,5697 }, {5609,6256,6213 ,5658,5657,6459 }, - {6102,3868,3879 ,2464,970,2549 }, {5055,3979,1261 ,6470,6471,6472 }, - {4247,4246,381 ,6468,3665,6450 }, {381,1627,3930 ,6450,5573,6467 }, - {7293,7248,7322 ,6473,73,75 }, {5926,3758,3714 ,6419,1789,3970 }, - {6004,6003,5974 ,6123,6122,6474 }, {5993,3953,5580 ,6475,5713,6404 }, - {7423,7422,7370 ,3749,1404,3750 }, {1701,1949,216 ,6476,3637,4644 }, - {5851,5530,5881 ,6095,6189,6109 }, {4574,6062,5974 ,6477,6478,6474 }, - {1532,1051,217 ,4771,4588,4643 }, {6075,5922,5908 ,6042,4292,4293 }, - {5662,5898,6035 ,5915,5706,6025 }, {1605,6206,6205 ,17,6464,6463 }, - {5971,5903,5902 ,6479,6480,5769 }, {5902,5903,686 ,5769,6480,5770 }, - {5753,5055,5916 ,6481,6470,6175 }, {3522,6227,758 ,6451,6416,5663 }, - {6024,3410,5486 ,6482,6483,5718 }, {6225,5610,6248 ,4872,6153,6154 }, - {4517,6058,5416 ,6484,6460,6461 }, {5923,5591,3979 ,2073,3597,6471 }, - {1854,635,5971 ,2184,2183,6479 }, {4540,4526,4515 ,19,187,30 }, - {5318,3360,6023 ,6039,6485,6040 }, {5972,5988,4147 ,399,2516,2517 }, - {6225,6248,6224 ,4872,6154,6458 }, {1661,6179,3906 ,5672,6486,5697 }, - {6070,4486,3582 ,820,819,1378 }, {6240,6241,1661 ,5756,6410,5672 }, - {3717,3715,5630 ,6487,6124,4874 }, {3902,4807,5970 ,4437,6449,3518 }, - {3051,5474,5877 ,6488,4243,4242 }, {5388,5446,6317 ,5428,5985,3207 }, - {6032,5472,5869 ,6489,6301,6224 }, {5472,3409,6032 ,6301,6490,6489 }, - {3360,3386,5472 ,6485,6491,6301 }, {3409,5472,6032 ,6490,6301,6489 }, - {20,2657,3506 ,3961,2185,6492 }, {6018,4304,1747 ,340,4579,266 }, - {5185,6142,3632 ,5939,5941,5898 }, {5887,5888,6087 ,6493,5752,6193 }, - {6089,6050,6087 ,6494,4094,6193 }, {6023,6024,5646 ,6040,6482,6431 }, - {5055,5476,5916 ,6470,6495,6175 }, {5906,6006,3560 ,6417,6496,5779 }, - {5630,5610,5609 ,4874,6153,5658 }, {5888,6093,6050 ,5752,5771,4094 }, - {5319,747,5781 ,5739,5692,5694 }, {4229,4246,4270 ,4877,3665,4900 }, - {3680,3499,405 ,6497,5661,5662 }, {6084,3499,3680 ,6194,5661,6497 }, - {5961,5906,3560 ,6456,6417,5779 }, {6006,6043,3560 ,6496,6498,5779 }, - {5866,5867,4094 ,5302,6499,6225 }, {3560,6043,5318 ,5779,6498,6039 }, - {5867,5869,5866 ,6499,6224,5302 }, {5273,5298,5272 ,3819,3818,5956 }, - {5513,6123,4147 ,6500,4221,2517 }, {1417,3522,381 ,6501,6451,6450 }, - {3638,278,8051 ,5950,1489,6172 }, {5866,4094,4410 ,5302,6225,5735 }, - {5192,5866,4410 ,6454,5302,5735 }, {3525,5935,5885 ,5665,5778,6502 }, - {6168,5316,1008 ,6430,6087,6427 }, {3517,1729,5580 ,6503,3666,6404 }, - {6052,3311,3762 ,5717,5303,5172 }, {5303,5981,5650 ,5737,6415,2713 }, - {5303,5315,5044 ,5737,6445,5173 }, {1700,706,807 ,6072,4784,6073 }, - {3581,3508,6017 ,6504,6505,6506 }, {3508,5964,6017 ,6505,6507,6506 }, - {3879,5317,6102 ,2549,6508,2464 }, {5317,5382,3831 ,6508,6509,2321 }, - {6102,5317,3831 ,2464,6508,2321 }, {5382,4154,3803 ,6509,6510,6436 }, - {3831,5382,3803 ,2321,6509,6436 }, {1606,4054,206 ,3677,3679,3932 }, - {3953,3018,6124 ,5713,6125,6511 }, {5567,3559,5955 ,6413,6412,6512 }, - {5351,3386,5318 ,6513,6491,6039 }, {3443,6077,5887 ,6302,5753,6493 }, - {5988,20,4147 ,2516,3961,2517 }, {6145,5247,4023 ,6514,4096,3827 }, - {6087,6050,5394 ,6193,4094,5660 }, {5416,5345,5671 ,6461,6438,6515 }, - {507,565,564 ,1523,1614,3529 }, {3576,5912,6017 ,5890,5892,6506 }, - {807,3606,3563 ,6073,6137,6074 }, {5747,6030,5254 ,6036,4037,4042 }, - {6118,5923,3979 ,2074,2073,6471 }, {5753,6118,3979 ,6481,2074,6471 }, - {1729,5993,5580 ,3666,6475,6404 }, {6093,686,6086 ,5771,5770,6516 }, - {4260,6139,6138 ,3527,2059,6014 }, {5867,5869,4094 ,6499,6224,6225 }, - {3522,758,1627 ,6451,5663,5573 }, {5909,6136,5824 ,4260,5810,6517 }, - {5630,3715,5610 ,4874,6124,6153 }, {4147,3506,3384 ,2517,6492,6518 }, - {5888,6089,6087 ,5752,6494,6193 }, {6124,3018,3715 ,6511,6125,6124 }, - {3829,2273,3051 ,6195,5322,6488 }, {4517,5416,3018 ,6484,6461,6125 }, - {5853,3560,5318 ,6038,5779,6039 }, {3509,6128,5123 ,6519,5981,6433 }, - {4443,6212,6211 ,703,5562,5558 }, {5490,3747,5749 ,6055,2925,6052 }, - {5296,4023,5297 ,4296,3827,3826 }, {4055,4054,206 ,3678,3679,3932 }, - {6103,6123,5897 ,4220,4221,6414 }, {4486,3454,3582 ,819,710,1378 }, - {2204,6032,5867 ,6520,6489,6499 }, {5869,5867,5866 ,6224,6499,5302 }, - {6032,5869,5867 ,6489,6224,6499 }, {3658,6023,5524 ,6521,6040,6426 }, - {3584,6078,3558 ,5888,6522,5964 }, {3506,5368,3409 ,6492,5751,6490 }, - {5315,2636,5477 ,6445,2712,5399 }, {3680,405,4642 ,6497,5662,6226 }, - {6119,4410,5670 ,5736,5735,6227 }, {4374,4517,3018 ,5712,6484,6125 }, - {5299,5300,6041 ,3800,3793,3801 }, {3018,5416,5905 ,6125,6461,6142 }, - {5293,3584,3558 ,5889,5888,5964 }, {6218,3658,5935 ,5780,6521,5778 }, - {5246,5227,6040 ,5176,4097,5317 }, {3405,8086,1153 ,1798,4823,2245 }, - {3563,3606,1510 ,6074,6137,3788 }, {3905,5562,6152 ,4194,4193,6044 }, - {6163,3620,3860 ,1453,6001,6523 }, {5928,3904,3903 ,4072,1264,296 }, - {5882,3772,3804 ,4397,4250,4324 }, {3905,6152,6166 ,4194,6044,4971 }, - {5192,6251,5303 ,6454,6457,5737 }, {1417,381,4229 ,6501,6450,4877 }, - {635,5534,5971 ,2183,6524,6479 }, {6226,5981,5562 ,4192,6415,4193 }, - {1629,3510,1518 ,4207,6267,3978 }, {4367,6225,6224 ,228,4872,6458 }, - {3360,5472,5450 ,6485,6301,6525 }, {6244,5663,5356 ,5667,4259,4258 }, - {6006,5351,6043 ,6496,6513,6498 }, {3384,3506,3409 ,6518,6492,6490 }, - {5368,6077,3443 ,5751,5753,6302 }, {3409,5368,3443 ,6490,5751,6302 }, - {6077,5888,5887 ,5753,5752,6493 }, {4623,5861,5939 ,6452,6453,4276 }, - {6119,5670,5303 ,5736,6227,5737 }, {7973,7972,7948 ,3624,6059,3625 }, - {5567,2040,3039 ,6413,4833,2686 }, {6270,3780,3611 ,6082,6526,5673 }, - {5868,3720,3803 ,6527,6437,6436 }, {3525,4292,3688 ,5665,6400,5574 }, - {3337,5123,3951 ,4125,6433,6528 }, {970,5997,6018 ,268,6529,340 }, - {4154,5868,3803 ,6510,6527,6436 }, {6251,4410,5303 ,6457,5735,5737 }, - {5671,5345,3599 ,6515,6438,6428 }, {4286,3517,5580 ,4274,6503,6404 }, - {6161,6160,3720 ,6530,6442,6437 }, {5868,6161,3720 ,6527,6530,6437 }, - {6161,6187,6160 ,6530,6531,6442 }, {2896,3497,3845 ,6110,5738,3635 }, - {5903,2357,686 ,6480,3933,5770 }, {5906,3679,6006 ,6417,6447,6496 }, - {6227,3522,5906 ,6416,6451,6417 }, {4528,5316,5550 ,4935,6087,6047 }, - {3953,3717,5580 ,5713,6487,6404 }, {6139,5875,6250 ,2059,6434,6532 }, - {5390,6317,5446 ,3205,3207,5985 }, {4293,3954,4957 ,2732,3463,2733 }, - {5417,5451,5987 ,5923,6219,5621 }, {6207,6242,6241 ,6156,6081,6410 }, - {41,5037,5390 ,6533,3206,3205 }, {5116,3337,3951 ,4126,4125,6528 }, - {6306,6336,6335 ,5669,5670,6534 }, {1729,4153,5993 ,3666,5711,6475 }, - {249,6103,1158 ,644,4220,2744 }, {6103,249,5988 ,4220,644,2516 }, - {4642,6022,5670 ,6226,6535,6227 }, {5533,6172,6195 ,6239,5451,5529 }, - {6239,6223,6055 ,5918,6151,3874 }, {2960,5388,5949 ,4975,5428,5086 }, - {6043,5351,5318 ,6498,6513,6039 }, {5318,3386,3360 ,6039,6491,6485 }, - {3386,3409,5472 ,6491,6490,6301 }, {297,1416,140 ,1918,6100,1919 }, - {3902,685,3935 ,4437,2674,4346 }, {5631,5453,5863 ,6536,937,6537 }, - {6045,5453,5631 ,935,937,6536 }, {4515,6207,6206 ,30,6156,6464 }, - {5879,5417,5987 ,6220,5923,5621 }, {6050,6086,6096 ,4094,6516,4092 }, - {101,6196,6204 ,5,4,4050 }, {253,6228,751 ,2564,1366,1365 }, - {3679,5351,6006 ,6447,6513,6496 }, {3715,5851,5609 ,6124,6095,5658 }, - {3360,5450,3410 ,6485,6525,6483 }, {3410,5450,2204 ,6483,6525,6520 }, - {5450,6032,2204 ,6525,6489,6520 }, {3608,3583,6067 ,5799,5772,6266 }, - {6206,6241,6240 ,6464,6410,5756 }, {6024,6023,3410 ,6482,6040,6483 }, - {3411,1741,5392 ,4280,5695,4022 }, {1700,323,706 ,6072,4767,4784 }, - {6212,6213,6247 ,5562,6459,5575 }, {1022,793,1051 ,4198,4200,4588 }, - {6195,6172,5920 ,5529,5451,5453 }, {3658,5853,6023 ,6521,6038,6040 }, - {5928,6236,3879 ,4072,6538,2549 }, {6213,6256,6255 ,6459,5657,6102 }, - {6247,6213,6255 ,5575,6459,6102 }, {5515,6045,6038 ,5567,935,6264 }, - {6038,6045,5631 ,6264,935,6536 }, {6210,6245,6244 ,5566,5560,5667 }, - {4540,4515,6206 ,19,30,6464 }, {3797,5875,6139 ,2061,6434,2059 }, - {5893,1008,3414 ,6425,6427,4934 }, {6897,8112,8079 ,54,3530,55 }, - {5294,6010,5692 ,5891,6197,6229 }, {4278,4306,4277 ,5583,4552,4532 }, - {3563,1510,3497 ,6074,3788,5738 }, {2896,3563,3497 ,6110,6074,5738 }, - {5415,41,5390 ,5951,6533,3205 }, {4422,4391,6212 ,5561,992,5562 }, - {6074,3515,501 ,6539,1357,4313 }, {3470,5917,3079 ,6401,6402,6540 }, - {2657,1854,3506 ,2185,2184,6492 }, {7719,7697,7744 ,6541,651,6101 }, - {3879,6236,5317 ,2549,6538,6508 }, {3410,2204,5486 ,6483,6520,5718 }, - {3597,3546,3598 ,1810,2557,802 }, {5486,2204,3311 ,5718,6520,5303 }, - {262,5997,970 ,4758,6529,268 }, {359,262,970 ,4841,4758,268 }, - {5997,6019,6018 ,6529,6542,340 }, {6019,4304,6018 ,6542,4579,340 }, - {2318,8049,2319 ,3185,5393,3228 }, {959,3515,535 ,1358,1357,2001 }, - {5940,5926,3714 ,1625,6419,3970 }, {6224,6213,4391 ,6458,6459,992 }, - {1158,6103,252 ,2744,4220,2743 }, {5489,5474,5911 ,4244,4243,5924 }, - {6005,5515,5117 ,5691,5567,1113 }, {6209,6210,6244 ,6012,5566,5667 }, - {6245,6273,6272 ,5560,5683,5755 }, {6243,5356,6271 ,5666,4258,6152 }, - {6206,6207,6241 ,6464,6156,6410 }, {6204,6196,6203 ,4050,4,1249 }, - {5449,5314,3523 ,6543,6411,6544 }, {5951,2321,3509 ,6000,5999,6519 }, - {5934,5370,6091 ,616,2171,2173 }, {1510,5862,747 ,3788,3790,5692 }, - {3497,1510,747 ,5738,3788,5692 }, {949,5029,1083 ,2133,2159,2095 }, - {6138,6139,6250 ,6014,2059,6532 }, {808,556,2960 ,4734,4043,4975 }, - {4292,4517,4374 ,6400,6484,5712 }, {5416,3599,5302 ,6461,6428,3795 }, - {4144,5175,5985 ,4761,6384,5365 }, {5953,6000,3870 ,5659,6094,6096 }, - {7856,7855,7803 ,4635,6545,4636 }, {6061,465,4304 ,6546,4578,4579 }, - {5988,249,20 ,2516,644,3961 }, {6061,501,465 ,6546,4313,4578 }, - {5934,6091,5980 ,616,2173,5557 }, {1729,3517,4286 ,3666,6503,4274 }, - {5213,5214,5234 ,428,6272,6273 }, {6005,5117,186 ,5691,1113,1265 }, - {6244,6245,6272 ,5667,5560,5755 }, {6273,5880,5663 ,5683,5700,4259 }, - {6272,6273,5663 ,5755,5683,4259 }, {5956,6151,5951 ,6240,6024,6000 }, - {6203,6196,6239 ,1249,4,5918 }, {3629,3524,5160 ,5965,6187,6186 }, - {3523,3629,5160 ,6544,5965,6186 }, {5329,5711,6033 ,6191,6248,5620 }, - {6076,5329,6033 ,4217,6191,5620 }, {5576,5475,5876 ,4039,4038,2146 }, - {5530,5915,3870 ,6189,6120,6096 }, {5924,1696,6042 ,6091,6547,5703 }, - {3436,5576,5876 ,2145,4039,2146 }, {5475,5489,5876 ,4038,4244,2146 }, - {843,706,46 ,2423,4784,2232 }, {5302,3599,5706 ,3795,6428,3796 }, - {5895,5749,3731 ,6053,6052,6041 }, {6145,5925,5899 ,6514,5710,6548 }, - {5195,5213,5212 ,6235,428,430 }, {6004,5963,5871 ,6123,6409,4124 }, - {1125,2142,4528 ,5723,4936,4935 }, {706,806,807 ,4784,4783,6073 }, - {882,3396,3271 ,3846,3867,1727 }, {262,5998,5997 ,4758,6549,6529 }, - {5998,6020,6019 ,6549,6550,6542 }, {5997,5998,6019 ,6529,6549,6542 }, - {6020,6021,4304 ,6550,6551,4579 }, {6019,6020,4304 ,6542,6550,4579 }, - {6048,6061,4304 ,6552,6546,4579 }, {6021,6048,4304 ,6551,6552,4579 }, - {6074,501,6061 ,6539,4313,6546 }, {6048,6074,6061 ,6552,6539,6546 }, - {3632,3628,5703 ,5898,4213,5748 }, {6118,5753,5863 ,2074,6481,6537 }, - {5861,5870,5939 ,6453,4275,4276 }, {5453,6118,5863 ,937,2074,6537 }, - {3829,5475,3217 ,6195,4038,6117 }, {6012,6013,5963 ,6408,6060,6409 }, - {6129,6130,3951 ,5983,6300,6528 }, {5123,6129,3951 ,6433,5983,6528 }, - {5789,5386,6258 ,6140,5870,6553 }, {6217,5355,5703 ,6554,6555,5748 }, - {5881,5530,3870 ,6109,6189,6096 }, {6174,6234,6002 ,2381,5973,5975 }, - {1701,1700,2896 ,6476,6072,6110 }, {5875,5490,3444 ,6434,6055,6054 }, - {5528,5416,5302 ,6143,6461,3795 }, {5749,5922,3731 ,6052,4292,6041 }, - {5891,5876,5933 ,5714,2146,615 }, {938,803,322 ,983,144,1250 }, - {5869,3680,4094 ,6224,6497,6225 }, {5903,2912,2357 ,6480,3934,3933 }, - {1153,3012,2624 ,2245,3491,3132 }, {3599,5893,5706 ,6428,6425,3796 }, - {6222,6223,6239 ,6253,6151,5918 }, {6229,5987,5608 ,6556,5621,4356 }, - {4094,3680,4642 ,6225,6497,6226 }, {6577,3816,4426 ,97,5623,1056 }, - {262,670,5998 ,4758,4760,6549 }, {6048,3515,6074 ,6552,1357,6539 }, - {6035,6038,5654 ,6025,6264,6026 }, {5974,6003,3470 ,6474,6122,6401 }, - {5631,5863,6188 ,6536,6537,6174 }, {5654,5631,6188 ,6026,6536,6174 }, - {4292,3525,6058 ,6400,5665,6460 }, {6013,5956,5963 ,6060,6240,6409 }, - {5872,6051,6130 ,6557,6037,6300 }, {6129,5872,6130 ,5983,6557,6300 }, - {5530,5905,5915 ,6189,6142,6120 }, {1293,532,746 ,5764,5766,2661 }, - {6250,5875,3444 ,6532,6434,6054 }, {5953,6135,6000 ,5659,6258,6094 }, - {3446,3781,5487 ,3718,5849,6558 }, {5301,3631,5384 ,2698,5922,6121 }, - {686,2357,6086 ,5770,3933,6516 }, {5749,3747,6249 ,6052,2925,4032 }, - {6227,5361,758 ,6416,5664,5663 }, {7745,7744,7697 ,650,6101,651 }, - {670,5985,5998 ,4760,5365,6549 }, {6021,6020,5998 ,6551,6550,6549 }, - {6048,6085,3515 ,6552,6559,1357 }, {5628,4642,405 ,6560,6226,5662 }, - {7886,2295,2263 ,6561,3160,3080 }, {6164,6165,7062 ,6562,4219,6563 }, - {3856,5995,1268 ,5754,5564,4568 }, {3856,6013,6012 ,5754,6060,6408 }, - {5995,3856,6012 ,5564,5754,6408 }, {1121,5979,6051 ,6564,6028,6037 }, - {5872,1121,6051 ,6557,6564,6037 }, {3606,4164,1410 ,6137,6139,3911 }, - {5216,5235,5215 ,6323,6385,6313 }, {5475,3829,5877 ,4038,6195,4242 }, - {5686,5747,5651 ,5777,6036,5920 }, {5253,5302,3779 ,6163,3795,6565 }, - {2142,3779,6031 ,4936,6565,6429 }, {1608,421,746 ,4128,5815,2661 }, - {7802,7826,7801 ,6566,1616,1618 }, {3973,5388,636 ,6007,5428,6020 }, - {3989,3366,5252 ,5822,5894,5746 }, {1608,1293,421 ,4128,5764,5815 }, - {6086,2357,6096 ,6516,3933,4092 }, {3822,6044,3576 ,5715,614,5890 }, - {1153,8123,1332 ,2245,2247,3492 }, {3758,3712,3714 ,1789,1439,3970 }, - {5670,6022,3787 ,6227,6535,5819 }, {6135,5851,6000 ,6258,6095,6094 }, - {6172,5124,6146 ,5451,6567,6568 }, {4045,5654,4425 ,5916,6026,6268 }, - {3926,3970,3925 ,3125,3225,3227 }, {5175,5998,5985 ,6384,6549,5365 }, - {5175,5494,5998 ,6384,6569,6549 }, {5494,6021,5998 ,6569,6551,6549 }, - {6049,6048,6021 ,6570,6552,6551 }, {2260,6049,6021 ,6571,6570,6551 }, - {6085,6100,3515 ,6559,6572,1357 }, {3515,6100,535 ,1357,6572,2001 }, - {3548,5876,5489 ,6013,2146,4244 }, {5898,5515,6038 ,5706,5567,6264 }, - {1272,3856,1268 ,5740,5754,4568 }, {4164,5449,5540 ,6139,6543,6162 }, - {1661,6269,3611 ,5672,6083,5673 }, {3039,5526,5567 ,2686,6256,6413 }, - {6209,6244,6243 ,6012,5667,5666 }, {2621,5704,3683 ,5416,5579,5417 }, - {5323,5325,5324 ,5701,5655,5986 }, {4528,2159,1125 ,4935,6440,5723 }, - {3953,6124,3715 ,5713,6511,6124 }, {3891,3855,6847 ,3820,1511,4004 }, - {5494,5175,5020 ,6569,6384,6373 }, {1214,6085,6048 ,6573,6559,6552 }, - {1214,6101,6100 ,6573,6574,6572 }, {6085,1214,6100 ,6559,6573,6572 }, - {6101,6121,535 ,6574,6575,2001 }, {6100,6101,535 ,6572,6574,2001 }, - {6140,6153,535 ,2333,5514,2001 }, {6121,6140,535 ,6575,2333,2001 }, - {6140,3704,5040 ,2333,2332,5469 }, {6196,713,6197 ,4,3,6252 }, - {3584,6112,6078 ,5888,6406,6522 }, {6017,5912,3581 ,6506,5892,6504 }, - {5934,5489,5932 ,616,4244,5925 }, {1410,4164,5540 ,3911,6139,6162 }, - {5610,5630,5609 ,6153,4874,5658 }, {4167,5957,6063 ,6173,4054,5685 }, - {6136,4028,5824 ,5810,5823,6517 }, {3328,3288,3307 ,3973,2058,2057 }, - {6104,5858,3436 ,5696,4041,2145 }, {5976,5927,5925 ,5903,5318,5710 }, - {6228,253,2663 ,1366,2564,3593 }, {5905,5416,5528 ,6142,6461,6143 }, - {6008,5706,3779 ,3797,3796,6565 }, {5706,3414,2142 ,3796,4934,4936 }, - {5931,5928,3915 ,6576,4072,1575 }, {4308,5931,3915 ,6577,6576,1575 }, - {5931,6237,6236 ,6576,6578,6538 }, {6003,6001,5917 ,6122,6221,6402 }, - {758,3525,3688 ,5663,5665,5574 }, {1627,758,3688 ,5573,5663,5574 }, - {5929,5045,3628 ,6029,4214,4213 }, {7240,7239,7220 ,4312,1527,4367 }, - {6153,6140,5040 ,5514,2333,5469 }, {5979,1121,6125 ,6028,6564,4395 }, - {6078,6112,3508 ,6522,6406,6505 }, {5904,6189,3621 ,5851,6579,625 }, - {5849,5578,6189 ,5853,587,6579 }, {5904,5849,6189 ,5851,5853,6579 }, - {3583,5652,5651 ,5772,5774,5920 }, {5166,5120,5167 ,3427,5857,5856 }, - {5608,3472,3007 ,4356,3155,818 }, {5982,6229,5608 ,4218,6556,4356 }, - {5193,5252,4466 ,5846,5746,5854 }, {5326,5325,6041 ,3714,5655,3801 }, - {5322,5695,6145 ,4297,6465,6514 }, {4465,6210,6209 ,257,5566,6012 }, - {5928,5931,6236 ,4072,6576,6538 }, {3561,5317,6236 ,6580,6508,6538 }, - {6237,3561,6236 ,6578,6580,6538 }, {4260,4278,4277 ,3527,5583,4532 }, - {5384,5403,6016 ,6121,5921,6228 }, {5846,257,5086 ,6065,6067,6064 }, - {5303,5670,5981 ,5737,6227,6415 }, {3525,5885,5345 ,5665,6502,6438 }, - {5942,5382,5317 ,6581,6509,6508 }, {3561,5942,5317 ,6580,6581,6508 }, - {4488,4154,5382 ,6582,6510,6509 }, {3007,1696,5924 ,818,6547,6091 }, - {1811,1810,1750 ,777,779,745 }, {6621,5629,6605 ,5552,5162,5164 }, - {6058,3525,5345 ,6460,5665,6438 }, {5961,3560,5935 ,6456,5779,5778 }, - {5487,3781,5417 ,6558,5849,5923 }, {5904,3621,4200 ,5851,625,6583 }, - {7128,7129,7160 ,5391,1026,6271 }, {5302,6008,3779 ,3795,3797,6565 }, - {3472,4486,3007 ,3155,819,818 }, {5962,3469,4466 ,5745,5860,5854 }, - {3659,5314,5449 ,3875,6411,6543 }, {5314,3629,3523 ,6411,5965,6544 }, - {4164,3659,5449 ,6139,3875,6543 }, {6001,6081,6181 ,6221,6223,6251 }, - {5942,4488,5382 ,6581,6582,6509 }, {6026,5868,4154 ,6584,6527,6510 }, - {4488,6026,4154 ,6582,6584,6510 }, {3386,168,3409 ,6491,6585,6490 }, - {6148,6161,5868 ,6586,6530,6527 }, {4153,3953,5993 ,5711,5713,6475 }, - {7351,7403,7402 ,6587,6588,6589 }, {1,1022,6202 ,4622,4198,6590 }, - {2896,3845,1949 ,6110,3635,3637 }, {5963,5956,3337 ,6409,6240,4125 }, - {5933,6044,3822 ,615,614,5715 }, {5890,5686,5713 ,6591,5777,5776 }, - {5947,6230,5876 ,6027,2144,2146 }, {6014,6083,6015 ,6592,3989,3988 }, - {7129,7128,7101 ,1026,5391,4073 }, {6196,6222,6239 ,4,6253,5918 }, - {3409,3443,5472 ,6490,6302,6301 }, {3701,6149,5922 ,733,732,4292 }, - {6132,5929,3628 ,5897,6029,4213 }, {7370,7343,7344 ,3750,2750,2749 }, - {5930,7058,5979 ,6593,6594,6028 }, {3666,6068,6137 ,5699,5896,5809 }, - {5939,5649,5865 ,4276,6093,6092 }, {3836,5939,5865 ,6103,4276,6092 }, - {5608,3007,5924 ,4356,818,6091 }, {5982,5608,5924 ,4218,4356,6091 }, - {601,3286,2990 ,3905,56,3646 }, {5663,6136,5909 ,4259,5810,4260 }, - {5952,5991,3620 ,6595,5482,6001 }, {4147,20,3506 ,2517,3961,6492 }, - {6034,5952,3620 ,6049,6595,6001 }, {3012,1153,1332 ,3491,2245,3492 }, - {6026,6148,5868 ,6584,6586,6527 }, {249,348,20 ,644,643,3961 }, - {6161,6599,6187 ,6530,6596,6531 }, {5910,6354,5590 ,5121,6597,6386 }, - {6249,3701,5922 ,4032,733,4292 }, {5915,5975,6072 ,6120,6161,6155 }, - {3804,3832,3831 ,4324,2322,2321 }, {3831,3803,3804 ,2321,6436,4324 }, - {3929,5999,4166 ,5024,5184,6084 }, {3838,5999,1252 ,6257,5184,5023 }, - {6140,6101,6122 ,2333,6574,1797 }, {6295,6553,3283 ,1290,780,262 }, - {6219,6191,6220 ,6598,6599,6600 }, {6660,2138,6219 ,6601,6602,6598 }, - {5185,3632,5703 ,5939,5898,5748 }, {5355,5185,5703 ,6555,5939,5748 }, - {7909,7886,2263 ,6603,6561,3080 }, {3834,1298,6386 ,4083,5088,4085 }, - {5621,5560,1164 ,6604,6605,5087 }, {1298,5621,1164 ,5088,6604,5087 }, - {5913,5076,5560 ,6606,6607,6605 }, {5621,5913,5560 ,6604,6606,6605 }, - {5913,5919,5904 ,6606,6608,5851 }, {5076,5913,5904 ,6607,6606,5851 }, - {5919,5369,5904 ,6608,6609,5851 }, {5320,6053,6145 ,4464,4463,6514 }, - {4623,5939,3836 ,6452,4276,6103 }, {5782,4623,3836 ,5693,6452,6103 }, - {6248,5609,6213 ,6154,5658,6459 }, {3787,6159,5918 ,5819,5082,5820 }, - {1696,3007,3582 ,6547,818,1378 }, {3607,6073,4164 ,6138,3873,6139 }, - {5962,3608,3469 ,5745,5799,5860 }, {6034,3620,6175 ,6049,6001,6004 }, - {5602,3822,5964 ,5716,5715,6507 }, {5964,3576,6017 ,6507,5890,6506 }, - {5706,5893,3414 ,3796,6425,4934 }, {5247,6145,5899 ,4096,6514,6548 }, - {5253,3779,5896 ,6163,6565,6176 }, {5896,3779,4648 ,6176,6565,5722 }, - {3292,3294,3308 ,4067,3895,3894 }, {4485,6209,6208 ,32,6012,6011 }, - {6169,6191,6190 ,2334,6599,6610 }, {6190,6191,6219 ,6610,6599,6598 }, - {3522,6059,5906 ,6451,6446,6417 }, {5452,3905,5386 ,2714,4194,5870 }, - {5449,3523,3893 ,6543,6544,3789 }, {5540,5449,3893 ,6162,6543,3789 }, - {3523,5160,5861 ,6544,6186,6453 }, {3893,3523,5861 ,3789,6544,6453 }, - {7286,7344,7285 ,3460,2749,2751 }, {747,5862,5782 ,5692,3790,5693 }, - {5862,4623,5782 ,3790,6452,5693 }, {6159,3609,5918 ,5082,5081,5820 }, - {806,6073,3607 ,4783,3873,6138 }, {6203,6239,6238 ,1249,5918,5919 }, - {6034,6175,5889 ,6049,6004,1587 }, {6179,3575,3906 ,6486,5887,5697 }, - {5205,4178,5248 ,5219,3825,6469 }, {7973,8020,7972 ,3624,1405,6059 }, - {6170,3725,3537 ,3719,3971,1735 }, {7568,7567,7508 ,5544,5545,6196 }, - {187,1171,3498 ,6439,6441,5800 }, {7311,7310,7287 ,4334,4283,3459 }, - {6076,3248,5870 ,4217,4216,4275 }, {5969,6076,5870 ,6188,4217,4275 }, - {5969,5870,5861 ,6188,4275,6453 }, {5160,5969,5861 ,6186,6188,6453 }, - {5937,1298,3834 ,6611,5088,4083 }, {4044,5937,3834 ,4322,6611,4083 }, - {5937,1382,1298 ,6611,6612,5088 }, {1298,1382,5621 ,5088,6612,6604 }, - {1382,6047,5913 ,6612,6613,6606 }, {5621,1382,5913 ,6604,6612,6606 }, - {6047,5603,5913 ,6613,6614,6606 }, {5913,5603,5919 ,6606,6614,6608 }, - {5402,5369,5919 ,6615,6609,6608 }, {5603,5402,5919 ,6614,6615,6608 }, - {5687,6065,5369 ,6420,6616,6609 }, {5402,5687,5369 ,6615,6420,6609 }, - {2912,5903,1379 ,3934,6480,4137 }, {3620,5907,3860 ,6001,6116,6523 }, - {5703,6165,1464 ,5748,4219,5768 }, {5602,5964,3508 ,5716,6507,6505 }, - {3507,5602,3508 ,5671,5716,6505 }, {3773,3812,3811 ,4279,1513,4323 }, - {537,5567,5955 ,4834,6413,6512 }, {5610,3715,5609 ,6153,6124,5658 }, - {6292,6060,6282 ,5403,5914,6617 }, {7509,7568,7508 ,1874,5544,6196 }, - {381,3930,4247 ,6450,6467,6468 }, {6154,6182,6169 ,2387,6618,2334 }, - {6169,6182,6191 ,2334,6618,6599 }, {5072,4044,6261 ,6619,4322,4321 }, - {3527,5687,5402 ,6620,6420,6615 }, {158,4200,3621 ,5258,6583,625 }, - {2582,1017,3451 ,873,1793,1720 }, {6089,5888,6050 ,6494,5752,4094 }, - {6165,6164,1464 ,4219,6562,5768 }, {6073,3659,4164 ,3873,3875,6139 }, - {2160,5328,5370 ,6621,2172,2171 }, {5956,5860,6151 ,6240,5998,6024 }, - {6038,5631,5654 ,6264,6536,6026 }, {6147,6172,6146 ,5452,5451,6568 }, - {6013,3943,5956 ,6060,4755,6240 }, {422,1701,216 ,4689,6476,4644 }, - {5126,4447,5563 ,6170,717,4605 }, {3018,5905,5530 ,6125,6142,6189 }, - {5294,5692,5912 ,5891,6229,5892 }, {6056,5478,5883 ,5808,5806,5850 }, - {4278,4308,4307 ,5583,6577,4562 }, {5878,5892,5549 ,2684,2683,6435 }, - {5462,5961,5935 ,6418,6456,5778 }, {3725,6170,6182 ,3971,3719,6618 }, - {6154,3725,6182 ,2387,3971,6618 }, {6170,6192,6191 ,3719,6622,6599 }, - {6182,6170,6191 ,6618,3719,6599 }, {6192,6200,6191 ,6622,6623,6599 }, - {6191,6200,6220 ,6599,6623,6600 }, {6200,6231,6220 ,6623,6624,6600 }, - {7851,7850,7825 ,6625,3769,3768 }, {6047,1382,5603 ,6613,6612,6614 }, - {5247,5899,5227 ,4096,6548,4097 }, {5703,1464,932 ,5748,5768,5765 }, - {5246,6134,6039 ,5176,2729,2728 }, {5980,6010,5294 ,5557,6197,5891 }, - {6028,6029,3860 ,5316,1454,6523 }, {5958,3507,3508 ,5702,5671,6505 }, - {6112,5958,3508 ,6406,5702,6505 }, {3631,5487,5417 ,5922,6558,5923 }, - {1723,5476,1261 ,6626,6495,6472 }, {4307,4308,3915 ,4562,6577,1575 }, - {2321,3262,3509 ,5999,6627,6519 }, {3471,5627,3262 ,4104,6628,6627 }, - {6177,3471,3262 ,6629,4104,6627 }, {6246,3666,6274 ,5559,5699,5684 }, - {5947,5948,5717 ,6027,5719,4023 }, {5876,3548,5933 ,2146,6013,615 }, - {4541,3631,3079 ,6630,5922,6540 }, {6192,6170,6200 ,6622,3719,6623 }, - {6231,6262,5072 ,6624,6631,6619 }, {3982,4044,5072 ,6632,4322,6619 }, - {6262,3982,5072 ,6631,6632,6619 }, {4044,3982,5937 ,4322,6632,6611 }, - {3982,1007,1382 ,6632,6633,6612 }, {5937,3982,1382 ,6611,6632,6612 }, - {5484,5603,1382 ,6634,6614,6612 }, {1007,5484,1382 ,6633,6634,6612 }, - {6106,5402,5603 ,6635,6615,6614 }, {5484,6106,5603 ,6634,6635,6614 }, - {5748,3527,5402 ,6636,6620,6615 }, {6106,5748,5402 ,6635,6636,6615 }, - {4653,4600,4679 ,4347,4360,3852 }, {6523,5367,5349 ,5319,6034,5320 }, - {6057,5935,5524 ,6423,5778,6426 }, {966,5526,3039 ,2565,6256,2686 }, - {806,3607,6267 ,4783,6138,6407 }, {5588,3835,5550 ,6088,6046,6047 }, - {1293,6217,932 ,5764,6554,5765 }, {3415,5488,5246 ,6115,2730,5176 }, - {5911,5474,5932 ,5924,4243,5925 }, {1661,6111,6179 ,5672,5674,6486 }, - {635,2938,5534 ,2183,4320,6524 }, {6050,3765,5394 ,4094,4093,5660 }, - {4425,6177,3262 ,6268,6629,6627 }, {2321,4425,3262 ,5999,6268,6627 }, - {5534,1629,5971 ,6524,4207,6479 }, {2938,1629,5534 ,4320,4207,6524 }, - {2204,5867,5866 ,6520,6499,5302 }, {7291,7268,7316 ,3774,3773,5948 }, - {6213,6212,4391 ,6459,5562,992 }, {3631,4541,5487 ,5922,6630,6558 }, - {6170,6193,6200 ,3719,6637,6623 }, {6262,6231,3982 ,6631,6624,6632 }, - {3982,3667,1007 ,6632,6638,6633 }, {6011,5484,1007 ,6639,6634,6633 }, - {5484,5748,6106 ,6634,6636,6635 }, {5451,3446,5608 ,6219,3718,4356 }, - {6217,5703,932 ,6554,5748,5765 }, {3415,6186,5488 ,6115,4365,2730 }, - {5899,5925,5227 ,6548,5710,4097 }, {3560,5853,3658 ,5779,6038,6521 }, - {4153,4292,4374 ,5711,6400,5712 }, {5559,5687,3527 ,6640,6420,6620 }, - {1741,4466,6104 ,5695,5854,5696 }, {955,377,5787 ,3991,4311,4305 }, - {5302,5253,5975 ,3795,6163,6161 }, {5860,4045,2321 ,5998,5916,5999 }, - {3248,5982,5649 ,4216,4218,6093 }, {1261,3979,5591 ,6472,6471,3597 }, - {1629,1379,5971 ,4207,4137,6479 }, {3575,6179,3584 ,5887,6486,5888 }, - {5886,5887,6087 ,6192,6493,6193 }, {2645,3041,3535 ,4418,3182,442 }, - {3451,6183,6170 ,1720,6641,3719 }, {6183,6193,6170 ,6641,6637,3719 }, - {6193,6221,6200 ,6637,6642,6623 }, {6200,6221,6231 ,6623,6642,6624 }, - {3982,6127,3667 ,6632,6643,6638 }, {6127,1058,1007 ,6643,6644,6633 }, - {3667,6127,1007 ,6638,6643,6633 }, {6097,6011,1007 ,6645,6639,6633 }, - {1058,6097,1007 ,6644,6645,6633 }, {5460,5484,6011 ,6646,6634,6639 }, - {6097,5460,6011 ,6645,6646,6639 }, {1608,5992,1293 ,4128,4197,5764 }, - {5355,6217,1293 ,6555,6554,5764 }, {3558,6078,3581 ,5964,6522,6504 }, - {5957,5824,4105 ,4054,6517,4053 }, {5627,6184,6128 ,6628,6089,5981 }, - {4045,4425,2321 ,5916,6268,5999 }, {6104,5254,5858 ,5696,4042,4041 }, - {5824,4028,3411 ,6517,5823,4280 }, {5455,6509,5013 ,686,1452,684 }, - {5322,6145,4023 ,4297,6514,3827 }, {5971,1379,5903 ,6479,4137,6480 }, - {5627,6128,3509 ,6628,5981,6519 }, {3262,5627,3509 ,6627,6628,6519 }, - {6044,5294,3576 ,614,5891,5890 }, {572,571,475 ,1947,1885,1884 }, - {1605,6197,713 ,17,6252,3 }, {3041,3618,3535 ,3182,37,442 }, - {3451,6194,6193 ,1720,6647,6637 }, {6183,3451,6193 ,6641,1720,6637 }, - {6194,6201,6221 ,6647,6648,6642 }, {6193,6194,6221 ,6637,6647,6642 }, - {6232,6231,6221 ,6649,6624,6642 }, {6201,6232,6221 ,6648,6649,6642 }, - {6232,1746,3982 ,6649,6650,6632 }, {6231,6232,3982 ,6624,6649,6632 }, - {3982,1746,6127 ,6632,6650,6643 }, {6025,5748,5484 ,6651,6636,6634 }, - {5460,6025,5484 ,6646,6651,6634 }, {3716,5559,5748 ,6652,6640,6636 }, - {6025,3716,5748 ,6651,6652,6636 }, {5629,5048,5559 ,5162,6421,6640 }, - {3716,5629,5559 ,6652,5162,6640 }, {5992,5355,1293 ,4197,6555,5764 }, - {5580,3717,5630 ,6404,6487,4874 }, {5403,5417,5879 ,5921,5923,6220 }, - {6016,5403,5879 ,6228,5921,6220 }, {6150,1723,7053 ,6653,6626,6654 }, - {5909,5824,4167 ,4260,6517,6173 }, {6270,6271,3780 ,6082,6152,6526 }, - {6142,5854,3632 ,5941,6265,5898 }, {1605,6205,6223 ,17,6463,6151 }, - {6223,6205,6254 ,6151,6463,5758 }, {6055,5129,3659 ,3874,5698,3875 }, - {5705,5592,4567 ,4127,3671,6655 }, {5228,5205,5247 ,4095,5219,4096 }, - {5291,6148,6026 ,6656,6586,6584 }, {5232,5277,5255 ,6238,6237,3682 }, - {4444,6211,6210 ,582,5558,5566 }, {5011,5457,1252 ,6657,6308,5023 }, - {3443,5886,5869 ,6302,6192,6224 }, {3943,4045,5860 ,4755,5916,5998 }, - {3845,5319,6216 ,3635,5739,6658 }, {3953,4374,3018 ,5713,5712,6125 }, - {2291,350,2342 ,1115,1114,5690 }, {35,6228,2663 ,1338,1366,3593 }, - {3451,6171,6194 ,1720,6659,6647 }, {5912,5692,5329 ,5892,6229,6191 }, - {5632,5912,5329 ,6660,5892,6191 }, {5965,5632,5329 ,6190,6660,6191 }, - {3629,3581,3524 ,5965,6504,6187 }, {3471,6150,1219 ,4104,6653,4102 }, - {5854,6131,3632 ,6265,6050,5898 }, {6197,1605,6223 ,6252,17,6151 }, - {1605,4540,6206 ,17,19,6464 }, {6196,6197,6222 ,4,6252,6253 }, - {2636,5386,5789 ,2712,5870,6140 }, {5457,5020,4241 ,6308,6373,6321 }, - {5020,5457,5011 ,6373,6308,6657 }, {5973,5871,6110 ,6246,4124,6247 }, - {42,297,6783 ,5195,1918,3632 }, {5907,3415,5246 ,6116,6115,5176 }, - {5925,5927,5227 ,5710,5318,4097 }, {4572,3638,8038 ,5949,5950,4121 }, - {5987,6229,5982 ,5621,6556,4218 }, {6188,5863,5916 ,6174,6537,6175 }, - {6093,6086,6050 ,5771,6516,4094 }, {3443,5887,5886 ,6302,6493,6192 }, - {6171,3451,1017 ,6659,1720,1793 }, {6201,6194,6171 ,6648,6647,6659 }, - {6263,873,1746 ,6661,6662,6650 }, {6232,6263,1746 ,6649,6661,6650 }, - {873,6109,6127 ,6662,6663,6643 }, {1746,873,6127 ,6650,6662,6643 }, - {6109,4046,1058 ,6663,6664,6644 }, {6127,6109,1058 ,6643,6663,6644 }, - {3045,6097,1058 ,6665,6645,6644 }, {4046,3045,1058 ,6664,6665,6644 }, - {5900,5460,6097 ,6666,6646,6645 }, {3045,5900,6097 ,6665,6666,6645 }, - {5900,5448,5460 ,6666,6667,6646 }, {5381,6025,5460 ,6668,6651,6646 }, - {5448,5381,5460 ,6667,6668,6646 }, {7976,7975,7952 ,1715,6669,5475 }, - {5251,6041,5273 ,3802,3801,3819 }, {5692,6016,5879 ,6229,6228,6220 }, - {5329,5692,5879 ,6191,6229,6220 }, {6090,5874,6053 ,4897,4896,4463 }, - {6029,5976,6066 ,1454,5903,1455 }, {5996,5705,4567 ,6249,4127,6655 }, - {3582,3660,5362 ,1378,709,6670 }, {6205,6240,6254 ,6463,5756,5758 }, - {5863,5753,5916 ,6537,6481,6175 }, {5849,1124,5578 ,5853,5852,587 }, - {6212,6247,6246 ,5562,5575,5559 }, {6211,6212,6246 ,5558,5562,5559 }, - {5567,537,2040 ,6413,4834,4833 }, {4484,4465,6209 ,258,257,6012 }, - {262,359,210 ,4758,4841,4592 }, {1455,5202,808 ,1917,5550,4734 }, - {2140,4565,5447 ,5635,5944,5633 }, {6149,3648,3445 ,732,734,1791 }, - {3385,3836,5921 ,6104,6103,5705 }, {6269,6270,3611 ,6083,6082,5673 }, - {1854,5971,5368 ,2184,6479,5751 }, {5124,3970,6146 ,6567,3225,6568 }, - {3611,6063,6111 ,5673,5685,5674 }, {6157,6171,1017 ,5569,6659,1793 }, - {6157,4063,6171 ,5569,4733,6659 }, {6202,6201,6171 ,6590,6648,6659 }, - {4063,6202,6171 ,4733,6590,6659 }, {6202,6233,6232 ,6590,6671,6649 }, - {6201,6202,6232 ,6648,6590,6649 }, {6232,6233,6263 ,6649,6671,6661 }, - {5183,3716,6025 ,6672,6652,6651 }, {5381,5183,6025 ,6668,6672,6651 }, - {5691,5629,3716 ,6673,5162,6652 }, {5183,5691,3716 ,6672,6673,6652 }, - {5691,5938,5629 ,6673,5163,5162 }, {6078,3508,3581 ,6522,6505,6504 }, - {6203,6238,806 ,1249,5919,4783 }, {7203,7220,7185 ,4366,4367,5073 }, - {3620,5946,5907 ,6001,5481,6116 }, {5533,6195,3609 ,6239,5529,5081 }, - {5992,5185,5355 ,4197,5939,6555 }, {3611,3780,6063 ,5673,6526,5685 }, - {3829,3051,5877 ,6195,6488,4242 }, {6024,5743,5646 ,6482,6432,6431 }, - {5526,252,5897 ,6256,2743,6414 }, {5368,5971,5901 ,5751,6479,6674 }, - {5368,5901,5888 ,5751,6674,5752 }, {808,4987,1455 ,4734,1491,1917 }, - {3606,1410,1510 ,6137,3911,3788 }, {5749,6249,5922 ,6052,4032,4292 }, - {2204,5866,5864 ,6520,5302,5304 }, {3780,4167,6063 ,6526,6173,5685 }, - {5884,3446,5451 ,5848,3718,6219 }, {4269,6199,4229 ,4860,4859,4877 }, - {1426,4063,6157 ,4648,4733,5569 }, {1,6202,4063 ,4622,6590,4733 }, - {8049,8035,8018 ,5393,6675,6676 }, {843,6203,806 ,2423,1249,4783 }, - {3581,5632,5965 ,6504,6660,6190 }, {3524,3581,5965 ,6187,6504,6190 }, - {5824,5957,4167 ,6517,4054,6173 }, {6179,6111,5575 ,6486,5674,5686 }, - {4485,4484,6209 ,32,258,6012 }, {5489,5475,5877 ,4244,4038,4242 }, - {7511,7510,7453 ,6677,1873,1187 }, {1700,422,81 ,6072,4689,4673 }, - {3900,552,8132 ,884,3929,413 }, {5129,3906,3575 ,5698,5697,5887 }, - {6037,3856,1272 ,5235,5754,5740 }, {75,107,930 ,3962,2548,3594 }, - {3688,4153,1729 ,5574,5711,3666 }, {253,1664,249 ,2564,2268,644 }, - {808,5941,4987 ,4734,4976,1491 }, {5941,5967,4987 ,4976,1352,1491 }, - {5876,5891,5948 ,2146,5714,5719 }, {5947,5876,5948 ,6027,2146,5719 }, - {3584,5575,6112 ,5888,5686,6406 }, {6057,5524,5994 ,6423,6426,6424 }, - {3801,5362,3660 ,4974,6670,709 }, {4147,3384,3409 ,2517,6518,6490 }, - {5986,306,1251 ,4603,6234,4604 }, {6202,6252,6233 ,6590,6678,6671 }, - {6233,6252,6263 ,6671,6678,6661 }, {6252,1484,873 ,6678,6679,6662 }, - {6263,6252,873 ,6661,6678,6662 }, {1484,5525,6109 ,6679,6680,6663 }, - {873,1484,6109 ,6662,6679,6663 }, {5525,5589,4046 ,6680,6681,6664 }, - {6109,5525,4046 ,6663,6680,6664 }, {1242,3045,4046 ,6682,6665,6664 }, - {5589,1242,4046 ,6681,6682,6664 }, {5539,5900,3045 ,6683,6666,6665 }, - {1242,5539,3045 ,6682,6683,6665 }, {5539,5448,5900 ,6683,6667,6666 }, - {5271,5381,5448 ,6684,6668,6667 }, {5539,5271,5448 ,6683,6684,6667 }, - {5271,5183,5381 ,6684,6672,6668 }, {5938,5502,5081 ,5163,1256,1258 }, - {806,6238,6073 ,4783,5919,3873 }, {6066,5976,5874 ,1455,5903,4896 }, - {5632,3581,5912 ,6660,6504,5892 }, {1696,3582,5362 ,6547,1378,6670 }, - {3524,5965,5969 ,6187,6190,6188 }, {6273,6274,5880 ,5683,5684,5700 }, - {3499,5394,5628 ,5661,5660,6560 }, {3547,3598,3546 ,803,802,2557 }, - {3879,3904,5928 ,2549,1264,4072 }, {6063,202,5575 ,5685,4105,5686 }, - {5981,6226,3905 ,6415,4192,4194 }, {3087,3131,3130 ,3573,3732,3414 }, - {5943,6283,7088 ,5402,4298,4299 }, {3629,3558,3581 ,5965,5964,6504 }, - {5316,3835,5588 ,6087,6046,6088 }, {3248,5649,5939 ,4216,6093,4276 }, - {7333,7332,3304 ,4353,6685,1050 }, {7208,7226,7225 ,1671,6686,1672 }, - {5949,5967,2960 ,5086,1352,4975 }, {2960,5967,5941 ,4975,1352,4976 }, - {6179,5575,3584 ,6486,5686,5888 }, {5345,5885,6057 ,6438,6502,6423 }, - {253,751,6228 ,2564,1365,1366 }, {168,4147,3409 ,6585,2517,6490 }, - {1022,1,969 ,4198,4622,4199 }, {3515,3719,501 ,1357,1356,4313 }, - {58,1332,8143 ,2156,3492,2570 }, {6202,1022,6252 ,6590,4198,6678 }, - {5176,5183,5271 ,6687,6672,6684 }, {6064,5691,5183 ,6688,6673,6672 }, - {5176,6064,5183 ,6687,6688,6672 }, {5502,5938,5691 ,1256,5163,6673 }, - {6064,5502,5691 ,6688,1256,6673 }, {6218,3560,3658 ,5780,5779,6521 }, - {5351,168,3386 ,6513,6585,6491 }, {4268,6199,4269 ,4848,4859,4860 }, - {6084,6087,3499 ,6194,6193,5661 }, {5361,5462,5935 ,5664,6418,5778 }, - {6087,5394,3499 ,6193,5660,5661 }, {3822,3576,5964 ,5715,5890,6507 }, - {3456,8083,5771 ,1145,2103,2868 }, {2263,2262,7909 ,3080,3359,6603 }, - {5824,3411,4105 ,6517,4280,4053 }, {5351,5513,168 ,6513,6500,6585 }, - {2206,5592,2224 ,176,3671,2660 }, {807,806,6267 ,6073,4783,6407 }, - {5513,4147,168 ,6500,2517,6585 }, {7923,7948,7896 ,816,3625,4355 }, - {2204,5864,3311 ,6520,5304,5303 }, {5129,3575,5293 ,5698,5887,5889 }, - {2663,783,35 ,3593,679,1338 }, {6252,6264,1484 ,6678,6689,6679 }, - {5754,5271,5539 ,6690,6684,6683 }, {5502,6064,5176 ,1256,6688,6687 }, - {5854,3951,6130 ,6265,6528,6300 }, {6051,6132,6130 ,6037,5897,6300 }, - {5211,5191,6433 ,6071,2900,6691 }, {5695,5320,6145 ,6465,4464,6514 }, - {2573,2660,3569 ,3253,455,4381 }, {4485,6208,6207 ,32,6011,6156 }, - {4517,4292,6058 ,6484,6400,6460 }, {6266,1700,3563 ,399,6072,6074 }, - {3870,5915,3686 ,6096,6120,6119 }, {5932,5370,5934 ,5925,2171,616 }, - {422,1700,1701 ,4689,6072,6476 }, {4321,6225,4343 ,4873,4872,1818 }, - {4356,166,4404 ,155,3729,5072 }, {5978,6114,5195 ,5581,1805,6235 }, - {5212,5978,5195 ,430,5581,6235 }, {4246,4247,1729 ,3665,6468,3666 }, - {5314,5293,3629 ,6411,5889,5965 }, {5528,5302,5712 ,6143,3795,6160 }, - {3797,3747,5490 ,2061,2925,6055 }, {4515,4485,6207 ,30,32,6156 }, - {5915,5820,5975 ,6120,6144,6161 }, {3679,5990,5351 ,6447,6692,6513 }, - {807,6267,3606 ,6073,6407,6137 }, {1022,6253,6252 ,4198,6693,6678 }, - {6253,6265,6264 ,6693,6694,6689 }, {6252,6253,6264 ,6678,6693,6689 }, - {6265,4189,1484 ,6694,6695,6679 }, {6264,6265,1484 ,6689,6694,6679 }, - {4189,5184,5525 ,6695,6696,6680 }, {1484,4189,5525 ,6679,6695,6680 }, - {5184,3874,5589 ,6696,6697,6681 }, {5525,5184,5589 ,6680,6696,6681 }, - {3908,1242,5589 ,6698,6682,6681 }, {3874,3908,5589 ,6697,6698,6681 }, - {3776,5539,1242 ,6699,6683,6682 }, {3908,3776,1242 ,6698,6699,6682 }, - {5780,5754,5539 ,6700,6690,6683 }, {3776,5780,5539 ,6699,6700,6683 }, - {4344,5271,5754 ,6701,6684,6690 }, {5780,4344,5754 ,6700,6701,6690 }, - {5968,5176,5271 ,6702,6687,6684 }, {4344,5968,5271 ,6701,6702,6684 }, - {5088,5502,5176 ,5004,1256,6687 }, {5968,5088,5176 ,6702,5004,6687 }, - {6240,1661,6268 ,5756,5672,5757 }, {1723,6150,3471 ,6626,6653,4104 }, - {5476,1723,3471 ,6495,6626,4104 }, {4367,6224,4391 ,228,6458,992 }, - {5233,5978,5212 ,429,5581,430 }, {1204,4499,4500 ,338,4510,4523 }, - {6014,6015,5838 ,6592,3988,3987 }, {5910,6014,5838 ,5121,6592,3987 }, - {3408,3435,3434 ,3484,6703,737 }, {6076,6033,5982 ,4217,5620,4218 }, - {1008,5316,3414 ,6427,6087,4934 }, {1252,3839,3838 ,5023,6310,6257 }, - {6059,3559,3679 ,6446,6412,6447 }, {5897,5990,3679 ,6414,6692,6447 }, - {5316,4528,3414 ,6087,4935,4934 }, {1629,931,1379 ,4207,3944,4137 }, - {6123,5513,5351 ,4221,6500,6513 }, {5981,6007,5562 ,6415,6704,4193 }, - {6137,6068,3989 ,5809,5896,5822 }, {1022,1051,6253 ,4198,4588,6693 }, - {3395,5116,5854 ,5940,4126,6265 }, {3780,5356,4167 ,6526,4258,6173 }, - {5192,5044,3762 ,6454,5173,5172 }, {5916,5476,6177 ,6175,6495,6629 }, - {6052,3762,3835 ,5717,5172,6046 }, {5477,2636,5789 ,5399,2712,6140 }, - {8080,8141,8132 ,1094,3295,413 }, {4299,1511,2431 ,5984,4921,2977 }, - {5486,6052,3835 ,5718,5717,6046 }, {4028,5193,3411 ,5823,5846,4280 }, - {7532,7565,7507 ,6705,5080,6706 }, {5901,5971,5888 ,6674,6479,5752 }, - {5971,5902,5888 ,6479,5769,5752 }, {3506,1854,5368 ,6492,2184,5751 }, - {5990,6123,5351 ,6692,4221,6513 }, {3559,5897,3679 ,6412,6414,6447 }, - {6145,6053,5925 ,6514,4463,5710 }, {5869,6084,3680 ,6224,6194,6497 }, - {3366,5461,5252 ,5894,5744,5746 }, {3908,3874,3776 ,6698,6697,6699 }, - {5116,3951,5854 ,4126,6528,6265 }, {2208,5502,5088 ,1257,1256,5004 }, - {5450,5472,6032 ,6525,6301,6489 }, {5246,5488,6134 ,5176,2730,2729 }, - {2159,5549,1171 ,6440,6435,6441 }, {3836,5865,5921 ,6103,6092,5705 }, - {5892,3498,1171 ,2683,5800,6441 }, {6042,1696,5230 ,5703,6547,5704 }, - {4247,1627,1729 ,6468,5573,3666 }, {4425,5916,6177 ,6268,6175,6629 }, - {5549,5892,1171 ,6435,2683,6441 }, {4278,6138,5931 ,5583,6014,6576 }, - {4308,4278,5931 ,6577,5583,6576 }, {6138,6250,6237 ,6014,6532,6578 }, - {5316,5743,3835 ,6087,6432,6046 }, {5233,5232,5978 ,429,6238,5581 }, - {5983,6082,5232 ,6707,1348,6238 }, {5590,6014,5910 ,6386,6592,5121 }, - {5931,6138,6237 ,6576,6014,6578 }, {6250,3444,3561 ,6532,6054,6580 }, - {3559,6059,3522 ,6412,6446,6451 }, {1696,5362,5230 ,6547,6670,5704 }, - {1701,2896,1949 ,6476,6110,3637 }, {5865,6042,5921 ,6092,5703,5705 }, - {6036,5789,5883 ,6141,6140,5850 }, {5478,6036,5883 ,5806,6141,5850 }, - {6237,6250,3561 ,6578,6532,6580 }, {1620,5872,6129 ,5982,6557,5983 }, - {5245,4949,4724 ,5880,5526,4737 }, {3469,3608,6067 ,5860,5799,6266 }, - {5891,5933,3822 ,5714,615,5715 }, {1051,1858,217 ,4588,4590,4643 }, - {1051,1532,6253 ,4588,4771,6693 }, {6253,1532,6265 ,6693,4771,6694 }, - {1532,5275,4189 ,4771,6708,6695 }, {6265,1532,4189 ,6694,4771,6695 }, - {4349,5184,4189 ,6709,6696,6695 }, {5275,4349,4189 ,6708,6709,6695 }, - {3927,3874,5184 ,6710,6697,6696 }, {4349,3927,5184 ,6709,6710,6696 }, - {3927,6117,3874 ,6710,6711,6697 }, {5959,3776,3874 ,6712,6699,6697 }, - {6117,5959,3874 ,6711,6712,6697 }, {5565,5780,3776 ,6713,6700,6699 }, - {5959,5565,3776 ,6712,6713,6699 }, {5565,3610,4344 ,6713,6714,6701 }, - {5780,5565,4344 ,6700,6713,6701 }, {5873,5968,4344 ,6715,6702,6701 }, - {3610,5873,4344 ,6714,6715,6701 }, {5393,5088,5968 ,5005,5004,6702 }, - {5873,5393,5968 ,6715,5005,6702 }, {6110,5871,3395 ,6247,4124,5940 }, - {6105,6062,1268 ,5565,6478,4568 }, {7370,7422,7396 ,3750,1404,6716 }, - {2343,6563,2210 ,2685,464,2510 }, {5995,6012,5974 ,5564,6408,6474 }, - {5789,6258,5954 ,6140,6553,5868 }, {5883,5789,5954 ,5850,6140,5868 }, - {6258,5386,5954 ,6553,5870,5868 }, {5476,3471,6177 ,6495,4104,6629 }, - {5954,5386,3657 ,5868,5870,5869 }, {5983,5232,5255 ,6707,6238,3682 }, - {6241,6269,1661 ,6410,6083,5672 }, {5895,5942,3561 ,6053,6581,6580 }, - {5743,5486,3835 ,6432,5718,6046 }, {5386,6185,6178 ,5870,6051,5881 }, - {3657,5386,6178 ,5869,5870,5881 }, {5232,6082,5978 ,6238,1348,5581 }, - {6271,5356,3780 ,6152,4258,6526 }, {3444,5895,3561 ,6054,6053,6580 }, - {3731,4488,5942 ,6041,6582,6581 }, {3559,3522,1417 ,6412,6451,6501 }, - {5955,3559,1417 ,6512,6412,6501 }, {3717,3953,3715 ,6487,5713,6124 }, - {5630,5610,6225 ,4874,6153,4872 }, {6185,3892,6178 ,6051,4970,5881 }, - {5956,5951,3337 ,6240,6000,4125 }, {5712,5302,5975 ,6160,3795,6161 }, - {5417,3781,5451 ,5923,5849,6219 }, {5895,3731,5942 ,6053,6041,6581 }, - {6174,6002,6173 ,2381,5975,3711 }, {5935,3658,5524 ,5778,6521,6426 }, - {5426,5447,5970 ,5634,5633,3518 }, {537,5955,1417 ,4834,6512,6501 }, - {5743,6024,5486 ,6432,6482,5718 }, {6007,3787,5562 ,6704,5819,4193 }, - {252,6103,5897 ,2743,4220,6414 }, {5885,5935,6057 ,6502,5778,6423 }, - {4574,5301,1268 ,6477,2698,4568 }, {5974,3470,3079 ,6474,6401,6540 }, - {5995,5974,6062 ,5564,6474,6478 }, {5781,5782,3385 ,5694,5693,6104 }, - {6568,5395,5427 ,6717,5348,5811 }, {6075,6026,4488 ,6042,6584,6582 }, - {3731,6075,4488 ,6041,6042,6582 }, {5628,3609,6144 ,6560,5081,5083 }, - {6091,5384,6010 ,2173,6121,6197 }, {6069,6082,5983 ,6180,1348,6707 }, - {5255,6069,5983 ,3682,6180,6707 }, {58,8143,8128 ,2156,2570,959 }, - {4170,5447,5579 ,3731,5633,5942 }, {1002,1283,5788 ,2976,2978,6086 }, - {3844,4170,5579 ,2443,3731,5942 }, {5788,3844,5579 ,6086,2443,5942 }, - {4299,2431,1002 ,5984,2977,2976 }, {6075,5291,6026 ,6042,6656,6584 }, - {5908,6148,5291 ,4293,6586,6656 }, {6023,3360,3410 ,6040,6485,6483 }, - {6095,5973,6081 ,6222,6246,6223 }, {6081,6110,3395 ,6223,6247,5940 }, - {5981,3787,6007 ,6415,5819,6704 }, {5562,5918,5945 ,4193,5820,6396 }, - {5996,4567,6156 ,6249,6655,6250 }, {7743,7744,7769 ,5534,6101,5535 }, - {5329,5879,5711 ,6191,6220,6248 }, {5393,5362,3801 ,5005,6670,4974 }, - {5871,5963,3337 ,4124,6409,4125 }, {6105,5995,6062 ,5565,5564,6478 }, - {3217,5475,6030 ,6117,4038,4037 }, {4410,4094,5670 ,5735,6225,6227 }, - {6075,5908,5291 ,6042,4293,6656 }, {5559,3527,5748 ,6640,6620,6636 }, - {1121,5936,6125 ,6564,4393,4395 }, {6167,6259,5590 ,5974,6718,6386 }, - {6002,6167,5590 ,5975,5974,6386 }, {6003,5871,5973 ,6122,4124,6246 }, - {5871,5116,3395 ,4124,4126,5940 }, {5671,3599,5416 ,6515,6428,6461 }, - {6199,537,4229 ,4859,4834,4877 }, {537,1417,4229 ,4834,6501,4877 }, - {5670,3787,5981 ,6227,5819,6415 }, {6012,6004,5974 ,6408,6123,6474 }, - {6068,3366,3989 ,5896,5894,5822 }, {1620,1122,5872 ,5982,6719,6557 }, - {6215,5275,1532 ,3636,6708,4771 }, {1949,6215,1532 ,3637,3636,4771 }, - {3845,4349,5275 ,3635,6709,6708 }, {6215,3845,5275 ,3636,3635,6708 }, - {3845,3927,4349 ,3635,6710,6709 }, {3845,6216,3927 ,3635,6658,6710 }, - {6216,5319,6117 ,6658,5739,6711 }, {3927,6216,6117 ,6710,6658,6711 }, - {6117,5319,5959 ,6711,5739,6712 }, {5319,5781,5959 ,5739,5694,6712 }, - {5781,3385,5565 ,5694,6104,6713 }, {5959,5781,5565 ,6712,5694,6713 }, - {5565,3385,3610 ,6713,6104,6714 }, {3385,5921,3610 ,6104,5705,6714 }, - {5230,5873,3610 ,5704,6715,6714 }, {5921,5230,3610 ,5705,5704,6714 }, - {5362,5393,5873 ,6670,5005,6715 }, {5230,5362,5873 ,5704,6670,6715 }, - {8131,8105,8075 ,2307,3791,2308 }, {5897,6123,5990 ,6414,4221,6692 }, - {5951,3509,5123 ,6000,6519,6433 }, {3337,5951,5123 ,4125,6000,6433 }, - {6208,6243,6242 ,6011,5666,6081 }, {3469,6067,5651 ,5860,6266,5920 }, - {3498,6036,5478 ,5800,6141,5806 }, {5193,4466,1741 ,5846,5854,5695 }, - {4444,4443,6211 ,582,703,5558 }, {5854,6130,6131 ,6265,6300,6050 }, - {3902,3935,4807 ,4437,4346,6449 }, {7082,3415,5946 ,6462,6115,5481 }, - {7873,7897,7846 ,817,6015,4162 }, {8086,8099,8087 ,4823,2867,385 }, - {5520,1185,1516 ,1304,6159,1254 }, {257,3284,1380 ,6067,6720,6721 }, - {5706,6031,3779 ,3796,6429,6565 }, {1313,2373,1947 ,3674,6722,6723 }, - {7089,7115,7103 ,5608,1024,6455 }, {2220,1072,6709 ,6724,6725,6726 }, - {5085,1313,1947 ,3675,3674,6723 }, {2872,4204,784 ,311,38,4211 }, - {4542,6378,6481 ,6727,6728,6729 }, {5069,257,255 ,6730,6067,6731 }, - {352,351,255 ,593,592,6731 }, {5578,3621,6189 ,587,625,6579 }, - {6965,6964,6944 ,6732,6733,2633 }, {7005,7046,7004 ,6734,6735,5502 }, - {6914,6945,6913 ,3532,2632,2634 }, {6945,6965,6944 ,2632,6732,2633 }, - {2406,6530,1217 ,6736,6398,6399 }, {1215,6644,6950 ,3937,5098,1349 }, - {7719,7744,7743 ,6541,6101,5534 }, {4986,3399,5441 ,5096,715,5194 }, - {4831,4486,3472 ,3154,819,3155 }, {144,6430,5353 ,853,2748,851 }, - {7005,7004,6964 ,6734,5502,6733 }, {6965,7005,6964 ,6732,6734,6733 }, - {7046,7045,7004 ,6735,5503,5502 }, {2335,2284,7045 ,6737,6738,5503 }, - {7046,2335,7045 ,6735,6737,5503 }, {3609,5920,6990 ,5081,5453,6739 }, - {6858,6859,6910 ,6740,472,6741 }, {6158,7087,7086 ,6112,6742,6743 }, - {5978,6079,6114 ,5581,5580,1805 }, {3598,6598,3597 ,802,804,1810 }, - {6598,5908,3597 ,804,4293,1810 }, {4988,5020,5011 ,6744,6373,6657 }, - {6569,4988,5011 ,6745,6744,6657 }, {1118,4853,2284 ,6746,6747,6738 }, - {2335,1118,2284 ,6737,6746,6738 }, {6284,7086,5952 ,6113,6743,6595 }, - {3445,3597,5908 ,1791,1810,4293 }, {7062,1464,6164 ,6563,5768,6562 }, - {1380,6313,6312 ,6721,6748,6749 }, {879,4122,3397 ,1155,137,136 }, - {6859,6911,6910 ,472,474,6741 }, {255,257,1380 ,6731,6067,6721 }, - {6532,3496,6556 ,6184,3105,4273 }, {6557,6599,6598 ,6750,6596,804 }, - {6556,6557,6598 ,4273,6750,804 }, {6148,5908,6598 ,6586,4293,804 }, - {6599,6148,6598 ,6596,6586,804 }, {7453,7452,7424 ,1187,1875,1188 }, - {4568,5074,185 ,1492,6751,1493 }, {7736,2451,450 ,6752,2102,1653 }, - {3496,6557,6556 ,3105,6750,4273 }, {3733,4021,4426 ,5610,1712,1056 }, - {6690,7022,3649 ,6753,6754,6755 }, {5363,5383,6506 ,3269,3547,3549 }, - {2304,3302,2303 ,6756,6757,6758 }, {6571,5367,6523 ,4146,6034,5319 }, - {6468,6510,3496 ,6759,6760,3105 }, {3464,6468,3496 ,3151,6759,3105 }, - {6510,6558,6557 ,6760,6761,6750 }, {3496,6510,6557 ,3105,6760,6750 }, - {6558,6559,6557 ,6761,6762,6750 }, {6559,6600,6599 ,6762,6763,6596 }, - {6557,6559,6599 ,6750,6762,6596 }, {6600,6187,6599 ,6763,6531,6596 }, - {5823,7038,7073 ,661,3832,662 }, {6808,6807,6753 ,3465,6764,6765 }, - {3421,2851,3278 ,1156,3051,1158 }, {6703,6754,6701 ,5161,713,6766 }, - {6754,6789,6753 ,713,6767,6765 }, {7451,7509,7479 ,3963,1874,4423 }, - {3358,3357,3383 ,880,883,882 }, {5694,5715,5693 ,5326,4076,4075 }, - {3408,6468,3435 ,3484,6759,6703 }, {6533,6559,6558 ,6768,6762,6761 }, - {6510,6533,6558 ,6760,6768,6761 }, {6190,6219,2138 ,6610,6598,6602 }, - {6808,6859,6858 ,3465,472,6740 }, {6789,6808,6753 ,6767,3465,6765 }, - {5086,257,5069 ,6064,6067,6730 }, {7267,7291,7266 ,4331,3774,6769 }, - {5027,2272,4755 ,5516,1201,6770 }, {3936,3733,4024 ,5599,5610,5600 }, - {5693,7028,6988 ,4075,3587,6771 }, {5694,6989,6949 ,5326,4586,4585 }, - {5367,6571,5401 ,6034,4146,4145 }, {6393,6439,3408 ,1556,6772,3484 }, - {3357,6393,3408 ,883,1556,3484 }, {6439,6469,6468 ,6772,6773,6759 }, - {3408,6439,6468 ,3484,6772,6759 }, {6469,6511,6510 ,6773,6774,6760 }, - {6468,6469,6510 ,6759,6773,6760 }, {6510,6511,6533 ,6760,6774,6768 }, - {6511,6560,6559 ,6774,6775,6762 }, {6533,6511,6559 ,6768,6774,6762 }, - {6601,6600,6559 ,6776,6763,6762 }, {6560,6601,6559 ,6775,6776,6762 }, - {6601,6099,6187 ,6776,6777,6531 }, {6600,6601,6187 ,6763,6776,6531 }, - {297,6464,6783 ,1918,3633,3632 }, {6919,6918,6895 ,6778,6779,5434 }, - {2211,4988,1270 ,6780,6744,6781 }, {4988,6569,1270 ,6744,6745,6781 }, - {6948,6947,6920 ,6782,4587,6783 }, {6525,6526,6573 ,3703,6784,3733 }, - {6614,6651,6650 ,3886,6785,3833 }, {5300,6526,6525 ,3793,6784,3703 }, - {6866,6896,6842 ,6786,6787,5433 }, {6318,6319,5151 ,6788,6789,1803 }, - {7064,7048,7007 ,5181,6790,4628 }, {5415,5579,4565 ,5951,5942,5944 }, - {6393,6419,6439 ,1556,6791,6772 }, {6469,6485,6511 ,6773,6792,6774 }, - {3449,3478,3482 ,2440,885,2494 }, {6910,6911,6941 ,6741,474,6793 }, - {2271,3281,6309 ,5071,6794,3690 }, {5742,3815,3373 ,6448,5556,693 }, - {2244,6660,4570 ,6795,6601,4082 }, {5087,1117,6679 ,6796,4928,4824 }, - {2340,2303,2308 ,6797,6758,6798 }, {6389,6868,5041 ,5301,5554,6799 }, - {5559,5048,5687 ,6640,6421,6420 }, {2093,6342,2957 ,553,5265,6326 }, - {2303,3302,2308 ,6758,6757,6798 }, {6158,7086,6284 ,6112,6743,6113 }, - {7083,5991,5952 ,5483,5482,6595 }, {6318,5151,6356 ,6788,1803,3787 }, - {7453,7510,7452 ,1187,1873,1875 }, {2095,5081,1117 ,6800,1258,4928 }, - {7924,7974,7923 ,815,4171,816 }, {2237,2236,2373 ,6801,6802,6722 }, - {7087,5514,7088 ,6742,6128,4299 }, {6090,6053,6260 ,4897,4463,3397 }, - {5009,6391,6463 ,6803,6804,5159 }, {4575,7585,7640 ,3128,6805,5351 }, - {5514,7087,6158 ,6128,6742,6112 }, {5447,5564,5579 ,5633,5943,5942 }, - {5751,5752,7038 ,5878,4374,3832 }, {3359,6420,6419 ,824,6806,6791 }, - {6393,3359,6419 ,1556,824,6791 }, {6419,6420,6439 ,6791,6806,6772 }, - {6420,6470,6469 ,6806,6807,6773 }, {6439,6420,6469 ,6772,6806,6773 }, - {6470,6471,6485 ,6807,6808,6792 }, {6469,6470,6485 ,6773,6807,6792 }, - {6471,6512,6511 ,6808,6809,6774 }, {6485,6471,6511 ,6792,6808,6774 }, - {6561,6560,6511 ,6810,6775,6774 }, {6512,6561,6511 ,6809,6810,6774 }, - {6561,6180,6601 ,6810,6811,6776 }, {6560,6561,6601 ,6775,6810,6776 }, - {6601,6180,6099 ,6776,6811,6777 }, {3674,6160,6099 ,4286,6442,6777 }, - {6180,3674,6099 ,6811,4286,6777 }, {7033,7034,6152 ,6395,6045,6044 }, - {6682,5087,6679 ,6812,6796,4824 }, {184,4016,4017 ,4052,5969,5342 }, - {6942,6962,6961 ,521,3260,6813 }, {7000,7001,7024 ,6814,6815,6816 }, - {6160,6187,6099 ,6442,6531,6777 }, {7649,7648,7621 ,3950,3952,6010 }, - {5454,6682,3978 ,6817,6812,4549 }, {6711,2450,5014 ,3473,3444,6818 }, - {6554,3261,6521 ,3647,5015,6819 }, {350,5117,6115 ,1114,1113,3645 }, - {6713,6644,1215 ,6820,5098,3937 }, {6713,5056,6644 ,6820,1300,5098 }, - {3261,4190,2384 ,5015,3439,6821 }, {6844,6896,6866 ,6822,6787,6786 }, - {7105,7130,7104 ,5279,6823,5280 }, {7220,7219,7184 ,4367,1529,5074 }, - {6962,7001,7000 ,3260,6815,6814 }, {7025,7043,7042 ,6824,5895,5338 }, - {6319,5118,5125 ,6789,6825,5467 }, {3359,6394,6420 ,824,6826,6806 }, - {6420,6471,6470 ,6806,6808,6807 }, {6602,6180,6561 ,6827,6811,6810 }, - {6918,6946,6919 ,6779,594,6778 }, {1025,6657,6714 ,5299,6828,5300 }, - {2337,2405,5578 ,6829,4165,587 }, {2245,6359,6344 ,1260,6830,1450 }, - {5512,5511,5471 ,1589,1588,2051 }, {6379,6380,6394 ,6831,6832,6826 }, - {3359,6379,6394 ,824,6831,6826 }, {6380,6421,6420 ,6832,6833,6806 }, - {6394,6380,6420 ,6826,6832,6806 }, {6421,6472,6471 ,6833,6834,6808 }, - {6420,6421,6471 ,6806,6833,6808 }, {6472,6513,6512 ,6834,6835,6809 }, - {6471,6472,6512 ,6808,6834,6809 }, {6513,3600,6561 ,6835,5331,6810 }, - {6512,6513,6561 ,6809,6835,6810 }, {3623,6602,6561 ,4206,6827,6810 }, - {3600,3623,6561 ,5331,4206,6810 }, {6602,3623,6180 ,6827,4206,6811 }, - {6359,5074,4568 ,6830,6751,1492 }, {6344,6359,4568 ,1450,6830,1492 }, - {6739,6738,5437 ,6836,5057,6259 }, {6754,6753,6701 ,713,6765,6766 }, - {1990,5059,1217 ,1305,1253,6399 }, {7042,2251,7077 ,5338,3676,6837 }, - {7041,7042,7077 ,6838,5338,6837 }, {2251,5085,7077 ,3676,3675,6837 }, - {5085,4142,7077 ,3675,6839,6837 }, {6966,7006,6965 ,6840,6841,6732 }, - {2406,1217,6814 ,6736,6399,6842 }, {6421,6440,6472 ,6833,6843,6834 }, - {5527,3221,835 ,5977,183,3030 }, {2320,8049,8018 ,1406,5393,6676 }, - {6573,6574,6613 ,3733,3885,3746 }, {6342,2093,6555 ,5265,553,597 }, - {6854,6893,6906 ,3344,6844,4294 }, {6088,6069,5255 ,3670,6180,3682 }, - {6389,5041,5102 ,5301,6799,6845 }, {6688,6389,5102 ,6846,5301,6845 }, - {5054,5053,6688 ,6847,6848,6846 }, {5053,6389,6688 ,6848,5301,6846 }, - {6708,5053,5054 ,6148,6848,6847 }, {6696,6708,5054 ,6849,6148,6847 }, - {6442,6300,6708 ,6850,6146,6148 }, {2237,2308,6297 ,6801,6798,6851 }, - {6297,6300,6442 ,6851,6146,6850 }, {2308,6300,6297 ,6798,6146,6851 }, - {2409,2340,2237 ,6852,6797,6801 }, {3326,6380,6379 ,826,6832,6831 }, - {7047,7046,7005 ,6853,6735,6734 }, {7006,7047,7005 ,6841,6853,6734 }, - {2455,5001,4018 ,475,477,5084 }, {6613,6614,6650 ,3746,3886,3833 }, - {2340,2409,1075 ,6797,6852,6854 }, {2340,2308,2237 ,6797,6798,6801 }, - {4853,2340,1075 ,6747,6797,6854 }, {6959,6998,6997 ,6003,6855,4368 }, - {8019,8018,7972 ,1407,6676,6059 }, {5436,6681,5405 ,6201,5406,5518 }, - {5331,5330,5277 ,6198,3345,6237 }, {4960,306,5986 ,6856,6234,4603 }, - {4191,4296,3802 ,4474,6023,4475 }, {6737,6736,6704 ,6857,1622,5160 }, - {6681,6737,6704 ,5406,6857,5160 }, {2284,2248,7044 ,6738,6858,6859 }, - {7045,7044,7002 ,5503,6859,1047 }, {7006,7005,6965 ,6841,6734,6732 }, - {6941,6942,6961 ,6793,521,6813 }, {6966,6965,6914 ,6840,6732,3532 }, - {6141,6140,6122 ,2386,2333,1797 }, {6347,6381,6380 ,451,6860,6832 }, - {3326,6347,6380 ,826,451,6832 }, {6381,6422,6421 ,6860,6861,6833 }, - {6380,6381,6421 ,6832,6860,6833 }, {6422,6441,6440 ,6861,6862,6843 }, - {6421,6422,6440 ,6833,6861,6843 }, {6441,6473,6472 ,6862,6863,6834 }, - {6440,6441,6472 ,6843,6862,6834 }, {3550,6513,6472 ,4179,6835,6834 }, - {6473,3550,6472 ,6863,4179,6834 }, {6513,3550,3600 ,6835,4179,5331 }, - {6742,6746,4784 ,3422,6864,3423 }, {6864,6863,6839 ,6865,6866,6867 }, - {6840,6864,6839 ,6868,6865,6867 }, {7045,2284,7044 ,5503,6738,6859 }, - {4853,1075,2248 ,6747,6854,6858 }, {2284,4853,2248 ,6738,6747,6858 }, - {4746,6302,2210 ,5260,6869,2510 }, {2373,2236,2235 ,6722,6802,6870 }, - {7241,7287,7240 ,4465,3459,4312 }, {5140,1936,2094 ,3712,848,5570 }, - {7026,7045,7002 ,5504,5503,1047 }, {7026,7002,7003 ,5504,1047,1046 }, - {6915,6966,6914 ,6871,6840,3532 }, {7049,7064,7007 ,5454,5181,4628 }, - {6571,6572,6611 ,4146,3734,1391 }, {6840,6839,6792 ,6868,6867,5056 }, - {6812,6840,6792 ,6872,6868,5056 }, {6912,6913,6943 ,519,2634,520 }, - {1118,2304,2340 ,6746,6756,6797 }, {7002,7044,7043 ,1047,6859,5895 }, - {6291,6690,6027 ,6873,6753,6874 }, {6690,6687,6027 ,6753,6875,6874 }, - {455,456,1344 ,2205,301,300 }, {6351,8077,8079 ,1568,2078,55 }, - {3495,3496,6532 ,735,3105,6184 }, {6690,3649,4549 ,6753,6755,877 }, - {6687,6690,4549 ,6875,6753,877 }, {2236,6291,6027 ,6802,6873,6874 }, - {1947,2373,2235 ,6723,6722,6870 }, {2235,2236,6027 ,6870,6802,6874 }, - {3649,6661,6621 ,6755,6876,5552 }, {4549,3649,6621 ,877,6755,5552 }, - {5028,4027,1938 ,5109,3470,5934 }, {6661,5048,5629 ,6876,6421,5162 }, - {7004,7026,7003 ,5502,5504,1046 }, {206,3373,3815 ,3932,693,5556 }, - {7004,7003,6963 ,5502,1046,1048 }, {6915,6914,6862 ,6871,3532,6877 }, - {6312,6313,6747 ,6749,6748,6878 }, {7048,7047,7006 ,6790,6853,6841 }, - {6812,6792,6793 ,6872,5056,6879 }, {6794,6812,6793 ,6880,6872,6879 }, - {6911,6942,6941 ,474,521,6793 }, {6861,6913,6912 ,3533,2634,519 }, - {6860,6861,6912 ,473,3533,519 }, {6754,6808,6789 ,713,3465,6767 }, - {6687,4549,5077 ,6875,877,876 }, {6617,6616,6576 ,6881,6882,6883 }, - {6617,6655,6616 ,6881,5407,6882 }, {6655,6654,6616 ,5407,6884,6882 }, - {3556,3557,3580 ,3385,6070,6069 }, {4010,7030,4043 ,4343,6885,419 }, - {4009,4010,4043 ,3870,4343,419 }, {7030,3765,6096 ,6885,4093,4092 }, - {6685,6687,5077 ,6886,6875,876 }, {6621,6661,5629 ,5552,6876,5162 }, - {5404,6617,6576 ,3559,6881,6883 }, {6964,7004,6963 ,6733,5502,1048 }, - {6894,6915,6862 ,6887,6871,6877 }, {6894,6862,6863 ,6887,6877,6866 }, - {6348,6382,6381 ,1607,6888,6860 }, {6347,6348,6381 ,451,1607,6860 }, - {6382,6423,6422 ,6888,6889,6861 }, {6381,6382,6422 ,6860,6888,6861 }, - {6423,3465,6441 ,6889,6890,6862 }, {6422,6423,6441 ,6861,6889,6862 }, - {3465,3500,6473 ,6890,4177,6863 }, {6441,3465,6473 ,6862,6890,6863 }, - {6473,3500,3550 ,6863,4177,4179 }, {6794,6793,6755 ,6880,6879,5055 }, - {6756,6794,6755 ,6891,6880,5055 }, {6913,6944,6943 ,2634,2633,520 }, - {6480,5350,6507 ,1524,1526,2680 }, {7007,7048,7006 ,4628,6790,6841 }, - {5930,6125,6143 ,6593,4395,4394 }, {6140,6121,6101 ,2333,6575,6574 }, - {6893,6856,6906 ,6844,6892,4294 }, {4981,4950,1216 ,1350,1302,6893 }, - {5011,1264,4746 ,6657,5022,5260 }, {2279,2248,1313 ,5337,6858,3674 }, - {6964,6963,6943 ,6733,1048,520 }, {6864,6894,6863 ,6865,6887,6866 }, - {6741,6740,6705 ,5388,6894,4158 }, {6310,6746,6742 ,3918,6864,3422 }, - {5907,5246,3860 ,6116,5176,6523 }, {6382,3465,6423 ,6888,6890,6889 }, - {7102,7101,7088 ,1240,4073,4299 }, {4567,6155,6156 ,6655,3726,6250 }, - {3399,5143,5441 ,715,6099,5194 }, {6756,6755,6740 ,6891,5055,6894 }, - {3973,5446,5388 ,6007,5985,5428 }, {2235,6027,2139 ,6870,6874,6895 }, - {6737,6790,6736 ,6857,711,1622 }, {6619,6554,6521 ,1272,3647,6819 }, - {7076,7075,7039 ,4886,4420,4376 }, {1026,4981,1216 ,6218,1350,6893 }, - {1072,1026,1216 ,6725,6218,6893 }, {6172,4010,5124 ,5451,4343,6567 }, - {5686,5890,5747 ,5777,6591,6036 }, {4261,3777,4290 ,1583,1582,6270 }, - {6750,6751,6786 ,631,5242,4139 }, {6961,7000,6960 ,6813,6814,6896 }, - {7000,7042,7041 ,6814,5338,6838 }, {6999,7000,7041 ,6897,6814,6838 }, - {6909,6910,6960 ,6898,6741,6896 }, {7000,6999,6960 ,6814,6897,6896 }, - {6858,6910,6857 ,6740,6741,6899 }, {6910,6961,6960 ,6741,6813,6896 }, - {6944,6964,6943 ,2633,6733,520 }, {6738,6791,6737 ,5057,6900,6857 }, - {6348,6357,6382 ,1607,5271,6888 }, {6383,3437,3465 ,6901,4142,6890 }, - {6382,6383,3465 ,6888,6901,6890 }, {5694,7009,5716 ,5326,5325,6075 }, - {351,5069,255 ,592,6730,6731 }, {3418,1554,248 ,6366,6304,4114 }, - {5491,5479,5516 ,5419,5387,5420 }, {6741,6756,6740 ,5388,6891,6894 }, - {2221,2291,5114 ,6902,1115,5689 }, {1264,5011,1252 ,5022,6657,5023 }, - {7025,7002,7043 ,6824,1047,5895 }, {6862,6914,6861 ,6877,3532,3533 }, - {6998,7040,7039 ,6855,6903,4376 }, {7040,7076,7039 ,6903,4886,4376 }, - {6910,6909,6857 ,6741,6898,6899 }, {6838,6858,6806 ,6904,6740,6905 }, - {6807,6838,6806 ,6764,6904,6905 }, {6858,6857,6806 ,6740,6899,6905 }, - {3435,3464,3434 ,6703,3151,737 }, {4780,4819,3139 ,5150,5216,5400 }, - {6532,6556,3547 ,6184,4273,803 }, {6146,3970,3926 ,6568,3225,3125 }, - {6899,6146,3926 ,4005,6568,3125 }, {3787,6022,6159 ,5819,6535,5082 }, - {5533,5628,3765 ,6239,6560,4093 }, {7030,5533,3765 ,6885,6239,4093 }, - {4835,4021,3936 ,2130,1712,5599 }, {6753,6752,6700 ,6765,6906,6907 }, - {4547,5058,2163 ,242,5647,243 }, {6071,5590,6354 ,1346,6386,6597 }, - {7675,7697,7719 ,5192,651,6541 }, {6755,6738,6739 ,5055,5057,6836 }, - {6358,6383,6382 ,5270,6901,6888 }, {6357,6358,6382 ,5271,5270,6888 }, - {5628,5533,3609 ,6560,6239,5081 }, {6740,6755,6739 ,6894,5055,6836 }, - {1882,4547,4543 ,894,242,3002 }, {6810,6861,6860 ,6908,3533,473 }, - {6809,6810,6860 ,712,6908,473 }, {6807,6806,6752 ,6764,6905,6906 }, - {3464,3435,6468 ,3151,6703,6759 }, {4043,4007,4009 ,419,3909,3870 }, - {4987,6464,5966 ,1491,3633,5054 }, {5059,3177,4233 ,1253,1255,6185 }, - {4950,5005,4115 ,1302,1301,6909 }, {6862,6861,6810 ,6877,3533,6908 }, - {6701,6753,6700 ,6766,6765,6907 }, {6302,5078,1074 ,6869,5261,2715 }, - {6618,6656,6617 ,6910,5517,6881 }, {6791,6790,6737 ,6900,711,6857 }, - {7744,7795,7794 ,6101,3804,5749 }, {5437,6738,6681 ,6259,5057,5406 }, - {6840,6894,6864 ,6868,6887,6865 }, {6740,6739,5437 ,6894,6836,6259 }, - {342,209,4892 ,4542,4499,5386 }, {6844,6843,6795 ,6822,6911,5418 }, - {5693,6988,6947 ,4075,6771,4587 }, {6810,6809,6790 ,6908,712,711 }, - {6864,6915,6894 ,6865,6871,6887 }, {7620,7647,7675 ,5033,4967,5192 }, - {5950,5013,8108 ,2873,684,2287 }, {6753,6807,6752 ,6765,6764,6906 }, - {7769,7794,7768 ,5535,5749,5536 }, {1170,5627,3471 ,4103,6628,4104 }, - {6847,6900,6899 ,4004,6912,4005 }, {4043,7030,6096 ,419,6885,4092 }, - {3408,3382,3383 ,3484,870,882 }, {6752,6806,6788 ,6906,6905,6913 }, - {6701,6700,6652 ,6766,6907,6914 }, {6653,6701,6652 ,6915,6766,6914 }, - {6656,6655,6617 ,5517,5407,6881 }, {6738,6737,6681 ,5057,6857,5406 }, - {1076,4617,7075 ,4887,4431,4420 }, {5477,5442,5315 ,5399,5174,6445 }, - {6843,6842,6813 ,6911,5433,6916 }, {6350,3362,6358 ,704,1200,5270 }, - {6349,6350,6358 ,705,704,5270 }, {3362,6384,6383 ,1200,1786,6901 }, - {6358,3362,6383 ,5270,1200,6901 }, {6384,3387,6383 ,1786,1787,6901 }, - {6383,3387,3437 ,6901,1787,4142 }, {6791,6810,6790 ,6900,6908,711 }, - {5910,6079,6354 ,5121,5580,6597 }, {6987,7007,6966 ,6917,4628,6840 }, - {6844,6795,5551 ,6822,5418,6260 }, {3814,6799,3855 ,5511,6918,1511 }, - {6799,6761,3855 ,6918,6919,1511 }, {6761,6848,6847 ,6919,6920,4004 }, - {3855,6761,6847 ,1511,6919,4004 }, {6848,6901,6900 ,6920,6921,6912 }, - {6847,6848,6900 ,4004,6920,6912 }, {6901,6899,6900 ,6921,4005,6912 }, - {6147,6146,6899 ,5452,6568,4005 }, {6901,6147,6899 ,6921,5452,4005 }, - {6744,5188,3350 ,3986,4812,2541 }, {7900,7926,7899 ,6922,6923,4116 }, - {3277,8098,2901 ,961,907,3468 }, {6144,4642,5628 ,5083,6226,6560 }, - {2160,5474,2154 ,6621,4243,5321 }, {6616,6653,6615 ,6882,6915,6924 }, - {6297,6696,6694 ,6851,6849,6925 }, {6534,6578,3656 ,4257,6926,4195 }, - {973,6465,2141 ,2017,6927,2015 }, {5593,6920,6896 ,5935,6783,6787 }, - {6795,6843,6813 ,5418,6911,6916 }, {3504,3441,3505 ,2986,2987,6928 }, - {3556,3521,3504 ,3385,4307,2986 }, {1285,1226,1320 ,1944,2354,2356 }, - {3605,3626,3604 ,3383,4196,3384 }, {6572,6612,6611 ,3734,1389,1391 }, - {6125,5930,5979 ,4395,6593,6028 }, {5404,5371,5405 ,3559,3558,5518 }, - {6715,6761,6799 ,6929,6919,6918 }, {3814,6715,6799 ,5511,6929,6918 }, - {1936,2341,2094 ,848,845,5570 }, {1590,1552,1656 ,3028,2266,1166 }, - {3605,3556,3580 ,3383,3385,6069 }, {6159,4642,6144 ,5082,6226,5083 }, - {5087,5077,2095 ,6796,876,6800 }, {6466,6758,973 ,6930,6931,2017 }, - {6909,6908,6857 ,6898,6932,6899 }, {5437,6681,5436 ,6259,5406,6201 }, - {2957,6342,1120 ,6326,5265,5339 }, {6564,6508,6639 ,6933,6934,3699 }, - {3320,6564,6639 ,5455,6933,3699 }, {3192,4784,6387 ,3063,3423,6935 }, - {3656,6578,3714 ,4195,6926,3970 }, {7148,7147,7120 ,6936,6937,6938 }, - {5405,6618,5404 ,5518,6910,3559 }, {5473,7096,5120 ,3429,4337,5857 }, - {6022,4642,6159 ,6535,6226,5082 }, {7286,7285,7261 ,3460,2751,1528 }, - {2139,6685,6682 ,6895,6886,6812 }, {3714,3655,3656 ,3970,3936,4195 }, - {1809,6605,5081 ,878,5164,1258 }, {6682,6685,5087 ,6812,6886,6796 }, - {2095,1809,5081 ,6800,878,1258 }, {5077,1809,2095 ,876,878,6800 }, - {6685,5077,5087 ,6886,876,6796 }, {5359,3815,5977 ,3713,5556,809 }, - {1074,6466,973 ,2715,6930,2017 }, {6466,6465,6758 ,6930,6927,6931 }, - {6653,6652,6615 ,6915,6914,6924 }, {6576,6616,6575 ,6883,6882,6939 }, - {6354,6079,6377 ,6597,5580,1347 }, {1562,5130,4392 ,4649,5571,844 }, - {4856,2244,4570 ,4081,6795,4082 }, {3288,3328,3362 ,2058,3973,1200 }, - {6321,3288,3362 ,706,2058,1200 }, {5441,297,42 ,5194,1918,5195 }, - {6001,6156,5917 ,6221,6250,6402 }, {6443,6486,3557 ,6940,6941,6070 }, - {3504,6443,3557 ,2986,6940,6070 }, {6486,6534,3626 ,6941,4257,4196 }, - {3557,6486,3626 ,6070,6941,4196 }, {5747,5890,6214 ,6036,6591,6942 }, - {6578,6092,3714 ,6926,1623,3970 }, {5742,5977,3815 ,6448,809,5556 }, - {6302,1074,2210 ,6869,2715,2510 }, {2342,5008,2221 ,5690,6118,6902 }, - {6640,5435,6611 ,4224,4223,1391 }, {6176,6716,6715 ,1624,6943,6929 }, - {7232,7231,7196 ,2124,6944,2401 }, {6716,6762,6761 ,6943,6945,6919 }, - {6715,6716,6761 ,6929,6943,6919 }, {6762,6815,6761 ,6945,6946,6919 }, - {6761,6815,6848 ,6919,6946,6920 }, {6902,6901,6848 ,6947,6921,6920 }, - {6815,6902,6848 ,6946,6947,6920 }, {6952,6147,6901 ,6948,5452,6921 }, - {6902,6952,6901 ,6947,6948,6921 }, {6952,6990,5920 ,6948,6739,5453 }, - {6147,6952,5920 ,5452,6948,5453 }, {7001,7002,7025 ,6815,1047,6824 }, - {6652,6700,6699 ,6914,6907,630 }, {2234,2139,5454 ,6949,6895,6817 }, - {2241,2234,5454 ,6950,6949,6817 }, {5454,2139,6682 ,6817,6895,6812 }, - {5300,5327,6041 ,3793,3702,3801 }, {4207,1444,1745 ,4247,4035,6233 }, - {5498,1742,1218 ,5590,707,3255 }, {6616,6615,6575 ,6882,6924,6939 }, - {6844,6866,6843 ,6822,6786,6911 }, {1380,3284,6313 ,6721,6720,6748 }, - {5593,6896,6867 ,5935,6787,6951 }, {6896,6919,6895 ,6787,6778,5434 }, - {5024,2138,6660 ,6952,6602,6601 }, {5087,2095,1117 ,6796,6800,4928 }, - {6486,6487,6534 ,6941,6953,4257 }, {6717,6763,6762 ,6954,6955,6945 }, - {6716,6717,6762 ,6943,6954,6945 }, {6763,6816,6815 ,6955,6956,6946 }, - {6762,6763,6815 ,6945,6955,6946 }, {6903,6902,6815 ,6957,6947,6946 }, - {6816,6903,6815 ,6956,6957,6946 }, {3393,3365,3394 ,4441,4240,6958 }, - {5111,5110,7166 ,5990,5953,5971 }, {6805,6855,6854 ,3343,6959,3344 }, - {6391,4550,6659 ,6804,716,3577 }, {6642,6652,6651 ,6960,6914,6785 }, - {6958,6959,6997 ,4314,6003,4368 }, {6575,6615,6574 ,6939,6924,3885 }, - {6651,6652,6699 ,6785,6914,630 }, {6752,6751,6699 ,6906,5242,630 }, - {6641,6642,6651 ,6961,6960,6785 }, {2260,6021,5494 ,6571,6551,6569 }, - {7239,7286,7261 ,1527,3460,1528 }, {6466,6315,6465 ,6930,6962,6927 }, - {6315,6392,306 ,6962,6963,6234 }, {6528,6576,6527 ,6964,6883,6965 }, - {5086,351,2246 ,6064,592,591 }, {6313,4445,6747 ,6748,3648,6878 }, - {2336,6521,5496 ,1261,6819,6966 }, {6228,1664,253 ,1366,2268,2564 }, - {7050,7067,7028 ,6967,3585,3587 }, {6443,3441,3442 ,6940,2987,4460 }, - {6708,6710,5053 ,6148,6968,6848 }, {6443,6487,6486 ,6940,6953,6941 }, - {6565,6579,6578 ,6969,6970,6926 }, {6534,6565,6578 ,4257,6969,6926 }, - {6579,6092,6578 ,6970,1623,6926 }, {6691,6717,6716 ,6971,6954,6943 }, - {6176,6691,6716 ,1624,6971,6943 }, {6903,6953,6952 ,6957,6972,6948 }, - {6902,6903,6952 ,6947,6957,6948 }, {6991,6990,6952 ,6973,6739,6948 }, - {6953,6991,6952 ,6972,6973,6948 }, {6991,7032,5918 ,6973,6974,5820 }, - {6990,6991,5918 ,6739,6973,5820 }, {1275,6108,1221 ,1688,6975,1759 }, - {6642,6641,6614 ,6960,6961,3886 }, {6615,6642,6614 ,6924,6960,3886 }, - {6615,6614,6574 ,6924,3886,3885 }, {3311,5192,3762 ,5303,6454,5172 }, - {6999,6998,6959 ,6897,6855,6003 }, {6315,306,4960 ,6962,6234,6856 }, - {6909,6960,6908 ,6898,6896,6932 }, {6576,6575,6527 ,6883,6939,6965 }, - {5104,5086,2246 ,6063,6064,591 }, {3284,4445,6313 ,6720,3648,6748 }, - {5438,6705,5437 ,4156,4158,6259 }, {5251,5210,6433 ,3802,6033,6691 }, - {6444,6488,6487 ,6976,6977,6953 }, {6443,6444,6487 ,6940,6976,6953 }, - {6488,6535,6534 ,6977,6978,4257 }, {6487,6488,6534 ,6953,6977,4257 }, - {6534,6535,6565 ,4257,6978,6969 }, {6535,6580,6579 ,6978,6979,6970 }, - {6565,6535,6579 ,6969,6978,6970 }, {6622,6092,6579 ,6980,1623,6970 }, - {6580,6622,6579 ,6979,6980,6970 }, {6622,6662,6176 ,6980,6981,1624 }, - {6092,6622,6176 ,1623,6980,1624 }, {6176,6662,6691 ,1624,6981,6971 }, - {6662,6718,6717 ,6981,6982,6954 }, {6691,6662,6717 ,6971,6981,6954 }, - {6718,6764,6763 ,6982,6983,6955 }, {6717,6718,6763 ,6954,6982,6955 }, - {6817,6816,6763 ,6984,6956,6955 }, {6764,6817,6763 ,6983,6984,6955 }, - {6871,6903,6816 ,6985,6957,6956 }, {6817,6871,6816 ,6984,6985,6956 }, - {6871,6954,6953 ,6985,6986,6972 }, {6903,6871,6953 ,6957,6985,6972 }, - {6992,6991,6953 ,6987,6973,6972 }, {6954,6992,6953 ,6986,6987,6972 }, - {6992,5945,7032 ,6987,6396,6974 }, {6991,6992,7032 ,6973,6987,6974 }, - {5945,5918,7032 ,6396,5820,6974 }, {2808,1987,1920 ,522,3614,581 }, - {6315,5009,6465 ,6962,6803,6927 }, {3425,1724,8103 ,1859,3880,1291 }, - {6960,6999,6959 ,6896,6897,6003 }, {7974,7973,7923 ,4171,3624,816 }, - {3359,3326,6379 ,824,826,6831 }, {6071,6354,6377 ,1346,6597,1347 }, - {5633,6949,5611 ,6179,4585,6177 }, {405,3499,5628 ,5662,5661,6560 }, - {6661,6688,5048 ,6876,6846,6421 }, {7027,7007,7008 ,3175,4628,3176 }, - {2406,1990,6530 ,6736,1305,6398 }, {3442,6444,6443 ,4460,6976,6940 }, - {6706,7814,5479 ,5389,6988,5387 }, {7092,7104,7091 ,3968,5280,4153 }, - {1879,5049,4744 ,1283,5038,4424 }, {6856,6907,6906 ,6892,6002,4294 }, - {7041,7040,6998 ,6838,6903,6855 }, {1394,2074,1027 ,6989,6990,6991 }, - {6793,6792,6755 ,6879,5056,5055 }, {2304,2303,2340 ,6756,6758,6797 }, - {3609,6990,5918 ,5081,6739,5820 }, {6345,4595,5118 ,191,5289,6825 }, - {6444,6474,6488 ,6976,6992,6977 }, {6488,6514,6535 ,6977,6993,6978 }, - {6999,7041,6998 ,6897,6838,6855 }, {6960,6959,6907 ,6896,6003,6002 }, - {4900,2274,4545 ,585,893,586 }, {6614,6641,6651 ,3886,6961,6785 }, - {6793,6811,6792 ,6879,399,5056 }, {7566,7565,7532 ,6009,5080,6705 }, - {3394,6395,3442 ,6958,6994,4460 }, {3393,3394,3442 ,4441,6958,4460 }, - {6395,6445,6444 ,6994,6995,6976 }, {3442,6395,6444 ,4460,6994,6976 }, - {6445,6446,6474 ,6995,6996,6992 }, {6444,6445,6474 ,6976,6995,6992 }, - {6446,6489,6488 ,6996,6997,6977 }, {6474,6446,6488 ,6992,6996,6977 }, - {6489,6490,6514 ,6997,6998,6993 }, {6488,6489,6514 ,6977,6997,6993 }, - {6490,6536,6535 ,6998,6999,6978 }, {6514,6490,6535 ,6993,6998,6978 }, - {6536,6581,6580 ,6999,7000,6979 }, {6535,6536,6580 ,6978,6999,6979 }, - {6623,6622,6580 ,7001,6980,6979 }, {6581,6623,6580 ,7000,7001,6979 }, - {6623,6663,6662 ,7001,7002,6981 }, {6622,6623,6662 ,6980,7001,6981 }, - {6663,6719,6718 ,7002,7003,6982 }, {6662,6663,6718 ,6981,7002,6982 }, - {6719,6765,6764 ,7003,7004,6983 }, {6718,6719,6764 ,6982,7003,6983 }, - {6765,6766,6764 ,7004,7005,6983 }, {6766,6818,6817 ,7005,7006,6984 }, - {6764,6766,6817 ,6983,7005,6984 }, {6818,6849,6817 ,7006,7007,6984 }, - {6872,6871,6817 ,7008,6985,6984 }, {6849,6872,6817 ,7007,7008,6984 }, - {6872,6955,6954 ,7008,7009,6986 }, {6871,6872,6954 ,6985,7008,6986 }, - {6955,6993,6992 ,7009,7010,6987 }, {6954,6955,6992 ,6986,7009,6987 }, - {6993,7033,5945 ,7010,6395,6396 }, {6992,6993,5945 ,6987,7010,6396 }, - {3970,5124,3969 ,3225,6567,3226 }, {4845,5187,5070 ,3921,5144,6043 }, - {5878,3835,3762 ,2684,6046,5172 }, {4672,5878,3762 ,2682,2684,5172 }, - {6908,6960,6907 ,6932,6896,6002 }, {7041,7077,7076 ,6838,6837,4886 }, - {7048,2074,7047 ,6790,6990,6853 }, {2222,4746,2210 ,2509,5260,2510 }, - {6479,6528,6478 ,2659,6964,7011 }, {6963,7002,7001 ,1048,1047,6815 }, - {248,4530,3350 ,4114,1376,2541 }, {4489,4068,3778 ,5584,408,5585 }, - {6301,6710,6708 ,6147,6968,6148 }, {6811,6839,6792 ,399,6867,5056 }, - {5926,5940,6715 ,6419,1625,6929 }, {5081,2208,1117 ,1258,1257,4928 }, - {6014,5590,6259 ,6592,6386,6718 }, {3394,6396,6395 ,6958,7012,6994 }, - {6396,6446,6445 ,7012,6996,6995 }, {6395,6396,6445 ,6994,7012,6995 }, - {6663,6720,6719 ,7002,7013,7003 }, {6720,6766,6765 ,7013,7005,7004 }, - {6719,6720,6765 ,7003,7013,7004 }, {6873,6872,6849 ,7014,7008,7007 }, - {6818,6873,6849 ,7006,7014,7007 }, {6955,6922,6993 ,7009,7015,7010 }, - {6855,6856,6805 ,6959,6892,3343 }, {3291,3293,3292 ,1203,1988,4067 }, - {4704,4763,5217 ,4788,4787,4026 }, {1987,1220,1920 ,3614,4885,581 }, - {7040,7041,7076 ,6903,6838,4886 }, {6392,6315,6466 ,6963,6962,6930 }, - {6528,6527,6478 ,6964,6965,7011 }, {6388,6436,6435 ,3184,3681,2658 }, - {2138,3704,6190 ,6602,2332,6610 }, {5315,5442,5044 ,6445,5174,5173 }, - {3394,6385,6396 ,6958,7016,7012 }, {6446,6490,6489 ,6996,6998,6997 }, - {6663,6692,6720 ,7002,7017,7013 }, {6873,6922,6872 ,7014,7015,7008 }, - {6872,6922,6955 ,7008,7015,7009 }, {6994,6993,6922 ,7018,7010,7015 }, - {6994,7034,7033 ,7018,6045,6395 }, {6993,6994,7033 ,7010,7018,6395 }, - {4672,5477,6036 ,2682,5399,6141 }, {5744,5716,7009 ,6090,6075,5325 }, - {685,2629,794 ,2674,2673,3392 }, {2336,6619,6521 ,1261,1272,6819 }, - {6619,1265,4190 ,1272,1273,3439 }, {6908,6907,6856 ,6932,6002,6892 }, - {5005,2336,5496 ,1301,1261,6966 }, {6857,6908,6856 ,6899,6932,6892 }, - {3600,3550,3578 ,5331,4179,4205 }, {6951,2211,6870 ,2615,6780,2616 }, - {1264,403,5078 ,5022,5547,5261 }, {2409,2237,2373 ,6852,6801,6722 }, - {5075,1215,184 ,1586,3937,4052 }, {7068,6166,7034 ,7019,4971,6045 }, - {6324,6360,3394 ,4239,7020,6958 }, {3365,6324,3394 ,4240,4239,6958 }, - {6360,6361,6385 ,7020,7021,7016 }, {3394,6360,6385 ,6958,7020,7016 }, - {6361,6397,6396 ,7021,7022,7012 }, {6385,6361,6396 ,7016,7021,7012 }, - {6397,6447,6446 ,7022,7023,6996 }, {6396,6397,6446 ,7012,7022,6996 }, - {6447,6475,6446 ,7023,7024,6996 }, {6475,6491,6490 ,7024,7025,6998 }, - {6446,6475,6490 ,6996,7024,6998 }, {6491,6537,6536 ,7025,7026,6999 }, - {6490,6491,6536 ,6998,7025,6999 }, {6537,6582,6581 ,7026,7027,7000 }, - {6536,6537,6581 ,6999,7026,7000 }, {6624,6623,6581 ,7028,7001,7000 }, - {6582,6624,6581 ,7027,7028,7000 }, {6624,6664,6663 ,7028,7029,7002 }, - {6623,6624,6663 ,7001,7028,7002 }, {6663,6664,6692 ,7002,7029,7017 }, - {6664,6721,6720 ,7029,7030,7013 }, {6692,6664,6720 ,7017,7029,7013 }, - {6721,6767,6766 ,7030,7031,7005 }, {6720,6721,6766 ,7013,7030,7005 }, - {6767,6819,6818 ,7031,7032,7006 }, {6766,6767,6818 ,7005,7031,7006 }, - {6874,6873,6818 ,7033,7014,7006 }, {6819,6874,6818 ,7032,7033,7006 }, - {6874,6923,6922 ,7033,7034,7015 }, {6873,6874,6922 ,7014,7033,7015 }, - {6971,6994,6922 ,7035,7018,7015 }, {6923,6971,6922 ,7034,7035,7015 }, - {6971,7035,7034 ,7035,7036,6045 }, {6994,6971,7034 ,7018,7035,6045 }, - {3311,5866,5192 ,5303,5302,6454 }, {7035,7068,7034 ,7036,7019,6045 }, - {2357,2798,4043 ,3933,420,419 }, {8019,2320,8018 ,1407,1406,6676 }, - {1204,1205,4499 ,338,3187,4510 }, {5076,5904,4200 ,6607,5851,6583 }, - {6687,6685,2139 ,6875,6886,6895 }, {6027,6687,2139 ,6874,6875,6895 }, - {2374,2455,4018 ,7037,475,5084 }, {1551,4921,2093 ,5275,2079,553 }, - {3952,4068,4489 ,5597,408,5584 }, {6036,5892,4672 ,6141,2683,2682 }, - {535,6153,5105 ,2001,5514,5456 }, {2455,2374,1027 ,475,7037,6991 }, - {5274,5251,6433 ,6145,3802,6691 }, {6805,6856,6855 ,3343,6892,6959 }, - {6434,6479,6478 ,3127,2659,7011 }, {6433,6434,6478 ,6691,3127,7011 }, - {7161,7186,7169 ,2559,5370,5437 }, {912,2209,2240 ,4471,7038,4478 }, - {4445,6748,6747 ,3648,3408,6878 }, {5647,5688,6890 ,5333,5519,5334 }, - {7533,7509,7510 ,7039,1874,1873 }, {6836,5353,8111 ,852,851,4359 }, - {2211,2222,6870 ,6780,2509,2616 }, {4017,5075,184 ,5342,1586,4052 }, - {6324,6361,6360 ,4239,7021,7020 }, {2290,7936,2292 ,1982,5528,1983 }, - {5394,3765,5628 ,5660,4093,6560 }, {2074,1071,1027 ,6990,3382,6991 }, - {2248,2279,7043 ,6858,5337,5895 }, {6997,6998,7039 ,4368,6855,4376 }, - {6758,6465,973 ,6931,6927,2017 }, {2205,6563,6898 ,2874,464,1946 }, - {6690,6694,7022 ,6753,6925,6754 }, {6696,5054,3649 ,6849,6847,6755 }, - {6173,6071,6088 ,3711,1346,3670 }, {7972,8018,7994 ,6059,6676,5582 }, - {2211,1270,2222 ,6780,6781,2509 }, {1749,6713,1215 ,555,6820,3937 }, - {6566,6583,6582 ,7040,7041,7027 }, {6537,6566,6582 ,7026,7040,7027 }, - {6583,6624,6582 ,7041,7028,7027 }, {4550,4960,5986 ,716,6856,4603 }, - {5009,6315,6391 ,6803,6962,6804 }, {6315,4960,6391 ,6962,6856,6804 }, - {6391,4960,4550 ,6804,6856,716 }, {3217,6030,5747 ,6117,4037,6036 }, - {5006,4981,1026 ,1351,1350,6218 }, {5503,5463,5504 ,7042,5801,5781 }, - {6569,5011,4746 ,6745,6657,5260 }, {3936,3820,4835 ,5599,5591,2130 }, - {6148,6599,6161 ,6586,6596,6530 }, {5191,6434,6433 ,2900,3127,6691 }, - {6291,6297,6690 ,6873,6851,6753 }, {6694,6696,7022 ,6925,6849,6754 }, - {7795,7846,7794 ,3804,4162,5749 }, {5075,1749,1215 ,1586,555,3937 }, - {6116,6325,6324 ,4170,7043,4239 }, {3336,6116,6324 ,3922,4170,4239 }, - {6325,6362,6361 ,7043,7044,7021 }, {6324,6325,6361 ,4239,7043,7021 }, - {6362,6398,6397 ,7044,7045,7022 }, {6361,6362,6397 ,7021,7044,7022 }, - {6398,6448,6447 ,7045,7046,7023 }, {6397,6398,6447 ,7022,7045,7023 }, - {6447,6448,6475 ,7023,7046,7024 }, {6448,6492,6491 ,7046,7047,7025 }, - {6475,6448,6491 ,7024,7046,7025 }, {6492,6538,6537 ,7047,7048,7026 }, - {6491,6492,6537 ,7025,7047,7026 }, {6537,6538,6566 ,7026,7048,7040 }, - {6538,6584,6583 ,7048,7049,7041 }, {6566,6538,6583 ,7040,7048,7041 }, - {6625,6624,6583 ,7050,7028,7041 }, {6584,6625,6583 ,7049,7050,7041 }, - {6625,6665,6664 ,7050,7051,7029 }, {6624,6625,6664 ,7028,7050,7029 }, - {6665,6722,6721 ,7051,7052,7030 }, {6664,6665,6721 ,7029,7051,7030 }, - {6722,6768,6767 ,7052,7053,7031 }, {6721,6722,6767 ,7030,7052,7031 }, - {6768,6820,6819 ,7053,7054,7032 }, {6767,6768,6819 ,7031,7053,7032 }, - {6875,6874,6819 ,7055,7033,7032 }, {6820,6875,6819 ,7054,7055,7032 }, - {6875,6924,6923 ,7055,7056,7034 }, {6874,6875,6923 ,7033,7055,7034 }, - {6924,6972,6971 ,7056,7057,7035 }, {6923,6924,6971 ,7034,7056,7035 }, - {7036,7035,6971 ,7058,7036,7035 }, {6972,7036,6971 ,7057,7058,7035 }, - {7036,7069,7068 ,7058,7059,7019 }, {7035,7036,7068 ,7036,7058,7019 }, - {4901,3892,7068 ,3596,4970,7019 }, {7069,4901,7068 ,7059,3596,7019 }, - {6527,6575,6526 ,6965,6939,6784 }, {6575,6574,6526 ,6939,3885,6784 }, - {6478,6527,5300 ,7011,6965,3793 }, {6527,6526,5300 ,6965,6784,3793 }, - {6433,6478,5300 ,6691,7011,3793 }, {5274,6433,5300 ,6145,6691,3793 }, - {1269,1125,2159 ,5724,5723,6440 }, {187,1269,2159 ,6439,5724,6440 }, - {4391,4422,4443 ,992,5561,703 }, {6702,6701,6653 ,7060,6766,6915 }, - {6654,6702,6653 ,6884,7060,6915 }, {6297,6694,6690 ,6851,6925,6753 }, - {6465,4985,2141 ,6927,2703,2015 }, {3156,1658,1657 ,3164,1782,1781 }, - {2257,5056,6713 ,554,1300,6820 }, {949,4994,5029 ,2133,5076,2159 }, - {6788,6787,6751 ,6913,5269,5242 }, {6700,6752,6699 ,6907,6906,630 }, - {6806,6805,6787 ,6905,3343,5269 }, {6752,6788,6751 ,6906,6913,5242 }, - {6788,6806,6787 ,6913,6905,5269 }, {6857,6855,6805 ,6899,6959,3343 }, - {6857,6856,6855 ,6899,6892,6959 }, {6806,6857,6805 ,6905,6899,3343 }, - {3454,5089,7052 ,710,3442,708 }, {3926,3891,6899 ,3125,3820,4005 }, - {4978,4567,5592 ,178,6655,3671 }, {2236,2237,6291 ,6802,6801,6873 }, - {5590,6071,6173 ,6386,1346,3711 }, {6918,6917,6895 ,6779,5515,5434 }, - {1749,2257,6713 ,555,554,6820 }, {4979,3319,5056 ,5363,1259,1300 }, - {6492,6515,6538 ,7047,7061,7048 }, {6768,6821,6820 ,7053,7062,7054 }, - {6821,6876,6875 ,7062,7063,7055 }, {6820,6821,6875 ,7054,7062,7055 }, - {6876,6925,6924 ,7063,7064,7056 }, {6875,6876,6924 ,7055,7063,7056 }, - {6973,6972,6924 ,7065,7057,7056 }, {6925,6973,6924 ,7064,7065,7056 }, - {6973,7036,6972 ,7065,7058,7057 }, {7030,4010,5533 ,6885,4343,6239 }, - {1881,2234,2241 ,7066,6949,6950 }, {2209,1881,2241 ,7038,7066,6950 }, - {1881,2209,1076 ,7066,7038,4887 }, {4142,1881,1076 ,6839,7066,4887 }, - {7077,4142,1076 ,6837,6839,4887 }, {5009,6463,4985 ,6803,5159,2703 }, - {7076,7077,1076 ,4886,6837,4887 }, {5926,6715,3814 ,6419,6929,5511 }, - {6108,1172,1221 ,6975,1758,1759 }, {7436,7412,7386 ,4245,3765,5512 }, - {3441,6443,3505 ,2987,6940,6928 }, {5370,5474,2160 ,2171,4243,6621 }, - {2257,4979,5056 ,554,5363,1300 }, {6286,6326,6325 ,4349,7067,7043 }, - {6116,6286,6325 ,4170,4349,7043 }, {6326,6363,6362 ,7067,7068,7044 }, - {6325,6326,6362 ,7043,7067,7044 }, {6363,6399,6398 ,7068,7069,7045 }, - {6362,6363,6398 ,7044,7068,7045 }, {6399,6449,6448 ,7069,7070,7046 }, - {6398,6399,6448 ,7045,7069,7046 }, {6449,6493,6492 ,7070,7071,7047 }, - {6448,6449,6492 ,7046,7070,7047 }, {6493,6516,6515 ,7071,7072,7061 }, - {6492,6493,6515 ,7047,7071,7061 }, {6516,6539,6538 ,7072,7073,7048 }, - {6515,6516,6538 ,7061,7072,7048 }, {6539,6585,6584 ,7073,7074,7049 }, - {6538,6539,6584 ,7048,7073,7049 }, {6626,6625,6584 ,7075,7050,7049 }, - {6585,6626,6584 ,7074,7075,7049 }, {6626,6666,6665 ,7075,7076,7051 }, - {6625,6626,6665 ,7050,7075,7051 }, {6666,6723,6722 ,7076,7077,7052 }, - {6665,6666,6722 ,7051,7076,7052 }, {6769,6768,6722 ,7078,7053,7052 }, - {6723,6769,6722 ,7077,7078,7052 }, {6769,6822,6821 ,7078,7079,7062 }, - {6768,6769,6821 ,7053,7078,7062 }, {6822,6877,6876 ,7079,7080,7063 }, - {6821,6822,6876 ,7062,7079,7063 }, {6877,6926,6925 ,7080,7081,7064 }, - {6876,6877,6925 ,7063,7080,7064 }, {6974,6973,6925 ,7082,7065,7064 }, - {6926,6974,6925 ,7081,7082,7064 }, {6974,7010,6973 ,7082,7083,7065 }, - {7010,7036,6973 ,7083,7058,7065 }, {7010,7070,7069 ,7083,7084,7059 }, - {7036,7010,7069 ,7058,7083,7059 }, {3813,5926,3814 ,1515,6419,5511 }, - {7972,7994,7948 ,6059,5582,3625 }, {4082,1426,3452 ,4716,4648,1794 }, - {2235,2234,1881 ,6870,6949,7066 }, {307,4989,6463 ,5095,5094,5159 }, - {2209,912,4617 ,7038,4471,4431 }, {7044,2248,7043 ,6859,6858,5895 }, - {2248,1075,1313 ,6858,6854,3674 }, {186,187,1171 ,1265,6439,6441 }, - {3133,1269,187 ,7085,5724,6439 }, {186,3133,187 ,1265,7085,6439 }, - {5890,4648,1269 ,6591,5722,5724 }, {3133,5890,1269 ,7085,6591,5724 }, - {1126,6037,1272 ,7086,5235,5740 }, {2237,6297,6291 ,6801,6851,6873 }, - {6748,1990,2406 ,3408,1305,6736 }, {6747,6748,2406 ,6878,3408,6736 }, - {4684,2245,3319 ,5217,1260,1259 }, {4979,4684,3319 ,5363,5217,1259 }, - {6305,6327,6326 ,7087,7088,7067 }, {6286,6305,6326 ,4349,7087,7067 }, - {6326,6327,6363 ,7067,7088,7068 }, {6769,6770,6822 ,7078,7089,7079 }, - {4901,7069,7070 ,3596,7059,7084 }, {4900,6378,2274 ,585,6728,893 }, - {6689,2405,2337 ,3065,4165,6829 }, {6378,1882,2274 ,6728,894,893 }, - {3556,3504,3557 ,3385,2986,6070 }, {4570,5072,6261 ,4082,6619,4321 }, - {5444,4544,5040 ,3410,748,5469 }, {7533,7568,7509 ,7039,5544,1874 }, - {6666,6724,6723 ,7076,7090,7077 }, {6724,6770,6769 ,7090,7089,7078 }, - {6723,6724,6769 ,7077,7090,7078 }, {6787,6805,6804 ,5269,3343,3342 }, - {978,977,4965 ,1620,3931,784 }, {6615,6652,6642 ,6924,6914,6960 }, - {3786,3400,1451 ,4074,3659,175 }, {6005,186,1171 ,5691,1265,6441 }, - {1026,733,5006 ,6218,4051,1351 }, {6507,6529,6479 ,2680,5025,2659 }, - {4988,5494,5020 ,6744,6569,6373 }, {8109,8127,8119 ,2938,2970,184 }, - {2246,2452,7066 ,591,7091,3586 }, {4684,6603,6359 ,5217,3985,6830 }, - {2245,4684,6359 ,1260,5217,6830 }, {6286,6287,6305 ,4349,4377,7087 }, - {6287,6328,6327 ,4377,7092,7088 }, {6305,6287,6327 ,7087,4377,7088 }, - {6328,6364,6363 ,7092,7093,7068 }, {6327,6328,6363 ,7088,7092,7068 }, - {6364,6400,6399 ,7093,7094,7069 }, {6363,6364,6399 ,7068,7093,7069 }, - {6400,6424,6399 ,7094,7095,7069 }, {6424,6450,6449 ,7095,7096,7070 }, - {6399,6424,6449 ,7069,7095,7070 }, {6450,6494,6493 ,7096,7097,7071 }, - {6449,6450,6493 ,7070,7096,7071 }, {6495,6516,6493 ,7098,7072,7071 }, - {6494,6495,6493 ,7097,7098,7071 }, {6540,6539,6516 ,7099,7073,7072 }, - {6495,6540,6516 ,7098,7099,7072 }, {6586,6585,6539 ,7100,7074,7073 }, - {6540,6586,6539 ,7099,7100,7073 }, {6586,6627,6626 ,7100,7101,7075 }, - {6585,6586,6626 ,7074,7100,7075 }, {6627,6667,6666 ,7101,7102,7076 }, - {6626,6627,6666 ,7075,7101,7076 }, {6667,6668,6666 ,7102,7103,7076 }, - {6668,6725,6724 ,7103,7104,7090 }, {6666,6668,6724 ,7076,7103,7090 }, - {6725,6771,6770 ,7104,7105,7089 }, {6724,6725,6770 ,7090,7104,7089 }, - {6771,6823,6822 ,7105,7106,7079 }, {6770,6771,6822 ,7089,7105,7079 }, - {6823,6878,6877 ,7106,7107,7080 }, {6822,6823,6877 ,7079,7106,7080 }, - {6927,6926,6877 ,7108,7081,7080 }, {6878,6927,6877 ,7107,7108,7080 }, - {6975,6974,6926 ,7109,7082,7081 }, {6927,6975,6926 ,7108,7109,7081 }, - {6975,7011,7010 ,7109,7110,7083 }, {6974,6975,7010 ,7082,7109,7083 }, - {7011,7071,7070 ,7110,7111,7084 }, {7010,7011,7070 ,7083,7110,7084 }, - {7071,1261,7070 ,7111,6472,7084 }, {1261,5591,7070 ,6472,3597,7084 }, - {4986,6659,3399 ,5096,3577,715 }, {6854,6855,6893 ,3344,6959,6844 }, - {1076,2209,4617 ,4887,7038,4431 }, {4746,5078,6302 ,5260,5261,6869 }, - {2291,2221,3133 ,1115,6902,7085 }, {186,2291,3133 ,1265,1115,7085 }, - {2221,6214,5890 ,6902,6942,6591 }, {3133,2221,5890 ,7085,6902,6591 }, - {4950,4115,1216 ,1302,6909,6893 }, {5072,6220,6231 ,6619,6600,6624 }, - {2260,5494,4988 ,6571,6569,6744 }, {2211,2260,4988 ,6780,6571,6744 }, - {5444,5040,5024 ,3410,5469,6952 }, {2157,5444,5024 ,3411,3410,6952 }, - {5591,4901,7070 ,3597,3596,7084 }, {6627,6668,6667 ,7101,7103,7102 }, - {5330,5331,5350 ,3345,6198,1526 }, {5611,6920,5593 ,6177,6783,5935 }, - {6234,6259,6167 ,5973,6718,5974 }, {2717,819,728 ,2676,2244,1266 }, - {2025,2000,2027 ,2430,2428,2492 }, {2342,2221,5114 ,5690,6902,5689 }, - {5435,5401,6611 ,4223,4145,1391 }, {6529,6576,6528 ,5025,6883,6964 }, - {6870,2222,2205 ,2616,2509,2874 }, {6603,1116,5074 ,3985,1401,6751 }, - {6359,6603,5074 ,6830,3985,6751 }, {6476,6495,6494 ,7112,7098,7097 }, - {6450,6476,6494 ,7096,7112,7097 }, {6586,6628,6627 ,7100,7113,7101 }, - {6627,6628,6668 ,7101,7113,7103 }, {6163,3860,6029 ,1453,6523,1454 }, - {5059,7081,2220 ,1253,7114,6724 }, {1171,187,3498 ,6441,6439,5800 }, - {7022,6696,3649 ,6754,6849,6755 }, {2260,2211,6951 ,6571,6780,2615 }, - {2157,5024,2244 ,3411,6952,6795 }, {6278,6288,6287 ,3967,7115,4377 }, - {6277,6278,6287 ,4387,3967,4377 }, {6288,6329,6328 ,7115,7116,7092 }, - {6287,6288,6328 ,4377,7115,7092 }, {6329,6365,6364 ,7116,7117,7093 }, - {6328,6329,6364 ,7092,7116,7093 }, {6365,6401,6400 ,7117,7118,7094 }, - {6364,6365,6400 ,7093,7117,7094 }, {6401,6425,6424 ,7118,7119,7095 }, - {6400,6401,6424 ,7094,7118,7095 }, {6425,6451,6450 ,7119,7120,7096 }, - {6424,6425,6450 ,7095,7119,7096 }, {6450,6451,6476 ,7096,7120,7112 }, - {6451,6496,6495 ,7120,7121,7098 }, {6476,6451,6495 ,7112,7120,7098 }, - {6496,6541,6540 ,7121,7122,7099 }, {6495,6496,6540 ,7098,7121,7099 }, - {6541,6587,6586 ,7122,7123,7100 }, {6540,6541,6586 ,7099,7122,7100 }, - {6587,6607,6586 ,7123,7124,7100 }, {6607,6629,6628 ,7124,7125,7113 }, - {6586,6607,6628 ,7100,7124,7113 }, {6629,6669,6668 ,7125,7126,7103 }, - {6628,6629,6668 ,7113,7125,7103 }, {6669,6726,6725 ,7126,7127,7104 }, - {6668,6669,6725 ,7103,7126,7104 }, {6772,6771,6725 ,7128,7105,7104 }, - {6726,6772,6725 ,7127,7128,7104 }, {6772,6824,6823 ,7128,7129,7106 }, - {6771,6772,6823 ,7105,7128,7106 }, {6824,6879,6878 ,7129,7130,7107 }, - {6823,6824,6878 ,7106,7129,7107 }, {6879,6880,6878 ,7130,7131,7107 }, - {6880,6928,6927 ,7131,7132,7108 }, {6878,6880,6927 ,7107,7131,7108 }, - {6976,6975,6927 ,7133,7109,7108 }, {6928,6976,6927 ,7132,7133,7108 }, - {6976,7012,7011 ,7133,7134,7110 }, {6975,6976,7011 ,7109,7133,7110 }, - {7053,7071,7011 ,6654,7111,7110 }, {7012,7053,7011 ,7134,6654,7110 }, - {1723,1261,7071 ,6626,6472,7111 }, {7053,1723,7071 ,6654,6626,7111 }, - {8127,3221,8119 ,2970,183,184 }, {6792,6839,6810 ,5056,6867,6908 }, - {7533,7595,7568 ,7039,4019,5544 }, {3979,5055,5753 ,6471,6470,6481 }, - {5008,3217,6214 ,6118,6117,6942 }, {2221,5008,6214 ,6902,6118,6942 }, - {6214,3217,5747 ,6942,6117,6036 }, {6654,6653,6616 ,6884,6915,6882 }, - {6529,6528,6479 ,5025,6964,2659 }, {8038,3638,8052 ,4121,5950,4122 }, - {6049,2260,6951 ,6570,6571,2615 }, {6606,2157,2244 ,5085,3411,6795 }, - {4990,1124,6065 ,7135,5852,6616 }, {1116,2239,185 ,1401,1377,1493 }, - {5074,1116,185 ,6751,1401,1493 }, {6824,6880,6879 ,7129,7131,7130 }, - {6880,6929,6928 ,7131,7136,7132 }, {6929,6976,6928 ,7136,7133,7132 }, - {6977,7013,7012 ,7137,7138,7134 }, {6976,6977,7012 ,7133,7137,7134 }, - {7013,7053,7012 ,7138,6654,7134 }, {7570,7625,7598 ,5204,7139,7140 }, - {6910,6941,6961 ,6741,6793,6813 }, {7000,7024,7042 ,6814,6816,5338 }, - {5328,2160,2238 ,2172,6621,2697 }, {5045,7061,6165 ,4214,7141,4219 }, - {4649,5125,5118 ,5288,5467,6825 }, {1214,6049,6951 ,6573,6570,2615 }, - {6606,2244,4856 ,5085,6795,4081 }, {3040,8072,8076 ,619,511,617 }, - {6824,6850,6880 ,7129,7142,7131 }, {6929,6977,6976 ,7136,7137,7133 }, - {7054,7053,7013 ,7143,6654,7138 }, {7081,4233,2220 ,7114,6185,6724 }, - {5124,4010,3969 ,6567,4343,3226 }, {7454,7511,7453 ,3996,6677,1187 }, - {5057,1214,6951 ,1795,6573,2615 }, {2454,6606,4856 ,1345,5085,4081 }, - {4209,3777,185 ,6269,1582,1493 }, {2239,4209,185 ,1377,6269,1493 }, - {6279,6289,6288 ,3966,7144,7115 }, {6278,6279,6288 ,3967,3966,7115 }, - {6289,6330,6329 ,7144,7145,7116 }, {6288,6289,6329 ,7115,7144,7116 }, - {6366,6365,6329 ,7146,7117,7116 }, {6330,6366,6329 ,7145,7146,7116 }, - {6366,6402,6401 ,7146,7147,7118 }, {6365,6366,6401 ,7117,7146,7118 }, - {6402,6403,6425 ,7147,7148,7119 }, {6401,6402,6425 ,7118,7147,7119 }, - {6403,6452,6451 ,7148,7149,7120 }, {6425,6403,6451 ,7119,7148,7120 }, - {6452,6497,6496 ,7149,7150,7121 }, {6451,6452,6496 ,7120,7149,7121 }, - {6497,6542,6541 ,7150,7151,7122 }, {6496,6497,6541 ,7121,7150,7122 }, - {6542,6588,6587 ,7151,7152,7123 }, {6541,6542,6587 ,7122,7151,7123 }, - {6588,6589,6607 ,7152,7153,7124 }, {6587,6588,6607 ,7123,7152,7124 }, - {6589,6630,6629 ,7153,7154,7125 }, {6607,6589,6629 ,7124,7153,7125 }, - {6630,6670,6669 ,7154,7155,7126 }, {6629,6630,6669 ,7125,7154,7126 }, - {6727,6726,6669 ,7156,7127,7126 }, {6670,6727,6669 ,7155,7156,7126 }, - {6773,6772,6726 ,7157,7128,7127 }, {6727,6773,6726 ,7156,7157,7127 }, - {6773,6825,6824 ,7157,7158,7129 }, {6772,6773,6824 ,7128,7157,7129 }, - {6825,6826,6850 ,7158,7159,7142 }, {6824,6825,6850 ,7129,7158,7142 }, - {6826,6881,6880 ,7159,7160,7131 }, {6850,6826,6880 ,7142,7159,7131 }, - {6881,6930,6929 ,7160,7161,7136 }, {6880,6881,6929 ,7131,7160,7136 }, - {6930,6978,6977 ,7161,7162,7137 }, {6929,6930,6977 ,7136,7161,7137 }, - {6978,7014,7013 ,7162,7163,7138 }, {6977,6978,7013 ,7137,7162,7138 }, - {7055,7054,7013 ,5485,7143,7138 }, {7014,7055,7013 ,7163,5485,7138 }, - {7055,7053,7054 ,5485,6654,7143 }, {7055,7079,7053 ,5485,5486,6654 }, - {6618,6617,5404 ,6910,6881,3559 }, {2273,2342,350 ,5322,5690,1114 }, - {1273,2273,350 ,6403,5322,1114 }, {5254,6030,5576 ,4042,4037,4039 }, - {1214,5057,6122 ,6573,1795,1797 }, {6101,1214,6122 ,6574,6573,1797 }, - {2272,2454,6386 ,1201,1345,4085 }, {4853,1118,2340 ,6747,6746,6797 }, - {875,2252,6836 ,579,590,852 }, {7053,7079,6150 ,6654,5486,6653 }, - {6366,6403,6402 ,7146,7148,7147 }, {6542,6589,6588 ,7151,7153,7152 }, - {1219,6150,7079 ,4102,6653,5486 }, {6916,6967,6915 ,596,4629,6871 }, - {6841,6840,6812 ,7164,6868,6872 }, {6967,6987,6966 ,4629,6917,6840 }, - {6794,6841,6812 ,6880,7164,6872 }, {6864,6894,6840 ,6865,6887,6868 }, - {6651,6699,6698 ,6785,630,629 }, {6967,6966,6915 ,4629,6840,6871 }, - {5944,1273,5653 ,4754,6403,3644 }, {4465,4483,4444 ,257,549,582 }, - {6392,3929,306 ,6963,5024,6234 }, {3302,6301,6300 ,6757,6147,6146 }, - {4755,2272,1164 ,6770,1201,5087 }, {2454,4856,6386 ,1345,4081,4085 }, - {6682,6679,3978 ,6812,4824,4549 }, {2252,144,6836 ,590,853,852 }, - {6542,6567,6589 ,7151,7165,7153 }, {6773,6826,6825 ,7157,7159,7158 }, - {5838,6355,6079 ,3987,3786,5580 }, {6650,6651,6698 ,3833,6785,629 }, - {7450,7478,7422 ,399,1402,1404 }, {5944,1126,1273 ,4754,7086,6403 }, - {1074,973,8073 ,2715,2017,2567 }, {3929,4166,306 ,5024,6084,6234 }, - {3675,3674,6180 ,4300,4286,6811 }, {2272,6386,1164 ,1201,4085,5087 }, - {6961,6962,7000 ,6813,3260,6814 }, {1220,1271,875 ,4885,588,579 }, - {6280,6290,6289 ,4079,7166,7144 }, {6279,6280,6289 ,3966,4079,7144 }, - {6290,6331,6330 ,7166,7167,7145 }, {6289,6290,6330 ,7144,7166,7145 }, - {6331,6367,6366 ,7167,7168,7146 }, {6330,6331,6366 ,7145,7167,7146 }, - {6367,6404,6403 ,7168,7169,7148 }, {6366,6367,6403 ,7146,7168,7148 }, - {6404,6426,6403 ,7169,7170,7148 }, {6426,6453,6452 ,7170,7171,7149 }, - {6403,6426,6452 ,7148,7170,7149 }, {6498,6497,6452 ,7172,7150,7149 }, - {6453,6498,6452 ,7171,7172,7149 }, {6498,6543,6542 ,7172,7173,7151 }, - {6497,6498,6542 ,7150,7172,7151 }, {6543,6544,6567 ,7173,7174,7165 }, - {6542,6543,6567 ,7151,7173,7165 }, {6544,6590,6589 ,7174,7175,7153 }, - {6567,6544,6589 ,7165,7174,7153 }, {6590,6631,6630 ,7175,7176,7154 }, - {6589,6590,6630 ,7153,7175,7154 }, {6631,6671,6670 ,7176,7177,7155 }, - {6630,6631,6670 ,7154,7176,7155 }, {6728,6727,6670 ,7178,7156,7155 }, - {6671,6728,6670 ,7177,7178,7155 }, {6728,6774,6773 ,7178,7179,7157 }, - {6727,6728,6773 ,7156,7178,7157 }, {6774,6775,6773 ,7179,7180,7157 }, - {6775,6827,6826 ,7180,7181,7159 }, {6773,6775,6826 ,7157,7180,7159 }, - {6827,6882,6881 ,7181,7182,7160 }, {6826,6827,6881 ,7159,7181,7160 }, - {6882,6931,6930 ,7182,7183,7161 }, {6881,6882,6930 ,7160,7182,7161 }, - {6931,6979,6978 ,7183,7184,7162 }, {6930,6931,6978 ,7161,7183,7162 }, - {6979,7015,7014 ,7184,7185,7163 }, {6978,6979,7014 ,7162,7184,7163 }, - {7056,7055,7014 ,7186,5485,7163 }, {7015,7056,7014 ,7185,7186,7163 }, - {1170,1219,7055 ,4103,4102,5485 }, {7056,1170,7055 ,7186,4103,5485 }, - {5499,5704,2621 ,5572,5579,5416 }, {6660,6220,5072 ,6601,6600,6619 }, - {4570,6660,5072 ,4082,6601,6619 }, {1121,5872,1122 ,6564,6557,6719 }, - {6786,6787,6804 ,4139,5269,3342 }, {3834,4570,4044 ,4083,4082,4322 }, - {5426,4807,5010 ,5634,6449,6006 }, {4803,5290,5267 ,4776,5247,4647 }, - {2154,2273,1273 ,5321,5322,6403 }, {1126,2154,1273 ,7086,5321,6403 }, - {5516,5551,6795 ,5420,6260,5418 }, {403,3929,6466 ,5547,5024,6930 }, - {5054,6688,6661 ,6847,6846,6876 }, {2015,2048,1994 ,759,5461,5102 }, - {7001,7025,7024 ,6815,6824,6816 }, {7048,7064,2074 ,6790,5181,6990 }, - {1271,2252,875 ,588,590,579 }, {7064,2073,2074 ,5181,5183,6990 }, - {6498,6544,6543 ,7172,7174,7173 }, {6728,6775,6774 ,7178,7180,7179 }, - {6660,6219,6220 ,6601,6598,6600 }, {4486,3340,3454 ,819,114,710 }, - {6526,6574,6573 ,6784,3885,3733 }, {5738,5688,5689 ,5520,5519,5741 }, - {307,6391,6659 ,5095,6804,3577 }, {3929,6392,6466 ,5024,6963,6930 }, - {5113,5914,5150 ,141,1566,6032 }, {6435,6436,6480 ,2658,3681,1524 }, - {3649,5054,6661 ,6755,6847,6876 }, {7067,2246,7066 ,3585,591,3586 }, - {1197,2384,1987 ,478,6821,3614 }, {4190,1271,1220 ,3439,588,4885 }, - {7007,7006,6966 ,4628,6841,6840 }, {6631,6672,6671 ,7176,7187,7177 }, - {6672,6728,6671 ,7187,7178,7177 }, {6807,6808,6858 ,6764,3465,6740 }, - {6703,6701,6702 ,5161,6766,7060 }, {2337,5578,1124 ,6829,587,5852 }, - {4990,2337,1124 ,7135,6829,5852 }, {1025,2450,6657 ,5299,3444,6828 }, - {6689,2337,4990 ,3065,6829,7135 }, {1608,746,5592 ,4128,2661,3671 }, - {5277,5330,5276 ,6237,3345,1525 }, {7068,3892,6166 ,7019,4970,4971 }, - {6034,6284,5952 ,6049,6113,6595 }, {403,6466,1074 ,5547,6930,2715 }, - {5078,403,1074 ,5261,5547,2715 }, {6807,6858,6838 ,6764,6740,6904 }, - {158,4755,5560 ,5258,6770,6605 }, {5076,158,5560 ,6607,5258,6605 }, - {1213,3261,1197 ,3700,5015,478 }, {1987,4190,1220 ,3614,3439,4885 }, - {1620,6128,6184 ,5982,5981,6089 }, {6281,3526,6290 ,4078,7188,7166 }, - {6280,6281,6290 ,4079,4078,7166 }, {3526,6332,6331 ,7188,7189,7167 }, - {6290,3526,6331 ,7166,7188,7167 }, {6368,6367,6331 ,7190,7168,7167 }, - {6332,6368,6331 ,7189,7190,7167 }, {6368,6405,6404 ,7190,7191,7169 }, - {6367,6368,6404 ,7168,7190,7169 }, {6405,6427,6426 ,7191,7192,7170 }, - {6404,6405,6426 ,7169,7191,7170 }, {6427,6454,6453 ,7192,7193,7171 }, - {6426,6427,6453 ,7170,7192,7171 }, {6454,6499,6498 ,7193,7194,7172 }, - {6453,6454,6498 ,7171,7193,7172 }, {6499,6517,6498 ,7194,7195,7172 }, - {6517,6545,6544 ,7195,7196,7174 }, {6498,6517,6544 ,7172,7195,7174 }, - {6545,6591,6590 ,7196,7197,7175 }, {6544,6545,6590 ,7174,7196,7175 }, - {6632,6631,6590 ,7198,7176,7175 }, {6591,6632,6590 ,7197,7198,7175 }, - {6632,6673,6672 ,7198,7199,7187 }, {6631,6632,6672 ,7176,7198,7187 }, - {6729,6728,6672 ,7200,7178,7187 }, {6673,6729,6672 ,7199,7200,7187 }, - {6729,6776,6775 ,7200,7201,7180 }, {6728,6729,6775 ,7178,7200,7180 }, - {6776,6828,6827 ,7201,7202,7181 }, {6775,6776,6827 ,7180,7201,7181 }, - {6828,6883,6882 ,7202,7203,7182 }, {6827,6828,6882 ,7181,7202,7182 }, - {6883,6932,6931 ,7203,7204,7183 }, {6882,6883,6931 ,7182,7203,7183 }, - {6932,6980,6979 ,7204,7205,7184 }, {6931,6932,6979 ,7183,7204,7184 }, - {6980,7016,7015 ,7205,7206,7185 }, {6979,6980,7015 ,7184,7205,7185 }, - {7057,7056,7015 ,7207,7186,7185 }, {7016,7057,7015 ,7206,7207,7185 }, - {1122,1170,7056 ,6719,4103,7186 }, {7057,1122,7056 ,7207,6719,7186 }, - {1170,1122,1620 ,4103,6719,5982 }, {6655,6703,6654 ,5407,5161,6884 }, - {6430,5733,5353 ,2748,1584,851 }, {4023,5248,4178 ,3827,6469,3825 }, - {6319,6345,5118 ,6789,191,6825 }, {5734,5733,4261 ,6230,1584,1583 }, - {2450,6689,6657 ,3444,3065,6828 }, {2621,3815,5359 ,5416,5556,3713 }, - {7196,7231,7212 ,2401,6944,7208 }, {2160,2154,1126 ,6621,5321,7086 }, - {1272,2160,1126 ,5740,6621,7086 }, {7024,7025,7042 ,6816,6824,5338 }, - {4755,1164,5560 ,6770,5087,6605 }, {2384,4190,1987 ,6821,3439,3614 }, - {7998,7997,7950 ,7209,7210,7211 }, {5514,6158,6293 ,6128,6112,7212 }, - {6703,6702,6654 ,5161,7060,6884 }, {6307,6301,3302 ,5206,6147,6757 }, - {4018,6307,3302 ,5084,5206,6757 }, {6657,6689,4990 ,6828,3065,7135 }, - {6521,3261,6639 ,6819,5015,3699 }, {5496,6521,6564 ,6966,6819,6933 }, - {6521,6639,6508 ,6819,3699,6934 }, {6741,6794,6756 ,5388,6880,6891 }, - {6896,6895,6842 ,6787,5434,5433 }, {5005,5496,4115 ,1301,6966,6909 }, - {6792,6810,6791 ,5056,6908,6900 }, {1075,2409,2373 ,6854,6852,6722 }, - {5057,6951,6796 ,1795,2615,2617 }, {6345,6319,3533 ,191,6789,192 }, - {6521,6508,6564 ,6819,6934,6933 }, {3261,2384,1197 ,5015,6821,478 }, - {5369,1124,5904 ,6609,5852,5851 }, {5566,5605,5604 ,5759,5761,7213 }, - {5140,2621,5359 ,3712,5416,3713 }, {3505,6443,3504 ,6928,6940,2986 }, - {5496,6564,3320 ,6966,6933,5455 }, {5514,5943,7088 ,6128,5402,4299 }, - {7065,7064,7049 ,5182,5181,5454 }, {3261,1213,6639 ,5015,3700,3699 }, - {5496,3320,5058 ,6966,5455,5647 }, {6867,6844,5551 ,6951,6822,6260 }, - {4926,920,3367 ,3897,57,5798 }, {5014,2450,1025 ,6818,3444,5299 }, - {2238,2160,1272 ,2697,6621,5740 }, {6906,6907,6958 ,4294,6002,4314 }, - {876,4232,4208 ,5340,6299,6356 }, {6711,6710,6301 ,3473,6968,6147 }, - {1216,4115,4547 ,6893,6909,242 }, {7387,3303,3322 ,3615,1049,1182 }, - {5552,5593,6867 ,5936,5935,6951 }, {1124,5369,6065 ,5852,6609,6616 }, - {3526,6281,6060 ,7188,4078,5914 }, {6060,6333,6332 ,5914,7214,7189 }, - {3526,6060,6332 ,7188,5914,7189 }, {6369,6368,6332 ,7215,7190,7189 }, - {6333,6369,6332 ,7214,7215,7189 }, {6406,6405,6368 ,7216,7191,7190 }, - {6369,6406,6368 ,7215,7216,7190 }, {6428,6427,6405 ,7217,7192,7191 }, - {6406,6428,6405 ,7216,7217,7191 }, {6455,6454,6427 ,7218,7193,7192 }, - {6428,6455,6427 ,7217,7218,7192 }, {6455,6500,6499 ,7218,7219,7194 }, - {6454,6455,6499 ,7193,7218,7194 }, {6500,6518,6517 ,7219,7220,7195 }, - {6499,6500,6517 ,7194,7219,7195 }, {6518,6546,6545 ,7220,7221,7196 }, - {6517,6518,6545 ,7195,7220,7196 }, {6546,6592,6591 ,7221,7222,7197 }, - {6545,6546,6591 ,7196,7221,7197 }, {6592,6633,6632 ,7222,7223,7198 }, - {6591,6592,6632 ,7197,7222,7198 }, {6633,6674,6673 ,7223,7224,7199 }, - {6632,6633,6673 ,7198,7223,7199 }, {6730,6729,6673 ,7225,7200,7199 }, - {6674,6730,6673 ,7224,7225,7199 }, {6730,6777,6776 ,7225,7226,7201 }, - {6729,6730,6776 ,7200,7225,7201 }, {6777,6829,6828 ,7226,7227,7202 }, - {6776,6777,6828 ,7201,7226,7202 }, {6829,6884,6883 ,7227,7228,7203 }, - {6828,6829,6883 ,7202,7227,7203 }, {6884,6933,6932 ,7228,7229,7204 }, - {6883,6884,6932 ,7203,7228,7204 }, {6933,6981,6980 ,7229,7230,7205 }, - {6932,6933,6980 ,7204,7229,7205 }, {6981,6126,7016 ,7230,7231,7206 }, - {6980,6981,7016 ,7205,7230,7206 }, {6126,6107,7016 ,7231,7232,7206 }, - {7016,6107,7057 ,7206,7232,7207 }, {5693,5714,7028 ,4075,4077,3587 }, - {1122,7057,5936 ,6719,7207,4393 }, {4554,1989,6323 ,1307,3016,1451 }, - {3860,5246,6028 ,6523,5176,5316 }, {4115,5058,4547 ,6909,5647,242 }, - {6319,6318,6304 ,6789,6788,7233 }, {1121,1122,5936 ,6564,6719,4393 }, - {4115,5496,5058 ,6909,6966,5647 }, {1882,1072,4547 ,894,6725,242 }, - {6711,5014,1025 ,3473,6818,5299 }, {6178,3892,4901 ,5881,4970,3596 }, - {6867,5551,5552 ,6951,6260,5936 }, {7370,7396,7343 ,3750,6716,2750 }, - {6307,6711,6301 ,5206,3473,6147 }, {2343,8067,8097 ,2685,2168,1710 }, - {4784,4542,3285 ,3423,6727,3064 }, {4542,6481,4900 ,6727,6729,585 }, - {7197,5112,5111 ,7234,6021,5990 }, {6387,4784,3285 ,6935,3423,3064 }, - {3285,4542,4900 ,3064,6727,585 }, {6633,6645,6674 ,7223,7235,7224 }, - {6995,6107,6126 ,7236,7232,7231 }, {6981,6995,6126 ,7230,7236,7231 }, - {5503,5504,5531 ,7042,5781,5783 }, {5713,4648,5890 ,5776,5722,6591 }, - {2374,2304,1118 ,7037,6756,6746 }, {1027,2374,1118 ,6991,7037,6746 }, - {5838,6318,6355 ,3987,6788,3786 }, {6378,2220,1882 ,6728,6724,894 }, - {6709,1072,1882 ,6726,6725,894 }, {2244,5024,6660 ,6795,6952,6601 }, - {6710,6711,1025 ,6968,3473,5299 }, {1524,1655,4872 ,4326,4089,5522 }, - {6839,6862,6810 ,6867,6877,6908 }, {7650,7699,7649 ,7237,3951,3950 }, - {7595,7594,7568 ,4019,4020,5544 }, {5970,4170,4146 ,3518,3731,3730 }, - {2374,4018,3302 ,7037,5084,6757 }, {4144,5292,5175 ,4761,5093,6384 }, - {1217,2220,6378 ,6399,6724,6728 }, {1072,1216,4547 ,6725,6893,242 }, - {5024,5040,2138 ,6952,5469,6602 }, {5040,3704,2138 ,5469,2332,6602 }, - {5111,7166,7197 ,5990,5971,7234 }, {6234,5845,7255 ,5973,143,7238 }, - {5112,7197,6120 ,6021,7234,142 }, {6281,7089,5115 ,4078,5608,5913 }, - {6829,6851,6884 ,7227,7239,7228 }, {6885,6934,6933 ,7240,7241,7229 }, - {6884,6885,6933 ,7228,7240,7229 }, {6934,6982,6981 ,7241,7242,7230 }, - {6933,6934,6981 ,7229,7241,7230 }, {6981,6982,6995 ,7230,7242,7236 }, - {6982,7017,6107 ,7242,7243,7232 }, {6995,6982,6107 ,7236,7242,7232 }, - {7017,5930,6143 ,7243,6593,4394 }, {6107,7017,6143 ,7232,7243,4394 }, - {7424,7451,7423 ,1188,3963,3749 }, {4835,1218,8118 ,2130,3255,2129 }, - {7650,7649,7595 ,7237,3950,4019 }, {2209,2241,2240 ,7038,6950,4478 }, - {1394,1027,1118 ,6989,6991,6746 }, {2335,1394,1118 ,6737,6989,6746 }, - {4784,2406,4542 ,3423,6736,6727 }, {6814,1217,4542 ,6842,6399,6727 }, - {4530,1554,4209 ,1376,6304,6269 }, {4574,1268,6062 ,6477,4568,6478 }, - {2220,6709,1882 ,6724,6726,894 }, {5166,5473,5120 ,3427,3429,5857 }, - {1655,1524,2432 ,4089,4326,5427 }, {5427,5395,5428 ,5811,5348,5812 }, - {8093,8076,8072 ,2288,617,511 }, {6569,4746,2222 ,6745,5260,2509 }, - {5277,5276,5255 ,6237,1525,3682 }, {6180,3623,3675 ,6811,4206,4300 }, - {6868,6657,5041 ,5554,6828,6799 }, {2304,2374,3302 ,6756,7037,6757 }, - {6318,6356,6355 ,6788,3787,3786 }, {6746,6747,4784 ,6864,6878,3423 }, - {4542,1217,6378 ,6727,6399,6728 }, {7160,7185,7159 ,6271,5073,5075 }, - {7267,7246,7291 ,4331,3772,3774 }, {4552,1309,3371 ,7244,3709,3708 }, - {3265,4552,3371 ,1033,7244,3708 }, {7127,7128,7159 ,7245,5391,5075 }, - {6292,6334,6333 ,5403,7246,7214 }, {6060,6292,6333 ,5914,5403,7214 }, - {6334,6335,6333 ,7246,6534,7214 }, {6370,6369,6333 ,7247,7215,7214 }, - {6335,6370,6333 ,6534,7247,7214 }, {6407,6406,6369 ,7248,7216,7215 }, - {6370,6407,6369 ,7247,7248,7215 }, {6429,6428,6406 ,7249,7217,7216 }, - {6407,6429,6406 ,7248,7249,7216 }, {6429,6456,6455 ,7249,7250,7218 }, - {6428,6429,6455 ,7217,7249,7218 }, {6456,6501,6500 ,7250,7251,7219 }, - {6455,6456,6500 ,7218,7250,7219 }, {6519,6518,6500 ,7252,7220,7219 }, - {6501,6519,6500 ,7251,7252,7219 }, {6547,6546,6518 ,7253,7221,7220 }, - {6519,6547,6518 ,7252,7253,7220 }, {6547,6593,6592 ,7253,7254,7222 }, - {6546,6547,6592 ,7221,7253,7222 }, {6593,6634,6633 ,7254,7255,7223 }, - {6592,6593,6633 ,7222,7254,7223 }, {6633,6634,6645 ,7223,7255,7235 }, - {6634,6675,6674 ,7255,7256,7224 }, {6645,6634,6674 ,7235,7255,7224 }, - {6675,6731,6730 ,7256,7257,7225 }, {6674,6675,6730 ,7224,7256,7225 }, - {6731,6778,6777 ,7257,7258,7226 }, {6730,6731,6777 ,7225,7257,7226 }, - {6778,6830,6829 ,7258,7259,7227 }, {6777,6778,6829 ,7226,7258,7227 }, - {6829,6830,6851 ,7227,7259,7239 }, {6830,6885,6884 ,7259,7240,7228 }, - {6851,6830,6884 ,7239,7259,7228 }, {6935,6934,6885 ,7260,7241,7240 }, - {6934,6935,6982 ,7241,7260,7242 }, {1664,6228,751 ,2268,1366,1365 }, - {7047,2335,7046 ,6853,6737,6735 }, {5053,1025,6389 ,6848,5299,5301 }, - {6714,6657,6868 ,5300,6828,5554 }, {3872,5082,1551 ,4811,5218,5275 }, - {2406,6814,4542 ,6736,6842,6727 }, {6312,6747,6746 ,6749,6878,6864 }, - {3510,75,2837 ,6267,3962,2511 }, {2837,1518,3510 ,2511,3978,6267 }, - {6114,6355,5151 ,1805,3786,1803 }, {5085,1947,1881 ,3675,6723,7066 }, - {5041,6065,5687 ,6799,6616,6420 }, {3336,3309,6285 ,3922,3892,3923 }, - {4831,3340,4486 ,3154,114,819 }, {6318,5838,6303 ,6788,3987,7261 }, - {6303,6304,6318 ,7261,7233,6788 }, {7116,7115,7089 ,7262,1024,5608 }, - {6432,6553,3265 ,1032,780,1033 }, {5914,5170,5150 ,1566,3683,6032 }, - {7149,7173,7165 ,7263,7264,7265 }, {6292,6335,6334 ,5403,6534,7246 }, - {6371,6370,6335 ,7266,7247,6534 }, {6370,6371,6407 ,7247,7266,7248 }, - {6593,6608,6634 ,7254,7267,7255 }, {6688,5102,5101 ,6846,6845,6422 }, - {5491,6795,6741 ,5419,5418,5388 }, {1380,6312,6310 ,6721,6749,3918 }, - {6747,2406,4784 ,6878,6736,3423 }, {6310,6312,6746 ,3918,6749,6864 }, - {1126,5944,6037 ,7086,4754,5235 }, {4574,3079,5301 ,6477,6540,2698 }, - {209,648,4892 ,4499,4679,5386 }, {3079,3631,5301 ,6540,5922,2698 }, - {4233,1026,1072 ,6185,6218,6725 }, {5102,5041,5007 ,6845,6799,7268 }, - {5007,5041,5687 ,7268,6799,6420 }, {7047,1394,2335 ,6853,6989,6737 }, - {5479,5491,6741 ,5387,5419,5388 }, {6795,6794,6741 ,5418,6880,5388 }, - {255,1380,256 ,6731,6721,7269 }, {3277,2567,227 ,961,2842,2157 }, - {6083,6303,5838 ,3989,7261,3987 }, {7797,7875,7848 ,3506,4118,343 }, - {6620,3265,6553 ,7270,1033,780 }, {1309,4552,3935 ,3709,7244,4346 }, - {6079,6343,5838 ,5580,5122,3987 }, {6895,6916,6864 ,5434,596,6865 }, - {6962,6963,7001 ,3260,1048,6815 }, {1170,6184,5627 ,4103,6089,6628 }, - {4588,4587,8020 ,775,774,1405 }, {6292,6306,6335 ,5403,5669,6534 }, - {6608,6635,6634 ,7267,7271,7255 }, {6676,6675,6634 ,7272,7256,7255 }, - {6635,6676,6634 ,7271,7272,7255 }, {6676,6732,6731 ,7272,7273,7257 }, - {6675,6676,6731 ,7256,7272,7257 }, {6732,6779,6778 ,7273,7274,7258 }, - {6731,6732,6778 ,7257,7273,7258 }, {6779,6800,6778 ,7274,7275,7258 }, - {6800,6831,6830 ,7275,7276,7259 }, {6778,6800,6830 ,7258,7275,7259 }, - {6831,6886,6885 ,7276,7277,7240 }, {6830,6831,6885 ,7259,7276,7240 }, - {6936,6935,6885 ,7278,7260,7240 }, {6886,6936,6885 ,7277,7278,7240 }, - {6936,6983,6982 ,7278,7279,7242 }, {6935,6936,6982 ,7260,7278,7242 }, - {6983,7018,7017 ,7279,7280,7243 }, {6982,6983,7017 ,7242,7279,7243 }, - {7058,5930,7017 ,6594,6593,7243 }, {7018,7058,7017 ,7280,6594,7243 }, - {1120,1167,876 ,5339,5341,5340 }, {3498,6005,1171 ,5800,5691,6441 }, - {5101,5007,5687 ,6422,7268,6420 }, {2240,2241,3978 ,4478,6950,4549 }, - {6795,6813,6794 ,5418,6916,6880 }, {3281,1380,6310 ,6794,6721,3918 }, - {6688,5101,5048 ,6846,6422,6421 }, {352,255,256 ,593,6731,7269 }, - {353,352,256 ,7281,593,7269 }, {6842,6841,6794 ,5433,7164,6880 }, - {6813,6842,6794 ,6916,5433,6880 }, {5101,5102,5007 ,6422,6845,7268 }, - {6865,6840,6841 ,5435,6868,7164 }, {6842,6865,6841 ,5433,5435,7164 }, - {7500,4595,6345 ,326,5289,191 }, {7271,7320,7270 ,2554,7282,2555 }, - {7898,7874,7848 ,4117,344,343 }, {6314,6553,6646 ,1132,780,1133 }, - {7539,7515,7538 ,1080,1079,2014 }, {5051,6620,6553 ,7283,7270,780 }, - {6314,5051,6553 ,1132,7283,780 }, {5051,3265,6620 ,7283,1033,7270 }, - {6798,4552,3265 ,7284,7244,1033 }, {5051,6798,3265 ,7283,7284,1033 }, - {5010,4807,4552 ,6006,6449,7244 }, {6798,5010,4552 ,7284,6006,7244 }, - {5943,6306,6292 ,5402,5669,5403 }, {2247,2452,353 ,7285,7091,7281 }, - {1380,3281,256 ,6721,6794,7269 }, {4588,8020,7995 ,775,1405,4135 }, - {6048,6049,1214 ,6552,6570,6573 }, {3820,1218,4835 ,5591,3255,2130 }, - {6372,6371,6335 ,7286,7266,6534 }, {6336,6372,6335 ,5670,7286,6534 }, - {6372,6408,6407 ,7286,7287,7248 }, {6371,6372,6407 ,7266,7286,7248 }, - {6408,6429,6407 ,7287,7249,7248 }, {6457,6456,6429 ,7288,7250,7249 }, - {6408,6457,6429 ,7287,7288,7249 }, {6502,6501,6456 ,7289,7251,7250 }, - {6457,6502,6456 ,7288,7289,7250 }, {6520,6519,6501 ,7290,7252,7251 }, - {6502,6520,6501 ,7289,7290,7251 }, {6520,6548,6547 ,7290,7291,7253 }, - {6519,6520,6547 ,7252,7290,7253 }, {6548,6594,6593 ,7291,7292,7254 }, - {6547,6548,6593 ,7253,7291,7254 }, {6609,6608,6593 ,7293,7267,7254 }, - {6594,6609,6593 ,7292,7293,7254 }, {6609,6635,6608 ,7293,7271,7267 }, - {7058,5929,5979 ,6594,6029,6028 }, {5359,5977,1936 ,3713,809,848 }, - {6914,6965,6945 ,3532,6732,2632 }, {7066,2452,7065 ,3586,7091,5182 }, - {6895,6864,6840 ,5434,6865,6868 }, {5404,6576,6529 ,3559,6883,5025 }, - {5350,5404,6529 ,1526,3559,5025 }, {5205,5166,5190 ,5219,3427,5858 }, - {6855,6856,6893 ,6959,6892,6844 }, {2308,3302,6300 ,6798,6757,6146 }, - {6297,6442,6696 ,6851,6850,6849 }, {6865,6895,6840 ,5435,5434,6868 }, - {6916,6915,6864 ,596,6871,6865 }, {7028,7066,7027 ,3587,3586,3175 }, - {1492,5075,4017 ,1585,1586,5342 }, {876,4017,4232 ,5340,5342,6299 }, - {7875,7898,7848 ,4118,4117,343 }, {7925,7924,7874 ,7294,815,344 }, - {7023,6314,2259 ,7295,1132,1131 }, {5645,6798,5051 ,7296,7284,7283 }, - {2140,5010,6798 ,5635,6006,7284 }, {5645,2140,6798 ,7296,5635,7284 }, - {7846,7897,7845 ,4162,6015,5725 }, {2452,352,353 ,7091,593,7281 }, - {6114,6079,6355 ,1805,5580,3786 }, {5248,4023,5247 ,6469,3827,4096 }, - {6502,6548,6520 ,7289,7291,7290 }, {6594,6635,6609 ,7292,7271,7293 }, - {2452,2247,7065 ,7091,7285,5182 }, {7028,7027,6968 ,3587,3175,595 }, - {5917,4541,3079 ,6402,6630,6540 }, {1283,3844,5788 ,2978,2443,6086 }, - {6464,297,5966 ,3633,1918,5054 }, {1071,2455,1027 ,3382,475,6991 }, - {6442,6708,6696 ,6850,6148,6849 }, {6320,3533,7439 ,190,192,7297 }, - {7925,7974,7924 ,7294,4171,815 }, {3426,2566,2527 ,4438,2635,2637 }, - {7023,5051,6314 ,7295,7283,1132 }, {5892,6036,3498 ,2683,6141,5800 }, - {4170,5970,5447 ,3731,3518,5633 }, {6947,6988,6946 ,4587,6771,594 }, - {7066,7065,7027 ,3586,5182,3175 }, {5363,5396,5383 ,3269,3548,3547 }, - {6458,6457,6408 ,7298,7288,7287 }, {6503,6502,6457 ,7299,7289,7288 }, - {6458,6503,6457 ,7298,7299,7288 }, {6549,6548,6502 ,7300,7291,7289 }, - {6503,6549,6502 ,7299,7300,7289 }, {6595,6594,6548 ,7301,7292,7291 }, - {6549,6595,6548 ,7300,7301,7291 }, {6636,6635,6594 ,7302,7271,7292 }, - {6595,6636,6594 ,7301,7302,7292 }, {6677,6676,6635 ,7303,7272,7271 }, - {6636,6677,6635 ,7302,7303,7271 }, {6733,6732,6676 ,7304,7273,7272 }, - {6677,6733,6676 ,7303,7304,7272 }, {6780,6779,6732 ,7305,7274,7273 }, - {6733,6780,6732 ,7304,7305,7273 }, {6801,6800,6779 ,7306,7275,7274 }, - {6780,6801,6779 ,7305,7306,7274 }, {6801,6832,6831 ,7306,7307,7276 }, - {6800,6801,6831 ,7275,7306,7276 }, {6887,6886,6831 ,7308,7277,7276 }, - {6832,6887,6831 ,7307,7308,7276 }, {6937,6936,6886 ,7309,7278,7277 }, - {6887,6937,6886 ,7308,7309,7277 }, {6937,6984,6983 ,7309,7310,7279 }, - {6936,6937,6983 ,7278,7309,7279 }, {7019,7018,6983 ,7311,7280,7279 }, - {6984,7019,6983 ,7310,7311,7279 }, {7019,7059,7058 ,7311,7312,6594 }, - {7018,7019,7058 ,7280,7311,6594 }, {7059,5045,5929 ,7312,4214,6029 }, - {7058,7059,5929 ,6594,7312,6029 }, {6967,7007,6987 ,4629,4628,6917 }, - {6988,7028,6968 ,6771,3587,595 }, {6920,6947,6919 ,6783,4587,6778 }, - {6863,6862,6839 ,6866,6877,6867 }, {6169,6190,3704 ,2334,6610,2332 }, - {5476,5055,1261 ,6495,6470,6472 }, {4595,4649,5118 ,5289,5288,6825 }, - {6920,6919,6896 ,6783,6778,6787 }, {4597,4583,7788 ,5541,5498,7313 }, - {7534,7533,7511 ,7314,7039,6677 }, {7115,7102,7103 ,1024,1240,6455 }, - {4553,2259,6351 ,7315,1131,1568 }, {6322,4553,6351 ,3531,7315,1568 }, - {4553,6296,2259 ,7315,7316,1131 }, {6298,7023,2259 ,7317,7295,1131 }, - {6296,6298,2259 ,7316,7317,1131 }, {5100,5051,7023 ,7318,7283,7295 }, - {6298,5100,7023 ,7317,7318,7295 }, {5100,7078,5051 ,7318,7319,7283 }, - {1262,5645,5051 ,7320,7296,7283 }, {7078,1262,5051 ,7319,7320,7283 }, - {4565,2140,5645 ,5944,5635,7296 }, {1262,4565,5645 ,7320,5944,7296 }, - {6463,4566,4985 ,5159,5143,2703 }, {6009,6337,6336 ,6129,7321,5670 }, - {5943,6009,6336 ,5402,6129,5670 }, {6337,6373,6372 ,7321,7322,7286 }, - {6336,6337,6372 ,5670,7321,7286 }, {6373,6409,6408 ,7322,7323,7287 }, - {6372,6373,6408 ,7286,7322,7287 }, {6459,6458,6408 ,7324,7298,7287 }, - {6409,6459,6408 ,7323,7324,7287 }, {6550,6549,6503 ,7325,7300,7299 }, - {6637,6636,6595 ,7326,7302,7301 }, {6780,6832,6801 ,7305,7307,7306 }, - {6988,6968,6946 ,6771,595,594 }, {6947,6946,6918 ,4587,594,6779 }, - {6792,6839,6811 ,5056,6867,399 }, {6156,1165,5917 ,6250,3153,6402 }, - {2241,5454,3978 ,6950,6817,4549 }, {2234,2235,2139 ,6949,6870,6895 }, - {3192,6387,3285 ,3063,6935,3064 }, {1313,1075,2373 ,3674,6854,6722 }, - {8037,8036,7996 ,4528,7327,7328 }, {5115,7103,6282 ,5913,6455,6617 }, - {41,1262,7078 ,6533,7320,7319 }, {5100,41,7078 ,7318,6533,7319 }, - {6029,6028,5976 ,1454,5316,5903 }, {1270,6569,2222 ,6781,6745,2509 }, - {6867,6896,6844 ,6951,6787,6822 }, {3838,4166,5999 ,6257,6084,5184 }, - {6637,6677,6636 ,7326,7303,7302 }, {6780,6801,6832 ,7305,7306,7307 }, - {7059,7072,5045 ,7312,7329,4214 }, {4577,4576,4575 ,5352,1628,3128 }, - {6792,6811,6793 ,5056,399,6879 }, {5447,4565,5564 ,5633,5944,5943 }, - {6947,6918,6919 ,4587,6779,6778 }, {3437,3500,3465 ,4142,4177,6890 }, - {3500,3551,3550 ,4177,4180,4179 }, {3670,4326,489 ,281,219,3639 }, - {3051,2273,5474 ,6488,5322,4243 }, {5415,4565,41 ,5951,5944,6533 }, - {6866,6842,6843 ,6786,5433,6911 }, {7818,4584,4598 ,5309,5308,5343 }, - {7187,7186,7161 ,7330,5370,2559 }, {1634,5482,1423 ,768,3565,151 }, - {4553,6298,6296 ,7315,7317,7316 }, {1262,41,4565 ,7320,6533,5944 }, - {6391,307,6463 ,6804,5095,5159 }, {2247,353,1071 ,7285,7281,3382 }, - {1215,5006,184 ,3937,1351,4052 }, {6293,6338,6337 ,7212,7331,7321 }, - {6009,6293,6337 ,6129,7212,7321 }, {6338,6374,6373 ,7331,7332,7322 }, - {6337,6338,6373 ,7321,7331,7322 }, {6410,6409,6373 ,7333,7323,7322 }, - {6374,6410,6373 ,7332,7333,7322 }, {6460,6459,6409 ,7334,7324,7323 }, - {6410,6460,6409 ,7333,7334,7323 }, {6460,6458,6459 ,7334,7298,7324 }, - {6504,6503,6458 ,7335,7299,7298 }, {6460,6504,6458 ,7334,7335,7298 }, - {6551,6550,6503 ,7336,7325,7299 }, {6504,6551,6503 ,7335,7336,7299 }, - {6551,6549,6550 ,7336,7300,7325 }, {6596,6595,6549 ,7337,7301,7300 }, - {6551,6596,6549 ,7336,7337,7300 }, {6638,6637,6595 ,7338,7326,7301 }, - {6596,6638,6595 ,7337,7338,7301 }, {6678,6677,6637 ,7339,7303,7326 }, - {6638,6678,6637 ,7338,7339,7326 }, {6678,6693,6677 ,7339,7340,7303 }, - {6734,6733,6677 ,7341,7304,7303 }, {6693,6734,6677 ,7340,7341,7303 }, - {6781,6780,6733 ,7342,7305,7304 }, {6734,6781,6733 ,7341,7342,7304 }, - {6781,6801,6780 ,7342,7306,7305 }, {6833,6832,6801 ,7343,7307,7306 }, - {6781,6833,6801 ,7342,7343,7306 }, {6888,6887,6832 ,7344,7308,7307 }, - {6833,6888,6832 ,7343,7344,7307 }, {6888,6938,6937 ,7344,7345,7309 }, - {6887,6888,6937 ,7308,7344,7309 }, {6938,6985,6984 ,7345,7346,7310 }, - {6937,6938,6984 ,7309,7345,7310 }, {7020,7019,6984 ,7347,7311,7310 }, - {6985,7020,6984 ,7346,7347,7310 }, {7020,7060,7059 ,7347,7348,7312 }, - {7019,7020,7059 ,7311,7347,7312 }, {7060,7061,7072 ,7348,7141,7329 }, - {7059,7060,7072 ,7312,7348,7329 }, {7072,7061,5045 ,7329,7141,4214 }, - {5672,5744,7009 ,5324,6090,5325 }, {2074,2247,1071 ,6990,7285,3382 }, - {1071,353,3661 ,3382,7281,789 }, {1165,4541,5917 ,3153,6630,6402 }, - {1165,3446,5487 ,3153,3718,6558 }, {4541,1165,5487 ,6630,3153,6558 }, - {6319,5125,5151 ,6789,5467,1803 }, {4597,5136,4630 ,5541,1817,827 }, - {4598,4631,5137 ,5343,1816,1815 }, {7705,7754,7704 ,7349,7350,3562 }, - {3352,7438,3321 ,2724,7351,2461 }, {2249,6352,6322 ,3751,7352,3531 }, - {3176,4553,6322 ,7353,7315,3531 }, {6352,3176,6322 ,7352,7353,3531 }, - {6376,6298,4553 ,7354,7317,7315 }, {3176,6376,4553 ,7353,7354,7315 }, - {6438,5100,6298 ,7355,7318,7317 }, {6376,6438,6298 ,7354,7355,7317 }, - {5037,41,5100 ,3206,6533,7318 }, {6438,5037,5100 ,7355,3206,7318 }, - {1809,4549,6605 ,878,877,5164 }, {6505,6504,6460 ,7356,7335,7334 }, - {6597,6596,6551 ,7357,7337,7336 }, {7020,7061,7060 ,7347,7141,7348 }, - {5211,5170,5191 ,6071,3683,2900 }, {2452,2246,352 ,7091,591,593 }, - {6919,6946,6918 ,6778,594,6779 }, {351,5086,5069 ,592,6064,6730 }, - {6969,1165,6156 ,7358,3153,6250 }, {5001,2271,4018 ,477,5071,5084 }, - {3336,6324,3335 ,3922,4239,4222 }, {4445,6757,6748 ,3648,3409,3408 }, - {2220,4233,1072 ,6724,6185,6725 }, {7818,4598,5137 ,5309,5343,1815 }, - {7204,7203,7186 ,5120,4366,5370 }, {535,5105,6686 ,2001,5456,750 }, - {7227,7247,7269 ,7359,7360,7361 }, {6956,6376,3176 ,7362,7354,7353 }, - {5137,4597,7788 ,1815,5541,7313 }, {4142,5085,1881 ,6839,3675,7066 }, - {7065,2247,2073 ,5182,7285,5183 }, {7027,7065,7049 ,3175,5182,5454 }, - {6505,6551,6504 ,7356,7336,7335 }, {6597,6638,6596 ,7357,7338,7337 }, - {6678,6734,6693 ,7339,7341,7340 }, {6834,6833,6781 ,7363,7343,7342 }, - {7020,7037,7061 ,7347,7364,7141 }, {5751,7038,5812 ,5878,3832,660 }, - {6949,6947,6948 ,4585,4587,6782 }, {5059,4233,7081 ,1253,6185,7114 }, - {1125,3779,2142 ,5723,6565,4936 }, {4068,3952,3259 ,408,5597,407 }, - {7998,8021,7997 ,7209,7365,7210 }, {5910,6343,6079 ,5121,5122,5580 }, - {6710,1025,5053 ,6968,5299,6848 }, {3281,6310,6309 ,6794,3918,3690 }, - {6481,6378,4900 ,6729,6728,585 }, {7818,5137,7788 ,5309,1815,7313 }, - {7187,7204,7186 ,7330,5120,5370 }, {7908,7907,7858 ,7366,7367,7368 }, - {227,8128,3277 ,2157,959,961 }, {6956,6438,6376 ,7362,7355,7354 }, - {6158,6034,6293 ,6112,6049,7212 }, {6034,6339,6338 ,6049,6048,7331 }, - {6293,6034,6338 ,7212,6049,7331 }, {6339,6375,6374 ,6048,7369,7332 }, - {6338,6339,6374 ,7331,6048,7332 }, {6411,6410,6374 ,7370,7333,7332 }, - {6375,6411,6374 ,7369,7370,7332 }, {6461,6460,6410 ,7371,7334,7333 }, - {6411,6461,6410 ,7370,7371,7333 }, {6506,6505,6460 ,3549,7356,7334 }, - {6461,6506,6460 ,7371,3549,7334 }, {5395,6551,6505 ,5348,7336,7356 }, - {6506,5395,6505 ,3549,5348,7356 }, {5395,6568,6551 ,5348,6717,7336 }, - {5427,6597,6551 ,5811,7357,7336 }, {6568,5427,6551 ,6717,5811,7336 }, - {5463,6638,6597 ,5801,7338,7357 }, {5427,5463,6597 ,5811,5801,7357 }, - {5463,5503,6638 ,5801,7042,7338 }, {5531,6678,6638 ,5783,7339,7338 }, - {5503,5531,6638 ,7042,5783,7338 }, {5566,6734,6678 ,5759,7341,7339 }, - {5531,5566,6678 ,5783,5759,7339 }, {6782,6781,6734 ,7372,7342,7341 }, - {5566,6782,6734 ,5759,7372,7341 }, {6835,6834,6781 ,7373,7363,7342 }, - {6782,6835,6781 ,7372,7373,7342 }, {6835,6833,6834 ,7373,7343,7363 }, - {6889,6888,6833 ,7374,7344,7343 }, {6835,6889,6833 ,7373,7374,7343 }, - {6889,6904,6888 ,7374,7375,7344 }, {6939,6938,6888 ,7376,7345,7344 }, - {6904,6939,6888 ,7375,7376,7344 }, {6939,6940,6938 ,7376,7377,7345 }, - {6986,6985,6938 ,7378,7346,7345 }, {6940,6986,6938 ,7377,7378,7345 }, - {6986,7021,7020 ,7378,7379,7347 }, {6985,6986,7020 ,7346,7378,7347 }, - {7020,7021,7037 ,7347,7379,7364 }, {7021,7062,7061 ,7379,6563,7141 }, - {7037,7021,7061 ,7364,7379,7141 }, {4752,1297,4323 ,5196,4048,4045 }, - {712,3702,6757 ,3650,1303,3409 }, {4010,4009,3968 ,4343,3870,3848 }, - {3529,1941,3371 ,3837,1034,3708 }, {6155,6970,6969 ,3726,3688,7358 }, - {6156,6155,6969 ,6250,3726,7358 }, {6970,4831,1165 ,3688,3154,3153 }, - {6969,6970,1165 ,7358,3688,3153 }, {978,4965,4966 ,1620,784,4646 }, - {1217,5059,2220 ,6399,1253,6724 }, {3281,2271,2212 ,6794,5071,476 }, - {2073,2247,2074 ,5183,7285,6990 }, {5027,6684,2272 ,5516,1161,1201 }, - {5136,4597,5137 ,1817,5541,1815 }, {4803,468,505 ,4776,1369,986 }, - {7103,7102,6283 ,6455,1240,4298 }, {6323,2249,6531 ,1451,3751,2280 }, - {4551,6352,2249 ,7380,7352,3751 }, {6323,4551,2249 ,1451,7380,3751 }, - {1989,3176,6352 ,3016,7353,7352 }, {4551,1989,6352 ,7380,3016,7352 }, - {5012,6956,3176 ,5212,7362,7353 }, {1989,5012,3176 ,3016,5212,7353 }, - {6417,6438,6956 ,762,7355,7362 }, {5012,6417,6956 ,5212,762,7362 }, - {6417,5388,5037 ,762,5428,3206 }, {6438,6417,5037 ,7355,762,3206 }, - {5037,5388,6317 ,3206,5428,3207 }, {353,256,2212 ,7281,7269,476 }, - {5211,6433,5210 ,6071,6691,6033 }, {6165,7061,7062 ,4219,7141,6563 }, - {5604,6782,5566 ,7213,7372,5759 }, {6940,6939,6904 ,7377,7376,7375 }, - {6889,6940,6904 ,7374,7377,7375 }, {835,3984,5527 ,3030,6017,5977 }, - {4545,2274,4980 ,586,893,241 }, {3661,353,2212 ,789,7281,476 }, - {256,3281,2212 ,7269,6794,476 }, {6465,5009,4985 ,6927,6803,2703 }, - {6968,7008,6967 ,595,3176,4629 }, {5940,6176,6715 ,1625,1624,6929 }, - {3321,3353,3352 ,2461,2424,2724 }, {2074,1394,7047 ,6990,6989,6853 }, - {5974,3079,4574 ,6474,6540,6477 }, {7660,7707,7659 ,7381,7382,7383 }, - {6792,6791,6738 ,5056,6900,5057 }, {6009,5514,6293 ,6129,6128,7212 }, - {5744,5718,5756 ,6090,6126,6076 }, {6531,6509,6323 ,2280,1452,1451 }, - {4586,2163,911 ,5257,243,1159 }, {6946,6916,6917 ,594,596,5515 }, - {6411,6462,6461 ,7370,7384,7371 }, {6462,6506,6461 ,7384,3549,7371 }, - {5715,7050,7028 ,4076,6967,3587 }, {3779,1125,4648 ,6565,5723,5722 }, - {2871,3284,257 ,3649,6720,6067 }, {5106,5120,7096 ,5926,5857,4337 }, - {4392,2094,2341 ,844,5570,845 }, {5041,4990,6065 ,6799,7135,6616 }, - {4803,5267,4777 ,4776,4647,3917 }, {5604,6835,6782 ,7213,7373,7372 }, - {5669,6905,5710 ,5553,2506,5254 }, {2343,2210,1074 ,2685,2510,2715 }, - {158,5027,4755 ,5258,5516,6770 }, {8007,2250,7957 ,3854,3716,7385 }, - {7050,5715,5755 ,6967,4076,4047 }, {1947,2235,1881 ,6723,6870,7066 }, - {5405,6656,6618 ,5518,5517,6910 }, {4552,4807,3935 ,7244,6449,4346 }, - {6657,4990,5041 ,6828,7135,6799 }, {4043,2956,2798 ,419,399,420 }, - {5611,6949,6920 ,6177,4585,6783 }, {7262,7287,7241 ,4335,3459,4465 }, - {7769,7744,7794 ,5535,6101,5749 }, {4021,3733,3936 ,1712,5610,5599 }, - {6323,1989,4551 ,1451,3016,7380 }, {4983,4569,2158 ,480,1344,1160 }, - {6949,6948,6920 ,4585,6782,6783 }, {6918,6946,6917 ,6779,594,5515 }, - {4229,381,4246 ,4877,6450,3665 }, {6092,5940,3714 ,1623,1625,3970 }, - {6483,2164,4961 ,3584,3583,2905 }, {5889,6066,6375 ,1587,1455,7369 }, - {6339,5889,6375 ,6048,1587,7369 }, {6090,6411,6375 ,4897,7370,7369 }, - {6066,6090,6375 ,1455,4897,7369 }, {6090,6260,6462 ,4897,3397,7384 }, - {6411,6090,6462 ,7370,4897,7384 }, {5363,6506,6462 ,3269,3549,7384 }, - {6260,5363,6462 ,3397,3269,7384 }, {4530,4209,2239 ,1376,6269,1377 }, - {5120,5106,5121 ,5857,5926,5855 }, {5605,5647,5604 ,5761,5333,7213 }, - {5647,6835,5604 ,5333,7373,7213 }, {5647,6852,6835 ,5333,5335,7373 }, - {6890,6889,6835 ,5334,7374,7373 }, {6852,6890,6835 ,5335,5334,7373 }, - {5750,6940,6889 ,5156,7377,7374 }, {6890,5750,6889 ,5334,5156,7374 }, - {5774,6986,6940 ,5720,7378,7377 }, {5750,5774,6940 ,5156,5720,7377 }, - {5807,7021,6986 ,4769,7379,7378 }, {5774,5807,6986 ,5720,4769,7378 }, - {7021,5807,7062 ,7379,4769,6563 }, {5807,5839,7062 ,4769,4768,6563 }, - {5839,5840,7062 ,4768,4770,6563 }, {5840,4271,1464 ,4770,5767,5768 }, - {7062,5840,1464 ,6563,4770,5768 }, {5668,5626,5669 ,5912,1460,5553 }, - {5076,4200,158 ,6607,6583,5258 }, {4983,6341,2808 ,480,479,522 }, - {7050,5755,7067 ,6967,4047,3585 }, {4978,6413,6155 ,178,112,3726 }, - {4567,4978,6155 ,6655,178,3726 }, {6413,3340,6970 ,112,114,3688 }, - {7733,7758,7707 ,7386,7387,7382 }, {7708,7733,7707 ,7388,7386,7382 }, - {7733,7780,7758 ,7386,7389,7387 }, {7834,7884,7833 ,7390,7391,7392 }, - {7523,7581,7522 ,7393,7394,7395 }, {7518,7546,7545 ,7396,7397,7398 }, - {7629,7684,7683 ,7399,7400,5267 }, {7656,7629,7683 ,7401,7399,5267 }, - {2328,2282,2283 ,3664,3663,3687 }, {2281,2280,8054 ,3626,3613,7402 }, - {2278,2277,4572 ,3495,3522,5949 }, {7956,8004,7981 ,7403,7404,4252 }, - {7173,7172,7165 ,7264,7405,7265 }, {7574,7656,7603 ,7406,7401,5266 }, - {2250,8006,7957 ,3716,7407,7385 }, {7907,7932,7906 ,7367,7408,7409 }, - {4803,505,5290 ,4776,986,5247 }, {7827,7878,7826 ,7410,7411,1616 }, - {7852,7901,7851 ,7412,7413,6625 }, {7116,7141,7140 ,7262,7414,7415 }, - {7263,7312,7262 ,627,4333,4335 }, {402,7690,448 ,5349,6466,1172 }, - {7540,7574,7603 ,5709,7406,5266 }, {7629,7656,7574 ,7399,7401,7406 }, - {8021,8037,7997 ,7365,4528,7210 }, {7604,7605,7657 ,7416,5239,7417 }, - {7726,7774,7725 ,7418,7419,7420 }, {3351,7497,7438 ,1444,7421,7351 }, - {3352,3351,7438 ,2724,1444,7351 }, {7297,7273,7274 ,7422,3467,7423 }, - {3298,3830,4518 ,7424,5882,1281 }, {7678,7677,7623 ,7425,7426,7427 }, - {7399,7425,7398 ,7428,3995,7429 }, {7379,7378,7319 ,7430,7431,7432 }, - {7252,7297,7274 ,3437,7422,7423 }, {7327,7326,7273 ,7433,7434,3467 }, - {7297,7327,7273 ,7422,7433,3467 }, {7327,7355,7326 ,7433,7435,7434 }, - {7403,7457,7486 ,6588,1078,7436 }, {7457,7539,7538 ,1078,1080,2014 }, - {7856,7905,7855 ,4635,7437,6545 }, {7626,7625,7624 ,7438,7139,7439 }, - {7405,7461,7460 ,7440,7441,7442 }, {7431,7405,7460 ,7443,7440,7442 }, - {7461,7489,7460 ,7441,7444,7442 }, {7489,7519,7518 ,7444,7445,7396 }, - {7460,7489,7518 ,7442,7444,7396 }, {7757,7758,7779 ,7446,7387,7447 }, - {7833,7832,7809 ,7392,7448,7449 }, {7707,7758,7757 ,7382,7387,7446 }, - {7732,7707,7757 ,7450,7382,7446 }, {4022,3534,6836 ,3503,942,852 }, - {7320,7380,7379 ,7282,7451,7430 }, {7833,7883,7832 ,7392,7452,7448 }, - {7519,7547,7546 ,7445,7453,7397 }, {7518,7519,7546 ,7396,7445,7397 }, - {7771,7770,7721 ,4336,4272,7454 }, {7547,7607,7546 ,7453,7455,7397 }, - {7661,7660,7607 ,7456,7381,7455 }, {7606,7659,7633 ,7457,7383,7458 }, - {7726,7725,7681 ,7418,7420,7459 }, {7622,7676,7650 ,7460,7461,7237 }, - {7606,7633,7578 ,7457,7458,7462 }, {7661,7708,7660 ,7456,7388,7381 }, - {7480,7511,7454 ,7463,6677,3996 }, {7781,7780,7733 ,7464,7389,7386 }, - {7325,7382,7381 ,7465,7466,7467 }, {7885,7884,7834 ,7468,7391,7390 }, - {6322,8112,6897 ,3531,3530,54 }, {2048,2096,3875 ,5461,788,5845 }, - {7885,7909,7884 ,7468,6603,7391 }, {2262,7936,7884 ,3359,5528,7391 }, - {7909,2262,7884 ,6603,3359,7391 }, {7382,7405,7431 ,7466,7440,7443 }, - {7582,7581,7523 ,7469,7394,7393 }, {7438,7497,7437 ,7351,7421,5938 }, - {7273,7250,7251 ,3467,3418,2671 }, {7497,7496,7437 ,7421,7470,5938 }, - {2526,2525,2485 ,2636,2937,2815 }, {7273,7296,7250 ,3467,7471,3418 }, - {7684,7705,7683 ,7400,7349,5267 }, {7705,7704,7683 ,7349,3562,5267 }, - {6784,2278,4572 ,7472,3495,5949 }, {7110,7133,7108 ,1990,1989,4111 }, - {7355,7405,7382 ,7435,7440,7466 }, {7582,7637,7581 ,7469,7473,7394 }, - {7714,4744,7983 ,4468,4424,4361 }, {5026,8062,7983 ,4225,3272,4361 }, - {5643,5660,5659 ,4617,3054,3053 }, {7698,7720,7747 ,333,4271,331 }, - {7613,7634,7612 ,5106,7474,5107 }, {7516,7575,7574 ,5708,7475,7406 }, - {6784,4572,8039 ,7472,5949,7476 }, {7274,7273,7252 ,7423,3467,3437 }, - {3824,4744,7714 ,3462,4424,4468 }, {3824,1879,4744 ,3462,1283,4424 }, - {3976,3830,3298 ,919,5882,7424 }, {7398,7425,7397 ,7429,3995,1186 }, - {7253,7297,7252 ,1855,7422,3437 }, {7356,7355,7327 ,7477,7435,7433 }, - {2487,2486,7615 ,4427,2814,2816 }, {6316,6311,2162 ,4435,1097,3278 }, - {3824,3298,1879 ,3462,7424,1283 }, {7935,2290,7960 ,5527,1982,3505 }, - {7461,7490,7489 ,7441,7478,7444 }, {7489,7490,7519 ,7444,7478,7445 }, - {7608,7607,7547 ,7479,7455,7453 }, {2250,2285,8006 ,3716,3701,7407 }, - {7881,7907,7906 ,7480,7367,7409 }, {7709,7708,7661 ,7481,7388,7456 }, - {7262,7311,7287 ,4335,4334,3459 }, {7693,3298,3824 ,724,7424,3462 }, - {3298,7693,3976 ,7424,724,919 }, {2292,7936,2262 ,1983,5528,3359 }, - {7734,7733,7708 ,7482,7386,7388 }, {7709,7734,7708 ,7481,7482,7388 }, - {7835,7834,7780 ,7483,7390,7389 }, {7781,7835,7780 ,7464,7483,7389 }, - {7229,7270,7247 ,2556,2555,7360 }, {1788,145,113 ,676,810,677 }, - {3290,3289,7196 ,1204,2056,2401 }, {7517,7516,7459 ,7484,5708,7485 }, - {7883,7934,7882 ,7452,7486,7487 }, {3294,3293,7110 ,3895,1988,1990 }, - {7150,7149,3293 ,7488,7263,1988 }, {7150,7173,7149 ,7488,7264,7263 }, - {7637,7713,7692 ,7473,7489,7490 }, {7251,7250,3288 ,2671,3418,2058 }, - {7960,2290,162 ,3505,1982,2822 }, {7224,7223,7189 ,7491,7492,7493 }, - {6276,7107,6277 ,4316,7494,4387 }, {6282,7103,6283 ,6617,6455,4298 }, - {7713,7736,7692 ,7489,6752,7490 }, {7809,7832,7808 ,7449,7448,7495 }, - {7266,7315,7265 ,6769,7496,7497 }, {7230,7229,7210 ,7498,2556,7499 }, - {7211,7210,7173 ,7500,7499,7264 }, {7211,7230,7210 ,7500,7498,7499 }, - {7321,7353,7320 ,7501,7502,7282 }, {7188,7187,7161 ,7503,7330,2559 }, - {7631,7658,7605 ,7504,7505,5239 }, {7997,8037,7996 ,7210,4528,7328 }, - {7266,7265,7245 ,6769,7497,7506 }, {7935,7960,7933 ,5527,3505,7507 }, - {7931,7981,7930 ,7508,4252,4278 }, {7804,7803,7775 ,4634,4636,7509 }, - {7753,7804,7775 ,3643,4634,7509 }, {7779,7809,7808 ,7447,7449,7495 }, - {7757,7779,7808 ,7446,7447,7495 }, {7727,7775,7726 ,3563,7509,7418 }, - {7934,7933,7882 ,7486,7507,7487 }, {8025,8024,7980 ,7510,7511,4253 }, - {7383,7355,7382 ,7512,7435,7466 }, {7321,7320,7271 ,7501,7282,2554 }, - {7980,8003,7955 ,4253,3776,4254 }, {7855,7904,7854 ,6545,7513,7514 }, - {7353,7380,7320 ,7502,7451,7282 }, {7541,7516,7517 ,7515,5708,7484 }, - {7541,7575,7516 ,7515,7475,5708 }, {7934,7935,7933 ,7486,5527,7507 }, - {7713,2451,7736 ,7489,2102,6752 }, {7732,7757,7731 ,7450,7446,7516 }, - {8116,3976,8129 ,172,919,723 }, {7707,7732,7731 ,7382,7450,7516 }, - {7535,7534,7480 ,7517,7314,7463 }, {7685,7707,7731 ,7518,7382,7516 }, - {7755,7805,7754 ,7519,7520,7350 }, {7707,7685,7659 ,7382,7518,7383 }, - {1274,6108,1275 ,1687,6975,1688 }, {7980,8024,8003 ,4253,7511,3776 }, - {2281,2326,2280 ,3626,3588,3613 }, {7164,7195,7171 ,7521,7522,4306 }, - {7209,7228,7227 ,7523,7524,7359 }, {7228,7247,7227 ,7524,7360,7359 }, - {7855,7854,7803 ,6545,7514,4636 }, {7574,7575,7629 ,7406,7475,7399 }, - {7805,7804,7754 ,7520,4634,7350 }, {7578,7633,7577 ,7462,7458,7525 }, - {7325,7381,7354 ,7465,7467,7526 }, {7755,7754,7705 ,7519,7350,7349 }, - {8005,8004,7956 ,7527,7404,7403 }, {7540,7603,7628 ,5709,5266,7528 }, - {7798,7825,7771 ,4728,3768,4336 }, {7093,7107,6276 ,4318,7494,4316 }, - {7093,7119,7107 ,4318,7529,7494 }, {7119,7147,7146 ,7529,6937,7530 }, - {7683,7682,7655 ,5267,3564,5268 }, {7880,7879,7857 ,7531,7532,7533 }, - {7604,7657,7630 ,7416,7417,7534 }, {7275,7253,7254 ,1854,1855,450 }, - {7298,7297,7253 ,7535,7422,1855 }, {2285,2283,8004 ,3701,3687,7404 }, - {8005,2285,8004 ,7527,3701,7404 }, {7254,7253,6348 ,450,1855,1607 }, - {5558,5569,5599 ,2302,2075,2213 }, {7603,7655,7628 ,5266,5268,7528 }, - {7427,7426,7399 ,7536,7537,7428 }, {7275,7298,7253 ,1854,7535,1855 }, - {7298,7327,7297 ,7535,7433,7422 }, {7406,7405,7355 ,7538,7440,7435 }, - {7356,7406,7355 ,7477,7538,7435 }, {7490,7547,7519 ,7478,7453,7445 }, - {7662,7661,7607 ,7539,7456,7455 }, {7608,7662,7607 ,7479,7539,7455 }, - {1314,7638,1315 ,1409,7540,1627 }, {1877,3802,3948 ,282,4475,280 }, - {3948,3802,4296 ,280,4475,6023 }, {7720,7698,7648 ,4271,333,3952 }, - {7655,7682,7628 ,5268,3564,7528 }, {68,4368,3530 ,4087,4148,7541 }, - {7481,7535,7480 ,7542,7517,7463 }, {7734,7781,7733 ,7482,7464,7386 }, - {7537,7601,7572 ,7543,7544,5124 }, {7886,7885,7834 ,6561,7468,7390 }, - {7835,7886,7834 ,7483,6561,7390 }, {7886,7909,7885 ,6561,6603,7468 }, - {7884,7935,7883 ,7391,5527,7452 }, {7221,7241,7204 ,7545,4465,5120 }, - {3976,8116,5483 ,919,172,171 }, {7900,7951,7926 ,6922,7546,6923 }, - {1877,3825,3802 ,282,7547,4475 }, {59,2553,395 ,967,2716,966 }, - {7901,7900,7851 ,7413,6922,6625 }, {2283,8041,8004 ,3687,7548,7404 }, - {7753,7775,7727 ,3643,7509,3563 }, {7497,7523,7496 ,7421,7393,7470 }, - {7878,7877,7826 ,7411,7549,1616 }, {7459,7516,7458 ,7485,5708,7550 }, - {7900,7899,7849 ,6922,4116,7551 }, {7515,7487,7540 ,1079,5707,5709 }, - {7648,7699,7720 ,3952,3951,4271 }, {7149,7165,7133 ,7263,7265,1989 }, - {8107,2731,872 ,658,659,2962 }, {2410,3400,1877 ,1,3659,282 }, - {1315,1355,1314 ,1627,1408,1409 }, {7907,7958,7932 ,7367,3855,7408 }, - {7454,7453,7397 ,3996,1187,1186 }, {59,2689,2553 ,967,2821,2716 }, - {209,342,1178 ,4499,4542,206 }, {7328,7327,7298 ,7552,7433,7535 }, - {7275,7328,7298 ,1854,7552,7535 }, {7686,7659,7685 ,7553,7383,7518 }, - {7328,7356,7327 ,7552,7477,7433 }, {7432,7405,7406 ,7554,7440,7538 }, - {7633,7659,7632 ,7458,7383,7555 }, {7659,7686,7632 ,7383,7553,7555 }, - {7537,7572,7514 ,7543,5124,3610 }, {7601,7600,7572 ,7544,7556,5124 }, - {7432,7461,7405 ,7554,7441,7440 }, {7548,7547,7490 ,7557,7453,7478 }, - {7609,7608,7547 ,7558,7479,7453 }, {7548,7609,7547 ,7557,7558,7453 }, - {7663,7662,7608 ,7559,7539,7479 }, {7609,7663,7608 ,7558,7559,7479 }, - {7663,7661,7662 ,7559,7456,7539 }, {7710,7709,7661 ,7560,7481,7456 }, - {7663,7710,7661 ,7559,7560,7456 }, {7735,7734,7709 ,7561,7482,7481 }, - {7710,7735,7709 ,7560,7561,7481 }, {7295,7325,7354 ,7562,7465,7526 }, - {2410,1877,3670 ,1,282,281 }, {3400,1538,1451 ,3659,0,175 }, - {3400,3786,1877 ,3659,4074,282 }, {1877,3786,3825 ,282,4074,7547 }, - {3802,3825,1537 ,4475,7547,4473 }, {976,977,1029 ,782,3931,3930 }, - {7849,7899,7875 ,7551,4116,4118 }, {7782,7781,7734 ,7563,7464,7482 }, - {7836,7835,7781 ,7564,7483,7464 }, {7782,7836,7781 ,7563,7564,7464 }, - {7296,7295,7250 ,7471,7562,3418 }, {7836,7886,7835 ,7564,6561,7483 }, - {7721,7720,7676 ,7454,4271,7461 }, {2263,2295,2294 ,3080,3160,3081 }, - {7883,7882,7832 ,7452,7487,7448 }, {3296,3295,2265 ,4159,7565,2662 }, - {7637,7666,7636 ,7473,7566,7567 }, {7229,7247,7210 ,2556,7360,7499 }, - {7801,7853,7800 ,1618,1617,7568 }, {7110,7108,7109 ,1990,4111,4112 }, - {7802,7801,7751 ,6566,1618,4332 }, {7479,7508,7478 ,4423,6196,1402 }, - {3786,1934,3825 ,4074,4620,7547 }, {1934,3514,1537 ,4620,7569,4473 }, - {3825,1934,1537 ,7547,4620,4473 }, {3514,7764,1537 ,7569,7570,4473 }, - {1537,7764,1745 ,4473,7570,6233 }, {7764,7739,4207 ,7570,7571,4247 }, - {1745,7764,4207 ,6233,7570,4247 }, {4961,2164,3191 ,2905,3583,2903 }, - {7620,7646,7645 ,5033,5191,5034 }, {7457,7487,7515 ,1078,5707,1079 }, - {7429,7487,7457 ,7572,5707,1078 }, {3351,7523,7497 ,1444,7393,7421 }, - {2283,2282,2281 ,3687,3663,3626 }, {7296,7325,7295 ,7471,7465,7562 }, - {7106,7105,7092 ,7573,5279,3968 }, {7879,7905,7856 ,7532,7437,4635 }, - {7484,7483,7427 ,3612,7574,7536 }, {7289,7347,7288 ,7575,7576,7577 }, - {7513,7512,7483 ,3611,5203,7574 }, {7373,7399,7347 ,4989,7428,7576 }, - {7456,7455,7400 ,7578,7579,7580 }, {7754,7804,7753 ,7350,4634,3643 }, - {7320,7379,7319 ,7282,7430,7432 }, {7465,7464,7436 ,7581,4246,4245 }, - {7627,7626,7572 ,7582,7438,5124 }, {7313,7346,7312 ,7583,7584,4333 }, - {7488,7517,7459 ,7585,7484,7485 }, {3514,1934,7764 ,7569,4620,7570 }, - {937,975,974 ,5067,4266,5366 }, {8035,2275,8034 ,6675,3152,7586 }, - {7430,7429,7457 ,7587,7572,1078 }, {7209,7227,7246 ,7523,7359,3772 }, - {7429,7430,7457 ,7572,7587,1078 }, {7221,7263,7241 ,7545,627,4465 }, - {7345,7397,7372 ,7588,1186,4373 }, {7736,450,7691 ,6752,1653,4419 }, - {7582,7667,7637 ,7469,7589,7473 }, {7667,7668,7637 ,7589,7590,7473 }, - {7996,7995,7949 ,7328,4135,7591 }, {7377,7430,7429 ,7592,7587,7572 }, - {7318,7292,7352 ,7593,7594,7595 }, {7268,7292,7318 ,3773,7594,7593 }, - {7227,7269,7268 ,7359,7361,3773 }, {7246,7227,7268 ,3772,7359,3773 }, - {7269,7292,7268 ,7361,7594,3773 }, {7637,7636,7580 ,7473,7567,7596 }, - {7581,7637,7580 ,7394,7473,7596 }, {7230,7271,7229 ,7498,2554,2556 }, - {6346,7275,7253 ,4883,1854,1855 }, {7357,7356,7328 ,7597,7477,7552 }, - {7832,7882,7859 ,7448,7487,7598 }, {7646,7620,7675 ,5191,5033,5192 }, - {1934,2350,7764 ,4620,7599,7570 }, {7764,2350,7739 ,7570,7599,7571 }, - {3338,3530,4368 ,4147,7541,4148 }, {4445,3284,2871 ,3648,6720,3649 }, - {7422,7477,7449 ,1404,1403,7600 }, {7778,7731,7807 ,7601,7516,7602 }, - {7832,7859,7831 ,7448,7598,7603 }, {7731,7757,7807 ,7516,7446,7602 }, - {7357,7406,7356 ,7597,7538,7477 }, {7491,7490,7461 ,7604,7478,7441 }, - {7432,7491,7461 ,7554,7604,7441 }, {7549,7548,7490 ,7605,7557,7478 }, - {7610,7609,7548 ,7606,7558,7557 }, {7549,7610,7548 ,7605,7606,7557 }, - {7960,7959,7933 ,3505,7607,7507 }, {7516,7574,7540 ,5708,7406,5709 }, - {7689,7690,402 ,6444,6466,5349 }, {7225,7245,7207 ,1672,7506,7608 }, - {7225,7207,7194 ,1672,7608,1670 }, {7291,7315,7266 ,3774,7496,6769 }, - {7291,7349,7315 ,3774,7609,7496 }, {7163,7164,7171 ,5460,7521,4306 }, - {7174,7173,7150 ,7610,7264,7488 }, {7996,8036,7995 ,7328,7327,4135 }, - {7664,7663,7609 ,7611,7559,7558 }, {7610,7664,7609 ,7606,7611,7558 }, - {7806,7805,7755 ,7612,7520,7519 }, {6796,6898,8063 ,2617,1946,465 }, - {2350,7813,7739 ,7599,7613,7571 }, {7813,3338,7739 ,7613,4147,7571 }, - {4741,3634,3530 ,5508,5507,7541 }, {7808,7832,7831 ,7495,7448,7603 }, - {7806,7829,7805 ,7612,7614,7520 }, {7633,7632,7576 ,7458,7555,7615 }, - {3297,7734,7735 ,4203,7482,7561 }, {7710,3297,7735 ,7560,4203,7561 }, - {7783,7782,7734 ,7616,7563,7482 }, {3297,7783,7734 ,4203,7616,7482 }, - {7295,7354,7324 ,7562,7526,7617 }, {2265,7836,7782 ,2662,7564,7563 }, - {7316,7317,7350 ,5948,5947,7618 }, {7629,7657,7705 ,7399,7417,7349 }, - {7959,7960,2306 ,7607,3505,6405 }, {8039,4572,8038 ,7476,5949,4121 }, - {7401,7456,7400 ,7619,7578,7580 }, {7245,7265,7244 ,7506,7497,7620 }, - {7245,7244,7207 ,7506,7620,7608 }, {7132,7164,7163 ,7621,7521,5460 }, - {7148,7132,7163 ,6936,7621,5460 }, {7783,2265,7782 ,7616,2662,7563 }, - {7250,7295,7249 ,3418,7562,7622 }, {7849,7875,7797 ,7551,4118,3506 }, - {7932,7931,7906 ,7408,7508,7409 }, {156,3691,1934 ,5539,7623,4620 }, - {1934,3691,2350 ,4620,7623,7599 }, {7763,7838,3338 ,7624,7625,4147 }, - {7813,7763,3338 ,7613,7624,4147 }, {4911,4741,3530 ,7626,5508,7541 }, - {936,937,974 ,5234,5067,5366 }, {7289,7288,7242 ,7575,7577,7627 }, - {7829,7857,7805 ,7614,7533,7520 }, {2265,7886,7836 ,2662,6561,7564 }, - {2907,3791,2941 ,111,3739,109 }, {7668,7713,7637 ,7590,7489,7473 }, - {7604,7575,7541 ,7416,7475,7515 }, {7575,7604,7629 ,7475,7416,7399 }, - {7604,7630,7629 ,7416,7534,7399 }, {7630,7657,7629 ,7534,7417,7399 }, - {7118,7119,7145 ,7628,7529,7629 }, {7386,7412,7385 ,5512,3765,7630 }, - {7206,7243,7224 ,7631,7632,7491 }, {7206,7224,7190 ,7631,7491,7633 }, - {7191,7206,7190 ,7634,7631,7633 }, {7290,7289,7243 ,7635,7575,7632 }, - {7290,7348,7289 ,7635,4990,7575 }, {7538,7537,7485 ,2014,7543,7636 }, - {7654,7653,7601 ,7637,7638,7544 }, {8036,8037,4589 ,7327,4528,776 }, - {156,3740,3691 ,5539,7639,7623 }, {3691,3740,2350 ,7623,7639,7599 }, - {3740,7763,7813 ,7639,7624,7613 }, {2350,3740,7813 ,7599,7639,7613 }, - {7763,7122,7838 ,7624,7640,7625 }, {7838,7122,3338 ,7625,7640,4147 }, - {7122,8046,3530 ,7640,7641,7541 }, {3338,7122,3530 ,4147,7640,7541 }, - {3737,1350,791 ,7642,509,7643 }, {7957,7956,7931 ,7385,7403,7508 }, - {7748,7771,7722 ,7644,4336,7645 }, {7932,7957,7931 ,7408,7385,7508 }, - {7577,7633,7576 ,7525,7458,7615 }, {7995,8036,4588 ,4135,7327,775 }, - {7581,7580,7522 ,7394,7596,7395 }, {8041,2283,2281 ,7548,3687,3626 }, - {7905,7904,7855 ,7437,7513,6545 }, {7690,7689,7613 ,6466,6444,5106 }, - {7635,7690,7613 ,7646,6466,5106 }, {7193,7192,7162 ,7647,7648,7649 }, - {7147,7193,7162 ,6937,7647,7649 }, {7222,7263,7221 ,628,627,7545 }, - {7117,7116,7089 ,7650,7262,5608 }, {7090,7117,7089 ,4080,7650,5608 }, - {7143,7170,7190 ,7651,7652,7633 }, {7170,7191,7190 ,7652,7634,7633 }, - {7144,7170,7143 ,7653,7652,7651 }, {7131,7144,7143 ,7654,7653,7651 }, - {7130,7131,7143 ,6823,7654,7651 }, {7119,7118,7107 ,7529,7628,7494 }, - {7553,7579,7552 ,7655,7656,5546 }, {7553,7613,7579 ,7655,5106,7656 }, - {7193,7194,7192 ,7647,1670,7648 }, {7981,8004,7980 ,4252,7404,4253 }, - {7521,7580,7520 ,7657,7596,7658 }, {3740,1413,7763 ,7639,7659,7624 }, - {7122,3423,8046 ,7640,7660,7641 }, {3423,8046,3530 ,7660,7641,7541 }, - {8046,3423,3530 ,7641,7660,7541 }, {8046,3633,4911 ,7641,7661,7626 }, - {3530,8046,4911 ,7541,7641,7626 }, {6869,4741,4911 ,7662,5508,7626 }, - {3633,6869,4911 ,7661,7662,7626 }, {1350,3737,5571 ,509,7642,4616 }, - {6869,4956,4741 ,7662,5381,5508 }, {7162,7192,7170 ,7649,7648,7652 }, - {7749,7748,7723 ,7663,7644,7664 }, {7748,7799,7771 ,7644,4727,4336 }, - {2281,8054,8024 ,3626,7402,7511 }, {6658,5570,3121 ,7665,508,2076 }, - {7272,7293,7322 ,7666,6473,75 }, {7654,7703,7653 ,7637,7667,7638 }, - {7774,7773,7725 ,7419,7668,7420 }, {7207,7206,7191 ,7608,7631,7634 }, - {7346,7345,7312 ,7584,7588,4333 }, {3615,5700,5681 ,201,4501,2934 }, - {3530,3634,68 ,7541,5507,4087 }, {7522,7580,7521 ,7395,7596,7657 }, - {7148,7163,7147 ,6936,5460,6937 }, {7171,7195,7209 ,4306,7522,7523 }, - {7851,7900,7850 ,6625,6922,3769 }, {7248,7293,7272 ,73,6473,7666 }, - {7205,7204,7188 ,7669,5120,7503 }, {7602,7628,7601 ,5867,7528,7544 }, - {7428,7427,7373 ,7670,7536,4989 }, {7374,7428,7373 ,4988,7670,4989 }, - {7194,7207,7192 ,1670,7608,7648 }, {7465,7521,7464 ,7581,7657,4246 }, - {7854,7878,7827 ,7514,7411,7410 }, {7580,7553,7520 ,7596,7655,7658 }, - {7579,7613,7552 ,7656,5106,5546 }, {3740,156,4264 ,7639,5539,5540 }, - {1413,6477,7763 ,7659,7671,7624 }, {7763,6477,7122 ,7624,7671,7640 }, - {7122,6477,3423 ,7640,7671,7660 }, {3423,3633,8046 ,7660,7661,7641 }, - {5039,6869,3633 ,7672,7662,7661 }, {2733,4956,6869 ,5367,5381,7662 }, - {5039,2733,6869 ,7672,5367,7662 }, {5046,1081,2993 ,5232,3745,3744 }, - {7680,7679,7625 ,7673,7674,7139 }, {7118,7145,7144 ,7628,7629,7653 }, - {7651,7680,7625 ,7675,7673,7139 }, {7858,7907,7881 ,7368,7367,7480 }, - {8054,8053,8024 ,7402,7676,7511 }, {7511,7533,7510 ,6677,7039,1873 }, - {7692,7736,7691 ,7490,6752,4419 }, {7272,7322,7321 ,7666,75,7501 }, - {3843,935,3933 ,7677,5264,7678 }, {6552,2278,6784 ,7679,3495,7472 }, - {7325,7383,7382 ,7465,7512,7466 }, {2278,2243,2242 ,3495,1993,2551 }, - {5681,2453,5659 ,2934,42,3053 }, {7192,7207,7191 ,7648,7608,7634 }, - {7462,7494,7435 ,4310,4309,6244 }, {795,1511,1344 ,302,4921,300 }, - {1511,795,4377 ,4921,302,3090 }, {7426,7425,7399 ,7537,3995,7428 }, - {7347,7346,7313 ,7576,7584,7583 }, {7189,7205,7188 ,7493,7669,7503 }, - {7288,7347,7313 ,7577,7576,7583 }, {7131,7130,7105 ,7654,6823,5279 }, - {7313,7312,7263 ,7583,4333,627 }, {7680,7701,7679 ,7673,3515,7674 }, - {7224,7242,7223 ,7491,7627,7492 }, {7437,7496,7465 ,5938,7470,7581 }, - {7496,7522,7465 ,7470,7395,7581 }, {4897,7837,4264 ,5442,5967,5540 }, - {7837,3912,3740 ,5967,7680,7639 }, {3740,3912,1413 ,7639,7680,7659 }, - {6477,7669,3423 ,7671,7681,7660 }, {3423,7861,3633 ,7660,7682,7661 }, - {7385,7412,7384 ,7630,3765,7683 }, {8136,8063,8140 ,2449,465,2269 }, - {7553,7552,7495 ,7655,5546,4308 }, {7520,7553,7495 ,7658,7655,4308 }, - {7878,7929,7877 ,7411,7684,7549 }, {7626,7624,7570 ,7438,7439,5204 }, - {8060,3783,8037 ,7685,5311,4528 }, {8043,5557,5558 ,6397,756,2302 }, - {7205,7221,7204 ,7669,7545,5120 }, {3326,6346,6347 ,826,4883,451 }, - {8021,8051,8037 ,7365,6172,4528 }, {2280,2243,8053 ,3613,1993,7676 }, - {7571,7626,7570 ,5126,7438,5204 }, {2484,2485,2524 ,3499,2815,3395 }, - {6898,6870,2205 ,1946,2616,2874 }, {7599,7626,7571 ,5125,7438,5126 }, - {7513,7571,7512 ,3611,5126,5203 }, {7651,7625,7626 ,7675,7139,7438 }, - {7223,7222,7189 ,7492,628,7493 }, {2141,8101,8091 ,2015,383,2167 }, - {3862,7810,1494 ,550,552,1083 }, {14,302,223 ,1986,622,1985 }, - {91,14,223 ,1984,1986,1985 }, {7384,7359,7332 ,7683,7686,6685 }, - {7333,7384,7332 ,4353,7683,6685 }, {7411,7410,7359 ,3767,3766,7686 }, - {7384,7411,7359 ,7683,3767,7686 }, {7232,7248,7231 ,2124,73,6944 }, - {7691,7690,7666 ,4419,6466,7566 }, {7223,7264,7222 ,7492,626,628 }, - {7288,7263,7264 ,7577,627,626 }, {7798,7851,7825 ,4728,6625,3768 }, - {7850,7849,7824 ,3769,7551,3770 }, {7999,8022,7998 ,4583,4123,7209 }, - {7751,7750,7701 ,4332,7687,3515 }, {8040,8039,8001 ,7688,7476,7689 }, - {8002,8040,8001 ,3777,7688,7689 }, {1413,3912,6477 ,7659,7680,7671 }, - {7669,3863,7861 ,7681,7690,7682 }, {3423,7669,7861 ,7660,7681,7682 }, - {3863,3538,3633 ,7690,7691,7661 }, {7861,3863,3633 ,7682,7690,7661 }, - {3633,3538,5039 ,7661,7691,7672 }, {5039,4573,2733 ,7672,5314,5367 }, - {4303,85,166 ,3728,2504,3729 }, {7876,7927,7902 ,1725,1716,7692 }, - {7958,8007,7957 ,3855,3854,7385 }, {7706,7730,7657 ,7693,4413,7417 }, - {7955,8003,7979 ,4254,3776,3775 }, {7299,7275,6346 ,7694,1854,4883 }, - {7329,7328,7275 ,7695,7552,1854 }, {7299,7329,7275 ,7694,7695,1854 }, - {7329,7357,7328 ,7695,7597,7552 }, {7652,7651,7626 ,7696,7675,7438 }, - {8054,2280,8053 ,7402,3613,7676 }, {2326,2325,2280 ,3588,1994,3613 }, - {7730,7776,7729 ,4413,4412,7697 }, {8043,5558,5599 ,6397,2302,2213 }, - {4558,7918,4559 ,4209,5555,5237 }, {7754,7753,7704 ,7350,3643,3562 }, - {7399,7398,7346 ,7428,7429,7584 }, {7701,7700,7679 ,3515,7698,7674 }, - {7349,7375,7315 ,7609,7699,7496 }, {3402,1538,3400 ,3660,0,3659 }, - {1973,3862,1494 ,551,550,1083 }, {7597,7596,7534 ,7700,7701,7314 }, - {7534,7535,7481 ,7314,7517,7542 }, {7929,7977,7928 ,7684,1714,1724 }, - {7104,7117,7090 ,5280,7650,4080 }, {7091,7104,7090 ,4153,5280,4080 }, - {7656,7683,7603 ,7401,5267,5266 }, {7635,7613,7553 ,7646,5106,7655 }, - {7522,7521,7465 ,7395,7657,7581 }, {3895,3912,7837 ,4455,7680,5967 }, - {3912,7388,6477 ,7680,7702,7671 }, {7388,7669,6477 ,7702,7681,7671 }, - {3863,587,5039 ,7690,7703,7672 }, {3538,3863,5039 ,7691,7690,7672 }, - {587,3401,4573 ,7703,7704,5314 }, {5039,587,4573 ,7672,7703,5314 }, - {7462,7520,7495 ,4310,7658,4308 }, {7265,7290,7243 ,7497,7635,7632 }, - {7569,7597,7534 ,7705,7700,7314 }, {7407,7406,7357 ,7706,7538,7597 }, - {7407,7432,7406 ,7706,7554,7538 }, {7270,7292,7247 ,2555,7594,7360 }, - {1991,597,153 ,7707,791,663 }, {8105,8137,8107 ,3791,2174,658 }, - {7219,7261,7218 ,1529,1528,7708 }, {5523,8055,3121 ,7709,7710,2076 }, - {5644,3724,3615 ,4133,4132,201 }, {3724,5683,5682 ,4132,3939,3783 }, - {3615,3724,5682 ,201,4132,3783 }, {7346,7398,7345 ,7584,7429,7588 }, - {7464,7463,7412 ,4246,7711,3765 }, {7689,402,7712 ,6444,5349,5350 }, - {7130,7117,7104 ,6823,7650,5280 }, {7623,7677,7622 ,7427,7426,7460 }, - {7133,7165,7132 ,1989,7265,7621 }, {7144,7145,7170 ,7653,7629,7652 }, - {3634,4813,68 ,5507,4088,4087 }, {7334,3912,3895 ,7712,7680,4455 }, - {3376,7334,3895 ,4466,7712,4455 }, {7388,7584,7669 ,7702,7713,7681 }, - {7584,3863,7669 ,7713,7690,7681 }, {587,7583,3401 ,7703,7714,7704 }, - {7954,7953,7903 ,7715,7716,7717 }, {7904,7954,7903 ,7513,7715,7717 }, - {7139,7140,7161 ,1025,7415,2559 }, {7115,7116,7139 ,1024,7262,1025 }, - {7263,7262,7241 ,627,4335,4465 }, {7492,7490,7491 ,7718,7478,7604 }, - {5556,5555,5535 ,3511,6338,3512 }, {8023,8022,7999 ,4582,4123,4583 }, - {6431,5495,6522 ,7719,4134,7720 }, {3121,791,6658 ,2076,7643,7665 }, - {6658,791,1350 ,7665,7643,509 }, {1991,2654,597 ,7707,2773,791 }, - {1350,5570,6658 ,509,508,7665 }, {7715,3724,5495 ,7721,4132,4134 }, - {3338,4207,7739 ,4147,4247,7571 }, {7458,7516,7487 ,7550,5708,5707 }, - {7482,7481,7425 ,7722,7542,3995 }, {7722,7721,7676 ,7645,7454,7461 }, - {7677,7722,7676 ,7426,7645,7461 }, {7455,7514,7484 ,7579,3610,3612 }, - {7751,7801,7750 ,4332,1618,7687 }, {7244,7265,7243 ,7620,7497,7632 }, - {5026,4744,4859 ,4225,4424,5039 }, {2618,7334,3376 ,4454,7712,4466 }, - {7334,2618,3912 ,7712,4454,7680 }, {3912,2618,7388 ,7680,4454,7702 }, - {4546,3863,7584 ,7723,7690,7713 }, {4546,881,3863 ,7723,7724,7690 }, - {881,587,3863 ,7724,7703,7690 }, {7876,7902,7852 ,1725,7692,7412 }, - {8050,3783,4589 ,4529,5311,776 }, {382,1444,4207 ,3534,4035,4247 }, - {7721,7770,7720 ,7454,4272,4271 }, {7550,7549,7490 ,7725,7605,7478 }, - {7492,7550,7490 ,7718,7725,7478 }, {7824,7797,7747 ,3770,3506,331 }, - {7379,7404,7378 ,7430,7726,7431 }, {7116,7140,7139 ,7262,7415,1025 }, - {382,4207,1005 ,3534,4247,3535 }, {8059,8034,2275 ,4235,7586,3152 }, - {7689,7688,7613 ,6444,6443,5106 }, {7886,2296,2295 ,6561,2664,3160 }, - {3737,5495,5571 ,7642,4134,4616 }, {1218,3008,6891 ,3255,546,813 }, - {1517,1622,235 ,600,4238,598 }, {145,2694,2627 ,810,811,2776 }, - {3934,5683,3724 ,7727,3939,4132 }, {3934,3009,5683 ,7727,3747,3939 }, - {2419,2959,890 ,3353,4608,3283 }, {7647,7675,7696 ,4967,5192,7728 }, - {7495,7552,7494 ,4308,5546,4309 }, {7130,7143,7142 ,6823,7651,7729 }, - {8073,8085,2343 ,2567,2016,2685 }, {3693,4590,278 ,5347,1490,1489 }, - {7978,7977,7929 ,7730,1714,7684 }, {7876,7852,7853 ,1725,7412,1617 }, - {4819,3401,3139 ,5216,7704,5400 }, {6292,6282,6283 ,5403,6617,4298 }, - {2618,3682,7388 ,4454,7731,7702 }, {3682,7839,7388 ,7731,7732,7702 }, - {7388,7839,7584 ,7702,7732,7713 }, {881,6712,587 ,7724,7733,7703 }, - {6712,7911,7583 ,7733,7734,7714 }, {587,6712,7583 ,7703,7733,7714 }, - {7911,3139,3401 ,7734,5400,7704 }, {7583,7911,3401 ,7714,7734,7704 }, - {7771,7824,7770 ,4336,3770,4272 }, {7803,7854,7828 ,4636,7514,7735 }, - {7117,7142,7141 ,7650,7729,7414 }, {7485,7514,7456 ,7636,3610,7578 }, - {7849,7850,7824 ,7551,3769,3770 }, {7711,7710,7663 ,7736,7560,7559 }, - {7850,7849,7824 ,3769,7551,3770 }, {7664,7711,7663 ,7611,7736,7559 }, - {7675,7647,7696 ,5192,4967,7728 }, {5535,5519,5536 ,3512,6337,754 }, - {2296,7886,2264 ,2664,6561,4166 }, {818,2844,906 ,1018,1329,1019 }, - {3121,8055,791 ,2076,7710,7643 }, {3737,6522,5495 ,7642,7720,4134 }, - {4573,3401,4819 ,5314,7704,5216 }, {6169,6140,6141 ,2334,2333,2386 }, - {3934,5038,3009 ,7727,3628,3747 }, {4883,1388,4774 ,5538,6311,5332 }, - {7117,7130,7142 ,7650,6823,7729 }, {7375,7428,7374 ,7699,7670,4988 }, - {3693,278,3638 ,5347,1489,5950 }, {8001,8039,8023 ,7689,7476,4582 }, - {8001,8023,8000 ,7689,4582,4581 }, {7877,7928,7876 ,7549,1724,1725 }, - {5495,6431,7715 ,4134,7719,7721 }, {5080,7839,3682 ,7737,7732,7731 }, - {5080,7584,7839 ,7737,7713,7732 }, {5080,2307,7584 ,7737,7738,7713 }, - {7584,2307,4546 ,7713,7738,7723 }, {6712,3139,7911 ,7733,5400,7734 }, - {6797,8108,8123 ,2246,2287,2247 }, {7312,7345,7311 ,4333,7588,4334 }, - {7116,7117,7141 ,7262,7650,7414 }, {7825,7851,7798 ,3768,6625,4728 }, - {7933,7959,7908 ,7507,7607,7366 }, {3297,3296,7783 ,4203,4159,7616 }, - {7882,7933,7908 ,7487,7507,7366 }, {886,2379,213 ,3464,3238,3606 }, - {5519,7962,5536 ,6337,7739,754 }, {7962,5522,5536 ,7739,755,754 }, - {3702,4249,1185 ,1303,6136,6159 }, {5523,2708,8055 ,7709,7740,7710 }, - {2708,6437,791 ,7740,399,7643 }, {8055,2708,791 ,7710,7740,7643 }, - {3737,7785,6522 ,7642,7741,7720 }, {6431,1266,3724 ,7719,7742,4132 }, - {7715,6431,3724 ,7721,7719,4132 }, {1266,3739,3934 ,7742,7743,7727 }, - {3724,1266,3934 ,4132,7742,7727 }, {3702,1185,5520 ,1303,6159,1304 }, - {7725,7702,7703 ,7420,3513,7667 }, {7675,7719,7696 ,5192,6541,7728 }, - {7385,7384,7333 ,7630,7683,4353 }, {7625,7678,7623 ,7139,7425,7427 }, - {7373,7427,7399 ,4989,7536,7428 }, {7750,7772,7748 ,7687,7744,7644 }, - {3875,2618,3184 ,5845,4454,4453 }, {3875,3682,2618 ,5845,7731,4454 }, - {2307,3483,4546 ,7738,7745,7723 }, {3483,881,4546 ,7745,7724,7723 }, - {2277,3693,4572 ,3522,5347,5949 }, {7997,7996,7950 ,7210,7328,7211 }, - {2486,2527,2526 ,2814,2637,2636 }, {7803,7828,7775 ,4636,7735,7509 }, - {7376,7351,7402 ,7746,6587,6589 }, {3296,2265,7783 ,4159,2662,7616 }, - {7882,7908,7859 ,7487,7366,7598 }, {2264,7886,2265 ,4166,6561,2662 }, - {7912,5519,5493 ,7747,6337,6336 }, {5519,7912,7962 ,6337,7747,7739 }, - {7962,7984,5522 ,7739,7748,755 }, {2708,3914,791 ,7740,7749,7643 }, - {6437,2708,791 ,399,7740,7643 }, {3914,3790,3737 ,7749,399,7642 }, - {791,3914,3737 ,7643,7749,7642 }, {7785,7360,6522 ,7741,7750,7720 }, - {6522,7360,6431 ,7720,7750,7719 }, {3739,3910,3934 ,7743,7751,7727 }, - {3934,3910,5038 ,7727,7751,3628 }, {7597,7622,7596 ,7700,7460,7701 }, - {7625,7623,7624 ,7139,7427,7439 }, {7190,7189,7142 ,7633,7493,7729 }, - {7173,7210,7172 ,7264,7499,7405 }, {8039,8038,8023 ,7476,4121,4582 }, - {7540,7628,7602 ,5709,7528,5867 }, {7765,5080,3682 ,7752,7737,7731 }, - {3875,7765,3682 ,5845,7752,7731 }, {3483,1526,6712 ,7745,7753,7733 }, - {881,3483,6712 ,7724,7745,7733 }, {1526,3171,6712 ,7753,7754,7733 }, - {3171,3139,6712 ,7754,5400,7733 }, {3171,4846,3139 ,7754,4237,5400 }, - {7877,7876,7826 ,7549,1725,1616 }, {3295,3296,177 ,7565,4159,3618 }, - {3297,3313,3296 ,4203,4160,4159 }, {7912,7963,7962 ,7747,7755,7739 }, - {7962,7963,7984 ,7739,7755,7748 }, {7963,7985,5522 ,7755,7756,755 }, - {7984,7963,5522 ,7748,7755,755 }, {5522,7985,5523 ,755,7756,7709 }, - {3914,7467,3737 ,7749,7757,7642 }, {3790,3914,3737 ,399,7749,7642 }, - {3737,7467,7785 ,7642,7757,7741 }, {7360,1266,6431 ,7750,7742,7719 }, - {3910,3739,1266 ,7751,7743,7742 }, {3567,5038,3910 ,7758,3628,7751 }, - {3614,4839,5038 ,7759,5484,3628 }, {3567,3614,5038 ,7758,7759,3628 }, - {3614,5092,4839 ,7759,3953,5484 }, {7701,7724,7700 ,3515,7760,7698 }, - {7143,7190,7142 ,7651,7633,7729 }, {7288,7313,7263 ,7577,7583,627 }, - {7727,7726,7681 ,3563,7418,7459 }, {7682,7727,7681 ,3564,3563,7459 }, - {7580,7636,7553 ,7596,7567,7655 }, {7636,7666,7635 ,7567,7566,7646 }, - {7761,7765,3875 ,7761,7752,5845 }, {7761,5080,7765 ,7761,7737,7752 }, - {3639,2307,5080 ,7762,7738,7737 }, {7761,3639,5080 ,7761,7762,7737 }, - {7936,7935,7884 ,5528,5527,7391 }, {7777,7778,7807 ,4411,7601,7602 }, - {7607,7660,7606 ,7455,7381,7457 }, {5493,7888,7912 ,6336,7763,7747 }, - {5214,5215,5235 ,6272,6313,6385 }, {7985,8044,2708 ,7756,7764,7740 }, - {5523,7985,2708 ,7709,7756,7740 }, {3914,3188,7467 ,7749,7765,7757 }, - {7467,6482,7785 ,7757,7766,7741 }, {7785,6482,7360 ,7741,7766,7750 }, - {7360,6482,1266 ,7750,7766,7742 }, {4020,3910,1266 ,7767,7751,7742 }, - {2097,2165,2164 ,846,1041,3583 }, {7426,7482,7425 ,7537,7722,3995 }, - {7773,7802,7751 ,7668,6566,4332 }, {7573,7540,7602 ,2013,5709,5867 }, - {7151,7150,3293 ,1202,7488,1988 }, {7731,7778,7777 ,7516,7601,4411 }, - {7756,7731,7777 ,7768,7516,4411 }, {7685,7731,7756 ,7518,7516,7768 }, - {7212,7211,7173 ,7208,7500,7264 }, {6610,3483,2307 ,7769,7745,7738 }, - {3639,6610,2307 ,7762,7769,7738 }, {3483,6610,1526 ,7745,7769,7753 }, - {8067,8091,8083 ,2168,2167,2103 }, {7320,7319,7292 ,7282,7432,7594 }, - {7515,7540,7573 ,1079,5709,2013 }, {7830,7829,7806 ,7770,7614,7612 }, - {7636,7635,7553 ,7567,7646,7655 }, {7950,7996,7949 ,7211,7328,7591 }, - {7481,7480,7454 ,7542,7463,3996 }, {7397,7424,7371 ,1186,1188,3748 }, - {7888,7938,7912 ,7763,7771,7747 }, {7912,7938,7963 ,7747,7771,7755 }, - {7985,8027,8044 ,7756,7772,7764 }, {8044,8027,2708 ,7764,7772,7740 }, - {8027,3689,3914 ,7772,7773,7749 }, {2708,8027,3914 ,7740,7772,7749 }, - {6482,1939,1266 ,7766,7774,7742 }, {1939,7862,1266 ,7774,7775,7742 }, - {1266,7862,4020 ,7742,7775,7767 }, {4020,3567,3910 ,7767,7758,7751 }, - {3567,5092,3614 ,7758,3953,7759 }, {7600,7627,7572 ,7556,7582,5124 }, - {7145,7146,7162 ,7629,7530,7649 }, {7146,7147,7162 ,7530,6937,7649 }, - {7773,7751,7752 ,7668,4332,3514 }, {402,448,7759 ,5349,1172,399 }, - {7270,7320,7292 ,2555,7282,7594 }, {7403,7429,7457 ,6588,7572,1078 }, - {7174,7212,7173 ,7610,7208,7264 }, {7935,7934,7883 ,5527,7486,7452 }, - {7686,7685,7632 ,7553,7518,7555 }, {6483,7761,3875 ,3584,7761,5845 }, - {2096,6483,3875 ,788,3584,5845 }, {6610,3767,1526 ,7769,7776,7753 }, - {3767,3171,1526 ,7776,7754,7753 }, {3767,6412,4846 ,7776,7777,4237 }, - {3171,3767,4846 ,7754,7776,4237 }, {235,1622,4846 ,598,4238,4237 }, - {6412,235,4846 ,7777,598,4237 }, {1993,4075,342 ,4544,4521,4542 }, - {3405,3456,8086 ,1798,1145,4823 }, {7272,7271,7230 ,7666,2554,7498 }, - {7956,7981,7931 ,7403,4252,7508 }, {3136,2654,2548 ,2783,2773,2813 }, - {5479,7840,5493 ,5387,7778,6336 }, {2287,8007,99 ,3735,3854,3743 }, - {7840,7888,5493 ,7778,7763,6336 }, {7963,7938,7985 ,7755,7771,7756 }, - {8027,3723,3689 ,7772,7779,7773 }, {3723,3821,3914 ,7779,7780,7749 }, - {3689,3723,3914 ,7773,7779,7749 }, {3914,3821,3188 ,7749,7780,7765 }, - {3188,7787,7467 ,7765,7781,7757 }, {7467,7787,6482 ,7757,7781,7766 }, - {1939,3374,7862 ,7774,7782,7775 }, {3374,3586,4020 ,7782,7783,7767 }, - {7862,3374,4020 ,7775,7782,7767 }, {3586,3722,4020 ,7783,7784,7767 }, - {3823,3567,4020 ,7785,7758,7767 }, {3722,3823,4020 ,7784,7785,7767 }, - {3823,5092,3567 ,7785,3953,7758 }, {7189,7188,7141 ,7493,7503,7414 }, - {7242,7264,7223 ,7627,626,7492 }, {7658,7706,7657 ,7505,7693,7417 }, - {7554,7523,3351 ,7786,7393,1444 }, {7128,7127,7114 ,5391,7245,5392 }, - {7910,6483,2096 ,3582,3584,788 }, {2207,3639,7761 ,7787,7762,7761 }, - {6483,2207,7761 ,3584,7787,7761 }, {8086,3456,5771 ,4823,1145,2868 }, - {7352,7377,7403 ,7595,7592,6588 }, {7212,7230,7211 ,7208,7498,7500 }, - {7614,7582,7523 ,7788,7469,7393 }, {7554,7614,7523 ,7786,7788,7393 }, - {7276,6346,3326 ,3696,4883,826 }, {4555,4582,7716 ,5496,5478,5549 }, - {7840,7889,7888 ,7778,7789,7763 }, {7889,7913,7888 ,7789,7790,7763 }, - {7913,7939,7938 ,7790,7791,7771 }, {7888,7913,7938 ,7763,7790,7771 }, - {7939,7964,7938 ,7791,7792,7771 }, {7938,7964,7985 ,7771,7792,7756 }, - {7964,8010,7985 ,7792,7793,7756 }, {7985,8010,8027 ,7756,7793,7772 }, - {3723,1123,3821 ,7779,7794,7780 }, {3821,7784,7787 ,7780,7795,7781 }, - {3188,3821,7787 ,7765,7780,7781 }, {7787,7784,6482 ,7781,7795,7766 }, - {7784,1939,6482 ,7795,7774,7766 }, {3766,5092,3823 ,3954,3953,7785 }, - {7556,3766,3823 ,5116,3954,7785 }, {8133,8131,8107 ,2131,2307,658 }, - {7242,7288,7264 ,7627,7577,626 }, {8022,8021,7998 ,4123,7365,7209 }, - {7351,7352,7403 ,6587,7595,6588 }, {7377,7429,7403 ,7592,7572,6588 }, - {7276,7299,6346 ,3696,7694,4883 }, {7330,7329,7299 ,7796,7695,7694 }, - {7358,7357,7329 ,7797,7597,7695 }, {7330,7358,7329 ,7796,7797,7695 }, - {1973,1659,4685 ,551,5132,4894 }, {3896,6610,3639 ,7798,7769,7762 }, - {2207,3896,3639 ,7787,7798,7762 }, {3896,3767,6610 ,7798,7776,7769 }, - {7317,7318,7351 ,5947,7593,6587 }, {7250,7249,7232 ,3418,7622,2124 }, - {7233,7250,7232 ,2125,3418,2124 }, {7640,7641,4577 ,5351,5356,5352 }, - {6320,7439,7500 ,190,7297,326 }, {7500,7524,4595 ,326,1630,5289 }, - {4557,4558,4584 ,5310,4209,5308 }, {7433,7432,7407 ,7799,7554,7706 }, - {3766,4922,4585 ,3954,5256,3955 }, {5479,7814,7840 ,5387,6988,7778 }, - {7863,7864,7889 ,7800,7801,7789 }, {7840,7863,7889 ,7778,7800,7789 }, - {7864,7914,7913 ,7801,7802,7790 }, {7889,7864,7913 ,7789,7801,7790 }, - {7913,7914,7939 ,7790,7802,7791 }, {7964,7986,8010 ,7792,7803,7793 }, - {7986,8010,8027 ,7803,7793,7772 }, {8010,7986,8027 ,7793,7803,7772 }, - {8010,8045,3723 ,7793,7804,7779 }, {8027,8010,3723 ,7772,7793,7779 }, - {1123,7784,3821 ,7794,7795,7780 }, {7784,5023,1939 ,7795,7805,7774 }, - {5023,3374,1939 ,7805,7782,7774 }, {3374,3722,3586 ,7782,7784,7783 }, - {7556,3823,3722 ,5116,7785,7784 }, {3121,5522,5523 ,2076,755,7709 }, - {7653,7702,7652 ,7638,3513,7696 }, {7120,7119,7093 ,6938,7529,4318 }, - {7272,7321,7271 ,7666,7501,2554 }, {7208,7209,7226 ,1671,7523,6686 }, - {7433,7434,7432 ,7799,7806,7554 }, {7492,7491,7432 ,7718,7604,7554 }, - {7434,7492,7432 ,7806,7718,7554 }, {3830,3976,5483 ,5882,919,171 }, - {4961,2207,6483 ,2905,7787,3584 }, {6802,3767,3896 ,7807,7776,7798 }, - {6802,3842,3767 ,7807,7808,7776 }, {3767,3842,6412 ,7776,7808,7777 }, - {6412,3842,235 ,7777,7808,598 }, {2485,2486,2526 ,2815,2814,2636 }, - {7243,7242,7224 ,7632,7627,7491 }, {7318,7352,7351 ,7593,7595,6587 }, - {7488,7541,7517 ,7585,7515,7484 }, {7439,7468,7500 ,7297,7809,326 }, - {7468,7524,7500 ,7809,1630,326 }, {99,8007,2306 ,3743,3854,6405 }, - {7611,7610,7549 ,7810,7606,7605 }, {7550,7611,7549 ,7725,7810,7605 }, - {6706,7841,7840 ,5389,7811,7778 }, {7814,6706,7840 ,6988,5389,7778 }, - {7841,7864,7863 ,7811,7801,7800 }, {7840,7841,7863 ,7778,7811,7800 }, - {7939,7914,7964 ,7791,7802,7792 }, {8010,7986,8045 ,7793,7803,7804 }, - {8045,61,1123 ,7804,7812,7794 }, {3723,8045,1123 ,7779,7804,7794 }, - {1123,3512,7784 ,7794,7813,7795 }, {7784,7176,5023 ,7795,7814,7805 }, - {5023,3827,3374 ,7805,7815,7782 }, {3827,7817,3722 ,7815,7816,7784 }, - {3374,3827,3722 ,7782,7815,7784 }, {8009,7556,3722 ,7817,5116,7784 }, - {8022,8052,8021 ,4123,4122,7365 }, {8052,8051,8021 ,4122,6172,7365 }, - {7776,7806,7755 ,4412,7612,7519 }, {7209,7246,7225 ,7523,3772,1672 }, - {7226,7209,7225 ,6686,7523,1672 }, {7729,7755,7705 ,7697,7519,7349 }, - {7728,7729,7705 ,7818,7697,7349 }, {7248,7294,7323 ,73,7819,74 }, - {7611,7664,7610 ,7810,7611,7606 }, {3352,3378,3351 ,2724,1442,1444 }, - {3859,2207,4961 ,7820,7787,2905 }, {3859,3896,2207 ,7820,7798,7787 }, - {7268,7318,7317 ,3773,7593,5947 }, {6143,7057,6107 ,4394,7207,7232 }, - {6304,7439,3533 ,7233,7297,192 }, {7525,7524,7468 ,7821,1630,7809 }, - {7525,4575,7524 ,7821,3128,1630 }, {4589,3783,4638 ,776,5311,5154 }, - {2333,7711,7664 ,3537,7736,7611 }, {2333,7710,7711 ,3537,7560,7736 }, - {7841,7865,7864 ,7811,7822,7801 }, {7914,7940,7964 ,7802,7823,7792 }, - {7940,7987,7986 ,7823,7824,7803 }, {7964,7940,7986 ,7792,7823,7803 }, - {7986,8028,8045 ,7803,7825,7804 }, {61,1097,3512 ,7812,7826,7813 }, - {1123,61,3512 ,7794,7812,7813 }, {3512,1097,7784 ,7813,7826,7795 }, - {7176,6683,5023 ,7814,7827,7805 }, {5023,6683,3827 ,7805,7827,7815 }, - {3827,3692,7817 ,7815,7828,7816 }, {7817,7762,3722 ,7816,7829,7784 }, - {7762,8009,3722 ,7829,7817,7784 }, {6760,7556,8009 ,5117,5116,7817 }, - {7762,6760,8009 ,7829,5117,7817 }, {5522,3121,5558 ,755,2076,2302 }, - {7625,7624,7598 ,7139,7439,7140 }, {7600,7653,7627 ,7556,7638,7582 }, - {7653,7652,7627 ,7638,7696,7582 }, {7349,7401,7375 ,7609,7619,7699 }, - {7459,7458,7404 ,7485,7550,7726 }, {3314,3297,7710 ,3575,4203,7560 }, - {2333,3314,7710 ,3537,3575,7560 }, {1274,1316,1315 ,1687,1657,1627 }, - {2548,3896,3859 ,2813,7798,7820 }, {2654,6802,3896 ,2773,7807,7798 }, - {2548,2654,3896 ,2813,2773,7798 }, {7171,7209,7208 ,4306,7523,1671 }, - {7628,7681,7654 ,7528,7459,7637 }, {5112,6120,5113 ,6021,142,141 }, - {7057,6143,5936 ,7207,4394,4393 }, {6083,6304,6303 ,3989,7233,7261 }, - {7413,7439,6304 ,7830,7297,7233 }, {7469,7468,7439 ,7831,7809,7297 }, - {7413,7469,7439 ,7830,7831,7297 }, {7525,7585,4575 ,7821,6805,3128 }, - {7555,3351,3377 ,7832,1444,1443 }, {177,2265,3295 ,3618,2662,7565 }, - {6705,7815,6706 ,4158,5146,5389 }, {6706,7815,7841 ,5389,5146,7811 }, - {7865,7890,7864 ,7822,7833,7801 }, {7864,7890,7914 ,7801,7833,7802 }, - {7914,7890,7940 ,7802,7833,7823 }, {7986,7987,8028 ,7803,7824,7825 }, - {8028,8056,8045 ,7825,7834,7804 }, {8045,8056,61 ,7804,7834,7812 }, - {1097,7301,7784 ,7826,7835,7795 }, {7784,7301,7176 ,7795,7835,7814 }, - {7176,7301,6683 ,7814,7835,7827 }, {448,401,402 ,1172,1387,5349 }, - {7347,7399,7346 ,7576,7428,7584 }, {7514,7572,7513 ,3610,5124,3611 }, - {7657,7728,7705 ,7417,7818,7349 }, {7780,7834,7809 ,7389,7390,7449 }, - {7628,7654,7601 ,7528,7637,7544 }, {2834,3859,4961 ,2860,7820,2905 }, - {2834,2548,3859 ,2860,2813,7820 }, {1991,3842,6802 ,7707,7808,7807 }, - {2654,1991,6802 ,2773,7707,7807 }, {3842,1991,153 ,7808,7707,663 }, - {7834,7833,7809 ,7390,7392,7449 }, {7255,6167,6234 ,7238,5974,5973 }, - {7361,6304,6083 ,7836,7233,3989 }, {7361,7413,6304 ,7836,7830,7233 }, - {7469,7525,7468 ,7831,7821,7809 }, {7641,7640,7585 ,5356,5351,6805 }, - {7668,7667,7582 ,7590,7589,7469 }, {2285,2330,2329 ,3701,3693,3680 }, - {2165,2097,2143 ,1041,846,953 }, {7815,7842,7841 ,5146,7837,7811 }, - {7841,7842,7865 ,7811,7837,7822 }, {7890,7915,7940 ,7833,7838,7823 }, - {7987,8011,8028 ,7824,7839,7825 }, {8011,3003,8056 ,7839,7840,7834 }, - {8028,8011,8056 ,7825,7839,7834 }, {8056,3003,61 ,7834,7840,7812 }, - {7301,3416,6683 ,7835,7841,7827 }, {6683,3416,3827 ,7827,7841,7815 }, - {3416,3894,3827 ,7841,7842,7815 }, {3827,3894,3692 ,7815,7842,7828 }, - {3692,7762,7817 ,7828,7829,7816 }, {7760,6760,7762 ,7843,5117,7829 }, - {7760,2814,6760 ,7843,5255,5117 }, {2049,2096,2048 ,758,788,5461 }, - {6760,2814,4636 ,5117,5255,5118 }, {7902,7952,7901 ,7692,5475,7413 }, - {7119,7120,7147 ,7529,6938,6937 }, {7292,7319,7352 ,7594,7432,7595 }, - {7703,7702,7653 ,7667,3513,7638 }, {2834,2616,2548 ,2860,2812,2813 }, - {7625,7679,7624 ,7139,7674,7439 }, {7414,7413,7361 ,7844,7830,7836 }, - {7526,7525,7469 ,7845,7821,7831 }, {7526,7585,7525 ,7845,6805,7821 }, - {4583,4556,7788 ,5498,5497,7313 }, {7865,7842,7890 ,7822,7837,7833 }, - {7915,7965,7940 ,7838,7846,7823 }, {7940,7965,7987 ,7823,7846,7824 }, - {3301,1097,61 ,7847,7826,7812 }, {3003,3301,61 ,7840,7847,7812 }, - {1097,7811,7301 ,7826,7848,7835 }, {5035,7762,3692 ,7849,7829,7828 }, - {3894,5035,3692 ,7842,7849,7828 }, {5035,6846,7762 ,7849,7850,7829 }, - {401,2334,7712 ,1387,7851,5350 }, {7749,7750,7748 ,7663,7687,7644 }, - {7121,7132,7120 ,7852,7621,6938 }, {7132,7148,7120 ,7621,6936,6938 }, - {7829,7880,7857 ,7614,7531,7533 }, {7438,7437,3321 ,7351,5938,2461 }, - {7345,7372,7311 ,7588,4373,4334 }, {2484,7713,7668 ,3499,7489,7590 }, - {7949,7974,7925 ,7591,4171,7294 }, {6278,7106,7092 ,3967,7573,3968 }, - {7243,7289,7242 ,7632,7575,7627 }, {7596,7595,7533 ,7701,4019,7039 }, - {7140,7141,7161 ,7415,7414,2559 }, {7770,7824,7747 ,4272,3770,331 }, - {7291,7316,7350 ,3774,5948,7618 }, {7336,6083,6014 ,7853,3989,6592 }, - {7336,7361,6083 ,7853,7836,3989 }, {7470,7469,7413 ,7854,7831,7830 }, - {7414,7470,7413 ,7844,7854,7830 }, {7557,7585,7526 ,7855,6805,7845 }, - {7557,7616,7585 ,7855,7856,6805 }, {7616,7641,7585 ,7856,5356,6805 }, - {7658,7657,7605 ,7505,7417,5239 }, {7300,7299,7276 ,7857,7694,3696 }, - {3306,7300,7276 ,3695,7857,3696 }, {7868,4557,7818 ,7858,5310,5309 }, - {2281,2282,2327 ,3626,3663,3627 }, {6705,5439,7815 ,4158,4157,5146 }, - {7842,7866,7890 ,7837,7859,7833 }, {7866,7915,7890 ,7859,7838,7833 }, - {7965,7988,8011 ,7846,7860,7839 }, {7987,7965,8011 ,7824,7846,7839 }, - {3301,7812,7811 ,7847,7861,7848 }, {1097,3301,7811 ,7826,7847,7848 }, - {7811,7812,7301 ,7848,7861,7835 }, {6745,7762,6846 ,7862,7829,7850 }, - {7762,6745,7760 ,7829,7862,7843 }, {6745,3789,2814 ,7862,7863,5255 }, - {7760,6745,2814 ,7843,7862,5255 }, {3789,1973,4906 ,7863,551,4895 }, - {2814,3789,4906 ,5255,7863,4895 }, {7750,7800,7772 ,7687,7568,7744 }, - {7194,7193,7147 ,1670,7647,6937 }, {8006,8005,7956 ,7407,7527,7403 }, - {7300,7330,7299 ,7857,7796,7694 }, {7408,7407,7357 ,7864,7706,7597 }, - {7358,7408,7357 ,7797,7864,7597 }, {7434,7433,7407 ,7806,7799,7706 }, - {7408,7434,7407 ,7864,7806,7706 }, {7493,7492,7434 ,7865,7718,7806 }, - {7493,7550,7492 ,7865,7725,7718 }, {7362,7361,7336 ,7866,7836,7853 }, - {7362,7414,7361 ,7866,7844,7836 }, {7470,7526,7469 ,7854,7845,7831 }, - {7616,7671,7641 ,7856,7867,5356 }, {7641,7671,4579 ,5356,7867,5355 }, - {7815,5440,7842 ,5146,5147,7837 }, {7842,5440,5410 ,7837,5147,4285 }, - {7867,7916,7915 ,7868,7869,7838 }, {7866,7867,7915 ,7859,7868,7838 }, - {7916,7941,7915 ,7869,7870,7838 }, {7915,7941,7965 ,7838,7870,7846 }, - {7988,8029,3003 ,7860,7871,7840 }, {8011,7988,3003 ,7839,7860,7840 }, - {3003,4030,3301 ,7840,7872,7847 }, {7812,8026,7301 ,7861,7873,7835 }, - {8026,3991,3416 ,7873,7874,7841 }, {7301,8026,3416 ,7835,7873,7841 }, - {3991,3481,3416 ,7874,7875,7841 }, {3481,8042,3894 ,7875,399,7842 }, - {3416,3481,3894 ,7841,7875,7842 }, {3789,1852,1973 ,7863,7876,551 }, - {8036,4589,4588 ,7327,776,775 }, {7400,7455,7428 ,7580,7579,7670 }, - {7121,7120,7094 ,7852,6938,4317 }, {7957,8006,7956 ,7385,7407,7403 }, - {7859,7908,7858 ,7598,7366,7368 }, {7611,7687,7664 ,7810,7877,7611 }, - {7687,2333,7664 ,7877,3537,7611 }, {7302,6014,6259 ,7878,6592,6718 }, - {7302,7336,6014 ,7878,7853,6592 }, {7415,7414,7362 ,7879,7844,7866 }, - {7501,7526,7470 ,7880,7845,7854 }, {7558,7557,7526 ,7881,7855,7845 }, - {7501,7558,7526 ,7880,7881,7845 }, {7672,7671,7616 ,5450,7867,7856 }, - {7671,7672,4579 ,7867,5450,5355 }, {6531,8134,8120 ,2280,3263,906 }, - {7676,7720,7699 ,7461,4271,3951 }, {5410,7867,7866 ,4285,7868,7859 }, - {7842,5410,7866 ,7837,4285,7859 }, {7916,7942,7941 ,7869,7882,7870 }, - {7942,7966,7965 ,7882,7883,7846 }, {7941,7942,7965 ,7870,7882,7846 }, - {7966,7988,7965 ,7883,7860,7846 }, {8029,4622,4030 ,7871,7884,7872 }, - {3003,8029,4030 ,7840,7871,7872 }, {4030,4622,3301 ,7872,7884,7847 }, - {3481,6294,3894 ,7875,7885,7842 }, {8042,3481,3894 ,399,7875,7842 }, - {6294,7670,5035 ,7885,7886,7849 }, {3894,6294,5035 ,7842,7885,7849 }, - {936,3933,935 ,5234,7678,5264 }, {7400,7428,7375 ,7580,7670,7699 }, - {8006,2285,8005 ,7407,3701,7527 }, {7685,7756,7730 ,7518,7768,4413 }, - {7708,7707,7660 ,7388,7382,7381 }, {7857,7879,7856 ,7533,7532,4635 }, - {7884,7883,7833 ,7391,7452,7392 }, {7363,7362,7336 ,7887,7866,7853 }, - {7302,7363,7336 ,7878,7887,7853 }, {7440,7470,7414 ,7888,7854,7844 }, - {7415,7440,7414 ,7879,7888,7844 }, {7440,7501,7470 ,7888,7880,7854 }, - {7617,7616,7557 ,7889,7856,7855 }, {7558,7617,7557 ,7881,7889,7855 }, - {7685,7706,7658 ,7518,7693,7505 }, {7867,5410,5411 ,7868,4285,4284 }, - {7555,7554,3351 ,7832,7786,1444 }, {7949,7995,7974 ,7591,4135,4171 }, - {4264,7837,3740 ,5540,5967,7639 }, {7942,7989,7988 ,7882,7890,7860 }, - {7966,7942,7988 ,7883,7882,7860 }, {2950,3301,4622 ,7891,7847,7884 }, - {2950,7812,3301 ,7891,7861,7847 }, {7812,2950,8026 ,7861,7891,7873 }, - {3481,7670,6294 ,7875,7886,7885 }, {7670,6604,6846 ,7886,7892,7850 }, - {5035,7670,6846 ,7849,7886,7850 }, {6604,5042,6745 ,7892,7893,7862 }, - {6846,6604,6745 ,7850,7892,7862 }, {5042,7737,3789 ,7893,7894,7863 }, - {6745,5042,3789 ,7862,7893,7863 }, {7737,7080,1852 ,7894,7895,7876 }, - {3789,7737,1852 ,7863,7894,7876 }, {7080,7810,1973 ,7895,552,551 }, - {1852,7080,1973 ,7876,7895,551 }, {8004,8025,7980 ,7404,7510,4253 }, - {7685,7658,7631 ,7518,7505,7504 }, {7352,7319,7377 ,7595,7432,7592 }, - {7622,7650,7595 ,7460,7237,4019 }, {7730,7756,7777 ,4413,7768,4411 }, - {7295,7324,7323 ,7562,7617,74 }, {7706,7685,7730 ,7693,7518,4413 }, - {7294,7295,7323 ,7819,7562,74 }, {6234,6167,6259 ,5973,5974,6718 }, - {6167,7302,6259 ,5974,7878,6718 }, {7363,7415,7362 ,7887,7879,7866 }, - {7502,7501,7440 ,7896,7880,7888 }, {7502,7558,7501 ,7896,7881,7880 }, - {7617,7672,7616 ,7889,5450,7856 }, {1210,5017,4144 ,4739,5092,4761 }, - {4562,8057,1381 ,5018,7897,5193 }, {4839,4730,5038 ,5484,5438,3628 }, - {7867,5418,7916 ,7868,3471,7869 }, {7916,5418,7942 ,7869,3471,7882 }, - {7989,8012,7988 ,7890,7898,7860 }, {8030,8029,7988 ,7899,7871,7860 }, - {8012,8030,7988 ,7898,7899,7860 }, {8030,4622,8029 ,7899,7884,7871 }, - {2950,7466,8026 ,7891,7900,7873 }, {7466,3991,8026 ,7900,7874,7873 }, - {1312,3481,3991 ,7901,7875,7874 }, {7466,1312,3991 ,7900,7901,7874 }, - {3481,3937,7670 ,7875,7902,7886 }, {5042,7063,7737 ,7893,7903,7894 }, - {7737,7063,7080 ,7894,7903,7895 }, {7928,7977,7927 ,1724,1714,1716 }, - {7225,7267,7245 ,1672,4331,7506 }, {7958,7957,7932 ,3855,7385,7408 }, - {7632,7685,7631 ,7555,7518,7504 }, {7615,7614,7554 ,2816,7788,7786 }, - {7555,7615,7554 ,7832,2816,7786 }, {7615,7582,7614 ,2816,7469,7788 }, - {2485,7668,7582 ,2815,7590,7469 }, {7615,2485,7582 ,2816,2815,7469 }, - {8051,8060,8037 ,6172,7685,4528 }, {6319,6304,3533 ,6789,7233,192 }, - {7666,7690,7635 ,7566,6466,7646 }, {2485,2484,7668 ,2815,3499,7590 }, - {7303,7302,6167 ,7904,7878,5974 }, {7389,7415,7363 ,7905,7879,7887 }, - {7441,7440,7415 ,7906,7888,7879 }, {7389,7441,7415 ,7905,7906,7879 }, - {7559,7558,7502 ,7907,7881,7896 }, {7642,7672,7617 ,7908,5450,7889 }, - {7642,4555,7672 ,7908,5496,5450 }, {4555,7716,7672 ,5496,5549,5450 }, - {2484,2451,7713 ,3499,2102,7489 }, {5411,5418,7867 ,4284,3471,7868 }, - {7315,7375,7314 ,7496,7699,7909 }, {5418,7917,7942 ,3471,4920,7882 }, - {8012,7989,8030 ,7898,7890,7899 }, {3843,4622,8030 ,7677,7884,7899 }, - {7740,3937,3481 ,7910,7902,7875 }, {1312,7740,3481 ,7901,7910,7875 }, - {3937,7740,7670 ,7902,7910,7886 }, {7810,3862,1494 ,552,550,1083 }, - {5598,8043,5599 ,2216,6397,2213 }, {6706,6741,6705 ,5389,5388,4158 }, - {7267,7266,7245 ,4331,6769,7506 }, {7295,7294,7249 ,7562,7819,7622 }, - {3305,7300,3306 ,879,7857,3695 }, {7331,7330,7300 ,7911,7796,7857 }, - {7331,7358,7330 ,7911,7797,7796 }, {7824,7849,7797 ,3770,7551,3506 }, - {7337,7363,7302 ,7912,7887,7878 }, {7303,7337,7302 ,7904,7912,7878 }, - {7337,7389,7363 ,7912,7905,7887 }, {7441,7502,7440 ,7906,7896,7888 }, - {7586,7617,7558 ,7913,7889,7881 }, {7559,7586,7558 ,7907,7913,7881 }, - {7586,7642,7617 ,7913,7908,7889 }, {7409,7408,7358 ,6245,7864,7797 }, - {7435,7434,7408 ,6244,7806,7864 }, {7409,7435,7408 ,6245,6244,7864 }, - {7613,7688,7634 ,5106,6443,7474 }, {7917,7967,7942 ,4920,7914,7882 }, - {7942,7967,7989 ,7882,7914,7890 }, {3933,4622,3843 ,7678,7884,7677 }, - {3251,2950,4622 ,7915,7891,7884 }, {3933,3251,4622 ,7678,7915,7884 }, - {3251,7466,2950 ,7915,7900,7891 }, {7466,7498,1312 ,7900,7916,7901 }, - {7740,6054,7670 ,7910,7917,7886 }, {6054,7786,7670 ,7917,7918,7886 }, - {7670,7786,6604 ,7886,7918,7892 }, {7786,6845,5042 ,7918,7919,7893 }, - {6604,7786,5042 ,7892,7918,7893 }, {6845,6743,7063 ,7919,7920,7903 }, - {5042,6845,7063 ,7893,7919,7903 }, {6743,1351,7080 ,7920,1162,7895 }, - {7063,6743,7080 ,7903,7920,7895 }, {1351,8008,7810 ,1162,1163,552 }, - {7080,1351,7810 ,7895,1162,552 }, {7810,8008,3862 ,552,1163,550 }, - {4582,4581,7716 ,5478,5430,5549 }, {7534,7596,7533 ,7314,7701,7039 }, - {7676,7699,7650 ,7461,3951,7237 }, {7175,7212,7174 ,2347,7208,7610 }, - {7551,7550,7493 ,5108,7725,7865 }, {7255,7303,6167 ,7238,7904,5974 }, - {7390,7389,7337 ,7921,7905,7912 }, {7503,7502,7441 ,7922,7896,7906 }, - {7503,7559,7502 ,7922,7907,7896 }, {7643,7642,7586 ,7923,7908,7913 }, - {7612,7611,7550 ,5107,7810,7725 }, {7551,7612,7550 ,5108,5107,7725 }, - {860,1183,953 ,1385,1238,1237 }, {8013,7989,7967 ,7924,7890,7914 }, - {8013,8030,7989 ,7924,7899,7890 }, {7816,7466,3251 ,7925,7900,7915 }, - {6198,7498,7466 ,7926,7916,7900 }, {7816,6198,7466 ,7925,7926,7900 }, - {6198,1312,7498 ,7926,7901,7916 }, {8049,2317,8035 ,5393,3129,6675 }, - {7902,7901,7852 ,7692,7413,7412 }, {7175,7174,7151 ,2347,7610,1202 }, - {7665,7687,7611 ,7927,7877,7810 }, {2334,2333,7687 ,7851,3537,7877 }, - {5672,5718,5744 ,5324,6126,6090 }, {7304,7303,7255 ,7928,7904,7238 }, - {7304,7337,7303 ,7928,7912,7904 }, {7442,7441,7389 ,7929,7906,7905 }, - {7390,7442,7389 ,7921,7929,7905 }, {7527,7559,7503 ,7930,7907,7922 }, - {7527,7586,7559 ,7930,7913,7907 }, {7694,4555,7642 ,7931,5496,7908 }, - {7643,7694,7642 ,7923,7931,7908 }, {935,8030,8013 ,5264,7899,7924 }, - {4058,4055,1253 ,4708,3678,2353 }, {7816,3251,3933 ,7925,7915,7678 }, - {6198,7961,1312 ,7926,7932,7901 }, {3565,7740,1312 ,7933,7910,7901 }, - {7961,3565,1312 ,7932,7933,7901 }, {7740,3565,6054 ,7910,7933,7917 }, - {7248,7272,7231 ,73,7666,6944 }, {7291,7350,7349 ,3774,7618,7609 }, - {7350,7376,7349 ,7618,7746,7609 }, {7349,7376,7401 ,7609,7746,7619 }, - {7376,7402,7401 ,7746,6589,7619 }, {8050,8037,3783 ,4529,4528,5311 }, - {7401,7400,7375 ,7619,7580,7699 }, {7192,7191,7170 ,7648,7634,7652 }, - {4126,3141,4176 ,124,4701,4703 }, {6347,6346,7860 ,451,4883,452 }, - {5483,5261,3830 ,171,173,5882 }, {6282,6060,5115 ,6617,5914,5913 }, - {5109,5108,7166 ,4182,4184,5971 }, {1934,1418,22 ,4620,4619,4066 }, - {7891,7918,4558 ,7934,5555,4209 }, {2410,3670,489 ,1,281,3639 }, - {7256,7255,5845 ,7935,7238,143 }, {6120,7256,5845 ,142,7935,143 }, - {7338,7337,7304 ,7936,7912,7928 }, {7338,7390,7337 ,7936,7921,7912 }, - {7442,7503,7441 ,7929,7922,7906 }, {7587,7586,7527 ,7937,7913,7930 }, - {7587,7643,7586 ,7937,7923,7913 }, {7717,4556,4555 ,7938,5497,5496 }, - {7694,7717,4555 ,7931,7938,5496 }, {2317,2275,8035 ,3129,3152,6675 }, - {4558,4557,7891 ,4209,5310,7934 }, {17,15,438 ,5190,497,498 }, - {4423,3672,160 ,4289,4277,4295 }, {4680,160,3672 ,5368,4295,4277 }, - {974,7816,3933 ,5366,7925,7678 }, {936,974,3933 ,5234,5366,7678 }, - {3565,7961,6198 ,7933,7932,7926 }, {3565,6759,6054 ,7933,7939,7917 }, - {6054,6759,7786 ,7917,7939,7918 }, {6759,7738,6845 ,7939,7940,7919 }, - {7786,6759,6845 ,7918,7939,7919 }, {7738,1067,6743 ,7940,7941,7920 }, - {6845,7738,6743 ,7919,7940,7920 }, {1067,1352,1351 ,7941,1262,1162 }, - {6743,1067,1351 ,7920,7941,1162 }, {7627,7652,7626 ,7582,7696,7438 }, - {7315,7314,7265 ,7496,7909,7497 }, {7402,7403,7401 ,6589,6588,7619 }, - {7569,7534,7536 ,7705,7314,5205 }, {5106,7134,5108 ,5926,7942,4184 }, - {7166,7177,7197 ,5971,7943,7234 }, {7197,7177,6120 ,7234,7943,142 }, - {7277,7255,7256 ,7944,7238,7935 }, {7277,7304,7255 ,7944,7928,7238 }, - {7391,7390,7338 ,7945,7921,7936 }, {7471,7503,7442 ,7946,7922,7929 }, - {7528,7527,7503 ,7947,7930,7922 }, {7471,7528,7503 ,7946,7947,7922 }, - {7644,7643,7587 ,7948,7923,7937 }, {7717,7766,4556 ,7938,7949,5497 }, - {4556,7766,7788 ,5497,7949,7313 }, {7891,7868,7918 ,7934,7858,5555 }, - {7918,7968,4560 ,5555,7950,5236 }, {7607,7606,7578 ,7455,7457,7462 }, - {7805,7857,7804 ,7520,7533,4634 }, {7189,7222,7205 ,7493,628,7669 }, - {7652,7702,7651 ,7696,3513,7675 }, {1091,160,4680 ,4597,4295,5368 }, - {974,7335,7816 ,5366,4265,7925 }, {7335,6198,7816 ,4265,7926,7925 }, - {6759,1067,7738 ,7939,7941,7940 }, {7483,7482,7426 ,7574,7722,7537 }, - {7605,7604,7542 ,5239,7416,5240 }, {7210,7247,7228 ,7499,7360,7524 }, - {7207,7244,7206 ,7608,7620,7631 }, {7456,7403,7485 ,7578,6588,7636 }, - {7773,7827,7802 ,7668,7410,6566 }, {3303,7385,7333 ,1049,7630,4353 }, - {7094,7120,7093 ,4317,6938,4318 }, {7487,7458,7515 ,5707,7550,1079 }, - {3518,6133,7096 ,4338,4390,4337 }, {6133,5106,7096 ,4390,5926,4337 }, - {7152,5108,7134 ,7951,4184,7942 }, {7152,7166,5108 ,7951,5971,4184 }, - {7213,6120,7177 ,7952,142,7943 }, {7339,7338,7304 ,7953,7936,7928 }, - {7277,7339,7304 ,7944,7953,7928 }, {7416,7442,7390 ,7954,7929,7921 }, - {7391,7416,7390 ,7945,7954,7921 }, {7416,7471,7442 ,7954,7946,7929 }, - {7528,7587,7527 ,7947,7937,7930 }, {7673,7643,7644 ,7955,7923,7948 }, - {7673,7694,7643 ,7955,7931,7923 }, {7673,7717,7694 ,7955,7938,7931 }, - {7819,7818,7788 ,7956,5309,7313 }, {7766,7819,7788 ,7949,7956,7313 }, - {7919,7918,7868 ,7957,5555,7858 }, {7918,7919,7968 ,5555,7957,7950 }, - {4560,7968,4561 ,5236,7950,5361 }, {7857,7856,7804 ,7533,4635,4634 }, - {7430,7458,7487 ,7587,7550,5707 }, {4957,8062,5026 ,2733,3272,4225 }, - {1945,3565,6198 ,7958,7933,7926 }, {7335,1945,6198 ,4265,7958,7926 }, - {3565,1945,6759 ,7933,7958,7939 }, {1067,1353,1352 ,7941,1205,1262 }, - {7118,7131,7105 ,7628,7654,5279 }, {7801,7800,7750 ,1618,7568,7687 }, - {7401,7403,7456 ,7619,6588,7578 }, {7701,7750,7724 ,3515,7687,7760 }, - {7404,7430,7378 ,7726,7587,7431 }, {7429,7430,7487 ,7572,7587,5707 }, - {7132,7165,7164 ,7621,7265,7521 }, {7458,7487,7515 ,7550,5707,1079 }, - {7111,5106,6133 ,7959,5926,4390 }, {7178,7177,7166 ,7960,7943,5971 }, - {7152,7178,7166 ,7951,7960,5971 }, {7234,7256,6120 ,7961,7935,142 }, - {7213,7234,6120 ,7952,7961,142 }, {7234,7277,7256 ,7961,7944,7935 }, - {7339,7391,7338 ,7953,7945,7936 }, {7472,7471,7416 ,7962,7946,7954 }, - {7472,7528,7471 ,7962,7947,7946 }, {7588,7587,7528 ,7963,7937,7947 }, - {7588,7644,7587 ,7963,7948,7937 }, {7718,7717,7673 ,7964,7938,7955 }, - {7718,7766,7717 ,7964,7949,7938 }, {7819,7868,7818 ,7956,7858,5309 }, - {7943,7968,7919 ,7965,7950,7957 }, {7968,8014,4561 ,7950,7966,5361 }, - {7437,7465,7436 ,5938,7581,4245 }, {7188,7204,7187 ,7503,5120,7330 }, - {4582,4556,4583 ,5478,5497,5498 }, {3734,1945,7335 ,7967,7958,4265 }, - {1945,3448,6759 ,7958,7968,7939 }, {3448,5614,6759 ,7968,7969,7939 }, - {5614,1267,1067 ,7969,1353,7941 }, {6759,5614,1067 ,7939,7969,7941 }, - {1067,1267,1353 ,7941,1353,1205 }, {7750,7749,7723 ,7687,7663,7664 }, - {7724,7750,7723 ,7760,7687,7664 }, {7106,7118,7105 ,7573,7628,5279 }, - {7538,7602,7537 ,2014,5867,7543 }, {7403,7486,7485 ,6588,7436,7636 }, - {7927,7952,7902 ,1716,5475,7692 }, {7725,7773,7752 ,7420,7668,3514 }, - {7486,7457,7538 ,7436,1078,2014 }, {7545,7546,7578 ,7398,7397,7462 }, - {7123,7134,5106 ,7970,7942,5926 }, {7111,7123,5106 ,7959,7970,5926 }, - {7123,7152,7134 ,7970,7951,7942 }, {7178,7213,7177 ,7960,7952,7943 }, - {7278,7277,7234 ,7971,7944,7961 }, {7364,7391,7339 ,7972,7945,7953 }, - {7417,7416,7391 ,7973,7954,7945 }, {7364,7417,7391 ,7972,7973,7945 }, - {7529,7528,7472 ,7974,7947,7962 }, {7618,7644,7588 ,7975,7948,7963 }, - {7618,7673,7644 ,7975,7955,7948 }, {7767,7766,7718 ,7976,7949,7964 }, - {7869,7868,7819 ,7977,7858,7956 }, {7869,7919,7868 ,7977,7957,7858 }, - {7990,7968,7943 ,7978,7950,7965 }, {7968,7990,8014 ,7950,7978,7966 }, - {7990,8031,4561 ,7978,7979,5361 }, {8014,7990,4561 ,7966,7978,5361 }, - {4561,8031,4562 ,5361,7979,5018 }, {99,2306,100 ,3743,6405,2823 }, - {8051,278,3783 ,6172,1489,5311 }, {7518,7545,7544 ,7396,7398,7980 }, - {7456,7514,7455 ,7578,3610,7579 }, {7544,7545,7578 ,7980,7398,7462 }, - {7141,7188,7161 ,7414,7503,2559 }, {976,3734,7335 ,782,7967,4265 }, - {5614,7937,1267 ,7969,7981,1353 }, {7570,7598,7569 ,5204,7140,7705 }, - {7598,7624,7597 ,7140,7439,7700 }, {7679,7700,7678 ,7674,7698,7425 }, - {7780,7809,7779 ,7389,7449,7447 }, {7084,7111,6133 ,4391,7959,4390 }, - {7153,7152,7123 ,7982,7951,7970 }, {7214,7213,7178 ,7983,7952,7960 }, - {7235,7234,7213 ,7984,7961,7952 }, {7214,7235,7213 ,7983,7984,7952 }, - {7340,7339,7277 ,7985,7953,7944 }, {7278,7340,7277 ,7971,7985,7944 }, - {7340,7364,7339 ,7985,7972,7953 }, {7417,7472,7416 ,7973,7962,7954 }, - {7589,7588,7528 ,7986,7963,7947 }, {7529,7589,7528 ,7974,7986,7947 }, - {7589,7618,7588 ,7986,7975,7963 }, {7674,7673,7618 ,7987,7955,7975 }, - {7674,7718,7673 ,7987,7964,7955 }, {7789,7819,7766 ,7988,7956,7949 }, - {7767,7789,7766 ,7976,7988,7949 }, {7892,7919,7869 ,7989,7957,7977 }, - {7944,7943,7919 ,7990,7965,7957 }, {7892,7944,7919 ,7989,7990,7957 }, - {8032,8031,7990 ,7991,7979,7978 }, {8031,8032,4562 ,7979,7991,5018 }, - {8008,1428,3862 ,1163,1126,550 }, {8032,8057,4562 ,7991,7897,5018 }, - {7657,7729,7728 ,7417,7697,7818 }, {1029,3734,976 ,3930,7967,782 }, - {7499,1945,3734 ,7992,7958,7967 }, {1029,7499,3734 ,3930,7992,7967 }, - {7499,4297,3448 ,7992,7993,7968 }, {1945,7499,3448 ,7958,7992,7968 }, - {7624,7623,7597 ,7439,7427,7700 }, {7724,7723,7678 ,7760,7664,7425 }, - {7700,7724,7678 ,7698,7760,7425 }, {7131,7118,7144 ,7654,7628,7653 }, - {7486,7538,7485 ,7436,2014,7636 }, {7602,7601,7537 ,5867,7544,7543 }, - {7799,7798,7771 ,4727,4728,4336 }, {7853,7852,7800 ,1617,7412,7568 }, - {7485,7537,7514 ,7636,7543,3610 }, {7273,7325,7296 ,3467,7465,7471 }, - {7147,7163,7194 ,6937,5460,1670 }, {7097,7111,7084 ,7994,7959,4391 }, - {7097,7123,7111 ,7994,7970,7959 }, {7179,7178,7152 ,7995,7960,7951 }, - {7153,7179,7152 ,7982,7995,7951 }, {7235,7278,7234 ,7984,7971,7961 }, - {7365,7364,7340 ,7996,7972,7985 }, {7473,7472,7417 ,7997,7962,7973 }, - {7473,7529,7472 ,7997,7974,7962 }, {7619,7618,7589 ,7998,7975,7986 }, - {7695,7718,7674 ,7999,7964,7987 }, {7695,7767,7718 ,7999,7976,7964 }, - {7843,7819,7789 ,8000,7956,7988 }, {7843,7869,7819 ,8000,7977,7956 }, - {7843,7892,7869 ,8000,7989,7977 }, {7944,7990,7943 ,7990,7978,7965 }, - {8032,1381,8057 ,7991,5193,7897 }, {8025,2281,8024 ,7510,3626,7511 }, - {7355,7383,7325 ,7435,7512,7465 }, {7222,7221,7205 ,628,7545,7669 }, - {4578,4577,7641 ,5353,5352,5356 }, {4297,6695,5614 ,7993,8001,7969 }, - {3448,4297,5614 ,7968,7993,7969 }, {6695,7887,7937 ,8001,8002,7981 }, - {5614,6695,7937 ,7969,8001,7981 }, {7887,1314,1267 ,8002,1409,1353 }, - {7937,7887,1267 ,7981,8002,1353 }, {7722,7771,7721 ,7645,4336,7454 }, - {7598,7597,7569 ,7140,7700,7705 }, {7901,7951,7900 ,7413,7546,6922 }, - {7758,7780,7779 ,7387,7389,7447 }, {2487,7615,7555 ,4427,2816,7832 }, - {7085,7084,6186 ,8003,4391,4365 }, {7082,7085,6186 ,6462,8003,4365 }, - {7085,7097,7084 ,8003,7994,4391 }, {7124,7123,7097 ,8004,7970,7994 }, - {7124,7153,7123 ,8004,7982,7970 }, {7198,7178,7179 ,8005,7960,7995 }, - {7198,7214,7178 ,8005,7983,7960 }, {7279,7278,7235 ,8006,7971,7984 }, - {7279,7340,7278 ,8006,7985,7971 }, {7418,7417,7364 ,8007,7973,7972 }, - {7365,7418,7364 ,7996,8007,7972 }, {7504,7529,7473 ,8008,7974,7997 }, - {7504,7589,7529 ,8008,7986,7974 }, {7741,7767,7695 ,8009,7976,7999 }, - {7741,7789,7767 ,8009,7988,7976 }, {7893,7892,7843 ,8010,7989,8000 }, - {7991,7990,7944 ,8011,7978,7990 }, {7991,8032,7990 ,8011,7991,7978 }, - {8032,8058,1381 ,7991,8012,5193 }, {3304,7300,3305 ,1050,7857,879 }, - {1029,978,3671 ,3930,1620,3089 }, {5437,6705,6740 ,6259,4158,6894 }, - {1178,342,4075 ,206,4542,4521 }, {7679,7678,7625 ,7674,7425,7139 }, - {7570,7569,7536 ,5204,7705,5205 }, {7624,7679,7625 ,7439,7674,7139 }, - {7681,7703,7654 ,7459,7667,7637 }, {7827,7826,7802 ,7410,1616,6566 }, - {3304,7331,7300 ,1050,7911,7857 }, {7359,7358,7331 ,7686,7797,7911 }, - {7098,7097,7085 ,8013,7994,8003 }, {7154,7153,7124 ,8014,7982,8004 }, - {7154,7179,7153 ,8014,7995,7982 }, {7236,7235,7214 ,8015,7984,7983 }, - {7198,7236,7214 ,8005,8015,7983 }, {7305,7340,7279 ,8016,7985,8006 }, - {7305,7365,7340 ,8016,7996,7985 }, {7418,7473,7417 ,8007,7997,7973 }, - {7590,7589,7504 ,8017,7986,8008 }, {7790,7789,7741 ,8018,7988,8009 }, - {7790,7843,7789 ,8018,8000,7988 }, {7945,7944,7892 ,8019,7990,7989 }, - {7893,7945,7892 ,8010,8019,7989 }, {8015,8032,7991 ,8020,7991,8011 }, - {8015,8058,8032 ,8020,8012,7991 }, {213,2379,4163 ,3606,3238,4863 }, - {7850,7900,7849 ,3769,6922,7551 }, {7359,7409,7358 ,7686,6245,7797 }, - {7119,7146,7145 ,7529,7530,7629 }, {7854,7903,7878 ,7514,7717,7411 }, - {3671,7499,1029 ,3089,7992,3930 }, {3671,1077,4297 ,3089,2694,7993 }, - {7499,3671,4297 ,7992,3089,7993 }, {1077,308,4297 ,2694,8021,7993 }, - {4297,308,6695 ,7993,8021,8001 }, {308,4047,7887 ,8021,8022,8002 }, - {6695,308,7887 ,8001,8021,8002 }, {7887,4047,1314 ,8002,8022,1409 }, - {2403,3191,2165 ,2904,2903,1041 }, {7107,7118,7106 ,7494,7628,7573 }, - {8041,2281,8025 ,7548,3626,7510 }, {7494,7493,7434 ,4309,7865,7806 }, - {7435,7494,7434 ,6244,4309,7806 }, {8004,8041,8025 ,7404,7548,7510 }, - {5946,7085,7082 ,5481,8003,6462 }, {7125,7124,7097 ,8023,8004,7994 }, - {7098,7125,7097 ,8013,8023,7994 }, {7180,7179,7154 ,8024,7995,8014 }, - {7180,7198,7179 ,8024,8005,7995 }, {7280,7279,7235 ,8025,8006,7984 }, - {7236,7280,7235 ,8015,8025,7984 }, {7280,7305,7279 ,8025,8016,8006 }, - {7366,7365,7305 ,8026,7996,8016 }, {7366,7418,7365 ,8026,8007,7996 }, - {7443,7473,7418 ,8027,7997,8007 }, {7505,7504,7473 ,8028,8008,7997 }, - {7443,7505,7473 ,8027,8028,7997 }, {7505,7560,7504 ,8028,8029,8008 }, - {7560,7590,7504 ,8029,8017,8008 }, {7742,7741,7695 ,8030,8009,7999 }, - {7844,7843,7790 ,8031,8000,8018 }, {7844,7893,7843 ,8031,8010,8000 }, - {7945,7991,7944 ,8019,8011,7990 }, {8047,8058,8015 ,8032,8012,8020 }, - {8047,6921,1381 ,8032,8033,5193 }, {8058,8047,1381 ,8012,8032,5193 }, - {2487,7555,3377 ,4427,7832,1443 }, {7634,7611,7612 ,7474,7810,5107 }, - {7542,7604,7541 ,5240,7416,7515 }, {4047,7638,1314 ,8022,7540,1409 }, - {8030,935,3843 ,7899,5264,7677 }, {3298,4518,1879 ,7424,1281,1283 }, - {6277,7107,6278 ,4387,7494,3967 }, {7776,7830,7806 ,4412,7770,7612 }, - {7982,2306,8007 ,3853,6405,3854 }, {7634,7665,7611 ,7474,7927,7810 }, - {7688,7687,7665 ,6443,7877,7927 }, {7634,7688,7665 ,7474,6443,7927 }, - {7083,7085,5946 ,5483,8003,5481 }, {7083,7098,7085 ,5483,8013,8003 }, - {7155,7154,7124 ,8034,8014,8004 }, {7125,7155,7124 ,8023,8034,8004 }, - {7215,7198,7180 ,8035,8005,8024 }, {7199,7215,7180 ,8036,8035,8024 }, - {7257,7236,7198 ,8037,8015,8005 }, {7215,7257,7198 ,8035,8037,8005 }, - {7281,7280,7236 ,8038,8025,8015 }, {7257,7281,7236 ,8037,8038,8015 }, - {7306,7305,7280 ,8039,8016,8025 }, {7281,7306,7280 ,8038,8039,8025 }, - {7419,7418,7366 ,8040,8007,8026 }, {7419,7443,7418 ,8040,8027,8007 }, - {7505,7561,7560 ,8028,8041,8029 }, {7791,7790,7741 ,8042,8018,8009 }, - {7742,7791,7741 ,8030,8042,8009 }, {7894,7893,7844 ,8043,8010,8031 }, - {7969,7991,7945 ,8044,8011,8019 }, {8016,8015,7991 ,8045,8020,8011 }, - {7969,8016,7991 ,8044,8045,8011 }, {3785,6921,8047 ,8046,8033,8032 }, - {6921,1169,1381 ,8033,5100,5193 }, {3785,1169,6921 ,8046,5100,8033 }, - {3948,4296,303 ,280,6023,4086 }, {1583,1253,1606 ,5415,2353,3677 }, - {5493,5518,5492 ,6336,6316,6315 }, {7959,2306,7958 ,7607,6405,3855 }, - {5557,5556,5536 ,756,3511,754 }, {1315,1356,1355 ,1627,1530,1408 }, - {308,7638,4047 ,8021,7540,8022 }, {402,7759,448 ,5349,399,1172 }, - {7681,7725,7703 ,7459,7420,7667 }, {7908,7959,7907 ,7366,7607,7367 }, - {4368,68,1005 ,4148,4087,3535 }, {7326,7355,7325 ,7434,7435,7465 }, - {643,644,740 ,3285,1176,1595 }, {7099,7098,7083 ,8047,8013,5483 }, - {7181,7180,7154 ,8048,8024,8014 }, {7155,7181,7154 ,8034,8048,8014 }, - {7181,7199,7180 ,8048,8036,8024 }, {7181,7215,7199 ,8048,8035,8036 }, - {7258,7257,7215 ,8049,8037,8035 }, {7282,7281,7257 ,8050,8038,8037 }, - {7258,7282,7257 ,8049,8050,8037 }, {7282,7306,7281 ,8050,8039,8038 }, - {7282,7307,7306 ,8050,8051,8039 }, {7444,7443,7419 ,8052,8027,8040 }, - {7791,7844,7790 ,8042,8031,8018 }, {7920,7945,7893 ,8053,8019,8010 }, - {7894,7920,7893 ,8043,8053,8010 }, {7920,7969,7945 ,8053,8044,8019 }, - {8048,8047,8015 ,8054,8032,8020 }, {8016,8048,8015 ,8045,8054,8020 }, - {7546,7607,7578 ,7397,7455,7462 }, {3842,153,235 ,7808,663,598 }, - {7638,1274,1315 ,7540,1687,1627 }, {7639,308,1077 ,8055,8021,2694 }, - {1078,7639,1077 ,5060,8055,2694 }, {7639,2993,308 ,8055,3744,8021 }, - {308,2993,7638 ,8021,3744,7540 }, {2993,1274,7638 ,3744,1687,7540 }, - {7624,7625,7570 ,7439,7139,5204 }, {7244,7243,7206 ,7620,7632,7631 }, - {7463,7462,7410 ,7711,4310,3766 }, {7107,7106,6278 ,7494,7573,3967 }, - {7464,7462,7463 ,4246,4310,7711 }, {2306,7982,7958 ,6405,3853,3855 }, - {7126,7125,7098 ,8056,8023,8013 }, {7099,7126,7098 ,8047,8056,8013 }, - {7167,7181,7155 ,8057,8048,8034 }, {7237,7215,7181 ,8058,8035,8048 }, - {7237,7258,7215 ,8058,8049,8035 }, {7237,7282,7258 ,8058,8050,8049 }, - {7341,7307,7282 ,8059,8051,8050 }, {7307,7341,7282 ,8051,8059,8050 }, - {7367,7392,7366 ,8060,8061,8026 }, {7392,7419,7366 ,8061,8040,8026 }, - {7506,7505,7443 ,8062,8028,8027 }, {7444,7506,7443 ,8052,8062,8027 }, - {7506,7562,7561 ,8062,8063,8041 }, {7505,7506,7561 ,8028,8062,8041 }, - {7820,7844,7791 ,8064,8031,8042 }, {7820,7894,7844 ,8064,8043,8031 }, - {7970,7969,7920 ,8065,8044,8053 }, {7970,8016,7969 ,8065,8045,8044 }, - {3721,3785,8047 ,8066,8046,8032 }, {8048,3721,8047 ,8054,8066,8032 }, - {3721,1169,3785 ,8066,5100,8046 }, {4580,4579,7672 ,5410,5355,5450 }, - {3293,7149,7133 ,1988,7263,1989 }, {7542,7541,7488 ,5240,7515,7585 }, - {7959,7958,7907 ,7607,3855,7367 }, {7677,7676,7622 ,7426,7461,7460 }, - {5046,7639,1078 ,5232,8055,5060 }, {5046,2993,7639 ,5232,3744,8055 }, - {489,2955,2473 ,3639,3640,4303 }, {7571,7570,7512 ,5126,5204,5203 }, - {7807,7830,7776 ,7602,7770,4412 }, {7151,7174,7150 ,1202,7610,7488 }, - {7777,7807,7776 ,4411,7602,4412 }, {4557,7868,7891 ,5310,7858,7934 }, - {7776,7755,7729 ,4412,7519,7697 }, {5952,7099,7083 ,6595,8047,5483 }, - {7135,7155,7125 ,8067,8034,8023 }, {7126,7135,7125 ,8056,8067,8023 }, - {7156,7167,7155 ,8068,8057,8034 }, {7135,7156,7155 ,8067,8068,8034 }, - {7167,7156,7181 ,8057,8068,8048 }, {7156,7200,7181 ,8068,8069,8048 }, - {7200,7216,7181 ,8069,8070,8048 }, {7216,7237,7181 ,8070,8058,8048 }, - {7283,7282,7237 ,8071,8050,8058 }, {7283,7307,7282 ,8071,8051,8050 }, - {7307,7368,7341 ,8051,8072,8059 }, {7392,7444,7419 ,8061,8052,8040 }, - {7870,7894,7820 ,8073,8043,8064 }, {7921,7920,7894 ,8074,8053,8043 }, - {7870,7921,7894 ,8073,8074,8043 }, {8017,8016,7970 ,8075,8045,8065 }, - {4563,1169,3721 ,3024,5100,8066 }, {7484,7513,7483 ,3612,3611,7574 }, - {7596,7622,7595 ,7701,7460,4019 }, {7404,7458,7430 ,7726,7550,7587 }, - {7231,7230,7212 ,6944,7498,7208 }, {2243,6552,8053 ,1993,7679,7676 }, - {489,1101,2410 ,3639,2,1 }, {7100,7099,5952 ,8076,8047,6595 }, - {7100,7112,7099 ,8076,8077,8047 }, {7112,7126,7099 ,8077,8056,8047 }, - {7156,7135,7126 ,8068,8067,8056 }, {7135,7156,7126 ,8067,8068,8056 }, - {7156,7201,7200 ,8068,8078,8069 }, {7201,7216,7200 ,8078,8070,8069 }, - {7238,7237,7216 ,8079,8058,8070 }, {7201,7238,7216 ,8078,8079,8070 }, - {7308,7307,7283 ,8080,8051,8071 }, {7308,7368,7307 ,8080,8072,8051 }, - {7445,7444,7392 ,8081,8052,8061 }, {7445,7506,7444 ,8081,8062,8052 }, - {7506,7563,7562 ,8062,8082,8063 }, {7821,7870,7820 ,8083,8073,8064 }, - {7921,7970,7920 ,8074,8065,8053 }, {8033,8048,8016 ,8084,8054,8045 }, - {8017,8033,8016 ,8075,8084,8045 }, {7332,7331,3304 ,6685,7911,1050 }, - {7332,7359,7331 ,6685,7686,7911 }, {7464,7521,7462 ,4246,7657,4310 }, - {7196,7212,7175 ,2401,7208,2347 }, {6108,1274,2993 ,6975,1687,3744 }, - {1172,6108,2993 ,1758,6975,3744 }, {7427,7483,7426 ,7536,7574,7537 }, - {5107,5121,5106 ,4183,5855,5926 }, {7410,7409,7359 ,3766,6245,7686 }, - {7136,7135,7126 ,8085,8067,8056 }, {7112,7136,7126 ,8077,8085,8056 }, - {7136,7157,7156 ,8085,8086,8068 }, {7135,7136,7156 ,8067,8085,8068 }, - {7259,7237,7238 ,8087,8058,8079 }, {7259,7283,7237 ,8087,8071,8058 }, - {7369,7368,7308 ,8088,8072,8080 }, {7369,7393,7368 ,8088,8089,8072 }, - {7474,7506,7445 ,8090,8062,8081 }, {7474,7564,7563 ,8090,8091,8082 }, - {7506,7474,7563 ,8062,8090,8082 }, {7871,7870,7821 ,8092,8073,8083 }, - {7971,7970,7921 ,8093,8065,8074 }, {7971,8017,7970 ,8093,8075,8065 }, - {6340,8048,8033 ,8094,8054,8084 }, {6340,3721,8048 ,8094,8066,8054 }, - {2315,4563,3721 ,3068,3024,8066 }, {6340,2315,3721 ,8094,3068,8066 }, - {7521,7520,7462 ,7657,7658,4310 }, {7828,7827,7773 ,7735,7410,7668 }, - {7412,7463,7410 ,3765,7711,3766 }, {7723,7748,7722 ,7664,7644,7645 }, - {7953,7978,7929 ,7716,7730,7684 }, {7133,7132,7108 ,1989,7621,4111 }, - {7632,7631,7605 ,7555,7504,5239 }, {7552,7551,7493 ,5546,5108,7865 }, - {7494,7552,7493 ,4309,5546,7865 }, {7086,7100,5952 ,6743,8076,6595 }, - {7202,7201,7156 ,8095,8078,8068 }, {7157,7202,7156 ,8086,8095,8068 }, - {7309,7308,7283 ,8096,8080,8071 }, {7259,7309,7283 ,8087,8096,8071 }, - {7394,7393,7369 ,8097,8089,8088 }, {7420,7445,7392 ,8098,8081,8061 }, - {7922,7921,7870 ,8099,8074,8073 }, {7871,7922,7870 ,8092,8099,8073 }, - {7992,8017,7971 ,8100,8075,8093 }, {7992,8033,8017 ,8100,8084,8075 }, - {8024,8040,8002 ,7511,7688,3777 }, {7774,7828,7773 ,7419,7735,7668 }, - {7852,7851,7825 ,7412,6625,3768 }, {2293,2292,2262 ,2561,1983,3359 }, - {7723,7722,7677 ,7664,7645,7426 }, {7678,7723,7677 ,7425,7664,7426 }, - {7314,7374,7348 ,7909,4988,4990 }, {7145,7162,7170 ,7629,7649,7652 }, - {7903,7953,7929 ,7717,7716,7684 }, {8024,8002,8003 ,7511,3777,3776 }, - {8024,8053,8040 ,7511,7676,7688 }, {7576,7632,7605 ,7615,7555,5239 }, - {7688,7712,2334 ,6443,5350,7851 }, {7687,7688,2334 ,7877,6443,7851 }, - {7113,7112,7100 ,8101,8077,8076 }, {7086,7113,7100 ,6743,8101,8076 }, - {7217,7238,7201 ,8102,8079,8078 }, {7202,7217,7201 ,8095,8102,8078 }, - {7217,7259,7238 ,8102,8087,8079 }, {7309,7369,7308 ,8096,8088,8080 }, - {7446,7445,7420 ,8103,8081,8098 }, {7475,7474,7445 ,8104,8090,8081 }, - {7446,7475,7445 ,8103,8104,8081 }, {7474,7530,7564 ,8090,8105,8091 }, - {7822,7871,7821 ,8106,8092,8083 }, {7922,7971,7921 ,8099,8093,8074 }, - {8034,8033,7992 ,7586,8084,8100 }, {8034,6340,8033 ,7586,8094,8084 }, - {2369,2334,401 ,3257,7851,1387 }, {7730,7729,7657 ,4413,7697,7417 }, - {7903,7929,7878 ,7717,7684,7411 }, {7314,7348,7290 ,7909,4990,7635 }, - {7348,7373,7347 ,4990,4989,7576 }, {7800,7852,7825 ,7568,7412,3768 }, - {7772,7748,7722 ,7744,7644,7645 }, {7748,7772,7722 ,7644,7744,7645 }, - {7455,7427,7428 ,7579,7536,7670 }, {7512,7536,7482 ,5203,5205,7722 }, - {7904,7903,7854 ,7513,7717,7514 }, {7165,7172,7195 ,7265,7405,7522 }, - {7576,7605,7543 ,7615,5239,5238 }, {7249,7294,7248 ,7622,7819,73 }, - {7249,7248,7232 ,7622,73,2124 }, {7623,7622,7597 ,7427,7460,7700 }, - {7137,7136,7112 ,8107,8085,8077 }, {7113,7137,7112 ,8101,8107,8077 }, - {7137,7158,7157 ,8107,8108,8086 }, {7136,7137,7157 ,8085,8107,8086 }, - {7158,7182,7157 ,8108,8109,8086 }, {7182,7202,7157 ,8109,8095,8086 }, - {7260,7259,7217 ,8110,8087,8102 }, {7342,7369,7309 ,8111,8088,8096 }, - {7342,7394,7369 ,8111,8097,8088 }, {7531,7530,7474 ,8112,8105,8090 }, - {7475,7531,7474 ,8104,8112,8090 }, {7530,7531,7564 ,8105,8112,8091 }, - {7872,7871,7822 ,8113,8092,8106 }, {7946,7971,7922 ,8114,8093,8099 }, - {7993,7992,7971 ,8115,8100,8093 }, {7946,7993,7971 ,8114,8115,8093 }, - {8059,6340,8034 ,4235,8094,7586 }, {2316,2315,6340 ,3067,3068,8094 }, - {8059,2316,6340 ,4235,3067,8094 }, {2333,2334,2369 ,3537,7851,3257 }, - {7326,7325,7273 ,7434,7465,3467 }, {7425,7481,7454 ,3995,7542,3996 }, - {7231,7272,7230 ,6944,7666,7498 }, {7269,7247,7292 ,7361,7360,7594 }, - {7455,7484,7427 ,7579,3612,7536 }, {8060,8051,3783 ,7685,6172,5311 }, - {7800,7799,7748 ,7568,4727,7644 }, {7772,7800,7748 ,7744,7568,7644 }, - {7483,7512,7482 ,7574,5203,7722 }, {7375,7374,7314 ,7699,4988,7909 }, - {7209,7210,7228 ,7523,7499,7524 }, {7164,7165,7195 ,7521,7265,7522 }, - {7172,7210,7209 ,7405,7499,7523 }, {7195,7172,7209 ,7522,7405,7523 }, - {3321,7386,7387 ,2461,5512,3615 }, {7158,7183,7182 ,8108,8116,8109 }, - {7183,7202,7182 ,8116,8095,8109 }, {7218,7217,7202 ,7708,8102,8095 }, - {7183,7218,7202 ,8116,7708,8095 }, {7284,7309,7259 ,8117,8096,8087 }, - {7260,7284,7259 ,8110,8117,8087 }, {7284,7342,7309 ,8117,8111,8096 }, - {7395,7394,7342 ,8118,8097,8111 }, {7395,7421,7394 ,8118,8119,8097 }, - {7447,7475,7446 ,8120,8104,8103 }, {7591,7564,7531 ,8121,8091,8112 }, - {7895,7871,7872 ,8122,8092,8113 }, {7895,7922,7871 ,8122,8099,8092 }, - {7895,7946,7922 ,8122,8114,8099 }, {8035,8034,7992 ,6675,7586,8100 }, - {7993,8035,7992 ,8115,6675,8100 }, {7629,7705,7684 ,7399,7349,7400 }, - {7692,7691,7666 ,7490,4419,7566 }, {7637,7692,7666 ,7473,7490,7566 }, - {7690,7691,449 ,6466,4419,1169 }, {7929,7928,7877 ,7684,1724,7549 }, - {7378,7377,7319 ,7431,7592,7432 }, {7828,7854,7827 ,7735,7514,7410 }, - {278,4612,3783 ,1489,5249,5311 }, {7800,7825,7799 ,7568,3768,4727 }, - {7572,7571,7513 ,5124,5126,3611 }, {7265,7314,7290 ,7497,7909,7635 }, - {7386,7385,3303 ,5512,7630,1049 }, {7398,7397,7345 ,7429,1186,7588 }, - {7350,7317,7376 ,7618,5947,7746 }, {7317,7351,7376 ,5947,6587,7746 }, - {7114,7113,7086 ,5392,8101,6743 }, {7087,7114,7086 ,6742,5392,6743 }, - {7218,7260,7217 ,7708,8110,8102 }, {7343,7342,7284 ,2750,8111,8117 }, - {7476,7475,7447 ,8123,8104,8120 }, {7592,7591,7531 ,8124,8121,8112 }, - {7793,7822,7792 ,5726,8106,8125 }, {7793,7845,7822 ,5726,5725,8106 }, - {7845,7872,7822 ,5725,8113,8106 }, {7947,7946,7895 ,4354,8114,8122 }, - {7947,7993,7946 ,4354,8115,8114 }, {5644,3615,5660 ,4133,201,3054 }, - {7108,7121,7094 ,4111,7852,4317 }, {7108,7132,7121 ,4111,7621,7852 }, - {7601,7653,7600 ,7544,7638,7556 }, {7523,7522,7496 ,7393,7395,7470 }, - {7775,7828,7774 ,7509,7735,7419 }, {7480,7534,7511 ,7463,7314,6677 }, - {7348,7347,7289 ,4990,7576,7575 }, {7651,7702,7680 ,7675,3513,7673 }, - {7702,7701,7680 ,3513,3515,7673 }, {8053,6552,6784 ,7676,7679,7472 }, - {8053,6784,8039 ,7676,7472,7476 }, {8040,8053,8039 ,7688,7676,7476 }, - {7775,7774,7726 ,7509,7419,7418 }, {7378,7430,7377 ,7431,7587,7592 }, - {7095,7108,7094 ,3924,4111,4317 }, {6552,2243,2278 ,7679,1993,3495 }, - {7114,7127,7137 ,5392,7245,8107 }, {7113,7114,7137 ,8101,5392,8107 }, - {7127,7159,7158 ,7245,5075,8108 }, {7137,7127,7158 ,8107,7245,8108 }, - {7184,7183,7158 ,5074,8116,8108 }, {7159,7184,7158 ,5075,5074,8108 }, - {7261,7260,7218 ,1528,8110,7708 }, {7261,7284,7260 ,1528,8117,8110 }, - {7396,7395,7342 ,6716,8118,8111 }, {7343,7396,7342 ,2750,6716,8111 }, - {7507,7531,7475 ,6706,8112,8104 }, {7476,7507,7475 ,8123,6706,8104 }, - {7593,7592,7531 ,5032,8124,8112 }, {7896,7895,7872 ,4355,8122,8113 }, - {7845,7896,7872 ,5725,4355,8113 }, {7994,7993,7947 ,5582,8115,4354 }, - {7142,7189,7141 ,7729,7493,7414 }, {7725,7752,7702 ,7420,3514,3513 }, - {7682,7681,7628 ,3564,7459,7528 }, {7412,7411,7384 ,3765,3767,7683 }, - {7952,7951,7901 ,5475,7546,7413 }, {7572,7626,7599 ,5124,7438,5125 }, - {7190,7224,7189 ,7633,7491,7493 }, {7482,7536,7481 ,7722,5205,7542 }, - {7536,7534,7481 ,5205,7314,7542 }, {7660,7659,7606 ,7381,7383,7457 }, - {7088,7101,7087 ,4299,4073,6742 }, {7101,7114,7087 ,4073,5392,6742 }, - {7219,7218,7183 ,1529,7708,8116 }, {7184,7219,7183 ,5074,1529,8116 }, - {7285,7284,7261 ,2751,8117,1528 }, {7285,7343,7284 ,2751,2750,8117 }, - {7422,7395,7396 ,1404,8118,6716 }, {7449,7448,7395 ,7600,8126,8118 }, - {7422,7449,7395 ,1404,7600,8118 }, {7449,7477,7448 ,7600,1403,8126 }, - {7565,7531,7507 ,5080,8112,6706 }, {7531,7565,7593 ,8112,5080,5032 }, - {7565,7593,7592 ,5080,5032,8124 }, {7593,7565,7592 ,5032,5080,8124 }, - {7593,7645,7592 ,5032,5034,8124 }, {7896,7947,7895 ,4355,4354,8122 }, - {8018,8035,7993 ,6676,6675,8115 }, {7994,8018,7993 ,5582,6676,8115 }, - {5658,5642,5659 ,49,526,3053 } -}; -F32 vertices [8146][3] = { -{-0.128951f,0.113893f,0.0385904f},{-0.183541f,0.121141f,0.0176007f},{-0.3356f,0.267483f,0.245606f}, -{0.0990868f,0.131443f,0.150773f},{-0.0330697f,0.108447f,0.0577602f},{-0.0525675f,0.103219f,0.0581332f}, -{-0.129472f,0.111128f,0.0174721f},{0.321716f,0.0172985f,0.188251f},{0.350352f,0.00204496f,0.112787f}, -{-0.0496094f,0.0212148f,0.245825f},{0.284245f,0.0735475f,0.150304f},{-0.296733f,0.306228f,-0.0948973f}, -{0.364127f,-0.0330021f,0.152767f},{0.399804f,-0.11262f,0.0755796f},{-0.10546f,0.41535f,-0.00363974f}, -{-0.407308f,0.165107f,0.333295f},{-0.354815f,0.10291f,0.350047f},{-0.415655f,0.1722f,0.323829f}, -{0.154706f,0.137848f,0.112556f},{0.363426f,-0.0163596f,0.0577281f},{0.344481f,-0.0712067f,-0.05722f}, -{-0.47357f,0.192405f,0.152844f},{-0.433269f,-0.00220573f,0.173326f},{-0.336095f,0.297592f,0.112524f}, -{-0.431192f,0.27075f,0.192f},{0.136655f,0.102968f,0.212456f},{0.172493f,0.0906273f,0.222398f}, -{-0.0907206f,0.100833f,0.0403717f},{-0.212472f,0.206855f,0.0920485f},{-0.390158f,0.166136f,0.336426f}, -{0.0818719f,0.1177f,0.189036f},{-0.181875f,0.080962f,0.174573f},{-0.396981f,-0.110421f,-0.0611684f}, -{0.263654f,0.0893219f,0.152702f},{-0.469351f,0.0768914f,0.0379088f},{0.367586f,-0.0504292f,-0.00258511f}, -{0.373245f,-0.0554837f,0.00500307f},{-0.449931f,0.0420887f,0.0196393f},{-0.104874f,0.430758f,-0.0208675f}, -{-0.392389f,0.230186f,0.285811f},{-0.0333784f,0.0170798f,0.258757f},{-0.0197004f,0.434816f,-0.137706f}, -{-0.131067f,0.378811f,-0.0749943f},{-0.091563f,0.102653f,0.0566477f},{0.175284f,0.113482f,0.190798f}, -{-0.190955f,0.149211f,0.11763f},{-0.0545288f,0.10055f,0.0386676f},{0.216376f,0.0805312f,0.210058f}, -{0.231906f,0.0788913f,0.208501f},{0.328282f,0.00609625f,0.189769f},{0.313646f,0.0244043f,0.192785f}, -{-0.149188f,0.359988f,0.0320504f},{-0.466033f,0.134156f,0.0375229f},{-0.234677f,0.209048f,0.17485f}, -{0.385283f,-0.0772258f,0.133456f},{0.382538f,-0.0547828f,0.0755796f},{0.370396f,-0.091817f,0.193274f}, -{-0.244857f,0.283997f,0.11408f},{-0.047275f,0.301502f,-0.0171956f},{-0.337446f,0.231304f,0.282833f}, -{-0.358197f,0.206437f,0.307669f},{-0.338095f,-0.305122f,0.135758f},{-0.470593f,0.0595544f,0.0379023f}, -{-0.377309f,0.116614f,0.351275f},{-0.30036f,0.324536f,-0.112678f},{-0.115717f,0.107591f,0.0241086f}, -{-0.190904f,0.150715f,0.0987171f},{-0.335529f,0.291643f,0.0379345f},{-0.484316f,0.0419215f,0.264172f}, -{0.152538f,0.102801f,0.211254f},{0.153812f,0.0916176f,0.225215f},{-0.103138f,0.104698f,0.03515f}, -{-0.297048f,0.295598f,0.0953281f},{0.30371f,0.0399987f,0.18802f},{-0.0883798f,0.41845f,-0.0178644f}, -{0.363773f,-0.092788f,-0.0374522f},{-0.352076f,0.35386f,-0.0974052f},{0.0567345f,0.0797466f,0.236108f}, -{0.294335f,-0.114228f,0.291643f},{0.0244911f,0.341809f,-0.0740619f},{-0.166352f,0.361223f,0.0361275f}, -{-0.0908749f,0.0966914f,0.0203724f},{-0.166281f,0.380907f,0.020636f},{-0.0159899f,0.0208225f,0.260622f}, -{-0.474798f,0.157956f,0.154394f},{-0.395045f,0.398315f,-0.246642f},{-0.390222f,0.11662f,0.352786f}, -{-0.444934f,-0.0373236f,0.0384554f},{-0.215192f,0.210083f,0.100627f},{0.345137f,0.00202567f,0.153461f}, -{0.308855f,-0.0536767f,0.250738f},{-0.108379f,0.398566f,-0.000488712f},{-0.377316f,0.201955f,0.313469f}, -{-0.375985f,0.234179f,0.285843f},{-0.145947f,0.392849f,0.0136523f},{0.358667f,-0.0149577f,0.13352f}, -{-0.436697f,-0.00081026f,0.0371307f},{0.399283f,-0.148799f,0.133494f},{0.212189f,0.114485f,0.152053f}, -{0.103035f,-0.451593f,0.311926f},{0.120237f,-0.454622f,0.308067f},{0.00337286f,0.115282f,0.039375f}, -{-0.222993f,0.303122f,0.0742677f},{-0.475924f,0.161339f,0.133758f},{-0.120218f,0.428983f,-0.0409697f}, -{0.369708f,-0.0305328f,0.035433f},{-0.482419f,0.11327f,0.230475f},{0.367676f,-0.0728915f,-0.0214784f}, -{0.377534f,-0.0744864f,-0.000623756f},{0.401881f,-0.148638f,0.0949037f},{-0.0891837f,0.436147f,-0.0219092f}, -{-0.0690814f,0.436153f,-0.035285f},{-0.358043f,0.0803189f,0.354516f},{-0.358088f,0.233304f,0.28655f}, -{-0.279319f,0.131166f,0.262429f},{0.385425f,-0.113488f,0.172059f},{0.0420919f,0.1286f,0.114562f}, -{0.00494195f,0.120401f,0.0939713f},{-0.337381f,0.293245f,0.0591042f},{0.192235f,0.0807048f,0.225292f}, -{0.040645f,0.109752f,0.187454f},{-0.315466f,0.284177f,0.210495f},{-0.355651f,0.284788f,0.207601f}, -{0.230845f,0.0689946f,0.221376f},{-0.16468f,0.398669f,-0.0160702f},{-0.17619f,0.390978f,-0.014932f}, -{-0.262805f,-0.44967f,0.30907f},{-0.242143f,-0.449754f,0.311906f},{-0.374988f,0.341597f,-0.0915855f}, -{-0.352281f,0.342844f,-0.0752323f},{0.356976f,-0.0173049f,0.150998f},{0.362082f,-0.0148741f,0.0948973f}, -{0.380981f,-0.114093f,-0.0206617f},{-0.184435f,0.345584f,0.0454455f},{-0.16558f,0.340909f,0.0421787f}, -{-0.165709f,-0.44913f,0.288852f},{-0.337619f,0.365731f,-0.130787f},{-0.0571911f,-0.128935f,0.325623f}, -{-0.451268f,0.191614f,0.0565062f},{0.00446607f,0.0954117f,0.188926f},{-0.123356f,0.411967f,-0.0662551f}, -{-0.464734f,-0.114524f,0.0774059f},{-0.306225f,0.300492f,-0.0590077f},{-0.299421f,0.28583f,-0.0583004f}, -{-0.354731f,0.187467f,-0.0365647f},{-0.341722f,0.209897f,0.299611f},{-0.372673f,0.355731f,-0.115045f}, -{-0.385148f,0.355165f,-0.137288f},{0.300727f,0.0312016f,0.203305f},{0.36657f,-0.0529501f,0.171216f}, -{0.351934f,-0.00997397f,0.15514f},{0.369599f,-0.0327063f,0.114176f},{0.00206744f,0.0217871f,0.263509f}, -{-0.356776f,0.0418636f,0.346581f},{0.198151f,0.0257355f,0.254262f},{0.193315f,0.0190926f,0.258641f}, -{-0.427719f,0.00261084f,0.190399f},{-0.126475f,0.393608f,0.0137423f},{-0.296354f,0.0562426f,-0.0510337f}, -{-0.347265f,0.359448f,-0.298396f},{-0.479319f,0.22715f,0.16903f},{0.0213916f,0.0220122f,0.268834f}, -{0.136095f,-0.453715f,0.305103f},{-0.0334105f,0.025877f,0.251471f},{-0.467107f,0.112029f,0.0377737f}, -{0.173676f,0.0207453f,0.26203f},{-0.388287f,0.395016f,-0.225517f},{0.286335f,0.0563905f,0.187177f}, -{0.310623f,-0.0532523f,-0.0824153f},{0.380795f,-0.0549564f,0.0370728f},{0.11616f,0.0201923f,0.278827f}, -{-0.316392f,0.233002f,0.268442f},{-0.356088f,0.115495f,0.345635f},{0.0212115f,0.12235f,0.0571364f}, -{0.137176f,0.139404f,0.078647f},{-0.300958f,0.264255f,0.243015f},{0.331607f,0.0236327f,0.15067f}, -{0.251944f,-0.450976f,0.265889f},{0.216614f,-0.108614f,0.324626f},{0.0391209f,0.0243529f,0.272146f}, -{0.350159f,0.00359473f,0.094788f},{-0.396447f,-0.0920678f,-0.0587827f},{0.375457f,-0.0619272f,0.15114f}, -{0.378859f,-0.0548986f,0.11417f},{-0.389926f,0.0207453f,-0.016649f},{-0.39327f,0.168007f,-0.0230282f}, -{0.154043f,-0.0357931f,-0.124575f},{0.175965f,-0.0327128f,-0.11952f},{0.175354f,0.128549f,0.150741f}, -{0.13623f,0.136156f,0.134343f},{0.0985016f,0.136529f,0.115032f},{-0.131163f,0.107514f,0.129051f}, -{-0.108649f,0.108993f,0.0566349f},{-0.0375068f,0.0839973f,0.175737f},{-0.298868f,0.291508f,0.0583325f}, -{-0.192608f,0.14804f,0.134388f},{-0.146976f,0.401826f,-5.1427e-05f},{-0.260792f,-0.433774f,0.302235f}, -{-0.242266f,-0.432822f,0.30464f},{-0.223154f,-0.432128f,0.300621f},{0.0791068f,0.13325f,0.0571686f}, -{-0.472972f,0.157127f,0.17238f},{0.0598019f,0.0811099f,-0.0603196f},{-0.202678f,-0.451117f,0.299971f}, -{-0.185881f,-0.450905f,0.295753f},{-0.180223f,-0.444307f,0.292126f},{-0.295923f,0.435491f,-0.378264f}, -{-0.316868f,0.344902f,-0.112511f},{-0.109878f,0.101553f,0.133636f},{-0.413f,0.264339f,0.224707f}, -{-0.317749f,0.291528f,0.0568664f},{0.0993119f,0.117025f,0.191344f},{-0.468773f,-0.114106f,0.115938f}, -{0.247809f,0.0718048f,0.209132f},{-0.314025f,0.307527f,-0.0538375f},{-0.106804f,0.102318f,0.0163725f}, -{-0.108328f,0.0983956f,0.000623793f},{-0.124166f,0.104466f,-0.00183272f},{-0.430954f,-0.0187196f,0.0181731f}, -{0.359516f,-0.056757f,0.189949f},{0.360924f,-0.0149963f,0.114183f},{-0.203379f,0.301547f,0.0699592f}, -{0.043931f,0.0177872f,0.276653f},{-0.127337f,0.404874f,0.00522815f},{0.377033f,-0.0575802f,0.132555f}, -{0.396801f,-0.148973f,0.15276f},{-0.0911514f,0.443073f,-0.0347513f},{-0.0342529f,0.307598f,-0.0218771f}, -{0.250387f,0.0819974f,0.190296f},{0.0618919f,0.132337f,0.0979776f},{0.370126f,-0.0498955f,0.154092f}, -{-0.0687534f,0.038256f,0.2229f},{-0.0509598f,0.0395936f,0.228051f},{-0.0323752f,0.0376065f,0.242114f}, -{-0.0172824f,0.0393235f,0.24722f},{-0.376454f,0.0422173f,0.34442f},{0.101807f,-0.434835f,0.310807f}, -{0.117163f,-0.4376f,0.307746f},{0.137092f,-0.437143f,0.30435f},{0.157136f,-0.433986f,0.301926f}, -{-0.221996f,0.323739f,0.0583004f},{-0.204993f,0.322929f,0.060056f},{-0.183592f,0.321295f,0.0548343f}, -{0.0599755f,0.0178001f,0.277393f},{-0.371612f,0.134066f,0.346529f},{-0.279319f,0.293347f,0.151384f}, -{0.30418f,0.0563133f,0.149526f},{0.351574f,-0.0295231f,-0.00458504f},{-0.42516f,0.173576f,0.00514455f}, -{0.34978f,-0.0516575f,-0.0410598f},{-0.128784f,0.378316f,0.0167776f},{-0.374165f,0.0802225f,0.35258f}, -{0.335857f,-0.0209511f,-0.0349313f},{0.348609f,-0.0342754f,-0.0208739f},{0.403219f,-0.148555f,0.0756182f}, -{-0.35369f,-0.109778f,-0.0902029f},{-0.334462f,-0.109334f,-0.0956625f},{-0.376461f,-0.110086f,-0.0797852f}, -{-0.0688499f,0.453792f,-0.0389762f},{-0.059101f,0.454365f,-0.0439343f},{-0.241449f,0.312247f,0.0639594f}, -{-0.275866f,0.297437f,0.0577409f},{-0.2969f,0.288724f,0.040526f},{-0.12452f,0.104569f,0.134652f}, -{-0.0449085f,0.106698f,0.07684f},{-0.352564f,0.250764f,0.267805f},{-0.335652f,0.284769f,0.209781f}, -{-0.391682f,-0.0901193f,0.243947f},{0.358397f,-0.0700492f,-0.0368027f},{-0.455037f,0.160406f,0.031118f}, -{-0.207244f,-0.435819f,0.296801f},{-0.202177f,-0.427382f,0.290885f},{-0.184473f,-0.42978f,0.288955f}, -{-0.167245f,-0.431086f,0.284589f},{-0.145227f,-0.433259f,0.278943f},{-0.336938f,0.340073f,-0.0762226f}, -{-0.328333f,0.331713f,-0.0703771f},{-0.316347f,0.321379f,-0.0740426f},{-0.0900582f,-0.471773f,0.341359f}, -{-0.0745475f,-0.4318f,0.343346f},{-0.0611524f,-0.427793f,0.349043f},{-0.0524774f,-0.43423f,0.355699f}, -{-0.0348703f,-0.432513f,0.355416f},{0.174666f,0.132105f,0.133629f},{-0.183251f,0.359828f,0.0370985f}, -{0.249275f,0.0920421f,0.17031f},{0.0221054f,-0.433337f,0.336742f},{0.0347738f,-0.433311f,0.332144f}, -{0.0444905f,-0.433279f,0.329301f},{0.0604707f,-0.433246f,0.324092f},{0.0795826f,-0.433169f,0.317919f}, -{0.0923282f,-0.433124f,0.313591f},{0.174236f,-0.432449f,0.299399f},{0.231996f,0.02189f,0.248693f}, -{0.00507055f,0.11743f,0.131449f},{-0.234034f,0.228925f,0.155545f},{-0.29074f,0.399447f,-0.339243f}, -{-0.127562f,0.398585f,-0.0706215f},{-0.237796f,0.133533f,0.232089f},{-0.313929f,0.1495f,0.285862f}, -{0.381296f,-0.054815f,0.0948651f},{-0.145844f,0.379223f,0.0246809f},{-0.121864f,0.413382f,-0.00367832f}, -{-0.491158f,0.0606347f,0.172946f},{0.400736f,-0.130665f,0.0370921f},{0.0808816f,0.0201151f,0.278145f}, -{-0.186242f,0.340336f,-0.0426803f},{-0.156436f,0.340195f,-0.0631619f},{-0.224472f,-0.266313f,0.252731f}, -{-0.128655f,-0.114562f,-0.142401f},{-0.35713f,0.187769f,0.318234f},{-0.429134f,0.265882f,0.206386f}, -{-0.286734f,0.292544f,0.0523327f},{-0.331015f,0.359448f,-0.337012f},{0.100733f,0.101122f,0.215254f}, -{-0.0531848f,0.10417f,0.0738497f},{-0.14904f,0.117983f,0.0386033f},{-0.373393f,0.250249f,0.26711f}, -{-0.354667f,0.335179f,-0.0613485f},{-0.268779f,-0.426436f,0.293341f},{-0.139356f,-0.426771f,0.273869f}, -{0.0225041f,0.1217f,0.133989f},{-0.0367802f,0.104563f,0.0376901f},{-0.0706633f,0.0942864f,0.0189769f}, -{-0.0814154f,-0.420617f,0.334337f},{-0.0697952f,-0.416919f,0.335648f},{-0.0543552f,-0.414475f,0.34179f}, -{-0.0346131f,-0.419498f,0.351449f},{-0.0170702f,-0.416186f,0.34624f},{0.00188095f,-0.415935f,0.338169f}, -{0.0178869f,-0.420668f,0.334645f},{0.0227227f,-0.414559f,0.328748f},{0.040407f,-0.417208f,0.323945f}, -{0.0598148f,-0.417697f,0.317784f},{0.0748561f,-0.419485f,0.314562f},{0.0834925f,-0.416739f,0.312395f}, -{0.0984952f,-0.418398f,0.310614f},{0.117794f,-0.423285f,0.306016f},{0.137111f,-0.423485f,0.305263f}, -{0.156243f,-0.418347f,0.308177f},{0.0988103f,0.076422f,0.243336f},{0.155008f,0.0210347f,0.268956f}, -{-0.411038f,0.251838f,0.247265f},{-0.410543f,0.241999f,0.261284f},{-0.391753f,0.24632f,0.26695f}, -{-0.177489f,0.115868f,0.134214f},{-0.411945f,0.133436f,0.346246f},{-0.219327f,0.284595f,0.0821324f}, -{0.352751f,-0.0704608f,-0.0463586f},{0.398987f,-0.130729f,0.114176f},{0.116977f,-0.0373878f,-0.131128f}, -{-0.368583f,-0.130304f,-0.0862995f},{-0.3536f,-0.12826f,-0.09237f},{-0.334333f,-0.127784f,-0.0978939f}, -{0.273512f,0.0615929f,0.194058f},{-0.141073f,0.358805f,0.025742f},{0.0806308f,0.134607f,0.114048f}, -{-0.364178f,0.164824f,0.332523f},{0.155439f,0.131526f,0.151584f},{-0.31679f,0.289103f,0.040764f}, -{-0.321736f,0.316562f,-0.055458f},{-0.0391209f,-0.127449f,0.348703f},{-0.112816f,0.109611f,0.0410405f}, -{0.0798013f,-0.0758304f,0.343391f},{0.0260024f,0.107771f,0.182155f},{0.0239188f,0.112517f,0.170445f}, -{0.419128f,-0.26322f,0.170818f},{-0.261185f,-0.414012f,0.28657f},{-0.241172f,-0.412675f,0.287026f}, -{-0.223064f,-0.41227f,0.283753f},{-0.203772f,-0.416855f,0.282943f},{-0.184422f,-0.416925f,0.28019f}, -{-0.16511f,-0.417112f,0.276949f},{-0.152256f,-0.417196f,0.276872f},{-0.144545f,-0.412295f,0.271689f}, -{-0.130668f,-0.413524f,0.27093f},{-0.33933f,0.299868f,0.00039872f},{-0.454754f,0.269998f,0.156593f}, -{-0.355445f,0.290942f,0.187936f},{-0.373869f,0.292685f,0.171158f},{0.0630687f,-0.0750779f,0.341372f}, -{0.301582f,0.036359f,-0.0174399f},{-0.484972f,0.043542f,0.227652f},{-0.0878975f,-0.413839f,0.323211f}, -{-0.0364779f,-0.410373f,0.346343f},{0.117851f,-0.412816f,0.316787f},{0.137098f,-0.412019f,0.321604f}, -{0.153001f,-0.412225f,0.319591f},{0.17729f,-0.415324f,0.301707f},{-0.12652f,0.0335809f,0.221356f}, -{-0.107344f,0.0338317f,0.222655f},{-0.206402f,0.0359345f,0.223974f},{0.0985531f,0.0223723f,0.27902f}, -{0.386113f,-0.167943f,-0.0205717f},{0.059982f,0.0266615f,0.27331f},{-0.324829f,0.228083f,0.278538f}, -{-0.143298f,0.411575f,-0.0170091f},{0.230079f,0.0996624f,0.170142f},{0.00333428f,0.11907f,0.0747822f}, -{-0.0342851f,0.0952124f,0.1515f},{0.367097f,-0.0327707f,0.133501f},{0.305479f,-0.430276f,0.202965f}, -{0.325279f,-0.432378f,0.19436f},{-0.201129f,0.301309f,-0.0341211f},{-0.0687792f,0.41562f,-0.106234f}, -{0.285402f,-0.132542f,-0.130118f},{-0.0900582f,-0.113913f,-0.148195f},{-0.470496f,0.263483f,0.113199f}, -{0.327536f,0.0208932f,0.172078f},{0.349542f,-0.0149705f,0.174387f},{-0.0496415f,0.456307f,-0.0524292f}, -{0.284888f,-0.115135f,0.298344f},{0.155542f,0.11745f,0.189016f},{-0.0713836f,0.10136f,0.0766085f}, -{-0.462342f,0.0766857f,0.0217871f},{0.0812353f,-0.0669496f,0.329192f},{-0.304309f,0.282583f,-0.0389569f}, -{-0.0486705f,-0.130169f,0.338889f},{-0.20372f,-0.40854f,0.273586f},{-0.184428f,-0.408527f,0.272853f}, -{-0.16513f,-0.408553f,0.27212f},{-0.0558985f,-0.105444f,-0.178663f},{-0.0901482f,0.0915726f,0.00273948f}, -{-0.0718337f,-0.401498f,0.322466f},{-0.0522974f,-0.402662f,0.335115f},{-0.0364715f,-0.402199f,0.338381f}, -{-0.017321f,-0.402579f,0.335468f},{0.0019967f,-0.39737f,0.321578f},{0.0213466f,-0.399299f,0.320356f}, -{0.0406707f,-0.398978f,0.322536f},{0.0598727f,-0.40427f,0.318427f},{0.0792611f,-0.403119f,0.324491f}, -{0.0985981f,-0.40245f,0.327597f},{0.117851f,-0.402424f,0.330645f},{0.135806f,-0.396515f,0.336793f}, -{0.143484f,-0.402212f,0.331796f},{0.156288f,-0.402759f,0.327501f},{0.174705f,-0.396778f,0.321295f}, -{-0.393251f,0.175088f,0.330491f},{-0.266657f,0.267921f,0.222089f},{0.0760586f,0.0268223f,0.274274f}, -{-0.374171f,0.17521f,0.329848f},{-0.355291f,0.17002f,0.325764f},{-0.338089f,0.249195f,0.268101f}, -{-0.320527f,0.247998f,0.262757f},{-0.415784f,-0.109244f,-0.0416578f},{-0.395386f,0.21018f,0.303913f}, -{-0.373586f,0.262403f,0.248712f},{0.32452f,-0.413421f,0.205273f},{0.343323f,-0.413382f,0.207029f}, -{0.362815f,-0.411697f,0.208823f},{-0.164191f,0.322324f,0.0389441f},{0.313955f,0.0451368f,0.154735f}, -{-0.158712f,0.3742f,0.0292852f},{0.359059f,-0.0334201f,0.173133f},{-0.0546896f,0.469323f,-0.0593164f}, -{-0.0400727f,0.470847f,-0.0605961f},{0.0435195f,0.129552f,0.0968393f},{-0.0157969f,0.114247f,0.0771229f}, -{0.117157f,0.11671f,0.191499f},{0.174936f,0.0809942f,0.231182f},{-0.335555f,0.259117f,0.257201f}, -{-0.375753f,0.328079f,-0.0684286f},{-0.333819f,0.326189f,-0.0550915f},{0.079872f,0.130202f,0.15094f}, -{-0.299608f,0.266673f,-0.01771f},{-0.319523f,0.302595f,-0.0367641f},{-0.338307f,0.319167f,-0.0382881f}, -{-0.259654f,-0.401923f,0.265567f},{-0.243262f,-0.398174f,0.263728f},{-0.223244f,-0.399826f,0.264204f}, -{-0.202125f,-0.398084f,0.262995f},{-0.184454f,-0.395691f,0.263561f},{-0.165162f,-0.39537f,0.266602f}, -{-0.145806f,-0.395466f,0.26576f},{-0.125568f,-0.396727f,0.264596f},{-0.33623f,0.347474f,-0.0918877f}, -{-0.320829f,0.338645f,-0.0939005f},{-0.312437f,0.328272f,-0.0923378f},{-0.0557121f,-0.394527f,0.325006f}, -{-0.0364844f,-0.394418f,0.326536f},{-0.0172053f,-0.394688f,0.322485f},{0.0600206f,-0.393145f,0.332857f}, -{0.0792804f,-0.393036f,0.334504f},{0.0985531f,-0.392971f,0.336433f},{0.117832f,-0.39245f,0.340124f}, -{0.156339f,-0.393138f,0.334845f},{0.0406707f,0.0795794f,0.232591f},{0.398286f,-0.112762f,0.0371114f}, -{-0.471802f,0.0156329f,0.116536f},{-0.261165f,0.293142f,0.0956368f},{-0.389849f,0.0793929f,0.346034f}, -{-0.306746f,0.246095f,0.256661f},{-0.298302f,0.250976f,0.25041f},{-0.280772f,0.250551f,0.242918f}, -{-0.46694f,0.210668f,0.209247f},{-0.390171f,0.13925f,0.349031f},{0.0996849f,0.137295f,0.0955082f}, -{0.392878f,-0.114517f,0.148445f},{0.387965f,-0.0771036f,0.114151f},{-0.167509f,0.121359f,0.0380695f}, -{-0.292045f,0.246281f,-0.0204173f},{-0.305653f,0.278718f,-0.0245008f},{-0.330192f,0.311321f,-0.0337481f}, -{-0.353953f,0.322311f,-0.0375743f},{-0.241449f,-0.391994f,0.247227f},{0.133755f,0.0177294f,0.27601f}, -{-0.225064f,-0.391692f,0.245606f},{-0.206987f,-0.391981f,0.254288f},{-0.108894f,-0.390431f,0.265098f}, -{-0.12789f,0.110627f,0.113739f},{0.061924f,-0.0584675f,0.301945f},{-0.164365f,0.120048f,0.0967365f}, -{-0.355046f,0.31082f,-0.0183531f},{-0.0707984f,-0.389782f,0.304061f},{-0.0525739f,-0.386701f,0.310003f}, -{-0.0365165f,-0.386663f,0.310286f},{-0.0172696f,-0.386669f,0.309868f},{-0.00438252f,-0.386444f,0.312517f}, -{0.00376513f,-0.378708f,0.321295f},{0.0171538f,-0.381898f,0.327108f},{0.0257387f,-0.378104f,0.334478f}, -{0.0406578f,-0.379377f,0.337777f},{0.0599562f,-0.378895f,0.342388f},{0.077647f,-0.380804f,0.345982f}, -{0.0985402f,-0.38256f,0.350053f},{0.117819f,-0.380734f,0.347635f},{0.137118f,-0.382952f,0.347024f}, -{0.156358f,-0.378824f,0.342722f},{0.174801f,-0.375428f,0.33705f},{-0.293306f,0.246314f,-0.0577537f}, -{-0.475679f,0.0771036f,0.0506543f},{-0.0662712f,-0.117623f,-0.186586f},{-0.344249f,0.391113f,-0.324369f}, -{-0.370872f,0.116569f,0.349307f},{-0.281731f,0.208894f,-0.0190347f},{-0.5f,0.077991f,0.134201f}, -{0.324681f,0.0208289f,3.86026e-05f},{-0.229899f,0.187364f,0.172335f},{0.0615768f,0.0731359f,0.242584f}, -{0.0284267f,0.126562f,0.092563f},{-0.288219f,0.291238f,-0.147847f},{-0.4415f,-0.0193434f,0.147532f}, -{-0.467686f,0.192663f,0.210759f},{-0.0701425f,0.0924215f,0.13388f},{-0.0727147f,0.0979968f,0.0376001f}, -{0.323954f,-0.394007f,0.213292f},{0.342847f,-0.391544f,0.213176f},{0.360828f,-0.392122f,0.216964f}, -{0.383348f,-0.393878f,0.215118f},{-0.147085f,0.03751f,0.223375f},{-0.458689f,0.0765956f,0.0121733f}, -{-0.302842f,0.289637f,-0.244211f},{-0.340597f,0.0807112f,0.352227f},{0.289364f,-0.0535095f,0.267676f}, -{-0.454792f,0.0574001f,0.0140382f},{-0.0704125f,0.457548f,-0.0739462f},{-0.186518f,0.37503f,0.0173564f}, -{-0.296971f,0.266294f,-0.0383975f},{-0.372493f,-0.095058f,0.263213f},{0.211855f,0.119019f,0.132903f}, -{0.00716052f,-0.077483f,0.319726f},{0.051172f,0.127642f,0.144291f},{-0.146391f,0.0978682f,0.152773f}, -{-0.220929f,-0.380824f,0.227735f},{-0.216241f,-0.383261f,0.241401f},{-0.203604f,-0.379036f,0.247278f}, -{-0.190981f,-0.378271f,0.254436f},{-0.181271f,-0.377904f,0.258603f},{-0.16522f,-0.377525f,0.26302f}, -{-0.145799f,-0.377525f,0.263201f},{-0.126462f,-0.377846f,0.259464f},{-0.107247f,-0.382239f,0.256744f}, -{0.322456f,0.0356259f,0.151847f},{-0.0665799f,-0.379133f,0.281078f},{-0.0513521f,-0.377171f,0.28455f}, -{-0.039391f,-0.375152f,0.289258f},{-0.0298029f,-0.374271f,0.298216f},{-0.0215267f,-0.374946f,0.305939f}, -{-0.0127359f,-0.370882f,0.315977f},{0.0824057f,-0.373371f,0.353378f},{0.0985209f,-0.372914f,0.357963f}, -{0.117813f,-0.373351f,0.35251f},{0.137092f,-0.373358f,0.352329f},{0.382756f,-0.0957782f,-0.00136328f}, -{0.394679f,-0.112903f,0.133449f},{-0.389026f,0.097283f,0.348497f},{-0.431128f,0.0196778f,0.305193f}, -{-0.480425f,0.0439793f,0.0615414f},{-0.359651f,0.151455f,0.335198f},{-0.371837f,0.151802f,0.340677f}, -{-0.390171f,0.152741f,0.343436f},{-0.485364f,0.0427446f,0.0765506f},{0.0792932f,0.075213f,0.243651f}, -{0.211874f,0.12071f,0.11453f},{0.368583f,-0.386328f,0.219067f},{0.378274f,-0.386367f,0.21906f}, -{-0.337825f,0.0426482f,0.344201f},{0.249532f,0.0981319f,0.152027f},{0.249686f,0.101135f,0.132594f}, -{0.362307f,-0.0139031f,0.0751937f},{0.00535994f,0.324311f,-0.055323f},{-0.38965f,0.24396f,-0.0210861f}, -{-0.449429f,0.0762419f,-0.000784522f},{-0.313697f,0.286287f,-0.0181602f},{0.191984f,0.110247f,0.189306f}, -{0.00652389f,0.103746f,0.175383f},{-0.165008f,0.392573f,0.00273305f},{-0.35423f,0.294576f,0.173287f}, -{-0.148886f,0.0846597f,0.176888f},{0.207527f,0.0906723f,0.204206f},{-0.107138f,-0.373834f,0.252796f}, -{-0.0889843f,-0.372149f,0.249992f},{-0.304759f,0.320221f,-0.0956882f},{-0.127182f,0.0754059f,0.190611f}, -{-0.108315f,0.0574837f,0.2049f},{-0.0575576f,-0.37148f,0.267104f},{-0.00203533f,-0.363551f,0.329006f}, -{0.00388088f,-0.355828f,0.334677f},{0.0214687f,-0.361326f,0.336883f},{0.0406707f,-0.360625f,0.344819f}, -{0.055667f,-0.361725f,0.348799f},{0.0642198f,-0.358439f,0.352214f},{0.0792354f,-0.359455f,0.35694f}, -{0.0985466f,-0.358947f,0.36204f},{0.117877f,-0.3595f,0.356509f},{0.137105f,-0.359757f,0.353866f}, -{0.156378f,-0.360297f,0.347982f},{0.175998f,-0.359152f,0.34514f},{0.0201054f,0.075766f,0.229575f}, -{-0.297833f,0.399453f,-0.320176f},{-0.259847f,0.0370792f,0.247214f},{0.138578f,0.0243465f,0.272544f}, -{-0.165155f,0.0381917f,0.222835f},{-0.403103f,0.116607f,0.351969f},{0.347426f,-0.0926658f,-0.0576316f}, -{-0.0582393f,0.457156f,-0.0944021f},{-0.298656f,0.344491f,-0.167519f},{-0.411636f,0.151661f,0.338497f}, -{0.0989132f,0.137185f,0.0767821f},{-0.409958f,0.207415f,0.298743f},{0.293691f,0.0650204f,0.156272f}, -{-0.0904505f,0.103559f,0.0759075f},{0.330603f,-0.375428f,0.21762f},{0.34297f,-0.373139f,0.213922f}, -{0.360808f,-0.371872f,0.216546f},{0.368557f,-0.377094f,0.219009f},{0.381483f,-0.372547f,0.219614f}, -{-0.43289f,0.251657f,0.227395f},{-0.313485f,0.255098f,0.254429f},{0.330089f,-0.0531044f,0.230706f}, -{0.30589f,0.0219928f,0.206784f},{0.38237f,-0.0548664f,0.0563198f},{-0.0897302f,0.0956175f,0.133906f}, -{-0.0879168f,0.0444037f,0.215536f},{-0.184441f,0.134401f,0.132915f},{0.19945f,0.101727f,0.198026f}, -{0.193605f,0.0958233f,0.20917f},{-0.354416f,0.26648f,0.245555f},{-0.279338f,0.282004f,0.207678f}, -{-0.355966f,0.29347f,0.0546414f},{-0.354564f,0.296814f,0.0935083f},{-0.201322f,-0.362393f,0.24315f}, -{-0.188229f,-0.361403f,0.253625f},{-0.179206f,-0.358567f,0.258988f},{-0.165188f,-0.359371f,0.263709f}, -{-0.145793f,-0.359403f,0.263953f},{-0.126449f,-0.359776f,0.259644f},{-0.107138f,-0.360413f,0.252352f}, -{-0.0891901f,-0.360021f,0.246384f},{-0.280136f,0.293167f,0.0344748f},{-0.461268f,0.0941706f,0.0216842f}, -{-0.336494f,0.291135f,0.190476f},{-0.0558021f,-0.358059f,0.265053f},{-0.0402142f,-0.356979f,0.28747f}, -{-0.0301695f,-0.353416f,0.302608f},{-0.0231215f,-0.359397f,0.309508f},{-0.0135977f,-0.353481f,0.321681f}, -{-0.410967f,0.0985306f,0.345783f},{-0.396711f,0.102801f,0.350773f},{-0.184447f,0.0336581f,0.224674f}, -{-0.111086f,0.423491f,-0.0134915f},{-0.415186f,0.115971f,0.347674f},{-0.128141f,0.419125f,-0.017665f}, -{-0.290862f,0.286274f,-0.096402f},{0.0463683f,0.437201f,-0.168316f},{0.342841f,-0.1328f,-0.0902865f}, -{0.392421f,-0.0813864f,0.0755796f},{0.356911f,-0.0199286f,0.0177872f},{-0.467024f,-0.0708209f,0.0964471f}, -{0.120951f,0.0267194f,0.27448f},{0.39419f,-0.368914f,0.21897f},{0.215842f,0.0233819f,0.252313f}, -{0.273377f,0.0811099f,0.153577f},{-0.455577f,-0.0533295f,0.056577f},{-0.372969f,0.334041f,-0.0745571f}, -{-0.0136491f,0.113437f,0.0573872f},{-0.357477f,0.241433f,0.278769f},{-0.318064f,0.290531f,0.191627f}, -{-0.335851f,0.278583f,0.225047f},{0.0617826f,0.127681f,0.153719f},{-0.296881f,0.294119f,0.0776695f}, -{-0.319003f,0.265046f,0.24715f},{-0.373631f,0.290094f,0.039928f},{0.00397734f,0.118176f,0.0573101f}, -{-0.260651f,0.281702f,0.190142f},{-0.0537893f,0.0964342f,0.0183338f},{-0.0705605f,-0.359037f,0.248056f}, -{0.0407607f,0.122652f,0.154567f},{-0.0711907f,0.0987235f,0.114543f},{-0.393714f,0.293534f,0.135147f}, -{-0.398814f,0.379653f,-0.243941f},{-0.390955f,-0.0752966f,-0.0572136f},{0.0226327f,0.119938f,0.0371885f}, -{0.0990097f,0.135777f,0.0580818f},{0.118314f,0.138742f,0.0781197f},{0.193155f,0.128774f,0.0961062f}, -{0.00290986f,-0.337115f,0.339397f},{0.021816f,-0.338973f,0.343912f},{0.0373589f,-0.346973f,0.348111f}, -{0.0422784f,-0.339674f,0.352953f},{0.0599305f,-0.341577f,0.356876f},{0.0792289f,-0.34159f,0.357185f}, -{0.0984695f,-0.341307f,0.36004f},{0.117845f,-0.341372f,0.359075f},{0.13715f,-0.341886f,0.354227f}, -{0.154841f,-0.339893f,0.35168f},{0.159619f,-0.346709f,0.349931f},{0.174454f,-0.341944f,0.348754f}, -{0.137098f,0.0797274f,0.234539f},{0.117382f,0.0827112f,0.233671f},{0.370126f,-0.168323f,-0.0577537f}, -{0.174396f,0.134163f,0.114022f},{-0.37837f,0.0210347f,-0.026295f},{-0.108617f,0.021382f,0.232044f}, -{-0.244954f,0.282261f,0.132735f},{0.134796f,0.118331f,0.188508f},{0.080155f,0.135185f,0.0954117f}, -{0.154551f,0.135873f,0.13087f},{-0.0725411f,0.100325f,0.0573808f},{0.344815f,-0.357931f,0.214012f}, -{0.362172f,-0.354844f,0.214276f},{0.379804f,-0.352291f,0.21708f},{0.384653f,-0.358947f,0.218803f}, -{0.397688f,-0.359384f,0.218797f},{-0.3901f,0.188508f,0.322067f},{-0.0718144f,-0.112768f,-0.167171f}, -{-0.0893123f,0.0619337f,-0.0559532f},{-0.196769f,0.307952f,0.0660493f},{-0.0233851f,-0.14159f,0.366895f}, -{0.234459f,0.103842f,0.15258f},{0.360461f,-0.0519726f,-0.020398f},{0.29238f,0.0452911f,0.195595f}, -{-0.375309f,0.320311f,-0.0550657f},{-0.316623f,0.297585f,0.135912f},{-0.372744f,0.282557f,0.208784f}, -{-0.280592f,0.294556f,0.0755024f},{0.0436802f,0.118305f,0.171557f},{0.288746f,0.0260571f,-0.0418957f}, -{-0.129755f,0.113996f,0.0951288f},{-0.180081f,-0.338169f,0.260667f},{-0.1652f,-0.341037f,0.266506f}, -{-0.145831f,-0.340947f,0.267734f},{-0.126501f,-0.341346f,0.263303f},{-0.111459f,-0.340401f,0.257233f}, -{-0.102952f,-0.343745f,0.252982f},{-0.0870229f,-0.340838f,0.24742f},{-0.0707341f,-0.340246f,0.248249f}, -{-0.0517122f,0.0829363f,0.168059f},{0.210993f,0.110421f,0.169995f},{-0.413405f,0.28711f,0.152953f}, -{-0.497717f,0.108736f,0.150156f},{0.0393395f,0.123218f,0.0331629f},{0.23114f,0.112247f,0.112472f}, -{-0.130822f,0.421536f,-0.0386354f},{-0.0864056f,0.0360117f,0.224057f},{-0.0163243f,0.0771229f,0.207485f}, -{-0.00137296f,0.0807627f,0.209697f},{-0.330996f,0.341243f,-0.336857f},{-0.0541558f,0.100904f,0.114061f}, -{-0.126514f,0.0204431f,0.231858f},{-0.301518f,0.303784f,-0.0767113f},{-0.0140221f,0.0699463f,0.219871f}, -{0.364416f,-0.0319861f,0.0164239f},{-0.224549f,0.247941f,0.115726f},{0.274798f,-0.0509115f,0.27484f}, -{0.00366868f,0.07248f,0.224784f},{-0.287434f,0.264455f,0.238854f},{0.370429f,-0.0281663f,0.0755924f}, -{-0.486129f,0.0955853f,0.211286f},{0.402524f,-0.352805f,0.217183f},{-0.392492f,-0.263876f,0.0960741f}, -{-0.373374f,0.363596f,-0.131899f},{-0.14733f,0.115932f,0.0203659f},{0.0461496f,0.417517f,-0.14905f}, -{-0.033507f,0.477419f,-0.0714961f},{0.0626121f,-0.0658371f,0.325713f},{-0.375084f,-0.149693f,0.26284f}, -{-0.239854f,-0.356509f,0.0785891f},{0.0611009f,0.100196f,0.210476f},{-0.317877f,0.29727f,0.153204f}, -{0.0992733f,-0.0652519f,0.325842f},{-0.278129f,0.268152f,0.230417f},{-0.015932f,0.110305f,0.0387448f}, -{-0.0342465f,0.10972f,0.0948008f},{-0.412993f,0.28383f,0.1722f},{-0.0337063f,0.0961191f,0.00265588f}, -{-0.0527154f,0.0908974f,-0.00167195f},{-0.0925019f,0.43587f,-0.0756246f},{0.0438153f,0.125867f,0.0425581f}, -{0.174287f,0.134928f,0.0941706f},{0.0213144f,-0.318408f,0.354863f},{0.0406578f,-0.323224f,0.361603f}, -{0.0599305f,-0.322896f,0.366046f},{0.0792418f,-0.323166f,0.36249f},{0.097312f,-0.323829f,0.363519f}, -{0.117806f,-0.327424f,0.364271f},{0.138397f,-0.323951f,0.364387f},{0.157805f,-0.321604f,0.357699f}, -{0.177978f,-0.320755f,0.346786f},{0.027848f,0.0706215f,0.238475f},{-0.223636f,0.0383203f,0.226314f}, -{0.156378f,0.0796759f,0.235767f},{0.319929f,0.0325649f,0.169229f},{-0.147516f,0.116594f,0.0961384f}, -{-0.313749f,0.293438f,0.17811f},{0.136738f,0.138626f,0.116125f},{0.362358f,-0.337057f,0.210244f}, -{0.381406f,-0.336684f,0.213376f},{0.400826f,-0.336677f,0.215897f},{0.419559f,-0.337655f,0.210829f}, -{0.394679f,-0.0948008f,0.0948716f},{0.394659f,-0.0958233f,0.0384039f},{-0.407849f,0.0805119f,0.336343f}, -{-0.446053f,0.00109963f,0.0566477f},{-0.35713f,0.225485f,-0.0908974f},{-0.290335f,0.285129f,-0.168438f}, -{-0.410299f,-0.0898621f,0.228141f},{-0.392196f,-0.111135f,0.247098f},{-0.0742517f,0.0636057f,0.19465f}, -{-0.239397f,0.266126f,0.172747f},{-0.353374f,0.279753f,0.221408f},{-0.342693f,0.295778f,0.0180895f}, -{-0.0354876f,0.101129f,0.021517f},{-0.394749f,0.288923f,0.172605f},{-0.167168f,-0.32107f,0.268577f}, -{-0.145838f,-0.322485f,0.272036f},{-0.126526f,-0.322742f,0.268795f},{-0.107189f,-0.323552f,0.26003f}, -{-0.0880133f,-0.319681f,0.25241f},{-0.0712421f,-0.318022f,0.250693f},{-0.0627793f,-0.324871f,0.257233f}, -{-0.410318f,-0.0753159f,-0.0372528f},{0.251095f,0.101746f,0.11134f},{0.295132f,0.0662294f,0.136947f}, -{0.00121216f,-0.316614f,0.345314f},{0.0165557f,-0.3217f,0.349564f},{0.10499f,-0.317848f,0.370618f}, -{0.117826f,-0.31788f,0.370683f},{0.130649f,-0.317958f,0.36993f},{0.176743f,0.0583454f,0.244372f}, -{0.00338572f,0.0562169f,0.241201f},{0.117774f,0.062088f,0.253291f},{-0.497955f,0.0649368f,0.134272f}, -{-0.0534099f,0.0723128f,0.189705f},{-0.0364844f,0.0692196f,0.207273f},{-0.14578f,0.0250796f,0.232591f}, -{-0.0344716f,0.0794894f,0.189962f},{-0.374126f,0.210926f,0.306852f},{-0.238844f,0.227845f,0.173602f}, -{-0.410041f,0.290004f,0.114492f},{0.193695f,0.128131f,0.11462f},{-0.291917f,0.284917f,-0.187685f}, -{-0.185264f,0.100659f,-0.0401788f},{0.378094f,-0.0563068f,0.0186489f},{-0.335382f,0.168773f,-0.0424552f}, -{-0.433976f,0.0221343f,0.0171313f},{-0.0405421f,0.318511f,-0.00552392f},{-0.446175f,0.191441f,0.0372014f}, -{-0.303505f,0.396077f,-0.300685f},{-0.369811f,0.296563f,0.0990451f},{-0.41217f,0.00878431f,0.29855f}, -{-0.294347f,0.26549f,-0.148452f},{-0.355812f,0.360175f,-0.111141f},{-0.122121f,0.0838623f,0.17701f}, -{0.0430565f,0.127186f,0.132041f},{0.251764f,0.0635028f,0.215164f},{-0.414492f,0.283952f,0.0753931f}, -{0.0616346f,0.114093f,0.190187f},{-0.393785f,0.291817f,0.153937f},{-0.21853f,0.153397f,0.19463f}, -{0.0606958f,0.12844f,0.0401531f},{0.156468f,0.137963f,0.0765185f},{0.212903f,0.120986f,0.0972252f}, -{0.285743f,0.0747886f,0.133443f},{0.322745f,0.0380438f,0.133648f},{0.0598791f,0.122337f,0.171763f}, -{0.00871031f,-0.310562f,0.351905f},{0.022324f,-0.300466f,0.358908f},{0.0406643f,-0.300415f,0.366676f}, -{0.0599112f,-0.304537f,0.371184f},{0.0776341f,-0.302247f,0.37139f},{0.0824636f,-0.309083f,0.368606f}, -{0.0985209f,-0.304286f,0.373448f},{0.117813f,-0.304202f,0.374747f},{0.137195f,-0.304408f,0.372367f}, -{0.154828f,-0.298685f,0.366991f},{0.0213273f,0.057548f,0.247812f},{0.0985338f,0.0622102f,0.25522f}, -{0.0792868f,0.0621138f,0.253786f},{0.058355f,0.0599402f,0.252802f},{-0.315717f,0.074795f,-0.0470338f}, -{-0.147941f,-0.114858f,-0.139577f},{-0.499453f,0.0780746f,0.153526f},{0.0407157f,0.0706859f,0.241639f}, -{-0.495396f,0.0597666f,0.154915f},{-0.497685f,0.0649883f,0.147159f},{-0.228092f,-0.345076f,0.134433f}, -{-0.294791f,0.267618f,-0.0566284f},{0.390299f,-0.0928009f,0.13505f},{-0.460947f,-0.0740812f,0.151326f}, -{0.34706f,-0.0158773f,-0.00152405f},{-0.109517f,-0.0777853f,-0.137243f},{0.376653f,-0.0786599f,0.17076f}, -{0.365869f,-0.0745892f,0.190193f},{0.363696f,-0.318266f,0.207685f},{0.381502f,-0.319244f,0.204617f}, -{0.40073f,-0.318884f,0.209607f},{0.420138f,-0.319025f,0.208566f},{0.437951f,-0.319417f,0.20245f}, -{0.369933f,-0.0920999f,-0.0271052f},{0.368705f,-0.130832f,-0.0560946f},{-0.049873f,-0.115167f,-0.203183f}, -{0.211038f,0.0167455f,0.257136f},{-0.379412f,0.294782f,0.094132f},{-0.26359f,-0.323951f,0.152619f}, -{-0.262368f,-0.314035f,0.17366f},{-0.237912f,-0.316794f,0.186206f},{-0.0326967f,0.10682f,0.0438636f}, -{-0.20235f,0.169075f,0.116073f},{0.118134f,-0.0765313f,0.340639f},{0.21082f,0.0727243f,0.222758f}, -{0.273814f,0.0845117f,0.133552f},{-0.168879f,-0.303199f,0.271104f},{-0.145831f,-0.303958f,0.276428f}, -{-0.130816f,-0.302563f,0.275625f},{-0.122237f,-0.305829f,0.272621f},{-0.106399f,-0.303109f,0.263882f}, -{-0.0943796f,-0.310286f,0.256275f},{-0.0640912f,-0.298698f,0.260358f},{0.116778f,0.137475f,0.0591364f}, -{-0.00123792f,-0.300711f,0.346651f},{0.0064017f,-0.29565f,0.352953f},{0.193656f,0.0561783f,0.240912f}, -{0.0406964f,0.0575415f,0.251998f},{-0.454329f,0.265078f,0.171268f},{-0.374191f,0.188579f,0.321874f}, -{0.393528f,-0.0958104f,0.112845f},{-0.28784f,0.306234f,-0.186463f},{-0.294412f,0.22542f,-0.0373107f}, -{0.116006f,0.0725186f,0.244153f},{-0.406286f,0.187454f,0.318363f},{0.0225812f,0.0918942f,0.209479f}, -{0.00791934f,0.0883766f,0.206103f},{0.389103f,-0.0944214f,0.0165911f},{0.0820649f,0.110228f,0.202257f}, -{0.34052f,-0.0172085f,-0.0195042f},{-0.0263304f,0.489116f,-0.0886531f},{0.248947f,0.0197486f,0.243478f}, -{-0.168088f,0.11853f,0.0191827f},{-0.316135f,0.285476f,0.0214334f},{0.199309f,0.123707f,0.138098f}, -{-0.130539f,0.340433f,-0.0769428f},{-0.168757f,0.282673f,-0.0395293f},{-0.262477f,-0.306961f,0.190913f}, -{-0.248754f,-0.306363f,0.199158f},{-0.241404f,-0.30264f,0.209144f},{-0.226517f,-0.30462f,0.214861f}, -{-0.220517f,-0.297997f,0.228745f},{-0.334128f,0.292537f,0.0193499f},{0.12034f,0.102511f,0.213646f}, -{-0.00977785f,0.106678f,0.150645f},{-0.158596f,-0.299476f,0.275477f},{-0.0922382f,-0.295058f,0.258191f}, -{-0.0836661f,-0.298428f,0.254474f},{-0.0729784f,-0.298473f,0.253889f},{0.26649f,0.0916369f,0.117077f}, -{-0.190666f,0.149082f,0.0814894f},{0.0624771f,-0.285798f,0.370689f},{0.0792096f,-0.282055f,0.372721f}, -{0.0985145f,-0.281901f,0.37465f},{0.117839f,-0.282023f,0.372728f},{0.135021f,-0.285078f,0.371737f}, -{-0.384634f,0.285682f,-0.0195363f},{-0.365921f,0.285611f,-0.167583f},{-0.436446f,-0.072017f,-0.00124753f}, -{-0.393296f,0.420102f,-0.300125f},{0.117993f,0.13842f,0.114485f},{-0.00438894f,0.0613614f,0.234333f}, -{0.138333f,0.0583068f,0.250847f},{0.156365f,0.0574451f,0.249677f},{-0.0237452f,0.0260378f,0.254352f}, -{-0.0153532f,0.492762f,-0.111482f},{0.377837f,-0.168593f,-0.0397929f},{-0.185393f,0.0721263f,0.186496f}, -{-0.483801f,0.0392528f,0.243741f},{0.042587f,0.0987364f,0.206804f},{-0.224215f,0.077676f,-0.0580238f}, -{0.230002f,-0.0166554f,-0.0977975f},{-0.4969f,0.0778046f,0.108485f},{0.367573f,-0.299026f,0.205903f}, -{0.380936f,-0.299971f,0.193582f},{0.400666f,-0.305829f,0.201531f},{0.420099f,-0.305752f,0.203434f}, -{0.436356f,-0.306016f,0.20063f},{0.341741f,-5.1417e-05f,0.171615f},{-0.202093f,0.36168f,-0.0182052f}, -{-0.201186f,0.151178f,0.0380438f},{-0.488078f,0.0601395f,0.0764606f},{-0.448252f,-0.0895855f,0.0194399f}, -{-0.393412f,0.344047f,-0.149654f},{0.117459f,0.135951f,0.132729f},{-0.164313f,0.113675f,-0.000199332f}, -{0.0989518f,0.127655f,0.167152f},{-0.0500788f,-0.228308f,0.323366f},{-0.27694f,-0.0338896f,-0.106755f}, -{-0.370396f,0.0185782f,-0.0336966f},{-0.295794f,-0.126658f,-0.111752f},{-0.0936851f,-0.131616f,-0.151423f}, -{-0.238973f,-0.29491f,0.222932f},{-0.202286f,-0.297161f,0.245465f},{-0.0333719f,0.109694f,0.0763513f}, -{-0.0296614f,0.0897592f,0.167371f},{0.0786695f,0.123347f,0.175994f},{-0.168943f,-0.283489f,0.272975f}, -{-0.160821f,-0.281419f,0.276036f},{-0.145825f,-0.285753f,0.278242f},{-0.129748f,-0.285824f,0.277483f}, -{-0.120128f,-0.286074f,0.274557f},{-0.108096f,-0.280833f,0.269747f},{0.400743f,-0.26821f,0.171223f}, -{0.00295487f,-0.278332f,0.355899f},{0.0232565f,-0.279888f,0.357892f},{0.0406385f,-0.278293f,0.364316f}, -{0.0567345f,-0.277907f,0.368689f},{0.141478f,-0.276448f,0.368638f},{0.156448f,-0.27821f,0.365229f}, -{0.175676f,-0.279399f,0.358818f},{0.211045f,0.0524614f,0.239362f},{0.214877f,0.0620109f,0.23178f}, -{0.232105f,0.0548535f,0.232835f},{-0.145909f,0.111578f,0.00139547f},{-0.0464005f,0.0519598f,0.221433f}, -{-0.0185621f,0.057683f,0.231478f},{-0.0368445f,0.0611492f,0.217067f},{-0.0323945f,0.0551622f,0.226597f}, -{-0.19781f,0.074332f,0.192772f},{0.192299f,0.127025f,0.132755f},{-0.205212f,0.0821774f,-0.0591942f}, -{0.388569f,-0.0771293f,0.0370728f},{0.400678f,-0.297257f,0.19555f},{0.420054f,-0.297135f,0.198842f}, -{0.441655f,-0.301283f,0.19654f},{-0.335304f,0.297888f,0.131488f},{-0.0276101f,0.469104f,-0.12489f}, -{0.352101f,-0.0110286f,0.0177551f},{0.360448f,-0.0154014f,0.0383075f},{-0.279878f,-0.245182f,0.263554f}, -{-0.287602f,0.306273f,-0.167165f},{0.193502f,0.121919f,0.155243f},{-0.285531f,0.295643f,0.136697f}, -{-0.313794f,-0.127957f,-0.107437f},{-0.355831f,0.0205974f,-0.0426095f},{-0.130803f,0.359609f,-0.077766f}, -{-0.186859f,0.28138f,-0.0334073f},{-0.24433f,-0.134356f,-0.131822f},{-0.167233f,-0.133996f,-0.132356f}, -{-0.223951f,-0.281438f,0.243188f},{-0.20572f,-0.282975f,0.250712f},{-0.182113f,0.1366f,0.0737919f}, -{0.135484f,0.108517f,0.202894f},{-0.18356f,-0.279534f,0.265901f},{-0.0879747f,-0.278223f,0.260789f}, -{-0.0702068f,-0.279232f,0.261265f},{0.305427f,0.0570914f,0.132311f},{-0.375142f,0.289232f,0.0207582f}, -{0.264046f,0.0732838f,0.189634f},{-0.411848f,0.287926f,0.0950838f},{-0.205019f,0.0557153f,0.210122f}, -{-0.480084f,0.19674f,0.133552f},{0.338147f,0.00136971f,0.0018842f},{-0.476972f,0.230957f,0.175345f}, -{-0.355728f,0.297624f,0.133636f},{-0.246651f,0.279303f,0.167962f},{-0.0530112f,0.0594643f,0.209794f}, -{-0.126539f,0.055741f,0.208636f},{-0.218845f,0.228192f,0.0964406f},{-0.336816f,-0.298267f,0.151834f}, -{-0.213984f,0.22834f,0.0841131f},{-0.391283f,0.306183f,-0.092087f},{0.0407736f,0.0879972f,0.222765f}, -{-0.45168f,-0.00158838f,0.11534f},{-0.455892f,-0.0366419f,0.0770072f},{0.381489f,-0.28293f,0.187782f}, -{0.401958f,-0.285271f,0.18701f},{0.420028f,-0.28419f,0.190418f},{0.440233f,-0.2852f,0.189164f}, -{0.454818f,-0.287116f,0.184952f},{-0.455397f,0.116048f,0.0136652f},{0.393431f,-0.149236f,0.165538f}, -{0.397322f,-0.130832f,0.133475f},{-0.389045f,0.304453f,-0.0738818f},{0.0624385f,0.131861f,0.116678f}, -{0.0188322f,0.124157f,0.0999389f},{-0.39282f,0.284447f,0.190309f},{-0.410717f,0.28057f,0.187344f}, -{-0.411199f,0.14786f,-0.0154207f},{-0.148224f,-0.0326613f,-0.120858f},{-0.277596f,-0.127739f,-0.117417f}, -{-0.109337f,-0.1326f,-0.147597f},{-0.432639f,0.0430211f,0.00153052f},{0.0408443f,-0.128112f,-0.22517f}, -{0.0573968f,-0.129739f,-0.22351f},{-0.352564f,-0.296453f,0.132806f},{-0.275538f,0.0185846f,-0.0867432f}, -{0.193212f,-0.0152085f,-0.111154f},{0.0769332f,-0.0398701f,-0.138259f},{-0.164197f,-0.260545f,0.278866f}, -{-0.145831f,-0.263123f,0.278891f},{-0.126565f,-0.263149f,0.278416f},{-0.0620655f,-0.272274f,0.272744f}, -{0.337304f,0.0145397f,0.15276f},{0.00310277f,0.112209f,0.149577f},{-0.0144401f,0.115237f,0.0962863f}, -{-0.327472f,0.350182f,-0.107154f},{0.00212531f,-0.260641f,0.36076f},{0.0213594f,-0.260738f,0.360857f}, -{0.0393653f,-0.261162f,0.366098f},{0.0534356f,-0.264596f,0.368779f},{0.0611781f,-0.258808f,0.373814f}, -{0.0792032f,-0.259554f,0.376354f},{0.0985724f,-0.259548f,0.375911f},{0.11661f,-0.258937f,0.372213f}, -{0.124263f,-0.264455f,0.369448f},{0.137131f,-0.260082f,0.368824f},{0.156442f,-0.260197f,0.367474f}, -{0.174088f,-0.26149f,0.362419f},{0.119292f,0.139082f,0.0968779f},{-0.2802f,0.290557f,0.171351f}, -{0.229996f,0.110646f,0.132195f},{-0.109022f,0.434558f,-0.0368477f},{-0.422999f,-0.0430147f,-0.0129578f}, -{-0.491306f,0.112672f,0.0954632f},{-0.0883412f,0.282075f,-0.017948f},{-0.374158f,0.301206f,-0.0189126f}, -{0.389772f,-0.0770072f,0.0948587f},{-0.483769f,0.113212f,0.211196f},{0.394511f,-0.28037f,0.180206f}, -{0.342667f,-0.0349056f,-0.0382753f},{-0.473531f,0.156477f,0.0759461f},{0.389534f,-0.149706f,0.175306f}, -{0.0982959f,0.134517f,0.133417f},{-0.337054f,0.333996f,-0.0643002f},{-0.276277f,0.294023f,0.13069f}, -{-0.277306f,0.0785955f,-0.0460114f},{-0.0315778f,-0.0332529f,-0.136658f},{-0.127317f,-0.132041f,-0.143925f}, -{-0.426549f,0.02861f,0.00250155f},{-0.128854f,0.301077f,-0.0589884f},{-0.278753f,-0.469413f,0.302203f}, -{0.0773062f,-0.12761f,-0.219813f},{0.172454f,-0.0427896f,-0.123256f},{-0.187843f,-0.26084f,0.272191f}, -{-0.113729f,-0.258757f,0.276936f},{-0.104051f,-0.259207f,0.271914f},{-0.0879875f,-0.259824f,0.26484f}, -{-0.0720845f,-0.259599f,0.266577f},{-0.0622327f,-0.263258f,0.277489f},{-0.394241f,0.269374f,0.228842f}, -{-0.335729f,0.357956f,-0.114884f},{0.0471721f,-0.255252f,0.371371f},{0.231275f,0.0376065f,0.242577f}, -{0.214254f,0.039047f,0.245555f},{0.193701f,0.0395293f,0.248011f},{0.137041f,0.0399408f,0.264982f}, -{-0.395611f,-0.0363718f,-0.0398958f},{-0.184415f,0.0511366f,0.207254f},{-0.165162f,0.0555931f,0.206206f}, -{0.13061f,0.0531944f,0.257889f},{0.0631459f,0.0534131f,0.257059f},{0.0792997f,0.0532909f,0.260519f}, -{-0.464458f,-0.0707308f,0.13496f},{0.0191151f,0.123244f,0.0768143f},{0.0229864f,0.0375293f,0.259985f}, -{0.0627729f,0.0895791f,0.227163f},{-0.463654f,-0.0535095f,0.115585f},{-0.467435f,0.175088f,0.249355f}, -{-0.335201f,0.126498f,-0.0476833f},{-0.374236f,0.166104f,0.335076f},{-0.386859f,0.219858f,0.297997f}, -{0.380821f,-0.264872f,0.188431f},{0.388351f,-0.261998f,0.177435f},{0.423237f,-0.271322f,0.180637f}, -{0.436163f,-0.271342f,0.18065f},{0.439211f,-0.262004f,0.171268f},{0.456632f,-0.264982f,0.169377f}, -{-0.456857f,0.178387f,0.0565448f},{-0.482386f,0.113315f,0.246545f},{-0.467062f,-0.0924408f,0.115816f}, -{0.194987f,0.116318f,0.171358f},{-0.261352f,0.307244f,0.0568342f},{-0.200421f,0.169313f,0.0971352f}, -{-0.374416f,0.271509f,0.233639f},{-0.335163f,0.112466f,-0.0477927f},{-0.258773f,0.226115f,-0.00123467f}, -{-0.393804f,0.03497f,-0.0210218f},{-0.354018f,0.0368027f,-0.039317f},{-0.355484f,-0.0172342f,-0.0600495f}, -{-0.31609f,0.322536f,-0.355358f},{0.0926755f,-0.130157f,-0.217909f},{-0.335259f,0.149905f,-0.0439343f}, -{-0.181123f,-0.254043f,0.278435f},{0.0232758f,0.118369f,0.151114f},{-0.261442f,0.291328f,0.114311f}, -{0.393675f,-0.243696f,0.164818f},{-0.241166f,0.285862f,0.0968522f},{0.00107712f,-0.243542f,0.364078f}, -{0.0200218f,-0.24322f,0.365615f},{0.0341693f,-0.246654f,0.369499f},{0.0419697f,-0.240822f,0.374194f}, -{0.0599434f,-0.241111f,0.381923f},{0.0792032f,-0.240925f,0.385602f},{0.0986367f,-0.241163f,0.383222f}, -{0.117961f,-0.24178f,0.375203f},{0.137105f,-0.241966f,0.371827f},{0.155162f,-0.241085f,0.371737f}, -{0.162937f,-0.246699f,0.368901f},{0.17383f,-0.244648f,0.364509f},{0.174834f,0.0401402f,0.253387f}, -{0.079274f,0.0400695f,0.267934f},{0.0985466f,0.0401531f,0.269933f},{-0.203836f,0.345873f,0.0429697f}, -{-0.164274f,0.0726086f,-0.0688466f},{-0.358075f,0.0621845f,0.353944f},{-0.145851f,0.0558053f,0.208431f}, -{-0.23478f,0.273348f,0.119552f},{-0.075422f,-0.128607f,-0.188123f},{-0.374101f,0.219845f,0.299508f}, -{-0.487467f,0.0957654f,0.246648f},{0.0421626f,0.379396f,-0.112202f},{-0.494547f,0.0776953f,0.0956625f}, -{-0.185155f,0.361628f,-0.0377029f},{-0.222581f,0.320916f,-0.0162438f},{-0.333845f,0.455805f,-0.360246f}, -{-0.0507476f,0.089682f,0.150297f},{-0.0236616f,0.476936f,-0.0794251f},{0.379033f,-0.132401f,-0.0399087f}, -{-0.301878f,0.374702f,-0.262763f},{0.145664f,0.124092f,0.178522f},{-0.108244f,0.0928395f,0.151944f}, -{0.0404135f,0.126813f,0.0586219f},{0.135054f,-0.127571f,-0.203164f},{-0.0112247f,0.438005f,-0.144606f}, -{-0.072734f,0.359937f,-0.0974245f},{-0.220543f,0.301103f,-0.0205524f},{-0.373818f,0.130832f,-0.0395228f}, -{-0.392614f,-0.243664f,0.169242f},{-0.261757f,-0.243966f,0.268281f},{0.0427349f,-0.0352593f,-0.139185f}, -{0.173277f,-0.0130157f,-0.115823f},{-0.233218f,0.26767f,-0.00997394f},{-0.354545f,0.149802f,-0.0412012f}, -{0.0590817f,-0.038436f,-0.138549f},{0.0962187f,-0.0401659f,-0.135404f},{-0.205199f,-0.245934f,0.271065f}, -{-0.18439f,-0.24041f,0.280261f},{-0.16513f,-0.240243f,0.28266f},{-0.145838f,-0.240417f,0.280621f}, -{-0.126539f,-0.240442f,0.280351f},{-0.110527f,-0.240725f,0.276801f},{-0.100849f,-0.241124f,0.272139f}, -{-0.0879618f,-0.241356f,0.26866f},{-0.0735121f,-0.243021f,0.271695f},{-0.00337933f,0.492537f,-0.113347f}, -{-0.317472f,0.297238f,0.116948f},{-0.0153661f,-0.243426f,0.356516f},{0.0277515f,-0.237652f,0.370252f}, -{0.0180412f,0.0443137f,0.254815f},{0.00199026f,0.0394907f,0.251567f},{-0.353812f,0.421253f,-0.335127f}, -{0.117768f,0.040063f,0.268416f},{-0.413405f,0.0750072f,0.329256f},{-0.374242f,0.0575351f,0.350259f}, -{-0.052844f,-0.108042f,-0.188348f},{0.384242f,-0.0774573f,0.0178387f},{-0.295814f,0.248108f,-0.0951802f}, -{0.374782f,-0.0371177f,0.0563133f},{-0.431687f,-0.0206167f,0.174149f},{-0.262426f,0.0814058f,-0.0432848f}, -{-0.232131f,0.222996f,0.147841f},{-0.498682f,0.0778946f,0.118131f},{0.381869f,-0.0774123f,0.152754f}, -{0.39637f,-0.0947558f,0.0563198f},{0.395785f,-0.0947944f,0.0755731f},{-0.0144465f,0.475528f,-0.0930967f}, -{-0.499794f,0.0954053f,0.134105f},{-0.181695f,0.13424f,0.111701f},{0.0992669f,-0.0618051f,0.315527f}, -{-0.173129f,0.125771f,0.0961706f},{0.0410308f,0.437073f,-0.180007f},{0.0606893f,0.131822f,0.0790457f}, -{0.0422205f,0.128587f,0.0778303f},{-0.299563f,-0.286512f,0.210231f},{-0.224967f,-0.134337f,-0.130665f}, -{-0.240542f,-0.24324f,0.268872f},{-0.222382f,-0.236063f,0.278094f},{-0.203759f,-0.236327f,0.277837f}, -{-0.0662455f,-0.244134f,0.282017f},{-0.0150638f,0.0949101f,0.171576f},{0.136841f,0.0919456f,0.224951f}, -{-0.0157776f,-0.22544f,0.361185f},{0.000794167f,-0.225234f,0.368072f},{0.0213787f,-0.223877f,0.374052f}, -{0.0406257f,-0.223716f,0.376637f},{0.0598534f,-0.223292f,0.38229f},{0.0791775f,-0.222861f,0.387647f}, -{0.0985981f,-0.222919f,0.387023f},{0.117948f,-0.223639f,0.377994f},{0.137118f,-0.223948f,0.373403f}, -{0.156416f,-0.223999f,0.372702f},{-0.090592f,0.0532009f,0.206051f},{-0.34297f,0.0636636f,0.352458f}, -{-0.0703997f,0.290158f,-0.0165332f},{-0.284708f,0.379017f,-0.377833f},{-0.45514f,0.209556f,0.0755989f}, -{-0.494347f,0.11282f,0.114749f},{0.268683f,-0.0564612f,0.282975f},{0.194537f,-0.111244f,0.342343f}, -{-0.296611f,0.363667f,-0.261374f},{-0.297511f,0.3259f,-0.302222f},{0.343941f,0.00187775f,0.0181281f}, -{-0.468908f,0.192592f,0.191454f},{-0.375361f,0.225684f,-0.0372335f},{-0.355014f,0.303019f,-0.000675201f}, -{-0.0488313f,0.474744f,-0.0766149f},{-0.246246f,0.281399f,0.151879f},{-0.257455f,0.269065f,0.208636f}, -{0.174744f,0.100158f,0.210199f},{0.079036f,0.134716f,0.0765378f},{0.0479759f,-0.0634577f,0.315347f}, -{-0.410916f,-0.244957f,0.0971287f},{-0.300309f,-0.225928f,0.270416f},{-0.285795f,-0.230604f,0.273258f}, -{-0.278888f,-0.223343f,0.279052f},{-0.261577f,-0.222134f,0.282904f},{-0.242298f,-0.222289f,0.282107f}, -{-0.22298f,-0.221877f,0.285393f},{-0.203714f,-0.222083f,0.285406f},{-0.184409f,-0.222237f,0.2821f}, -{-0.165091f,-0.22189f,0.285695f},{-0.145857f,-0.222089f,0.283624f},{-0.126565f,-0.222385f,0.2803f}, -{-0.108591f,-0.221703f,0.277496f},{-0.100791f,-0.227491f,0.27358f},{-0.0881419f,-0.223144f,0.272596f}, -{-0.0739558f,-0.223639f,0.284351f},{-0.336217f,0.297823f,0.151391f},{-0.147182f,0.0735861f,0.191962f}, -{-0.29937f,0.29637f,0.113135f},{0.00855597f,-0.219588f,0.371474f},{0.173477f,-0.226604f,0.366233f}, -{-0.0686891f,0.0249445f,0.235433f},{-0.107273f,0.0425002f,0.216681f},{0.156384f,0.0377673f,0.260976f}, -{0.0407028f,0.0399023f,0.263438f},{-0.444516f,0.265792f,0.186676f},{-0.467911f,0.154201f,0.265496f}, -{-0.302463f,0.414012f,-0.326099f},{-0.298592f,0.324703f,-0.321835f},{0.358082f,-0.131404f,-0.0702742f}, -{-0.353754f,-0.0912832f,-0.0882094f},{-0.299068f,-0.470126f,0.283869f},{-0.24132f,0.074795f,-0.0535545f}, -{0.374872f,-0.0370663f,0.0755796f},{-0.442111f,0.186791f,0.0245266f},{-0.288733f,0.291187f,-0.160702f}, -{-0.369458f,0.343667f,-0.225948f},{-0.20646f,0.188856f,0.0770972f},{-0.207276f,0.36332f,-0.00239219f}, -{-0.201894f,0.337012f,0.0508022f},{0.370101f,-0.030417f,0.0964921f},{-0.353863f,0.341475f,-0.266004f}, -{0.270104f,0.0767435f,0.174123f},{-0.0709399f,0.0747307f,0.171718f},{-0.295724f,-0.145172f,-0.112659f}, -{-0.293743f,-0.218173f,0.276744f},{-0.413405f,0.288653f,0.134073f},{-0.0186972f,-0.208701f,0.366625f}, -{0.00206101f,-0.206116f,0.373248f},{0.021353f,-0.205884f,0.376374f},{0.0405871f,-0.205492f,0.381493f}, -{0.0599112f,-0.205466f,0.381756f},{0.0791711f,-0.205145f,0.385036f},{0.0985788f,-0.20519f,0.385107f}, -{0.117954f,-0.20571f,0.37865f},{0.135883f,-0.207164f,0.371152f},{0.156429f,-0.210713f,0.370966f}, -{0.174756f,-0.206218f,0.364522f},{-0.139291f,0.0428411f,0.217427f},{-0.126533f,0.0425131f,0.215742f}, -{-0.0522266f,0.074705f,-0.0390855f},{-0.333394f,0.0598566f,0.34896f},{-0.287885f,0.291277f,-0.131764f}, -{-0.431173f,0.00439213f,0.228957f},{-0.341844f,0.305032f,-0.30435f},{-0.289981f,0.327424f,-0.226411f}, -{-0.128449f,0.417761f,-0.0551043f},{0.311414f,0.0249767f,-0.0201601f},{-0.44314f,0.0112151f,0.176419f}, -{0.0824057f,0.10318f,0.211826f},{-0.0110447f,0.0887367f,0.188309f},{-0.2751f,0.294196f,0.114035f}, -{0.136385f,0.13952f,0.0976303f},{-0.15259f,0.4069f,-0.0203916f},{-0.355439f,0.296917f,0.15249f}, -{-0.298379f,0.284621f,0.207093f},{-0.188627f,0.134748f,0.0421466f},{0.0227613f,-0.0735539f,0.322916f}, -{-0.300161f,-0.204315f,0.28037f},{-0.280792f,-0.203897f,0.284615f},{-0.262837f,-0.204418f,0.289843f}, -{-0.241012f,-0.204463f,0.290389f},{-0.223006f,-0.20344f,0.290955f},{-0.20372f,-0.203415f,0.291714f}, -{-0.184447f,-0.204051f,0.283586f},{-0.165085f,-0.203865f,0.286023f},{-0.145883f,-0.203762f,0.286615f}, -{-0.126617f,-0.20409f,0.282403f},{-0.107267f,-0.204598f,0.277039f},{-0.0923797f,-0.206405f,0.274557f}, -{-0.0837562f,-0.204218f,0.281071f},{-0.355046f,0.297367f,0.112987f},{-0.336307f,-0.150278f,0.285238f}, -{-0.487762f,0.134806f,0.114614f},{-0.482329f,0.0442815f,0.212868f},{-0.0140028f,-0.201762f,0.371712f}, -{0.143561f,-0.201923f,0.369551f},{0.156435f,-0.201974f,0.368638f},{-0.0880261f,0.024996f,0.233221f}, -{0.059982f,0.039973f,0.265908f},{0.348371f,0.00301599f,0.0364619f},{-0.443899f,0.00239864f,0.152715f}, -{-0.184351f,0.0597987f,0.197691f},{-0.210749f,0.187782f,0.114382f},{-0.461558f,-0.0538053f,0.0771036f}, -{-0.10955f,0.418713f,-0.0740105f},{-0.414472f,-0.091817f,-0.0382946f},{0.257905f,0.0859651f,0.173544f}, -{-0.297826f,0.293663f,0.170663f},{-0.451365f,0.272699f,0.0952381f},{-0.39556f,-0.0537346f,0.230526f}, -{0.0591974f,0.130395f,0.0599724f},{-0.375438f,0.296023f,0.132079f},{-0.491286f,0.0431047f,0.115115f}, -{-0.0537957f,-0.127237f,-0.212315f},{-0.255082f,-0.19854f,0.294325f},{-0.248722f,-0.198617f,0.294601f}, -{-0.373792f,0.288287f,0.1911f},{-0.437841f,-0.00634706f,0.157153f},{-0.017186f,-0.18802f,0.376239f}, -{0.00208673f,-0.187865f,0.378702f},{0.0213594f,-0.187898f,0.378142f},{0.0406257f,-0.187563f,0.382972f}, -{0.0599434f,-0.187647f,0.381595f},{0.0792032f,-0.187698f,0.380361f},{0.0985466f,-0.187724f,0.380329f}, -{0.117897f,-0.187975f,0.377743f},{0.135915f,-0.187492f,0.372753f},{0.143619f,-0.193074f,0.369165f}, -{0.156455f,-0.18674f,0.367422f},{0.17401f,-0.184701f,0.361686f},{-0.0700203f,0.055143f,0.207614f}, -{-0.0324266f,0.323289f,-0.0133693f},{-0.445693f,-0.0196842f,0.0576059f},{-0.165226f,0.099881f,-0.0395164f}, -{-0.390203f,0.130298f,0.351384f},{-0.489756f,0.0774895f,0.0763577f},{0.397733f,-0.130909f,0.0178001f}, -{-0.423919f,-0.0212533f,0.186965f},{-0.166274f,0.102022f,0.138716f},{-0.0904505f,0.0690332f,0.183981f}, -{-0.424838f,-0.0223851f,0.00402561f},{-0.415996f,0.0336452f,-0.00662999f},{0.264181f,0.0920742f,0.134542f}, -{-0.318205f,-0.188123f,0.283174f},{-0.300154f,-0.185499f,0.289611f},{-0.280862f,-0.185582f,0.28875f}, -{-0.265815f,-0.186804f,0.292094f},{-0.257217f,-0.183473f,0.296093f},{-0.242266f,-0.184669f,0.299109f}, -{-0.224633f,-0.182792f,0.295489f},{-0.219803f,-0.189782f,0.293573f},{-0.202402f,-0.186226f,0.292949f}, -{-0.184441f,-0.185788f,0.286177f},{-0.165123f,-0.186129f,0.282422f},{-0.145786f,-0.185647f,0.287598f}, -{-0.126623f,-0.18555f,0.288177f},{-0.107254f,-0.186309f,0.279528f},{-0.0896981f,-0.187306f,0.281039f}, -{-0.443513f,0.276358f,0.15258f},{-0.0693836f,0.0744928f,-0.0386868f},{-0.0388637f,0.480525f,-0.0943957f}, -{-0.238401f,0.0368927f,0.232385f},{-0.0433909f,0.0347127f,0.237664f},{-0.0368316f,0.043542f,0.234983f}, -{0.16266f,0.0439021f,0.255214f},{-0.389939f,-0.0189576f,-0.0366419f},{-0.392827f,0.0589627f,0.341256f}, -{0.375991f,-0.116807f,-0.0382431f},{0.0163692f,0.340201f,-0.0590527f},{-0.373039f,0.0968457f,0.351513f}, -{0.118276f,0.0920421f,0.224764f},{-0.429308f,-0.0382046f,-0.000225055f},{-0.457744f,0.0940291f,0.0119932f}, -{-0.49056f,0.0783962f,0.262975f},{-0.469474f,0.264268f,0.132761f},{-0.391174f,0.0139095f,0.310685f}, -{-0.385611f,0.245272f,-0.0363332f},{-0.353696f,0.296132f,0.16404f},{-0.324514f,0.333224f,-0.078229f}, -{-0.445108f,0.278448f,0.118652f},{-0.430279f,0.283663f,0.113212f},{-0.128526f,0.100627f,-0.0182566f}, -{-0.36657f,0.325887f,-0.206386f},{-0.0509855f,0.0969615f,0.131391f},{-0.210132f,-0.180721f,0.294126f}, -{-0.0818463f,-0.180721f,0.294106f},{-0.471004f,0.0357802f,0.191126f},{-0.448297f,-0.000610921f,0.13561f}, -{-0.442606f,-0.0213112f,0.0427639f},{-0.446162f,-0.0722549f,0.0192856f},{-0.0179448f,-0.171043f,0.377518f}, -{0.00211245f,-0.17004f,0.378657f},{0.0213466f,-0.170046f,0.378406f},{0.040645f,-0.169834f,0.38119f}, -{0.0600013f,-0.170014f,0.379062f},{0.0792032f,-0.170033f,0.378354f},{0.0985145f,-0.169911f,0.380059f}, -{0.117845f,-0.169995f,0.379563f},{0.137201f,-0.17022f,0.376792f},{0.153162f,-0.168606f,0.372104f}, -{-0.448915f,0.0937333f,-0.000842398f},{0.23289f,-0.166278f,0.335951f},{-0.204704f,0.284769f,0.0757275f}, -{-0.41471f,0.190798f,-0.0013247f},{-0.410382f,0.208546f,-0.00140187f},{0.0517186f,0.38346f,-0.142471f}, -{0.172467f,0.0976303f,-0.0358766f},{0.392601f,-0.0994695f,0.130214f},{-0.165143f,0.0250153f,0.233234f}, -{-0.29827f,0.296132f,0.150831f},{-0.454452f,0.274158f,0.115559f},{-0.322289f,0.35957f,-0.357005f}, -{0.242156f,0.106408f,0.12179f},{-0.355786f,0.296743f,0.017112f},{0.0784122f,-0.0586798f,0.301765f}, -{-0.319466f,-0.169577f,0.290094f},{-0.300148f,-0.171647f,0.293836f},{-0.300161f,-0.16476f,0.295264f}, -{-0.279596f,-0.168162f,0.292409f},{-0.267975f,-0.167229f,0.292486f},{-0.258265f,-0.166953f,0.295997f}, -{-0.242253f,-0.166612f,0.300723f},{-0.222999f,-0.166676f,0.300248f},{-0.203714f,-0.166805f,0.298601f}, -{-0.190859f,-0.167011f,0.296016f},{-0.181155f,-0.167435f,0.290428f},{-0.16513f,-0.167892f,0.284531f}, -{-0.145748f,-0.167429f,0.290029f},{-0.126636f,-0.167358f,0.29093f},{-0.107286f,-0.16811f,0.281476f}, -{-0.0915051f,-0.168207f,0.282132f},{-0.0812225f,-0.16667f,0.297444f},{-0.336269f,0.43371f,-0.360361f}, -{-0.460194f,-0.0583197f,0.0643581f},{-0.442053f,-0.0419022f,0.0256198f},{-0.46739f,-0.0706922f,0.115688f}, -{-0.437983f,-0.0187454f,0.0340954f},{-0.457654f,0.0156908f,0.0572972f},{0.290991f,0.0614128f,0.172091f}, -{0.214742f,-0.166014f,0.341918f},{-0.0160863f,0.102318f,0.155519f},{-0.19774f,0.0611749f,0.198425f}, -{-0.359625f,0.0961448f,0.352092f},{-0.288039f,0.321392f,-0.205775f},{-0.355407f,0.293135f,0.0276068f}, -{0.188698f,0.125179f,0.147976f},{0.154262f,0.138607f,0.0940549f},{-0.427314f,-0.0734253f,-0.0193563f}, -{0.0803543f,0.0910968f,0.226983f},{-0.411167f,0.2732f,0.208296f},{-0.338648f,0.35368f,-0.102029f}, -{-0.0346195f,-0.146696f,0.362464f},{-0.312951f,-0.162529f,0.294518f},{-0.287312f,-0.162586f,0.294827f}, -{0.0412044f,0.120472f,0.0201666f},{-0.316572f,0.433407f,-0.377158f},{-0.453796f,-0.0718498f,0.0387576f}, -{-0.0511013f,-0.0979711f,-0.165281f},{0.0969969f,0.13352f,0.0388605f},{-0.0171667f,-0.152555f,0.373692f}, -{0.00208029f,-0.152355f,0.37629f},{0.0213466f,-0.152265f,0.377435f},{0.040645f,-0.152239f,0.377911f}, -{0.0599627f,-0.152227f,0.377975f},{0.0792225f,-0.15222f,0.377821f},{0.0985274f,-0.152207f,0.37757f}, -{0.117826f,-0.152175f,0.377834f},{0.13715f,-0.152317f,0.376425f},{0.154892f,-0.152966f,0.372168f}, -{0.060117f,-0.116163f,-0.218372f},{0.31025f,0.043169f,0.171075f},{-0.390357f,0.0435742f,0.33777f}, -{-0.200421f,0.0423909f,0.216038f},{-0.184435f,0.0424231f,0.216263f},{-0.373863f,0.291676f,0.0576702f}, -{0.398563f,-0.112672f,0.0948651f},{0.284779f,0.036822f,-0.0329957f},{-0.356718f,0.368072f,-0.127121f}, -{0.362004f,-0.11354f,-0.0547699f},{0.37592f,-0.0959776f,0.184907f},{-0.352371f,0.329661f,-0.0511109f}, -{-0.350416f,0.349609f,-0.0879136f},{-0.335491f,0.306376f,-0.0183081f},{-0.144269f,0.413292f,-0.0335102f}, -{-0.323736f,-0.150741f,0.292306f},{-0.31508f,-0.147416f,0.296183f},{-0.300141f,-0.148715f,0.298659f}, -{-0.280882f,-0.148703f,0.298859f},{-0.261558f,-0.148896f,0.296454f},{-0.242253f,-0.1486f,0.300537f}, -{-0.222999f,-0.148433f,0.302981f},{-0.203739f,-0.148516f,0.301894f},{-0.187541f,-0.148992f,0.29583f}, -{-0.17792f,-0.149333f,0.291303f},{-0.165162f,-0.149828f,0.285136f},{-0.14578f,-0.149487f,0.289341f}, -{-0.126507f,-0.149468f,0.289354f},{-0.107247f,-0.149706f,0.285361f},{-0.0910871f,-0.149449f,0.289058f}, -{-0.0810617f,-0.15332f,0.29628f},{0.0787981f,0.131018f,0.0376194f},{0.135594f,0.138047f,0.0597023f}, -{-0.0820135f,0.450468f,-0.0409954f},{0.301183f,0.0536382f,0.169024f},{-0.485132f,0.0616443f,0.282312f}, -{0.212131f,-0.149873f,0.339121f},{0.232433f,-0.146503f,0.328195f},{0.248754f,-0.148066f,0.325469f}, -{-0.340905f,-0.186457f,0.272332f},{-0.0107617f,0.0523713f,0.2385f},{0.0427671f,0.0982798f,-0.035986f}, -{-0.0806887f,-0.12606f,-0.173506f},{0.382197f,-0.0955403f,0.172059f},{0.355509f,-0.0540304f,-0.0333044f}, -{-0.041063f,0.458423f,-0.0611106f},{-0.406119f,-0.0569306f,0.221491f},{-0.445584f,-0.111585f,0.0194849f}, -{-0.467069f,-0.0925436f,0.0965628f},{-0.495499f,0.115353f,0.154947f},{-0.44869f,-0.00164623f,0.0768464f}, -{-0.392023f,0.306074f,-0.111411f},{-0.076496f,-0.147506f,0.303444f},{0.174756f,0.134362f,0.0740491f}, -{0.193958f,0.128851f,0.0764992f},{0.202891f,0.125372f,0.0889489f},{-0.0161249f,-0.128883f,0.364271f}, -{-0.0108711f,-0.139282f,0.370477f},{0.00423457f,-0.13323f,0.372734f},{0.0213594f,-0.130105f,0.374708f}, -{0.0406514f,-0.130099f,0.374599f},{0.0599434f,-0.134394f,0.377383f},{0.0792675f,-0.130028f,0.37503f}, -{0.0985466f,-0.130182f,0.372824f},{0.116559f,-0.135539f,0.372322f},{0.137111f,-0.139237f,0.371872f}, -{0.156403f,-0.130774f,0.363783f},{0.175573f,-0.130221f,0.358779f},{-0.184473f,0.336015f,0.0502106f}, -{-0.461281f,-0.0544227f,0.13352f},{-0.355394f,0.323424f,-0.246577f},{-0.451744f,-0.00167199f,0.0960677f}, -{-0.488778f,0.0948844f,0.0762548f},{0.0491334f,0.377615f,-0.128253f},{-0.290123f,0.281174f,-0.147828f}, -{-0.316212f,0.293643f,0.0745378f},{-0.0885084f,0.00246939f,-0.107488f},{-0.245648f,0.267271f,0.188579f}, -{-0.35522f,-0.131314f,0.277071f},{-0.3365f,-0.131655f,0.284145f},{-0.0710685f,0.0887624f,-0.000945289f}, -{-0.0934343f,0.0889425f,-0.00949164f},{-0.322739f,-0.130086f,0.292222f},{-0.315183f,-0.132356f,0.295907f}, -{-0.300141f,-0.130523f,0.300273f},{-0.280882f,-0.130414f,0.301405f},{-0.261577f,-0.1306f,0.299547f}, -{-0.242304f,-0.1306f,0.29927f},{-0.223006f,-0.130626f,0.299257f},{-0.20372f,-0.130697f,0.297836f}, -{-0.190917f,-0.135443f,0.295482f},{-0.182994f,-0.130414f,0.289052f},{-0.165085f,-0.131719f,0.284235f}, -{-0.145838f,-0.131475f,0.287598f},{-0.126507f,-0.131237f,0.289669f},{-0.107228f,-0.135597f,0.291778f}, -{-0.0879168f,-0.13098f,0.293013f},{-0.0879425f,-0.130915f,0.294621f},{-0.0714736f,-0.130446f,0.305527f}, -{0.23069f,0.113077f,0.0939777f},{-0.00217037f,-0.124543f,0.368593f},{0.119446f,-0.123707f,0.368496f}, -{0.137131f,-0.126099f,0.365795f},{0.115916f,-0.127546f,-0.208752f},{0.0421819f,0.35959f,-0.110189f}, -{0.231108f,-0.129925f,0.322504f},{0.252638f,-0.130735f,0.320858f},{-0.345188f,0.0937848f,0.351076f}, -{0.335356f,0.013035f,0.165834f},{0.271017f,0.0599081f,-0.0193498f},{-0.298412f,0.278538f,0.223858f}, -{-0.328951f,0.294306f,-0.000379391f},{-0.418363f,-0.014051f,-0.00224428f},{-0.4484f,-0.0194592f,0.076885f}, -{-0.45278f,0.173801f,0.037285f},{-0.107222f,-0.126234f,0.295116f},{0.24287f,0.106646f,0.0991222f}, -{0.286901f,0.0755024f,0.11489f},{0.276155f,0.0845375f,0.111495f},{0.0599434f,-0.116639f,0.373654f}, -{0.189489f,-0.132259f,0.354304f},{0.100566f,0.0614064f,-0.078184f},{-0.320572f,0.340767f,-0.359242f}, -{-0.336127f,0.0973602f,0.345577f},{0.392601f,-0.0814186f,0.0563262f},{-0.475049f,0.0359538f,0.206598f}, -{-0.202871f,0.0996367f,-0.0388219f},{-0.316951f,0.284415f,0.00184562f},{-0.277454f,0.294537f,0.0929809f}, -{-0.411881f,0.0543841f,-0.0163403f},{-0.354937f,-0.114202f,0.277496f},{-0.338848f,-0.113758f,0.281329f}, -{-0.319877f,-0.114202f,0.288807f},{-0.298855f,-0.113508f,0.297515f},{-0.280856f,-0.11235f,0.300833f}, -{-0.26159f,-0.112273f,0.301514f},{-0.242311f,-0.112556f,0.297701f},{-0.221404f,-0.115038f,0.294524f}, -{-0.207964f,-0.111225f,0.295174f},{-0.199309f,-0.114434f,0.292441f},{-0.184338f,-0.113205f,0.288563f}, -{-0.165117f,-0.11352f,0.284197f},{-0.145799f,-0.113373f,0.28655f},{-0.130745f,-0.114434f,0.292351f}, -{-0.122173f,-0.111205f,0.295257f},{-0.107202f,-0.112594f,0.296164f},{-0.0895438f,-0.110395f,0.295277f}, -{-0.0847208f,-0.11736f,0.293862f},{-0.0688177f,-0.113231f,0.299566f},{0.314353f,0.048365f,0.130472f}, -{0.296232f,0.0672069f,0.114408f},{0.00345646f,-0.109315f,0.363069f},{0.0200218f,-0.107328f,0.365249f}, -{0.0278029f,-0.112376f,0.370966f},{0.0406385f,-0.112299f,0.371165f},{0.0792482f,-0.112266f,0.371261f}, -{0.0921224f,-0.112318f,0.369847f},{0.0998071f,-0.107225f,0.367133f},{0.117819f,-0.108222f,0.364914f}, -{0.137118f,-0.108517f,0.359686f},{0.154744f,-0.111231f,0.353577f},{0.169187f,-0.113726f,0.351455f}, -{0.176898f,-0.108588f,0.347018f},{-0.467133f,0.174959f,0.210861f},{0.231218f,-0.113398f,0.318016f}, -{0.251828f,-0.112318f,0.316897f},{-0.359857f,0.393962f,-0.302936f},{-0.208434f,0.229272f,0.0730202f}, -{-0.358165f,0.219794f,0.298222f},{-0.088547f,0.37865f,-0.0349249f},{-0.24251f,0.327996f,0.0392592f}, -{-0.463049f,-0.112775f,0.153268f},{-0.438703f,0.274789f,0.168599f},{0.165303f,0.106826f,0.204071f}, -{-0.453513f,-0.111006f,0.0386933f},{-0.306514f,-0.108421f,0.293007f},{-0.226227f,-0.108337f,0.293045f}, -{0.333002f,0.0246938f,0.134394f},{0.347587f,0.00345971f,0.133533f},{0.338957f,0.0162631f,0.133475f}, -{0.0406707f,-0.103675f,0.367075f},{0.0599434f,-0.103566f,0.367712f},{0.0792289f,-0.103592f,0.367641f}, -{0.159477f,-0.104897f,0.348362f},{-0.290444f,0.419639f,-0.358619f},{-0.108759f,0.0726922f,0.186817f}, -{-0.265885f,0.250294f,0.225896f},{-0.335877f,0.29619f,0.167975f},{0.0435195f,-0.0766727f,0.33979f}, -{-0.186865f,-0.0281727f,-0.11972f},{-0.352957f,-0.0965949f,0.276917f},{-0.338732f,-0.0955661f,0.281258f}, -{-0.319453f,-0.0954246f,0.283238f},{-0.301447f,-0.0940806f,0.28956f},{-0.29364f,-0.0991994f,0.295386f}, -{-0.28083f,-0.0990579f,0.297534f},{-0.261596f,-0.0989743f,0.298177f},{-0.24878f,-0.0991415f,0.296164f}, -{-0.241012f,-0.0939198f,0.291701f},{-0.226195f,-0.0947751f,0.292621f},{-0.216536f,-0.0945114f,0.296106f}, -{-0.206923f,-0.0944471f,0.296865f},{-0.197109f,-0.0947172f,0.293367f},{-0.184235f,-0.0950259f,0.288338f}, -{-0.165072f,-0.0954438f,0.282718f},{-0.145786f,-0.0953667f,0.28421f},{-0.127684f,-0.0940548f,0.290788f}, -{-0.120019f,-0.0992058f,0.29446f},{-0.105929f,-0.0954053f,0.296241f},{-0.0879361f,-0.0943121f,0.298826f}, -{-0.069956f,-0.0952252f,0.298318f},{-0.0487605f,-0.0967557f,0.301926f},{-0.181438f,0.136401f,0.0929488f}, -{-0.0902769f,0.101141f,0.113881f},{0.0040931f,-0.0975982f,0.353577f},{0.0213594f,-0.0953667f,0.354574f}, -{0.0420018f,-0.0918363f,0.357538f},{0.0599562f,-0.0907559f,0.35831f},{0.0792354f,-0.0907495f,0.358773f}, -{0.0985145f,-0.0908138f,0.358329f},{0.117716f,-0.0909424f,0.355905f},{0.133748f,-0.0955853f,0.354484f}, -{0.138578f,-0.0892833f,0.347783f},{0.156268f,-0.0918363f,0.341719f},{0.173933f,-0.0944986f,0.336664f}, -{0.19156f,-0.0969615f,0.333533f},{-0.26022f,0.285888f,0.170528f},{0.213739f,-0.0920099f,0.319154f}, -{0.233475f,-0.093296f,0.314524f},{0.251166f,-0.0991415f,0.313752f},{-0.374191f,0.0666924f,0.352407f}, -{-0.451043f,-0.0192405f,0.0961448f},{-0.332269f,-0.219575f,0.259066f},{0.16922f,0.0707759f,0.240893f}, -{0.338417f,-0.0910389f,-0.0671361f},{0.346854f,-0.0555995f,0.209562f},{0.261487f,0.087174f,0.165654f}, -{0.0286261f,0.125449f,0.0756375f},{-0.129755f,0.108183f,0.00322178f},{-0.392241f,0.283785f,0.039973f}, -{-0.228768f,0.249053f,0.13489f},{-0.447918f,0.154857f,0.0198f},{-0.28085f,-0.0902543f,0.293039f}, -{-0.26159f,-0.0902672f,0.292994f},{-0.11362f,-0.0901707f,0.293765f},{-0.0622456f,-0.0902736f,0.291688f}, -{-0.0538922f,-0.0907688f,0.293026f},{0.134308f,-0.0709559f,0.325321f},{-0.470091f,0.152883f,0.210977f}, -{0.0999292f,-0.0751423f,0.343082f},{0.00420885f,-0.0888267f,0.342671f},{0.0204013f,-0.0871612f,0.346156f}, -{0.0343429f,-0.0868396f,0.348561f},{0.178763f,-0.0881322f,0.330054f},{0.19646f,-0.0908845f,0.326806f}, -{0.253796f,-0.0897463f,0.309816f},{0.156384f,0.070808f,0.241735f},{-0.468966f,0.0231054f,0.152175f}, -{0.280695f,0.0716119f,0.166708f},{-0.357554f,0.245761f,-0.184843f},{0.374049f,-0.0370535f,0.0402302f}, -{-0.186544f,-0.115649f,-0.13042f},{-0.335002f,0.0190412f,-0.0548728f},{-0.162197f,0.122311f,0.0764606f}, -{-0.354558f,-0.0732066f,0.268615f},{-0.337439f,-0.0784991f,0.277329f},{-0.319511f,-0.0774187f,0.27992f}, -{-0.300167f,-0.0772065f,0.283534f},{-0.280869f,-0.0770072f,0.286615f},{-0.261596f,-0.0769043f,0.287316f}, -{-0.242278f,-0.0769107f,0.287534f},{-0.222974f,-0.0768271f,0.289772f},{-0.20372f,-0.0766727f,0.290885f}, -{-0.18439f,-0.0770264f,0.285644f},{-0.16513f,-0.0773223f,0.281798f},{-0.145883f,-0.0773737f,0.280351f}, -{-0.126475f,-0.0771165f,0.285656f},{-0.108514f,-0.0758368f,0.290955f},{-0.100752f,-0.0810135f,0.295367f}, -{-0.0879618f,-0.0809556f,0.29655f},{-0.0686634f,-0.0767049f,0.291386f},{-0.0557764f,-0.081052f,0.293881f}, -{-0.050201f,-0.0766663f,0.296762f},{-0.0362336f,-0.0759976f,0.29799f},{0.40091f,-0.245092f,0.150394f}, -{-0.0724446f,0.0829427f,0.152985f},{0.402138f,-0.256538f,0.157493f},{-0.490245f,0.0784348f,0.246738f}, -{-0.210395f,0.208585f,0.0772065f},{-0.292573f,0.346523f,-0.263657f},{0.134423f,-0.0784412f,0.33788f}, -{0.156352f,-0.0750201f,0.324215f},{0.175657f,-0.0749815f,0.322626f},{0.194833f,-0.0749622f,0.319424f}, -{0.212852f,-0.0760747f,0.314954f},{0.227038f,-0.0798431f,0.313025f},{0.234697f,-0.0746085f,0.310575f}, -{0.250323f,-0.0727565f,0.304672f},{0.0985466f,0.0534002f,0.261689f},{-0.16412f,0.0707566f,0.18865f}, -{-0.316167f,0.149918f,-0.0431754f},{-0.390055f,0.201903f,0.312916f},{-0.377869f,0.291637f,-0.000598033f}, -{0.117048f,0.126986f,0.171859f},{0.228131f,0.107681f,0.147397f},{-0.298328f,0.289502f,0.188965f}, -{-0.171721f,0.126465f,0.0792193f},{-0.394267f,0.293843f,0.118659f},{-0.368435f,0.269805f,-0.18838f}, -{-0.343079f,-0.0673612f,0.271503f},{-0.0879618f,-0.0721327f,0.292659f},{-0.0188386f,-0.0762934f,0.300775f}, -{-0.431944f,0.281341f,0.151494f},{-0.319935f,0.295103f,0.0825183f},{-0.449281f,0.0184367f,0.188753f}, -{-0.495003f,0.077213f,0.174181f},{-0.299055f,0.379898f,-0.382258f},{-0.454696f,-0.0543648f,0.153725f}, -{-0.393733f,0.325218f,-0.129874f},{-0.373265f,-0.262551f,0.170258f},{-0.291672f,0.307296f,-0.114421f}, -{0.0441111f,0.40254f,-0.166966f},{-0.468316f,-0.114234f,0.0966721f},{0.220575f,-0.0708402f,0.312273f}, -{-0.351915f,0.39645f,-0.316678f},{-0.262812f,-0.285476f,0.229433f},{0.35421f,-0.0017234f,0.0884666f}, -{-0.205791f,-0.115848f,-0.128941f},{0.0423748f,-0.0679528f,0.322678f},{-0.112154f,0.0929166f,-0.0174592f}, -{-0.338815f,-0.0536574f,0.266815f},{-0.318192f,-0.0603067f,0.277399f},{-0.300148f,-0.0547378f,0.279798f}, -{-0.280869f,-0.0545706f,0.282075f},{-0.261583f,-0.0544162f,0.28419f},{-0.242285f,-0.0545127f,0.282068f}, -{-0.223012f,-0.0546413f,0.279824f},{-0.202794f,-0.0572007f,0.28174f},{-0.184409f,-0.0545899f,0.280403f}, -{-0.16585f,-0.0558438f,0.278448f},{-0.145844f,-0.0640237f,0.276596f},{-0.126539f,-0.059355f,0.278518f}, -{-0.106437f,-0.0557217f,0.281663f},{-0.0879618f,-0.0543713f,0.284711f},{-0.069538f,-0.056847f,0.288081f}, -{-0.0525224f,-0.0628726f,0.294936f},{-0.0349024f,-0.0605189f,0.295386f},{-0.0236681f,-0.0628726f,0.295888f}, -{-0.0159191f,-0.0575223f,0.291746f},{0.00101924f,-0.0573551f,0.29255f},{0.0210186f,-0.0616893f,0.296447f}, -{0.037121f,-0.0612199f,0.300068f},{0.0586637f,-0.0628147f,0.316852f},{-0.336025f,-0.204122f,0.266287f}, -{-0.467313f,0.0428668f,0.0391242f},{-0.225179f,0.228018f,0.115655f},{0.117453f,-0.0579981f,0.303013f}, -{0.137375f,-0.0581846f,0.304575f},{0.154191f,-0.0535481f,0.307862f},{0.159882f,-0.0619465f,0.315077f}, -{0.177039f,-0.058146f,0.316704f},{0.193579f,-0.0581074f,0.315887f},{0.207572f,-0.0610977f,0.313475f}, -{0.215424f,-0.0565769f,0.310035f},{0.231745f,-0.0562232f,0.30453f},{0.137086f,0.0706859f,0.241111f}, -{-0.378223f,0.24814f,-0.0724864f},{-0.335221f,0.135822f,-0.0458699f},{-0.399457f,-0.0121604f,-0.0235491f}, -{-0.0535578f,0.379184f,-0.110112f},{-0.373367f,-0.0371435f,-0.0575544f},{-0.341786f,0.0280763f,0.335539f}, -{-0.308058f,0.311559f,-0.0721327f},{-0.401502f,0.251773f,0.254165f},{-0.373033f,-0.0535931f,0.248455f}, -{-0.35549f,-0.055876f,0.259921f},{-0.321003f,-0.0483972f,0.272152f},{-0.145857f,-0.0505321f,0.271638f}, -{-0.0525482f,-0.0540433f,0.291367f},{-0.0397254f,-0.0538568f,0.293502f},{0.0228385f,-0.0523263f,0.291245f}, -{-0.30326f,0.295431f,0.163101f},{0.0406771f,-0.0539018f,0.293322f},{0.0602585f,-0.0515353f,0.292653f}, -{-0.457262f,-0.0493746f,0.141185f},{0.0962509f,-0.0508729f,0.29354f},{0.101492f,-0.0563455f,0.300158f}, -{0.169477f,-0.0530979f,0.3128f},{0.201296f,-0.0528472f,0.312299f},{0.117749f,0.053233f,0.260467f}, -{0.390685f,-0.111643f,0.156985f},{-0.475345f,0.161223f,0.114472f},{-0.261197f,0.288878f,0.151693f}, -{-0.0545738f,0.307116f,-0.00269443f},{0.136841f,0.127707f,0.169808f},{-0.361349f,-0.0472461f,0.253355f}, -{-0.206948f,-0.0458634f,0.274229f},{-0.158558f,-0.0459149f,0.272531f},{-0.126572f,-0.0460757f,0.271059f}, -{-0.113704f,-0.0459535f,0.273689f},{-0.0673773f,-0.0400694f,0.280814f},{-0.0525675f,-0.0362882f,0.28356f}, -{-0.0365037f,-0.036121f,0.286338f},{-0.0172245f,-0.0361146f,0.28664f},{0.00206101f,-0.0360632f,0.286518f}, -{0.0208578f,-0.0357095f,0.289142f},{-0.392351f,0.292659f,0.0961898f},{0.0406771f,-0.0358573f,0.29028f}, -{0.0598984f,-0.035285f,0.291958f},{0.0781615f,-0.0362239f,0.293122f},{-0.475621f,0.174612f,0.133687f}, -{0.0998392f,-0.0348349f,0.29282f},{0.117716f,-0.0360889f,0.289277f},{0.136314f,-0.0352207f,0.285862f}, -{0.14387f,-0.045259f,0.296479f},{0.160956f,-0.0419407f,0.299695f},{0.17583f,-0.0401852f,0.302826f}, -{0.194936f,-0.0400952f,0.303386f},{0.214118f,-0.040243f,0.299997f},{0.400627f,-0.130632f,0.0948716f}, -{0.332828f,0.00700302f,-0.00363974f},{-0.330687f,0.0772065f,0.348323f},{-0.437873f,-0.0194528f,0.157249f}, -{-0.294431f,0.286209f,-0.0771293f},{-0.29355f,0.247529f,-0.0371885f},{0.0945532f,0.12343f,0.178676f}, -{-0.0610366f,0.0857529f,0.151423f},{-0.407038f,0.27657f,0.20301f},{-0.373811f,-0.0350985f,0.244642f}, -{-0.356866f,-0.0345455f,0.252011f},{-0.337137f,-0.0355423f,0.258706f},{-0.319485f,-0.0328349f,0.264853f}, -{-0.300148f,-0.032597f,0.270011f},{-0.282489f,-0.0302112f,0.270975f},{-0.277653f,-0.036822f,0.27556f}, -{-0.261583f,-0.0366419f,0.276969f},{-0.245507f,-0.0366934f,0.275818f},{-0.240671f,-0.0301212f,0.271554f}, -{-0.223006f,-0.0325391f,0.269052f},{-0.203727f,-0.0325648f,0.269348f},{-0.184435f,-0.0366419f,0.276364f}, -{-0.171515f,-0.0366676f,0.276287f},{-0.163728f,-0.0314395f,0.270943f},{-0.145728f,-0.0327385f,0.26304f}, -{-0.126591f,-0.0328221f,0.263175f},{-0.107337f,-0.0325198f,0.26794f},{-0.087949f,-0.0367898f,0.27574f}, -{0.0959036f,-0.034224f,0.294183f},{0.155201f,-0.0310601f,0.28484f},{0.232279f,-0.0337417f,0.288016f}, -{-0.13297f,0.0688852f,0.199402f},{0.391347f,-0.0905115f,0.120575f},{-0.222125f,0.188631f,0.0190476f}, -{-0.327948f,-0.150542f,-0.104074f},{-0.315054f,-0.145494f,-0.109051f},{-0.341986f,-0.0289958f,0.25367f}, -{-0.261596f,-0.0278126f,0.27212f},{-0.184415f,-0.0277805f,0.272699f},{-0.0880004f,-0.0278705f,0.270538f}, -{-0.0686055f,-0.0279026f,0.273438f},{0.0856082f,-0.0265715f,0.29401f},{0.17585f,-0.0274011f,0.289965f}, -{0.194974f,-0.0273303f,0.290267f},{0.214189f,-0.027311f,0.287695f},{0.194672f,0.06612f,0.235484f}, -{0.267969f,-0.0357545f,0.266493f},{-0.466728f,0.262821f,0.150381f},{0.385425f,-0.13188f,-0.0206167f}, -{0.389643f,-0.0724414f,0.075586f},{-0.281467f,0.0204109f,0.24486f},{-0.261525f,0.0205717f,0.241574f}, -{-0.310392f,0.290357f,-0.0356644f},{-0.145876f,0.111668f,0.115758f},{-0.432285f,0.0749429f,-0.0124433f}, -{-0.313697f,0.378174f,-0.37222f},{-0.167541f,-0.0280248f,-0.121835f},{-0.354661f,-0.0164046f,0.251362f}, -{-0.337542f,-0.0145526f,0.251555f},{-0.319485f,-0.0195621f,0.258197f},{-0.300148f,-0.0149705f,0.260056f}, -{-0.280862f,-0.014887f,0.261554f},{-0.261596f,-0.0147005f,0.263207f},{-0.242291f,-0.0145526f,0.263927f}, -{-0.222999f,-0.0146362f,0.261291f},{-0.203733f,-0.0146362f,0.263233f},{-0.184415f,-0.0144947f,0.265516f}, -{-0.164975f,-0.0144047f,0.263085f},{-0.148789f,-0.0191891f,0.257831f},{-0.144102f,-0.0126362f,0.253651f}, -{-0.127877f,-0.0141346f,0.253747f},{-0.120244f,-0.0194013f,0.257014f},{-0.106038f,-0.0157037f,0.259406f}, -{-0.0880133f,-0.0146104f,0.26248f},{-0.0687213f,-0.0144304f,0.26585f},{-0.0526189f,-0.0141989f,0.271464f}, -{-0.0365165f,-0.018411f,0.27801f},{-0.0172053f,-0.0138709f,0.278679f},{0.00208029f,-0.0137295f,0.280505f}, -{0.0214173f,-0.0134787f,0.285586f},{0.0407221f,-0.01335f,0.287695f},{0.0600013f,-0.0131635f,0.291328f}, -{0.0792225f,-0.0131957f,0.29118f},{0.0985016f,-0.0144626f,0.289393f},{0.117729f,-0.0134851f,0.284865f}, -{0.137124f,-0.0134915f,0.279895f},{0.154763f,-0.016321f,0.275908f},{0.17574f,-0.0187775f,0.277612f}, -{0.195f,-0.0187132f,0.281303f},{0.214209f,-0.0186554f,0.279039f},{0.232587f,-0.022443f,0.277393f}, -{0.234607f,-0.0142053f,0.268654f},{0.25132f,-0.0183917f,0.265387f},{0.269866f,-0.0204881f,0.259439f}, -{-0.120006f,0.380965f,0.00785186f},{0.372056f,-0.0647696f,-0.00768462f},{-0.243892f,0.0184367f,0.240076f}, -{-0.239089f,0.0251375f,0.2358f},{-0.0170766f,0.0842352f,0.193164f},{-0.263885f,0.147224f,-0.0295231f}, -{-0.18664f,-0.0739912f,-0.129475f},{0.000311863f,0.455419f,-0.155603f},{-0.150854f,0.281258f,-0.0423073f}, -{0.20698f,-0.0177743f,-0.10689f},{-0.319511f,-0.0106427f,0.254622f},{-0.113922f,-0.0103469f,0.254899f}, -{-0.0365615f,-0.00949807f,0.273014f},{0.159561f,-0.00969741f,0.272583f},{0.175689f,-0.00992251f,0.269599f}, -{0.195f,-0.00995464f,0.272146f},{0.214221f,-0.00994179f,0.270396f},{0.394685f,-0.113077f,0.0178708f}, -{0.400666f,-0.148715f,0.114189f},{-0.222929f,0.0250667f,0.234173f},{-0.475608f,0.112221f,0.0505772f}, -{0.077692f,-0.019337f,-0.134529f},{-0.316244f,0.168773f,-0.0416707f},{-0.110032f,0.377866f,-0.0883573f}, -{-0.296997f,0.14986f,-0.0396772f},{-0.315826f,0.0936369f,-0.0470403f},{0.190602f,-0.0323334f,-0.116961f}, -{0.0620462f,-0.0157037f,-0.135423f},{-0.20372f,0.0249639f,0.233382f},{-0.111054f,0.321475f,-0.0743384f}, -{-0.316868f,0.0550658f,-0.0475161f},{-0.337446f,-0.0023536f,0.255272f},{-0.318115f,0.00142761f,0.253362f}, -{-0.299672f,0.00104178f,0.252127f},{-0.280965f,0.00269444f,0.250243f},{-0.26159f,0.00277804f,0.250114f}, -{-0.242298f,0.00290668f,0.250043f},{-0.223019f,0.00295167f,0.25086f},{-0.205019f,0.00393556f,0.251522f}, -{-0.197296f,-0.00129897f,0.256673f},{-0.187644f,-0.0012604f,0.256731f},{-0.182808f,0.00538888f,0.250545f}, -{-0.165053f,0.00322176f,0.250944f},{-0.145741f,0.00293882f,0.246532f},{-0.126552f,0.00269444f,0.244899f}, -{-0.107318f,0.0028745f,0.247227f},{-0.0880004f,0.00292596f,0.24967f},{-0.070374f,0.00535674f,0.250912f}, -{-0.0655895f,-0.00108035f,0.257329f},{-0.0516415f,0.00210285f,0.261696f},{-0.0365937f,0.00380054f,0.265612f}, -{-0.0172245f,0.00390985f,0.270017f},{0.000485494f,0.00627631f,0.272699f},{0.00534707f,-0.000321504f,0.277117f}, -{0.0214173f,0.00432143f,0.27902f},{0.0407607f,0.00445003f,0.282512f},{0.0600077f,0.00450149f,0.284499f}, -{0.0792675f,0.00448859f,0.284557f},{0.0985081f,0.00456576f,0.284165f},{0.117723f,0.00455291f,0.283419f}, -{0.138642f,0.00290668f,0.279039f},{0.156853f,0.00437928f,0.2714f},{0.175657f,0.00355616f,0.265027f}, -{0.194974f,0.0035176f,0.265368f},{0.214241f,0.00346614f,0.263342f},{0.231874f,0.000932473f,0.260339f}, -{0.24642f,-0.00137614f,0.257316f},{0.252175f,0.00538245f,0.251587f},{0.26894f,0.00381982f,0.247175f}, -{0.322064f,-0.0572007f,0.2416f},{-0.291949f,0.326305f,-0.244404f},{0.269679f,-0.0710202f,-0.116948f}, -{-0.216819f,0.248796f,-0.00958167f},{-0.0730684f,-0.0932189f,-0.147172f},{-0.354384f,0.00255942f,0.286846f}, -{-0.148301f,-0.0143211f,-0.117212f},{-0.167258f,-0.115186f,-0.135996f},{-0.202228f,0.26403f,-0.0192791f}, -{-0.240812f,0.26194f,-0.00583259f},{-0.321658f,-0.108916f,-0.100903f},{-0.406144f,-0.109861f,-0.0514581f}, -{-0.184435f,0.0248224f,0.232462f},{-0.0559564f,0.00782613f,0.254391f},{0.236787f,0.00754962f,0.253677f}, -{-0.471461f,0.153892f,0.192476f},{0.354989f,-0.0195428f,0.165815f},{-0.0901997f,-0.0957333f,-0.143802f}, -{-0.354236f,0.000160776f,-0.0532459f},{0.134655f,-0.0128999f,-0.11999f},{-0.22026f,0.262988f,-0.0135172f}, -{-0.0951127f,0.335848f,-0.0834057f},{-0.0741166f,-0.106421f,-0.153333f},{-0.377425f,0.102852f,0.350574f}, -{-0.223025f,0.0160702f,0.241124f},{-0.203733f,0.0163467f,0.240127f},{-0.184435f,0.0163724f,0.239883f}, -{-0.165117f,0.0162567f,0.239793f},{-0.145761f,0.0162889f,0.238417f},{-0.100932f,0.0162567f,0.237523f}, -{-0.0880326f,0.0161667f,0.238951f},{-0.0687277f,0.0162567f,0.242089f},{-0.186582f,-0.0970837f,-0.130993f}, -{-0.205849f,-0.0971673f,-0.131198f},{-0.225983f,-0.0937719f,-0.131147f},{-0.244613f,-0.0927558f,-0.129166f}, -{0.0436095f,-0.0169962f,-0.133642f},{-0.408382f,0.166136f,-0.0139224f},{-0.148024f,-0.0964985f,-0.136401f}, -{-0.167329f,-0.09673f,-0.134388f},{-0.0181698f,-0.468686f,0.352194f},{-0.0138485f,-0.474975f,0.347751f}, -{-0.277743f,0.131031f,-0.0370406f},{-0.392981f,0.111701f,-0.0340182f},{-0.36958f,-0.147365f,-0.0868654f}, -{-0.335568f,-0.145288f,-0.100267f},{-0.244214f,-0.153127f,-0.128105f},{-0.0341115f,0.355397f,-0.101212f}, -{0.0795183f,-0.471322f,0.319308f},{-0.205611f,-0.152838f,-0.129764f},{-0.354609f,0.168599f,-0.0385261f}, -{-0.27928f,0.0571686f,0.270982f},{0.28728f,0.037973f,0.209768f},{-0.196344f,0.15885f,0.115514f}, -{-0.0909392f,0.321437f,-0.0765248f},{-0.411855f,0.0734253f,-0.021562f},{-0.0920967f,0.359847f,-0.0930324f}, -{0.00476189f,0.378239f,-0.128079f},{-0.257673f,0.263824f,0.00112538f},{-0.337176f,0.294003f,0.179235f}, -{0.193759f,-0.468075f,0.296003f},{0.203868f,-0.464377f,0.29401f},{0.226909f,-0.464377f,0.281824f}, -{0.232439f,-0.471187f,0.268165f},{-0.223359f,0.0587248f,0.226636f},{0.337877f,-0.0567763f,0.223099f}, -{0.283409f,-0.0394521f,0.260898f},{0.305871f,-0.038674f,0.243973f},{-0.359792f,0.411254f,-0.319675f}, -{-0.316932f,-0.0879393f,-0.100492f},{-0.295357f,0.0945178f,-0.0458249f},{0.0972734f,-0.0184753f,-0.130112f}, -{-0.319999f,0.0210283f,-0.0620752f},{-0.208865f,-0.48248f,0.296756f},{0.281396f,0.0496897f,0.200804f}, -{-0.0529083f,-0.470326f,0.357101f},{-0.0337449f,-0.470204f,0.355718f},{-0.224896f,-0.153005f,-0.128652f}, -{0.00438891f,-0.470583f,0.342085f},{0.0221504f,-0.470403f,0.336041f},{0.0362593f,-0.467927f,0.332909f}, -{0.0427028f,-0.471567f,0.329391f},{-0.263564f,-0.15341f,-0.125424f},{0.0613195f,-0.470731f,0.324369f}, -{0.0817562f,-0.450185f,0.318607f},{0.0937622f,-0.452223f,0.314877f},{-0.305151f,0.0611363f,0.319868f}, -{-0.261365f,0.170676f,0.226269f},{0.154873f,-0.453645f,0.302691f},{0.136127f,-0.0378509f,-0.128311f}, -{0.174036f,-0.45349f,0.29981f},{0.192531f,-0.450294f,0.297367f},{0.207353f,-0.448899f,0.294859f}, -{0.214967f,-0.453947f,0.292145f},{0.229758f,-0.449645f,0.285791f},{0.242529f,-0.450461f,0.277554f}, -{-0.0154175f,0.365416f,-0.0681393f},{-0.220948f,0.0948201f,0.228051f},{-0.00928267f,0.455734f,-0.103862f}, -{-0.298984f,0.267445f,-0.185949f},{0.306624f,-0.11352f,0.283611f},{-0.26986f,-0.102743f,-0.120941f}, -{-0.277705f,-0.108697f,-0.115308f},{-0.203251f,0.266191f,0.0780875f},{0.116006f,-0.473155f,0.309173f}, -{-0.393952f,-0.00196778f,0.282595f},{-0.260201f,-0.0911417f,-0.125533f},{-0.20664f,0.0760426f,0.208784f}, -{0.0097135f,0.476647f,-0.128587f},{-0.207714f,0.11282f,0.207672f},{-0.239873f,0.0960033f,0.249375f}, -{0.0360663f,0.124607f,0.138832f},{-0.112797f,0.338169f,-0.0814507f},{-0.242034f,-0.473245f,0.309964f}, -{-0.22116f,-0.473226f,0.307559f},{-0.200228f,-0.471837f,0.302923f},{-0.185547f,-0.471965f,0.29855f}, -{-0.175104f,-0.46884f,0.296363f},{-0.165863f,-0.473663f,0.29091f},{0.080335f,-0.0869425f,-0.19045f}, -{-0.0541687f,-0.450931f,0.358381f},{-0.0333912f,-0.452834f,0.356625f},{-0.0183178f,-0.453747f,0.352214f}, -{-0.00994504f,-0.450423f,0.349217f},{0.00439533f,-0.451465f,0.343661f},{0.0232372f,-0.450738f,0.337121f}, -{0.035777f,-0.450262f,0.33262f},{0.0452815f,-0.449876f,0.329552f},{0.0613517f,-0.449175f,0.324562f}, -{0.018665f,0.454686f,-0.13352f},{0.311247f,-0.0895276f,0.271033f},{0.271274f,-0.456088f,0.2118f}, -{0.285441f,-0.447471f,0.207325f},{-0.280271f,-0.151236f,-0.117662f},{-0.373528f,0.0931996f,-0.0408604f}, -{-0.292257f,0.000778124f,-0.0879071f},{0.397997f,-0.14896f,0.0178644f},{-0.242124f,0.112556f,0.245175f}, -{-0.261217f,-0.113392f,-0.12716f},{-0.296405f,0.362252f,-0.378322f},{0.115459f,-0.0204816f,-0.125121f}, -{-0.186608f,0.262988f,-0.0231375f},{0.291949f,0.00689366f,0.23196f},{-0.378467f,0.270114f,-0.0726857f}, -{-0.241282f,0.20991f,0.191331f},{-0.266432f,0.214675f,0.220945f},{-0.321922f,0.148928f,0.300318f}, -{-0.225006f,0.150625f,0.207273f},{-0.449828f,0.0165461f,0.208109f},{0.284508f,0.00328608f,0.240012f}, -{-0.427584f,0.278917f,0.17049f},{0.00499982f,0.381267f,-0.0894312f},{-0.0553069f,0.379377f,-0.0579595f}, -{-0.256998f,0.232591f,0.213659f},{0.270477f,-0.112845f,0.308093f},{0.344745f,-0.150709f,-0.0930774f}, -{0.192775f,-0.434263f,0.297823f},{0.209186f,-0.432417f,0.295637f},{0.219372f,-0.431883f,0.293373f}, -{0.231095f,-0.4316f,0.287881f},{0.244581f,-0.431517f,0.279149f},{0.255146f,-0.431465f,0.271811f}, -{0.26494f,-0.432809f,0.261349f},{0.27321f,-0.432964f,0.247317f},{0.285917f,-0.0718819f,0.279567f}, -{0.34805f,-0.0900614f,0.229112f},{0.284939f,-0.431819f,0.227575f},{0.290688f,-0.436957f,0.212559f}, -{-0.0194303f,0.32381f,-0.0228095f},{0.0190058f,0.416668f,-0.11754f},{-0.317016f,0.0406546f,0.324202f}, -{-0.225031f,-0.115771f,-0.13096f},{-0.29591f,-0.108099f,-0.111006f},{-0.128745f,-0.0962477f,-0.138915f}, -{-0.0921482f,0.378952f,-0.0964985f},{-0.257146f,0.212231f,0.213305f},{-0.31108f,0.215099f,0.26967f}, -{0.266625f,0.0558632f,0.208238f},{-0.0705347f,0.303026f,0.00482944f},{-0.160982f,0.264551f,0.0449439f}, -{-0.241436f,-0.45848f,0.31334f},{-0.221925f,-0.453098f,0.308042f},{-0.347986f,0.126401f,-0.0469438f}, -{-0.056741f,0.419929f,-0.059683f},{-0.0377255f,0.417562f,-0.0774766f},{0.00501911f,0.418546f,-0.108415f}, -{-0.108431f,0.360291f,-0.0161152f},{-0.123684f,0.360297f,0.0013183f},{0.118681f,-0.0667567f,0.323301f}, -{-0.0209672f,-0.434443f,0.352278f},{-0.0112955f,-0.434044f,0.34923f},{0.00418956f,-0.433484f,0.343494f}, -{-0.288412f,0.311244f,-0.202566f},{-0.248072f,0.078467f,0.258558f},{-0.239661f,0.0757982f,0.249683f}, -{-0.149638f,0.267715f,0.0368927f},{-0.259879f,0.0952252f,0.267863f},{-0.231166f,0.0767757f,0.241549f}, -{-0.0733899f,0.417614f,-0.038436f},{0.298727f,0.0175557f,0.219131f},{-0.259866f,0.132851f,0.251034f}, -{-0.312057f,0.0155944f,0.265104f},{-0.338449f,0.149506f,0.320871f},{-0.300669f,0.00340824f,-0.0818816f}, -{-0.352905f,-0.0371435f,-0.0703514f},{-0.11135f,0.359725f,-0.0874891f},{-0.109395f,-0.114292f,-0.144587f}, -{-0.244433f,-0.115784f,-0.131648f},{-0.468921f,0.0167261f,0.133224f},{0.366917f,-0.111482f,0.208218f}, -{-0.198383f,0.11336f,0.189576f},{-0.0144787f,0.417626f,-0.0947879f},{-0.221199f,0.0771487f,0.229909f}, -{0.388229f,-0.0951802f,0.152728f},{-0.0728047f,0.360516f,-0.0380631f},{-0.0275329f,0.417562f,-0.0853734f}, -{-0.167503f,0.30617f,0.0426546f},{-0.204106f,0.13197f,0.189602f},{-0.198376f,0.13516f,0.172773f}, -{-0.192216f,0.125488f,0.167673f},{0.361046f,-0.170882f,-0.0731745f},{-0.229083f,0.28365f,0.0875856f}, -{0.358242f,-0.113347f,0.225941f},{-0.319581f,0.0599724f,0.339983f},{-0.0514099f,0.365204f,-0.0523841f}, -{0.268168f,0.0377544f,0.222404f},{-0.223347f,0.267413f,0.095669f},{-0.0961931f,0.384071f,-0.0266679f}, -{-0.112733f,0.380348f,-0.00300953f},{-0.0167101f,0.49093f,-0.096177f},{-0.490592f,0.0611877f,0.262905f}, -{-0.0644706f,0.415916f,-0.0521012f},{-0.0676409f,0.398431f,-0.0530208f},{0.189097f,-0.416366f,0.29518f}, -{0.197984f,-0.414379f,0.292962f},{0.21145f,-0.415858f,0.291116f},{0.230838f,-0.413607f,0.284338f}, -{0.244838f,-0.415363f,0.277721f},{0.254284f,-0.413954f,0.270718f},{0.267223f,-0.413929f,0.261072f}, -{0.277094f,-0.414038f,0.252693f},{0.28557f,-0.413279f,0.244648f},{0.293492f,-0.417189f,0.231073f}, -{0.305878f,-0.416784f,0.212713f},{-0.227327f,0.128941f,0.224642f},{-0.223224f,0.112466f,0.227247f}, -{-0.318803f,0.192f,0.286119f},{-0.101537f,0.393704f,-0.0115687f},{-0.297634f,-0.0333687f,-0.0998939f}, -{0.380833f,-0.409485f,0.203865f},{-0.3536f,-0.14678f,-0.0937976f},{-0.433192f,-0.208842f,0.0983956f}, -{-0.296804f,0.112357f,-0.0439471f},{-0.311974f,-0.108466f,-0.106807f},{0.327903f,-0.112755f,0.266737f}, -{-0.312842f,0.0968265f,0.324909f},{-0.321896f,0.0938169f,0.336722f},{-0.133099f,0.360361f,0.0160252f}, -{-0.182062f,0.285933f,0.0615414f},{0.0271084f,0.458397f,-0.14642f},{-0.0142793f,0.398547f,-0.0889875f}, -{-0.260168f,0.0768786f,0.267303f},{-0.245635f,0.248346f,0.189332f},{-0.255288f,0.250031f,0.207112f}, -{-0.254149f,0.180946f,0.213967f},{-0.0864249f,0.361982f,-0.0313945f},{-0.160506f,0.299064f,0.0497283f}, -{-0.146526f,0.304022f,0.0418829f},{-0.16522f,0.285142f,0.054307f},{0.3565f,-0.0403074f,0.185563f}, -{0.401939f,-0.130542f,0.0756053f},{-0.471544f,0.0158323f,0.0947558f},{-0.214549f,0.188586f,0.0321405f}, -{-0.0324845f,0.379506f,-0.0701778f},{-0.00051769f,0.474737f,-0.113488f},{-0.44822f,-0.0546284f,0.0385583f}, -{-0.246645f,0.0419408f,0.241221f},{-0.339844f,0.168355f,0.316575f},{-0.247905f,0.0936947f,0.258853f}, -{0.301106f,-0.410617f,0.225131f},{-0.277724f,0.15476f,0.247806f},{-0.282046f,0.0767114f,0.280949f}, -{0.402042f,-0.408662f,0.188142f},{0.418954f,-0.410295f,0.170355f},{0.435513f,-0.408321f,0.152812f}, -{0.438639f,-0.413022f,0.133706f},{0.0369602f,0.397331f,-0.12026f},{-0.015006f,0.379615f,-0.0788013f}, -{-0.296013f,0.194772f,0.249735f},{-0.271403f,0.0767499f,0.273618f},{-0.296052f,0.233439f,0.253876f}, -{-0.0931256f,0.358702f,-0.026057f},{0.0373653f,0.416655f,-0.134652f},{0.289987f,-0.0919456f,0.286853f}, -{-0.30328f,0.230276f,0.258461f},{0.289203f,-0.015723f,0.248269f},{-0.302868f,0.212533f,0.260371f}, -{-0.44323f,-0.0558117f,0.0205267f},{-0.192968f,0.266171f,0.0703579f},{0.374203f,-0.082634f,-0.0143404f}, -{0.0159127f,0.46994f,-0.135108f},{0.0217646f,0.378901f,-0.095611f},{0.0231021f,0.473882f,-0.151879f}, -{0.301756f,-0.0208418f,0.240513f},{0.309402f,-0.0145526f,0.228989f},{0.182943f,-0.402064f,0.308781f}, -{0.195637f,-0.397074f,0.30462f},{0.208427f,-0.395601f,0.296775f},{0.216311f,-0.399395f,0.289367f}, -{0.231893f,-0.396225f,0.281206f},{0.2504f,-0.396482f,0.270808f},{0.267165f,-0.396199f,0.261143f}, -{0.278348f,-0.400424f,0.254674f},{0.287988f,-0.395203f,0.249285f},{0.299434f,-0.395974f,0.239967f}, -{0.307318f,-0.396444f,0.228726f},{0.313742f,-0.401936f,0.215215f},{-0.303987f,0.113392f,0.300588f}, -{-0.294502f,0.113173f,0.285811f},{0.31917f,-0.0224945f,0.222327f},{0.400608f,-0.39247f,0.205743f}, -{0.408922f,-0.397061f,0.1951f},{0.420208f,-0.392611f,0.18883f},{0.427822f,-0.397479f,0.177113f}, -{0.439635f,-0.392412f,0.1684f},{0.446156f,-0.399505f,0.150465f},{0.361876f,-0.0899392f,0.208707f}, -{-0.231739f,0.0949937f,0.240803f},{0.22797f,-0.0427896f,0.297444f},{-0.000562705f,0.412919f,-0.102955f}, -{0.208209f,-0.114646f,0.336581f},{0.325954f,-0.0160188f,0.209434f},{-0.229314f,-0.422893f,0.297373f}, -{0.335549f,-0.0257162f,0.204546f},{0.342674f,-0.0180701f,0.189614f},{0.25267f,0.0384875f,0.231658f}, -{-0.247024f,0.193113f,0.203543f},{-0.293878f,0.160278f,0.252693f},{-0.277229f,0.176194f,0.231568f}, -{-0.183457f,0.30354f,0.0561461f},{0.0275457f,0.419292f,-0.126272f},{-0.354101f,0.131256f,0.338709f}, -{0.00573935f,0.343995f,-0.0542169f},{-0.0781551f,0.4f,-0.0418507f},{-0.3189f,0.113263f,0.32172f}, -{-0.146404f,0.283489f,0.0418765f},{-0.334025f,0.0150671f,0.30518f},{-0.00970067f,0.32399f,-0.0313044f}, -{-0.182737f,0.266159f,0.0625575f},{-0.0179705f,0.45576f,-0.0938105f},{-0.000446955f,0.455728f,-0.11372f}, -{-0.320874f,0.213749f,0.28192f},{0.302707f,0.0025787f,0.224906f},{-0.0689592f,0.379358f,-0.0494132f}, -{-0.104206f,0.378342f,-0.0179094f},{-0.1296f,0.268184f,0.0197164f},{0.370371f,-0.0281856f,0.0563133f}, -{0.187129f,-0.392251f,0.315f},{0.316977f,-0.392238f,0.220057f},{-0.284277f,0.108646f,0.277837f}, -{-0.278888f,0.114041f,0.272088f},{0.150313f,-0.00275876f,0.275464f},{0.412485f,-0.387647f,0.200412f}, -{0.431275f,-0.387942f,0.182316f},{0.455345f,-0.389711f,0.15114f},{-0.0200026f,0.310922f,-0.0380052f}, -{0.175824f,0.0522877f,-0.0882287f},{0.283087f,-0.0592392f,0.275515f},{0.00377156f,0.361879f,-0.0719591f}, -{0.390254f,-0.131533f,-0.00779394f},{0.242452f,0.0339861f,0.239407f},{-0.238729f,0.194444f,0.190971f}, -{-0.316668f,0.169814f,0.286049f},{-0.261217f,0.112659f,0.26275f},{-0.0322916f,0.455812f,-0.074795f}, -{-0.0542909f,0.3464f,-0.0339217f},{-0.127864f,0.341989f,0.00335682f},{-0.115446f,0.305971f,0.0322626f}, -{-0.129755f,0.323848f,0.0176007f},{-0.0725218f,0.318768f,0.0133436f},{-0.207887f,0.168895f,0.0386483f}, -{0.0360728f,0.453194f,-0.156779f},{0.0101058f,0.457323f,-0.124903f},{-0.211533f,0.188663f,0.0418186f}, -{-0.0685862f,0.325848f,0.002135f},{0.307324f,-0.0717597f,0.265432f},{-0.0945404f,0.40009f,-0.0183209f}, -{0.177837f,-0.382605f,0.330009f},{0.191991f,-0.377975f,0.322234f},{0.200344f,-0.382894f,0.311617f}, -{0.213064f,-0.378001f,0.30754f},{0.230722f,-0.376554f,0.301514f},{0.236652f,-0.382116f,0.290717f}, -{0.250394f,-0.377563f,0.282364f},{0.258966f,-0.38229f,0.272062f},{0.270091f,-0.377743f,0.265541f}, -{0.283795f,-0.375737f,0.258988f},{0.290463f,-0.378882f,0.253516f},{0.305235f,-0.376612f,0.245362f}, -{0.313382f,-0.382438f,0.232005f},{0.322231f,-0.377113f,0.225607f},{-0.292052f,0.0570336f,0.285714f}, -{0.262085f,-0.0423974f,0.279104f},{-0.305601f,0.0956368f,0.315276f},{-0.297235f,0.0772451f,0.304858f}, -{-0.496135f,0.0952059f,0.111591f},{-0.169921f,0.26612f,0.0528215f},{0.402376f,-0.375493f,0.214102f}, -{0.417096f,-0.373068f,0.204971f},{0.426922f,-0.375383f,0.195081f},{0.43851f,-0.372522f,0.187312f}, -{0.445326f,-0.379178f,0.174316f},{0.456529f,-0.372972f,0.168065f},{0.463731f,-0.380155f,0.151333f}, -{-0.319536f,0.0236584f,0.302132f},{0.02316f,0.398142f,-0.110672f},{-0.396113f,-0.0172985f,0.225954f}, -{-0.27838f,0.0343655f,0.251625f},{-0.204119f,0.247343f,0.0765635f},{-0.317221f,0.415234f,-0.373956f}, -{-0.00159804f,0.325687f,-0.0402495f},{-0.334713f,0.133533f,0.32381f},{-0.0906884f,0.286422f,0.00184562f}, -{0.331388f,-0.0875084f,0.251304f},{0.323703f,-0.094878f,0.262667f},{-0.335253f,0.191968f,0.304112f}, -{-0.0371403f,0.436661f,-0.0764413f},{0.0386708f,0.435954f,-0.149725f},{-0.0347031f,0.34287f,-0.0372014f}, -{-0.0741873f,0.340799f,-0.01971f},{-0.326018f,0.186438f,0.29691f},{0.0039066f,0.436693f,-0.113276f}, -{-0.470258f,0.19256f,0.17213f},{-0.1118f,0.339095f,-0.00407059f},{0.211913f,-0.109598f,-0.153326f}, -{0.170133f,0.0323526f,0.258873f},{0.203135f,-0.373306f,0.315803f},{0.24377f,-0.372367f,0.295527f}, -{0.261847f,-0.372773f,0.276416f},{-0.305415f,0.195048f,0.264442f},{-0.305382f,0.0776631f,0.321167f}, -{-0.465918f,0.174753f,0.172265f},{-0.241661f,0.0589627f,0.245458f},{-0.233565f,0.247799f,0.157506f}, -{0.470959f,-0.370734f,0.151024f},{0.00161085f,0.397589f,-0.0976882f},{-0.24631f,0.12907f,0.242384f}, -{0.341066f,-0.0399023f,0.206759f},{-0.210633f,0.208341f,0.0389377f},{-0.16785f,0.247285f,0.0393235f}, -{-0.192801f,0.0938427f,0.183113f},{0.249326f,-0.038436f,0.283213f},{-0.337317f,0.0215106f,0.322909f}, -{-0.153317f,0.299264f,0.0489309f},{0.0229285f,0.436655f,-0.130948f},{0.337767f,-0.0763126f,-0.0658564f}, -{-0.144063f,0.340651f,0.0202759f},{-0.0110833f,0.31743f,-0.0566734f},{-0.22815f,0.170766f,0.189544f}, -{-0.182615f,0.378534f,-0.0300955f},{0.403109f,-0.148568f,0.0563969f},{0.359426f,-0.0361403f,0.00128615f}, -{0.189502f,-0.359159f,0.335854f},{0.199071f,-0.359313f,0.327063f},{0.213135f,-0.359082f,0.319141f}, -{0.232253f,-0.357442f,0.31525f},{0.237815f,-0.362953f,0.309649f},{0.249886f,-0.357718f,0.301347f}, -{0.256021f,-0.363133f,0.290749f},{0.267769f,-0.358059f,0.28192f},{0.274766f,-0.363615f,0.272499f}, -{0.288528f,-0.359326f,0.266628f},{0.309479f,-0.359493f,0.252706f},{0.320102f,-0.358857f,0.242069f}, -{0.329478f,-0.360316f,0.229523f},{0.249211f,0.0551815f,0.223446f},{0.263847f,-0.0760683f,0.298569f}, -{-0.261467f,0.0591171f,0.261985f},{0.400839f,-0.148748f,0.0371628f},{-0.24278f,0.229446f,0.191351f}, -{0.420253f,-0.356452f,0.208701f},{0.441384f,-0.356554f,0.195061f},{0.452979f,-0.35586f,0.185743f}, -{0.462535f,-0.357005f,0.173551f},{0.477017f,-0.356818f,0.152979f},{-0.0931771f,0.328195f,0.000559486f}, -{-0.147105f,0.322556f,0.0240057f},{-0.332674f,0.215286f,0.291341f},{-0.484425f,0.0606154f,0.1922f}, -{0.0209736f,0.359635f,-0.0773866f},{0.370621f,-0.103174f,0.19856f},{0.0443297f,0.398135f,-0.130356f}, -{-0.347979f,0.195595f,0.310697f},{-0.188872f,0.111746f,0.168046f},{-0.302122f,0.0224752f,0.262448f}, -{-0.19581f,0.158921f,0.094595f},{-0.474637f,0.189756f,0.11262f},{-0.328218f,0.118562f,0.327571f}, -{0.302302f,-0.353873f,0.261619f},{0.337844f,-0.355133f,0.222398f},{0.287023f,0.0218385f,0.224835f}, -{-0.330565f,0.168226f,0.307302f},{0.43289f,-0.351185f,0.201994f},{0.470535f,-0.351847f,0.165847f}, -{-0.129517f,0.3041f,0.0381531f},{-0.495852f,0.115186f,0.132401f},{-0.371998f,-0.281168f,0.0942992f}, -{0.320964f,0.00302242f,0.202135f},{-0.066895f,0.346728f,-0.0293945f},{-0.0516607f,0.323623f,-0.0016398f}, -{-0.305247f,0.172001f,0.264429f},{-0.369978f,0.442159f,-0.334922f},{-0.273551f,0.213196f,0.231227f}, -{-0.0336999f,0.398508f,-0.0774766f},{-0.297016f,0.176271f,0.248076f},{0.191695f,-0.341108f,0.339211f}, -{0.210138f,-0.341057f,0.326305f},{0.230517f,-0.340343f,0.322015f},{0.244433f,-0.338542f,0.315752f}, -{0.252831f,-0.341102f,0.306717f},{0.262657f,-0.338806f,0.296859f},{0.271904f,-0.341185f,0.289078f}, -{0.285769f,-0.339462f,0.279714f},{0.29328f,-0.344915f,0.271271f},{0.306579f,-0.339539f,0.265554f}, -{0.326128f,-0.340896f,0.248629f},{0.334359f,-0.345429f,0.234443f},{0.342423f,-0.340285f,0.225851f}, -{0.349587f,-0.343204f,0.214932f},{-0.295216f,0.28457f,-0.205325f},{0.340031f,-0.0957268f,0.244892f}, -{-0.453384f,0.0383525f,0.308678f},{-0.206524f,0.09455f,0.209112f},{0.435783f,-0.33696f,0.202939f}, -{0.446336f,-0.339204f,0.197138f},{0.457866f,-0.337822f,0.187351f},{0.466059f,-0.338111f,0.176547f}, -{0.471904f,-0.338459f,0.167892f},{0.481846f,-0.338864f,0.153969f},{0.303202f,-0.0957333f,0.280055f}, -{0.0814218f,-0.0515803f,0.29255f},{-0.292264f,0.345217f,-0.341024f},{-0.0167551f,0.356053f,-0.0587505f}, -{-0.19363f,0.107617f,0.182425f},{-0.164204f,0.243098f,0.0192984f},{-0.247487f,0.230038f,0.203376f}, -{-0.124462f,0.283644f,0.0264107f},{-0.219212f,0.247761f,0.0982477f},{-0.21082f,0.130903f,0.203293f}, -{-0.292605f,0.211903f,0.251201f},{-0.310257f,0.172207f,0.274229f},{-0.241192f,0.170798f,0.20827f}, -{-0.218337f,0.132022f,0.212688f},{0.319665f,-0.335674f,0.258641f},{0.337111f,-0.336053f,0.238623f}, -{-0.324443f,0.16811f,0.299051f},{-0.310984f,0.0778753f,0.329893f},{-0.319781f,0.0780104f,0.33977f}, -{-0.212723f,0.247503f,0.0867303f},{-0.111903f,0.327655f,0.00719593f},{-0.287132f,0.171544f,0.23987f}, -{-0.195708f,0.242571f,0.0661265f},{0.317562f,-0.076139f,0.257664f},{-0.106804f,0.318067f,0.0255683f}, -{-0.302739f,0.130337f,0.282878f},{-0.310894f,0.135976f,0.290222f},{-0.191585f,0.28338f,0.0687952f}, -{0.194016f,-0.319443f,0.337095f},{0.211733f,-0.321552f,0.328806f},{0.231353f,-0.322774f,0.323301f}, -{0.24642f,-0.322466f,0.316903f},{0.256098f,-0.322536f,0.308241f},{0.267371f,-0.321765f,0.298537f}, -{0.273506f,-0.326838f,0.293785f},{0.287344f,-0.322485f,0.285579f},{0.307743f,-0.323861f,0.269805f}, -{0.323073f,-0.32143f,0.26149f},{0.332546f,-0.323867f,0.251092f},{0.340134f,-0.320414f,0.243246f}, -{0.347445f,-0.324067f,0.22879f},{0.353149f,-0.328247f,0.21652f},{-0.298476f,0.095341f,0.302948f}, -{-0.291113f,0.0951159f,0.290724f},{-0.48919f,0.0783126f,0.230668f},{0.457493f,-0.320871f,0.190232f}, -{0.467326f,-0.324928f,0.17838f},{0.474811f,-0.320138f,0.16993f},{0.483763f,-0.321134f,0.157011f}, -{0.488161f,-0.321031f,0.148625f},{-0.0857175f,0.396045f,-0.0339217f},{0.345233f,-0.114582f,0.246526f}, -{-0.0343365f,0.360728f,-0.0558889f},{-0.277596f,0.193653f,0.228842f},{-0.455699f,-0.0891225f,0.038719f}, -{0.402235f,-0.13053f,0.0563583f},{-0.488714f,0.0780746f,0.192135f},{0.346564f,-0.1702f,-0.0924986f}, -{-0.360197f,0.287618f,-0.207794f},{0.169702f,-0.313784f,0.354754f},{0.299865f,-0.318048f,0.275464f}, -{0.355612f,-0.314749f,0.220244f},{-0.177753f,0.242481f,0.0468217f},{0.448632f,-0.315919f,0.196881f}, -{0.311498f,0.00544677f,0.212662f},{-0.243262f,0.14869f,0.227414f},{-0.0769911f,0.379383f,-0.04405f}, -{-0.314482f,0.133552f,-0.0457734f},{-0.427822f,0.00120256f,0.0205267f},{-0.297923f,0.24859f,-0.113122f}, -{-0.236767f,0.180811f,0.193653f},{-0.285808f,0.376689f,-0.360123f},{-0.288335f,0.304633f,-0.13287f}, -{-0.354223f,-0.222089f,0.246018f},{0.206987f,-0.310447f,0.33343f},{0.219893f,-0.301328f,0.329514f}, -{0.23089f,-0.301251f,0.3228f},{0.244728f,-0.30273f,0.315688f},{0.254548f,-0.299733f,0.308582f}, -{0.269043f,-0.300884f,0.298859f},{0.289016f,-0.300582f,0.288184f},{0.30128f,-0.300659f,0.277631f}, -{0.308932f,-0.302312f,0.272866f},{0.325871f,-0.299572f,0.263927f},{0.333214f,-0.309154f,0.255915f}, -{0.343516f,-0.299733f,0.246076f},{0.351194f,-0.306865f,0.232899f},{-0.324096f,0.127372f,0.316569f}, -{0.458355f,-0.303045f,0.188367f},{0.474715f,-0.303276f,0.172342f},{0.484348f,-0.303739f,0.158034f}, -{0.488881f,-0.300743f,0.148433f},{-0.185644f,0.247124f,0.0580239f},{-0.318411f,0.0184432f,0.285946f}, -{-0.0494679f,0.415948f,-0.0680621f},{-0.266908f,0.231768f,0.224417f},{0.375901f,-0.0972251f,-0.0192984f}, -{-0.310476f,0.0367319f,0.308395f},{0.325941f,-0.0720298f,0.247214f},{-0.0904505f,0.342626f,-0.0160445f}, -{0.323137f,-0.0387383f,0.22861f},{-0.34477f,0.182007f,0.315257f},{-0.482824f,0.0947237f,0.0602039f}, -{0.172461f,-0.296769f,0.359423f},{0.180094f,-0.302177f,0.350683f},{0.192299f,-0.297347f,0.346175f}, -{0.209636f,-0.297097f,0.337314f},{0.358738f,-0.296884f,0.225099f},{-0.231481f,0.144838f,0.218501f}, -{-0.174866f,0.25187f,0.0510595f},{0.0382849f,0.358741f,-0.0940355f},{0.00948199f,0.40337f,-0.105437f}, -{-0.0533199f,0.355944f,-0.0437928f},{-0.329292f,0.154535f,0.309013f},{-0.299132f,0.0402945f,0.28493f}, -{-0.469062f,0.0256133f,0.0585576f},{-0.0876338f,0.318446f,0.0195621f},{-0.282431f,0.21445f,0.244815f}, -{-0.185753f,0.0938748f,0.170605f},{-0.2486f,0.162773f,0.222147f},{-0.409804f,-0.0203466f,-0.0172149f}, -{0.246278f,-0.0565512f,0.297617f},{0.31135f,-0.282692f,0.272609f},{0.371734f,-0.293926f,0.198457f}, -{0.274052f,-0.011035f,0.254854f},{0.462734f,-0.28037f,0.176682f},{0.473396f,-0.283309f,0.168239f}, -{0.482065f,-0.280004f,0.154928f},{0.488502f,-0.286955f,0.147256f},{0.355336f,-0.0938298f,0.221742f}, -{-0.385644f,-0.0936176f,-0.070255f},{-0.229443f,0.266911f,0.112292f},{-0.28103f,0.232713f,0.246024f}, -{-0.0279059f,0.436661f,-0.0857271f},{-0.0987525f,0.336832f,-0.00780037f},{-0.132468f,0.287444f,0.033761f}, -{0.192711f,-0.279515f,0.352574f},{0.201521f,-0.279393f,0.348388f},{0.211668f,-0.279393f,0.340433f}, -{0.22242f,-0.27938f,0.333378f},{0.233243f,-0.279367f,0.326395f},{0.245172f,-0.27783f,0.31727f}, -{0.253358f,-0.280332f,0.307932f},{0.268136f,-0.279386f,0.297643f},{0.288328f,-0.279059f,0.287335f}, -{0.303903f,-0.277464f,0.277837f},{0.32661f,-0.278718f,0.263561f},{0.345934f,-0.278891f,0.24623f}, -{0.36021f,-0.279412f,0.227253f},{0.36857f,-0.279464f,0.207698f},{0.372068f,-0.285174f,0.199029f}, -{-0.499177f,0.0955082f,0.153455f},{-0.482046f,0.209968f,0.133494f},{-0.0162342f,0.337552f,-0.0352014f}, -{-0.076258f,0.309418f,0.0135044f},{-0.0918524f,0.0832128f,-0.0219671f},{-0.349998f,0.15015f,0.330182f}, -{-0.290624f,0.0768914f,0.291476f},{0.281312f,-0.0972187f,0.295515f},{-0.0157326f,0.436661f,-0.096402f}, -{-0.016067f,0.313353f,-0.0512716f},{-0.266927f,0.0447767f,0.256918f},{-0.324392f,0.0462815f,0.338529f}, -{-0.298624f,0.0591621f,0.303161f},{0.353098f,-0.102164f,0.229716f},{-0.21318f,0.261574f,0.0861387f}, -{0.492849f,-0.283226f,0.134684f},{0.0356741f,0.375403f,-0.101309f},{0.269242f,-0.0938169f,0.302961f}, -{0.379361f,-0.150484f,-0.0398829f},{0.270863f,-0.0711231f,0.289502f},{0.362139f,-0.151886f,-0.0719526f}, -{-0.462303f,0.0608019f,0.0229896f},{-0.0906884f,0.305135f,0.0184882f},{-0.0543552f,0.337352f,-0.0225845f}, -{-0.2624f,0.148439f,0.243992f},{-0.293184f,0.131089f,0.270634f},{-0.197296f,0.094132f,0.193865f}, -{-0.279531f,0.0439793f,0.261503f},{-0.1169f,0.271001f,0.0107649f},{0.190441f,-0.262172f,0.355063f}, -{0.201984f,-0.261895f,0.349217f},{0.213565f,-0.261863f,0.343372f},{0.227301f,-0.261754f,0.334973f}, -{0.236838f,-0.263168f,0.327996f},{0.24894f,-0.260178f,0.320993f},{0.256567f,-0.266352f,0.309225f}, -{0.266528f,-0.260603f,0.301051f},{0.273293f,-0.266474f,0.293804f},{0.286933f,-0.261959f,0.285431f}, -{0.304257f,-0.261676f,0.276583f},{0.313299f,-0.261484f,0.27275f},{0.326166f,-0.261477f,0.263066f}, -{0.345298f,-0.261664f,0.245465f},{0.360191f,-0.262101f,0.227369f},{0.368622f,-0.262718f,0.211286f}, -{0.373535f,-0.263258f,0.201196f},{0.355747f,-0.0973281f,-0.0497219f},{-0.153548f,0.343011f,0.0336388f}, -{0.475203f,-0.262641f,0.15096f},{0.484431f,-0.260873f,0.133886f},{0.296695f,-0.0675219f,0.272589f}, -{0.0291791f,0.36586f,-0.0880936f},{-0.252953f,0.0538568f,0.254911f},{-0.296611f,0.305817f,-0.244442f}, -{0.394196f,-0.149301f,-0.00137614f},{-0.294939f,0.0174657f,0.250101f},{-0.0417382f,0.337487f,-0.0269187f}, -{-0.111884f,0.285277f,0.0182438f},{-0.300199f,-0.305321f,0.170843f},{-0.310874f,0.195203f,0.273663f}, -{-0.00157232f,0.374972f,-0.0819909f},{-0.467641f,-0.114054f,0.135192f},{-0.445532f,-0.0192534f,0.13469f}, -{-0.48319f,0.0278319f,0.0975724f},{0.335234f,-0.16685f,-0.104138f},{-0.446426f,0.0595158f,0.0026816f}, -{-0.0854732f,-0.131076f,-0.165879f},{-0.210762f,0.14786f,0.186386f},{-0.0732292f,0.438963f,-0.0937912f}, -{-0.352095f,0.359474f,-0.285496f},{-0.0802643f,0.422379f,-0.0257162f},{-0.00456257f,0.356265f,-0.0636635f}, -{0.257931f,-0.0343397f,0.272988f},{-0.107009f,0.301945f,0.0255362f},{-0.00114789f,0.3377f,-0.0415099f}, -{-0.0524774f,0.437343f,-0.0565962f},{0.0514549f,0.383396f,-0.136047f},{-0.292032f,0.0369249f,0.266107f}, -{-0.306997f,0.0260635f,0.278821f},{0.191142f,-0.244854f,0.356233f},{0.203566f,-0.244513f,0.351693f}, -{0.214048f,-0.244507f,0.344246f},{0.231854f,-0.244236f,0.33615f},{0.252522f,-0.244571f,0.323925f}, -{0.270097f,-0.244931f,0.304042f},{0.288315f,-0.245921f,0.287566f},{0.302289f,-0.242121f,0.279554f}, -{0.3109f,-0.245703f,0.273117f},{0.325864f,-0.244674f,0.265188f},{0.345368f,-0.244359f,0.245671f}, -{0.36014f,-0.244777f,0.227472f},{0.36967f,-0.245246f,0.212874f},{0.375084f,-0.245651f,0.203595f}, -{0.381914f,-0.246275f,0.190669f},{0.38936f,-0.245638f,0.174753f},{0.291145f,-0.0359409f,0.253278f}, -{0.254272f,-0.0530337f,0.29037f},{-0.28575f,0.144632f,0.258808f},{0.0141571f,0.329494f,-0.0638436f}, -{-0.414929f,0.245169f,0.0165204f},{-0.317208f,0.131674f,0.303855f},{-0.204415f,-0.261458f,0.263316f}, -{-0.423675f,-0.108736f,-0.0320054f},{0.29573f,0.0261407f,0.214739f},{-0.307177f,0.0458571f,0.313128f}, -{0.244658f,-0.239465f,0.332022f},{0.264008f,-0.239696f,0.314607f},{0.281171f,-0.240037f,0.294113f}, -{0.33515f,-0.239632f,0.25931f},{-0.475133f,0.157525f,0.0939584f},{-0.381605f,0.418109f,-0.31127f}, -{-0.317819f,-0.314434f,0.132928f},{0.387007f,-0.149873f,-0.0206167f},{-0.213379f,0.0719012f,0.221864f}, -{-0.300649f,0.284679f,-0.225845f},{-0.0719495f,0.00154982f,-0.110222f},{-0.294026f,0.342208f,-0.359886f}, -{-0.416755f,-0.208431f,0.173589f},{-0.397791f,0.244089f,-0.00489371f},{-0.0996592f,0.290222f,0.0133694f}, -{-0.0757886f,0.290743f,-0.00540174f},{-0.0452944f,0.384206f,-0.0660557f},{-0.0610817f,0.441446f,-0.0430918f}, -{-0.307685f,0.144851f,0.27774f},{0.38801f,-0.112652f,-0.00266871f},{0.336725f,-0.000199339f,0.183357f}, -{0.230157f,0.0578695f,-0.0557795f},{0.193065f,-0.22724f,0.359236f},{0.207109f,-0.22553f,0.353204f}, -{0.214093f,-0.228404f,0.348246f},{0.231925f,-0.226526f,0.34206f},{0.245539f,-0.226372f,0.333423f}, -{0.25296f,-0.226436f,0.327153f},{0.264831f,-0.2256f,0.31826f},{0.273602f,-0.228938f,0.305939f}, -{0.2842f,-0.224437f,0.298788f},{0.293023f,-0.228031f,0.292685f},{0.307209f,-0.227182f,0.283605f}, -{0.325337f,-0.226662f,0.267792f},{0.33742f,-0.225176f,0.258924f},{0.347478f,-0.227542f,0.246391f}, -{0.360371f,-0.227433f,0.227928f},{0.370146f,-0.227825f,0.213839f},{0.375457f,-0.228237f,0.204347f}, -{0.382949f,-0.225581f,0.190965f},{0.389721f,-0.227414f,0.176367f},{-0.464149f,0.17485f,0.191557f}, -{0.335317f,-0.00309312f,-0.0148612f},{-0.294605f,0.246108f,-0.0770907f},{-0.231636f,0.247857f,0.147918f}, -{-0.257378f,0.195544f,0.212797f},{-0.300174f,0.149108f,0.266191f},{-0.266483f,0.126832f,0.258397f}, -{-0.184467f,0.073078f,-0.0691939f},{0.316778f,-0.222109f,0.278094f},{0.393463f,-0.22135f,0.167634f}, -{0.398994f,-0.222141f,0.152883f},{0.40428f,-0.223215f,0.133481f},{-0.224337f,0.188322f,0.154188f}, -{0.0944117f,-0.00196778f,-0.126137f},{-0.141645f,0.261509f,0.0257677f},{-0.153027f,0.289978f,0.0494518f}, -{-0.202595f,0.225678f,0.0589692f},{-0.350725f,0.323347f,-0.265856f},{0.371689f,-0.0461207f,0.136742f}, -{-0.287788f,0.306292f,-0.147879f},{0.193508f,-0.20555f,0.359879f},{0.21318f,-0.209279f,0.354638f}, -{0.230407f,-0.209041f,0.345648f},{0.243481f,-0.20899f,0.33624f},{0.254413f,-0.208913f,0.329443f}, -{0.267898f,-0.208861f,0.320684f},{0.276869f,-0.209016f,0.310929f},{0.288103f,-0.209697f,0.301347f}, -{0.308038f,-0.2089f,0.288222f},{0.319832f,-0.207517f,0.278834f},{0.32796f,-0.209993f,0.269477f}, -{0.338552f,-0.209286f,0.258751f},{0.349098f,-0.209562f,0.245799f},{0.361194f,-0.205698f,0.22924f}, -{0.370628f,-0.206161f,0.21452f},{0.37565f,-0.206508f,0.204926f},{0.381772f,-0.204598f,0.19265f}, -{-0.18529f,0.230616f,0.0361275f},{-0.192518f,0.233105f,0.0540176f},{-0.26629f,0.193145f,0.221073f}, -{-0.309517f,0.153905f,0.275007f},{0.00359793f,-0.0376451f,-0.136844f},{0.400395f,-0.112588f,0.056339f}, -{0.377425f,-0.112543f,0.18883f},{-0.27231f,0.227221f,0.233259f},{-0.392177f,0.323874f,-0.150098f}, -{0.392782f,-0.0995724f,0.0242693f},{0.297447f,-0.204656f,0.295566f},{0.389238f,-0.203325f,0.179113f}, -{0.393997f,-0.203846f,0.168702f},{0.400254f,-0.203286f,0.152606f},{0.404016f,-0.205543f,0.137488f}, -{0.406575f,-0.203736f,0.0958555f},{0.373728f,-0.0370471f,0.0916627f},{-0.285036f,0.196302f,0.239285f}, -{-0.49265f,0.113205f,0.172702f},{0.391894f,-0.0905887f,0.0307064f},{-0.0491335f,0.400045f,-0.0677341f}, -{0.387496f,-0.131507f,0.172104f},{-0.30445f,0.042571f,0.302254f},{-0.289537f,0.331526f,-0.205775f}, -{0.210852f,-0.192984f,0.353641f},{0.230388f,-0.186656f,0.3435f},{0.2464f,-0.187447f,0.334999f}, -{0.256336f,-0.189357f,0.329636f},{0.26829f,-0.187139f,0.321404f},{0.277544f,-0.187287f,0.312067f}, -{0.287859f,-0.187396f,0.302119f},{0.297582f,-0.195955f,0.295823f},{0.3078f,-0.187319f,0.288036f}, -{0.320893f,-0.187293f,0.278641f},{0.330063f,-0.187415f,0.269316f},{0.336108f,-0.187647f,0.261021f}, -{0.3465f,-0.187949f,0.247799f},{0.405495f,-0.20337f,0.114061f},{-0.463545f,0.155307f,0.0387769f}, -{0.349606f,-0.0372335f,0.194515f},{-0.25541f,0.153847f,0.234912f},{0.335838f,-0.0763834f,0.238951f}, -{0.275666f,0.0441979f,0.211537f},{-0.40626f,0.244191f,0.00486803f},{-0.247198f,0.214655f,0.203582f}, -{-0.194151f,0.251947f,0.0684094f},{-0.393611f,-0.26093f,0.0749301f},{-0.336989f,0.11408f,0.337558f}, -{0.193643f,-0.184219f,0.35469f},{0.214215f,-0.182181f,0.348728f},{0.363619f,-0.184116f,0.227555f}, -{0.372885f,-0.184528f,0.21245f},{0.377219f,-0.183409f,0.203685f},{0.383579f,-0.18629f,0.191351f}, -{0.389901f,-0.185685f,0.179049f},{0.393913f,-0.185467f,0.168824f},{0.399412f,-0.186483f,0.153088f}, -{0.403026f,-0.186547f,0.134369f},{0.153992f,-0.0128613f,-0.119212f},{0.342526f,-0.0724093f,0.226012f}, -{0.351702f,-0.0712453f,0.212816f},{-0.323607f,0.0270281f,0.316819f},{-0.0144658f,0.347044f,-0.0496318f}, -{0.24024f,0.0426288f,0.236269f},{-0.410202f,0.0403524f,0.321244f},{0.295923f,-0.178888f,0.293463f}, -{-0.380415f,0.441587f,-0.323456f},{0.315099f,-0.0676699f,0.254069f},{0.255937f,0.0257355f,0.23643f}, -{-0.233983f,0.153783f,0.214951f},{-0.433906f,-0.109958f,-0.0181473f},{-0.293139f,0.346458f,-0.282949f}, -{-0.47429f,0.245143f,0.0948394f},{-0.298727f,0.325096f,-0.339359f},{0.270522f,0.0224816f,0.232616f}, -{-0.0683805f,0.449986f,-0.0894955f},{0.160712f,-0.171879f,0.369146f},{0.174441f,-0.167917f,0.363262f}, -{0.187348f,-0.165776f,0.355075f},{0.197418f,-0.167114f,0.348883f},{0.24514f,-0.170104f,0.333404f}, -{0.25141f,-0.16494f,0.328877f},{0.266342f,-0.165725f,0.318806f},{0.277287f,-0.165692f,0.311874f}, -{0.28674f,-0.165789f,0.302878f},{0.294534f,-0.16611f,0.291585f},{0.306186f,-0.165937f,0.285791f}, -{0.320392f,-0.165744f,0.278126f},{0.329388f,-0.165905f,0.268461f},{0.335632f,-0.166104f,0.26041f}, -{0.346127f,-0.166381f,0.247375f},{0.363381f,-0.166792f,0.227176f},{0.372846f,-0.167236f,0.212366f}, -{0.376017f,-0.167487f,0.205871f},{0.38365f,-0.16858f,0.191942f},{0.389933f,-0.166516f,0.179422f}, -{-0.392833f,0.0234912f,0.319732f},{-0.494894f,0.0964214f,0.174059f},{-0.0558921f,0.396836f,-0.0624288f}, -{-0.253506f,0.117868f,0.254564f},{-0.0270828f,0.450976f,-0.084441f},{-0.0342787f,0.379268f,-0.11507f}, -{-0.381399f,-0.0122504f,-0.0409311f},{0.369933f,-0.151256f,-0.0590527f},{0.0296164f,0.38456f,-0.104524f}, -{-0.0463297f,0.431909f,-0.0671232f},{-0.310083f,0.0582618f,0.327507f},{0.331279f,-0.00854636f,0.198232f}, -{0.35731f,-0.0743127f,0.205871f},{-0.392602f,-0.0367319f,0.227928f},{-0.215926f,0.0532652f,0.217864f}, -{-0.286309f,0.0630462f,0.280576f},{-0.46703f,0.191994f,0.0951931f},{-0.369458f,-0.277586f,0.132684f}, -{0.392743f,-0.117739f,0.00504166f},{-0.0631459f,0.365101f,-0.0467509f},{-0.325035f,0.00603198f,0.262551f}, -{-0.314392f,-0.0357866f,-0.090563f},{-0.0242726f,0.346928f,-0.0468859f},{0.394499f,-0.131205f,0.00184562f}, -{0.353773f,-0.0515031f,0.196663f},{0.173123f,-0.150137f,0.36467f},{0.190126f,-0.14977f,0.355461f}, -{0.199875f,-0.149822f,0.346921f},{0.265705f,-0.148503f,0.31808f},{0.276772f,-0.148452f,0.311302f}, -{0.285705f,-0.148632f,0.301598f},{0.294502f,-0.148818f,0.29165f},{0.306527f,-0.148613f,0.286537f}, -{0.321633f,-0.148272f,0.280094f},{0.330391f,-0.148478f,0.270075f},{0.337838f,-0.147269f,0.26014f}, -{0.347072f,-0.149712f,0.246455f},{0.360615f,-0.149519f,0.228887f},{0.370171f,-0.14995f,0.214572f}, -{0.375168f,-0.150304f,0.204784f},{0.381952f,-0.150812f,0.19184f},{0.298502f,-0.0759397f,0.275194f}, -{0.33124f,-0.0341532f,0.215356f},{-0.271448f,0.0951159f,0.273721f},{-0.0428379f,0.374792f,-0.062506f}, -{0.22062f,-0.145429f,0.331745f},{0.114508f,0.00446288f,-0.119282f},{-0.457262f,0.227337f,0.0693869f}, -{0.333793f,-0.0678563f,0.235941f},{-0.282194f,0.0950195f,0.280801f},{-0.353799f,0.226996f,-0.10954f}, -{-0.372088f,0.422546f,-0.321089f},{0.278161f,-0.0846404f,0.290981f},{0.373618f,-0.150953f,-0.0526349f}, -{-0.309517f,0.113656f,0.309482f},{0.197817f,-0.131153f,0.347841f},{0.210472f,-0.132478f,0.339745f}, -{0.219572f,-0.132677f,0.330292f},{0.266393f,-0.1328f,0.315308f},{0.27321f,-0.129957f,0.310016f}, -{0.284489f,-0.131507f,0.299926f},{0.294437f,-0.131539f,0.291804f},{0.306829f,-0.131243f,0.287161f}, -{0.321067f,-0.13107f,0.279361f},{0.33014f,-0.131211f,0.26985f},{0.33868f,-0.131423f,0.259509f}, -{0.348288f,-0.131822f,0.245246f},{0.359657f,-0.132337f,0.22787f},{0.369187f,-0.132099f,0.209877f}, -{0.373663f,-0.137539f,0.202727f},{0.379676f,-0.133372f,0.18964f},{-0.393232f,0.266429f,-0.0018713f}, -{-0.386332f,0.318556f,-0.0768207f},{-0.384435f,0.284216f,-0.00197419f},{-0.234812f,0.117643f,0.236436f}, -{-0.23489f,0.0535288f,0.23578f},{-0.374834f,0.170168f,-0.0319604f},{-0.0530176f,0.034687f,-0.0872898f}, -{-0.391007f,0.326273f,-0.111803f},{-0.105858f,0.346156f,-0.0124497f},{-0.284901f,-0.307379f,0.175287f}, -{0.0462268f,0.397987f,-0.147699f},{0.396698f,-0.112768f,0.114164f},{-0.294573f,0.346574f,-0.205781f}, -{-0.382042f,0.248281f,-0.0404103f},{0.301756f,-0.0592071f,0.263098f},{-0.451204f,0.0230218f,0.0365069f}, -{-0.496013f,0.0583776f,0.113443f},{-0.367014f,0.453825f,-0.336015f},{0.265088f,0.0171763f,0.238275f}, -{-0.350133f,0.114054f,-0.0474583f},{0.0793125f,-0.0969293f,-0.202637f},{-0.184801f,0.230391f,0.0214463f}, -{-0.297061f,0.264165f,-0.167287f},{0.0243175f,0.418411f,-0.166252f},{-0.467223f,0.19274f,0.230018f}, -{0.262966f,-0.118594f,0.314202f},{0.319414f,-0.118298f,0.277052f},{0.335613f,-0.118517f,0.260802f}, -{-0.214363f,0.270905f,0.0846661f},{-0.301241f,0.247413f,-0.132774f},{-0.216234f,0.117546f,0.218051f}, -{-0.340494f,0.127443f,0.332504f},{-0.486219f,0.134652f,0.095341f},{-0.0820585f,0.412855f,-0.0283914f}, -{-0.112675f,-0.091489f,-0.140169f},{-0.0356548f,0.30491f,-0.0375936f},{0.033642f,0.347982f,-0.0946079f}, -{-0.289055f,0.321347f,-0.218642f},{-0.289184f,0.180727f,0.236848f},{-0.334507f,-0.0908009f,-0.0943185f}, -{-0.380448f,0.248204f,-0.0540368f},{0.0440211f,0.38148f,-0.149088f},{-0.373059f,-0.0920935f,-0.0786277f}, -{-0.315627f,-0.0169255f,-0.0797981f},{0.00336643f,0.327083f,-0.0718112f},{-0.425436f,0.207395f,0.0221344f}, -{0.24876f,-0.472712f,-0.00379407f},{0.237996f,-0.475419f,-0.0214655f},{0.232973f,-0.473644f,-0.0391434f}, -{0.229333f,-0.47401f,-0.0584097f},{0.226768f,-0.473483f,-0.0764348f},{0.21536f,-0.483161f,-0.0790199f}, -{0.215553f,-0.479669f,-0.0921128f},{0.243217f,-0.466917f,0.259413f},{0.246272f,-0.467946f,0.247426f}, -{0.2504f,-0.468242f,0.230372f},{-0.357651f,0.244089f,-0.170348f},{-0.296708f,0.346574f,-0.186476f}, -{-0.284097f,0.395949f,-0.37966f},{-0.317807f,-0.306826f,0.154715f},{-0.27665f,-0.0892061f,-0.114967f}, -{0.377882f,-0.452609f,0.0563905f},{0.365676f,-0.455542f,0.0379345f},{0.36284f,-0.450699f,0.0208804f}, -{0.350294f,-0.457143f,0.0219286f},{0.246008f,-0.467168f,-0.0164175f},{0.223842f,-0.470146f,-0.0938298f}, -{0.212466f,-0.469425f,-0.113649f},{0.177528f,0.0819909f,-0.0581396f},{0.250445f,-0.0753995f,-0.128144f}, -{-0.287994f,0.321437f,-0.18647f},{0.257706f,-0.456783f,0.246899f},{0.266753f,-0.453702f,0.228102f}, -{-0.463313f,-0.0535996f,0.0963442f},{-0.292862f,0.266171f,-0.115655f},{-0.297691f,0.418154f,-0.340291f}, -{-0.486406f,0.0608212f,0.211479f},{-0.392788f,0.092788f,-0.0334844f},{-0.333645f,0.073824f,-0.0471689f}, -{0.401514f,-0.441831f,0.0767628f},{0.397302f,-0.441947f,0.0568471f},{0.388287f,-0.44295f,0.0376966f}, -{0.378776f,-0.444076f,0.0248802f},{0.359496f,-0.445253f,0.00628921f},{0.344159f,-0.450371f,0.0042957f}, -{-0.201231f,0.169287f,0.0771036f},{0.25568f,-0.458268f,-0.0205074f},{0.248497f,-0.453471f,-0.0392013f}, -{0.238356f,-0.459477f,-0.0585383f},{0.236433f,-0.456005f,-0.0791228f},{0.233108f,-0.455432f,-0.0939584f}, -{0.228137f,-0.453645f,-0.108517f},{0.22152f,-0.46122f,-0.110086f},{0.207739f,-0.462738f,-0.12325f}, -{0.193013f,-0.469998f,-0.129411f},{0.0398026f,-0.0865888f,-0.191074f},{-0.467647f,0.0190926f,0.246281f}, -{-0.292798f,0.305984f,-0.22515f},{-0.0710363f,-0.0359988f,-0.136619f},{-0.496019f,0.056204f,0.13433f}, -{0.261358f,-0.447111f,0.256468f},{0.26712f,-0.446609f,0.243651f},{-0.469294f,0.0183531f,0.0758561f}, -{-0.355683f,0.217601f,-0.0730137f},{-0.206736f,0.169075f,0.134896f},{-0.298148f,0.43724f,-0.361525f}, -{-0.29227f,0.266262f,-0.096357f},{-0.286824f,0.396965f,-0.360117f},{-0.429559f,0.152137f,1.28796e-05f}, -{0.417263f,-0.431394f,0.113746f},{0.418292f,-0.432346f,0.0939777f},{0.421462f,-0.43025f,0.0760104f}, -{0.417102f,-0.430546f,0.0568278f},{0.411803f,-0.430751f,0.0441787f},{0.405071f,-0.431832f,0.0345455f}, -{0.39608f,-0.428835f,0.0193756f},{0.381856f,-0.434423f,0.0154336f},{0.364422f,-0.435478f,-0.00126682f}, -{0.289216f,-0.0535674f,-0.0970258f},{-0.453339f,0.134864f,0.0193306f},{0.269724f,-0.452873f,-0.0171763f}, -{0.244555f,-0.449497f,-0.0585769f},{0.241179f,-0.449799f,-0.0713803f},{0.212524f,-0.450153f,-0.127932f}, -{0.155471f,0.0844796f,-0.0584097f},{-0.462837f,-0.0924729f,0.15249f},{-0.481685f,0.0599273f,0.0604418f}, -{-0.47656f,0.0597601f,0.0508215f},{-0.294315f,0.376772f,-0.302241f},{0.0440661f,0.419118f,-0.17339f}, -{-0.20855f,0.188798f,0.0578245f},{-0.287113f,0.417118f,-0.380277f},{-0.370043f,-0.258249f,0.186502f}, -{-0.210177f,0.16521f,0.154014f},{-0.401611f,-0.0144818f,0.216019f},{0.439886f,-0.415434f,0.114472f}, -{0.44069f,-0.416218f,0.0951867f},{0.440446f,-0.415112f,0.0759204f},{0.43597f,-0.415356f,0.0566927f}, -{0.423385f,-0.421093f,0.0375679f},{0.421539f,-0.41407f,0.020308f},{0.405296f,-0.4218f,0.0159866f}, -{0.315569f,-0.0725507f,-0.0834893f},{-0.108765f,0.0418508f,-0.0803318f},{0.302238f,-0.0549371f,-0.0891289f}, -{0.271486f,-0.43169f,-0.0343847f},{0.256265f,-0.438925f,-0.0425581f},{0.251686f,-0.436108f,-0.0600173f}, -{0.248471f,-0.43623f,-0.0766277f},{0.24631f,-0.434648f,-0.0921128f},{0.237391f,-0.440835f,-0.109984f}, -{0.230446f,-0.433742f,-0.127115f},{0.215707f,-0.436205f,-0.134716f},{0.00438248f,-0.0895598f,-0.188193f}, -{-0.293428f,0.266049f,-0.131738f},{-0.300553f,0.380226f,-0.282229f},{-0.296058f,0.326035f,-0.282981f}, -{-0.150725f,0.37847f,-0.061072f},{-0.45377f,0.0130671f,0.153905f},{-0.431726f,0.0403845f,0.320234f}, -{-0.458683f,0.0119096f,0.140979f},{-0.353413f,0.305341f,-0.246449f},{-0.204119f,0.169069f,0.0578631f}, -{-0.109042f,0.272705f,-0.00145331f},{0.449043f,-0.409749f,0.0951416f},{0.436105f,-0.410643f,0.0375422f}, -{0.40271f,-0.413234f,0.000861727f},{0.306546f,-0.074287f,-0.0900743f},{0.269499f,-0.0541912f,-0.108228f}, -{0.100129f,0.070165f,-0.0682357f},{0.262104f,-0.428867f,-0.0521269f},{0.243442f,-0.431086f,-0.110164f}, -{0.218157f,-0.0130735f,-0.101431f},{-0.108019f,-0.476364f,-0.188161f},{-0.335703f,-0.278917f,0.190425f}, -{-0.375515f,0.270152f,-0.130504f},{-0.431944f,0.190862f,0.0184432f},{-0.487537f,0.078184f,0.211369f}, -{-0.289775f,0.331468f,-0.186476f},{0.0238481f,0.341397f,-0.0924857f},{-0.288521f,0.39191f,-0.34404f}, -{-0.446143f,0.0075689f,0.264294f},{-0.225732f,0.208109f,0.13487f},{0.0427799f,0.363577f,-0.128607f}, -{0.458786f,-0.395499f,0.133726f},{0.461525f,-0.396122f,0.115688f},{0.459699f,-0.399807f,0.0951031f}, -{0.460323f,-0.397119f,0.0742484f},{0.458426f,-0.395183f,0.056577f},{0.446169f,-0.400977f,0.0373943f}, -{0.439462f,-0.396843f,0.0184432f},{0.428928f,-0.402225f,0.0122118f},{0.419314f,-0.399698f,-0.000353668f}, -{0.40662f,-0.403916f,-0.00723448f},{0.0976528f,0.0429054f,-0.0970322f},{0.287975f,-0.41598f,-0.036629f}, -{0.275512f,-0.417671f,-0.0584611f},{0.266586f,-0.418668f,-0.0745378f},{0.253018f,-0.421536f,-0.091161f}, -{0.24723f,-0.417035f,-0.110479f},{0.231848f,-0.41652f,-0.131038f},{0.307492f,-0.0907044f,-0.0960162f}, -{0.440838f,-0.2645f,0.0228546f},{0.459384f,-0.319617f,0.00039872f},{-0.03415f,-0.0165011f,-0.131044f}, -{-0.296032f,0.326208f,-0.12925f},{-0.259333f,-0.27547f,0.242835f},{-0.303955f,0.309939f,-0.303907f}, -{-0.239005f,0.247696f,0.173621f},{-0.208993f,0.152182f,0.0179416f},{-0.192061f,0.145751f,0.059818f}, -{-0.209662f,0.208521f,0.0578181f},{-0.0908363f,-0.0219285f,-0.124105f},{-0.300752f,0.304575f,-0.264204f}, -{-0.128597f,0.261561f,-0.000932427f},{-0.292573f,0.2663f,-0.0770715f},{0.468425f,-0.389859f,0.107919f}, -{0.469159f,-0.389807f,0.095058f},{0.46856f,-0.389717f,0.0790007f},{0.457275f,-0.390682f,0.03742f}, -{0.449937f,-0.391357f,0.0247388f},{0.432722f,-0.392901f,0.00583906f},{0.41172f,-0.394187f,-0.0133114f}, -{0.304617f,-0.411684f,-0.0309572f},{0.286933f,-0.407781f,-0.058609f},{0.280965f,-0.40809f,-0.0777981f}, -{0.272824f,-0.408469f,-0.0935468f},{0.13452f,0.0551622f,-0.0911482f},{0.00276838f,-0.0574065f,-0.142613f}, -{0.0986881f,-0.111392f,-0.208566f},{-0.0535256f,-0.0160316f,-0.128793f},{-0.480194f,0.112318f,0.0601846f}, -{-0.291717f,0.331552f,-0.167159f},{-0.291486f,0.376772f,-0.321514f},{0.0521751f,0.396354f,-0.154664f}, -{-0.466503f,0.262262f,0.0949037f},{-0.304456f,0.303926f,-0.282345f},{-0.230684f,0.208173f,0.154329f}, -{-0.438227f,-0.0888203f,-0.00416705f},{-0.317356f,-0.279991f,0.207871f},{-0.309556f,0.298486f,-0.301161f}, -{-0.394203f,0.00450149f,0.299174f},{-0.497608f,0.0648661f,0.118202f},{0.474933f,-0.375789f,0.133629f}, -{0.47793f,-0.375473f,0.114311f},{0.479036f,-0.375467f,0.0950195f},{0.47784f,-0.375454f,0.0757275f}, -{0.474194f,-0.375776f,0.0564869f},{0.470213f,-0.374702f,0.0414842f},{0.4642f,-0.378284f,0.0329765f}, -{0.456767f,-0.376547f,0.0194078f},{0.447642f,-0.382708f,0.0118903f},{0.439333f,-0.378206f,0.000366566f}, -{0.427507f,-0.384714f,-0.00714445f},{0.417784f,-0.379435f,-0.0165654f},{-0.0898074f,0.0744092f,-0.0384811f}, -{0.174776f,-0.0560882f,-0.130272f},{0.295061f,-0.127295f,-0.121662f},{0.306707f,-0.393621f,-0.0401016f}, -{0.294945f,-0.398193f,-0.0587891f},{0.292759f,-0.395338f,-0.0759461f},{0.289241f,-0.393987f,-0.0908781f}, -{0.272901f,-0.395241f,-0.112292f},{0.255384f,-0.402244f,-0.11734f},{0.326739f,-0.0737533f,-0.0754767f}, -{0.0427671f,0.0614836f,-0.0778946f},{0.024787f,0.0598694f,-0.0759268f},{0.0609787f,-0.0979068f,-0.204971f}, -{0.370113f,-0.108202f,-0.0410212f},{-0.293151f,0.346593f,-0.225067f},{-0.349182f,-0.294312f,0.142934f}, -{-0.198035f,0.154304f,0.0579017f},{-0.460979f,0.0270666f,0.192534f},{-0.284065f,0.227549f,-0.0155815f}, -{-0.211939f,0.163828f,0.0225845f},{0.263307f,0.0682615f,-0.015575f},{-0.144294f,-0.489791f,-0.179949f}, -{0.302778f,-0.387962f,-0.0587505f},{0.298405f,-0.388778f,-0.0717211f},{0.283737f,-0.389698f,-0.106762f}, -{0.306045f,0.0186232f,-0.0359667f},{-0.0324716f,0.064043f,-0.0604032f},{-0.0153082f,0.0546413f,-0.0698948f}, -{0.270367f,0.0232468f,-0.0589048f},{0.058818f,-0.471593f,-0.152047f},{-0.238632f,0.0343269f,-0.0881515f}, -{0.251802f,0.0835021f,0.000527333f},{0.052143f,0.412617f,-0.165088f},{-0.468483f,0.0215491f,0.265831f}, -{-0.231687f,0.203267f,0.167236f},{-0.221269f,0.170843f,0.173268f},{-0.335838f,-0.487309f,-0.0170734f}, -{-0.317948f,0.209016f,-0.0551107f},{-0.236909f,0.268364f,0.153243f},{-0.355079f,0.287393f,-0.246384f}, -{-0.217675f,0.171081f,0.0178065f},{-0.439738f,0.0120318f,0.289688f},{0.4833f,-0.35786f,0.13487f}, -{0.484611f,-0.361365f,0.114292f},{0.485865f,-0.361275f,0.0949809f},{0.484656f,-0.35912f,0.0740555f}, -{0.481518f,-0.357223f,0.0563648f},{0.474387f,-0.357886f,0.0371307f},{0.462078f,-0.359905f,0.0167197f}, -{0.453493f,-0.358374f,0.00308674f},{0.444111f,-0.362361f,-0.0054146f},{0.0754349f,0.029954f,-0.102312f}, -{-0.474747f,0.0408347f,0.0529051f},{0.323382f,-0.376927f,-0.03742f},{0.314842f,-0.377467f,-0.045722f}, -{0.310508f,-0.375879f,-0.0588663f},{0.30654f,-0.373197f,-0.0730652f},{0.299865f,-0.373756f,-0.0897206f}, -{0.29492f,-0.379743f,-0.0974952f},{0.289422f,-0.375679f,-0.110389f},{0.271101f,-0.378528f,-0.12743f}, -{0.00521203f,0.0642294f,-0.0609819f},{0.321961f,0.00183919f,-0.0339667f},{0.286959f,-0.0164046f,-0.0771872f}, -{0.420176f,-0.265104f,0.0233819f},{0.0740008f,-0.0641651f,-0.143892f},{-0.0716794f,0.0824154f,-0.0214269f}, -{-0.29319f,0.346394f,-0.302228f},{-0.280541f,-0.282036f,0.227324f},{-0.146101f,0.253818f,0.0192406f}, -{-0.390807f,-0.220514f,0.206148f},{0.0528182f,0.402739f,-0.162246f},{-0.233102f,0.193158f,0.179949f}, -{0.486489f,-0.352252f,0.127153f},{0.487975f,-0.352124f,0.114273f},{0.489042f,-0.352079f,0.0949487f}, -{0.488078f,-0.352059f,0.0788785f},{0.468322f,-0.354085f,0.0241922f},{0.0194559f,0.0802225f,-0.0425388f}, -{0.0245297f,0.0399794f,-0.0934954f},{-0.482663f,0.0772772f,0.0602553f},{0.330353f,-0.362123f,-0.0421208f}, -{0.317839f,-0.368535f,-0.0525771f},{0.280033f,-0.371969f,-0.123237f},{0.00321209f,0.0560947f,-0.074467f}, -{-0.0921032f,-0.0124755f,-0.115996f},{0.155805f,0.0435163f,-0.0979968f},{0.0239831f,0.0742355f,-0.0537089f}, -{-0.30209f,0.342954f,-0.148047f},{-0.376994f,-0.248468f,0.193524f},{-0.414832f,-0.0382303f,-0.0205588f}, -{0.489524f,-0.338581f,0.133584f},{0.492798f,-0.338304f,0.114247f},{0.494219f,-0.338182f,0.0949166f}, -{0.492907f,-0.338272f,0.0756117f},{0.488952f,-0.337687f,0.0576573f},{0.484521f,-0.343552f,0.0499148f}, -{0.48103f,-0.339385f,0.0371307f},{0.471615f,-0.340298f,0.0210733f},{0.465313f,-0.340876f,0.0115109f}, -{0.455661f,-0.337333f,-0.00201921f},{0.448323f,-0.346883f,-0.00728592f},{0.229906f,0.0176136f,-0.0870004f}, -{0.341992f,-0.359969f,-0.0333044f},{0.32272f,-0.355056f,-0.0600366f},{0.312495f,-0.356863f,-0.0761004f}, -{0.303948f,-0.356149f,-0.0911996f},{0.291717f,-0.357255f,-0.110556f},{0.279017f,-0.362921f,-0.123044f}, -{-0.0522717f,0.0835278f,-0.0215491f},{-0.0338478f,0.0893026f,-0.0163596f},{0.156378f,0.0574451f,-0.0872576f}, -{0.267853f,-0.168811f,-0.147146f},{-0.126996f,0.0363654f,-0.0887431f},{0.0581042f,0.100196f,-0.0409376f}, -{-0.486476f,0.112498f,0.0762162f},{-0.293994f,0.326189f,-0.263683f},{-0.396801f,-0.208456f,0.211575f}, -{-0.43289f,-0.204662f,0.135134f},{-0.458406f,-0.0975531f,0.0515611f},{-0.216016f,0.173004f,0.154812f}, -{-0.235005f,0.268229f,0.138819f},{0.487081f,-0.334253f,0.146368f},{-0.0743738f,-0.469959f,-0.181248f}, -{-0.301569f,0.0376644f,-0.0603903f},{-0.497852f,0.0824411f,0.166387f},{0.346326f,-0.34215f,-0.0371949f}, -{0.332333f,-0.343796f,-0.058474f},{0.319626f,-0.349854f,-0.0715218f},{0.272406f,-0.359423f,-0.128658f}, -{0.26303f,-0.355448f,-0.142593f},{-0.0144658f,-0.115945f,-0.219247f},{-0.0125495f,0.0629755f,-0.0593807f}, -{-0.444941f,0.172072f,0.0224495f},{0.00631167f,-0.0170863f,-0.127764f},{-0.0145301f,-0.106228f,-0.209845f}, -{-0.45222f,0.0166683f,0.285078f},{-0.477821f,0.0350407f,0.274229f},{-0.23015f,0.22796f,0.134928f}, -{-0.207681f,0.188823f,0.0963763f},{-0.218221f,0.208353f,0.11563f},{-0.0711649f,-0.482081f,0.33824f}, -{-0.374094f,-0.00108035f,0.286422f},{0.36958f,-0.0675798f,0.176342f},{0.492753f,-0.320472f,0.133578f}, -{0.496836f,-0.320125f,0.114241f},{0.498785f,-0.319958f,0.0948909f},{0.498148f,-0.320028f,0.0755539f}, -{0.493563f,-0.320356f,0.056249f},{0.488553f,-0.319424f,0.0412334f},{0.484058f,-0.322781f,0.0328607f}, -{0.473416f,-0.322298f,0.0177486f},{-0.22552f,-0.03306f,-0.116273f},{0.339709f,-0.333494f,-0.0574579f}, -{0.326713f,-0.335603f,-0.0747114f},{0.308263f,-0.338452f,-0.0923764f},{0.291325f,-0.340073f,-0.111836f}, -{0.280914f,-0.338613f,-0.125436f},{0.272721f,-0.342388f,-0.13397f},{0.265339f,-0.34143f,-0.145802f}, -{0.174396f,-0.105939f,-0.17647f},{0.265249f,-0.0190926f,-0.0887624f},{-0.0135334f,0.0801711f,-0.0390084f}, -{-0.110829f,-0.0155043f,-0.117257f},{-0.321427f,-0.122806f,-0.102132f},{-0.431456f,0.226842f,0.037002f}, -{-0.292598f,0.346593f,-0.244365f},{-0.289524f,0.376715f,-0.334407f},{-0.442336f,-0.0914504f,0.00441145f}, -{0.155136f,0.0992123f,-0.0394971f},{-0.220401f,0.0890261f,-0.0467252f},{-0.297453f,0.226758f,-0.0571235f}, -{-0.469004f,0.175049f,0.230147f},{-0.48101f,0.026893f,0.136169f},{-0.225771f,-0.290621f,0.23369f}, -{-0.47629f,0.208f,0.173692f},{-0.47476f,0.13559f,0.0549565f},{0.364159f,-0.322453f,-0.020726f}, -{0.357233f,-0.317565f,-0.0383332f},{0.351915f,-0.325732f,-0.0437799f},{0.321292f,-0.331642f,-0.0844281f}, -{0.298476f,-0.334195f,-0.104402f},{0.31126f,-0.0140253f,-0.0577152f},{0.2842f,-0.11408f,-0.124851f}, -{-0.493222f,0.0603582f,0.0957654f},{-0.39111f,-0.0106042f,0.244256f},{-0.332321f,0.414868f,-0.355223f}, -{-0.287962f,0.37674f,-0.343995f},{-0.288611f,0.321456f,-0.167165f},{0.191849f,0.0695348f,-0.0683f}, -{-0.292753f,0.346343f,-0.321507f},{0.272162f,0.0412913f,-0.0399923f},{-0.364384f,-0.290068f,0.113553f}, -{-0.290245f,0.303585f,-0.207402f},{-0.438266f,0.0121475f,0.198039f},{-0.353182f,-0.258943f,0.20755f}, -{-0.0692872f,-0.478042f,0.349088f},{0.493981f,-0.302543f,0.133571f},{0.49845f,-0.302164f,0.114215f}, -{0.5f,-0.302022f,0.0948523f},{0.499408f,-0.302125f,0.075451f},{0.494373f,-0.30255f,0.0561654f}, -{0.488656f,-0.304537f,0.0410662f},{0.483975f,-0.30199f,0.0326163f},{0.472972f,-0.30226f,0.0202695f}, -{-0.390042f,-0.0561139f,-0.0530401f},{-0.404878f,-0.0396f,-0.0322047f},{-0.261004f,0.172316f,-0.0231825f}, -{0.346963f,-0.318073f,-0.058892f},{0.339401f,-0.315096f,-0.0698112f},{0.331613f,-0.317353f,-0.078454f}, -{0.323736f,-0.317096f,-0.089862f},{0.31263f,-0.323109f,-0.0999839f},{0.305659f,-0.318221f,-0.10846f}, -{0.294913f,-0.320723f,-0.117089f},{0.286123f,-0.320543f,-0.12869f},{0.27793f,-0.3268f,-0.13651f}, -{0.267532f,-0.319366f,-0.146092f},{0.227559f,0.0757082f,-0.0364232f},{-0.203444f,0.0201601f,-0.0971351f}, -{0.243847f,0.07048f,-0.0308672f},{-0.289602f,0.281271f,-0.115675f},{-0.290766f,0.231555f,-0.0258319f}, -{0.384582f,-0.301264f,-0.00464292f},{-0.262683f,-0.4823f,0.299836f},{-0.372847f,-0.242108f,0.20863f}, -{-0.354024f,-0.299405f,0.114273f},{-0.411032f,-0.225543f,0.151416f},{-0.226414f,0.170818f,0.00573617f}, -{-0.409308f,0.226379f,0.00172344f},{-0.458477f,-0.115012f,0.0517025f},{0.367078f,-0.304923f,-0.0234912f}, -{0.363471f,-0.29817f,-0.0387833f},{0.35414f,-0.310646f,-0.0525321f},{0.440221f,-0.285418f,0.00548537f}, -{-0.489634f,0.0610013f,0.230771f},{-0.332147f,0.289515f,-0.319668f},{-0.256098f,-0.294389f,0.219202f}, -{-0.293325f,0.361718f,-0.28291f},{-0.292663f,0.32653f,-0.147854f},{-0.3932f,-0.261503f,0.116517f}, -{-0.28883f,0.301283f,-0.122131f},{-0.400505f,-0.231021f,0.173641f},{-0.429925f,0.00713804f,0.209775f}, -{0.498077f,-0.284345f,0.114189f},{0.499132f,-0.284261f,0.0948201f},{0.496688f,-0.284467f,0.075451f}, -{0.491164f,-0.284962f,0.0561461f},{0.487203f,-0.289791f,0.0432977f},{0.482676f,-0.28275f,0.0357609f}, -{0.473917f,-0.282885f,0.0238256f},{-0.447249f,0.22717f,0.0597537f},{-0.438143f,0.226983f,0.0499534f}, -{-0.489145f,0.0429632f,0.095849f},{0.357027f,-0.296839f,-0.0525642f},{0.350564f,-0.299006f,-0.0612585f}, -{0.342082f,-0.295997f,-0.0719462f},{0.333517f,-0.303662f,-0.0786791f},{0.326456f,-0.299733f,-0.091444f}, -{0.317511f,-0.300447f,-0.104061f},{0.306643f,-0.302846f,-0.112331f},{0.285698f,-0.30264f,-0.128067f}, -{-0.472245f,0.174682f,0.152979f},{-0.469969f,0.152953f,0.230263f},{-0.483094f,0.0261792f,0.114871f}, -{0.233037f,-0.0750651f,-0.134279f},{-0.497949f,0.10864f,0.137256f},{-0.28955f,0.281232f,-0.131738f}, -{0.309447f,-0.150008f,-0.116639f},{-0.378332f,-0.206643f,0.233356f},{-0.374178f,0.0174464f,0.323186f}, -{-0.154873f,0.253696f,0.0322498f},{-0.379097f,0.268429f,-0.0550014f},{-0.217025f,0.18946f,0.136144f}, -{-0.400029f,0.226224f,-0.00787111f},{0.371573f,-0.287991f,-0.0247452f},{0.294225f,-0.297347f,-0.119456f}, -{-0.418485f,0.228025f,0.0135044f},{-0.42152f,0.239954f,0.0242179f},{-0.146127f,0.0584097f,-0.0732259f}, -{-0.394048f,0.187267f,-0.0230346f},{-0.35032f,0.454493f,-0.352638f},{0.193405f,0.0027459f,-0.107791f}, -{0.081589f,0.100608f,-0.0416835f},{-0.0187743f,-0.00719588f,-0.126941f},{-0.292534f,0.361622f,-0.302228f}, -{-0.111498f,-0.470692f,0.322247f},{-0.395161f,0.417421f,-0.282634f},{-0.278509f,-0.48138f,0.29565f}, -{-0.452034f,0.0168805f,0.170657f},{0.249912f,-0.148297f,-0.151339f},{-0.473769f,0.209099f,0.18973f}, -{-0.337304f,-0.228623f,0.251394f},{-0.381907f,-0.26976f,0.114736f},{0.489659f,-0.269393f,0.131989f}, -{0.491434f,-0.262898f,0.113945f},{0.492939f,-0.262699f,0.0949745f},{0.491229f,-0.263754f,0.0761712f}, -{0.488727f,-0.26875f,0.0603775f},{0.482374f,-0.260397f,0.0549307f},{0.476316f,-0.262924f,0.0415485f}, -{-0.336732f,0.341301f,-0.320755f},{-0.370255f,0.226462f,-0.0519211f},{-0.486875f,0.0963699f,0.264159f}, -{-0.129002f,-0.0141925f,-0.118453f},{-0.473975f,0.0342111f,0.171956f},{0.384988f,-0.284827f,0.000218661f}, -{0.375277f,-0.280518f,-0.0187839f},{0.368731f,-0.277721f,-0.0397479f},{0.359631f,-0.277663f,-0.057773f}, -{0.353272f,-0.283605f,-0.0653548f},{0.346391f,-0.279721f,-0.0750522f},{0.32944f,-0.28219f,-0.0926465f}, -{0.319466f,-0.282113f,-0.104003f},{0.31234f,-0.282724f,-0.110183f},{0.29427f,-0.284422f,-0.118093f}, -{0.285814f,-0.285123f,-0.126459f},{0.275236f,-0.28192f,-0.137121f},{-0.299846f,0.416623f,-0.384965f}, -{-0.348873f,0.287103f,-0.285007f},{-0.481731f,0.112466f,0.261445f},{-0.490721f,0.0610913f,0.246835f}, -{-0.482721f,0.133417f,0.076885f},{-0.331993f,0.323031f,-0.336889f},{-0.361419f,-0.286872f,0.133931f}, -{0.0434166f,0.0389441f,-0.0937719f},{-0.381882f,-0.226649f,0.213209f},{-0.335645f,0.242629f,-0.188026f}, -{-0.464934f,0.0268287f,0.171313f},{-0.212331f,0.183621f,0.128427f},{-0.356551f,-0.2654f,0.192315f}, -{-0.464811f,-0.0710202f,0.0771808f},{0.0998135f,0.0067136f,-0.119469f},{-0.369374f,0.251902f,-0.150503f}, -{0.384724f,-0.263773f,-0.00166552f},{0.379303f,-0.262139f,-0.0197357f},{0.338102f,-0.275991f,-0.0849683f}, -{-0.260818f,0.0960612f,-0.036674f},{0.229462f,-0.0564998f,-0.129989f},{-0.0901997f,0.0448153f,-0.0784219f}, -{0.275017f,0.0672905f,0.000250814f},{-0.208537f,0.342838f,-0.0226938f},{-0.218665f,0.33842f,-0.0145654f}, -{-0.393907f,0.361911f,-0.225819f},{-0.288753f,0.291296f,-0.115675f},{-0.357632f,0.0262114f,0.336015f}, -{-0.2802f,-0.319816f,0.151204f},{0.00395806f,0.491913f,-0.130401f},{-0.108546f,0.0785827f,-0.037928f}, -{0.375007f,-0.257644f,-0.0355551f},{0.370068f,-0.261085f,-0.0441015f},{0.363824f,-0.260159f,-0.0559853f}, -{0.357625f,-0.260693f,-0.0655927f},{0.348629f,-0.261451f,-0.0752387f},{0.33897f,-0.262281f,-0.0850198f}, -{0.329677f,-0.263091f,-0.0945757f},{0.319382f,-0.263979f,-0.103945f},{0.305614f,-0.265239f,-0.113938f}, -{0.287537f,-0.265188f,-0.132986f},{0.0602585f,-0.0592071f,-0.144002f},{-0.374506f,0.339243f,-0.209807f}, -{-0.48773f,0.113192f,0.191936f},{-0.337098f,0.00798691f,0.287521f},{-0.0892158f,-0.452095f,-0.183222f}, -{-0.147966f,0.251985f,0.00030869f},{-0.317626f,-0.201383f,0.276441f},{-0.429514f,0.00733738f,0.2843f}, -{-0.456201f,0.191775f,0.0726279f},{-0.368409f,0.306633f,-0.168027f},{0.371798f,-0.0463908f,0.0113694f}, -{-0.449712f,-0.0191184f,0.115456f},{0.388711f,-0.241812f,-0.000347237f},{0.29854f,-0.261246f,-0.123199f}, -{0.193322f,0.0785634f,-0.0559982f},{-0.445076f,0.208418f,0.0577345f},{-0.446632f,0.114652f,0.000135062f}, -{-0.291299f,0.361577f,-0.321514f},{-0.143825f,0.0790135f,-0.0590656f},{-0.375097f,-0.0163917f,0.247973f}, -{-0.363078f,0.304755f,-0.189846f},{-0.384164f,0.248281f,-0.0467959f},{-0.486393f,0.0956818f,0.230578f}, -{0.395592f,-0.243368f,0.0176072f},{0.382673f,-0.240442f,-0.0205845f},{0.375734f,-0.24106f,-0.0366226f}, -{0.370049f,-0.241542f,-0.0461528f},{0.361844f,-0.24133f,-0.0578631f},{0.35668f,-0.247207f,-0.0655027f}, -{0.344455f,-0.24369f,-0.075348f},{0.336545f,-0.248879f,-0.0850455f},{0.329247f,-0.244101f,-0.0926529f}, -{0.319954f,-0.245812f,-0.104106f},{0.310488f,-0.246629f,-0.113681f},{0.302045f,-0.247388f,-0.123456f}, -{0.292071f,-0.244236f,-0.134555f},{0.191746f,-0.131372f,-0.183711f},{-0.0560657f,0.0633034f,-0.05722f}, -{-0.33852f,-0.267425f,0.209106f},{-0.408189f,-0.0210411f,0.207968f},{-0.355092f,0.00830841f,0.306119f}, -{-0.28775f,0.412212f,-0.363345f},{-0.0922511f,0.398026f,-0.094878f},{-0.470445f,0.153024f,0.249542f}, -{-0.306122f,0.290994f,-0.258841f},{0.394518f,-0.131931f,0.151461f},{0.0388572f,0.456101f,-0.170921f}, -{0.403649f,-0.224996f,0.0363525f},{0.398505f,-0.222951f,0.0189062f},{0.211135f,-0.130671f,-0.169917f}, -{0.0247484f,0.09219f,-0.0328735f},{-0.217874f,-0.35478f,0.114993f},{-0.148172f,0.0707695f,-0.0669753f}, -{-0.445616f,0.0105785f,0.0410791f},{-0.395753f,-0.226346f,0.190772f},{-0.289807f,0.311373f,-0.128562f}, -{-0.413726f,-0.00173626f,0.226334f},{-0.473853f,0.17447f,0.114408f},{-0.372769f,-0.278968f,0.115f}, -{0.407103f,-0.224147f,0.0562876f},{0.393553f,-0.221459f,0.00189706f},{0.389463f,-0.221806f,-0.00772964f}, -{0.383238f,-0.222353f,-0.0206103f},{0.376756f,-0.222925f,-0.0331693f},{0.369998f,-0.223485f,-0.0430468f}, -{0.360358f,-0.224282f,-0.0560239f},{0.344854f,-0.2256f,-0.0719269f},{0.329452f,-0.226874f,-0.0912253f}, -{0.318147f,-0.227851f,-0.107347f},{0.311839f,-0.228333f,-0.116691f},{0.302617f,-0.2256f,-0.126182f}, -{-0.126938f,0.0753609f,-0.0523327f},{-0.418189f,-0.0564933f,-0.0256004f},{-0.201379f,0.320523f,-0.0340053f}, -{0.289576f,0.0437799f,-0.0211054f},{-0.289962f,0.361538f,-0.33761f},{0.389624f,-0.0725443f,0.0563262f}, -{-0.276998f,-0.313218f,0.167654f},{-0.40718f,-0.221736f,0.171834f},{0.0390244f,0.449098f,-0.179878f}, -{-0.332185f,0.305341f,-0.333899f},{-0.312926f,-0.273162f,0.222655f},{0.408614f,-0.223125f,0.0928395f}, -{0.407874f,-0.22135f,0.0749108f},{-0.428935f,-0.0908717f,-0.0191826f},{-0.0479631f,0.471458f,-0.091399f}, -{-0.425977f,-0.0560368f,-0.0160638f},{0.0831967f,-0.0666924f,-0.148786f},{-0.288489f,0.361519f,-0.347256f}, -{-0.129748f,-0.0760812f,-0.133899f},{-0.479653f,0.210064f,0.146323f},{-0.343356f,0.357937f,-0.306935f}, -{-0.486457f,0.0412077f,0.153989f},{0.391669f,-0.126678f,0.159185f},{-0.362853f,0.219581f,-0.0601395f}, -{0.0237516f,-0.097611f,-0.205505f},{-0.359548f,0.440365f,-0.343513f},{0.250683f,0.061297f,-0.0380052f}, -{-0.352397f,0.2565f,-0.259509f},{0.406704f,-0.202302f,0.075721f},{0.405707f,-0.202399f,0.0564484f}, -{0.403431f,-0.202592f,0.0372078f},{0.399534f,-0.202932f,0.0179673f},{0.395135f,-0.203305f,0.00197423f}, -{0.390936f,-0.203665f,-0.00767819f},{0.383875f,-0.204238f,-0.0204431f},{0.376171f,-0.204868f,-0.0333237f}, -{0.36902f,-0.205453f,-0.0429439f},{0.359419f,-0.205299f,-0.057683f},{0.346622f,-0.20825f,-0.0732967f}, -{0.338899f,-0.207903f,-0.0846403f},{0.33007f,-0.208643f,-0.0942606f},{0.320938f,-0.209492f,-0.107334f}, -{0.31209f,-0.207247f,-0.117662f},{0.304186f,-0.208276f,-0.124137f},{-0.348384f,0.342317f,-0.284036f}, -{-0.463435f,0.174162f,0.0758754f},{-0.0486512f,0.452526f,-0.109765f},{-0.437288f,-0.036912f,0.0178515f}, -{-0.287653f,0.361377f,-0.357017f},{-0.356146f,0.241433f,-0.151095f},{-0.195598f,0.155313f,0.075914f}, -{-0.148108f,-0.0737147f,-0.130735f},{0.14133f,-0.107103f,-0.194894f},{-0.468625f,0.0943635f,0.0377994f}, -{-0.200338f,0.221523f,0.0386161f},{-0.221958f,0.0961384f,-0.0375936f},{-0.434401f,-0.0554902f,-3.21349e-05f}, -{-0.375329f,0.247966f,-0.0918041f},{-0.489807f,0.0955532f,0.192039f},{-0.465647f,-0.0924215f,0.135082f}, -{-0.476965f,0.210109f,0.155982f},{-0.439147f,-0.0508151f,0.012752f},{0.133317f,0.083007f,-0.0597923f}, -{-0.302971f,0.361506f,-0.204521f},{-0.319453f,-0.286955f,0.19263f},{-0.214652f,0.193659f,0.122118f}, -{0.404177f,-0.184483f,0.114234f},{0.405186f,-0.184412f,0.0949552f},{0.406189f,-0.184335f,0.0756825f}, -{0.405534f,-0.184406f,0.0564291f},{0.403225f,-0.184631f,0.0372078f},{0.398781f,-0.185023f,0.017948f}, -{0.393939f,-0.18692f,0.00299671f},{0.390113f,-0.184129f,-0.00556893f},{0.384145f,-0.18618f,-0.0205524f}, -{0.376242f,-0.186766f,-0.0365133f},{0.37111f,-0.187139f,-0.0461786f},{0.366667f,-0.187512f,-0.0558181f}, -{0.358789f,-0.187203f,-0.0704929f},{0.350661f,-0.193286f,-0.078184f},{0.343761f,-0.18836f,-0.0895662f}, -{0.333304f,-0.194669f,-0.0972444f},{0.325401f,-0.188663f,-0.107752f},{0.308463f,-0.187685f,-0.11752f}, -{-0.433809f,0.209929f,0.0358059f},{-0.345034f,0.306595f,-0.289315f},{-0.382981f,0.261805f,-0.0373814f}, -{-0.341838f,0.341352f,-0.304601f},{-0.388113f,0.346079f,-0.204952f},{-0.398852f,0.0174142f,-0.00798686f}, -{-0.412035f,0.0197743f,-0.00122181f},{-0.296122f,-0.0897206f,-0.106923f},{-0.301948f,0.364516f,-0.223478f}, -{-0.386737f,-0.241124f,0.18856f},{-0.319112f,0.290537f,-0.321147f},{-0.338076f,0.206218f,-0.0533487f}, -{-0.12697f,-0.407267f,-0.133867f},{-0.337954f,0.304826f,-0.320568f},{-0.419134f,0.21025f,0.0134851f}, -{-0.388872f,0.226096f,-0.0177036f},{-0.388595f,0.345294f,-0.132086f},{0.117434f,0.0805762f,-0.0601845f}, -{-0.29901f,0.363352f,-0.24279f},{-0.311588f,-0.313225f,0.14451f},{-0.214331f,0.203376f,0.0319475f}, -{0.393611f,-0.167204f,0.168843f},{0.398113f,-0.166876f,0.152773f},{0.40091f,-0.166696f,0.133513f}, -{0.402305f,-0.166606f,0.114221f},{0.403534f,-0.166528f,0.094923f},{0.404768f,-0.166451f,0.0756503f}, -{0.404312f,-0.166496f,0.0564226f},{0.401148f,-0.166773f,0.037195f},{0.395868f,-0.167249f,0.017993f}, -{0.392692f,-0.165918f,0.00290668f},{0.390479f,-0.169133f,-0.00570398f},{0.328179f,-0.168355f,-0.107971f}, -{-0.242484f,0.0954567f,-0.0339667f},{0.0977235f,-0.07684f,-0.168927f},{-0.184415f,0.0829427f,-0.0602038f}, -{-0.228298f,-0.255497f,0.259722f},{-0.126128f,0.11471f,0.0564998f},{-0.146963f,0.11898f,0.0773223f}, -{-0.430079f,0.282325f,0.0953989f},{-0.335414f,0.294782f,0.073824f},{-0.366808f,0.23007f,-0.0692132f}, -{-0.0931578f,0.0893219f,0.149326f},{-0.305775f,0.448204f,-0.372451f},{-0.316032f,0.450011f,-0.373281f}, -{-0.188209f,0.132562f,0.150548f},{-0.206929f,0.154124f,0.168522f},{-0.317549f,0.453014f,-0.358863f}, -{-0.130333f,0.0906209f,0.167557f},{-0.303402f,0.433298f,-0.345352f},{-0.308443f,0.443632f,-0.350227f}, -{-0.179335f,0.124279f,0.0366934f},{-0.191168f,0.128189f,0.0249896f},{-0.313228f,0.437652f,-0.336478f}, -{-0.322366f,0.450899f,-0.34361f},{-0.444799f,0.276518f,0.0951288f},{-0.337523f,0.455567f,-0.337385f}, -{-0.356281f,0.455972f,-0.341931f},{-0.311697f,0.42108f,-0.317887f},{-0.321691f,0.43542f,-0.323424f}, -{-0.337471f,0.449105f,-0.326665f},{-0.389502f,0.308234f,-0.129102f},{-0.298617f,0.296402f,0.132041f}, -{-0.215308f,0.160136f,0.179551f},{-0.392595f,0.260873f,0.244687f},{-0.335825f,0.441696f,-0.317353f}, -{-0.355593f,0.452333f,-0.320601f},{-0.167567f,0.0814058f,0.173416f},{-0.355805f,0.274969f,0.230449f}, -{-0.163104f,0.121951f,0.0571043f},{-0.172686f,0.125867f,0.0598116f},{-0.183509f,0.13296f,0.0551494f}, -{-0.438111f,0.274165f,0.0738883f},{-0.39037f,0.280261f,0.206212f},{-0.286933f,0.295437f,0.09891f}, -{-0.189541f,0.367107f,0.0278641f},{-0.310167f,0.405755f,-0.299785f},{-0.318315f,0.4167f,-0.302633f}, -{-0.324835f,0.428173f,-0.309591f},{-0.337414f,0.432629f,-0.303334f},{-0.36994f,0.449928f,-0.321591f}, -{0.230948f,0.0913283f,0.187473f},{-0.372377f,0.296679f,0.116266f},{0.270329f,-0.11226f,-0.131115f}, -{-0.109241f,0.0828205f,0.172181f},{-0.36284f,0.28774f,-0.188508f},{-0.352358f,0.287277f,-0.265683f}, -{-0.200942f,0.153596f,0.15177f},{0.00113499f,-0.0836629f,0.330485f},{-0.317067f,0.296203f,0.0975274f}, -{-0.397412f,0.274454f,0.215987f},{-0.429121f,0.278949f,0.07693f},{-0.323337f,0.296878f,-0.0186875f}, -{0.0342465f,0.466442f,-0.171712f},{0.0799106f,0.0706215f,-0.0687952f},{-0.341639f,0.218289f,-0.0877785f}, -{-0.431449f,0.283406f,0.132639f},{-0.330237f,0.42272f,-0.296106f},{-0.355143f,0.44095f,-0.302074f}, -{-0.338597f,0.314241f,-0.0275489f},{0.0233915f,-0.0809749f,0.338073f},{-0.0720588f,0.068403f,0.18575f}, -{-0.37576f,0.276531f,0.223311f},{-0.127504f,0.0984277f,0.151005f},{-0.354069f,0.0557667f,-0.0424938f}, -{-0.371419f,0.295662f,0.151609f},{-0.415745f,0.27138f,0.0370792f},{-0.370236f,0.315739f,-0.039973f}, -{-0.16884f,0.110607f,0.128999f},{-0.0144401f,0.113746f,0.11509f},{-0.160699f,0.0890775f,0.163107f}, -{-0.307247f,0.393351f,-0.284814f},{-0.315491f,0.401235f,-0.281521f},{-0.32171f,0.412205f,-0.289958f}, -{-0.369342f,0.447079f,-0.307064f},{-0.376493f,0.441323f,-0.300775f},{-0.387566f,0.438835f,-0.315012f}, -{-0.0306775f,0.108562f,0.111765f},{-0.451262f,0.26457f,0.0748851f},{-0.294997f,0.272962f,0.232475f}, -{-0.394325f,0.290004f,0.07675f},{-0.279422f,0.286518f,0.190367f},{-0.320758f,0.295894f,0.168618f}, -{-0.342372f,0.27374f,0.234976f},{-0.0900518f,0.0837143f,0.156786f},{-0.0354748f,0.100672f,0.136857f}, -{-0.109858f,0.0888846f,0.162252f},{0.176268f,0.121777f,0.172683f},{-0.166596f,0.0948201f,0.152947f}, -{-0.0411016f,0.104318f,0.115881f},{-0.149034f,0.107829f,0.128915f},{-0.186428f,-0.134111f,-0.131449f}, -{0.240864f,0.0891547f,0.184547f},{-0.266072f,0.309135f,0.0362561f},{-0.353317f,0.293238f,0.0387062f}, -{0.015141f,0.485644f,-0.152741f},{0.325215f,-0.0565898f,-0.0726407f},{-0.335562f,0.417601f,-0.281727f}, -{-0.35414f,0.431452f,-0.287849f},{0.15632f,0.109533f,0.200926f},{-0.111652f,0.107849f,0.112041f}, -{0.171162f,0.125732f,0.164606f},{0.264869f,0.0522877f,-0.0369055f},{0.400203f,-0.28601f,0.00717664f}, -{-0.319388f,0.279194f,0.223626f},{-0.409752f,0.281322f,0.0571364f},{-0.433713f,0.267953f,0.0543648f}, -{-0.267326f,0.278653f,0.208997f},{-0.0725668f,0.100846f,0.0960612f},{-0.261191f,0.290402f,0.133031f}, -{-0.183837f,0.113469f,0.152259f},{-0.464696f,0.244044f,0.0771615f},{0.260529f,0.0686216f,0.202148f}, -{-0.0344844f,0.0789814f,-0.0375743f},{-0.450934f,-0.0369441f,0.0577474f},{-0.196942f,0.340021f,-0.0344555f}, -{0.0789781f,0.0928973f,-0.0507379f},{0.214067f,0.0979454f,0.190264f},{0.205669f,0.10882f,0.181866f}, -{0.00593869f,0.48718f,-0.148947f},{-0.279383f,0.27774f,0.218797f},{-0.309968f,0.385808f,-0.260873f}, -{-0.320372f,0.399363f,-0.266821f},{-0.353239f,0.425633f,-0.278313f},{-0.371496f,0.433343f,-0.284197f}, -{-0.38938f,0.434545f,-0.300672f},{-0.380595f,0.294466f,0.150002f},{-0.14542f,-0.415112f,-0.13224f}, -{-0.0912929f,0.076422f,0.171576f},{0.155104f,0.127051f,0.16802f},{-0.335671f,0.296556f,0.0937076f}, -{-0.351838f,0.295617f,0.0773287f},{-0.243693f,0.319585f,0.0538697f},{0.137073f,0.132883f,0.152182f}, -{-0.47013f,0.156355f,0.0566799f},{-0.462323f,-0.0917205f,0.0598438f},{0.11935f,0.132227f,0.153474f}, -{-0.440922f,-0.0770072f,0.00667504f},{-0.146082f,0.0971866f,-0.0368348f},{-0.355728f,0.131931f,-0.0438699f}, -{-0.479653f,0.0475676f,0.192245f},{-0.35839f,0.294383f,0.0687695f},{-0.146603f,0.0898878f,0.168136f}, -{0.0804765f,0.132845f,0.132523f},{-0.33106f,0.404347f,-0.259246f},{-0.340597f,0.41281f,-0.265162f}, -{-0.355246f,0.418919f,-0.262332f},{-0.386293f,0.430526f,-0.2861f},{-0.461538f,-0.072152f,0.0592135f}, -{-0.288309f,0.0437028f,-0.0627182f},{-0.343336f,0.296164f,0.08347f},{0.0623806f,0.130375f,0.135288f}, -{0.0797948f,-0.0629498f,0.318909f},{-0.230877f,0.263702f,0.128967f},{-0.365876f,0.310183f,-0.182168f}, -{-0.464966f,-0.0927365f,0.0772773f},{-0.47721f,0.0314074f,0.22461f},{-0.42026f,0.00966527f,0.0123597f}, -{-0.406125f,0.187711f,-0.0143918f},{-0.177888f,0.0991286f,0.151969f},{0.027237f,0.126073f,0.112279f}, -{0.0190058f,0.122922f,0.118691f},{0.00530206f,0.119572f,0.112807f},{-0.3051f,0.373756f,-0.243593f}, -{-0.313459f,0.384123f,-0.244114f},{-0.322089f,0.394482f,-0.249677f},{-0.374557f,0.421697f,-0.264667f}, -{-0.378088f,0.427452f,-0.273914f},{0.219044f,0.102254f,0.177596f},{-0.387206f,0.269207f,-0.0319604f}, -{-0.48074f,0.0297547f,0.0799074f},{-0.393991f,0.286853f,0.0585898f},{0.0944375f,0.0846982f,0.234976f}, -{0.0764445f,0.126478f,0.16584f},{-0.388441f,-0.110948f,-0.0707823f},{-0.364101f,0.229986f,-0.0788528f}, -{-0.480361f,0.227729f,0.152683f},{-0.388557f,0.290627f,-0.0904215f},{0.308257f,0.0406997f,0.00110609f}, -{-0.400929f,-0.07048f,-0.0452718f},{-0.408286f,-0.0569885f,-0.0353236f},{-0.411064f,0.00485518f,0.00178774f}, -{-0.375721f,0.00127326f,-0.0385518f},{-0.385296f,0.295219f,0.110813f},{-0.0561622f,0.103682f,0.0916241f}, -{-0.467249f,0.209781f,0.0951224f},{-0.335041f,0.401756f,-0.24441f},{-0.355632f,0.412006f,-0.246732f}, -{-0.391798f,0.414746f,-0.265683f},{0.470805f,-0.247876f,0.0585576f},{-0.238587f,0.32462f,0.00147907f}, -{-0.109151f,0.110074f,0.0755603f},{-0.360043f,0.257895f,0.258191f},{0.101537f,0.0912768f,0.225864f}, -{-0.167053f,0.116504f,0.115141f},{0.287023f,0.0577538f,-0.0010739f},{0.284856f,0.0531816f,-0.0135301f}, -{-0.468876f,0.174335f,0.0951802f},{-0.399045f,-0.0575158f,-0.0449503f},{-0.400093f,0.378727f,-0.226436f}, -{-0.374956f,0.293463f,0.0760297f},{-0.425462f,0.277631f,0.0640816f},{-0.147458f,0.119385f,0.0581653f}, -{-0.307588f,0.374297f,-0.229079f},{-0.317787f,0.383943f,-0.225871f},{-0.325491f,0.393126f,-0.235574f}, -{-0.350609f,0.407144f,-0.240352f},{-0.372808f,0.413678f,-0.247793f},{0.47703f,-0.246468f,0.0764284f}, -{0.457545f,-0.244719f,0.054577f},{-0.392794f,0.206263f,-0.0201601f},{-0.318044f,0.274557f,0.233832f}, -{-0.19529f,0.141005f,0.15885f},{-0.418903f,-0.00291306f,0.195994f},{-0.110251f,0.109939f,0.0938877f}, -{0.336957f,0.0148291f,0.024996f},{0.329703f,0.0246037f,0.0209897f},{0.321279f,0.0350921f,0.0179866f}, -{0.312546f,0.0445259f,0.0194785f},{0.300553f,0.0499855f,0.00521528f},{-0.0685219f,-0.132896f,-0.203331f}, -{-0.244992f,0.32716f,0.0155172f},{-0.446407f,-0.0366998f,0.153982f},{-0.306334f,0.295019f,0.0873348f}, -{-0.336578f,0.396997f,-0.226526f},{-0.354583f,0.403067f,-0.225382f},{0.457339f,-0.237819f,0.0758818f}, -{0.439282f,-0.242822f,0.055696f},{-0.147137f,0.104035f,0.139082f},{-0.0884827f,0.102627f,0.0958747f}, -{-0.0929713f,0.0635414f,0.192791f},{0.201521f,0.0841066f,0.217446f},{-0.199193f,0.357339f,0.0339603f}, -{0.302103f,0.0538632f,0.0212405f},{0.290585f,0.0616122f,0.0160509f},{0.26568f,0.0744992f,0.000971048f}, -{-0.43916f,-0.103328f,-0.00632775f},{-0.442478f,-0.111842f,0.00203854f},{-0.497402f,0.0911225f,0.166406f}, -{-0.402846f,0.203852f,-0.0139802f},{-0.457789f,-0.0685766f,0.15732f},{0.264181f,0.0370149f,-0.0539018f}, -{-0.352989f,0.266654f,-0.266223f},{-0.349895f,0.305231f,-0.265753f},{-0.392872f,0.00293882f,-0.0182309f}, -{-0.483062f,0.0447574f,0.171904f},{-0.24251f,-0.278364f,0.244037f},{-0.461667f,-0.114781f,0.0613742f}, -{-0.0333334f,0.476094f,-0.111418f},{-0.308463f,0.37058f,-0.211029f},{-0.317376f,0.379827f,-0.208926f}, -{-0.372178f,0.404051f,-0.227247f},{-0.379489f,0.405768f,-0.237549f},{-0.299357f,0.269027f,-0.00139544f}, -{0.477088f,-0.244282f,0.0955082f},{0.439153f,-0.236635f,0.0765249f},{0.421128f,-0.237825f,0.0749429f}, -{0.419314f,-0.242944f,0.0557153f},{-0.460374f,0.110331f,0.0227003f},{-0.103575f,0.065895f,0.19373f}, -{-0.261287f,0.298331f,0.0754188f},{-0.243089f,0.300878f,0.0763898f},{-0.236362f,0.293277f,0.0843896f}, -{-0.454072f,0.27439f,0.134825f},{0.354757f,-0.00544677f,0.0437092f},{0.339497f,0.0163017f,0.0414006f}, -{0.332623f,0.0255554f,0.0390341f},{0.32326f,0.0375487f,0.037285f},{0.283171f,0.0696569f,0.0240378f}, -{0.269686f,0.0779203f,0.0181924f},{0.245751f,0.0899457f,0.00502237f},{-0.405611f,-0.0968008f,-0.0510208f}, -{-0.438028f,-0.0347256f,0.172837f},{-0.383103f,0.00792259f,-0.0261085f},{-0.338809f,0.360342f,-0.318999f}, -{-0.489891f,0.0399794f,0.133591f},{0.0094627f,-0.081798f,0.332973f},{-0.329896f,0.387981f,-0.203415f}, -{-0.340217f,0.394778f,-0.20973f},{-0.355574f,0.314614f,-0.0240507f},{-0.377991f,0.305109f,-0.0350792f}, -{0.460464f,-0.236353f,0.0941063f},{-0.275962f,0.296003f,0.0702357f},{0.100617f,0.108286f,0.205749f}, -{-0.0984117f,0.10646f,0.0950516f},{0.356628f,-0.00610268f,0.058384f},{0.349535f,0.00374265f,0.0549243f}, -{0.339934f,0.017022f,0.0585833f},{0.315807f,0.0460242f,0.0391756f},{0.306816f,0.0548278f,0.0411177f}, -{0.294232f,0.0638886f,0.0342047f},{0.250484f,0.0926272f,0.0191312f},{0.233706f,0.102003f,0.0171892f}, -{-0.147465f,0.036166f,-0.0884216f},{-0.396781f,0.341796f,-0.169486f},{-0.445191f,0.0280056f,0.0249381f}, -{-0.455976f,-0.0343011f,0.117044f},{-0.399013f,0.00875856f,-0.00724091f},{-0.127176f,0.0845696f,-0.0427317f}, -{-0.198408f,0.156471f,0.135269f},{0.115016f,-0.0638114f,0.317102f},{-0.304714f,0.35912f,-0.186592f}, -{-0.31072f,0.367512f,-0.188766f},{-0.320218f,0.377171f,-0.190232f},{-0.355548f,0.397447f,-0.206238f}, -{-0.373445f,0.395544f,-0.207402f},{0.477088f,-0.245683f,0.114575f},{0.451506f,-0.234211f,0.100293f}, -{0.441243f,-0.234005f,0.0964471f},{0.431976f,-0.235073f,0.0881644f},{0.412736f,-0.234275f,0.0805955f}, -{0.0239703f,0.103148f,0.190881f},{0.332636f,0.0271889f,0.0590271f},{0.32333f,0.0394714f,0.0564226f}, -{0.284232f,0.0735796f,0.0411948f},{0.27004f,0.0828591f,0.03506f},{0.212665f,0.113f,0.0187711f}, -{-0.478534f,0.0301598f,0.255979f},{-0.479448f,0.196611f,0.117495f},{-0.356821f,0.305463f,-0.227144f}, -{-0.460966f,0.191904f,0.0822739f},{-0.367432f,0.287933f,-0.149931f},{-0.45667f,-0.0321533f,0.0962156f}, -{0.248201f,0.0532009f,-0.0508022f},{-0.0157262f,0.109566f,0.133494f},{-0.284592f,0.29538f,0.113746f}, -{-0.0297643f,0.486023f,-0.0808334f},{-0.333722f,0.385505f,-0.187061f},{-0.342976f,0.390939f,-0.191402f}, -{-0.355657f,0.392547f,-0.189936f},{0.458824f,-0.236513f,0.115424f},{0.421507f,-0.235311f,0.0960162f}, -{-0.25921f,0.294865f,0.0855085f},{0.355419f,-0.00317029f,0.0764863f},{0.349342f,0.00503524f,0.0742613f}, -{0.307035f,0.0570336f,0.0588727f},{0.295138f,0.0672519f,0.0544034f},{0.262233f,0.0901515f,0.0422559f}, -{0.250394f,0.0972316f,0.0371307f},{0.228137f,0.107701f,0.0250796f},{0.19574f,0.119154f,0.0164239f}, -{-0.312656f,0.361332f,-0.372072f},{-0.481377f,0.209865f,0.117417f},{-0.443989f,-0.0430726f,0.168753f}, -{-0.459654f,-0.0406611f,0.112292f},{-0.44478f,0.278126f,0.137809f},{-0.390061f,0.280788f,0.0173564f}, -{-0.128552f,0.115482f,0.0745571f},{-0.371072f,0.297926f,-0.00272658f},{-0.304694f,0.354104f,-0.169692f}, -{-0.314495f,0.36494f,-0.168612f},{-0.32263f,0.3724f,-0.170059f},{-0.369805f,0.39173f,-0.192058f}, -{-0.394872f,0.379126f,-0.208585f},{0.474046f,-0.249818f,0.134298f},{0.453641f,-0.23443f,0.115418f}, -{0.440137f,-0.234076f,0.110331f},{0.420954f,-0.237388f,0.115302f},{0.413598f,-0.233452f,0.100183f}, -{0.25294f,-0.111566f,-0.137037f},{0.0383942f,0.104601f,0.196084f},{0.115466f,0.108762f,0.204881f}, -{0.34041f,0.0172792f,0.0793672f},{0.333317f,0.0269252f,0.0777274f},{0.324096f,0.0392913f,0.075078f}, -{0.286187f,0.0757339f,0.0602232f},{0.274708f,0.0836243f,0.0544677f},{0.264985f,0.0908846f,0.0569564f}, -{0.240156f,0.103527f,0.038539f},{0.229713f,0.109469f,0.0399409f},{0.210678f,0.118607f,0.039465f}, -{0.178737f,0.124163f,0.0155043f},{-0.478348f,0.214565f,0.168824f},{-0.0894087f,-0.0338703f,-0.131456f}, -{0.0999292f,-0.0885438f,-0.188676f},{-0.339047f,0.283894f,-0.305527f},{0.0879553f,-0.0823446f,0.351108f}, -{0.225797f,0.0867239f,0.198463f},{0.122147f,0.12426f,0.179261f},{-0.335253f,0.38092f,-0.170773f}, -{-0.348378f,0.389055f,-0.179621f},{-0.377785f,0.384843f,-0.184393f},{-0.389978f,0.37728f,-0.190778f}, -{0.459808f,-0.240565f,0.131944f},{0.439822f,-0.235362f,0.119745f},{0.411418f,-0.231735f,0.108935f}, -{0.407669f,-0.22553f,0.113996f},{0.305698f,0.0594129f,0.0766921f},{0.287164f,0.0762033f,0.0766535f}, -{0.249706f,0.100743f,0.0565255f},{0.196254f,0.123835f,0.0345841f},{0.186711f,0.125617f,0.0280377f}, -{0.172474f,0.127231f,0.0207132f},{0.154731f,0.12907f,0.017665f},{-0.374281f,-0.0746278f,-0.0723256f}, -{-0.498122f,0.0952895f,0.121231f},{-0.163258f,0.379737f,-0.0499984f},{0.162281f,0.123822f,0.175261f}, -{-0.44386f,-0.127134f,0.185685f},{-0.316205f,0.359931f,-0.151564f},{-0.354854f,0.385492f,-0.168085f}, -{-0.374268f,0.380804f,-0.170631f},{0.440259f,-0.239105f,0.134111f},{-0.0803994f,0.0880743f,0.144413f}, -{0.339741f,0.0176715f,0.098492f},{0.332796f,0.0273818f,0.0974567f},{0.322424f,0.0410083f,0.0941771f}, -{0.316019f,0.0491302f,0.0851934f},{0.274972f,0.0856821f,0.0744221f},{0.266425f,0.0920099f,0.0758175f}, -{0.231353f,0.11098f,0.0588534f},{0.212369f,0.120221f,0.0584097f},{0.188364f,0.127661f,0.0408733f}, -{0.17457f,0.130658f,0.0373557f},{0.136275f,0.130671f,0.0183531f},{0.11834f,0.0629433f,-0.0799717f}, -{-0.380486f,0.225883f,-0.0275361f},{-0.479396f,0.130022f,0.0632584f},{-0.477133f,0.0945629f,0.0505965f}, -{-0.367663f,0.295219f,0.084216f},{-0.0727276f,0.458262f,-0.0540883f},{-0.085576f,0.449574f,-0.0604868f}, -{-0.338063f,0.376001f,-0.154259f},{0.456767f,-0.248269f,0.14977f},{0.419372f,-0.244224f,0.134716f}, -{0.407443f,-0.240468f,0.132735f},{0.306257f,0.058847f,0.095251f},{0.287801f,0.0757339f,0.0952188f}, -{0.252381f,0.101f,0.075168f},{0.200768f,0.124543f,0.0515546f},{0.191335f,0.128581f,0.0587441f}, -{0.156165f,0.134021f,0.0397479f},{0.136558f,0.135256f,0.0385776f},{0.118996f,0.130954f,0.0188997f}, -{0.100482f,0.129096f,0.0171442f},{-0.0688242f,-0.0185718f,-0.127745f},{-0.458008f,-0.0671618f,0.0515482f}, -{0.420054f,-0.285148f,0.00508667f},{-0.350346f,0.442442f,-0.351911f},{-0.452863f,-0.0364875f,0.134729f}, -{-0.095042f,0.443433f,-0.0547635f},{-0.0495f,-0.189582f,0.356001f},{-0.303575f,0.339359f,-0.132311f}, -{-0.31009f,0.346568f,-0.131603f},{-0.320289f,0.356259f,-0.132285f},{-0.330321f,0.368245f,-0.145224f}, -{-0.354294f,0.378142f,-0.14941f},{-0.37131f,0.375396f,-0.154053f},{0.456825f,-0.25558f,0.159062f}, -{0.438388f,-0.247921f,0.153198f},{0.423404f,-0.248301f,0.148227f},{-0.40898f,0.277001f,0.0416578f}, -{-0.045976f,0.106029f,0.0999646f},{-0.103595f,0.104698f,0.116903f},{0.34005f,0.0165011f,0.116993f}, -{0.333021f,0.0263915f,0.116016f},{0.322456f,0.0400566f,0.114016f},{0.275718f,0.0854314f,0.0930131f}, -{0.266522f,0.0921321f,0.096775f},{0.242156f,0.106968f,0.0805376f},{0.231269f,0.112427f,0.0765956f}, -{0.213617f,0.120929f,0.0772001f},{0.17329f,0.133198f,0.055188f},{0.116591f,0.13552f,0.0403331f}, -{0.0802193f,0.127379f,0.0192598f},{-0.493428f,0.0950774f,0.0955275f},{0.2484f,0.0440115f,-0.0601652f}, -{-0.370294f,0.253812f,-0.169512f},{-0.111022f,0.432783f,-0.0540754f},{-0.341767f,0.371763f,-0.140233f}, -{0.00490979f,0.107778f,0.165281f},{0.354532f,-0.00598052f,0.126903f},{0.306489f,0.0576638f,0.11372f}, -{0.251905f,0.101791f,0.0923829f},{0.155001f,0.136703f,0.0576573f},{0.0638982f,0.126054f,0.0237292f}, -{-0.471158f,0.227446f,0.0947944f},{0.210871f,-0.0339346f,-0.115122f},{-0.364416f,0.301225f,-0.175711f}, -{-0.415199f,0.171673f,-0.00596764f},{-0.0313398f,0.106003f,0.124022f},{-0.373933f,-0.114942f,0.265239f}, -{-0.30726f,0.337359f,-0.118234f},{-0.354751f,0.372734f,-0.135635f},{-0.365773f,0.371879f,-0.139565f}, -{-0.377734f,0.368914f,-0.148497f},{-0.249732f,0.278743f,0.182431f},{0.414472f,-0.255606f,0.157571f}, -{-0.366409f,0.244616f,0.275818f},{0.0588116f,0.123977f,0.0193756f},{-0.0135591f,-0.0344555f,-0.136722f}, -{-0.334996f,-0.0174078f,-0.070943f},{-0.331845f,0.0327707f,-0.0486544f},{-0.296154f,0.189544f,-0.0344876f}, -{-0.311447f,0.0182952f,-0.068628f},{-0.411861f,0.00317034f,0.28592f},{-0.336385f,0.0394071f,-0.0446609f}, -{-0.373824f,0.149577f,-0.0365519f},{-0.206145f,-0.0328928f,-0.118819f},{-0.164853f,0.36213f,-0.0554644f}, -{-0.0502331f,0.364876f,-0.104202f},{0.0241696f,0.436738f,-0.172361f},{-0.0166265f,0.37692f,-0.118337f}, -{-0.0686377f,0.380663f,-0.106048f},{-0.29883f,-0.487836f,0.113443f},{-0.294437f,-0.484492f,0.132182f}, -{-0.308141f,-0.482139f,0.178213f},{-0.315858f,-0.470853f,0.190759f},{-0.322353f,-0.472705f,0.211215f}, -{-0.321221f,-0.473354f,0.227131f},{-0.318231f,-0.473683f,0.247111f},{-0.312199f,-0.470326f,0.264217f}, -{-0.261795f,-0.470602f,0.309167f},{-0.445468f,-0.10873f,0.186689f},{-0.0117649f,0.455272f,-0.144748f}, -{-0.143323f,0.321115f,-0.0665766f},{-0.0528568f,-0.072287f,-0.145269f},{-0.388891f,0.147744f,-0.0298704f}, -{-0.309575f,0.112421f,-0.0459663f},{-0.2584f,0.111939f,-0.0322755f},{-0.130366f,0.321269f,-0.0709109f}, -{-0.0524003f,-0.48237f,0.339783f},{-0.446195f,0.0441143f,0.316884f},{0.0216617f,-0.0345262f,-0.137951f}, -{-0.352847f,-0.483708f,0.0576445f},{-0.356075f,-0.469773f,0.0596958f},{-0.350429f,-0.475136f,0.0710717f}, -{-0.341156f,-0.469335f,0.0783448f},{-0.33214f,-0.475084f,0.0933861f},{-0.323581f,-0.473946f,0.103637f}, -{-0.313916f,-0.47374f,0.112247f},{-0.304296f,-0.473528f,0.120806f},{-0.295865f,-0.474049f,0.132092f}, -{-0.309215f,-0.47648f,0.173525f},{-0.388344f,-0.183197f,0.240886f},{-0.309209f,0.0606862f,-0.0464551f}, -{-0.144937f,-0.469689f,0.286795f},{-0.1356f,-0.476428f,0.291116f},{-0.127562f,-0.46976f,0.302588f}, -{-0.0715572f,-0.470249f,0.35494f},{-0.409167f,-0.16748f,0.227555f},{-0.387206f,-0.136735f,0.256403f}, -{-0.392647f,-0.167596f,0.24468f},{-0.37884f,0.348703f,-0.110453f},{-0.3565f,-0.472139f,0.0207839f}, -{-0.357188f,-0.468531f,0.0413749f},{-0.287049f,-0.46821f,0.139822f},{-0.307768f,-0.464139f,0.169345f}, -{-0.312887f,-0.453194f,0.195717f},{-0.313858f,-0.451362f,0.207813f},{-0.316077f,-0.453792f,0.227613f}, -{-0.314012f,-0.451928f,0.247651f},{-0.309286f,-0.455181f,0.261207f},{-0.304244f,-0.449902f,0.266995f}, -{-0.295235f,-0.449838f,0.283444f},{-0.286007f,-0.449561f,0.292402f},{-0.277171f,-0.450313f,0.299952f}, -{-0.374519f,-0.186734f,0.249908f},{-0.331883f,-0.183576f,0.278467f},{-0.462709f,0.150928f,0.280904f}, -{-0.433076f,0.152747f,0.323128f},{-0.104116f,-0.464307f,0.334298f},{-0.0812482f,-0.461503f,0.352503f}, -{-0.0733385f,-0.451188f,0.354908f},{-0.336172f,-0.166644f,0.281759f},{-0.43777f,-0.108312f,0.197183f}, -{-0.147838f,-0.133494f,-0.137751f},{-0.0286004f,-0.225665f,0.353288f},{-0.408935f,0.226353f,0.280621f}, -{-0.429083f,0.225813f,0.264442f},{-0.370602f,-0.166496f,0.26077f},{-0.298154f,-0.314164f,0.152426f}, -{-0.0732806f,0.0510787f,-0.0722163f},{-0.334481f,-0.451728f,0.0750458f},{-0.328919f,-0.46104f,0.0899135f}, -{-0.3187f,-0.451317f,0.0951738f},{-0.312122f,-0.457619f,0.107006f},{-0.298617f,-0.453034f,0.113488f}, -{-0.291473f,-0.460133f,0.125867f},{-0.279428f,-0.454069f,0.132092f},{-0.29737f,-0.452873f,0.171982f}, -{-0.45276f,0.174856f,0.284865f},{-0.44431f,0.173737f,0.29774f},{-0.427289f,0.16712f,0.318903f}, -{-0.406575f,-0.18373f,0.22281f},{-0.366802f,-0.217016f,0.238655f},{-0.141523f,-0.451227f,0.283952f}, -{-0.127375f,-0.449658f,0.302408f},{-0.113157f,-0.44805f,0.322588f},{-0.103524f,-0.449651f,0.334819f}, -{-0.0899618f,-0.448269f,0.342323f},{0.271126f,-0.130645f,-0.13741f},{-0.0707276f,-0.203961f,0.30428f}, -{0.00260761f,-0.072017f,0.302408f},{-0.455802f,-0.0919906f,0.169216f},{-0.44559f,-0.0912961f,0.186283f}, -{-0.0584965f,-0.210964f,0.323957f},{0.173792f,0.00269444f,-0.113758f},{-0.348648f,-0.455509f,0.0179094f}, -{-0.349555f,-0.452879f,0.0386161f},{-0.350937f,-0.451349f,0.0576959f},{-0.284753f,-0.445658f,0.118749f}, -{-0.308392f,-0.440108f,0.208173f},{-0.310733f,-0.437607f,0.229247f},{-0.309234f,-0.439041f,0.247343f}, -{-0.298547f,-0.434102f,0.265612f},{-0.291383f,-0.432648f,0.279033f},{-0.282592f,-0.432314f,0.288492f}, -{-0.272618f,-0.43661f,0.296852f},{-0.458715f,0.178895f,0.272531f},{-0.0676602f,-0.18647f,0.32253f}, -{-0.437976f,-0.0909038f,0.196875f},{-0.136584f,-0.445799f,0.288981f},{-0.0789525f,-0.443819f,0.349481f}, -{-0.428742f,-0.091116f,0.208373f},{-0.0783094f,-0.204212f,0.290229f},{-0.0325874f,-0.208662f,0.359738f}, -{-0.476387f,0.095849f,0.29264f},{-0.464033f,0.192155f,0.248481f},{-0.355021f,-0.18438f,0.264718f}, -{-0.421417f,-0.0858814f,0.216456f},{0.211874f,-0.0764349f,-0.137063f},{-0.419778f,-0.133899f,0.216379f}, -{-0.345446f,-0.44749f,0.0345069f},{-0.342796f,-0.444474f,0.0628726f},{-0.323922f,-0.444275f,0.0805248f}, -{-0.304701f,-0.443548f,0.102254f},{-0.445847f,0.190984f,0.282139f},{-0.304759f,-0.432591f,0.208778f}, -{-0.471949f,0.22553f,0.189505f},{-0.305209f,-0.42996f,0.245233f},{-0.472509f,0.133822f,0.267766f}, -{-0.0580335f,-0.23016f,0.305907f},{-0.412189f,0.111244f,-0.0244944f},{-0.350809f,-0.201775f,0.259194f}, -{-0.0508891f,-0.209247f,0.341642f},{-0.132706f,-0.43178f,0.286441f},{-0.123234f,-0.432751f,0.30037f}, -{-0.114218f,-0.430378f,0.308453f},{-0.106373f,-0.431169f,0.321893f},{-0.0908621f,-0.431697f,0.335636f}, -{-0.0644835f,-0.435356f,0.353127f},{-0.380763f,-0.0849297f,0.253034f},{-0.449288f,0.15413f,0.303514f}, -{-0.427443f,0.0979583f,0.336549f},{-0.438742f,-0.157519f,0.176856f},{-0.482116f,0.0947301f,0.28201f}, -{-0.358661f,-0.0896756f,0.271914f},{-0.463943f,0.11282f,0.299669f},{-0.446722f,0.114215f,0.321578f}, -{-0.450188f,0.0614321f,0.31981f},{-0.335607f,-0.433182f,0.0214527f},{-0.335639f,-0.43232f,0.0373557f}, -{-0.335767f,-0.432803f,0.0577666f},{-0.328237f,-0.435549f,0.0709881f},{-0.317929f,-0.430771f,0.0761326f}, -{-0.310128f,-0.435304f,0.0892769f},{-0.296978f,-0.432571f,0.0957654f},{-0.276869f,-0.433388f,0.110524f}, -{-0.261345f,-0.428771f,0.115553f},{-0.452278f,-0.129121f,0.171351f},{-0.387547f,0.358644f,-0.150857f}, -{-0.446857f,-0.147513f,0.169577f},{-0.295409f,-0.417614f,0.246751f},{-0.29301f,-0.420887f,0.262281f}, -{-0.283827f,-0.413819f,0.267406f},{-0.276895f,-0.417755f,0.282338f},{-0.426626f,-0.151667f,0.205633f}, -{-0.419507f,-0.151211f,0.216675f},{-0.464561f,0.244564f,0.187544f},{-0.0957429f,-0.426546f,0.329423f}, -{-0.427842f,-0.109006f,0.208855f},{-0.0315842f,-0.131005f,0.356342f},{-0.221398f,0.341102f,0.0392399f}, -{-0.436317f,0.0953217f,0.328613f},{-0.44842f,0.0965886f,0.319385f},{-0.0449986f,-0.204238f,0.353802f}, -{-0.409996f,-0.111668f,0.228552f},{-0.448921f,0.0793415f,0.320652f},{-0.413983f,-0.189016f,0.209865f}, -{-0.418858f,-0.170207f,0.213517f},{-0.393039f,-0.149989f,0.249072f},{-0.472271f,0.114196f,0.286049f}, -{-0.0770361f,-0.184045f,0.303456f},{-0.0587087f,-0.111366f,0.307225f},{-0.466773f,0.13143f,0.28448f}, -{-0.304373f,-0.426906f,0.08338f},{-0.283203f,-0.42724f,0.0985177f},{-0.470818f,0.0794573f,0.303206f}, -{0.0180348f,-0.0658371f,0.300537f},{-0.472207f,0.0373171f,0.287296f},{-0.424388f,-0.0574644f,0.203434f}, -{-0.427327f,-0.186952f,0.188206f},{-0.355272f,0.247703f,-0.205633f},{-0.460972f,0.221845f,0.219196f}, -{-0.220324f,0.282094f,-0.0189769f},{-0.424035f,-0.167602f,0.206501f},{-0.124147f,-0.416437f,0.282898f}, -{-0.109241f,-0.416295f,0.30163f},{-0.100746f,-0.418707f,0.315951f},{-0.0343944f,-0.111752f,0.340491f}, -{-0.428613f,-0.0390084f,0.190444f},{-0.435346f,-0.151584f,0.188849f},{-0.433398f,0.177049f,0.306505f}, -{-0.292097f,0.0731423f,-0.0469374f},{-0.477171f,0.117597f,0.271355f},{-0.0671586f,-0.220334f,0.299341f}, -{-0.435584f,0.21434f,0.271271f},{-0.447121f,0.208231f,0.264737f},{-0.454374f,0.194328f,0.267734f}, -{-0.428761f,0.208746f,0.282788f},{-0.0505161f,-0.112524f,0.320318f},{-0.319511f,-0.419511f,0.057175f}, -{-0.30845f,-0.41908f,0.0689946f},{-0.295852f,-0.417035f,0.0745828f},{-0.280985f,-0.413292f,0.0778046f}, -{-0.276342f,-0.419292f,0.0914569f},{-0.260008f,-0.414514f,0.0950452f},{-0.254246f,-0.419215f,0.105797f}, -{-0.240407f,-0.41562f,0.111405f},{-0.461442f,-0.128395f,0.151854f},{-0.430112f,0.0803511f,0.327166f}, -{-0.05822f,-0.191975f,0.341243f},{-0.205334f,0.361525f,0.0194656f},{-0.462625f,0.20989f,0.229748f}, -{-0.464291f,0.225054f,0.206225f},{-0.280007f,-0.405132f,0.244384f},{-0.278727f,-0.404682f,0.250751f}, -{-0.273512f,-0.404321f,0.2605f},{-0.410048f,0.0592521f,0.327919f},{-0.420376f,-0.116427f,0.216385f}, -{-0.0372239f,-0.191145f,0.365609f},{-0.0676474f,-0.147635f,0.320208f},{-0.334912f,-0.035658f,-0.0790007f}, -{-0.11571f,-0.410964f,0.289772f},{-0.0967975f,-0.410778f,0.307231f},{-0.459918f,0.0791679f,0.313257f}, -{-0.462799f,0.173641f,0.266088f},{-0.37394f,-0.132311f,0.265773f},{-0.45458f,0.131385f,0.306106f}, -{-0.410099f,-0.133423f,0.229388f},{-0.458336f,0.0964535f,0.311083f},{-0.432144f,-0.168991f,0.189698f}, -{-0.434587f,0.197093f,0.28893f},{-0.384557f,0.377666f,-0.178637f},{-0.4486f,-0.0571364f,0.170059f}, -{-0.300611f,-0.412051f,0.061027f},{-0.268567f,-0.411479f,0.0835214f},{-0.245236f,-0.411138f,0.101141f}, -{-0.430729f,0.114665f,0.337526f},{-0.18547f,0.381428f,0.0018006f},{-0.446433f,0.225388f,0.245452f}, -{-0.259577f,-0.398058f,0.244584f},{-0.0629658f,-0.182476f,0.334748f},{-0.455841f,-0.10945f,0.169776f}, -{-0.354686f,-0.167313f,0.270351f},{0.021816f,0.453555f,-0.172824f},{-0.435938f,0.256969f,0.215987f}, -{-0.0266197f,-0.19027f,0.371377f},{-0.108488f,-0.400058f,0.282358f},{-0.0891708f,-0.400842f,0.300203f}, -{-0.0369474f,-0.167924f,0.367345f},{-0.480599f,0.0447832f,0.280563f},{-0.428247f,-0.131031f,0.205395f}, -{-0.471255f,0.0615029f,0.30219f},{-0.467905f,0.0962027f,0.302376f},{-0.0124852f,-0.112241f,0.356728f}, -{-0.0659497f,-0.201543f,0.315546f},{-0.43069f,0.0579081f,0.325719f},{-0.391721f,0.361043f,-0.168503f}, -{-0.0402592f,-0.108016f,0.328118f},{-0.0692936f,-0.165249f,0.322337f},{-0.415758f,0.231973f,0.269123f}, -{-0.0491078f,-0.168175f,0.356812f},{-0.411855f,-0.150805f,0.227131f},{-0.276252f,-0.403736f,0.0591621f}, -{-0.263139f,-0.393949f,0.0586862f},{-0.256587f,-0.398482f,0.0750908f},{-0.238754f,-0.400064f,0.0943507f}, -{-0.221173f,-0.398187f,0.111971f},{-0.0518022f,-0.0313495f,-0.13678f},{-0.461873f,0.126678f,0.295373f}, -{-0.0625993f,-0.164754f,0.334028f},{-0.469712f,0.248879f,0.172548f},{-0.316585f,0.30408f,-0.337539f}, -{-0.0603357f,-0.142793f,0.331031f},{-0.453995f,0.254313f,0.193306f},{-0.450413f,0.248108f,0.208013f}, -{-0.407579f,-0.202386f,0.205923f},{-0.0902254f,-0.390258f,0.286531f},{-0.454368f,0.227427f,0.228777f}, -{-0.458053f,0.230726f,0.216012f},{-0.0643806f,-0.130118f,0.316434f},{-0.0579306f,-0.16876f,0.342966f}, -{-0.386557f,0.300614f,-0.0578695f},{-0.39601f,0.0348349f,0.328883f},{-0.413051f,0.214733f,0.289624f}, -{-0.0550433f,-0.147391f,0.341378f},{-0.396743f,0.361866f,-0.187402f},{-0.0361757f,-0.0912382f,0.305373f}, -{-0.246677f,-0.390277f,0.0792386f},{-0.226819f,-0.3942f,0.100441f},{-0.26366f,-0.134787f,-0.126446f}, -{-0.436922f,-0.132131f,0.193434f},{-0.0280152f,-0.167911f,0.37211f},{-0.277692f,0.11217f,-0.0390727f}, -{-0.0964053f,-0.386515f,0.274377f},{-0.0776663f,-0.386161f,0.292003f},{-0.36648f,0.256198f,-0.207614f}, -{-0.396286f,-0.190219f,0.229266f},{-0.454702f,0.211504f,0.249484f},{-0.0332691f,-0.0973731f,0.320819f}, -{-0.241224f,-0.374187f,0.0783962f},{-0.236639f,-0.385737f,0.087277f},{-0.224665f,-0.375756f,0.0965114f}, -{-0.214909f,-0.377351f,0.114614f},{-0.360847f,-0.149674f,0.271348f},{-0.226536f,-0.378438f,0.208411f}, -{-0.211958f,-0.374168f,0.234558f},{-0.0197968f,-0.0909424f,0.325385f},{-0.0126588f,-0.0958169f,0.341037f}, -{-0.485203f,0.0782483f,0.283611f},{-0.0904955f,-0.378817f,0.263721f},{-0.0708563f,-0.373782f,0.263747f}, -{-0.435166f,0.231472f,0.252204f},{-0.44177f,-0.0567249f,0.181435f},{-0.433931f,-0.0563455f,0.191724f}, -{-0.478322f,0.0742806f,0.295245f},{-0.445044f,0.130986f,0.32208f},{-0.0440468f,-0.116491f,0.332086f}, -{-0.358898f,0.270249f,-0.244905f},{-0.415553f,-0.0548728f,0.211466f},{-0.195482f,0.374914f,0.00135046f}, -{-0.398286f,0.398322f,-0.265959f},{-0.422491f,-0.182303f,0.20245f},{-0.0459182f,-0.146323f,0.352953f}, -{-0.25505f,-0.376264f,0.0585705f},{-0.221964f,-0.359371f,0.208373f},{-0.216093f,-0.359487f,0.222231f}, -{-0.21026f,-0.356882f,0.230481f},{-0.457905f,0.155063f,0.288119f},{-0.427893f,0.242931f,0.244635f}, -{-0.0752998f,-0.37139f,0.253111f},{-0.360834f,-0.114736f,0.274769f},{-0.0265812f,-0.115848f,0.350857f}, -{-0.460439f,0.205389f,0.24441f},{-0.416607f,-0.0167583f,0.196129f},{-0.350076f,-0.14824f,0.277245f}, -{-0.411231f,-0.0384168f,0.209408f},{-0.315588f,0.00127969f,-0.0730394f},{0.172898f,-0.129436f,-0.189807f}, -{-0.0764638f,-0.166014f,0.30682f},{-0.428452f,0.19211f,0.298402f},{-0.250825f,-0.368805f,0.0646989f}, -{-0.389058f,0.322427f,-0.0952188f},{-0.356712f,-0.206849f,0.252834f},{-0.0465419f,-0.357403f,0.278377f}, -{-0.224099f,0.347449f,0.0191119f},{-0.404106f,-0.150387f,0.237523f},{-0.43651f,0.136484f,0.328948f}, -{-0.465236f,0.0237292f,0.280358f},{-0.0378927f,-0.229034f,0.34296f},{-0.225662f,-0.354555f,0.0966657f}, -{-0.218459f,-0.360895f,0.133224f},{-0.222896f,-0.359718f,0.149725f},{-0.226658f,-0.35885f,0.169487f}, -{-0.446072f,0.241349f,0.225652f},{-0.208961f,-0.338079f,0.231587f},{-0.199334f,-0.338716f,0.243671f}, -{-0.190434f,-0.338407f,0.253085f},{-0.430259f,0.0928652f,-0.0149255f},{-0.370763f,-0.20236f,0.24396f}, -{-0.428697f,0.131423f,0.336812f},{-0.134944f,-0.137848f,-0.140883f},{-0.380165f,-0.168901f,0.253413f}, -{-0.452137f,-0.0750715f,0.171486f},{-0.394569f,-0.13215f,0.248159f},{-0.223205f,-0.347867f,0.118093f}, -{-0.228395f,-0.3415f,0.153513f},{-0.229205f,-0.337693f,0.171049f},{-0.226941f,-0.341288f,0.187949f}, -{-0.222208f,-0.337764f,0.206135f},{-0.216453f,-0.335346f,0.219665f},{-0.0562201f,-0.340066f,0.26477f}, -{-0.045288f,-0.339288f,0.281103f},{-0.0397254f,-0.338864f,0.28927f},{-0.0306775f,-0.338214f,0.302916f}, -{-0.0162857f,-0.338002f,0.322864f},{-0.443153f,-0.0753416f,0.186393f},{-0.436272f,-0.0714447f,0.194534f}, -{-0.443584f,0.144947f,0.317508f},{-0.489775f,0.132587f,0.152317f},{-0.427597f,-0.0741005f,0.205923f}, -{-0.417887f,-0.0715154f,0.215511f},{-0.426838f,0.145391f,0.332716f},{-0.409778f,-0.0735732f,0.224424f}, -{-0.243153f,-0.337134f,0.138073f},{-0.238452f,-0.33559f,0.146426f},{-0.234761f,0.336902f,0.0319668f}, -{-0.373387f,0.055458f,-0.0377222f},{-0.00782935f,-0.332369f,0.334369f},{-0.399386f,-0.0709559f,0.233349f}, -{-0.39073f,-0.0730523f,0.24169f},{0.0324137f,-0.332806f,0.353249f},{-0.376943f,-0.0711102f,0.251529f}, -{-0.465454f,0.0420951f,0.300209f},{-0.354879f,0.254474f,-0.242256f},{-0.368152f,-0.0732902f,0.259702f}, -{-0.41426f,0.194174f,0.307849f},{-0.175792f,0.340015f,-0.0504935f},{-0.335864f,0.00357545f,0.269503f}, -{-0.247333f,-0.325314f,0.159545f},{-0.240471f,-0.322408f,0.170895f},{-0.229861f,-0.320549f,0.193357f}, -{-0.223835f,-0.318286f,0.208328f},{-0.216009f,-0.317411f,0.22324f},{-0.208305f,-0.316961f,0.233716f}, -{-0.200177f,-0.316536f,0.243838f},{-0.191431f,-0.316221f,0.253355f},{-0.181309f,-0.316112f,0.261619f}, -{-0.387148f,-0.0592585f,0.240423f},{-0.0554613f,-0.321855f,0.268049f},{-0.0459503f,-0.321289f,0.281219f}, -{-0.0403621f,-0.32089f,0.289322f},{-0.0314427f,-0.320228f,0.303116f},{-0.0189415f,-0.319321f,0.322369f}, -{-0.0096428f,-0.318704f,0.335745f},{-0.0709527f,-0.0763191f,-0.143185f},{-0.412138f,0.0923057f,-0.0229381f}, -{-0.315684f,0.037748f,-0.054159f},{-0.371953f,0.0373943f,-0.032089f},{-0.0837626f,-0.0820745f,-0.141339f}, -{-0.31917f,0.112447f,-0.0468409f},{-0.383554f,0.306099f,-0.0502749f},{-0.14951f,0.301977f,-0.0557538f}, -{-0.146989f,0.359506f,-0.0680235f},{-0.0904248f,0.416559f,-0.0912253f},{-0.242909f,0.281901f,-0.00522811f}, -{-0.14686f,0.340272f,-0.0684029f},{-0.277113f,0.00260441f,-0.0934632f},{-0.20972f,-0.303701f,0.234783f}, -{-0.192158f,-0.302948f,0.253696f},{-0.184525f,-0.297354f,0.261445f},{-0.056831f,-0.303765f,0.26886f}, -{-0.0474165f,-0.303135f,0.28221f},{-0.0416996f,-0.302781f,0.290126f},{-0.0336099f,-0.302768f,0.302871f}, -{-0.0201698f,-0.301997f,0.324903f},{-0.0113919f,-0.300704f,0.336915f},{-0.0915244f,-0.0767114f,-0.138401f}, -{-0.31216f,-0.0945757f,-0.104903f},{-0.456374f,0.113887f,0.308447f},{-0.401116f,-0.172303f,0.235144f}, -{-0.0199254f,-0.107077f,0.34858f},{-0.373367f,0.0742934f,-0.0393878f},{-0.379554f,0.0314266f,-0.0254075f}, -{-0.244844f,0.00772325f,-0.102247f},{0.118289f,-0.0103276f,-0.120999f},{-0.165966f,0.320832f,-0.0557924f}, -{0.00993857f,0.455458f,-0.164033f},{-0.233404f,0.301f,-0.0111057f},{-0.0537764f,0.398206f,-0.11527f}, -{-0.0713321f,0.322536f,-0.07711f},{-0.28993f,-0.0385711f,-0.105804f},{-0.0270956f,-0.0990386f,0.332851f}, -{-0.00941129f,-0.0826083f,0.319334f},{-0.255114f,0.31945f,0.0384232f},{-0.0271278f,-0.296923f,0.316292f}, -{-0.00624098f,-0.065567f,0.292544f},{-0.244638f,0.304627f,0.000353705f},{0.00179091f,-0.0658114f,0.293675f}, -{-0.461127f,0.187479f,0.260963f},{-0.360635f,-0.270557f,0.17285f},{-0.277834f,0.149751f,-0.0346741f}, -{-0.459937f,0.0661715f,0.313636f},{-0.356075f,0.252288f,-0.226777f},{-0.300714f,0.0763063f,-0.046275f}, -{-0.386563f,0.341648f,-0.116253f},{-0.0601685f,-0.287084f,0.268679f},{-0.0514228f,-0.285534f,0.283702f}, -{-0.0382014f,-0.284692f,0.306009f},{-0.0291856f,-0.282936f,0.322395f},{-0.0161056f,-0.28219f,0.341063f}, -{-0.00232471f,-0.286537f,0.350111f},{-0.335253f,-0.258924f,0.224443f},{-0.385155f,0.333037f,-0.0948522f}, -{-0.0333591f,0.43542f,-0.127803f},{-0.411855f,-0.207029f,0.191672f},{-0.446426f,0.022861f,0.300524f}, -{-0.277055f,0.170085f,-0.031208f},{-0.258972f,0.00383911f,-0.100222f},{-0.317266f,-0.245445f,0.248603f}, -{-0.272728f,0.300087f,0.0339153f},{0.230388f,-0.091444f,-0.138401f},{-0.0138677f,-0.131848f,-0.228597f}, -{-0.201257f,-0.274763f,0.258956f},{-0.0455002f,-0.279547f,0.297772f},{-0.225276f,0.00369118f,-0.1068f}, -{-0.370486f,0.256436f,-0.188766f},{-0.382705f,0.288885f,-0.0146748f},{0.00393876f,0.417485f,-0.150645f}, -{-0.385271f,0.287875f,-0.0345519f},{-0.260792f,-0.0337803f,-0.108704f},{-0.244831f,-0.0334458f,-0.111713f}, -{0.134803f,-0.116003f,-0.199814f},{-0.392602f,0.0739076f,-0.032269f},{-0.239179f,0.224848f,0.00285523f}, -{-0.334828f,0.0558953f,-0.0452461f},{-0.372088f,-0.0197486f,-0.0509951f},{-0.460844f,0.239163f,0.200495f}, -{-0.0567988f,-0.266589f,0.285836f},{-0.0479953f,-0.264763f,0.302518f},{-0.0426835f,-0.269921f,0.308286f}, -{-0.0364136f,-0.265336f,0.322581f},{-0.0279445f,-0.261644f,0.338645f},{-0.0204399f,-0.265175f,0.346375f}, -{-0.0110511f,-0.263548f,0.353577f},{-0.0415196f,-0.213491f,0.350619f},{-0.366557f,-0.114839f,-0.0851419f}, -{-0.387206f,0.286878f,-0.0535867f},{-0.0459696f,-0.223099f,0.337192f},{-0.23932f,0.111945f,-0.0249381f}, -{-0.368429f,-0.18074f,0.257278f},{-0.39747f,0.150677f,-0.024115f},{-0.40736f,0.0371628f,-0.0137487f}, -{-0.279833f,0.0576059f,-0.0560432f},{-0.18682f,-0.0372271f,-0.123777f},{-0.200994f,0.287045f,-0.0307f}, -{-0.405437f,0.0665188f,0.332163f},{-0.392486f,-0.00592905f,0.264339f},{-0.167876f,-0.0127777f,-0.115559f}, -{-0.431706f,0.110852f,-0.014887f},{-0.383843f,0.297264f,-0.0405903f},{-0.359869f,-0.136562f,0.274609f}, -{-0.205707f,-0.134285f,-0.130022f},{-0.379097f,-0.128182f,-0.07911f},{-0.167509f,-0.0371756f,-0.123816f}, -{-0.148236f,0.00388414f,-0.109051f},{-0.108071f,-0.0344876f,-0.125636f},{-0.130231f,-0.0316775f,-0.120556f}, -{-0.12252f,-0.0370213f,-0.122819f},{-0.390344f,-0.203717f,0.223781f},{-0.060863f,-0.248262f,0.289296f}, -{-0.053069f,-0.248204f,0.302363f},{-0.0462461f,-0.24533f,0.317038f},{-0.0402077f,-0.246346f,0.327642f}, -{-0.0311919f,-0.245748f,0.34132f},{-0.0216617f,-0.250088f,0.350182f},{-0.479345f,0.10862f,0.276891f}, -{-0.0165558f,0.416456f,-0.13696f},{-0.240613f,0.00181343f,-0.105263f},{-0.251307f,-0.00141475f,-0.104061f}, -{-0.243983f,-0.345249f,0.0791357f},{-0.37976f,-0.146928f,-0.0790264f},{-0.284489f,0.186753f,-0.0306292f}, -{-0.166853f,-0.488621f,-0.151378f},{-0.176988f,-0.48165f,-0.146484f},{-0.182602f,-0.485354f,-0.132883f}, -{-0.182563f,-0.469779f,-0.130819f},{-0.188415f,-0.472615f,-0.114112f},{-0.192512f,-0.473065f,-0.0986913f}, -{-0.196087f,-0.475657f,-0.0901064f},{-0.20235f,-0.47093f,-0.0717211f},{0.132764f,-0.0231825f,-0.122986f}, -{0.039944f,-0.48882f,-0.149474f},{0.0224976f,-0.0872319f,-0.190502f},{0.135433f,-0.056204f,-0.131603f}, -{-0.343092f,-0.463438f,0.00420567f},{-0.487267f,0.130806f,0.17258f},{-0.163702f,-0.472956f,-0.165364f}, -{-0.171027f,-0.467946f,-0.152497f},{0.0600398f,0.0716697f,-0.0695862f},{0.0408508f,-0.0974438f,-0.205595f}, -{0.300392f,-0.193556f,-0.123572f},{-0.335619f,-0.449535f,0.00314461f},{-0.173612f,0.381512f,-0.0386097f}, -{-0.186685f,0.381075f,-0.0166104f},{-0.482695f,0.130813f,0.191782f},{0.00456896f,0.0809042f,-0.037928f}, -{-0.322411f,0.376715f,-0.357763f},{-0.327215f,0.382174f,-0.349963f},{-0.33297f,0.377602f,-0.33707f}, -{-0.342153f,0.376753f,-0.319134f},{-0.351407f,0.377486f,-0.301778f},{-0.358731f,0.376528f,-0.286988f}, -{-0.278682f,-0.454268f,0.148587f},{-0.289023f,-0.458513f,0.162754f},{-0.480489f,0.126414f,0.211106f}, -{-0.478753f,0.126446f,0.223967f},{-0.314533f,0.396926f,-0.373435f},{-0.337992f,0.395717f,-0.337192f}, -{-0.372744f,0.393184f,-0.287315f},{-0.145664f,0.399228f,-0.0548149f},{-0.369689f,0.404225f,-0.30208f}, -{-0.341741f,0.412334f,-0.341507f},{-0.47602f,0.131777f,0.231665f},{-0.175091f,-0.462834f,-0.138176f}, -{-0.181431f,-0.450474f,-0.112922f},{-0.187457f,-0.452378f,-0.0945114f},{-0.198215f,-0.457599f,-0.0711102f}, -{-0.322347f,-0.449432f,-0.0207646f},{-0.342114f,-0.444552f,0.0145719f},{-0.475576f,0.130928f,0.249651f}, -{-0.388409f,0.328401f,-0.169358f},{-0.372094f,0.323552f,-0.188772f},{-0.360223f,0.323527f,-0.227272f}, -{-0.346783f,0.324787f,-0.280885f},{-0.306334f,-0.447471f,0.184972f},{-0.377014f,0.354587f,-0.231073f}, -{-0.0322337f,0.0547957f,-0.0699784f},{-0.213797f,0.357223f,0.0104113f},{-0.489724f,0.134935f,0.133899f}, -{-0.388544f,0.426584f,-0.314903f},{-0.360107f,0.341494f,-0.246744f},{-0.362088f,0.26956f,-0.22697f}, -{-0.144899f,-0.453497f,-0.168876f},{-0.161966f,-0.451529f,-0.151211f},{-0.168705f,-0.448545f,-0.137327f}, -{-0.177663f,-0.450487f,-0.128858f},{-0.191148f,-0.446744f,-0.0748786f},{-0.31416f,-0.436713f,-0.0157422f}, -{-0.320385f,-0.433047f,0.000713822f},{-0.331632f,-0.436893f,0.00825056f},{-0.398807f,0.272673f,0.0160702f}, -{-0.259255f,0.306582f,0.0174271f},{-0.16639f,0.0193627f,-0.0963892f},{-0.2655f,-0.443079f,0.133751f}, -{-0.267403f,-0.442867f,0.143391f},{-0.274734f,-0.442063f,0.153011f},{-0.281171f,-0.433812f,0.169056f}, -{-0.295209f,-0.439838f,0.175518f},{-0.299312f,-0.434674f,0.188206f},{-0.183753f,0.0188611f,-0.0958747f}, -{-0.145092f,0.0895598f,-0.0480499f},{-0.0156812f,-0.0939005f,-0.187737f},{-0.357644f,0.287502f,-0.227073f}, -{-0.388158f,0.288332f,-0.0727115f},{-0.436201f,0.137925f,-0.000482281f},{-0.443339f,0.13341f,0.00564614f}, -{-0.152873f,-0.447098f,-0.158869f},{-0.205546f,-0.434385f,-0.0600752f},{0.249809f,-0.0934632f,-0.132144f}, -{0.117897f,0.0717212f,-0.0700427f},{-0.460078f,-0.0407382f,0.096267f},{-0.299132f,-0.431536f,-0.0201794f}, -{-0.373547f,0.270056f,-0.149796f},{-0.371355f,0.26994f,-0.169081f},{-0.256085f,-0.435066f,0.130497f}, -{-0.260657f,-0.432256f,0.151358f},{-0.307132f,-0.429189f,0.226957f},{-0.410871f,0.260808f,0.0192406f}, -{-0.105749f,0.430977f,-0.0668081f},{-0.476682f,0.187891f,0.13361f},{-0.328989f,0.397312f,-0.352194f}, -{-0.146269f,-0.434886f,-0.151243f},{-0.163432f,-0.433066f,-0.131983f},{-0.169901f,-0.431382f,-0.113977f}, -{-0.172757f,-0.428777f,-0.0962863f},{-0.178994f,-0.435002f,-0.0914054f},{-0.182042f,-0.430301f,-0.075168f}, -{-0.188068f,-0.430121f,-0.0630205f},{-0.286296f,-0.426526f,-0.0209511f},{-0.295679f,-0.423536f,-0.0106942f}, -{-0.299158f,-0.419125f,0.00205783f},{-0.30964f,-0.421794f,0.00842418f},{-0.319067f,-0.420539f,0.0211762f}, -{-0.317787f,-0.415877f,0.0403717f},{-0.241513f,-0.42742f,0.130504f},{-0.246574f,-0.426957f,0.146503f}, -{-0.265088f,-0.424977f,0.165782f},{-0.278605f,-0.41899f,0.188264f},{-0.289878f,-0.422186f,0.194675f}, -{-0.296367f,-0.421485f,0.207543f},{-0.296624f,-0.416861f,0.226842f},{-0.214041f,0.35705f,0.000868158f}, -{-0.372178f,0.360188f,-0.245767f},{-0.126732f,-0.431999f,-0.168876f},{-0.137542f,-0.430205f,-0.159088f}, -{-0.150487f,-0.428944f,-0.139693f},{-0.107415f,0.0218128f,-0.0993151f},{-0.107826f,-0.0982734f,-0.142163f}, -{-0.260014f,-0.410617f,-0.0196071f},{-0.275139f,-0.418797f,-0.0182245f},{-0.280573f,-0.410868f,0.00176845f}, -{-0.301145f,-0.413131f,0.0196264f},{-0.478277f,0.24097f,0.165397f},{-0.479788f,0.243169f,0.150992f}, -{-0.23289f,-0.419176f,0.124035f},{-0.22716f,-0.414501f,0.131655f},{-0.237886f,-0.41652f,0.151256f}, -{-0.245224f,-0.411183f,0.167319f},{-0.258143f,-0.416636f,0.172162f},{-0.261242f,-0.410231f,0.188219f}, -{-0.279152f,-0.410733f,0.208804f},{-0.234761f,0.338857f,0.0169963f},{-0.109099f,-0.452146f,-0.183042f}, -{-0.12879f,-0.415852f,-0.150034f},{-0.137793f,-0.420694f,-0.146503f},{-0.160075f,-0.418713f,-0.114035f}, -{-0.16293f,-0.414913f,-0.093386f},{-0.167233f,-0.411961f,-0.0760554f},{0.137401f,0.0762162f,-0.0700106f}, -{0.193206f,0.0219222f,-0.0992637f},{0.117524f,0.0372528f,-0.107186f},{-0.242619f,-0.416353f,-0.0356644f}, -{-0.269223f,-0.407247f,-0.0048487f},{-0.283293f,-0.406013f,0.0177808f},{-0.290901f,-0.405099f,0.0402945f}, -{-0.286155f,-0.405035f,0.0532973f},{-0.477178f,0.13523f,0.211067f},{-0.359001f,0.238642f,-0.130349f}, -{-0.36293f,0.23879f,-0.11426f},{-0.227366f,-0.410913f,0.117514f},{-0.229591f,-0.410585f,0.1464f}, -{-0.251204f,-0.408251f,0.181776f},{-0.265866f,-0.406694f,0.201074f},{-0.277969f,-0.405363f,0.226771f}, -{-0.344474f,0.321842f,-0.289502f},{-0.364229f,0.372869f,-0.272808f},{-0.39909f,0.359654f,-0.208148f}, -{-0.167573f,0.0368927f,-0.0891675f},{-0.149921f,-0.409479f,-0.111752f},{-0.155606f,-0.408765f,-0.0957525f}, -{-0.171252f,-0.408855f,-0.0626539f},{-0.128803f,0.0930517f,-0.0337481f},{-0.236954f,-0.399897f,-0.031761f}, -{-0.245462f,-0.39474f,-0.0222887f},{-0.253468f,-0.396849f,-0.0136394f},{-0.260998f,-0.394527f,0.00144692f}, -{-0.266863f,-0.393839f,0.0208096f},{-0.271139f,-0.397891f,0.0370406f},{-0.266123f,-0.39137f,0.0419858f}, -{-0.218028f,-0.398206f,0.130208f},{-0.225591f,-0.397363f,0.149455f},{-0.230806f,-0.39528f,0.164496f}, -{-0.234845f,-0.397846f,0.173094f},{-0.239385f,-0.395852f,0.188129f},{-0.240941f,-0.393396f,0.2058f}, -{-0.255069f,-0.398714f,0.210662f},{-0.260741f,-0.398077f,0.226726f},{0.422581f,-0.303431f,-0.008855f}, -{-0.252979f,0.320176f,0.0204238f},{0.00404165f,0.0687116f,-0.0526414f},{0.0234815f,0.028057f,-0.100048f}, -{-0.0830745f,0.449953f,-0.0670911f},{-0.121877f,-0.402958f,-0.124716f},{-0.132301f,-0.403363f,-0.111636f}, -{-0.142712f,-0.403344f,-0.095984f},{-0.159342f,-0.401923f,-0.0683258f},{-0.166024f,-0.395119f,-0.0611942f}, -{0.0255393f,-0.0174078f,-0.13015f},{0.210421f,0.0209189f,-0.0930066f},{0.233301f,-0.11307f,-0.146889f}, -{-0.430722f,0.244603f,0.0369442f},{-0.476554f,0.228989f,0.109829f},{-0.47928f,0.226076f,0.118395f}, -{-0.480772f,0.227626f,0.133404f},{-0.236536f,-0.391434f,0.223472f},{-0.231886f,-0.391839f,0.233137f}, -{-0.374023f,0.377255f,-0.266467f},{-0.392621f,0.402617f,-0.285676f},{-0.36331f,0.354966f,-0.253329f}, -{-0.360352f,0.305559f,-0.207871f},{-0.0127617f,0.0691425f,-0.0529565f},{0.225211f,-0.100518f,-0.143616f}, -{0.22833f,-0.0351178f,-0.111739f},{0.400203f,-0.263914f,0.0221408f},{0.286573f,-0.0366419f,-0.0902029f}, -{-0.107479f,0.0613164f,-0.058474f},{-0.0914923f,-0.401138f,-0.148098f},{-0.0929006f,-0.395145f,-0.13352f}, -{-0.104617f,-0.399666f,-0.133211f},{-0.110173f,-0.396367f,-0.114093f},{-0.107247f,-0.390348f,-0.0948522f}, -{-0.127697f,-0.397434f,-0.09228f},{-0.128777f,-0.393499f,-0.074049f},{-0.146777f,-0.391113f,-0.0637793f}, -{0.0422655f,-0.00248225f,-0.127166f},{-0.0146008f,0.0365648f,-0.0938812f},{-0.234967f,-0.384721f,-0.0298961f}, -{-0.244812f,-0.378399f,-0.0178837f},{-0.255571f,-0.377197f,0.00137618f},{-0.26114f,-0.376528f,0.0207518f}, -{-0.260188f,-0.376483f,0.0402431f},{-0.477473f,0.209826f,0.107881f},{-0.34704f,0.303643f,-0.280718f}, -{-0.216215f,-0.381248f,0.131378f},{-0.222665f,-0.379615f,0.149326f},{-0.227957f,-0.378978f,0.168689f}, -{-0.229198f,-0.378753f,0.188039f},{-0.421314f,0.195351f,0.0116652f},{-0.157689f,0.393672f,-0.0470981f}, -{-0.358873f,0.35858f,-0.267483f},{-0.394415f,0.342664f,-0.187473f},{0.250214f,0.0217163f,-0.0747114f}, -{-0.0781615f,-0.389061f,-0.137552f},{-0.0940581f,-0.38829f,-0.114215f},{-0.0967204f,-0.388026f,-0.101341f}, -{-0.110759f,-0.386779f,-0.0755217f},{-0.224793f,-0.379332f,-0.0364618f},{0.309421f,-0.0351564f,-0.0752451f}, -{-0.354301f,0.212225f,-0.053233f},{-0.356635f,0.2038f,-0.0401016f},{-0.212697f,-0.371692f,0.11727f}, -{-0.0316228f,-0.0987171f,-0.188586f},{0.100437f,0.099045f,-0.0386418f},{-0.376178f,0.319128f,-0.169448f}, -{-0.362467f,0.319096f,-0.211157f},{-0.300559f,0.395724f,-0.384277f},{-0.365618f,0.363963f,-0.259812f}, -{0.286451f,0.0156522f,-0.0540175f},{-0.110263f,0.00234718f,-0.109418f},{-0.067493f,-0.378502f,-0.149243f}, -{-0.0773962f,-0.376142f,-0.133758f},{-0.0834796f,-0.375563f,-0.11426f},{-0.0887785f,-0.374985f,-0.094968f}, -{-0.0947783f,-0.374297f,-0.0758946f},{0.327723f,-0.0941063f,-0.0786534f},{-0.261332f,0.0396836f,-0.0784283f}, -{0.0601363f,0.00210928f,-0.129205f},{-0.234491f,-0.361545f,-0.0312144f},{-0.24433f,-0.360458f,-0.0179544f}, -{-0.250625f,-0.359802f,-0.00518309f},{-0.254902f,-0.359339f,0.00448862f},{-0.258381f,-0.358934f,0.0206553f}, -{-0.256709f,-0.359005f,0.0401981f},{-0.252696f,-0.359326f,0.0564162f},{-0.248773f,-0.359622f,0.0660429f}, -{-0.226395f,-0.361004f,0.187975f},{-0.43997f,0.24032f,0.0500048f},{-0.185708f,0.0393235f,-0.0903636f}, -{0.399167f,-0.275515f,0.0147327f},{-0.0604257f,-0.373454f,-0.159667f},{0.021726f,0.00198706f,-0.117398f}, -{-0.447256f,0.245632f,0.0579596f},{0.192859f,-0.110691f,-0.169872f},{-0.224626f,0.343153f,-0.00208995f}, -{-0.0538279f,0.437452f,-0.112003f},{-0.46213f,0.169589f,0.056577f},{-0.0297193f,0.454834f,-0.127893f}, -{0.174493f,0.040108f,-0.0943442f},{0.0798784f,0.0608919f,-0.0775023f},{-0.128925f,0.00378121f,-0.110685f}, -{0.306341f,-0.131018f,-0.115533f},{-0.0521945f,-0.359069f,-0.170985f},{-0.0679753f,-0.35905f,-0.153082f}, -{-0.0764767f,-0.358188f,-0.136973f},{-0.0822835f,-0.357609f,-0.127276f},{-0.085396f,-0.357255f,-0.114395f}, -{-0.0885663f,-0.356863f,-0.0950709f},{-0.0949005f,-0.355339f,-0.0771744f},{-0.244137f,0.0433684f,-0.0812385f}, -{0.00293558f,0.00318962f,-0.117385f},{-0.224826f,-0.359513f,-0.0394714f},{-0.238709f,-0.335687f,-0.0346741f}, -{-0.246516f,-0.342497f,-0.0181602f},{-0.250452f,-0.346484f,-0.00528599f},{-0.254458f,-0.340947f,0.00241795f}, -{-0.259416f,-0.341108f,0.0204624f},{-0.262072f,-0.339925f,0.0405711f},{-0.260541f,-0.339442f,0.0580367f}, -{-0.250715f,-0.346066f,0.0657085f},{-0.229764f,-0.34777f,0.0946079f},{-0.478084f,0.143294f,0.0695798f}, -{-0.0194624f,0.454918f,-0.137661f},{0.230504f,0.0393814f,-0.0744413f},{-0.0131475f,0.00237936f,-0.119089f}, -{-0.261159f,-0.336709f,0.0775474f},{-0.336951f,0.323102f,-0.320594f},{-0.482161f,0.146793f,0.15314f}, -{-0.36412f,0.292286f,-0.172464f},{-0.341118f,0.323173f,-0.304447f},{-0.154821f,0.402887f,-0.0399472f}, -{-0.480985f,0.14397f,0.17247f},{-0.373953f,0.305714f,-0.150085f},{0.0102794f,0.469335f,-0.163043f}, -{-0.047185f,-0.340516f,-0.185473f},{-0.0574934f,-0.341931f,-0.17566f},{-0.0636925f,-0.339854f,-0.168181f}, -{-0.0706248f,-0.340658f,-0.15314f},{-0.0778142f,-0.339886f,-0.136979f},{-0.0816469f,-0.339539f,-0.127417f}, -{-0.0869393f,-0.338999f,-0.114536f},{-0.0922639f,-0.33842f,-0.0952124f},{-0.0962638f,-0.33887f,-0.0771679f}, -{-0.249024f,-0.326324f,-0.0227131f},{-0.257783f,-0.31788f,-0.0144175f},{-0.261165f,-0.323372f,-0.000257208f}, -{-0.266278f,-0.326858f,0.0179351f},{-0.2693f,-0.329141f,0.0326421f},{-0.14104f,0.379949f,-0.068075f}, -{0.217597f,-0.0904601f,-0.14069f},{-0.478329f,0.144021f,0.185338f},{-0.297132f,0.16867f,-0.03742f}, -{-0.349812f,0.27331f,-0.282345f},{-0.0403556f,0.45639f,-0.116228f},{0.00156584f,0.473284f,-0.154117f}, -{-0.185734f,0.0511109f,-0.0847947f},{-0.0329411f,0.0695283f,-0.0532716f},{0.281827f,-0.0642809f,-0.104826f}, -{-0.0320729f,0.00306098f,-0.118446f},{0.208871f,-0.118466f,-0.163217f},{0.154847f,-0.090473f,-0.168831f}, -{-0.167374f,-0.073914f,-0.129115f},{-0.072599f,0.340851f,-0.0886917f},{-0.366667f,0.381724f,-0.279457f}, -{-0.230234f,0.324427f,-0.00771678f},{-0.444722f,0.260564f,0.0609241f},{-0.390074f,0.266377f,-0.0179994f}, -{-0.205019f,0.0589885f,-0.0783897f},{0.155696f,0.0757082f,-0.0711424f},{0.289036f,0.00604484f,-0.0610784f}, -{-0.05386f,-0.32471f,-0.188232f},{-0.0694608f,-0.322703f,-0.169332f},{-0.0759301f,-0.322035f,-0.153191f}, -{-0.0800071f,-0.326137f,-0.137089f},{-0.0837948f,-0.318942f,-0.132272f},{-0.0893701f,-0.3208f,-0.114633f}, -{-0.0966689f,-0.319919f,-0.0953024f},{-0.0994277f,-0.324112f,-0.0823703f},{-0.106489f,-0.321019f,-0.0733867f}, -{0.292431f,-0.0723835f,-0.100453f},{-0.149445f,-0.470924f,-0.172438f},{0.0788817f,0.000926046f,-0.129231f}, -{0.0984116f,0.0203595f,-0.112736f},{0.118257f,-0.112266f,-0.202592f},{0.20846f,-0.0947365f,-0.144658f}, -{0.17383f,-0.073406f,-0.137037f},{-0.316964f,-0.304125f,-0.00106747f},{-0.464509f,0.227427f,0.0788464f}, -{-0.42925f,0.260982f,0.0385454f},{-0.163574f,0.397498f,-0.0332915f},{-0.351902f,0.235922f,-0.147539f}, -{-0.22152f,0.0564291f,-0.0771293f},{-0.0656667f,-0.318678f,-0.179255f},{0.252535f,-0.0537153f,-0.117816f}, -{-0.0121893f,-0.0160059f,-0.130941f},{-0.127453f,-0.47147f,-0.182393f},{0.0793254f,0.0197872f,-0.113578f}, -{0.458831f,-0.267097f,0.0243722f},{-0.317376f,-0.295476f,-0.0150156f},{-0.333626f,-0.295296f,0.00389057f}, -{-0.336764f,-0.2979f,0.0185653f},{-0.257429f,0.283187f,0.00455936f},{-0.405932f,-0.241375f,0.133391f}, -{-0.354153f,0.0745571f,-0.0441465f},{-0.419096f,0.153346f,-0.00814763f},{-0.327099f,0.327359f,-0.349841f}, -{-0.322951f,0.394309f,-0.360857f},{-0.37075f,0.287991f,-0.130658f},{-0.36621f,0.267438f,-0.206064f}, -{-0.162898f,0.000514465f,-0.108871f},{-0.0462654f,-0.312871f,-0.199325f},{-0.0594933f,-0.300961f,-0.192058f}, -{-0.0672487f,-0.304775f,-0.182322f},{-0.0766374f,-0.303913f,-0.169435f},{-0.0791132f,-0.30808f,-0.153217f}, -{-0.0874795f,-0.302756f,-0.133976f},{-0.0951449f,-0.30208f,-0.114762f},{-0.0997557f,-0.306196f,-0.101907f}, -{-0.103241f,-0.300428f,-0.0942606f},{-0.11018f,-0.303219f,-0.0795022f},{0.321511f,-0.0410662f,-0.0695154f}, -{-0.0158227f,-0.485991f,-0.167647f},{0.139388f,-0.0693933f,-0.138169f},{-0.333099f,-0.281046f,-0.0147905f}, -{-0.340777f,-0.283181f,-0.00391625f},{-0.351394f,-0.27911f,0.00481658f},{-0.357329f,-0.284029f,0.0211119f}, -{-0.386068f,-0.0377737f,-0.0490209f},{-0.321331f,0.235285f,-0.148297f},{-0.374062f,-0.280004f,0.0586155f}, -{-0.372666f,-0.282261f,0.0763448f},{0.249629f,-0.0174978f,-0.0920742f},{-0.164429f,0.0917784f,-0.0503135f}, -{0.335099f,-0.00899008f,-0.0222115f},{-0.374043f,0.250539f,-0.110157f},{0.213424f,0.0407125f,-0.0809684f}, -{-0.363529f,0.337031f,-0.230636f},{0.214073f,0.0646603f,-0.0599209f},{-0.0494036f,-0.298383f,-0.201749f}, -{-0.0826758f,-0.298711f,-0.153262f},{0.288766f,-0.16813f,-0.13006f},{0.349883f,-0.104794f,-0.0615607f}, -{-0.369618f,-0.275419f,0.0223337f},{-0.372994f,-0.277071f,0.0392592f},{-0.363252f,0.274158f,-0.210945f}, -{-0.371876f,0.252358f,-0.130369f},{-0.166056f,0.0586155f,-0.0782997f},{-0.0469342f,0.0687309f,-0.0523006f}, -{-0.0710235f,-0.281856f,-0.185775f},{-0.0795184f,-0.290126f,-0.172843f},{-0.084097f,-0.283644f,-0.168522f}, -{-0.0888235f,-0.284647f,-0.153436f},{-0.0927912f,-0.284152f,-0.134047f},{-0.0977621f,-0.2852f,-0.119077f}, -{-0.102334f,-0.281837f,-0.110601f},{-0.110096f,-0.283251f,-0.095013f},{0.193167f,-0.0444487f,-0.125932f}, -{0.210254f,-0.0442237f,-0.126176f},{-0.338134f,-0.268429f,-0.02416f},{-0.353317f,-0.261464f,-0.0176714f}, -{-0.356571f,-0.267033f,-0.00273944f},{-0.370892f,-0.259053f,0.00223789f},{-0.378017f,-0.261207f,0.018829f}, -{-0.380563f,-0.266255f,0.036031f},{-0.388769f,-0.257387f,0.0409119f},{-0.393296f,-0.259284f,0.0599917f}, -{-0.224581f,0.0217292f,-0.0973795f},{-0.277493f,0.0697534f,-0.0489566f},{-0.348191f,0.38202f,-0.311495f}, -{0.230028f,-0.150413f,-0.164348f},{-0.174428f,0.365828f,-0.0464615f},{-0.00840811f,0.473888f,-0.144368f}, -{-0.127317f,0.0454583f,-0.0789106f},{-0.0519373f,-0.281245f,-0.207955f},{0.328263f,0.00381982f,-0.0205267f}, -{-0.385071f,-0.255535f,0.0264493f},{-0.403219f,-0.253587f,0.0716247f},{-0.412279f,-0.24295f,0.0788335f}, -{-0.410434f,-0.242712f,0.115668f},{-0.479229f,0.245175f,0.114061f},{-0.48065f,0.245272f,0.133314f}, -{-0.391457f,0.382155f,-0.259612f},{0.0414745f,0.078602f,-0.057355f},{0.097119f,0.032134f,-0.104755f}, -{-0.380975f,0.283476f,-0.0952445f},{-0.297859f,0.0199222f,-0.0754959f},{-0.0166522f,0.476062f,-0.133867f}, -{0.251429f,0.0748529f,-0.0162503f},{-0.0652552f,-0.273599f,-0.199029f},{-0.0789203f,-0.263027f,-0.187409f}, -{-0.0867143f,-0.266744f,-0.16957f},{-0.0926819f,-0.266152f,-0.153474f},{-0.0976142f,-0.267895f,-0.135738f}, -{-0.105775f,-0.264969f,-0.11498f},{-0.11371f,-0.266043f,-0.10136f},{0.223366f,-0.0452654f,-0.125063f}, -{-0.164191f,0.0825826f,-0.0599595f},{-0.25966f,-0.4712f,-0.0566155f},{0.152616f,-0.130073f,-0.200251f}, -{-0.0534999f,-0.0905823f,-0.151551f},{-0.358661f,-0.248108f,-0.036076f},{-0.376686f,-0.248043f,-0.0189383f}, -{-0.392094f,-0.241774f,0.000803851f},{-0.394306f,-0.243728f,0.0184432f},{-0.400145f,-0.245285f,0.03297f}, -{-0.409405f,-0.240314f,0.0406161f},{-0.412511f,-0.241253f,0.0587698f},{-0.476059f,0.249876f,0.155802f}, -{-0.381837f,0.372689f,-0.253587f},{0.0425099f,0.051426f,-0.0864795f},{-0.242034f,0.0572844f,-0.0712903f}, -{-0.0562265f,-0.265136f,-0.213819f},{-0.0698724f,-0.258506f,-0.203993f},{-0.0831645f,-0.258114f,-0.182509f}, -{-0.101318f,-0.260763f,-0.130954f},{-0.0545867f,0.00329893f,-0.113752f},{0.212292f,0.0944536f,-0.0180444f}, -{0.227436f,0.0886209f,-0.0160766f},{-0.371393f,-0.23879f,-0.0384425f},{-0.389373f,-0.238487f,-0.0192598f}, -{-0.405399f,-0.236391f,0.0231697f},{-0.261307f,-0.265336f,0.252268f},{-0.478856f,0.152291f,0.101656f}, -{-0.480149f,0.152413f,0.114511f},{-0.194711f,0.375621f,-0.00891288f},{-0.479936f,0.152529f,0.133803f}, -{-0.374621f,0.285663f,-0.112993f},{-0.382448f,0.336967f,-0.195331f},{-0.260792f,0.0700299f,-0.0518826f}, -{-0.0886049f,-0.241594f,-0.185068f},{-0.0948683f,-0.248005f,-0.166535f},{-0.0981866f,-0.252121f,-0.150317f}, -{-0.105325f,-0.246873f,-0.13424f},{-0.112263f,-0.246918f,-0.116922f},{0.0821356f,-0.110093f,-0.211627f}, -{0.289306f,-0.150799f,-0.128838f},{-0.205109f,0.00398703f,-0.106389f},{0.23579f,0.0818816f,-0.0195878f}, -{0.136121f,-0.081618f,-0.163789f},{-0.00316069f,0.0486287f,-0.0831098f},{-0.378866f,-0.22953f,-0.0397801f}, -{-0.387393f,-0.222649f,-0.0356902f},{-0.396486f,-0.225935f,-0.0234333f},{-0.404749f,-0.22207f,-0.0145719f}, -{-0.408363f,-0.222977f,0.000681669f},{-0.414556f,-0.223279f,0.0187132f},{-0.420665f,-0.226301f,0.0392914f}, -{-0.425784f,-0.221337f,0.0586155f},{-0.420003f,-0.226192f,0.0779525f},{-0.419192f,-0.225961f,0.097238f}, -{-0.417244f,-0.22625f,0.116575f},{-0.41498f,-0.224546f,0.135044f},{0.000530508f,0.43643f,-0.153616f}, -{0.247841f,-0.0313559f,-0.101321f},{-0.0731263f,-0.242809f,-0.209009f},{-0.101801f,-0.242712f,-0.150336f}, -{-0.0732871f,0.0348092f,-0.0873284f},{0.220356f,-0.122761f,-0.158651f},{0.1365f,0.0392913f,-0.106254f}, -{0.155336f,0.0330022f,-0.105945f},{0.100579f,-0.0536767f,-0.136889f},{0.115652f,-0.0799138f,-0.168798f}, -{-0.423469f,-0.217395f,0.0263722f},{-0.427533f,-0.216951f,0.0392463f},{-0.425494f,-0.216983f,0.0779011f}, -{-0.424337f,-0.21688f,0.0971866f},{-0.425662f,-0.216598f,0.116234f},{-0.423797f,-0.216681f,0.12851f}, -{-0.423861f,0.225183f,0.0220893f},{0.100174f,-0.066583f,-0.146233f},{0.117781f,0.100608f,-0.0368284f}, -{-0.0833832f,-0.235883f,-0.199106f},{-0.0978393f,-0.231247f,-0.170998f},{-0.104392f,-0.227542f,-0.162303f}, -{-0.107845f,-0.228681f,-0.150445f},{-0.111389f,-0.228263f,-0.134343f},{-0.116778f,-0.227954f,-0.12181f}, -{0.249667f,-0.128967f,-0.145706f},{0.232472f,0.0238128f,-0.0833221f},{-0.392711f,-0.207087f,-0.0382303f}, -{-0.402485f,-0.210778f,-0.0256197f},{-0.409521f,-0.204836f,-0.0176264f},{-0.417019f,-0.205781f,-0.000688062f}, -{-0.421044f,-0.208868f,0.0135237f},{-0.426826f,-0.203151f,0.0212534f},{-0.433874f,-0.203408f,0.0392656f}, -{-0.436709f,-0.205222f,0.0569371f},{-0.434806f,-0.20726f,0.077856f},{-0.434021f,-0.208842f,0.115675f}, -{-0.426948f,-0.203672f,0.15186f},{-0.419392f,-0.211967f,0.157686f},{-0.347349f,0.218392f,-0.0859072f}, -{-0.14086f,0.412385f,-0.0475226f},{0.154384f,0.0215427f,-0.112858f},{-0.243057f,0.0221793f,-0.0944857f}, -{-0.0784766f,-0.222494f,-0.212701f},{-0.0877432f,-0.221594f,-0.20236f},{-0.0960066f,-0.220906f,-0.18609f}, -{-0.184673f,0.00709305f,-0.102762f},{0.00357221f,0.0290087f,-0.101013f},{-0.358982f,-0.204495f,-0.0763319f}, -{-0.374499f,-0.206366f,-0.0561397f},{-0.107196f,-0.404501f,-0.146388f},{-0.421803f,-0.200129f,0.00706088f}, -{-0.441404f,-0.198405f,0.0617279f},{-0.441841f,-0.198167f,0.0778239f},{-0.442954f,-0.198026f,0.097103f}, -{-0.442626f,-0.197916f,0.116363f},{-0.424305f,-0.199215f,0.171094f},{-0.42233f,-0.199756f,0.181563f}, -{-0.033552f,-0.110974f,-0.208964f},{-0.32481f,0.410244f,-0.363647f},{-0.0341886f,-0.0863831f,-0.153114f}, -{0.215347f,0.00317677f,-0.0975853f},{-0.10544f,-0.206263f,-0.166702f},{-0.111595f,-0.210225f,-0.150535f}, -{-0.114463f,-0.209916f,-0.134433f},{-0.116205f,-0.214244f,-0.121559f},{-0.0201055f,0.0293688f,-0.101296f}, -{0.205476f,0.0341211f,-0.0879907f},{0.173072f,0.0181537f,-0.109257f},{-0.361824f,-0.192013f,-0.0803061f}, -{-0.369959f,-0.185345f,-0.0759332f},{-0.376383f,-0.192547f,-0.0622745f},{0.0600784f,0.0621138f,-0.0787177f}, -{-0.397058f,-0.190901f,-0.0428603f},{-0.40473f,-0.187197f,-0.0339732f},{-0.413649f,-0.188984f,-0.0203209f}, -{-0.420009f,-0.191859f,-0.00282304f},{-0.425642f,-0.184708f,0.00212857f},{-0.429642f,-0.186438f,0.0198643f}, -{-0.437494f,-0.185814f,0.0391563f},{-0.44478f,-0.185177f,0.0584419f},{-0.447198f,-0.184791f,0.0777853f}, -{-0.448767f,-0.184624f,0.0970323f},{-0.447564f,-0.184586f,0.116279f},{-0.443822f,-0.183865f,0.134227f}, -{-0.439828f,-0.189216f,0.141912f},{-0.434227f,-0.185383f,0.154863f},{-0.432086f,-0.185505f,0.170991f}, -{-0.372679f,0.204006f,-0.0321276f},{-0.380731f,0.208058f,-0.0269509f},{-0.385669f,0.292717f,-0.108125f}, -{-0.384955f,0.275155f,-0.0501334f},{-0.457513f,0.0372528f,0.0313302f},{-0.0781293f,-0.209074f,-0.214385f}, -{-0.0885985f,-0.202334f,-0.204315f},{-0.0979165f,-0.200945f,-0.190463f},{-0.100585f,-0.203717f,-0.181891f}, -{-0.290669f,0.41126f,-0.387428f},{0.246542f,-0.0402495f,-0.109823f},{-0.388872f,-0.187074f,-0.0538118f}, -{-0.421507f,-0.183107f,-0.0125269f},{-0.376924f,0.270249f,-0.111244f},{0.135272f,0.0239992f,-0.115713f}, -{-0.221777f,0.0372528f,-0.0892061f},{0.458972f,-0.281965f,0.0151571f},{-0.0687985f,-0.207048f,-0.221607f}, -{-0.11216f,-0.187557f,-0.147654f},{-0.125125f,-0.188746f,-0.129906f},{0.136224f,-0.0950966f,-0.185917f}, -{-0.159561f,0.0490338f,-0.0826726f},{-0.377965f,-0.167352f,-0.077065f},{-0.39329f,-0.17031f,-0.0587762f}, -{-0.408022f,-0.171068f,-0.038436f},{-0.418806f,-0.170213f,-0.0255233f},{-0.427141f,-0.169647f,-0.0158773f}, -{-0.435854f,-0.168728f,0.000495179f},{-0.437925f,-0.172734f,0.0198257f},{-0.447076f,-0.167744f,0.0390856f}, -{-0.448728f,-0.167544f,0.0583583f},{-0.449712f,-0.167326f,0.0776695f},{-0.450297f,-0.167146f,0.0969229f}, -{-0.449378f,-0.167216f,0.116215f},{-0.447352f,-0.167255f,0.135494f},{-0.444323f,-0.165081f,0.153101f}, -{-0.437745f,-0.172175f,0.158059f},{-0.437012f,-0.167917f,0.170966f},{-0.128308f,0.265824f,-0.0182373f}, -{-0.376872f,0.270306f,-0.0919713f},{-0.345265f,0.336864f,-0.291669f},{-0.0700781f,-0.189023f,-0.223221f}, -{-0.080573f,-0.185692f,-0.212334f},{-0.0877818f,-0.185081f,-0.202456f},{-0.096013f,-0.184386f,-0.189428f}, -{-0.10144f,-0.183943f,-0.179705f},{-0.104682f,-0.183647f,-0.166747f},{-0.146892f,0.0471175f,-0.0806276f}, -{-0.107704f,0.0503778f,-0.0712196f},{-0.0904633f,0.050918f,-0.071869f},{-0.445179f,-0.163641f,0.0197807f}, -{-0.441044f,0.20481f,0.0434584f},{-0.463165f,0.257715f,0.0824154f},{-0.436195f,0.124215f,-0.00681005f}, -{-0.463442f,0.258603f,0.168477f},{-0.0122279f,0.490132f,-0.127771f},{0.192447f,-0.0923057f,-0.150741f}, -{-0.3861f,-0.159468f,-0.0706472f},{-0.397045f,-0.146979f,-0.0587698f},{-0.403296f,-0.153828f,-0.0512009f}, -{-0.41109f,-0.150014f,-0.0392527f},{-0.42851f,-0.152034f,-0.0189962f},{-0.438639f,-0.152702f,-0.00389053f}, -{-0.442375f,-0.149513f,0.00473942f},{-0.449436f,-0.150426f,0.0197421f},{-0.456413f,-0.15069f,0.0377351f}, -{-0.456812f,-0.153821f,0.0583325f},{-0.458169f,-0.153641f,0.0776052f},{-0.456612f,-0.153764f,0.0968458f}, -{-0.455628f,-0.153757f,0.116144f},{-0.455841f,-0.151474f,0.137005f},{-0.4526f,-0.149384f,0.154612f}, -{-0.457108f,0.24904f,0.0697727f},{-0.435938f,0.174599f,0.0144369f},{-0.0689142f,-0.16804f,-0.222166f}, -{-0.0786438f,-0.167577f,-0.212385f},{-0.0840327f,-0.167126f,-0.202559f},{-0.0928748f,-0.166419f,-0.186303f}, -{-0.102688f,-0.166516f,-0.165609f},{-0.111601f,-0.165641f,-0.149005f},{-0.387663f,-0.146169f,-0.0707115f}, -{-0.460053f,-0.145236f,0.0454391f},{-0.460496f,-0.145031f,0.0583261f},{-0.462857f,-0.144896f,0.0775538f}, -{-0.462085f,-0.144812f,0.0968072f},{-0.461165f,-0.144761f,0.116106f},{-0.460696f,-0.144613f,0.132137f}, -{0.20037f,0.0470274f,-0.0826468f},{-0.377708f,0.301283f,-0.134021f},{-0.0979422f,-0.161397f,-0.173499f}, -{-0.119028f,-0.15995f,-0.142497f},{0.0999871f,0.079528f,-0.0589499f},{-0.413495f,-0.129668f,-0.0401402f}, -{-0.432227f,-0.131147f,-0.0199608f},{-0.438915f,-0.133674f,-0.00619914f},{-0.441867f,-0.13098f,0.00160125f}, -{-0.448889f,-0.133185f,0.0197421f},{-0.45678f,-0.132594f,0.0357674f},{-0.46004f,-0.132388f,0.0454262f}, -{-0.462721f,-0.132111f,0.0582489f},{-0.465982f,-0.131777f,0.0775023f},{-0.466663f,-0.131616f,0.0967493f}, -{-0.466638f,-0.131533f,0.116028f},{-0.464985f,-0.131501f,0.135288f},{-0.457262f,-0.136041f,0.157654f}, -{-0.299678f,-0.473072f,-0.0419022f},{-0.066014f,-0.152439f,-0.219549f},{-0.0736086f,-0.147384f,-0.208488f}, -{-0.0819427f,-0.153545f,-0.199479f},{-0.0862963f,-0.14867f,-0.186367f},{-0.0939488f,-0.148085f,-0.166966f}, -{-0.109556f,-0.148574f,-0.150355f},{-0.221694f,-0.48738f,-0.0727243f},{-0.388229f,-0.128594f,-0.0705443f}, -{-0.396473f,-0.128941f,-0.0591492f},{-0.423617f,-0.126382f,-0.0322497f},{-0.282952f,0.025549f,-0.0794444f}, -{-0.439114f,0.213549f,0.0500113f},{0.213347f,0.0806791f,-0.0397029f},{-0.402633f,-0.123334f,-0.0517282f}, -{0.279788f,-0.0226423f,-0.084949f},{0.00770069f,-0.0975017f,-0.202469f},{-0.463043f,0.028475f,0.0439793f}, -{-0.438684f,0.159937f,0.012154f},{-0.0821935f,-0.135037f,-0.183261f},{0.112443f,0.0452011f,-0.0996109f}, -{-0.440008f,0.0363911f,0.0118517f},{0.0788109f,-0.0764541f,-0.171068f},{-0.0513071f,0.0541204f,-0.0704157f}, -{-0.0725218f,0.0608791f,-0.0584676f},{0.138854f,-0.0875727f,-0.173519f},{0.262078f,-0.0821195f,-0.124337f}, -{-0.0899425f,0.0356066f,-0.0881129f},{0.26658f,-0.0933218f,-0.125514f},{0.276104f,-0.0914312f,-0.121025f}, -{0.401579f,-0.301399f,-0.00446929f},{0.289235f,-0.0925565f,-0.113353f},{-0.0525868f,0.045259f,-0.0791357f}, -{0.0232886f,-0.079618f,-0.172843f},{0.0404006f,-0.114704f,-0.220147f},{-0.127575f,0.0222308f,-0.0996624f}, -{-0.091055f,-0.415993f,-0.167731f},{0.461493f,-0.258995f,0.0334973f},{0.0801485f,0.0388862f,-0.0953474f}, -{0.0859811f,0.0112665f,-0.121064f},{0.211128f,-0.0580817f,-0.13316f},{-0.0155011f,0.0200122f,-0.109476f}, -{0.13569f,0.0984984f,-0.0425517f},{0.209334f,0.0733224f,-0.0531558f},{0.37383f,-0.297662f,-0.0146426f}, -{0.174473f,-0.0865953f,-0.153281f},{-0.0290248f,-0.00725378f,-0.126877f},{0.264291f,0.0159287f,-0.0684544f}, -{0.30427f,-0.0999389f,-0.105508f},{0.312405f,-0.103913f,-0.101238f},{0.325838f,-0.111083f,-0.093624f}, -{-0.0629594f,-0.448423f,-0.178367f},{0.153825f,0.00549181f,-0.117688f},{0.0600527f,0.0406289f,-0.0930709f}, -{0.193785f,-0.0587441f,-0.134587f},{0.232806f,0.00174273f,-0.0917398f},{0.176403f,0.0629755f,-0.0804797f}, -{0.440729f,-0.274448f,0.0136845f},{0.185399f,0.0156072f,-0.10664f},{0.32742f,-0.0140767f,-0.0403781f}, -{0.119909f,-0.0675284f,-0.140188f},{-0.120462f,-0.417369f,-0.160014f},{-0.184846f,0.0625961f,-0.0775216f}, -{0.334127f,-0.108717f,-0.0832449f},{0.339111f,-0.118543f,-0.0857078f},{0.348841f,-0.12961f,-0.0804411f}, -{-0.278059f,0.0395357f,-0.0715282f},{0.212832f,0.054352f,-0.0712839f},{0.303788f,0.0003923f,-0.0545191f}, -{-0.0927848f,-0.471593f,-0.185357f},{0.438767f,-0.321842f,-0.0139288f},{0.0999292f,0.0890518f,-0.0499019f}, -{-0.279441f,-0.470139f,-0.048963f},{0.0730555f,0.0125012f,-0.122363f},{0.00391304f,0.0395164f,-0.0928394f}, -{-0.260947f,0.0601653f,-0.0610141f},{-0.205894f,-0.449568f,-0.0642744f},{0.19574f,0.0603518f,-0.0749622f}, -{0.119163f,-0.094087f,-0.189229f},{-0.0131282f,-0.0585062f,-0.143738f},{0.250722f,-0.165963f,-0.157017f}, -{-0.260921f,0.0503457f,-0.0701842f},{0.268876f,-0.184875f,-0.148606f},{-0.105344f,0.0338253f,-0.0893669f}, -{0.455017f,-0.300942f,0.00419281f},{0.156976f,-0.110363f,-0.188618f},{-0.074033f,0.0204431f,-0.097939f}, -{-0.10908f,-0.418257f,-0.165628f},{0.273441f,-0.298261f,-0.137044f},{-0.109234f,-0.428674f,-0.175023f}, -{0.406022f,-0.243195f,0.03742f},{0.00558501f,-0.131558f,-0.23043f},{0.025385f,-0.152947f,-0.238134f}, -{0.41743f,-0.248706f,0.0421723f},{0.0629336f,0.012572f,-0.12242f},{0.0616732f,0.0293945f,-0.101669f}, -{0.0579434f,0.0198579f,-0.113565f},{0.269679f,0.00122184f,-0.0735732f},{0.0437703f,-0.146934f,-0.230874f}, -{-0.242703f,0.0852513f,-0.042333f},{-0.186017f,-0.00108035f,-0.107206f},{-0.0730748f,0.0450918f,-0.0788142f}, -{0.264979f,-0.303926f,-0.144748f},{-0.0814283f,0.433549f,-0.0899842f},{0.420144f,-0.274981f,0.0141282f}, -{0.00217032f,-0.46875f,-0.16359f},{0.0806758f,-0.0558438f,-0.1386f},{0.230292f,-0.165487f,-0.167686f}, -{-0.0914344f,-0.485078f,-0.183917f},{0.079737f,0.0510144f,-0.0862352f},{0.059519f,0.0513617f,-0.086531f}, -{-0.0636282f,0.435066f,-0.105038f},{-0.0721102f,0.311488f,-0.0673805f},{0.0599562f,-0.0869232f,-0.19056f}, -{-0.0704897f,-0.490711f,-0.165313f},{0.1921f,0.0920035f,-0.034687f},{0.17421f,-0.0966528f,-0.167056f}, -{0.321433f,0.0167261f,-0.0155172f},{0.0605029f,-0.0743577f,-0.167403f},{0.0802386f,0.0808591f,-0.0602617f}, -{0.0422526f,0.0701264f,-0.0677534f},{-0.127317f,0.0551558f,-0.0697598f},{0.156667f,-0.0688402f,-0.134427f}, -{0.287216f,-0.00226358f,-0.0682293f},{0.137787f,0.0655992f,-0.0798302f},{0.0426642f,-0.0758882f,-0.168644f}, -{0.0249027f,0.0505129f,-0.0853477f},{-0.0142793f,0.0467831f,-0.0810392f},{-0.112752f,0.40072f,-0.0819266f}, -{-0.0764188f,0.420405f,-0.10026f},{-0.148532f,0.021099f,-0.0983505f},{-0.0322337f,0.0449889f,-0.0789685f}, -{0.0228963f,0.47257f,-0.16847f},{0.344397f,-0.112003f,-0.0732838f},{0.334835f,-0.0549564f,-0.0632262f}, -{0.00402235f,-0.0809749f,-0.165081f},{0.0234044f,-0.0553422f,-0.143532f},{0.193618f,0.0389377f,-0.0904022f}, -{-0.0916209f,-0.427208f,-0.176412f},{-0.292843f,0.400572f,-0.387647f},{-0.277351f,-0.432546f,-0.0328735f}, -{0.0236101f,-0.130761f,-0.229671f},{0.0407672f,0.0189962f,-0.109791f},{0.232697f,-0.13069f,-0.155558f}, -{-0.0537443f,0.0202373f,-0.097791f},{-0.255513f,-0.419279f,-0.0314331f},{0.17466f,0.0740234f,-0.0699334f}, -{-0.270593f,0.296801f,0.0237677f},{-0.176981f,0.35514f,-0.0475869f},{-0.0332369f,0.0160252f,-0.107681f}, -{0.341156f,-0.0549757f,-0.0554966f},{0.171097f,0.0880808f,-0.0495739f},{0.322231f,-0.020308f,-0.0531044f}, -{-0.0737501f,-0.486299f,-0.181094f},{-0.0327803f,-0.0931031f,-0.173191f},{0.258316f,0.0655092f,-0.025459f}, -{0.244568f,-0.00190989f,-0.0878557f},{0.0420533f,-0.0569692f,-0.145031f},{-0.033552f,-0.0750973f,-0.145648f}, -{-0.299814f,0.281284f,0.0215106f},{-0.279737f,0.283412f,0.0196843f},{-0.209373f,0.327385f,-0.0267129f}, -{0.196408f,0.0842545f,-0.0456062f},{-0.0161635f,-0.0777531f,-0.149191f},{-0.2435f,-0.47057f,-0.0622038f}, -{0.00624094f,-0.0729365f,-0.149339f},{0.0225748f,-0.0716054f,-0.151667f},{0.151915f,-0.0584418f,-0.128607f}, -{0.302399f,-0.0201666f,-0.0695926f},{0.235443f,-0.146613f,-0.15968f},{0.202723f,0.00639853f,-0.103077f}, -{-0.0153082f,-0.470872f,-0.168438f},{0.022652f,0.0167455f,-0.107334f},{-0.219732f,0.0707759f,-0.0663708f}, -{0.0407607f,-0.0696055f,-0.151082f},{0.0616411f,-0.0680557f,-0.152548f},{-0.279319f,-0.487778f,-0.0510337f}, -{-0.293659f,-0.489888f,-0.0501784f},{0.00330855f,0.0187904f,-0.109469f},{0.0112504f,0.0501527f,-0.0848847f}, -{-0.30555f,0.276789f,0.00826342f},{-0.296746f,0.274049f,0.0108936f},{-0.283763f,0.27257f,0.0122118f}, -{-0.280612f,0.265593f,0.00686153f},{0.26386f,-0.137719f,-0.142786f},{0.269647f,-0.0361596f,-0.0978554f}, -{0.260156f,-0.0403396f,-0.104575f},{-0.203038f,0.0361982f,-0.0896177f},{0.417604f,-0.298164f,-0.00448858f}, -{-0.205939f,-0.073959f,-0.131282f},{-0.0925919f,-0.436269f,-0.180843f},{-0.0775634f,-0.436153f,-0.17975f}, -{0.116546f,0.024758f,-0.115083f},{0.20718f,-0.00464937f,-0.10401f},{0.285299f,-0.0771229f,-0.109115f}, -{-0.0350568f,0.0268866f,-0.0985177f},{-0.0529211f,-0.469638f,-0.17683f},{0.115234f,-0.073078f,-0.15377f}, -{0.192241f,-0.0755088f,-0.13833f},{0.0680782f,-0.0558631f,-0.140079f},{0.134211f,-0.0758111f,-0.150696f}, -{-0.129465f,-0.447863f,-0.175904f},{-0.110122f,-0.437568f,-0.179467f},{-0.146391f,-0.399132f,-0.0756053f}, -{-0.090174f,0.0221536f,-0.0997267f},{0.296984f,-0.0316839f,-0.0818816f},{0.0440661f,0.0283978f,-0.100518f}, -{0.153336f,-0.0782933f,-0.147989f},{0.173522f,0.02861f,-0.10127f},{-0.245719f,0.0662872f,-0.0612649f}, -{-0.279467f,0.248005f,-0.0012411f},{-0.260773f,0.24596f,-0.000218624f},{0.321485f,-0.129147f,-0.106768f}, -{0.023713f,-0.114099f,-0.220867f},{0.24413f,-0.0576059f,-0.124697f},{-0.0915759f,-0.406855f,-0.157082f}, -{-0.296058f,-0.270261f,0.234983f},{0.00615734f,0.0945629f,-0.0194463f},{0.168081f,-0.0794766f,-0.144581f}, -{0.252497f,0.00464294f,-0.0807627f},{0.24822f,0.0340825f,-0.0687502f},{0.313916f,0.00494521f,-0.0416064f}, -{0.0403749f,-0.469702f,-0.154934f},{-0.293164f,0.254763f,-0.00702226f},{0.0280087f,-0.0414585f,-0.142111f}, -{0.0618211f,0.0931031f,-0.0507122f},{-0.17102f,0.00729881f,-0.103051f},{-0.25896f,0.0220122f,-0.0907302f}, -{-0.0683676f,-0.430282f,-0.176702f},{0.143471f,0.0490145f,-0.0974759f},{0.154763f,0.0894312f,-0.0509244f}, -{-0.245468f,-0.429954f,-0.0434134f},{-0.0755121f,-0.0124948f,-0.121404f},{-0.261712f,-0.45576f,-0.0506865f}, -{0.188447f,0.0503714f,-0.0862159f},{-0.0160284f,0.0915083f,-0.0186553f},{-0.288245f,0.242037f,-0.0126362f}, -{0.439166f,-0.300878f,-0.00482298f},{-0.0333334f,0.0365197f,-0.0880615f},{0.317967f,-0.0967814f,-0.0894826f}, -{0.0200926f,0.0669368f,-0.0639915f},{-0.281396f,-0.450725f,-0.0420436f},{0.161168f,-0.0859651f,-0.159204f}, -{-0.013334f,-0.0866789f,-0.168876f},{-0.260336f,-0.435716f,-0.0411498f},{-0.299878f,-0.48938f,-0.0368348f}, -{0.292592f,-0.109083f,-0.117334f},{-0.275538f,0.230571f,-0.006212f},{0.331504f,-0.140124f,-0.101926f}, -{0.305942f,-0.112993f,-0.110987f},{-0.242639f,-0.437381f,-0.049381f},{0.317446f,-0.113977f,-0.103482f}, -{-0.0706248f,-0.414482f,-0.167506f},{0.0406385f,0.0091251f,-0.118633f},{0.252342f,-0.320954f,-0.156136f}, -{0.331214f,-0.127102f,-0.098209f},{-0.286836f,0.4f,-0.387441f},{-0.00711554f,-0.0828655f,-0.163146f}, -{0.323568f,-0.148651f,-0.108871f},{-0.205218f,0.0724285f,-0.0683129f},{-0.223134f,-0.454969f,-0.0629433f}, -{0.38266f,-0.335706f,-0.0232275f},{-0.27431f,0.21917f,-0.00699654f},{-0.260381f,0.211685f,-0.00508663f}, -{0.390312f,-0.260422f,0.0117103f},{0.337832f,-0.0386418f,-0.05059f},{0.117459f,0.0530465f,-0.0919584f}, -{0.0412752f,-0.0419022f,-0.14186f},{-0.227938f,0.0485451f,-0.0817144f},{0.0407221f,-0.1673f,-0.236796f}, -{-0.194158f,-0.250577f,0.273869f},{-0.184525f,0.0924215f,-0.0508601f},{0.0057715f,-0.0839266f,-0.174837f}, -{0.0450371f,0.0886145f,-0.0488473f},{0.0313655f,0.0688788f,-0.0662808f},{0.160294f,0.06794f,-0.079335f}, -{-0.299466f,-0.441446f,-0.0306742f},{0.11807f,-0.0567377f,-0.135372f},{0.176113f,-0.472351f,-0.135089f}, -{-0.126842f,0.0658114f,-0.0616507f},{0.1534f,-0.100363f,-0.182406f},{0.245191f,-0.114536f,-0.141526f}, -{-0.202826f,-0.490389f,-0.073779f},{-0.267294f,0.205112f,-0.012932f},{-0.240606f,0.205492f,0.00168485f}, -{0.327459f,-0.0332272f,-0.058564f},{0.235661f,0.0680685f,-0.041034f},{0.0310183f,-0.140105f,-0.232719f}, -{0.046066f,-0.16876f,-0.235111f},{-0.205585f,0.0910453f,-0.0491688f},{0.0606829f,-0.108337f,-0.213601f}, -{0.0407607f,-0.107411f,-0.2147f},{0.0204913f,-0.107128f,-0.215125f},{0.00281983f,-0.105373f,-0.210572f}, -{0.00645958f,-0.114749f,-0.220302f},{-0.147851f,-0.488614f,-0.17031f},{-0.123697f,-0.451297f,-0.179313f}, -{0.204485f,0.089592f,-0.0321211f},{0.11699f,0.0893798f,-0.0503521f},{0.134745f,0.0903315f,-0.0517154f}, -{0.440002f,-0.250082f,0.0402109f},{0.459384f,-0.251735f,0.0420758f},{-0.259615f,0.192746f,-0.0167261f}, -{-0.241024f,0.190868f,-0.00066877f},{-0.0119771f,-0.0980033f,-0.198862f},{0.0374296f,-0.153371f,-0.236552f}, -{0.276483f,-0.150227f,-0.137668f},{-0.111543f,-0.408559f,-0.15114f},{0.289222f,-0.204649f,-0.134684f}, -{0.271126f,-0.203061f,-0.148297f},{0.419784f,-0.320067f,-0.0175557f},{0.466393f,-0.311077f,0.0125913f}, -{0.103788f,-0.127289f,-0.213157f},{0.0621813f,-0.0796309f,-0.178985f},{0.213032f,-0.148741f,-0.175615f}, -{-0.276123f,0.19364f,-0.0243014f},{-0.237057f,0.175345f,-0.000983873f},{-0.051217f,-0.0594772f,-0.143346f}, -{-0.0341115f,-0.0593293f,-0.144195f},{-0.195013f,0.140182f,0.0354008f},{-0.0339764f,-0.483142f,-0.170406f}, -{0.267313f,-0.151204f,-0.143571f},{0.382132f,-0.321662f,-0.0154464f},{0.40372f,-0.320575f,-0.0174657f}, -{0.157876f,-0.12471f,-0.195923f},{-0.0725218f,-0.447722f,-0.179679f},{-0.0453651f,-0.133153f,-0.221298f}, -{-0.0336806f,-0.13051f,-0.223787f},{0.205791f,-0.147436f,-0.179486f},{-0.243204f,-0.489933f,-0.0578631f}, -{0.235147f,-0.0296067f,-0.103167f},{-0.247056f,0.172014f,-0.0121411f},{-0.221713f,0.154066f,-2.57039e-05f}, -{-0.201141f,0.135912f,0.0175429f},{0.288097f,-0.18638f,-0.132729f},{-0.241725f,-0.454281f,-0.0580045f}, -{-0.223064f,-0.473014f,-0.0674833f},{-0.0658854f,-0.465335f,-0.178657f},{0.252683f,-0.304164f,-0.154522f}, -{0.100559f,-0.0970258f,-0.199235f},{0.172474f,-0.114562f,-0.183679f},{0.0369409f,0.0861901f,-0.0460435f}, -{0.362075f,-0.339404f,-0.0242629f},{-0.0222147f,-0.0861901f,-0.159821f},{-0.233565f,0.161043f,-0.00777465f}, -{-0.213115f,0.148137f,0.00656572f},{0.0732227f,-0.117109f,-0.217337f},{0.18628f,-0.115874f,-0.178978f}, -{-0.123922f,-0.486454f,-0.18665f},{0.398839f,-0.335353f,-0.0244236f},{0.12106f,-0.0842031f,-0.173937f}, -{-0.296405f,0.0342433f,-0.0656313f},{-0.256805f,0.155757f,-0.0229317f},{-0.240356f,0.151571f,-0.0168869f}, -{-0.228626f,0.151365f,-0.0101476f},{-0.215733f,0.136684f,-0.00267514f},{-0.207392f,0.130748f,0.00387771f}, -{0.278091f,-0.176985f,-0.140973f},{0.00451752f,0.111064f,0.0211183f},{0.0224784f,0.115508f,0.018546f}, -{-0.279962f,-0.294164f,0.208308f},{0.317472f,0.0347706f,0.00812194f},{-0.226047f,0.135121f,-0.0132664f}, -{-0.202601f,0.121314f,7.07559e-05f},{-0.184576f,0.116607f,-0.000681631f},{-0.0169223f,0.103913f,0.0169963f}, -{-0.0125623f,0.107128f,0.0231697f},{0.040735f,0.11462f,0.00070096f},{0.060207f,0.119263f,0.00183276f}, -{0.0800906f,0.121507f,0.000392289f},{0.0970676f,0.121469f,-0.00239862f},{0.116057f,0.122093f,-0.00194847f}, -{0.136745f,0.122343f,-0.00261726f},{0.15668f,0.121314f,-0.00171054f},{0.175811f,0.118646f,0.000971048f}, -{0.194151f,0.112221f,-0.000225055f},{0.134372f,0.00556251f,-0.120093f},{-0.115472f,0.0872769f,-0.0327256f}, -{-0.114977f,0.0688852f,-0.0518761f},{-0.0382078f,-0.103366f,-0.193608f},{0.271293f,-0.0128099f,-0.0826275f}, -{-0.252992f,0.138716f,-0.0265072f},{-0.242188f,0.134099f,-0.0231053f},{-0.215167f,0.124703f,-0.00695796f}, -{0.00827945f,0.108106f,0.00857852f},{0.022832f,0.109945f,0.00102249f},{0.212755f,0.107791f,0.00616702f}, -{0.230613f,0.0964599f,0.00186491f},{0.22543f,-0.132883f,-0.161088f},{0.299415f,0.0143018f,-0.0474454f}, -{0.356455f,-0.0437028f,-0.0169641f},{-0.0465548f,0.0114209f,-0.107849f},{0.0974341f,0.052217f,-0.0876628f}, -{-0.221945f,0.114665f,-0.0193305f},{-0.202511f,0.112221f,-0.0181023f},{-0.186158f,0.112742f,-0.0161152f}, -{-0.239404f,-0.484917f,-0.0675219f},{-0.029102f,-0.117887f,-0.217536f},{0.406305f,-0.255284f,0.0327835f}, -{0.419893f,-0.255754f,0.033208f},{-0.0154754f,0.0985306f,-0.000263639f},{0.00136006f,0.101585f,-0.00249508f}, -{0.043005f,0.109611f,-0.0146876f},{0.0620076f,0.11327f,-0.016456f},{0.0792418f,0.113746f,-0.0197807f}, -{0.0978779f,0.113392f,-0.0183852f},{0.117909f,0.112839f,-0.0178644f},{0.137471f,0.114331f,-0.018501f}, -{0.155722f,0.113064f,-0.0185975f},{0.174686f,0.110357f,-0.0158837f},{0.21527f,0.101457f,-0.0032539f}, -{0.462657f,-0.297823f,0.0121604f},{0.440478f,-0.255458f,0.0329186f},{0.240201f,-0.104659f,-0.139391f}, -{-0.169046f,0.108524f,-0.0214012f},{-0.149214f,0.106916f,-0.0149063f},{0.0234172f,0.101907f,-0.01753f}, -{0.184396f,0.106157f,-0.0178579f},{0.193778f,0.101367f,-0.0190926f},{-0.133767f,-0.465335f,-0.178547f}, -{0.186409f,-0.0962027f,-0.161062f},{-0.261165f,-0.490434f,-0.0584804f},{-0.070599f,-0.397383f,-0.151481f}, -{-0.247622f,0.108164f,-0.0284299f},{-0.228555f,0.105804f,-0.0256197f},{-0.186595f,0.107688f,-0.0281663f}, -{-0.160519f,0.105386f,-0.025922f},{-0.147664f,0.103579f,-0.0243207f},{-0.0743545f,0.0868204f,-0.00658498f}, -{-0.0456031f,0.0891804f,-0.00969099f},{0.0395839f,0.103418f,-0.0260313f},{0.0621041f,0.107341f,-0.0307257f}, -{0.0794476f,0.107984f,-0.0316067f},{0.0958008f,0.106614f,-0.030282f},{0.126276f,0.106267f,-0.0302498f}, -{0.136899f,0.107109f,-0.0312851f},{0.150571f,0.106961f,-0.0313816f},{0.178949f,0.10244f,-0.0266872f}, -{0.150924f,-0.486299f,-0.125578f},{0.134089f,-0.486807f,-0.128459f},{0.118392f,-0.486319f,-0.133185f}, -{0.0979808f,-0.483766f,-0.142265f},{0.0787209f,-0.484049f,-0.14442f},{0.059982f,-0.483933f,-0.146986f}, -{0.0217517f,-0.486319f,-0.150915f},{0.00719267f,-0.489219f,-0.149513f},{-0.0549082f,-0.482345f,-0.175068f}, -{0.199855f,-0.476094f,-0.120543f},{0.157419f,-0.473052f,-0.139314f},{0.136558f,-0.470429f,-0.144278f}, -{0.117253f,-0.470191f,-0.1475f},{0.0979615f,-0.470023f,-0.149558f},{0.0786759f,-0.469998f,-0.151043f}, -{-0.22442f,-0.0713611f,-0.131063f},{0.0219896f,-0.470287f,-0.158329f},{-0.0307547f,-0.468165f,-0.172586f}, -{-0.324398f,-0.270171f,0.217665f},{0.0275329f,0.382361f,-0.144728f},{-0.0119193f,0.384026f,-0.123764f}, -{-0.243462f,-0.0706344f,-0.1266f},{-0.0151732f,0.39838f,-0.130722f},{-0.0697823f,0.398154f,-0.107617f}, -{-0.260908f,-0.0702357f,-0.11772f},{-0.276946f,-0.0708402f,-0.111238f},{-0.204202f,0.277444f,-0.0249509f}, -{-0.316591f,-0.489946f,-0.0348027f},{-0.321048f,-0.491084f,-0.0186618f},{0.152622f,-0.465959f,-0.142356f}, -{0.0141957f,-0.464461f,-0.161101f},{-0.29283f,-0.0713353f,-0.105617f},{-0.302418f,-0.0715925f,-0.101791f}, -{-0.315376f,-0.0719334f,-0.0965306f},{-0.334584f,-0.0724543f,-0.0895598f},{-0.112591f,0.302691f,-0.0615093f}, -{-0.349574f,-0.0743063f,-0.0856757f},{-0.358185f,-0.0715411f,-0.0805247f},{0.00820872f,0.399987f,-0.144671f}, -{-0.178859f,0.320749f,-0.0490531f},{-0.104971f,0.307752f,-0.0656828f},{-0.0410694f,0.441092f,-0.121565f}, -{-0.319614f,-0.474319f,-0.0342883f},{-0.333967f,-0.471162f,-0.0213819f},{-0.348937f,-0.484627f,0.00558826f}, -{0.265435f,-0.471522f,0.00476514f},{-0.0139835f,0.35932f,-0.107906f},{-0.0550175f,0.357673f,-0.0990772f}, -{0.194409f,-0.452731f,-0.134549f},{0.17511f,-0.452397f,-0.138684f},{0.162223f,-0.452275f,-0.140079f}, -{0.152564f,-0.452101f,-0.141938f},{0.136455f,-0.451857f,-0.144909f},{0.117151f,-0.451567f,-0.1486f}, -{0.0978714f,-0.451336f,-0.151744f},{0.0785859f,-0.45113f,-0.154561f},{0.0592939f,-0.451008f,-0.156516f}, -{0.0399955f,-0.450892f,-0.158567f},{0.0270891f,-0.455387f,-0.159744f},{0.0193981f,-0.449664f,-0.162477f}, -{0.00133434f,-0.450146f,-0.167326f},{-0.0147359f,-0.449799f,-0.171467f},{-0.0307868f,-0.449561f,-0.174721f}, -{-0.0500788f,-0.44942f,-0.176952f},{-0.242336f,-0.482878f,0.298177f},{-0.319247f,0.126562f,-0.0472203f}, -{-0.431095f,0.0555095f,-0.0061284f},{-0.287203f,-0.468345f,-0.044796f},{-0.38992f,0.13053f,-0.0323076f}, -{-0.349137f,-0.474191f,0.00606413f},{0.324231f,-0.464403f,0.0193563f},{0.323909f,-0.455207f,0.00261087f}, -{0.305087f,-0.4565f,-0.00248222f},{0.28811f,-0.462313f,-0.00134399f},{0.278348f,-0.461175f,-0.00605123f}, -{0.00435675f,0.360104f,-0.113617f},{-0.0341886f,0.364934f,-0.107283f},{-0.318012f,0.189827f,-0.0402752f}, -{-0.274509f,-0.458886f,-0.0477541f},{-0.300212f,-0.455368f,-0.037285f},{-0.316141f,-0.456108f,-0.0309186f}, -{0.297441f,-0.450699f,-0.00994178f},{0.287891f,-0.450957f,-0.0121411f},{-0.399508f,0.13024f,-0.0256133f}, -{0.200846f,-0.438417f,-0.139552f},{0.193019f,-0.432301f,-0.144613f},{0.175001f,-0.433189f,-0.146098f}, -{0.155702f,-0.433298f,-0.145513f},{0.136423f,-0.433311f,-0.146619f},{0.117144f,-0.433201f,-0.148548f}, -{0.0978393f,-0.432976f,-0.151384f},{0.0785666f,-0.432841f,-0.155043f},{0.0592167f,-0.432655f,-0.156818f}, -{0.039944f,-0.432616f,-0.158156f},{0.0271084f,-0.432443f,-0.159699f},{0.0174431f,-0.432198f,-0.162393f}, -{0.00134721f,-0.431812f,-0.166644f},{-0.014768f,-0.43144f,-0.170869f},{-0.0308511f,-0.431214f,-0.173673f}, -{-0.0501495f,-0.431182f,-0.175242f},{-0.0301823f,0.399897f,-0.12462f},{-0.268213f,-0.449658f,-0.046365f}, -{-0.33625f,0.00163985f,-0.0621202f},{0.345535f,-0.441259f,-0.00639849f},{0.34068f,-0.43333f,-0.0138259f}, -{0.323073f,-0.435671f,-0.0155429f},{0.305042f,-0.436352f,-0.0183467f},{0.289531f,-0.43389f,-0.0237677f}, -{0.274978f,-0.440031f,-0.0245394f},{0.207283f,-0.428874f,-0.142137f},{-0.277486f,0.0933989f,-0.0426095f}, -{-0.322244f,-0.0305328f,-0.0829812f},{-0.223347f,-0.434533f,-0.0549436f},{-0.259757f,-0.0169448f,-0.105681f}, -{-0.0928941f,0.310215f,-0.0684094f},{-0.166165f,0.340118f,-0.0569049f},{-0.188499f,0.320652f,-0.0430661f}, -{0.381329f,-0.429542f,0.00839203f},{0.355034f,-0.431163f,-0.0114594f},{0.219919f,-0.41499f,-0.139725f}, -{0.210324f,-0.414636f,-0.144092f},{0.194286f,-0.414263f,-0.149654f},{0.174924f,-0.414083f,-0.152683f}, -{0.155638f,-0.414045f,-0.154741f},{0.136391f,-0.418938f,-0.155474f},{0.117054f,-0.41861f,-0.158818f}, -{0.104251f,-0.418964f,-0.159448f},{0.0964117f,-0.411627f,-0.171467f},{0.0784058f,-0.417138f,-0.171377f}, -{0.0591203f,-0.417003f,-0.173808f},{0.0398283f,-0.417382f,-0.171075f},{0.0205492f,-0.417742f,-0.169647f}, -{0.00121859f,-0.412887f,-0.172342f},{-0.0148709f,-0.413151f,-0.169705f},{-0.0309347f,-0.413254f,-0.168895f}, -{-0.050201f,-0.413459f,-0.16784f},{-0.376846f,0.0291824f,0.336182f},{-0.0667085f,-0.0604096f,-0.141764f}, -{-0.0752677f,-0.0574966f,-0.139732f},{-0.0910935f,-0.0558696f,-0.135616f},{-0.10962f,-0.0549243f,-0.132491f}, -{-0.05377f,0.417266f,-0.115745f},{-0.128083f,-0.054069f,-0.128986f},{0.377477f,-0.422443f,-0.00421206f}, -{0.361381f,-0.416212f,-0.0185396f},{0.343838f,-0.418379f,-0.0225073f},{0.322964f,-0.420443f,-0.025041f}, -{0.310077f,-0.420385f,-0.0269252f},{0.136333f,-0.408276f,-0.167114f},{0.11697f,-0.407775f,-0.172985f}, -{0.0783544f,-0.407157f,-0.180187f},{0.059056f,-0.407177f,-0.181647f},{0.0397575f,-0.40746f,-0.180001f}, -{0.0204591f,-0.407492f,-0.179583f},{-0.372737f,0.188187f,-0.0315874f},{-0.396814f,-0.245272f,0.151635f}, -{0.0300472f,0.402907f,-0.161988f},{-0.185033f,-0.415022f,-0.0589884f},{-0.20417f,-0.415749f,-0.0533873f}, -{-0.219167f,-0.417845f,-0.0483007f},{-0.227629f,-0.415363f,-0.0426931f},{-0.380833f,-0.271683f,0.093952f}, -{-0.0345552f,0.417343f,-0.126768f},{0.380705f,-0.411922f,-0.0144947f},{0.339015f,-0.410701f,-0.028102f}, -{0.3229f,-0.410675f,-0.0285714f},{-0.251223f,-0.065387f,-0.121064f},{0.250477f,-0.395171f,-0.128118f}, -{0.234034f,-0.397981f,-0.133552f},{0.219848f,-0.400971f,-0.140954f},{0.212215f,-0.39481f,-0.148793f}, -{0.194312f,-0.399916f,-0.156046f},{0.174834f,-0.394283f,-0.166696f},{0.155542f,-0.394161f,-0.168837f}, -{0.13742f,-0.394341f,-0.175608f},{0.1169f,-0.392913f,-0.18292f},{0.0975756f,-0.39274f,-0.185345f}, -{0.0782643f,-0.392868f,-0.186013f},{0.0589916f,-0.392888f,-0.186933f},{0.0396804f,-0.392952f,-0.187254f}, -{0.0203884f,-0.392933f,-0.187499f},{0.00426672f,-0.393312f,-0.1851f},{-0.0102666f,-0.391357f,-0.18229f}, -{-0.0150188f,-0.398688f,-0.177518f},{-0.0311083f,-0.394534f,-0.172586f},{-0.0471593f,-0.395293f,-0.164104f}, -{-0.0565931f,-0.395942f,-0.15894f},{-0.153072f,0.320954f,-0.0621009f},{-0.107093f,0.396296f,-0.0868846f}, -{-0.167715f,0.302543f,-0.0497283f},{-0.185103f,0.30145f,-0.0428025f},{-0.108058f,0.345346f,-0.0850583f}, -{0.398556f,-0.396341f,-0.019292f},{0.382325f,-0.399434f,-0.0228417f},{0.364673f,-0.401614f,-0.0251117f}, -{0.359734f,-0.394f,-0.0299476f},{0.342192f,-0.396418f,-0.030507f},{0.322829f,-0.396495f,-0.030925f}, -{0.226369f,-0.391762f,-0.141198f},{0.194119f,-0.389749f,-0.1642f},{0.129658f,-0.388296f,-0.181383f}, -{-0.432073f,0.00232147f,0.247658f},{-0.184923f,-0.396682f,-0.0553037f},{-0.202884f,-0.398251f,-0.0495482f}, -{-0.223424f,-0.398296f,-0.0397093f},{-0.328552f,-0.0035883f,-0.0680685f},{-0.376268f,-0.264924f,0.15395f}, -{-0.355079f,-0.00288092f,0.268069f},{-0.0902833f,0.343147f,-0.0872769f},{0.377496f,-0.39182f,-0.0286229f}, -{0.256419f,-0.379737f,-0.13725f},{0.247854f,-0.376026f,-0.146317f},{0.234047f,-0.378007f,-0.152149f}, -{0.216588f,-0.381267f,-0.154972f},{0.211746f,-0.373904f,-0.162381f},{0.194087f,-0.375679f,-0.167885f}, -{0.176081f,-0.376091f,-0.17456f},{0.155464f,-0.379595f,-0.177113f},{0.13915f,-0.379493f,-0.178136f}, -{0.134513f,-0.372406f,-0.181383f},{0.116797f,-0.374348f,-0.184927f},{0.0975113f,-0.374059f,-0.189145f}, -{0.0783351f,-0.373814f,-0.193357f},{0.0589724f,-0.378483f,-0.194155f},{0.0395968f,-0.37856f,-0.195595f}, -{0.0202984f,-0.378599f,-0.19418f},{0.0030449f,-0.375711f,-0.194624f},{-0.0119836f,-0.374612f,-0.189846f}, -{-0.0269284f,-0.373711f,-0.182599f},{-0.0354426f,-0.377313f,-0.176271f},{-0.0504003f,-0.376695f,-0.166818f}, -{-0.210794f,-0.392843f,-0.0463393f},{-0.341433f,0.0888203f,-0.0460564f},{-0.0527925f,0.340221f,-0.0890389f}, -{0.401373f,-0.380669f,-0.0243658f},{0.380589f,-0.377705f,-0.0326999f},{0.361471f,-0.377859f,-0.0330021f}, -{0.341986f,-0.37784f,-0.0325327f},{0.226298f,-0.371924f,-0.160329f},{0.168332f,-0.370245f,-0.179705f}, -{0.1554f,-0.370136f,-0.181595f},{0.0589402f,-0.368676f,-0.200817f},{0.0395839f,-0.368554f,-0.203395f}, -{0.0202212f,-0.369062f,-0.199428f},{0.00733414f,-0.36921f,-0.1989f},{-0.354249f,0.0934375f,-0.0446931f}, -{-0.398344f,-0.251227f,0.136806f},{-0.108051f,-0.377563f,-0.0586862f},{-0.127337f,-0.377679f,-0.058384f}, -{-0.146635f,-0.377589f,-0.0602488f},{-0.165844f,-0.377673f,-0.060146f},{-0.185091f,-0.378174f,-0.0551172f}, -{-0.201019f,-0.378895f,-0.0484357f},{-0.2108f,-0.379229f,-0.0450918f},{-0.360641f,-0.031954f,-0.0637793f}, -{-0.0316164f,0.341442f,-0.0910903f},{0.431693f,-0.3751f,-0.00904149f},{0.396473f,-0.373351f,-0.0290151f}, -{0.253159f,-0.359448f,-0.152561f},{0.232581f,-0.357468f,-0.165924f},{0.213289f,-0.357159f,-0.17094f}, -{0.195167f,-0.35777f,-0.17483f},{0.174718f,-0.356329f,-0.181904f},{0.155329f,-0.356021f,-0.185917f}, -{0.136025f,-0.356169f,-0.185582f},{0.116745f,-0.355873f,-0.189383f},{0.0987331f,-0.356522f,-0.192862f}, -{0.084605f,-0.359815f,-0.197273f},{0.0768625f,-0.354201f,-0.199788f},{0.0588437f,-0.354825f,-0.203505f}, -{0.0395003f,-0.354612f,-0.206887f},{0.0201762f,-0.354831f,-0.206109f},{0.00404809f,-0.355043f,-0.204772f}, -{-0.0103759f,-0.353095f,-0.200675f},{-0.0152053f,-0.360503f,-0.194618f},{-0.0312884f,-0.356683f,-0.187235f}, -{-0.030118f,-0.479399f,0.347571f},{0.0219446f,0.397531f,-0.15269f},{-0.358667f,0.110723f,-0.0458442f}, -{-0.424781f,0.0588084f,-0.0108742f},{0.433861f,-0.359384f,-0.0142889f},{0.418909f,-0.360207f,-0.0223272f}, -{0.406106f,-0.3644f,-0.026983f},{0.398486f,-0.358799f,-0.0295875f},{0.380615f,-0.359686f,-0.0323269f}, -{0.361361f,-0.359853f,-0.0312401f},{-0.0793962f,0.398103f,-0.102235f},{-0.200968f,0.251413f,-0.0134979f}, -{-0.341304f,0.0793994f,-0.0464294f},{0.245494f,-0.353307f,-0.160914f},{0.187605f,-0.351982f,-0.179139f}, -{0.0910099f,-0.350683f,-0.198199f},{-0.0441111f,-0.352735f,-0.180618f},{-0.233243f,0.281978f,-0.0114851f}, -{-0.0771647f,0.377428f,-0.102164f},{-0.108128f,-0.359358f,-0.0574708f},{-0.127408f,-0.359423f,-0.0577023f}, -{-0.146712f,-0.359242f,-0.0606604f},{-0.165985f,-0.359172f,-0.0619658f},{-0.185232f,-0.359538f,-0.0583004f}, -{-0.204511f,-0.360201f,-0.0518568f},{-0.339915f,0.269303f,-0.283605f},{0.254966f,-0.340008f,-0.156072f}, -{0.245442f,-0.339577f,-0.161442f},{0.232549f,-0.339179f,-0.166464f},{0.213154f,-0.338658f,-0.173326f}, -{0.197167f,-0.338439f,-0.176895f},{0.187438f,-0.338214f,-0.179653f},{0.174608f,-0.338008f,-0.182618f}, -{0.155297f,-0.337758f,-0.186746f},{0.135986f,-0.337481f,-0.190971f},{0.116662f,-0.341661f,-0.196309f}, -{0.103813f,-0.341803f,-0.196888f},{0.0960773f,-0.335938f,-0.200688f},{0.0780586f,-0.336568f,-0.204579f}, -{0.0587473f,-0.336491f,-0.206225f},{0.0394489f,-0.336253f,-0.20962f},{0.0217324f,-0.338349f,-0.212752f}, -{0.00291629f,-0.337629f,-0.213241f},{-0.0121379f,-0.336652f,-0.208206f},{-0.0271728f,-0.335816f,-0.201248f}, -{-0.0355584f,-0.339333f,-0.19445f},{0.43806f,-0.342523f,-0.0153435f},{0.418954f,-0.34186f,-0.0241021f}, -{0.399823f,-0.34624f,-0.0280055f},{0.38064f,-0.346413f,-0.0280827f},{0.353471f,-0.346156f,-0.0265007f}, -{-0.412408f,0.130015f,-0.0195942f},{-0.316122f,-0.255265f,0.240365f},{-0.186936f,-0.0145526f,-0.112871f}, -{-0.296907f,0.131153f,-0.0426031f},{0.116668f,-0.332388f,-0.199248f},{0.0168837f,-0.331211f,-0.217305f}, -{0.0071991f,-0.331321f,-0.217118f},{-0.375753f,0.00609625f,0.303244f},{-0.109498f,-0.341667f,-0.0609241f}, -{-0.127491f,-0.340857f,-0.0605575f},{-0.146835f,-0.340677f,-0.0623324f},{-0.164474f,-0.342812f,-0.0642037f}, -{-0.186563f,-0.341648f,-0.063059f},{-0.20453f,-0.341282f,-0.0579338f},{-0.219411f,-0.340561f,-0.0491753f}, -{-0.227919f,-0.344323f,-0.0415485f},{-0.373702f,0.11208f,-0.0423523f},{0.0166908f,0.412739f,-0.157738f}, -{-0.206177f,-0.0147069f,-0.112755f},{0.232414f,-0.321205f,-0.16431f},{0.213135f,-0.320716f,-0.171113f}, -{0.193843f,-0.320343f,-0.176708f},{0.174557f,-0.31999f,-0.182065f},{0.155259f,-0.319797f,-0.186476f}, -{0.139195f,-0.319263f,-0.192142f},{0.129401f,-0.318671f,-0.198527f},{0.116598f,-0.318369f,-0.203492f}, -{0.0972605f,-0.318086f,-0.20764f},{0.0779685f,-0.31772f,-0.212656f},{0.058638f,-0.317707f,-0.21398f}, -{0.0408508f,-0.319945f,-0.214688f},{0.0232758f,-0.317199f,-0.22189f},{0.00712193f,-0.317347f,-0.220906f}, -{-0.00791295f,-0.316041f,-0.218687f},{-0.0164593f,-0.319456f,-0.214546f},{-0.0314813f,-0.318671f,-0.2069f}, -{-0.333761f,0.0945243f,-0.0467638f},{-0.101826f,-0.33561f,-0.0671297f},{-0.169342f,-0.335777f,-0.065985f}, -{-0.178943f,-0.33588f,-0.0656699f},{-0.128867f,0.289489f,-0.0502813f},{-0.389348f,0.055143f,-0.0314395f}, -{0.0361306f,-0.312729f,-0.219633f},{0.0188064f,0.379345f,-0.136735f},{-0.114701f,-0.326723f,-0.0646731f}, -{-0.127568f,-0.326813f,-0.0641008f},{-0.143671f,-0.326723f,-0.0642037f},{-0.148539f,-0.319597f,-0.0669946f}, -{-0.166217f,-0.321642f,-0.0696376f},{-0.185386f,-0.321867f,-0.0692003f},{-0.200408f,-0.320729f,-0.0664802f}, -{-0.20889f,-0.324208f,-0.0617858f},{-0.222549f,-0.322581f,-0.0527636f},{-0.230279f,-0.328845f,-0.0446866f}, -{-0.27829f,-0.0147905f,-0.0996752f},{-0.402839f,-0.255696f,0.0866918f},{-0.156641f,0.359429f,-0.0620752f}, -{-0.259853f,0.129867f,-0.0303977f},{-0.225507f,-0.0148805f,-0.111746f},{0.232311f,-0.302794f,-0.167403f}, -{0.214324f,-0.303141f,-0.175171f},{0.19374f,-0.301759f,-0.182271f},{0.174396f,-0.301206f,-0.189023f}, -{0.15513f,-0.301013f,-0.193872f},{0.142391f,-0.305534f,-0.195479f},{0.134507f,-0.299341f,-0.202058f}, -{0.116527f,-0.299746f,-0.208643f},{0.0971769f,-0.299553f,-0.211813f},{0.081081f,-0.299264f,-0.215794f}, -{0.0714157f,-0.299135f,-0.217761f},{0.0585608f,-0.299129f,-0.218829f},{0.0392624f,-0.298923f,-0.222064f}, -{0.0231922f,-0.298633f,-0.226571f},{0.00704477f,-0.298704f,-0.226906f},{-0.0123372f,-0.299238f,-0.222308f}, -{-0.0332305f,-0.302395f,-0.212347f},{-0.148166f,-0.0508858f,-0.125301f},{-0.127607f,-0.317276f,-0.0687245f}, -{-0.242896f,-0.315733f,-0.0365133f},{-0.167451f,-0.0509308f,-0.126793f},{-0.349825f,-0.274268f,0.182084f}, -{-0.302971f,0.0889425f,-0.0468152f},{-0.18675f,-0.0509437f,-0.127417f},{-0.298257f,0.209826f,-0.0373686f}, -{-0.206067f,-0.0509694f,-0.127006f},{-0.244806f,-0.0151764f,-0.108157f},{-0.296392f,-0.0161731f,-0.0915919f}, -{-0.224112f,-0.0520305f,-0.125938f},{0.245371f,-0.298781f,-0.161191f},{0.20662f,-0.297367f,-0.179917f}, -{-0.0283432f,-0.294974f,-0.218765f},{-0.238362f,-0.0558181f,-0.124401f},{-0.24251f,-0.265773f,0.253985f}, -{-0.244851f,-0.0492267f,-0.118369f},{-0.127684f,-0.302858f,-0.0759525f},{-0.146989f,-0.302871f,-0.0744606f}, -{-0.166287f,-0.302807f,-0.0762933f},{-0.185483f,-0.303051f,-0.0754702f},{-0.203521f,-0.302871f,-0.0691617f}, -{-0.21109f,-0.308685f,-0.0650333f},{-0.223887f,-0.304826f,-0.0569242f},{-0.236465f,-0.31026f,-0.046783f}, -{-0.280856f,-0.314929f,-0.00204493f},{-0.296888f,-0.313347f,0.00282951f},{-0.143819f,0.287103f,-0.0472782f}, -{-0.258921f,-0.0529436f,-0.113276f},{-0.351143f,0.0140575f,-0.0489502f},{-0.276875f,-0.052352f,-0.110086f}, -{-0.294849f,-0.0535738f,-0.106627f},{-0.409984f,0.019472f,0.307707f},{-0.399006f,0.0548214f,-0.0255812f}, -{-0.270586f,-0.020083f,-0.103309f},{0.266541f,-0.281946f,-0.143217f},{0.255744f,-0.286988f,-0.155622f}, -{0.247262f,-0.283284f,-0.164477f},{0.23233f,-0.284312f,-0.171621f},{0.219347f,-0.283901f,-0.176425f}, -{0.209707f,-0.283611f,-0.180534f},{0.193611f,-0.283329f,-0.185049f},{0.174293f,-0.282936f,-0.190077f}, -{0.15821f,-0.282486f,-0.196109f},{0.148584f,-0.282312f,-0.198836f},{0.135748f,-0.282036f,-0.202727f}, -{0.116353f,-0.281586f,-0.208797f},{0.0971448f,-0.28147f,-0.211228f},{0.0778142f,-0.281316f,-0.214578f}, -{0.0584965f,-0.281078f,-0.218199f},{0.0391981f,-0.28075f,-0.222964f},{0.0231729f,-0.28039f,-0.228591f}, -{0.00698689f,-0.280062f,-0.232835f},{-0.0123823f,-0.280383f,-0.230764f},{-0.0317321f,-0.281168f,-0.222803f}, -{-0.315324f,-0.0533874f,-0.0960741f},{-0.238606f,-0.294839f,-0.0495289f},{-0.24714f,-0.29882f,-0.0383653f}, -{-0.261332f,-0.29655f,-0.0361339f},{-0.268985f,-0.302711f,-0.0272724f},{-0.281782f,-0.302974f,-0.0227774f}, -{-0.302392f,-0.29992f,-0.0184303f},{-0.331465f,-0.053979f,-0.086994f},{-0.298386f,-0.24034f,0.26075f}, -{-0.42761f,0.12824f,-0.012797f},{-0.335369f,-0.242693f,0.242372f},{-0.341195f,-0.0541976f,-0.0824604f}, -{-0.353973f,-0.0546413f,-0.0768078f},{-0.368892f,-0.0567249f,-0.0698305f},{-0.127761f,-0.288569f,-0.0824411f}, -{-0.147072f,-0.28855f,-0.0815279f},{-0.166352f,-0.288389f,-0.0835857f},{-0.187238f,-0.28664f,-0.0815858f}, -{-0.204878f,-0.284955f,-0.0752902f},{-0.225694f,-0.288589f,-0.0600302f},{-0.281904f,-0.293039f,-0.0341789f}, -{-0.294656f,-0.293315f,-0.0295875f},{-0.377612f,-0.054069f,-0.0618244f},{-0.1754f,0.296756f,-0.0446545f}, -{-0.2782f,-0.265471f,0.245973f},{-0.362107f,-0.229041f,0.234423f},{0.26858f,-0.260242f,-0.148568f}, -{0.257635f,-0.267091f,-0.157378f},{0.248175f,-0.26648f,-0.165757f},{0.232266f,-0.265863f,-0.174573f}, -{0.219417f,-0.270159f,-0.178419f},{0.211533f,-0.264313f,-0.183132f},{0.193624f,-0.264789f,-0.189801f}, -{0.174178f,-0.264686f,-0.191132f},{0.159162f,-0.265895f,-0.195531f},{0.150596f,-0.262506f,-0.200804f}, -{0.135581f,-0.263824f,-0.203575f},{0.116302f,-0.263651f,-0.206964f},{0.0969904f,-0.263316f,-0.21207f}, -{0.0841355f,-0.267606f,-0.215331f},{0.0764059f,-0.261831f,-0.219999f},{0.0584065f,-0.262641f,-0.222584f}, -{0.0391338f,-0.262339f,-0.227279f},{0.0230957f,-0.261953f,-0.233394f},{0.00694188f,-0.266332f,-0.234957f}, -{-0.0124466f,-0.26421f,-0.234661f},{-0.0317964f,-0.262358f,-0.231047f},{-0.0467992f,-0.261567f,-0.222887f}, -{-0.117704f,0.383499f,-0.0835278f},{-0.0364072f,-0.48212f,0.341198f},{-0.127806f,-0.279161f,-0.0867046f}, -{-0.147137f,-0.279078f,-0.08565f},{-0.16639f,-0.278904f,-0.0886274f},{-0.18246f,-0.279303f,-0.0873605f}, -{-0.220678f,-0.280769f,-0.0675541f},{-0.243526f,-0.282145f,-0.0568277f},{-0.262773f,-0.282698f,-0.0507636f}, -{-0.279988f,-0.276287f,-0.056249f},{-0.285325f,-0.283521f,-0.0440564f},{-0.302797f,-0.281296f,-0.0386161f}, -{-0.321954f,-0.282673f,-0.0228095f},{-0.220665f,0.229311f,0.0031639f},{-0.0906949f,0.286428f,-0.0364425f}, -{0.0101379f,0.436443f,-0.16184f},{0.27719f,-0.263914f,-0.140484f},{0.006884f,-0.257233f,-0.236211f}, -{-0.0124208f,-0.257323f,-0.235844f},{-0.354661f,0.0166168f,0.323713f},{-0.127883f,-0.264937f,-0.0931095f}, -{-0.14724f,-0.264782f,-0.0925758f},{-0.16648f,-0.264699f,-0.0958426f},{-0.185689f,-0.26522f,-0.0914376f}, -{-0.206235f,-0.267007f,-0.0815793f},{-0.224247f,-0.266416f,-0.0773737f},{-0.243558f,-0.266422f,-0.0763512f}, -{-0.262837f,-0.266577f,-0.0745442f},{-0.282033f,-0.272332f,-0.0624996f},{-0.297942f,-0.27293f,-0.0529115f}, -{-0.317356f,-0.274866f,-0.034732f},{-0.347812f,-0.0224109f,-0.0663258f},{0.257731f,-0.244269f,-0.159005f}, -{0.248124f,-0.243896f,-0.163995f},{0.23287f,-0.24587f,-0.172798f},{0.211456f,-0.24796f,-0.183132f}, -{0.193405f,-0.246474f,-0.191087f},{0.177335f,-0.246134f,-0.196154f},{0.167618f,-0.245818f,-0.200322f}, -{0.154744f,-0.245413f,-0.205807f},{0.135478f,-0.245117f,-0.210707f},{0.117485f,-0.245902f,-0.213594f}, -{0.100193f,-0.249503f,-0.215736f},{0.0953442f,-0.242487f,-0.218488f},{0.0776277f,-0.244552f,-0.221131f}, -{0.0583486f,-0.244204f,-0.226707f},{0.0403234f,-0.244847f,-0.231767f},{0.0229349f,-0.24351f,-0.236661f}, -{0.00681327f,-0.243394f,-0.23886f},{-0.0125044f,-0.24351f,-0.238738f},{-0.0306132f,-0.242847f,-0.237137f}, -{-0.0383171f,-0.248609f,-0.233414f},{-0.0510755f,-0.244616f,-0.22661f},{-0.334828f,0.188579f,-0.0405839f}, -{-0.333163f,0.273882f,-0.295952f},{-0.198428f,-0.260988f,-0.0868846f},{-0.282091f,-0.26185f,-0.0785055f}, -{-0.301376f,-0.258152f,-0.0702807f},{-0.307382f,-0.262924f,-0.060416f},{-0.320366f,-0.258976f,-0.0545963f}, -{-0.326694f,-0.26495f,-0.039465f},{-0.33634f,-0.260789f,-0.0343526f},{-0.300212f,-0.249072f,0.252564f}, -{-0.336835f,-0.251272f,0.232552f},{0.283467f,-0.240983f,-0.142163f},{0.270657f,-0.240346f,-0.15069f}, -{0.219115f,-0.242822f,-0.178837f},{0.109749f,-0.240217f,-0.217208f},{0.0324845f,-0.239047f,-0.234745f}, -{-0.0638275f,-0.240713f,-0.219105f},{-0.129581f,-0.248333f,-0.100813f},{-0.146031f,-0.246899f,-0.100344f}, -{-0.166544f,-0.250494f,-0.102704f},{-0.166577f,-0.24351f,-0.105045f},{-0.185843f,-0.246526f,-0.0976367f}, -{-0.205058f,-0.247098f,-0.0892833f},{-0.224453f,-0.246918f,-0.0900935f},{-0.243732f,-0.246777f,-0.0927944f}, -{-0.262985f,-0.246995f,-0.0935661f},{-0.280991f,-0.246468f,-0.091071f},{-0.288643f,-0.252551f,-0.0838687f}, -{-0.301395f,-0.248088f,-0.0803832f},{-0.314218f,-0.253966f,-0.0657406f},{-0.332758f,-0.254088f,-0.051889f}, -{-0.349246f,-0.256423f,-0.031851f},{-0.264111f,-0.0108871f,-0.103058f},{-0.459564f,0.0280763f,0.295232f}, -{-0.222761f,0.242751f,-0.005331f},{0.231121f,-0.22425f,-0.16921f},{0.216517f,-0.223093f,-0.175872f}, -{0.208395f,-0.230565f,-0.181145f},{0.193405f,-0.228655f,-0.186785f},{0.177297f,-0.228102f,-0.194849f}, -{0.167721f,-0.227542f,-0.20317f},{0.154751f,-0.227092f,-0.208739f},{0.13542f,-0.226784f,-0.213588f}, -{0.122597f,-0.231227f,-0.215755f},{0.114803f,-0.225594f,-0.21816f},{0.0968425f,-0.226391f,-0.221073f}, -{0.0775763f,-0.226166f,-0.225266f},{0.0582778f,-0.225826f,-0.230468f},{0.0402527f,-0.226436f,-0.234481f}, -{0.0247612f,-0.224443f,-0.237208f},{0.0067361f,-0.225215f,-0.239786f},{-0.0125752f,-0.225253f,-0.240468f}, -{-0.0319829f,-0.225453f,-0.239336f},{-0.0523938f,-0.227125f,-0.230205f},{-0.067075f,-0.227041f,-0.220314f}, -{-0.302804f,0.325057f,-0.357101f},{-0.12481f,-0.24104f,-0.105932f},{-0.15376f,-0.241272f,-0.103604f}, -{-0.298251f,-0.24297f,-0.0873862f},{-0.320552f,-0.244069f,-0.0723128f},{-0.339349f,-0.244757f,-0.0569306f}, -{-0.0142214f,0.343577f,-0.0948908f},{-0.207077f,0.240359f,-0.00654639f},{0.291029f,-0.222366f,-0.13541f}, -{0.283357f,-0.22733f,-0.142124f},{0.270483f,-0.222147f,-0.150703f},{0.2555f,-0.220122f,-0.157976f}, -{0.247018f,-0.222893f,-0.161422f},{-0.0447735f,-0.22124f,-0.235742f},{-0.128083f,-0.227144f,-0.1088f}, -{-0.147394f,-0.22735f,-0.105675f},{-0.166647f,-0.227285f,-0.108067f},{-0.181682f,-0.22616f,-0.104948f}, -{-0.190016f,-0.228919f,-0.101026f},{-0.203559f,-0.230404f,-0.0983441f},{-0.224517f,-0.232796f,-0.0976946f}, -{-0.243815f,-0.232603f,-0.101527f},{-0.263101f,-0.232841f,-0.102376f},{-0.283988f,-0.23079f,-0.0994694f}, -{-0.300379f,-0.22971f,-0.096685f},{-0.314482f,-0.226816f,-0.0928073f},{-0.3207f,-0.234185f,-0.0835986f}, -{-0.333562f,-0.230108f,-0.0782547f},{-0.336687f,-0.235201f,-0.0708466f},{-0.352545f,-0.236166f,-0.0560239f}, -{-0.28748f,-0.479747f,0.292415f},{0.207797f,-0.204083f,-0.181563f},{0.194273f,-0.208585f,-0.187209f}, -{0.177258f,-0.20991f,-0.195582f},{0.167413f,-0.209408f,-0.202411f},{0.15468f,-0.208964f,-0.208553f}, -{0.13533f,-0.208726f,-0.212643f},{0.122462f,-0.208488f,-0.216462f},{0.112855f,-0.208366f,-0.218623f}, -{0.0967975f,-0.208167f,-0.222334f},{0.0774541f,-0.207652f,-0.229793f},{0.0581364f,-0.207505f,-0.232642f}, -{0.0420469f,-0.207472f,-0.233742f},{0.0324394f,-0.207427f,-0.234951f},{0.0228063f,-0.207376f,-0.236571f}, -{0.00668465f,-0.207363f,-0.237787f},{-0.0126459f,-0.207228f,-0.239786f},{-0.0320472f,-0.207337f,-0.239484f}, -{-0.0447799f,-0.212193f,-0.235697f},{-0.0525289f,-0.207177f,-0.230996f},{-0.208485f,-0.223137f,-0.103862f}, -{-0.224588f,-0.222951f,-0.106312f},{-0.243835f,-0.222919f,-0.108556f},{-0.263159f,-0.223176f,-0.108627f}, -{-0.279229f,-0.223478f,-0.104273f},{-0.333671f,-0.220372f,-0.0866596f},{-0.351207f,-0.220282f,-0.0727115f}, -{-0.358821f,-0.226726f,-0.0594643f},{-0.370667f,-0.222218f,-0.0515289f},{-0.386113f,0.0409569f,-0.0281148f}, -{-0.239307f,0.243812f,-0.00250151f},{0.254394f,-0.203492f,-0.157403f},{0.244806f,-0.203196f,-0.161686f}, -{0.231938f,-0.202772f,-0.167885f},{0.216929f,-0.203704f,-0.176142f},{-0.0388702f,0.396682f,-0.120768f}, -{-0.128173f,-0.208501f,-0.113861f},{-0.147458f,-0.208707f,-0.111475f},{-0.166737f,-0.208662f,-0.112884f}, -{-0.186017f,-0.208906f,-0.110865f},{-0.205341f,-0.208977f,-0.110035f},{-0.224639f,-0.208881f,-0.112807f}, -{-0.243918f,-0.208829f,-0.115173f},{-0.263242f,-0.209112f,-0.11361f},{-0.282496f,-0.209588f,-0.108691f}, -{-0.298579f,-0.209871f,-0.105495f},{-0.315884f,-0.211241f,-0.100067f},{-0.332989f,-0.203653f,-0.0944986f}, -{-0.0356677f,-0.0449182f,-0.141745f},{-0.0517443f,-0.0449503f,-0.141706f},{0.192402f,-0.186624f,-0.187769f}, -{0.177104f,-0.187293f,-0.194682f},{0.167528f,-0.186959f,-0.199293f},{0.154654f,-0.190991f,-0.206353f}, -{0.13533f,-0.190798f,-0.21009f},{0.120314f,-0.192013f,-0.215009f},{0.111633f,-0.188714f,-0.219125f}, -{0.0967203f,-0.189936f,-0.224141f},{0.0773898f,-0.189557f,-0.229999f},{0.0580528f,-0.189344f,-0.233574f}, -{0.0430629f,-0.190888f,-0.233973f},{0.0259123f,-0.189068f,-0.239131f},{0.00660105f,-0.188926f,-0.2416f}, -{-0.0127488f,-0.189068f,-0.24086f},{-0.0320601f,-0.189235f,-0.239555f},{-0.0513328f,-0.18984f,-0.232706f}, -{-0.308193f,-0.205389f,-0.104312f},{-0.348738f,-0.200778f,-0.0857979f},{-0.36502f,-0.2078f,-0.0682293f}, -{0.254374f,-0.185415f,-0.155905f},{0.244761f,-0.185081f,-0.160876f},{0.231841f,-0.184618f,-0.167589f}, -{0.215688f,-0.184026f,-0.176174f},{0.206183f,-0.183673f,-0.180978f},{0.0386965f,-0.184701f,-0.235838f}, -{-0.131459f,-0.194405f,-0.119462f},{-0.147542f,-0.189962f,-0.118614f},{-0.166834f,-0.190007f,-0.118607f}, -{-0.186126f,-0.190116f,-0.117642f},{-0.20381f,-0.192386f,-0.119224f},{-0.224703f,-0.19465f,-0.120864f}, -{-0.242741f,-0.188926f,-0.125308f},{-0.250503f,-0.19483f,-0.12208f},{-0.264966f,-0.192753f,-0.119224f}, -{-0.28265f,-0.190926f,-0.114067f},{-0.298695f,-0.191241f,-0.110492f},{-0.314829f,-0.188631f,-0.106144f}, -{-0.321189f,-0.196579f,-0.102087f},{-0.384666f,-0.200495f,-0.0488087f},{0.154513f,-0.168696f,-0.202257f}, -{0.135208f,-0.168265f,-0.208701f},{0.122231f,-0.167988f,-0.213401f},{0.112617f,-0.172059f,-0.219884f}, -{0.0965724f,-0.171564f,-0.227272f},{0.0772612f,-0.171287f,-0.232134f},{0.057982f,-0.171236f,-0.233568f}, -{0.0258287f,-0.170895f,-0.239851f},{0.0064853f,-0.170785f,-0.242179f},{-0.0128388f,-0.170985f,-0.240468f}, -{-0.0308383f,-0.172142f,-0.238134f},{-0.0514292f,-0.171821f,-0.231722f},{-0.231925f,-0.0466544f,-0.121996f}, -{-0.000286191f,0.396785f,-0.137282f},{-0.208659f,-0.185293f,-0.123424f},{-0.224742f,-0.185152f,-0.125771f}, -{-0.260162f,-0.185505f,-0.123867f},{-0.335362f,-0.183981f,-0.0998939f},{-0.353162f,-0.183698f,-0.0911675f}, -{-0.298759f,0.345281f,-0.371531f},{0.308964f,-0.170007f,-0.116054f},{-0.302694f,-0.0482235f,-0.103032f}, -{0.244555f,-0.171448f,-0.161004f},{0.215681f,-0.16593f,-0.175338f},{0.206035f,-0.165615f,-0.180052f}, -{0.193129f,-0.165281f,-0.185447f},{0.173856f,-0.164792f,-0.192727f},{-0.0385808f,-0.166979f,-0.235182f}, -{-0.128449f,-0.170522f,-0.133179f},{-0.147651f,-0.17112f,-0.126915f},{-0.166924f,-0.171371f,-0.124819f}, -{-0.186222f,-0.171326f,-0.12552f},{-0.205527f,-0.171268f,-0.127623f},{-0.224806f,-0.171326f,-0.128613f}, -{-0.244111f,-0.171416f,-0.128092f},{-0.263435f,-0.171737f,-0.125076f},{-0.281441f,-0.173306f,-0.117405f}, -{-0.296418f,-0.167056f,-0.113283f},{-0.314115f,-0.167384f,-0.10999f},{-0.327723f,-0.178129f,-0.104582f}, -{-0.365856f,-0.179563f,-0.0851162f},{-0.311562f,0.346207f,-0.370239f},{-0.112906f,-0.0277612f,-0.121623f}, -{0.115839f,-0.147288f,-0.212437f},{0.109389f,-0.158542f,-0.219015f},{0.0956914f,-0.150407f,-0.222301f}, -{0.0771711f,-0.149031f,-0.226379f},{0.0586958f,-0.150002f,-0.229555f},{0.00640814f,-0.152928f,-0.239028f}, -{-0.0116234f,-0.154008f,-0.237459f},{-0.0257387f,-0.157808f,-0.236037f},{-0.032999f,-0.148002f,-0.231105f}, -{-0.051455f,-0.149603f,-0.226173f},{-0.315093f,-0.235465f,0.25749f},{-0.327755f,-0.164342f,-0.105354f}, -{-0.337484f,-0.164696f,-0.101244f},{-0.353445f,-0.165262f,-0.0944921f},{-0.368332f,-0.164336f,-0.0870583f}, -{0.193065f,-0.146979f,-0.1862f},{0.173721f,-0.146606f,-0.192502f},{0.154513f,-0.146034f,-0.200945f}, -{0.135137f,-0.145764f,-0.205717f},{-0.0193917f,-0.148838f,-0.233806f},{-0.123883f,-0.149892f,-0.142754f}, -{-0.132726f,-0.153764f,-0.133989f},{-0.147741f,-0.152612f,-0.129636f},{-0.167014f,-0.152793f,-0.128234f}, -{-0.186312f,-0.152735f,-0.130169f},{-0.00889684f,0.422186f,-0.143655f},{0.102977f,-0.140529f,-0.216012f}, -{-0.318225f,-0.223105f,0.263252f},{-0.366892f,-0.00498377f,-0.0479984f},{-0.110251f,-0.491759f,-0.181525f}, -{-0.0913894f,-0.491913f,-0.170451f},{-0.131369f,-0.491315f,-0.169191f},{-0.111292f,-0.49145f,-0.169943f}, -{-0.0513714f,-0.491663f,-0.151969f},{-0.0331983f,-0.491624f,-0.149641f},{-0.0130961f,-0.49174f,-0.150439f}, -{0.0435452f,-0.489901f,-0.13197f},{0.0612102f,-0.489663f,-0.131745f},{0.0793768f,-0.489631f,-0.129423f}, -{0.0996656f,-0.489405f,-0.130517f},{0.155735f,-0.487618f,-0.114164f},{0.174171f,-0.486608f,-0.112215f}, -{0.189656f,-0.484576f,-0.112292f},{-0.165329f,-0.488055f,-0.165667f},{-0.125909f,-0.491836f,-0.151371f}, -{-0.108263f,-0.491585f,-0.151146f},{-0.0900904f,-0.49154f,-0.148818f},{-0.0699945f,-0.491643f,-0.149596f}, -{-0.0160992f,-0.490897f,-0.133668f},{0.00396448f,-0.490171f,-0.130407f},{0.0226391f,-0.4901f,-0.132845f}, -{0.0807787f,-0.488531f,-0.11381f},{0.0982894f,-0.488306f,-0.112389f},{0.114906f,-0.488319f,-0.11154f}, -{0.135118f,-0.488261f,-0.112492f},{0.191971f,-0.48628f,-0.09446f},{0.202884f,-0.484119f,-0.103309f}, -{0.20808f,-0.483258f,-0.0937976f},{-0.147793f,-0.491798f,-0.149712f},{-0.0728047f,-0.490479f,-0.133153f}, -{-0.0527861f,-0.48983f,-0.129809f},{-0.0355262f,-0.490274f,-0.129012f},{0.0236487f,-0.487901f,-0.116549f}, -{0.0419633f,-0.487168f,-0.112414f},{0.0601042f,-0.48718f,-0.110041f},{0.117163f,-0.485644f,-0.097193f}, -{0.135915f,-0.486377f,-0.095849f},{0.15513f,-0.487213f,-0.0912961f},{0.17367f,-0.487496f,-0.0935918f}, -{-0.449989f,0.00813478f,0.248461f},{-0.145728f,-0.491373f,-0.133513f},{-0.128584f,-0.491174f,-0.130298f}, -{-0.10991f,-0.490229f,-0.128678f},{-0.0921225f,-0.489714f,-0.128671f},{-0.031835f,-0.483238f,-0.116736f}, -{-0.0167037f,-0.484808f,-0.116517f},{0.00189381f,-0.484351f,-0.113276f},{0.0818527f,-0.485258f,-0.101489f}, -{0.0984116f,-0.484917f,-0.0985306f},{0.173901f,-0.486576f,-0.0760618f},{0.192023f,-0.486705f,-0.0736632f}, -{-0.168641f,-0.49165f,-0.132992f},{-0.0911964f,-0.483785f,-0.118948f},{-0.0730427f,-0.48464f,-0.120575f}, -{-0.0523617f,-0.482775f,-0.118221f},{0.00335356f,-0.475322f,-0.106074f},{0.0212051f,-0.480087f,-0.106177f}, -{0.0343301f,-0.480049f,-0.103611f},{0.041751f,-0.470757f,-0.0971737f},{0.0607922f,-0.471419f,-0.0942992f}, -{0.0806115f,-0.471117f,-0.0906723f},{0.100894f,-0.474075f,-0.0899521f},{0.119067f,-0.47585f,-0.0877528f}, -{0.133176f,-0.479046f,-0.0850133f},{0.139909f,-0.470962f,-0.0775409f},{0.157799f,-0.481946f,-0.0774637f}, -{0.196035f,-0.486415f,-0.0577216f},{0.211578f,-0.484203f,-0.0575994f},{0.224408f,-0.482055f,-0.0584868f}, -{-0.165348f,-0.491412f,-0.114614f},{-0.14686f,-0.490775f,-0.112742f},{-0.129395f,-0.488351f,-0.109109f}, -{-0.111466f,-0.484775f,-0.113057f},{-0.0891579f,-0.471522f,-0.108466f},{-0.0720395f,-0.471091f,-0.110614f}, -{-0.0527539f,-0.471252f,-0.110929f},{-0.032253f,-0.469728f,-0.108279f},{-0.013842f,-0.471046f,-0.106427f}, -{0.0209929f,-0.468191f,-0.099135f},{0.121511f,-0.465696f,-0.0803254f},{0.155632f,-0.472004f,-0.0701456f}, -{0.167721f,-0.478049f,-0.0589434f},{0.177007f,-0.484441f,-0.0562425f},{-0.184801f,-0.487926f,-0.112749f}, -{-0.104829f,-0.478531f,-0.108537f},{-0.00903832f,-0.46511f,-0.10192f},{0.004241f,-0.464802f,-0.099553f}, -{0.100585f,-0.462332f,-0.0827369f},{0.157169f,-0.467252f,-0.0572972f},{0.176551f,-0.484949f,-0.0377415f}, -{0.193264f,-0.486377f,-0.036121f},{0.212665f,-0.484074f,-0.0356001f},{0.224858f,-0.482512f,-0.0334201f}, -{-0.352043f,-0.278075f,0.16793f},{-0.18556f,-0.490968f,-0.0940098f},{-0.167451f,-0.491032f,-0.0915404f}, -{-0.14724f,-0.489013f,-0.0923507f},{-0.131041f,-0.483663f,-0.0930645f},{-0.112103f,-0.469548f,-0.0943121f}, -{-0.0604193f,-0.461059f,-0.103823f},{-0.0504261f,-0.459426f,-0.0961384f},{-0.0323109f,-0.459471f,-0.0937076f}, -{-0.0139642f,-0.459091f,-0.0916176f},{0.00604159f,-0.459297f,-0.0922414f},{0.0226713f,-0.45886f,-0.0893476f}, -{0.0390823f,-0.45877f,-0.0861644f},{0.0606829f,-0.457098f,-0.0773866f},{0.0806051f,-0.456603f,-0.0738947f}, -{0.0981608f,-0.456506f,-0.0734896f},{0.118192f,-0.457844f,-0.0712967f},{0.133902f,-0.459059f,-0.0670525f}, -{0.137812f,-0.458506f,-0.0571235f},{0.166589f,-0.477091f,-0.0439407f},{0.230665f,-0.482583f,-0.0170991f}, -{-0.196646f,-0.486422f,-0.0956625f},{-0.14614f,-0.484782f,-0.0751744f},{-0.126166f,-0.477297f,-0.0899778f}, -{-0.098611f,-0.46286f,-0.097791f},{-0.0876403f,-0.459458f,-0.0915533f},{-0.071255f,-0.458699f,-0.0931738f}, -{0.00337286f,-0.457902f,-0.0760361f},{0.0219189f,-0.457149f,-0.0742355f},{0.042169f,-0.456937f,-0.0752066f}, -{0.0984052f,-0.455722f,-0.0560882f},{0.116044f,-0.455445f,-0.0558374f},{0.151233f,-0.46104f,-0.049709f}, -{0.157226f,-0.469014f,-0.0381724f},{0.172171f,-0.485952f,-0.0186167f},{0.193097f,-0.486377f,-0.0180573f}, -{0.209939f,-0.485496f,-0.0155172f},{-0.185348f,-0.491637f,-0.0737532f},{-0.164024f,-0.490582f,-0.0734317f}, -{-0.127047f,-0.469368f,-0.0775602f},{-0.101569f,-0.460133f,-0.088936f},{-0.0714929f,-0.458892f,-0.0750972f}, -{-0.0540208f,-0.458911f,-0.0745764f},{-0.0364265f,-0.458705f,-0.0742484f},{-0.015469f,-0.458365f,-0.0733738f}, -{0.0257065f,-0.457432f,-0.0580045f},{0.0419826f,-0.456969f,-0.057175f},{0.0585351f,-0.456648f,-0.0541783f}, -{0.0773255f,-0.456313f,-0.0567248f},{0.136783f,-0.457207f,-0.0380245f},{0.147703f,-0.459304f,-0.0399601f}, -{0.149902f,-0.475174f,-0.0189254f},{0.157439f,-0.483457f,-0.0158194f},{0.174904f,-0.487335f,0.000244383f}, -{0.192376f,-0.487361f,0.000745975f},{0.210267f,-0.48664f,0.000649515f},{0.232549f,-0.483856f,-0.000456558f}, -{0.245937f,-0.48329f,0.00167842f},{-0.18529f,-0.491405f,-0.0561847f},{-0.167104f,-0.490087f,-0.0567055f}, -{-0.151047f,-0.484608f,-0.0584933f},{-0.109993f,-0.460133f,-0.0715025f},{-0.0922768f,-0.458937f,-0.0762226f}, -{-0.0308768f,-0.459117f,-0.0565898f},{-0.0139642f,-0.458551f,-0.0561204f},{0.00377799f,-0.457844f,-0.0550079f}, -{0.0625928f,-0.456969f,-0.0380502f},{0.0804443f,-0.456288f,-0.0380952f},{0.0988939f,-0.455683f,-0.0361532f}, -{0.119369f,-0.455046f,-0.037465f},{0.141034f,-0.465233f,-0.0178001f},{0.153747f,-0.486036f,0.00079742f}, -{0.213f,-0.485727f,0.0189962f},{0.229558f,-0.485804f,0.0199737f},{0.248606f,-0.484119f,0.0210347f}, -{0.26858f,-0.47558f,0.0111829f},{0.288714f,-0.472416f,0.0155494f},{0.306231f,-0.470776f,0.0226167f}, -{0.310443f,-0.463844f,0.00981964f},{-0.224781f,-0.491733f,-0.0549307f},{-0.206305f,-0.491907f,-0.0569756f}, -{-0.135613f,-0.470229f,-0.0626025f},{-0.124019f,-0.461033f,-0.068718f},{-0.0896531f,-0.45994f,-0.0568727f}, -{-0.0721617f,-0.459902f,-0.0563904f},{-0.0514485f,-0.459979f,-0.0551493f},{0.0043246f,-0.458435f,-0.0382045f}, -{0.0226648f,-0.458024f,-0.0361017f},{0.0414231f,-0.457709f,-0.0386097f},{0.0994469f,-0.456223f,-0.0193434f}, -{0.11672f,-0.456378f,-0.0175493f},{0.130005f,-0.457342f,-0.0212147f},{0.132829f,-0.472731f,0.00142119f}, -{0.141529f,-0.484267f,0.00572974f},{0.156886f,-0.488074f,0.0182824f},{0.173599f,-0.487438f,0.0210476f}, -{0.192595f,-0.487026f,0.0232919f},{0.270638f,-0.483528f,0.0369313f},{0.284952f,-0.479406f,0.0300312f}, -{0.310193f,-0.475811f,0.0396836f},{0.339658f,-0.462377f,0.0256391f},{-0.203045f,-0.491862f,-0.0386418f}, -{-0.184647f,-0.491309f,-0.0366355f},{-0.169342f,-0.490061f,-0.0335102f},{-0.155252f,-0.486203f,-0.0400759f}, -{-0.139999f,-0.470178f,-0.0516832f},{-0.127748f,-0.461014f,-0.0536188f},{-0.108225f,-0.459914f,-0.054667f}, -{-0.0706569f,-0.460564f,-0.0393814f},{-0.0535706f,-0.460442f,-0.0360696f},{-0.0367223f,-0.459979f,-0.0354779f}, -{-0.0162921f,-0.459374f,-0.0367126f},{0.0429086f,-0.457979f,-0.0213112f},{0.0609852f,-0.457612f,-0.0168162f}, -{0.0772612f,-0.45713f,-0.0159802f},{0.118276f,-0.459194f,-0.00186487f},{0.136436f,-0.486428f,0.0216328f}, -{0.199257f,-0.484287f,0.0293238f},{0.217154f,-0.481476f,0.0303463f},{0.232845f,-0.483676f,0.0385004f}, -{0.250696f,-0.486023f,0.0412141f},{0.289036f,-0.481714f,0.0449761f},{0.299518f,-0.480499f,0.0459921f}, -{0.327009f,-0.471303f,0.0436513f},{0.343368f,-0.465252f,0.0412077f},{-0.337967f,-0.286254f,0.172303f}, -{-0.260143f,-0.492505f,-0.0377673f},{-0.242118f,-0.492666f,-0.0351628f},{-0.221855f,-0.492312f,-0.0361146f}, -{-0.164705f,-0.489463f,-0.0183659f},{-0.14695f,-0.474859f,-0.036957f},{-0.1267f,-0.460628f,-0.0399087f}, -{-0.110257f,-0.460275f,-0.0357287f},{-0.0942703f,-0.460185f,-0.0367512f},{-0.0310762f,-0.460262f,-0.0180123f}, -{-0.0133469f,-0.459773f,-0.0178515f},{0.00447894f,-0.459111f,-0.0178386f},{0.0255651f,-0.45848f,-0.0171698f}, -{0.0810745f,-0.457406f,0.0002058f},{0.097865f,-0.457046f,0.000855296f},{0.120855f,-0.463786f,0.00681652f}, -{0.118437f,-0.470152f,0.0181152f},{0.12425f,-0.481592f,0.0237549f},{0.1367f,-0.487566f,0.0388476f}, -{0.154005f,-0.486242f,0.0387576f},{0.176743f,-0.48138f,0.0340954f},{0.194634f,-0.478551f,0.0351179f}, -{0.212723f,-0.475374f,0.0358574f},{0.22687f,-0.479457f,0.0424488f},{0.249918f,-0.484319f,0.0566349f}, -{0.26966f,-0.485065f,0.056159f},{0.286888f,-0.483457f,0.0581461f},{0.304309f,-0.481502f,0.0598823f}, -{0.315511f,-0.478956f,0.0598052f},{0.327999f,-0.474049f,0.0578567f},{0.344597f,-0.467297f,0.0551172f}, -{0.358268f,-0.462313f,0.0590335f},{0.366667f,-0.458294f,0.0572715f},{-0.402948f,-0.00846919f,0.222533f}, -{-0.262921f,-0.299225f,0.207421f},{-0.278419f,-0.492003f,-0.036031f},{-0.241172f,-0.49273f,-0.0190412f}, -{-0.22352f,-0.492139f,-0.0177743f},{-0.206788f,-0.491856f,-0.0170091f},{-0.186383f,-0.491251f,-0.0181473f}, -{-0.131356f,-0.46212f,-0.0335487f},{-0.0887399f,-0.460731f,-0.0191055f},{-0.0707727f,-0.460982f,-0.0164432f}, -{-0.0521559f,-0.460898f,-0.0187068f},{0.003823f,-0.460146f,0.000823143f},{0.0216231f,-0.459522f,0.000874589f}, -{0.0395389f,-0.458718f,0.000784559f},{0.0607344f,-0.45787f,0.00129258f},{0.101151f,-0.45832f,0.0177872f}, -{0.111183f,-0.461329f,0.014604f},{0.111813f,-0.474814f,0.0349442f},{0.118945f,-0.484544f,0.0412399f}, -{0.168789f,-0.475779f,0.0393492f},{0.234195f,-0.47585f,0.0525964f},{0.269107f,-0.48583f,0.0746986f}, -{0.288071f,-0.484242f,0.0758882f},{0.30537f,-0.482087f,0.079811f},{0.32416f,-0.47803f,0.0775281f}, -{0.344796f,-0.469792f,0.0765442f},{-0.388422f,-0.255927f,0.150561f},{-0.297801f,-0.492595f,-0.0188868f}, -{-0.28047f,-0.492601f,-0.0171313f},{-0.264644f,-0.492762f,-0.0178772f},{-0.200666f,-0.491412f,-0.000302223f}, -{-0.184525f,-0.491f,-0.00152405f},{-0.166377f,-0.490505f,0.00285523f},{-0.148854f,-0.486319f,-0.00328605f}, -{-0.125253f,-0.46084f,-0.0216392f},{-0.10872f,-0.460551f,-0.0185653f},{-0.0523552f,-0.46113f,-0.000713785f}, -{-0.0344008f,-0.460982f,0.00396774f},{-0.0170895f,-0.46048f,-0.000128595f},{0.045095f,-0.459136f,0.0183596f}, -{0.0618983f,-0.458763f,0.0190219f},{0.0790875f,-0.458423f,0.0222051f},{0.105955f,-0.462815f,0.0268223f}, -{0.118353f,-0.485785f,0.0573294f},{0.132205f,-0.488158f,0.0591107f},{0.148146f,-0.481541f,0.052352f}, -{0.158841f,-0.473869f,0.0475483f},{0.242536f,-0.476377f,0.0667246f},{0.251789f,-0.47801f,0.0780554f}, -{0.256014f,-0.481702f,0.0742999f},{0.361348f,-0.462094f,0.0778367f},{0.371265f,-0.4565f,0.0741841f}, -{0.383393f,-0.450474f,0.0784477f},{0.397643f,-0.443439f,0.0674384f},{-0.411135f,-0.00341467f,0.245548f}, -{-0.258638f,-0.492601f,-0.00105461f},{-0.240375f,-0.492293f,0.00120898f},{-0.222729f,-0.491901f,0.00151122f}, -{-0.147889f,-0.490621f,0.0184046f},{-0.120012f,-0.461149f,-0.00846916f},{-0.107961f,-0.461091f,-0.00315744f}, -{-0.0910678f,-0.460898f,0.00417352f},{-0.0740459f,-0.460847f,0.000559486f},{-0.0140221f,-0.46077f,0.0184882f}, -{0.00419598f,-0.460551f,0.0208032f},{0.0244911f,-0.460107f,0.0198257f},{0.080708f,-0.458995f,0.0389763f}, -{0.0930806f,-0.459606f,0.0371435f},{0.102206f,-0.467586f,0.0423202f},{0.0986688f,-0.470789f,0.0581653f}, -{0.13623f,-0.488184f,0.0748786f},{0.267995f,-0.483624f,0.0962734f},{0.286071f,-0.484467f,0.0948265f}, -{0.30382f,-0.481882f,0.0960741f},{0.314881f,-0.479592f,0.0962284f},{0.329105f,-0.474776f,0.0986657f}, -{0.344635f,-0.468905f,0.0984599f},{0.356056f,-0.463818f,0.0903444f},{0.364062f,-0.459651f,0.0960548f}, -{-0.314405f,-0.492216f,0.000662377f},{-0.297273f,-0.492743f,0.00171057f},{-0.279666f,-0.492441f,0.00209641f}, -{-0.222736f,-0.492036f,0.0190669f},{-0.205077f,-0.491598f,0.0193434f},{-0.187361f,-0.491077f,0.0195557f}, -{-0.166808f,-0.491367f,0.0210669f},{-0.133896f,-0.490949f,0.0273496f},{-0.105106f,-0.461857f,0.00686153f}, -{-0.07121f,-0.461323f,0.0179158f},{-0.0534227f,-0.461078f,0.0195557f},{-0.0327031f,-0.461072f,0.0208225f}, -{0.00517987f,-0.46059f,0.0368284f},{0.0227034f,-0.46023f,0.0383075f},{0.0410372f,-0.459786f,0.040436f}, -{0.0597762f,-0.459394f,0.0380245f},{0.107241f,-0.482544f,0.0744349f},{0.116154f,-0.486184f,0.0766213f}, -{0.25449f,-0.475644f,0.0960805f},{0.288759f,-0.482737f,0.110228f},{0.303235f,-0.480936f,0.106858f}, -{0.308109f,-0.477997f,0.113964f},{0.324732f,-0.473451f,0.110299f},{0.381599f,-0.451188f,0.0953989f}, -{0.396293f,-0.44423f,0.0976946f},{0.405579f,-0.439664f,0.0950002f},{-0.338539f,-0.48983f,0.000366566f}, -{-0.279531f,-0.492312f,0.0194142f},{-0.261969f,-0.492048f,0.0198386f},{-0.244446f,-0.491875f,0.0203273f}, -{-0.181894f,-0.491965f,0.0371692f},{-0.165181f,-0.491675f,0.0379602f},{-0.147992f,-0.491328f,0.0411305f}, -{-0.128456f,-0.491045f,0.0401788f},{-0.118411f,-0.490608f,0.0418379f},{-0.0931128f,-0.461921f,0.0210219f}, -{-0.0829137f,-0.461213f,0.0224623f},{-0.070091f,-0.461676f,0.0254783f},{-0.0473393f,-0.461297f,0.0336259f}, -{-0.0361178f,-0.461374f,0.0379216f},{-0.0264976f,-0.461721f,0.0401852f},{-0.0166458f,-0.460975f,0.0395614f}, -{0.0614803f,-0.459503f,0.0548536f},{0.0783222f,-0.459792f,0.0585255f},{0.096894f,-0.472789f,0.07911f}, -{0.118077f,-0.486344f,0.0929231f},{0.135517f,-0.48875f,0.0974695f},{0.250921f,-0.475547f,0.11089f}, -{0.268162f,-0.483329f,0.113617f},{0.345143f,-0.465252f,0.1168f},{0.354924f,-0.461966f,0.1119f}, -{0.365136f,-0.457162f,0.115501f},{0.3823f,-0.449715f,0.11208f},{-0.298662f,-0.297714f,0.189081f}, -{-0.377451f,-0.269091f,0.135025f},{-0.335613f,-0.491315f,0.0186875f},{-0.318752f,-0.49237f,0.0201859f}, -{-0.301428f,-0.492531f,0.0209575f},{-0.239938f,-0.491727f,0.0356066f},{-0.223681f,-0.491913f,0.0390856f}, -{-0.204672f,-0.492016f,0.0401338f},{-0.147626f,-0.489032f,0.0596766f},{-0.128346f,-0.490955f,0.0575802f}, -{-0.113202f,-0.490486f,0.0582875f},{-0.0844828f,-0.462133f,0.0346163f},{-0.0740201f,-0.4623f,0.0407897f}, -{-0.0580978f,-0.461741f,0.0396258f},{-0.0177004f,-0.461664f,0.0535996f},{-0.00942416f,-0.461098f,0.056712f}, -{0.00554642f,-0.460982f,0.0576895f},{0.0231343f,-0.46068f,0.0580753f},{0.0440918f,-0.46023f,0.0589949f}, -{0.0782129f,-0.46032f,0.0737854f},{0.0864827f,-0.462474f,0.0754703f},{0.105678f,-0.48147f,0.0965178f}, -{0.150828f,-0.487341f,0.112922f},{0.255172f,-0.481136f,0.12071f},{0.268432f,-0.482853f,0.130806f}, -{0.284476f,-0.48138f,0.126877f},{0.290823f,-0.47839f,0.133282f},{0.307112f,-0.474589f,0.132375f}, -{0.326578f,-0.469232f,0.129153f},{0.340249f,-0.464892f,0.129179f},{0.393482f,-0.443285f,0.114183f}, -{0.403733f,-0.438372f,0.117604f},{-0.293917f,-0.294312f,0.19991f},{-0.24359f,-0.287939f,0.232102f}, -{-0.352487f,-0.486274f,0.0193949f},{-0.296714f,-0.49201f,0.0359088f},{-0.280348f,-0.491984f,0.039227f}, -{-0.261236f,-0.491907f,0.0401466f},{-0.205444f,-0.488518f,0.0571493f},{-0.186203f,-0.487148f,0.0540176f}, -{-0.166435f,-0.486878f,0.0577859f},{-0.108617f,-0.490447f,0.0739076f},{-0.0683805f,-0.462699f,0.0558889f}, -{-0.0517765f,-0.462223f,0.0588341f},{-0.0363622f,-0.461889f,0.055831f},{-0.0263046f,-0.461445f,0.0574837f}, -{0.0231086f,-0.460795f,0.075676f},{0.04069f,-0.460532f,0.0761005f},{0.0583614f,-0.460101f,0.0763898f}, -{0.0958972f,-0.471734f,0.0962542f},{0.117408f,-0.484872f,0.114479f},{0.135992f,-0.487939f,0.114344f}, -{0.24377f,-0.476043f,0.125482f},{0.252632f,-0.483894f,0.134382f},{0.348654f,-0.459426f,0.137687f}, -{0.363053f,-0.45412f,0.134652f},{0.381997f,-0.44605f,0.132388f},{0.401579f,-0.43623f,0.133063f}, -{-0.352249f,-0.485689f,0.0385068f},{-0.336179f,-0.490563f,0.0381017f},{-0.317935f,-0.491489f,0.0414971f}, -{-0.260194f,-0.491901f,0.0559918f},{-0.242291f,-0.492235f,0.0587827f},{-0.223681f,-0.491283f,0.0605511f}, -{-0.142507f,-0.487026f,0.0685895f},{-0.129999f,-0.486968f,0.0777146f},{-0.0948298f,-0.48956f,0.0805505f}, -{-0.0349732f,-0.462262f,0.0750973f},{-0.0164529f,-0.461857f,0.0784991f},{-0.0175332f,-0.461394f,0.0708723f}, -{0.00209959f,-0.461381f,0.0748401f},{0.0640912f,-0.460448f,0.0936112f},{0.0788045f,-0.460641f,0.0956111f}, -{0.0865277f,-0.462455f,0.0929745f},{0.0987974f,-0.471065f,0.114922f},{0.136751f,-0.486602f,0.130787f}, -{0.154899f,-0.486119f,0.135205f},{0.170982f,-0.483753f,0.135179f},{0.230079f,-0.475168f,0.13361f}, -{0.233385f,-0.481219f,0.139745f},{0.23325f,-0.484184f,0.150188f},{0.249886f,-0.482955f,0.150516f}, -{0.263995f,-0.481033f,0.151674f},{0.273769f,-0.477727f,0.152632f},{0.289132f,-0.47401f,0.149275f}, -{0.308122f,-0.468699f,0.15069f},{0.326147f,-0.464795f,0.146735f},{0.343587f,-0.456024f,0.155088f}, -{0.416781f,-0.428198f,0.134568f},{0.426806f,-0.422732f,0.130735f},{-0.373091f,-0.224449f,0.226289f}, -{-0.315472f,-0.490749f,0.0568856f},{-0.298354f,-0.49129f,0.058011f},{-0.281107f,-0.491585f,0.0589306f}, -{-0.194756f,-0.478872f,0.0621974f},{-0.184139f,-0.477367f,0.0630012f},{-0.170737f,-0.476776f,0.0651941f}, -{-0.161541f,-0.477914f,0.0680943f},{-0.146069f,-0.477573f,0.0755796f},{-0.109678f,-0.489811f,0.0959969f}, -{-0.0912672f,-0.489155f,0.0983313f},{-0.0687599f,-0.463483f,0.0739848f},{-0.0555513f,-0.463246f,0.076467f}, -{-0.00233756f,-0.461567f,0.0857722f},{0.00869102f,-0.461381f,0.093669f},{0.0229478f,-0.461175f,0.0934825f}, -{0.0410951f,-0.460885f,0.0969101f},{0.0883026f,-0.463213f,0.105714f},{0.11344f,-0.475991f,0.135925f}, -{0.122109f,-0.482808f,0.134105f},{0.178042f,-0.484891f,0.15222f},{0.193663f,-0.485393f,0.151783f}, -{0.210858f,-0.485039f,0.154966f},{0.331362f,-0.461181f,0.153352f},{0.363818f,-0.448603f,0.153333f}, -{0.38291f,-0.440153f,0.150812f},{0.400138f,-0.430475f,0.153699f},{-0.428459f,0.0276519f,0.314106f}, -{-0.412016f,-0.00104821f,0.266345f},{-0.337407f,-0.489116f,0.0595544f},{-0.278824f,-0.491193f,0.0745893f}, -{-0.260316f,-0.491592f,0.0776052f},{-0.244568f,-0.491978f,0.0792708f},{-0.222562f,-0.486126f,0.0746729f}, -{-0.204357f,-0.476557f,0.0697534f},{-0.133214f,-0.478042f,0.0855921f},{-0.126983f,-0.477309f,0.0939198f}, -{-0.120919f,-0.481534f,0.0970387f},{-0.0656217f,-0.463863f,0.0924022f},{-0.0524196f,-0.463619f,0.094878f}, -{-0.033687f,-0.46241f,0.0964406f},{-0.0134626f,-0.462062f,0.0955982f},{-0.000260458f,-0.461818f,0.0980676f}, -{0.0259766f,-0.461683f,0.103296f},{0.0439118f,-0.460802f,0.111971f},{0.0603614f,-0.460583f,0.115135f}, -{0.0776984f,-0.460763f,0.115906f},{0.100746f,-0.465863f,0.131996f},{0.138301f,-0.483026f,0.15004f}, -{0.154397f,-0.484685f,0.154805f},{0.213385f,-0.484042f,0.170323f},{0.23134f,-0.483071f,0.170194f}, -{0.247146f,-0.481367f,0.169937f},{0.256587f,-0.478673f,0.17139f},{0.269403f,-0.473072f,0.168972f}, -{0.286984f,-0.466371f,0.170329f},{0.305608f,-0.464628f,0.162876f},{0.324809f,-0.454744f,0.171068f}, -{0.41134f,-0.426051f,0.149371f},{0.422993f,-0.418765f,0.150793f},{-0.31908f,-0.48965f,0.0752645f}, -{-0.301376f,-0.490338f,0.0783705f},{-0.240169f,-0.491553f,0.0943635f},{-0.229887f,-0.491746f,0.0897528f}, -{-0.111742f,-0.488872f,0.114209f},{-0.0909585f,-0.488711f,0.115405f},{-0.0327417f,-0.46257f,0.112479f}, -{-0.0152182f,-0.462204f,0.113977f},{0.00272336f,-0.461792f,0.11417f},{0.00360437f,-0.461529f,0.117983f}, -{0.0219253f,-0.461175f,0.113656f},{0.0347416f,-0.461631f,0.116684f},{0.0799299f,-0.460345f,0.131706f}, -{0.119517f,-0.469618f,0.152793f},{0.131034f,-0.475798f,0.156812f},{0.159869f,-0.482943f,0.169107f}, -{0.17403f,-0.484325f,0.171294f},{0.191412f,-0.484415f,0.172001f},{0.306006f,-0.458043f,0.174535f}, -{0.343889f,-0.447998f,0.173319f},{0.361008f,-0.444462f,0.167795f},{0.383148f,-0.432192f,0.169126f}, -{-0.221604f,-0.246654f,0.267053f},{-0.212086f,-0.27158f,0.254243f},{-0.347523f,-0.483804f,0.0699656f}, -{-0.335864f,-0.485766f,0.0786727f},{-0.295048f,-0.489753f,0.0946272f},{-0.2782f,-0.490775f,0.0961513f}, -{-0.259956f,-0.491187f,0.09455f},{-0.121491f,-0.476673f,0.109926f},{-0.119041f,-0.484312f,0.117276f}, -{-0.109614f,-0.488743f,0.130144f},{-0.0914151f,-0.486479f,0.131835f},{-0.0691071f,-0.469805f,0.118343f}, -{-0.0545481f,-0.463676f,0.117726f},{0.0204205f,-0.461355f,0.129018f},{0.0282659f,-0.461664f,0.132832f}, -{0.0416546f,-0.461066f,0.135025f},{0.0579113f,-0.460545f,0.135931f},{0.0933378f,-0.46014f,0.138928f}, -{0.107672f,-0.463901f,0.145211f},{0.138217f,-0.469721f,0.169634f},{0.154571f,-0.477014f,0.175306f}, -{0.19381f,-0.483689f,0.187557f},{0.21271f,-0.48338f,0.189968f},{0.231385f,-0.481907f,0.193621f}, -{0.242246f,-0.481103f,0.188142f},{0.25141f,-0.474898f,0.192212f},{0.269094f,-0.46702f,0.185602f}, -{0.293344f,-0.459561f,0.179094f},{0.362461f,-0.439735f,0.175557f},{0.396074f,-0.425543f,0.169737f}, -{0.399997f,-0.420784f,0.174039f},{-0.430259f,0.00304813f,0.26468f},{-0.316598f,-0.265516f,0.229362f}, -{-0.327922f,-0.484139f,0.0914633f},{-0.316308f,-0.486891f,0.0962863f},{-0.259879f,-0.491309f,0.111868f}, -{-0.243944f,-0.492023f,0.115803f},{-0.121466f,-0.482885f,0.132896f},{-0.0500402f,-0.463522f,0.133031f}, -{-0.0322401f,-0.462815f,0.133121f},{-0.0136877f,-0.461934f,0.134967f},{0.00335356f,-0.4614f,0.13332f}, -{0.0129867f,-0.461741f,0.135603f},{0.0622906f,-0.460062f,0.151121f},{0.080663f,-0.459496f,0.153217f}, -{0.0981094f,-0.459451f,0.153834f},{0.111459f,-0.460969f,0.155018f},{0.171084f,-0.474621f,0.19274f}, -{0.175554f,-0.481579f,0.186605f},{0.286907f,-0.455587f,0.193036f},{0.306296f,-0.449124f,0.1871f}, -{0.32598f,-0.444892f,0.184393f},{0.344127f,-0.435838f,0.190219f},{0.362062f,-0.430269f,0.190386f}, -{0.375483f,-0.42753f,0.184849f},{-0.33396f,0.239008f,-0.169165f},{-0.281917f,-0.48992f,0.114646f}, -{-0.242105f,-0.492582f,0.133816f},{-0.124565f,-0.478551f,0.13415f},{-0.109177f,-0.488544f,0.15204f}, -{-0.092579f,-0.485328f,0.150625f},{-0.0680782f,-0.46468f,0.133256f},{-0.00444683f,-0.461831f,0.143732f}, -{0.0050577f,-0.461207f,0.152079f},{0.0233336f,-0.460853f,0.154329f},{0.0421433f,-0.460275f,0.151847f}, -{0.0987396f,-0.45839f,0.170458f},{0.117716f,-0.458667f,0.170856f},{0.155477f,-0.467522f,0.186187f}, -{0.194383f,-0.483161f,0.209299f},{0.211906f,-0.482981f,0.209807f},{0.230086f,-0.48156f,0.209357f}, -{0.24867f,-0.473457f,0.211678f},{0.260477f,-0.467548f,0.20301f},{0.310321f,-0.438082f,0.194566f}, -{0.386248f,-0.417376f,0.189582f},{-0.335459f,0.246854f,-0.208836f},{-0.402743f,-0.236642f,0.149744f}, -{-0.313723f,-0.482608f,0.110054f},{-0.279338f,-0.489071f,0.129816f},{-0.261487f,-0.490685f,0.133816f}, -{-0.121517f,-0.483174f,0.150477f},{-0.069718f,-0.465033f,0.149481f},{-0.0525546f,-0.463104f,0.153577f}, -{-0.0356162f,-0.462377f,0.154079f},{-0.0186843f,-0.462043f,0.152625f},{-0.00848528f,-0.461329f,0.154066f}, -{0.0423491f,-0.460011f,0.169075f},{0.0605543f,-0.459387f,0.173403f},{0.0767017f,-0.458905f,0.172213f}, -{0.1307f,-0.457689f,0.179287f},{0.136205f,-0.457239f,0.186984f},{0.151439f,-0.458705f,0.193138f}, -{0.173747f,-0.470255f,0.209022f},{0.184351f,-0.479573f,0.209839f},{0.211758f,-0.483374f,0.227562f}, -{0.2268f,-0.481708f,0.226256f},{0.23707f,-0.47875f,0.219896f},{0.37212f,-0.421279f,0.196154f}, -{-0.323961f,-0.233658f,0.25414f},{-0.285865f,-0.251638f,0.255098f},{-0.468824f,0.0225716f,0.22699f}, -{-0.299254f,-0.277387f,0.224662f},{-0.241147f,-0.490351f,0.150297f},{-0.230549f,-0.492048f,0.152844f}, -{-0.127626f,-0.480448f,0.158349f},{-0.123774f,-0.487624f,0.171255f},{-0.108836f,-0.486975f,0.174946f}, -{-0.0972541f,-0.48475f,0.168445f},{-0.0860262f,-0.472049f,0.170927f},{-0.0317193f,-0.462127f,0.16739f}, -{-0.0195074f,-0.461664f,0.163686f},{-0.0138999f,-0.461001f,0.171197f},{0.00345646f,-0.460332f,0.174149f}, -{0.020472f,-0.460217f,0.170593f},{0.0814025f,-0.458352f,0.187203f},{0.0982058f,-0.457516f,0.189885f}, -{0.116713f,-0.456294f,0.193749f},{0.163426f,-0.461734f,0.201215f},{0.172673f,-0.473039f,0.224829f}, -{0.190962f,-0.483579f,0.226391f},{0.3207f,-0.423974f,0.199479f},{-0.350211f,-0.23589f,0.23906f}, -{-0.372802f,-0.0054532f,0.268416f},{-0.354828f,-0.243812f,0.226546f},{-0.445172f,0.00870071f,0.23097f}, -{-0.465107f,0.026012f,0.20773f},{-0.383052f,-0.00770392f,0.261021f},{-0.278547f,-0.488486f,0.151063f}, -{-0.261725f,-0.489521f,0.152651f},{-0.138005f,-0.479644f,0.167017f},{-0.131626f,-0.47994f,0.168599f}, -{-0.0711907f,-0.463959f,0.171358f},{-0.0532813f,-0.462229f,0.17348f},{-0.0302338f,-0.461464f,0.172908f}, -{0.0242275f,-0.459458f,0.188058f},{0.0425677f,-0.458963f,0.190219f},{0.0601042f,-0.458538f,0.191704f}, -{0.137774f,-0.454886f,0.20726f},{0.15459f,-0.455985f,0.208778f},{0.166075f,-0.460931f,0.212662f}, -{0.177631f,-0.48212f,0.233947f},{0.193913f,-0.484312f,0.24506f},{0.212652f,-0.483058f,0.246622f}, -{0.225128f,-0.480923f,0.248449f},{0.233648f,-0.477187f,0.246847f},{-0.355857f,-0.28183f,0.153378f}, -{-0.389238f,-0.260448f,0.134658f},{-0.29737f,-0.260757f,0.243446f},{-0.366872f,-0.275046f,0.145224f}, -{-0.296643f,-0.483894f,0.154587f},{-0.259275f,-0.488955f,0.167995f},{-0.24215f,-0.488672f,0.171326f}, -{-0.226247f,-0.489778f,0.173358f},{-0.173753f,-0.489907f,0.179936f},{-0.164159f,-0.489193f,0.188155f}, -{-0.146957f,-0.487881f,0.188341f},{-0.128861f,-0.487406f,0.192856f},{-0.109312f,-0.485245f,0.189383f}, -{-0.0912736f,-0.46994f,0.193383f},{-0.0498216f,-0.461586f,0.186348f},{-0.0412624f,-0.461799f,0.182515f}, -{-0.0314492f,-0.460648f,0.190425f},{-0.0145751f,-0.460024f,0.191029f},{0.00553356f,-0.459869f,0.190386f}, -{0.0617761f,-0.457394f,0.206656f},{0.0800842f,-0.456963f,0.208874f},{0.0986302f,-0.456082f,0.210733f}, -{0.119144f,-0.455124f,0.209453f},{0.156075f,-0.45668f,0.222276f},{0.1574f,-0.484126f,0.250918f}, -{0.175335f,-0.48446f,0.247175f},{-0.319626f,-0.20782f,0.27165f},{-0.329877f,-0.29554f,0.165345f}, -{-0.318694f,-0.29873f,0.170175f},{-0.451796f,0.0128677f,0.223247f},{-0.303878f,-0.310151f,0.0028488f}, -{-0.280785f,-0.321527f,0.0187711f},{-0.278489f,-0.302846f,0.191081f},{-0.360262f,-0.250693f,0.212096f}, -{-0.298862f,-0.485206f,0.169969f},{-0.280046f,-0.487785f,0.172785f},{-0.223237f,-0.487393f,0.190052f}, -{-0.205553f,-0.488795f,0.189325f},{-0.185952f,-0.488698f,0.193376f},{-0.113517f,-0.484608f,0.203151f}, -{-0.0732742f,-0.462783f,0.191261f},{-0.0518279f,-0.461342f,0.191479f},{0.00290986f,-0.458802f,0.206257f}, -{0.0227742f,-0.458345f,0.209923f},{0.0404006f,-0.457934f,0.210283f},{0.116302f,-0.454487f,0.225697f}, -{0.13398f,-0.453992f,0.225973f},{0.142211f,-0.48338f,0.250995f},{0.15612f,-0.484962f,0.26394f}, -{0.17347f,-0.484872f,0.265689f},{0.192113f,-0.483811f,0.267413f},{0.207804f,-0.482261f,0.267342f}, -{0.217199f,-0.479657f,0.268879f},{-0.414048f,-0.00264297f,0.20955f},{-0.455635f,0.0113823f,0.240758f}, -{-0.401682f,-0.00607055f,0.25549f},{-0.316006f,-0.307752f,0.0176329f},{-0.299061f,-0.317032f,0.0229575f}, -{-0.281904f,-0.324832f,0.0377994f},{-0.261352f,-0.487695f,0.187943f},{-0.241513f,-0.487174f,0.191672f}, -{-0.165979f,-0.486139f,0.206688f},{-0.146667f,-0.486634f,0.211189f},{-0.128224f,-0.486563f,0.209337f}, -{-0.106476f,-0.478358f,0.212508f},{-0.0748819f,-0.461889f,0.204566f},{-0.0675123f,-0.461014f,0.207228f}, -{-0.052889f,-0.460262f,0.208797f},{-0.0343944f,-0.459464f,0.210733f},{-0.0158484f,-0.45931f,0.208643f}, -{0.0431722f,-0.456564f,0.225253f},{0.0604064f,-0.456101f,0.228379f},{0.0771647f,-0.45569f,0.22915f}, -{0.0974213f,-0.455252f,0.228295f},{0.117942f,-0.484197f,0.266962f},{0.133774f,-0.484261f,0.266236f}, -{0.319678f,-0.462101f,0.0100833f},{-0.443185f,0.00970388f,0.279348f},{-0.382152f,-0.251735f,0.178387f}, -{-0.313729f,-0.311791f,0.0269573f},{-0.312694f,-0.315971f,0.0396258f},{-0.29856f,-0.32015f,0.0378252f}, -{-0.282194f,-0.328124f,0.0543841f},{-0.275146f,-0.331256f,0.0615222f},{-0.298875f,-0.486126f,0.189126f}, -{-0.282123f,-0.487528f,0.192798f},{-0.220871f,-0.486287f,0.207678f},{-0.202588f,-0.485798f,0.20998f}, -{-0.184055f,-0.485541f,0.208038f},{-0.129433f,-0.485984f,0.226739f},{-0.117099f,-0.484383f,0.226829f}, -{-0.0914215f,-0.464364f,0.209132f},{-0.0150767f,-0.458095f,0.225035f},{0.0032764f,-0.457169f,0.22915f}, -{0.0200283f,-0.456757f,0.229909f},{0.0820456f,-0.4543f,0.243561f},{0.100437f,-0.453754f,0.239143f}, -{0.101254f,-0.48284f,0.26522f},{0.139619f,-0.48448f,0.283238f},{0.155567f,-0.484357f,0.282377f}, -{0.174236f,-0.482827f,0.286017f},{0.19356f,-0.481251f,0.28282f},{0.213694f,-0.473342f,0.284544f}, -{-0.242465f,-0.257683f,0.258429f},{-0.335754f,-0.305109f,0.0369313f},{-0.32342f,-0.309578f,0.0344619f}, -{-0.300225f,-0.323256f,0.0579403f},{-0.311357f,-0.484068f,0.191132f},{-0.277576f,-0.487013f,0.207569f}, -{-0.25948f,-0.486852f,0.210135f},{-0.241854f,-0.486299f,0.210591f},{-0.185361f,-0.485168f,0.225523f}, -{-0.167522f,-0.485534f,0.228469f},{-0.149304f,-0.485862f,0.22697f},{-0.100997f,-0.468261f,0.221974f}, -{-0.0878911f,-0.461992f,0.219286f},{-0.0706183f,-0.459451f,0.225189f},{-0.0522459f,-0.4585f,0.229279f}, -{-0.0360921f,-0.45814f,0.230366f},{0.0267998f,-0.455291f,0.245587f},{0.0428314f,-0.45495f,0.244558f}, -{0.0601749f,-0.454275f,0.24751f},{0.0805086f,-0.483103f,0.272724f},{0.100457f,-0.484537f,0.283888f}, -{0.11845f,-0.48464f,0.286525f},{0.194846f,-0.476898f,0.290769f},{-0.349381f,-0.00533103f,0.254024f}, -{-0.417945f,0.0292209f,0.315366f},{-0.35014f,-0.296383f,0.0380181f},{-0.328983f,-0.311906f,0.0467638f}, -{-0.319491f,-0.316652f,0.0564419f},{-0.299132f,-0.326459f,0.075496f},{-0.28065f,-0.331513f,0.0751037f}, -{-0.259783f,-0.338375f,0.0941963f},{-0.314077f,-0.483554f,0.206939f},{-0.300154f,-0.486422f,0.211247f}, -{-0.241365f,-0.485901f,0.227195f},{-0.223919f,-0.485676f,0.227915f},{-0.206376f,-0.48529f,0.228481f}, -{-0.145735f,-0.485734f,0.244552f},{-0.127813f,-0.485907f,0.247388f},{-0.113614f,-0.485888f,0.249619f}, -{-0.0850616f,-0.460905f,0.230385f},{-0.0312048f,-0.45677f,0.244764f},{-0.0144465f,-0.456352f,0.245529f}, -{0.00379085f,-0.455651f,0.249831f},{0.0451786f,-0.482885f,0.284441f},{0.0596797f,-0.483939f,0.285316f}, -{0.0820391f,-0.484435f,0.285759f},{0.135118f,-0.482891f,0.295347f},{0.152448f,-0.480988f,0.297219f}, -{0.155619f,-0.473052f,0.30219f},{0.175303f,-0.470782f,0.298473f},{-0.256394f,-0.259394f,0.256828f}, -{-0.341291f,-0.307823f,0.0568599f},{-0.331067f,-0.314022f,0.0603389f},{-0.315787f,-0.320806f,0.0768979f}, -{-0.290624f,-0.329906f,0.0832063f},{-0.278303f,-0.333411f,0.0946529f},{-0.297794f,-0.486184f,0.226649f}, -{-0.280695f,-0.486621f,0.22787f},{-0.263139f,-0.486177f,0.228424f},{-0.201752f,-0.485181f,0.243426f}, -{-0.185348f,-0.484968f,0.246744f},{-0.16666f,-0.485489f,0.248352f},{-0.109061f,-0.485309f,0.264403f}, -{-0.0923604f,-0.485817f,0.270088f},{-0.0695573f,-0.45857f,0.245883f},{-0.0523038f,-0.456828f,0.247941f}, -{0.0218867f,-0.454416f,0.258217f},{0.0355583f,-0.453278f,0.26003f},{0.0248191f,-0.483174f,0.288042f}, -{0.0613839f,-0.48466f,0.301817f},{0.0791196f,-0.483997f,0.302061f},{0.0968168f,-0.483444f,0.302344f}, -{0.118443f,-0.481592f,0.302299f},{0.136578f,-0.474036f,0.304775f},{-0.285788f,-0.286988f,0.217832f}, -{-0.361201f,-0.288962f,0.044198f},{-0.353516f,-0.299328f,0.0592907f},{-0.334938f,-0.313469f,0.075631f}, -{-0.300444f,-0.327224f,0.0950195f},{-0.289094f,-0.33123f,0.0962799f},{-0.26431f,-0.335848f,0.111283f}, -{-0.312752f,-0.483721f,0.229716f},{-0.258432f,-0.485978f,0.243246f},{-0.242124f,-0.485926f,0.24668f}, -{-0.223096f,-0.4852f,0.249008f},{-0.165155f,-0.484962f,0.263406f},{-0.147092f,-0.48484f,0.26603f}, -{-0.129935f,-0.485161f,0.267162f},{-0.0487541f,-0.456423f,0.25695f},{-0.0353011f,-0.455644f,0.259033f}, -{-0.0153146f,-0.454911f,0.262506f},{0.00496123f,-0.453651f,0.265573f},{0.00542424f,-0.483746f,0.291965f}, -{0.0242018f,-0.484139f,0.303103f},{0.0405871f,-0.484306f,0.304479f},{0.101897f,-0.480113f,0.309109f}, -{-0.335484f,0.250577f,-0.225986f},{-0.362898f,-0.290209f,0.0564741f},{-0.354339f,-0.30028f,0.0768464f}, -{-0.334616f,-0.314601f,0.0951159f},{-0.31888f,-0.321205f,0.0966464f},{-0.298135f,-0.325674f,0.114292f}, -{-0.278232f,-0.331443f,0.114922f},{-0.298148f,-0.485489f,0.24551f},{-0.279885f,-0.486229f,0.248931f}, -{-0.220935f,-0.485341f,0.264673f},{-0.203469f,-0.485058f,0.265387f},{-0.185444f,-0.485007f,0.268075f}, -{-0.126944f,-0.483637f,0.281663f},{-0.108611f,-0.48428f,0.285001f},{-0.0926562f,-0.484164f,0.286441f}, -{-0.0740266f,-0.484402f,0.286891f},{-0.0540337f,-0.483515f,0.290383f},{-0.0409344f,-0.48338f,0.293058f}, -{-0.0318029f,-0.483586f,0.301965f},{-0.0139385f,-0.483849f,0.304878f},{0.00431173f,-0.484074f,0.303334f}, -{0.0427028f,-0.482505f,0.318678f},{0.0602649f,-0.482962f,0.314331f},{0.0708948f,-0.481316f,0.315218f}, -{0.0968618f,-0.47529f,0.315025f},{-0.306276f,-0.2129f,0.274448f},{-0.368274f,-0.235009f,0.223163f}, -{-0.343388f,-0.30862f,0.0938362f},{-0.316758f,-0.31979f,0.114929f},{-0.283467f,-0.329102f,0.11871f}, -{-0.263911f,-0.331243f,0.130414f},{-0.311948f,-0.482968f,0.245992f},{-0.279628f,-0.485631f,0.266242f}, -{-0.260149f,-0.485907f,0.265136f},{-0.242015f,-0.48565f,0.267689f},{-0.183419f,-0.483496f,0.282338f}, -{-0.167593f,-0.48246f,0.280197f},{-0.148552f,-0.481676f,0.279997f},{-0.0880133f,-0.48347f,0.301084f}, -{-0.0700074f,-0.483457f,0.30381f},{-0.0527539f,-0.483599f,0.304813f},{0.00475545f,-0.483721f,0.320067f}, -{0.0216552f,-0.482628f,0.322813f},{-0.315678f,-0.293425f,0.183177f},{-0.3545f,-0.298074f,0.0959712f}, -{-0.331002f,-0.314266f,0.112832f},{-0.299164f,-0.320819f,0.132941f},{-0.279917f,-0.325578f,0.134388f}, -{-0.309144f,-0.482165f,0.257034f},{-0.299048f,-0.483946f,0.266159f},{-0.240542f,-0.485373f,0.28266f}, -{-0.223237f,-0.485193f,0.284589f},{-0.204627f,-0.483972f,0.28646f},{-0.123247f,-0.481599f,0.296454f}, -{-0.107987f,-0.482968f,0.305257f},{-0.0522073f,-0.483193f,0.321269f},{-0.0351532f,-0.483669f,0.322601f}, -{-0.0178998f,-0.483727f,0.323604f},{0.0305231f,-0.480197f,0.328266f},{-0.328674f,-0.273869f,0.208707f}, -{-0.40064f,-0.25104f,0.0488023f},{-0.362519f,-0.289849f,0.0907431f},{-0.341458f,-0.307572f,0.116331f}, -{-0.455841f,0.0126491f,0.268384f},{-0.279165f,-0.484795f,0.283148f},{-0.261506f,-0.485431f,0.286383f}, -{-0.184827f,-0.479534f,0.292415f},{-0.117035f,-0.480132f,0.307701f},{-0.0909135f,-0.482885f,0.320941f}, -{-0.0729848f,-0.482994f,0.32379f},{-0.0158548f,-0.482525f,0.33678f},{0.00325711f,-0.481226f,0.334362f}, -{-0.38266f,-0.270782f,0.0813672f},{-0.332115f,-0.310569f,0.126498f},{-0.307389f,-0.476396f,0.27412f}, -{-0.294328f,-0.482017f,0.281894f},{-0.222954f,-0.482865f,0.298061f},{-0.101678f,-0.480898f,0.322433f}, -{-0.186711f,0.248082f,-0.0135751f},{-0.337054f,0.254307f,-0.24396f},{-0.202891f,0.23162f,0.00283594f}, -{-0.286515f,0.364336f,-0.372927f},{-0.335414f,0.260551f,-0.265149f},{-0.052098f,0.307945f,-0.0569499f}, -{-0.164152f,0.26057f,-0.024578f},{-0.305337f,0.251098f,-0.166741f},{-0.313472f,0.285663f,-0.283026f}, -{-0.302617f,0.215832f,-0.0542812f},{-0.316019f,0.243555f,-0.169171f},{-0.300804f,0.227684f,-0.0737082f}, -{-0.181181f,0.274711f,-0.031671f},{-0.0531462f,0.322594f,-0.0754188f},{-0.312958f,0.259509f,-0.210025f}, -{-0.302566f,0.23205f,-0.0907302f},{0.0233979f,0.356484f,-0.115662f},{-0.316771f,0.22252f,-0.0919199f}, -{0.00642742f,0.340221f,-0.0927558f},{-0.319633f,0.270544f,-0.262069f},{-0.31789f,0.215247f,-0.0716247f}, -{-0.182493f,0.241632f,-0.00751742f},{-0.145638f,0.259972f,-0.0193563f},{-0.318405f,0.226957f,-0.111032f}, -{-0.165168f,0.271027f,-0.0328992f},{-0.109562f,0.2732f,-0.0165396f},{-0.107003f,0.281837f,-0.0365711f}, -{-0.323716f,0.274055f,-0.296479f},{-0.16659f,0.243259f,0.0006045f},{-0.319781f,0.265651f,-0.243709f}, -{-0.148924f,0.270326f,-0.0333044f},{-0.0362722f,0.324607f,-0.0756438f},{-0.311234f,0.220604f,-0.0803125f}, -{-0.3227f,0.271239f,-0.284229f},{-0.323588f,0.256815f,-0.22843f},{-0.220781f,0.206829f,0.0185396f}, -{-0.167914f,0.251284f,-0.0151699f},{-0.0329861f,0.310312f,-0.0527443f},{-0.127337f,0.273367f,-0.0322948f}, -{-0.317382f,0.284158f,-0.302556f},{-0.127871f,0.280666f,-0.0416578f},{-0.187341f,0.234301f,0.00378125f}, -{0.0259895f,0.366509f,-0.128723f},{0.0123822f,0.333057f,-0.0819716f},{-0.313787f,0.265008f,-0.224777f}, -{-0.309061f,0.301977f,-0.319835f},{-0.303871f,0.269368f,-0.2069f},{-0.0717501f,0.30253f,-0.055368f}, -{-0.333034f,0.229555f,-0.136619f},{-0.316327f,0.232745f,-0.128478f},{-0.338886f,0.200733f,-0.0441529f}, -{0.00223463f,0.350227f,-0.103682f},{0.025295f,0.350658f,-0.108054f},{-0.0903219f,0.299785f,-0.0571364f}, -{-0.209681f,0.219363f,0.0212341f},{-0.337054f,0.234758f,-0.152272f},{-0.301788f,0.250449f,-0.149892f}, -{-0.313421f,0.313733f,-0.349333f},{-0.353355f,0.231812f,-0.127121f},{-0.0154754f,0.325109f,-0.0739269f}, -{-0.312546f,0.202521f,-0.0423137f},{-0.109736f,0.29165f,-0.0513167f},{-0.0731263f,0.292106f,-0.0378766f}, -{-0.305575f,0.235472f,-0.111611f},{-0.311228f,0.241394f,-0.150471f},{-0.305884f,0.31271f,-0.343847f}, -{-0.336295f,0.224366f,-0.114556f},{-0.0516286f,0.299514f,-0.0367383f},{-0.343401f,0.237722f,-0.161197f}, -{-0.0322787f,0.314794f,-0.0613163f},{-0.321163f,0.251079f,-0.204977f},{-0.309453f,0.279605f,-0.247323f}, -{-0.316295f,0.249214f,-0.188206f},{-0.0175589f,0.332697f,-0.0845503f},{-0.305434f,0.309495f,-0.323842f}, -{-0.21671f,0.219761f,0.014752f},{-0.306656f,0.273541f,-0.222578f},{-0.334147f,0.213974f,-0.0735667f}, -{-0.173252f,0.237356f,0.018829f},{-0.202177f,0.223048f,0.0189447f},{-0.311614f,0.284152f,-0.265889f}, -{-0.332655f,0.221189f,-0.0998553f},{-0.0605351f,0.294479f,-0.0306485f},{-0.305781f,0.256326f,-0.181872f}, -{-0.0967461f,0.291045f,-0.0472396f} -}; -F32 normals [8127][3] = { -{-0.832532f,-0.483226f,0.270894f},{-0.606785f,-0.758191f,0.23866f},{-0.859166f,-0.504979f,0.0826439f}, -{-0.240595f,0.95358f,-0.181105f},{-0.265911f,0.923509f,-0.276446f},{-0.24535f,0.952364f,-0.181124f}, -{-0.25194f,0.962547f,-0.100147f},{-0.246923f,0.963593f,-0.102551f},{-0.246718f,0.967484f,-0.0557298f}, -{-0.256239f,0.966543f,-0.0116836f},{-0.268399f,0.963145f,0.0176902f},{-0.202139f,0.974793f,-0.094442f}, -{-0.233286f,0.967463f,-0.0979436f},{-0.222973f,0.972356f,-0.0693283f},{-0.216571f,0.976026f,-0.0216751f}, -{-0.248769f,0.966837f,-0.0577895f},{-0.251386f,0.966483f,-0.0521194f},{-0.221256f,0.940455f,-0.258053f}, -{-0.203433f,0.958112f,-0.201585f},{-0.185273f,0.9538f,-0.236513f},{-0.202063f,0.969567f,-0.138239f}, -{-0.277194f,0.958376f,0.0684025f},{-0.323528f,0.932333f,0.16151f},{-0.301745f,0.944805f,0.127648f}, -{-0.263034f,0.964572f,0.0203178f},{-0.298731f,0.932268f,0.204049f},{-0.339448f,0.915328f,0.21668f}, -{-0.256592f,0.921503f,0.291534f},{-0.336811f,0.892243f,0.300767f},{-0.288256f,0.886991f,0.36077f}, -{-0.129143f,0.959514f,-0.25031f},{-0.104011f,0.980522f,-0.166606f},{-0.0715617f,0.949967f,-0.304043f}, -{-0.193212f,0.979873f,0.0501861f},{-0.264442f,0.963268f,0.0467423f},{-0.202161f,0.973002f,0.111346f}, -{0.780632f,0.608123f,0.144219f},{0.893498f,0.412557f,0.177367f},{0.918654f,0.348847f,0.185421f}, -{-0.235108f,0.898248f,0.371314f},{-0.0326766f,0.912646f,0.407443f},{-0.281134f,0.890492f,0.357753f}, -{-0.717587f,-0.696125f,-0.0218705f},{-0.848457f,-0.528571f,-0.0270921f},{-0.865329f,-0.500314f,-0.029859f}, -{-0.121792f,0.898055f,0.422685f},{0.101699f,0.854369f,0.509619f},{-0.140896f,0.896477f,0.420093f}, -{-0.808979f,-0.581233f,0.0878659f},{-0.770083f,-0.637941f,0.00173141f},{-0.756549f,-0.652533f,0.0428222f}, -{0.0971026f,0.799445f,0.592839f},{-0.145356f,0.83366f,0.532806f},{-0.00267177f,0.775686f,0.631113f}, -{-4.50738e-05f,-0.710945f,-0.703248f},{-0.0235271f,-0.794254f,-0.60713f},{0.259285f,-0.884284f,-0.388348f}, -{-0.945709f,0.157642f,0.284225f},{-0.904527f,0.212372f,0.36977f},{-0.885759f,0.334308f,0.321977f}, -{-0.263036f,0.950565f,0.16504f},{-0.323719f,0.90817f,0.265394f},{-0.28809f,0.926484f,0.242139f}, -{-0.95459f,0.0441123f,-0.29464f},{-0.969511f,-0.169456f,-0.177012f},{-0.954454f,-0.223864f,-0.197238f}, -{0.611328f,0.777271f,0.148758f},{0.684309f,0.653679f,0.323149f},{0.915668f,0.320355f,0.242746f}, -{0.819723f,0.357047f,0.447853f},{0.422096f,0.860388f,0.285599f},{0.187582f,0.923427f,0.334806f}, -{0.432844f,0.790824f,0.432717f},{0.0680594f,-0.941994f,0.328655f},{-0.290907f,-0.832029f,0.472335f}, -{0.146264f,-0.741517f,0.654797f},{-0.735765f,-0.280452f,0.616439f},{-0.722188f,-0.547927f,0.422161f}, -{-0.768976f,-0.134488f,0.624971f},{-0.453323f,0.851967f,-0.262013f},{-0.381382f,0.908381f,-0.171438f}, -{-0.417129f,0.849952f,-0.321845f},{0.254919f,0.880365f,0.399968f},{-0.190589f,0.854832f,0.482637f}, -{-0.210229f,0.889628f,0.405419f},{0.708171f,0.617f,0.343227f},{0.614328f,0.678828f,0.402236f}, -{0.720949f,0.588198f,0.36641f},{-0.346537f,0.869928f,0.350909f},{-0.60894f,0.719521f,0.333889f}, -{-0.33076f,0.856751f,0.395696f},{0.0127889f,0.924737f,0.380393f},{-0.142866f,0.666357f,0.731818f}, -{-0.034274f,0.677512f,0.734712f},{-0.0935795f,0.661009f,0.74452f},{-0.509054f,0.763082f,0.398208f}, -{0.224064f,-0.899254f,-0.375683f},{-0.272324f,-0.820453f,-0.50269f},{0.575326f,-0.786607f,-0.224161f}, -{-0.0317741f,0.983694f,0.177022f},{-0.208638f,0.977128f,-0.0411155f},{-0.0326322f,0.9994f,-0.0116285f}, -{-0.959056f,-0.281756f,0.0287399f},{-0.968295f,-0.230842f,0.0954859f},{-0.932971f,-0.284521f,-0.220485f}, -{-0.936459f,-0.146232f,-0.318844f},{-0.385196f,0.795521f,0.467728f},{-0.571132f,0.811969f,-0.120481f}, -{-0.285455f,0.884252f,0.369613f},{0.770701f,0.508164f,0.384433f},{0.653749f,0.544785f,0.525187f}, -{0.911702f,0.212638f,0.351546f},{-0.149382f,0.141765f,-0.978564f},{-0.215627f,0.158924f,-0.963456f}, -{-0.172656f,0.332476f,-0.927173f},{-0.11137f,0.20172f,-0.973091f},{-0.120247f,0.218757f,-0.968342f}, -{-0.116053f,0.208667f,-0.971077f},{-0.110441f,0.873446f,0.474232f},{-0.120825f,0.884438f,0.450746f}, -{-0.869758f,0.377088f,0.318317f},{-0.925243f,0.175597f,0.33629f},{-0.919523f,0.0685275f,0.387016f}, -{-0.482565f,0.874002f,0.0570216f},{-0.328802f,0.876258f,0.352222f},{-0.120162f,0.895601f,0.428322f}, -{0.118699f,0.915735f,0.38385f},{-0.89304f,0.449975f,0.00125259f},{-0.956562f,0.289896f,0.0308044f}, -{-0.950694f,0.305572f,-0.0529737f},{-0.202962f,0.974794f,-0.0926428f},{-0.149628f,0.983888f,-0.0978547f}, -{-0.14631f,0.98241f,-0.116032f},{0.694537f,0.621044f,0.363211f},{0.620447f,0.729884f,0.286908f}, -{0.765911f,0.603359f,0.22212f},{0.852062f,0.457413f,0.254488f},{0.752783f,0.532628f,0.386814f}, -{-0.239568f,0.74193f,0.626216f},{-0.075257f,0.613884f,0.785801f},{0.123843f,0.376122f,0.918257f}, -{-0.474381f,0.23936f,-0.847154f},{-0.455168f,-0.811384f,-0.366713f},{-0.213933f,-0.514973f,-0.830082f}, -{-0.254102f,0.948463f,-0.189341f},{-0.367517f,0.829036f,0.421461f},{-0.361859f,0.854456f,0.372778f}, -{-0.322071f,0.851882f,0.412998f},{-0.13554f,0.98971f,-0.045867f},{-0.160853f,0.986362f,-0.0348832f}, -{-0.131247f,0.991314f,0.00835482f},{-0.542619f,0.828757f,0.136845f},{-0.343869f,0.809416f,0.476025f}, -{-0.462981f,0.852059f,0.244222f},{-0.263369f,0.901217f,0.344157f},{-0.338921f,0.884725f,0.319992f}, -{-0.511586f,0.803891f,0.303379f},{0.874238f,0.377572f,0.305199f},{0.829018f,0.495984f,0.25832f}, -{0.792623f,0.48816f,0.365306f},{0.195735f,0.962956f,0.18548f},{0.116284f,0.964873f,0.235578f}, -{0.190608f,0.956302f,0.221709f},{-0.25398f,0.966403f,-0.0394976f},{-0.378434f,0.83816f,0.39278f}, -{0.662297f,0.72069f,0.204862f},{0.530429f,0.822517f,0.205209f},{0.684042f,0.704221f,0.190155f}, -{-0.0569504f,0.997668f,-0.0376106f},{0.0157645f,0.999316f,-0.0334653f},{0.0157935f,0.995784f,-0.0903618f}, -{-0.422862f,-0.852884f,-0.306229f},{-0.12403f,-0.948113f,-0.292743f},{-0.622856f,-0.765261f,-0.162564f}, -{-0.901512f,-0.161439f,0.401515f},{-0.83239f,-0.442392f,0.333789f},{-0.279616f,0.315223f,-0.90689f}, -{-0.187629f,0.239955f,-0.952479f},{-0.201018f,0.207097f,-0.957446f},{-0.351005f,0.870672f,0.344566f}, -{-0.356193f,0.83271f,0.423934f},{-0.930524f,0.364666f,-0.0338037f},{-0.934408f,0.34659f,-0.0822029f}, -{-0.581141f,-0.79047f,-0.193474f},{-0.401732f,-0.866777f,-0.295481f},{0.219798f,0.943023f,0.249793f}, -{0.441648f,0.744463f,0.500721f},{-0.170662f,0.962922f,-0.208942f},{-0.140103f,0.977176f,-0.15968f}, -{-0.174618f,0.973903f,-0.14499f},{-0.816847f,-0.468775f,-0.336171f},{-0.897091f,0.0904234f,-0.432495f}, -{-0.65512f,-0.498546f,-0.567689f},{-0.207628f,0.956946f,0.202842f},{-0.168441f,0.72412f,0.668788f}, -{-0.0670168f,0.91956f,0.387192f},{0.311933f,0.593536f,0.741898f},{0.17352f,0.787655f,0.591177f}, -{0.180038f,0.58603f,0.790035f},{-0.861929f,-0.365565f,0.351341f},{-0.914147f,-0.309319f,0.262025f}, -{-0.791212f,-0.558632f,0.248825f},{-0.053992f,0.998524f,0.00585528f},{0.654485f,0.747701f,0.112219f}, -{0.34414f,0.92576f,0.15664f},{-0.219221f,0.914674f,0.339579f},{-0.258173f,0.858933f,0.442246f}, -{-0.143065f,0.905573f,0.399337f},{0.84601f,0.500754f,0.183061f},{0.317856f,0.577094f,0.752283f}, -{0.161512f,0.786348f,0.596297f},{0.21221f,0.640586f,0.737981f},{-0.166751f,0.982628f,-0.0814596f}, -{-0.188703f,0.980337f,-0.0577052f},{-0.17194f,0.985102f,-0.00323838f},{-0.234144f,0.969227f,0.0759959f}, -{-0.208683f,0.971131f,0.115569f},{-0.17246f,0.983756f,0.0498251f},{-0.837886f,-0.545703f,0.0124777f}, -{-0.881945f,-0.46478f,0.0784393f},{-0.936872f,-0.341756f,-0.073981f},{-0.152191f,0.98821f,0.0167007f}, -{0.529805f,0.839465f,0.12085f},{0.35177f,0.933835f,0.0648909f},{0.645035f,0.758214f,0.0950878f}, -{0.897254f,0.430807f,0.0966417f},{0.82479f,0.54689f,0.143639f},{0.833868f,0.533647f,0.141017f}, -{0.398204f,0.869206f,-0.293111f},{0.368214f,0.912358f,-0.178942f},{0.460623f,0.862636f,-0.209012f}, -{0.757324f,0.632608f,0.162072f},{-0.196495f,0.847757f,0.492644f},{-0.281075f,0.806466f,0.520201f}, -{-0.178493f,0.831846f,0.525522f},{-0.965141f,0.251922f,-0.070975f},{-0.968395f,0.204029f,-0.143468f}, -{-0.98048f,0.122575f,-0.153733f},{-0.181622f,0.862125f,0.473026f},{-0.24619f,0.905469f,0.345711f}, -{-0.228317f,0.86714f,0.44265f},{-0.366278f,0.419914f,-0.830369f},{-0.283198f,0.181294f,-0.94177f}, -{-0.125999f,0.144635f,-0.98143f},{-0.139033f,0.985514f,-0.0971168f},{-0.689549f,0.612864f,0.385901f}, -{-0.466431f,0.7979f,0.381836f},{-0.429588f,0.835885f,0.341687f},{0.92412f,0.353599f,-0.144809f}, -{0.860566f,0.471663f,-0.192248f},{0.897956f,0.425678f,-0.111685f},{0.207155f,0.859726f,0.466859f}, -{0.500149f,-0.865185f,-0.036124f},{0.196539f,-0.87016f,-0.451878f},{0.407742f,-0.874513f,-0.262628f}, -{-0.165294f,0.961411f,0.219924f},{-0.174258f,0.925978f,0.334962f},{0.0209707f,0.954479f,-0.297539f}, -{-0.0411579f,0.958112f,-0.28342f},{-0.0430633f,0.986192f,-0.15991f},{-0.138388f,0.984372f,0.108905f}, -{-0.0864297f,0.979549f,0.181694f},{0.355448f,-0.675269f,-0.646272f},{0.982785f,-0.167672f,0.0775822f}, -{0.964467f,-0.220328f,0.145802f},{-0.106177f,0.990299f,-0.0896399f},{0.585177f,0.809954f,0.0392717f}, -{0.351387f,0.916776f,0.189866f},{0.28604f,0.941601f,-0.177678f},{-0.330406f,0.875489f,0.352634f}, -{0.421234f,0.845704f,0.327638f},{-0.000578385f,0.935843f,0.352418f},{0.797729f,0.521056f,0.303528f}, -{0.851595f,0.355876f,0.384888f},{0.833935f,0.461972f,0.301884f},{-0.281361f,0.950692f,0.130465f}, -{0.494345f,0.869264f,0.0014269f},{0.323062f,0.915374f,0.240255f},{0.236605f,0.903551f,0.357229f}, -{0.326762f,0.897894f,0.29498f},{-0.797988f,-0.554353f,0.236451f},{-0.715613f,-0.675862f,0.176376f}, -{-0.604376f,-0.751052f,0.265801f},{-0.986977f,-0.121592f,-0.105315f},{-0.980444f,-0.196796f,-0.000786544f}, -{-0.991928f,-0.0157392f,-0.12582f},{-0.164281f,0.191446f,-0.967657f},{-0.0978269f,0.992432f,0.074216f}, -{-0.0896928f,0.987081f,0.132768f},{-0.0518716f,0.987449f,0.149177f},{-0.0994765f,0.995001f,0.00874661f}, -{-0.0983143f,0.994234f,-0.0428241f},{-0.151126f,0.986293f,0.0662396f},{-0.105553f,0.987126f,0.120173f}, -{0.969847f,-0.00525719f,-0.243657f},{0.986459f,0.017394f,-0.163085f},{0.977914f,0.0433675f,-0.204458f}, -{0.785811f,0.528888f,0.32059f},{0.773372f,0.522143f,0.35953f},{0.866441f,0.410223f,0.2846f}, -{-0.51235f,0.858582f,0.0183004f},{0.220746f,0.613517f,0.7582f},{0.359838f,0.596592f,0.717353f}, -{0.263219f,0.954822f,-0.137951f},{0.236716f,0.968405f,-0.0784723f},{0.0393797f,0.987006f,-0.155785f}, -{-0.100373f,0.924854f,0.366837f},{-0.0789204f,0.886723f,0.455514f},{-0.0325974f,0.953865f,0.298462f}, -{-0.0996998f,0.958219f,0.268097f},{-0.124818f,0.990033f,0.0652242f},{0.858404f,0.470083f,0.20534f}, -{-0.351088f,0.90292f,0.247937f},{-0.266096f,0.623714f,0.734965f},{0.0546192f,0.518634f,0.85325f}, -{-0.00627665f,0.528708f,0.848781f},{0.0310592f,0.974423f,0.222564f},{0.225176f,0.961336f,0.158522f}, -{0.238458f,0.899672f,0.365688f},{0.112761f,0.816349f,0.566444f},{0.0456267f,0.831381f,0.553826f}, -{0.0552438f,0.806235f,0.589011f},{0.00507421f,0.703522f,0.710656f},{0.0903773f,0.728552f,0.679002f}, -{-0.417529f,0.874211f,0.247839f},{-0.503292f,0.783758f,0.363895f},{-0.760404f,-0.626818f,-0.169957f}, -{0.528696f,0.762456f,0.373016f},{-0.0576661f,0.99596f,0.0688321f},{-0.0702293f,0.979726f,0.187628f}, -{-0.050923f,0.994669f,-0.0896719f},{0.550602f,-0.79647f,0.249946f},{0.435275f,-0.856718f,0.276713f}, -{0.503614f,-0.851182f,0.147859f},{0.924068f,0.378481f,-0.0533867f},{0.91027f,0.413033f,-0.0285087f}, -{0.909985f,0.413706f,0.027824f},{0.151382f,0.614505f,0.774252f},{0.121735f,0.71707f,0.686288f}, -{-0.0654101f,0.661894f,0.746738f},{0.449607f,0.836161f,-0.314146f},{0.410327f,0.856681f,0.312616f}, -{-0.296897f,0.85146f,0.432283f},{0.657107f,-0.725019f,0.206298f},{0.302314f,-0.951098f,0.0633903f}, -{0.376669f,-0.920947f,0.0998864f},{0.358164f,0.823977f,0.439068f},{0.708367f,0.624326f,0.329293f}, -{0.265183f,0.860485f,0.435022f},{0.264323f,0.827217f,0.495829f},{-0.208083f,0.974072f,-0.088802f}, -{-0.0406154f,0.999099f,-0.0123014f},{-0.21748f,0.975297f,-0.0387011f},{0.811224f,0.532897f,0.240699f}, -{0.325329f,0.935848f,0.135462f},{0.298507f,0.933061f,0.200724f},{0.370731f,0.911821f,0.176466f}, -{0.177805f,0.850282f,0.495384f},{0.0782578f,0.861702f,0.501344f},{-0.862905f,-0.0410319f,0.503697f}, -{-0.780384f,-0.483438f,0.396596f},{-0.920067f,-0.0587012f,0.387339f},{0.120623f,0.907232f,0.402964f}, -{0.0912084f,0.872958f,0.479193f},{-0.223768f,0.248002f,-0.942562f},{-0.006037f,0.829609f,0.558313f}, -{-0.086073f,0.83628f,0.541505f},{-0.0751572f,0.794053f,0.603184f},{-0.1793f,0.932707f,0.312905f}, -{-0.35696f,0.931368f,0.0716437f},{0.723383f,0.662743f,0.193622f},{0.748428f,0.357617f,0.558539f}, -{0.382063f,0.804684f,0.454436f},{0.41987f,0.797587f,0.433088f},{0.60187f,0.723171f,0.338786f}, -{0.465439f,0.84667f,0.257909f},{0.604528f,0.760774f,0.236153f},{-0.14065f,0.878178f,0.457187f}, -{-0.156203f,0.926759f,0.341641f},{0.30724f,0.899647f,0.310225f},{0.983246f,0.063408f,-0.1709f}, -{0.96155f,0.0864831f,-0.260656f},{0.979863f,0.0529409f,-0.192523f},{0.0766483f,-0.724763f,-0.684722f}, -{0.0795982f,-0.728566f,-0.680335f},{0.240247f,-0.855735f,-0.458256f},{-0.168843f,0.172447f,-0.97044f}, -{-0.0397846f,0.99753f,-0.0578866f},{-0.187576f,0.976181f,-0.109024f},{-0.315613f,0.922592f,0.221838f}, -{-0.344408f,0.911946f,0.223021f},{-0.0535464f,0.762474f,0.6448f},{-0.150055f,0.814483f,0.560447f}, -{-0.11104f,0.754474f,0.646868f},{-0.838926f,0.210014f,-0.502093f},{-0.83238f,0.28807f,-0.473454f}, -{-0.797072f,0.470878f,-0.378087f},{-0.86169f,0.262894f,-0.434025f},{-0.815344f,0.425554f,-0.392578f}, -{0.57735f,0.57735f,0.57735f},{0.421857f,0.686834f,0.591857f},{-0.249799f,0.940143f,0.231799f}, -{-0.296301f,0.880261f,0.370604f},{0.0118961f,0.905903f,0.423318f},{0.0313234f,0.355953f,0.933979f}, -{0.0352841f,0.306272f,0.95129f},{-0.0893656f,0.270821f,0.958473f},{-0.713881f,0.490776f,-0.499512f}, -{-0.410273f,0.869831f,-0.273989f},{-0.894055f,0.352175f,-0.276838f},{-0.817393f,0.308118f,-0.486757f}, -{-0.875796f,0.121223f,-0.467212f},{0.669572f,-0.707121f,-0.227273f},{0.818913f,-0.535545f,-0.206332f}, -{0.84874f,-0.466955f,-0.248178f},{0.00732999f,0.884841f,0.465835f},{0.756543f,0.624106f,0.19528f}, -{0.00141355f,0.916707f,0.399559f},{0.0128935f,0.936489f,0.35046f},{0.555117f,-0.0206778f,-0.831515f}, -{0.733425f,-0.110287f,-0.670765f},{-0.336867f,0.804122f,0.489804f},{-0.547861f,0.721964f,-0.42263f}, -{-0.517719f,0.852632f,0.0706092f},{-0.659477f,0.748223f,0.0724769f},{0.00701106f,0.622457f,0.782623f}, -{-0.00356288f,0.708901f,0.705299f},{-0.0351258f,0.774387f,0.631736f},{-0.385602f,0.794284f,-0.469494f}, -{-0.409104f,0.75301f,-0.515373f},{-0.343588f,0.739178f,-0.579278f},{0.44348f,0.409973f,0.797024f}, -{0.172274f,0.553567f,0.814791f},{0.444401f,0.516545f,0.731907f},{-0.899018f,0.315509f,-0.303678f}, -{-0.0287092f,0.99401f,-0.105451f},{0.0238513f,0.996213f,-0.0836077f},{0.0498361f,0.992292f,-0.113461f}, -{-0.243806f,0.954737f,0.170399f},{0.935547f,0.345553f,-0.0731101f},{0.866464f,0.488525f,-0.10288f}, -{0.887404f,0.427894f,-0.171524f},{0.919118f,0.358308f,0.163822f},{-0.939902f,-0.308644f,0.146026f}, -{-0.985943f,-0.128784f,0.106447f},{-0.968025f,-0.242516f,0.0641434f},{-0.319472f,-0.322908f,0.890881f}, -{-0.212183f,-0.00597968f,0.977212f},{-0.460175f,-0.0230708f,0.887528f},{-0.311636f,0.272517f,0.910284f}, -{0.148652f,-0.861322f,-0.485827f},{0.249015f,-0.75173f,-0.61065f},{0.283906f,-0.838223f,-0.465596f}, -{-0.0533076f,-0.660568f,-0.748871f},{0.102555f,-0.808587f,-0.579369f},{0.78009f,0.447597f,0.437169f}, -{0.659098f,0.426598f,0.619358f},{0.700411f,0.468918f,0.538089f},{0.981422f,-0.0277144f,-0.189847f}, -{0.990634f,0.0115244f,-0.136058f},{0.969545f,-0.000610817f,-0.244911f},{-0.999433f,0.0331006f,-0.00624168f}, -{-0.969336f,0.0523667f,-0.240095f},{-0.985831f,-0.0135757f,-0.167188f},{-0.11484f,-0.497945f,-0.859571f}, -{0.145066f,-0.756207f,-0.638049f},{-0.992501f,0.0955116f,-0.0762813f},{0.0307977f,0.813263f,0.581081f}, -{-0.252137f,0.914982f,0.315017f},{0.0936196f,0.978662f,0.182908f},{0.0174373f,0.996305f,-0.0841026f}, -{0.0180337f,0.991815f,-0.126402f},{0.044532f,-0.527634f,-0.848304f},{0.0660403f,-0.585473f,-0.807998f}, -{0.100115f,-0.56197f,-0.821077f},{-0.359698f,0.0989121f,-0.927811f},{-0.43688f,0.0954944f,-0.894437f}, -{-0.37905f,0.204351f,-0.902531f},{0.000580824f,0.0870738f,-0.996202f},{0.128226f,0.0453576f,-0.990707f}, -{0.0914407f,-0.0043354f,-0.995801f},{-0.651647f,0.621586f,-0.434727f},{-0.803714f,0.589137f,-0.0834296f}, -{-0.559601f,0.820384f,0.117543f},{0.759113f,0.438907f,0.480737f},{0.573133f,0.785088f,0.234854f}, -{0.738396f,0.63573f,0.224987f},{-0.340618f,-0.160374f,0.926423f},{-0.0783287f,-0.180108f,0.980523f}, -{-0.46722f,-0.379277f,0.798658f},{-0.083493f,-0.271426f,0.958831f},{0.0491597f,-0.0662771f,0.996589f}, -{0.0134786f,0.189284f,0.98183f},{0.00493481f,0.319204f,0.947673f},{-0.0212024f,0.401033f,0.915818f}, -{-0.385416f,0.416591f,0.823351f},{-0.0488352f,0.466255f,0.883301f},{-0.290291f,0.499278f,0.816365f}, -{-0.128063f,0.542822f,0.830026f},{-0.129216f,0.549796f,0.825244f},{-0.32094f,0.562335f,0.762087f}, -{-0.267665f,0.633191f,0.72624f},{-0.033667f,0.973168f,0.227618f},{0.0168639f,0.985589f,0.168313f}, -{-0.566884f,0.712608f,0.413319f},{0.108542f,0.994073f,0.0061294f},{0.125559f,0.990888f,-0.0487381f}, -{0.0163092f,0.994743f,0.101096f},{-0.807412f,-0.589942f,-0.00735334f},{-0.838698f,-0.540232f,0.0688061f}, -{-0.881405f,-0.469341f,0.0533315f},{0.783992f,-0.593224f,-0.182874f},{0.992591f,-0.109235f,-0.0532095f}, -{0.818459f,-0.442588f,-0.366388f},{-0.974179f,-0.225775f,0.000958332f},{-0.958019f,-0.275422f,-0.0796435f}, -{0.707968f,-0.265502f,0.654438f},{0.636608f,-0.281028f,0.71816f},{0.661075f,-0.188496f,0.726257f}, -{0.0874356f,-0.56697f,-0.819085f},{0.0433353f,-0.476473f,-0.87812f},{0.11842f,-0.528335f,-0.840737f}, -{0.129623f,0.104684f,-0.986022f},{-0.998419f,-0.0542573f,-0.0146808f},{-0.688453f,0.673068f,-0.270208f}, -{0.0156894f,0.999522f,0.0266441f},{-0.868316f,-0.494385f,0.0401279f},{-0.18112f,0.613563f,0.768594f}, -{-0.15902f,0.663238f,0.73132f},{-0.271218f,0.702918f,0.657531f},{-0.257402f,0.951051f,-0.171013f}, -{0.981363f,0.0833966f,-0.173124f},{0.99075f,0.0716342f,-0.115248f},{0.973192f,0.181782f,-0.140896f}, -{0.945709f,0.233387f,-0.226198f},{0.914622f,0.287571f,-0.2842f},{0.942306f,0.247063f,-0.225875f}, -{-0.938596f,-0.343675f,-0.030422f},{-0.261763f,0.960149f,-0.097945f},{0.948742f,0.313295f,0.0416476f}, -{-0.95564f,-0.294067f,0.0166649f},{-0.957934f,-0.285956f,0.0243055f},{-0.972972f,-0.208945f,-0.0983253f}, -{-0.979352f,-0.200837f,-0.0230909f},{-0.991689f,-0.0808817f,-0.100057f},{-0.945654f,-0.320158f,-0.0568986f}, -{0.690643f,-0.557553f,-0.460594f},{-0.432122f,-0.506947f,-0.745839f},{0.275703f,-0.688974f,-0.6703f}, -{0.0354541f,0.98518f,-0.167817f},{-0.32858f,-0.451626f,0.829499f},{-0.404259f,-0.441563f,0.800997f}, -{-0.36329f,-0.46504f,0.807315f},{-0.505347f,-0.18888f,-0.841991f},{-0.461011f,-0.138001f,-0.876598f}, -{-0.492927f,-0.276201f,-0.825068f},{0.846028f,0.49353f,0.201657f},{-0.417774f,-0.370217f,0.829701f}, -{-0.372866f,-0.459566f,0.806083f},{-0.301297f,-0.340957f,0.890487f},{0.0386461f,0.5845f,0.810473f}, -{-0.117987f,0.798897f,0.589782f},{-0.252652f,0.769314f,0.586791f},{-0.0898694f,0.716685f,0.691582f}, -{0.984322f,0.159456f,0.0753966f},{0.981936f,0.164752f,0.0930547f},{0.991128f,0.103448f,0.0834458f}, -{0.288081f,0.922413f,0.257223f},{0.196528f,0.928668f,0.314568f},{-0.276998f,0.950331f,0.141926f}, -{-0.947481f,0.302294f,-0.104397f},{-0.880527f,0.290802f,-0.374307f},{-0.982123f,0.082863f,-0.16902f}, -{0.0308069f,0.690279f,0.722888f},{-0.136452f,0.749634f,0.647634f},{-0.0115687f,0.611309f,0.791308f}, -{0.235361f,0.812168f,0.533844f},{0.42321f,0.747665f,0.511752f},{-0.00294655f,0.777584f,0.628772f}, -{-0.0585491f,0.0909536f,-0.994133f},{0.139261f,0.0860385f,-0.986511f},{0.0998423f,0.12309f,-0.98736f}, -{0.103544f,0.953622f,-0.282637f},{0.124829f,0.977293f,-0.171217f},{0.194063f,0.963728f,-0.183217f}, -{-0.464984f,0.339376f,-0.817688f},{-0.437427f,0.436841f,-0.78602f},{-0.428677f,0.450404f,-0.783181f}, -{-0.197968f,0.136253f,-0.970692f},{-0.334069f,0.160007f,-0.928868f},{-0.237943f,0.127333f,-0.962896f}, -{-0.520129f,0.00462802f,-0.854075f},{-0.455464f,0.0948686f,-0.885185f},{-0.332832f,0.0880139f,-0.93887f}, -{-0.481026f,-0.475911f,-0.73629f},{-0.438014f,-0.337269f,-0.833302f},{-0.347452f,-0.547715f,-0.761108f}, -{-0.461713f,-0.302499f,-0.833856f},{-0.29253f,-0.428292f,0.854981f},{-0.254224f,-0.27951f,0.925875f}, -{-0.528736f,-0.202208f,0.824349f},{-0.27128f,-0.0983893f,0.957458f},{-0.119153f,0.0275033f,0.992495f}, -{-0.263646f,0.0184969f,0.964442f},{-0.203003f,-0.0257421f,0.97884f},{0.0881956f,0.0331346f,0.995552f}, -{0.289706f,0.260591f,0.920958f},{0.238046f,0.369706f,0.898138f},{0.186732f,0.437215f,0.879758f}, -{0.160958f,0.506869f,0.846863f},{0.14827f,0.502056f,0.85203f},{-0.00055618f,0.615531f,0.788112f}, -{-0.00716591f,0.635817f,0.771807f},{0.956568f,0.250869f,-0.148464f},{-0.0660797f,0.638031f,-0.76717f}, -{-0.0601667f,0.665157f,-0.744276f},{-0.140985f,0.719068f,-0.680488f},{0.783289f,-0.583404f,-0.214702f}, -{0.948733f,-0.302305f,-0.092287f},{0.965091f,-0.248812f,-0.0818032f},{-0.276455f,0.810676f,0.516117f}, -{-0.396968f,0.850953f,0.343942f},{-0.229114f,0.779813f,0.582579f},{0.119628f,0.98719f,-0.105573f}, -{0.212732f,0.971432f,-0.105193f},{-0.380376f,0.529341f,-0.758362f},{0.0247164f,-0.999286f,-0.0285692f}, -{0.0221136f,-0.999343f,-0.0287203f},{0.0171425f,-0.999755f,-0.01398f},{0.0309597f,-0.415753f,-0.908951f}, -{0.00724507f,-0.383477f,-0.923522f},{0.0188414f,-0.424607f,-0.905182f},{0.210939f,0.157303f,0.964759f}, -{-0.0297303f,0.747411f,0.663697f},{0.933011f,0.289119f,-0.214245f},{0.925733f,0.281444f,-0.252602f}, -{0.903201f,0.364361f,-0.226868f},{0.861819f,-0.100476f,0.497165f},{0.91382f,0.0166522f,0.405778f}, -{0.85081f,0.0456878f,0.523484f},{-0.922116f,0.28202f,-0.264888f},{-0.924562f,0.232143f,-0.302149f}, -{-0.916155f,0.231573f,-0.327161f},{0.801727f,0.22916f,-0.552014f},{0.806146f,0.340978f,-0.483593f}, -{0.844248f,0.252886f,-0.472539f},{-0.900107f,0.432791f,-0.0499879f},{-0.929252f,0.170879f,-0.327554f}, -{-0.889537f,0.200244f,-0.410641f},{-0.909881f,0.119352f,-0.397331f},{0.319048f,-0.927929f,0.19276f}, -{-0.0570574f,-0.998339f,0.00799306f},{-0.837236f,0.0170867f,-0.546575f},{-0.766305f,-0.107681f,-0.633389f}, -{-0.831586f,0.0795901f,-0.549664f},{0.377541f,0.316521f,0.870217f},{0.332509f,0.499481f,0.799972f}, -{0.460076f,0.517386f,0.721555f},{0.79541f,-0.566159f,-0.216304f},{0.911327f,-0.362557f,-0.195027f}, -{-0.74796f,-0.0695523f,-0.660089f},{-0.662762f,-0.235216f,-0.710929f},{-0.539195f,-0.222657f,-0.812214f}, -{0.0218027f,-0.433793f,0.900749f},{-0.00311731f,-0.163603f,0.986521f},{-0.0173914f,0.0456827f,0.998805f}, -{0.0400381f,0.178126f,0.983193f},{0.283483f,0.312944f,0.906479f},{0.134246f,-0.174071f,0.975539f}, -{0.212289f,-0.161507f,0.963768f},{0.229441f,-0.18882f,0.954832f},{0.412163f,0.305418f,0.858395f}, -{0.328776f,0.398539f,0.856197f},{0.424166f,0.363353f,0.829492f},{0.265865f,0.453263f,0.850804f}, -{0.18611f,0.546428f,0.816566f},{0.143454f,0.625752f,0.766718f},{0.120938f,0.656564f,0.744512f}, -{0.932122f,0.322601f,-0.16455f},{0.804729f,0.514737f,-0.29573f},{0.834634f,0.525578f,-0.164783f}, -{-0.988431f,0.0168326f,-0.150736f},{-0.992003f,-0.00163991f,-0.126204f},{-0.967043f,-0.0141034f,-0.254221f}, -{-0.00142562f,-0.579066f,-0.815279f},{-0.125423f,-0.384951f,-0.914375f},{-0.103741f,-0.44905f,-0.887464f}, -{-0.553529f,0.736759f,0.388319f},{-0.678903f,0.627379f,0.381427f},{-0.0588021f,0.897443f,0.437193f}, -{-0.943585f,-0.29927f,-0.141721f},{-0.965152f,-0.168369f,-0.200336f},{0.581388f,-0.170418f,-0.795579f}, -{0.894217f,0.330236f,-0.302192f},{0.891125f,-0.205523f,-0.404545f},{0.210297f,0.767182f,0.605977f}, -{0.00638082f,0.857806f,0.513935f},{0.470693f,0.834009f,0.287883f},{0.370541f,0.871789f,0.320441f}, -{0.440424f,0.862366f,0.249702f},{-0.873822f,-0.484636f,0.0395298f},{-0.891498f,-0.450702f,0.0458181f}, -{-0.87457f,-0.484822f,-0.00871369f},{0.197942f,0.935148f,-0.293798f},{0.197289f,-0.730094f,-0.654248f}, -{0.22188f,-0.765928f,-0.603426f},{0.238886f,-0.773997f,-0.586398f},{-0.683216f,-0.224521f,-0.694842f}, -{-0.127828f,0.30359f,-0.944189f},{-0.0162157f,0.431431f,-0.902f},{-0.193427f,0.498023f,-0.845316f}, -{-0.280182f,-0.707712f,-0.64857f},{-0.079535f,-0.668278f,-0.739648f},{-0.392839f,-0.472588f,-0.788884f}, -{0.176156f,0.338308f,0.924401f},{-0.581431f,0.267686f,-0.768298f},{-0.594621f,0.186383f,-0.782104f}, -{-0.642564f,0.383097f,-0.663587f},{0.0191206f,-0.600232f,0.799598f},{0.100352f,-0.466051f,0.879049f}, -{0.187243f,-0.175769f,0.96646f},{0.209566f,-0.253734f,0.944299f},{0.111776f,-0.225572f,0.967793f}, -{-0.420204f,-0.794529f,-0.438352f},{-0.0541604f,-0.944389f,-0.324339f},{-0.958418f,0.269381f,0.0941702f}, -{-0.924451f,0.370113f,0.0916944f},{-0.950281f,0.306645f,-0.0541689f},{0.980787f,-0.180823f,0.0732094f}, -{0.981284f,-0.17298f,0.0846207f},{-0.0940962f,0.884837f,0.4563f},{-0.534859f,0.744233f,0.400053f}, -{0.718215f,0.189478f,-0.669526f},{0.713162f,0.106936f,-0.692795f},{0.801152f,-0.0516452f,-0.596229f}, -{0.560765f,-0.485962f,-0.67036f},{0.647819f,-0.462094f,-0.60564f},{0.552192f,-0.596363f,-0.582611f}, -{0.461443f,-0.823847f,-0.329161f},{0.839854f,0.511735f,0.181034f},{0.824616f,0.534256f,0.185955f}, -{-0.439976f,0.866796f,0.234702f},{-0.118575f,0.752426f,0.647916f},{0.149029f,0.629448f,0.762618f}, -{0.037462f,0.830588f,0.555625f},{-0.327739f,0.0394449f,0.943944f},{-0.516321f,0.0528115f,0.854765f}, -{-0.414224f,-0.0682792f,0.90761f},{0.288967f,0.0433499f,-0.956357f},{0.275983f,-0.147043f,-0.949848f}, -{0.598083f,-0.304596f,-0.741294f},{0.293406f,-0.917829f,-0.267401f},{0.601447f,-0.775694f,-0.191208f}, -{0.0463522f,0.74153f,0.669317f},{-0.704225f,-0.683019f,-0.193784f},{-0.708969f,-0.696362f,-0.111547f}, -{-0.680494f,-0.689726f,-0.247398f},{-0.443656f,0.340732f,0.828898f},{-0.338882f,0.238957f,0.909977f}, -{-0.467223f,0.237607f,0.851614f},{-0.443441f,-0.0478543f,-0.895025f},{-0.542019f,0.148455f,-0.827149f}, -{-0.496647f,0.0266507f,-0.867543f},{0.452023f,0.320446f,0.83246f},{0.187312f,-0.289498f,0.938672f}, -{0.817561f,0.484433f,-0.311319f},{0.119843f,0.692326f,0.711563f},{-0.5423f,0.839989f,-0.0181133f}, -{-0.589584f,0.802126f,-0.0947837f},{0.272709f,0.847446f,0.455483f},{-0.999451f,0.0035897f,0.0329507f}, -{0.0652443f,0.925068f,0.374155f},{-0.417802f,0.0621403f,0.906411f},{-0.299806f,0.0578951f,0.952242f}, -{-0.230259f,-0.156221f,0.960508f},{-0.482247f,-0.62297f,0.615911f},{-0.72808f,-0.351511f,0.588506f}, -{-0.223557f,0.145718f,0.963737f},{-0.284359f,0.277931f,0.917548f},{-0.394802f,0.167585f,0.903353f}, -{-0.36651f,-0.490079f,-0.790881f},{0.958509f,-0.237533f,-0.157604f},{-0.340748f,-0.792406f,0.505947f}, -{-0.800663f,-0.422511f,0.424763f},{-0.793826f,-0.330462f,0.510524f},{-0.345477f,0.371662f,0.861692f}, -{-0.35377f,0.384884f,0.852474f},{-0.235416f,0.24056f,0.941653f},{-0.00349561f,-0.229967f,0.973192f}, -{-0.452241f,0.131272f,-0.882182f},{0.213681f,-0.17502f,0.961097f},{0.385113f,-0.504767f,0.772592f}, -{0.241816f,0.0985017f,0.96531f},{0.245202f,0.25789f,0.934542f},{0.141131f,0.377939f,0.91501f}, -{0.123254f,0.47787f,0.869741f},{-0.0193965f,0.480999f,0.876507f},{0.220321f,0.498587f,0.838373f}, -{0.158681f,0.595297f,0.787681f},{0.214315f,0.598022f,0.772294f},{0.103356f,-0.545978f,0.8314f}, -{0.198197f,-0.0823329f,0.976698f},{0.65635f,-0.1957f,-0.728633f},{0.628686f,-0.318384f,-0.709497f}, -{0.490731f,-0.0500569f,-0.869872f},{0.996762f,0.0511583f,-0.0620418f},{0.308099f,0.64451f,0.699773f}, -{0.424921f,0.473751f,0.771364f},{0.435549f,0.496107f,0.751116f},{-0.0848771f,-0.255893f,-0.962972f}, -{0.354992f,0.509584f,0.783776f},{0.394104f,0.427023f,0.813839f},{0.585515f,-0.690378f,-0.424912f}, -{0.527878f,-0.549372f,-0.647716f},{0.884681f,-0.463029f,-0.0542522f},{0.022149f,-0.999754f,0.000742555f}, -{-0.0122399f,-0.999713f,0.0206051f},{0.040471f,-0.998944f,0.0217503f},{-0.170834f,0.582988f,-0.794317f}, -{-0.178266f,0.547618f,-0.817518f},{-0.212052f,0.610169f,-0.763366f},{0.825517f,0.493208f,-0.274349f}, -{0.801331f,0.529124f,-0.279101f},{0.818077f,0.552458f,-0.159812f},{0.467831f,-0.658629f,-0.589357f}, -{0.435167f,-0.759272f,-0.483876f},{0.236562f,-0.797224f,-0.555402f},{-0.644491f,0.666855f,0.374081f}, -{-0.00960394f,0.452187f,0.891871f},{-0.177981f,0.559222f,0.809687f},{-0.199909f,0.532918f,0.822213f}, -{0.337658f,0.462369f,0.819879f},{0.229458f,0.26022f,0.937888f},{0.416462f,0.423903f,0.804279f}, -{0.76476f,0.595295f,0.246506f},{-0.929246f,0.137717f,-0.342835f},{-0.875507f,-0.151751f,-0.458759f}, -{-0.894451f,0.21627f,-0.391389f},{0.310225f,0.947793f,0.0738153f},{0.231035f,0.95555f,0.183157f}, -{-0.296947f,0.852665f,0.429865f},{-0.985861f,0.0887629f,-0.142124f},{0.0185413f,0.943008f,0.332254f}, -{0.057313f,0.886239f,0.459669f},{-0.679383f,-0.0801837f,-0.729389f},{0.113925f,-0.311895f,-0.943262f}, -{-0.139823f,0.0525498f,0.988781f},{-0.218846f,0.661597f,0.717214f},{-0.148582f,-0.206342f,-0.967133f}, -{0.7509f,-0.182971f,-0.634563f},{0.420695f,-0.203165f,0.88416f},{-0.420831f,-0.473534f,-0.773736f}, -{-0.229455f,-0.26095f,-0.937686f},{-0.246277f,-0.0434649f,-0.968224f},{0.13588f,0.282837f,0.949495f}, -{0.176315f,0.366131f,0.913707f},{0.116814f,0.371617f,0.921008f},{-0.00480627f,0.35768f,0.933832f}, -{0.0834085f,0.119904f,0.989276f},{0.161637f,-0.054116f,0.985365f},{0.00341052f,0.0398947f,0.999198f}, -{-0.0800531f,-0.726618f,0.682362f},{0.0654456f,-0.609214f,0.790301f},{-0.0633954f,-0.615952f,0.785229f}, -{-0.0539942f,-0.764143f,0.642783f},{0.00401556f,-0.756327f,0.654181f},{0.0838838f,-0.339017f,0.937033f}, -{0.0587523f,-0.4979f,0.865242f},{0.148705f,0.00212254f,0.988879f},{0.182487f,0.0571239f,0.981547f}, -{0.53245f,-0.726799f,-0.43389f},{0.571984f,-0.776758f,-0.263593f},{0.490454f,-0.797822f,-0.350621f}, -{0.914028f,0.298546f,-0.274632f},{0.908921f,0.0880075f,-0.407576f},{0.96965f,0.0520819f,-0.238887f}, -{-0.0686965f,0.325763f,-0.942952f},{-0.109446f,0.392498f,-0.913218f},{-0.132643f,0.414957f,-0.900121f}, -{0.465453f,-0.78761f,-0.403763f},{0.43965f,-0.708592f,-0.551911f},{0.469105f,-0.777133f,-0.419529f}, -{0.453067f,-0.76698f,-0.454393f},{0.454134f,-0.76286f,-0.460225f},{0.876326f,-0.436944f,-0.202812f}, -{0.900929f,-0.413532f,-0.131602f},{0.921355f,-0.355754f,-0.156663f},{-0.0920299f,0.658693f,0.746762f}, -{-0.155912f,0.746973f,0.646315f},{-0.28089f,0.68316f,0.674087f},{-0.0731005f,0.675791f,0.733459f}, -{-0.128286f,0.631681f,0.76454f},{0.210613f,0.314174f,0.925709f},{-0.449802f,0.403711f,-0.796678f}, -{-0.396683f,0.413545f,-0.819526f},{-0.273958f,0.956358f,0.101619f},{0.11902f,0.990924f,0.0624892f}, -{0.108584f,0.967612f,0.227895f},{0.0300621f,0.972509f,0.230917f},{-0.32827f,0.355206f,0.875253f}, -{0.278549f,0.478117f,0.832955f},{0.288245f,0.594488f,0.750666f},{0.0643547f,0.0265242f,0.997575f}, -{0.207224f,-0.0685004f,0.975892f},{0.0639272f,0.178665f,0.981831f},{-0.0313133f,-0.861465f,-0.506851f}, -{0.132764f,-0.817868f,-0.55988f},{0.122697f,-0.893873f,-0.431204f},{0.988041f,0.149777f,-0.036627f}, -{0.970806f,0.237743f,-0.0318535f},{0.982795f,0.17944f,0.0437546f},{0.252469f,0.598346f,0.760422f}, -{0.26065f,0.585606f,0.767547f},{0.264146f,0.574259f,0.774889f},{0.906751f,-0.385086f,-0.171788f}, -{0.684151f,-0.723747f,0.0901569f},{0.773584f,-0.525695f,0.353853f},{0.990162f,-0.133992f,0.0403111f}, -{-0.989415f,0.0943284f,0.110271f},{-0.493016f,-0.774648f,-0.39605f},{0.19466f,0.662258f,0.723548f}, -{0.559884f,0.373378f,0.739675f},{0.320414f,0.527874f,0.786564f},{0.974845f,-0.221106f,0.0280791f}, -{0.987799f,-0.15154f,0.0359129f},{0.948007f,-0.317943f,-0.0139217f},{0.807472f,-0.295769f,-0.510401f}, -{0.831477f,-0.257606f,-0.492224f},{0.830739f,-0.29772f,-0.470357f},{0.629038f,-0.619501f,-0.469605f}, -{0.731654f,-0.44459f,-0.516742f},{0.663327f,-0.609815f,-0.43373f},{-0.888495f,-0.373328f,0.266838f}, -{-0.853903f,-0.411981f,0.317996f},{-0.871296f,-0.349591f,0.344428f},{0.173781f,0.830788f,-0.528764f}, -{0.214551f,0.837516f,-0.502528f},{0.326037f,0.775801f,-0.540215f},{-0.109706f,-0.228527f,0.967336f}, -{-0.147056f,-0.079098f,0.98596f},{-0.19074f,0.0999919f,0.976535f},{0.678873f,-0.52574f,-0.512571f}, -{0.362062f,-0.744174f,-0.561351f},{-0.991329f,0.122094f,0.0485786f},{-0.974345f,0.217247f,-0.0587819f}, -{-0.287749f,0.942918f,0.167646f},{-0.360444f,0.226196f,0.90494f},{-0.212829f,0.253452f,0.943645f}, -{-0.1543f,0.180708f,0.971358f},{-0.240401f,0.248335f,0.938369f},{-0.281255f,0.382109f,0.880277f}, -{-0.291117f,0.434942f,0.852101f},{-0.241476f,0.380203f,0.892824f},{-0.138281f,0.282514f,0.949244f}, -{-0.118735f,0.0590326f,0.99117f},{0.794851f,-0.419047f,-0.438875f},{0.232186f,0.0291142f,0.972236f}, -{0.121233f,-0.0318358f,0.992113f},{0.199799f,0.0839047f,0.976238f},{0.346783f,-0.915641f,-0.203328f}, -{0.193206f,-0.847024f,-0.495199f},{0.289267f,-0.947851f,-0.133803f},{0.348006f,0.499509f,0.793336f}, -{0.208545f,0.31167f,0.927023f},{0.176429f,0.45395f,0.873386f},{0.131126f,-0.651578f,0.747162f}, -{0.477843f,0.446816f,0.756321f},{0.344739f,0.568001f,0.747349f},{0.900389f,-0.107033f,-0.421715f}, -{0.929053f,-0.117313f,-0.350853f},{0.924176f,-0.147349f,-0.352402f},{-0.0313202f,0.13272f,0.990659f}, -{-0.119674f,0.169113f,0.978304f},{0.0505059f,-0.0361727f,0.998068f},{0.178549f,0.161106f,0.970652f}, -{0.30095f,0.0349999f,0.952997f},{0.886396f,0.461951f,0.030055f},{0.869061f,0.492341f,-0.0483173f}, -{0.857969f,0.51363f,0.0086094f},{0.914986f,-0.377543f,-0.142346f},{0.93273f,-0.351435f,-0.0806737f}, -{0.927026f,-0.374765f,-0.0131579f},{0.946514f,-0.00024888f,-0.322663f},{-0.276014f,0.950969f,-0.139548f}, -{-0.264043f,0.963609f,-0.0416947f},{-0.23099f,0.971773f,0.0479709f},{-0.156866f,0.9795f,0.126387f}, -{-0.314375f,0.932833f,0.176042f},{-0.0245953f,0.890615f,0.454092f},{-0.448809f,-0.0923177f,0.888846f}, -{-0.502441f,-0.0916905f,0.859736f},{0.276028f,0.933338f,-0.229541f},{0.314576f,0.894929f,-0.316455f}, -{0.285181f,0.94836f,-0.13887f},{0.110223f,0.0102139f,0.993854f},{0.211547f,0.0628589f,0.975344f}, -{0.321092f,-0.0486882f,0.945795f},{-0.0376397f,0.660927f,0.749506f},{-0.00832555f,-0.522454f,0.852627f}, -{-0.132767f,-0.384428f,0.913558f},{0.223466f,0.216042f,0.950468f},{0.238549f,0.376728f,0.895081f}, -{0.238472f,0.243627f,0.940094f},{0.302267f,0.125011f,0.94499f},{0.248175f,0.366485f,0.896715f}, -{0.168671f,0.349832f,0.921503f},{0.199354f,0.22033f,0.954836f},{0.273515f,0.176255f,0.945581f}, -{0.191339f,0.153628f,0.969427f},{-0.0112707f,0.119995f,0.99271f},{0.0725509f,0.159063f,0.984599f}, -{-0.00629055f,0.274279f,0.96163f},{0.277965f,0.117253f,0.953408f},{0.110563f,-0.343456f,0.932638f}, -{0.216365f,-0.123f,0.968534f},{0.900057f,0.245717f,0.359889f},{0.863173f,0.257873f,0.43409f}, -{0.881976f,0.160588f,0.443091f},{0.370564f,-0.353545f,0.858888f},{0.336535f,-0.0878704f,0.937562f}, -{0.256114f,-0.421237f,0.870037f},{0.905595f,0.0066398f,-0.424091f},{0.932738f,0.00768876f,-0.360474f}, -{0.937288f,0.0876965f,-0.337345f},{-0.0369066f,-0.945463f,0.323632f},{-0.118818f,-0.685618f,0.718199f}, -{-0.0967924f,-0.804577f,0.585907f},{0.916949f,0.263347f,-0.299756f},{0.898078f,0.264743f,-0.351236f}, -{-0.75019f,0.547434f,0.370853f},{-0.604395f,0.689198f,0.399641f},{-0.672897f,0.729871f,0.120408f}, -{-0.245761f,-0.73762f,-0.628903f},{-0.470701f,-0.346268f,-0.811504f},{0.0348372f,-0.579103f,-0.81451f}, -{-0.230634f,-0.210479f,0.950003f},{-0.274659f,-0.0711949f,0.958902f},{-0.350123f,-0.292919f,0.889726f}, -{-0.320295f,0.106153f,0.941352f},{-0.450114f,0.11922f,0.884977f},{0.136917f,0.164934f,0.976755f}, -{0.20114f,-0.423575f,0.883248f},{0.269589f,0.227421f,0.935736f},{0.256735f,0.173565f,0.950769f}, -{0.297017f,0.215377f,0.930266f},{0.300127f,-0.148128f,0.942328f},{-0.16051f,-0.429314f,-0.888778f}, -{-0.0358626f,-0.295419f,-0.954694f},{-0.0163357f,-0.445626f,-0.89507f},{0.458862f,-0.882519f,-0.10298f}, -{0.414609f,-0.88476f,-0.212836f},{0.489972f,-0.830752f,-0.264156f},{-0.079701f,-0.146064f,0.986059f}, -{-0.0857675f,-0.0777074f,0.99328f},{0.00161508f,-0.337113f,0.941463f},{0.280664f,-0.42611f,0.860034f}, -{-0.643998f,-0.577076f,-0.502246f},{-0.0959296f,-0.748871f,-0.655736f},{0.986673f,0.113871f,0.116232f}, -{0.948721f,0.230491f,0.21634f},{0.944986f,0.307393f,0.111848f},{0.960212f,-0.067786f,-0.27092f}, -{0.960641f,-0.049122f,-0.273417f},{0.967143f,-0.0398146f,-0.251094f},{0.91918f,0.127727f,-0.37255f}, -{0.949064f,0.123437f,-0.289897f},{0.568969f,0.205887f,0.796169f},{0.844097f,0.53266f,-0.0614229f}, -{0.137129f,0.880111f,0.454534f},{0.246395f,0.858197f,0.450319f},{-0.269803f,0.959994f,0.0749557f}, -{0.234905f,0.788011f,0.569085f},{-0.389489f,0.854769f,0.343027f},{-0.959211f,0.275599f,0.0629244f}, -{-0.953233f,0.301814f,0.0159892f},{-0.949614f,0.309566f,0.0490065f},{0.403744f,0.911277f,-0.0810251f}, -{0.923159f,0.364994f,0.120653f},{-0.62943f,-0.776664f,0.0247036f},{-0.652388f,-0.757587f,-0.0212481f}, -{-0.44652f,-0.894258f,-0.030358f},{-0.0901651f,-0.339501f,0.936274f},{-0.0379441f,-0.271751f,0.961619f}, -{-0.332831f,-0.401591f,0.853199f},{-0.0344199f,-0.209496f,0.977203f},{-0.126661f,-0.114418f,0.985325f}, -{-0.148426f,0.0111855f,0.98886f},{-0.295706f,0.235843f,0.925708f},{-0.369193f,0.300019f,0.879594f}, -{-0.309604f,0.364565f,0.878201f},{-0.187306f,0.220824f,0.957159f},{-0.158949f,0.314932f,0.93571f}, -{-0.161938f,0.456189f,0.875024f},{-0.148022f,0.444858f,0.883284f},{0.739283f,-0.653128f,-0.163966f}, -{0.86335f,-0.487178f,-0.131467f},{0.933787f,0.353346f,-0.0564575f},{-0.0460242f,-0.614627f,-0.787474f}, -{-0.113948f,-0.561688f,-0.819465f},{0.0168824f,-0.095401f,0.995296f},{-0.274895f,-0.200561f,0.940323f}, -{0.0946811f,0.635809f,0.766018f},{0.231209f,-0.764021f,0.60234f},{0.142163f,-0.126555f,0.98172f}, -{0.25344f,0.386675f,0.886708f},{0.250599f,0.501617f,0.827998f},{0.195129f,0.482999f,0.853602f}, -{0.0557201f,0.685491f,0.725946f},{-0.0774665f,0.667998f,0.74012f},{-0.0840826f,0.594974f,0.799335f}, -{0.01558f,0.551834f,0.833808f},{0.00736914f,0.617291f,0.786701f},{-0.0983184f,0.553799f,0.826825f}, -{0.18891f,0.283061f,-0.940314f},{0.178401f,0.241742f,-0.9538f},{0.100226f,0.273544f,-0.956623f}, -{0.769923f,-0.243603f,0.58981f},{0.77465f,-0.286361f,0.563839f},{0.69038f,-0.0458631f,0.721991f}, -{-0.327666f,0.777839f,0.536285f},{-0.342358f,0.791542f,0.506214f},{-0.327755f,0.859105f,0.393083f}, -{0.883778f,0.438968f,0.162f},{0.877874f,0.383199f,0.287219f},{0.0517423f,0.993679f,-0.0996198f}, -{0.13168f,0.990813f,-0.0308326f},{-0.229953f,-0.378124f,0.896741f},{0.140701f,0.329084f,0.93376f}, -{0.173597f,0.234941f,0.956382f},{0.286773f,-0.404816f,0.868266f},{0.409235f,-0.270151f,0.871519f}, -{-0.379485f,-0.523586f,-0.76279f},{-0.483885f,-0.460032f,-0.744464f},{-0.429615f,-0.568608f,-0.70151f}, -{0.165898f,0.0505138f,0.984848f},{0.185924f,0.175633f,0.96674f},{0.14884f,0.141895f,0.978628f}, -{0.0918044f,0.142985f,0.985458f},{0.0755089f,0.581447f,0.810073f},{0.333992f,-0.909345f,-0.248074f}, -{0.442823f,-0.869149f,-0.220199f},{0.669263f,-0.711774f,-0.213223f},{0.329347f,-0.663973f,0.671319f}, -{0.307978f,-0.627619f,0.715013f},{0.2937f,-0.525645f,0.798397f},{0.459327f,-0.888265f,0.00217148f}, -{0.553339f,-0.766244f,0.32663f},{0.416483f,-0.890913f,-0.18115f},{0.448413f,-0.472512f,0.758722f}, -{0.463214f,-0.583903f,0.666701f},{0.0228558f,0.153535f,0.987879f},{0.108558f,0.236491f,0.96555f}, -{0.728112f,-0.605165f,-0.321914f},{0.858305f,-0.313243f,-0.406437f},{0.599198f,0.0898905f,0.795539f}, -{0.819297f,0.491738f,0.294864f},{0.243573f,-0.893407f,-0.377487f},{0.196902f,-0.829986f,-0.521874f}, -{0.788151f,-0.606299f,0.105922f},{-0.0230661f,0.00367931f,-0.999727f},{0.0283941f,0.0276503f,-0.999214f}, -{0.0669312f,0.0129073f,-0.997674f},{-0.334345f,-0.481378f,0.81024f},{-0.309593f,-0.428943f,0.848622f}, -{-0.302578f,-0.400369f,0.864958f},{-0.254114f,0.379369f,0.889666f},{0.498502f,0.24239f,0.832312f}, -{0.615686f,0.102185f,0.781338f},{0.553289f,0.0582414f,0.830951f},{-0.00204334f,-0.480179f,0.877168f}, -{-0.0856082f,-0.206093f,0.97478f},{0.327397f,-0.357655f,0.874582f},{0.215444f,-0.466779f,0.85773f}, -{0.162921f,-0.132008f,0.977768f},{0.0647925f,0.233047f,0.970305f},{0.111038f,0.205046f,0.972433f}, -{0.0158613f,-0.0419174f,0.998995f},{0.341844f,-0.269747f,0.900211f},{0.116874f,-0.149929f,0.981765f}, -{0.191889f,-0.0992769f,0.976382f},{0.0101111f,0.773999f,0.633107f},{-0.0530789f,0.700445f,0.71173f}, -{0.511712f,-0.845175f,-0.154368f},{0.850173f,-0.499442f,-0.166622f},{0.846485f,-0.503522f,-0.172997f}, -{0.82965f,-0.522815f,-0.195821f},{0.023405f,-0.999625f,-0.0142142f},{0.0242401f,-0.999656f,-0.00998008f}, -{-0.000303005f,-0.999841f,-0.017843f},{-0.933294f,0.332437f,0.135822f},{-0.881595f,0.351294f,0.31525f}, -{-0.928424f,0.283676f,0.239911f},{-0.772711f,0.476617f,-0.41923f},{-0.402891f,0.911185f,-0.0861461f}, -{-0.76101f,0.648736f,0.0022703f},{-0.525614f,0.828955f,-0.191216f},{-0.187238f,0.982224f,-0.0133145f}, -{-0.147426f,0.988429f,-0.0356911f},{-0.248234f,-0.244597f,-0.937311f},{-0.198606f,-0.362544f,-0.910559f}, -{0.3608f,-0.683461f,-0.63459f},{0.0953136f,0.0853679f,-0.99178f},{0.51823f,-0.849414f,-0.0996677f}, -{0.814328f,-0.553142f,-0.175794f},{0.748936f,-0.635619f,-0.187304f},{-0.191851f,-0.439151f,0.87769f}, -{-0.228831f,-0.274946f,0.933831f},{-0.144682f,-0.240387f,0.959834f},{0.0186087f,-0.229308f,0.973176f}, -{0.0844466f,-0.272325f,0.958493f},{0.048156f,-0.214678f,0.975497f},{0.0185073f,-0.0533244f,0.998406f}, -{-0.0645704f,0.122728f,0.990338f},{-0.0960451f,0.353497f,0.930492f},{-0.123316f,0.466299f,0.87599f}, -{-0.101722f,0.334442f,0.93691f},{-0.130158f,0.278541f,0.951564f},{-0.134554f,0.476146f,0.869011f}, -{-0.0530084f,0.525905f,0.84889f},{-0.164811f,0.321452f,0.932473f},{0.0823322f,0.227239f,0.970353f}, -{-0.0657198f,0.130476f,0.989271f},{-0.0768859f,-0.720243f,0.689448f},{0.239307f,0.762061f,0.601661f}, -{0.112851f,0.567339f,0.815715f},{0.141879f,0.648949f,0.747486f},{0.203347f,0.494481f,0.845067f}, -{-0.0796564f,-0.697736f,0.711913f},{-0.0953443f,-0.671122f,0.73519f},{-0.964209f,-0.241382f,0.109707f}, -{-0.972866f,-0.118573f,0.198676f},{-0.962037f,-0.177494f,0.207316f},{0.376109f,0.345327f,0.859821f}, -{0.260354f,0.334831f,0.905596f},{0.413984f,0.0796125f,0.906796f},{0.206149f,0.427033f,0.880423f}, -{0.255093f,0.478282f,0.840342f},{0.186982f,0.425432f,0.885463f},{0.188722f,0.418679f,0.888308f}, -{0.932563f,-0.313456f,-0.179087f},{0.00751608f,-0.992196f,0.124464f},{0.61083f,0.754516f,0.239982f}, -{0.210359f,0.975986f,0.0565786f},{0.237949f,0.971085f,-0.0193416f},{-0.992479f,-0.119535f,-0.0264097f}, -{-0.991905f,0.122125f,-0.0347969f},{-0.994124f,0.0788717f,-0.0741417f},{0.317099f,0.917356f,0.240638f}, -{-0.9839f,0.176112f,-0.0304254f},{-0.229285f,0.931458f,-0.282516f},{-0.249293f,0.95181f,-0.178636f}, -{-0.380595f,-0.0953641f,-0.919811f},{-0.356386f,-0.185595f,-0.91572f},{-0.532308f,0.33788f,-0.7762f}, -{-0.623105f,0.373117f,-0.687404f},{-0.591311f,0.340755f,-0.730915f},{-0.0939356f,0.50456f,-0.858251f}, -{-0.0584824f,0.31504f,-0.947275f},{-0.0717267f,0.392258f,-0.917055f},{-0.409762f,-0.0557136f,-0.91049f}, -{-0.432608f,0.0796261f,-0.898059f},{-0.290013f,-0.0614834f,-0.955046f},{-0.253383f,-0.515742f,0.818418f}, -{-0.147352f,0.43482f,0.88838f},{0.948899f,-0.0521516f,-0.311241f},{0.175427f,0.203394f,-0.963253f}, -{0.284098f,-0.0864908f,0.954886f},{0.156203f,-0.102557f,0.982386f},{-0.0343121f,-0.670962f,0.740697f}, -{0.282425f,0.113884f,0.952505f},{0.252265f,-0.0799734f,0.964348f},{0.608191f,-0.555626f,-0.566907f}, -{-0.281625f,0.0199879f,-0.959316f},{-0.321533f,0.14723f,-0.935382f},{0.00403764f,-0.409977f,0.912087f}, -{0.296786f,0.412455f,0.861278f},{0.314424f,0.491877f,0.811908f},{0.15597f,-0.148834f,0.976484f}, -{-0.265697f,-0.575302f,0.773585f},{-0.0127738f,-0.679576f,0.733494f},{-0.326234f,-0.480712f,0.813933f}, -{-0.872833f,-0.464274f,-0.150372f},{-0.994023f,0.0946819f,-0.0543493f},{-0.550761f,-0.834547f,0.0139191f}, -{0.0263528f,0.37496f,0.926666f},{0.216957f,0.471372f,0.854832f},{0.778666f,0.0794795f,0.622385f}, -{0.595223f,-0.107183f,0.79638f},{0.0160022f,0.466013f,0.884633f},{-0.217392f,0.653551f,0.72499f}, -{-0.331892f,-0.59942f,-0.728384f},{-0.200273f,-0.752239f,-0.627716f},{0.510712f,-0.829747f,-0.225151f}, -{0.546848f,-0.823202f,-0.15263f},{0.255635f,-0.202235f,0.945384f},{0.233842f,-0.286493f,0.929107f}, -{0.308997f,0.326208f,0.893369f},{0.539946f,0.154095f,0.827474f},{0.536519f,0.226703f,0.812867f}, -{-0.013535f,0.996342f,0.0843795f},{-0.430286f,-0.0933455f,-0.897853f},{-0.293771f,-0.0785507f,-0.952643f}, -{-0.316683f,-0.174593f,-0.932325f},{-0.616447f,0.263836f,-0.741878f},{-0.584456f,0.323832f,-0.744006f}, -{-0.533083f,0.332606f,-0.777943f},{-0.470009f,0.0371177f,-0.881881f},{-0.313697f,-0.230665f,-0.92108f}, -{-0.095108f,-0.430469f,0.89758f},{-0.205516f,-0.31961f,0.924993f},{-0.271736f,-0.129682f,0.953594f}, -{-0.16233f,-0.128129f,0.978382f},{-0.103393f,-0.147975f,0.983572f},{0.0371042f,-0.0618592f,0.997395f}, -{0.0800676f,0.0692281f,0.994383f},{0.0655334f,0.326109f,0.943058f},{0.0500331f,0.458989f,0.887032f}, -{-0.012976f,0.250275f,0.968088f},{-0.00278086f,0.254525f,0.967062f},{-0.00602506f,0.403435f,0.914989f}, -{-0.0166253f,0.507921f,0.861243f},{-0.066194f,0.569683f,0.819194f},{0.00612044f,0.50687f,0.862001f}, -{0.087804f,0.358904f,0.929235f},{0.135736f,0.0527765f,0.989338f},{0.219893f,0.0168975f,0.975378f}, -{0.155385f,0.67077f,0.725205f},{0.0911761f,0.307906f,0.947038f},{0.380764f,-0.112959f,0.917747f}, -{0.484482f,-0.25873f,0.835665f},{0.173278f,-0.0255334f,0.984542f},{-0.119044f,0.0165311f,0.992751f}, -{-0.0621672f,-0.226799f,0.971955f},{0.168582f,-0.017391f,0.985534f},{0.192135f,-0.114706f,0.974642f}, -{0.0907603f,0.0712779f,0.993319f},{0.900404f,0.273238f,-0.338546f},{0.867039f,0.330615f,-0.372743f}, -{0.817124f,0.438432f,-0.374281f},{0.272694f,0.0257412f,0.961756f},{-0.264704f,0.876336f,0.402451f}, -{-0.374633f,0.788444f,0.487858f},{-0.239863f,-0.388392f,-0.889729f},{0.322604f,-0.898675f,-0.297171f}, -{0.110378f,0.0512505f,-0.992567f},{0.195878f,0.100581f,-0.975456f},{-0.299195f,0.368799f,-0.88004f}, -{-0.281542f,0.416945f,-0.864229f},{-0.334622f,0.516404f,-0.788261f},{-0.388498f,-0.387132f,-0.836181f}, -{-0.413425f,-0.223328f,-0.882726f},{-0.528806f,-0.229046f,-0.817253f},{-0.567126f,0.252032f,-0.784123f}, -{-0.0622147f,-0.575088f,0.815722f},{-0.262476f,-0.0975698f,0.959993f},{-0.27464f,-0.0410216f,0.960672f}, -{0.863329f,0.259147f,-0.43302f},{0.79449f,0.262755f,-0.547491f},{0.948858f,-0.049514f,-0.311796f}, -{-0.00933192f,0.0898895f,0.995908f},{-0.0154903f,0.0603768f,0.998055f},{0.233006f,0.519745f,0.821933f}, -{0.252677f,0.61801f,0.744458f},{-0.995603f,0.0694375f,-0.062876f},{-0.984647f,0.104033f,-0.140169f}, -{0.834566f,0.388194f,-0.390903f},{0.776226f,0.519924f,-0.356584f},{0.231295f,-0.0027237f,0.97288f}, -{0.123915f,0.171485f,0.977363f},{-0.278264f,0.885621f,0.371812f},{-0.208952f,0.742162f,0.636816f}, -{0.328508f,0.831803f,0.447423f},{-0.790702f,0.415924f,-0.449218f},{-0.592127f,0.348571f,-0.726556f}, -{-0.830674f,0.424403f,-0.360365f},{-0.62552f,0.184799f,-0.758006f},{-0.707192f,0.19258f,-0.680289f}, -{-0.613638f,0.127083f,-0.779293f},{-0.11711f,0.556457f,-0.822582f},{-0.179243f,-0.348841f,0.919881f}, -{0.214004f,0.377502f,0.900941f},{0.205075f,0.336747f,0.918992f},{0.174838f,0.422701f,0.889244f}, -{0.995333f,0.0317781f,-0.0911228f},{0.190036f,0.479935f,0.856475f},{0.231393f,0.433377f,0.871f}, -{0.0713411f,0.536196f,0.841073f},{0.461727f,-0.531031f,0.710503f},{0.642623f,-0.481829f,0.595716f}, -{0.0459413f,-0.172653f,-0.983911f},{-0.0211f,-0.331505f,-0.943218f},{-0.40236f,-0.188535f,-0.895858f}, -{-0.0220802f,0.497801f,0.86701f},{-0.261378f,0.528952f,0.807398f},{-0.310862f,0.246872f,0.917834f}, -{0.0737522f,0.294186f,0.952898f},{-0.0285864f,0.741674f,0.670151f},{0.130811f,0.779388f,0.612734f}, -{-0.329985f,0.70659f,0.625971f},{-0.150434f,0.157564f,-0.975983f},{-0.491554f,0.143916f,-0.858873f}, -{-0.549467f,0.207409f,-0.809362f},{0.0243846f,-0.998918f,-0.0395968f},{0.0154174f,-0.997923f,-0.0625502f}, -{-9.56649e-05f,-0.999511f,-0.031265f},{-0.0736797f,-0.847043f,0.526393f},{0.264565f,-0.526851f,0.807734f}, -{0.0778558f,-0.911717f,0.403374f},{-0.0722179f,-0.489121f,0.869221f},{-0.0497511f,-0.601297f,0.797475f}, -{0.0221068f,-0.414748f,0.909668f},{-0.024475f,-0.345695f,0.938028f},{-0.00234594f,-0.214816f,0.976652f}, -{-0.13038f,-0.0390172f,0.990696f},{-0.113822f,-0.00113113f,0.9935f},{0.00753531f,0.0784319f,0.996891f}, -{0.162659f,0.186062f,0.96898f},{0.197465f,0.373061f,0.90655f},{0.0726495f,0.270119f,0.960082f}, -{-0.00348439f,0.248771f,0.968556f},{0.132326f,0.32448f,0.936591f},{0.125685f,0.424566f,0.896631f}, -{0.153659f,0.430838f,0.889251f},{0.0608871f,0.531499f,0.844868f},{0.0273649f,0.554899f,0.831467f}, -{0.23097f,0.2435f,0.941998f},{0.391681f,-0.408344f,0.824525f},{-0.0899755f,-0.418629f,0.903689f}, -{-0.028856f,-0.230136f,0.972731f},{0.192937f,0.450434f,0.871714f},{0.304815f,0.404069f,0.862448f}, -{0.121643f,0.294766f,0.947795f},{0.0704177f,0.301208f,0.950955f},{-0.0185777f,0.496515f,0.867829f}, -{0.30631f,-0.12352f,0.943884f},{0.0381325f,-0.0587734f,0.997543f},{0.168162f,0.216513f,0.961688f}, -{0.17938f,0.201108f,0.963005f},{0.752987f,-0.233579f,-0.615184f},{0.752213f,-0.142831f,-0.643253f}, -{0.770254f,-0.2229f,-0.597515f},{-0.0432317f,0.381112f,0.923518f},{0.626181f,-0.779599f,0.0110793f}, -{0.621676f,-0.776663f,0.101553f},{0.521547f,-0.846536f,0.106608f},{0.344659f,-0.897823f,-0.274087f}, -{0.170478f,0.931913f,0.320119f},{0.0544409f,0.956743f,0.285794f},{-0.285666f,0.883754f,0.370641f}, -{-0.486945f,0.656165f,0.576482f},{-0.475298f,0.151973f,-0.8666f},{-0.169494f,-0.477562f,-0.862094f}, -{-0.0223069f,-0.554443f,-0.831923f},{0.19763f,0.0676612f,-0.977939f},{0.12502f,0.240439f,-0.96258f}, -{0.096951f,0.348996f,-0.932096f},{0.106352f,0.388327f,0.915364f},{0.186485f,0.465086f,0.865401f}, -{0.085202f,0.629671f,0.772176f},{-0.640431f,-0.404267f,-0.653006f},{-0.780979f,-0.316721f,-0.538294f}, -{-0.865468f,-0.312483f,-0.39156f},{0.0972787f,-0.197405f,0.975483f},{-0.0786699f,0.434336f,0.897309f}, -{-0.076951f,0.450712f,0.889346f},{-0.0668306f,0.439939f,0.895537f},{0.156395f,0.469386f,0.869033f}, -{0.394926f,-0.0689772f,0.91612f},{0.144095f,0.697262f,0.702184f},{0.268524f,0.659339f,0.702259f}, -{0.137937f,0.582651f,0.800932f},{-0.150031f,0.52089f,0.840336f},{-0.157645f,0.567902f,0.807858f}, -{-0.0967734f,0.630328f,0.770274f},{0.622746f,0.181545f,0.761071f},{0.239313f,0.574192f,0.782964f}, -{0.108848f,0.65576f,0.747082f},{0.711315f,0.172817f,0.681297f},{0.874118f,0.338625f,-0.348211f}, -{0.849983f,0.440713f,-0.288621f},{0.82446f,0.469055f,0.316627f},{0.839611f,0.504318f,0.201784f}, -{0.839027f,0.51583f,0.173072f},{-0.0827286f,0.994895f,-0.0577841f},{-0.0780838f,0.996946f,-0.00112269f}, -{-0.0196054f,0.999522f,-0.0238855f},{0.331187f,0.80367f,0.494399f},{0.464725f,0.734652f,0.494285f}, -{-0.657859f,0.081471f,0.748722f},{-0.601514f,-0.443733f,0.66429f},{-0.284661f,-0.303841f,0.909202f}, -{-0.573281f,0.338316f,-0.746251f},{-0.481339f,0.161541f,-0.861521f},{-0.512204f,0.100924f,-0.852913f}, -{-0.362885f,0.109735f,-0.92535f},{-0.104948f,0.555117f,0.825125f},{0.165705f,0.470171f,0.86688f}, -{0.168167f,0.431262f,0.886416f},{0.162941f,-0.206219f,0.964844f},{-0.0583317f,-0.297053f,0.953078f}, -{0.354429f,-0.0278913f,0.934667f},{0.251351f,0.451336f,0.856223f},{0.280874f,0.479454f,0.831405f}, -{-0.140207f,0.763332f,0.630608f},{0.0888948f,0.824089f,0.559442f},{0.236753f,0.565998f,0.78968f}, -{0.276147f,0.490722f,0.826399f},{0.165164f,0.456367f,0.874328f},{0.884691f,0.122447f,0.44981f}, -{0.470696f,0.578142f,0.666482f},{0.885033f,-0.390748f,0.253046f},{0.629966f,-0.274177f,-0.726615f}, -{0.647971f,-0.158293f,-0.745035f},{0.611495f,-0.0616235f,-0.788845f},{0.639939f,-0.751124f,-0.162144f}, -{0.673719f,-0.105217f,-0.731459f},{0.639491f,-0.601652f,0.478608f},{0.513526f,-0.849419f,0.121567f}, -{0.610413f,-0.647956f,0.455575f},{0.334601f,0.299348f,0.893551f},{0.0441093f,0.711599f,0.7012f}, -{0.181251f,0.742361f,0.645018f},{0.00122868f,0.846108f,0.53301f},{-0.281939f,0.868014f,0.408732f}, -{-0.471426f,0.186922f,-0.861869f},{-0.580898f,0.240434f,-0.777656f},{-0.583811f,0.05227f,-0.810205f}, -{0.0320676f,-0.999039f,-0.0298842f},{0.018545f,-0.99914f,-0.0370961f},{0.0146236f,-0.99989f,-0.0025338f}, -{-0.0617785f,-0.382466f,0.921902f},{-0.00404173f,-0.25056f,0.968093f},{0.122372f,-0.278325f,0.95266f}, -{0.0523822f,-0.161823f,0.985429f},{-0.0111141f,-0.0130864f,0.999853f},{0.0363037f,0.192458f,0.980633f}, -{-0.0404656f,0.143099f,0.988881f},{0.0160188f,0.175992f,0.984261f},{-0.210937f,0.0895094f,0.973393f}, -{-0.109152f,0.311871f,0.943834f},{0.0474923f,0.41157f,0.91014f},{0.0645066f,0.39909f,0.91464f}, -{-0.00659182f,0.496609f,0.867949f},{-0.0486732f,0.553081f,0.831704f},{0.0117206f,0.595074f,0.803586f}, -{0.0765092f,0.587289f,0.805753f},{0.347041f,0.370975f,0.861359f},{0.0452194f,0.614496f,0.787623f}, -{0.00440938f,0.620018f,0.784575f},{0.0859346f,0.688023f,0.720583f},{0.0447807f,0.383445f,0.922477f}, -{0.195784f,0.612478f,0.765859f},{0.145692f,0.695086f,0.704009f},{-0.00125758f,0.736867f,0.676037f}, -{0.24172f,0.519188f,0.819766f},{0.285311f,0.497986f,0.818906f},{0.460259f,-0.715681f,-0.525322f}, -{0.983586f,0.0216339f,0.179138f},{0.943684f,0.208261f,0.257076f},{0.981252f,0.0914436f,0.169655f}, -{0.14865f,0.0434738f,0.987934f},{-0.0207364f,0.626854f,0.778861f},{0.0720591f,0.436571f,0.89678f}, -{0.139963f,0.204553f,0.968797f},{-0.995807f,-0.0625395f,0.066763f},{-0.212539f,0.290927f,-0.932839f}, -{-0.418814f,0.226659f,-0.87933f},{0.0559445f,-0.155077f,0.986317f},{-0.202485f,-0.659883f,-0.723571f}, -{0.271867f,0.425229f,0.863289f},{0.235252f,0.505083f,0.83039f},{-0.14351f,0.68616f,0.713155f}, -{-0.393631f,0.778218f,0.489317f},{-0.220183f,0.743723f,0.631186f},{0.00776272f,0.335611f,0.941969f}, -{0.91128f,0.340794f,-0.231146f},{0.876158f,0.161572f,0.454138f},{0.824709f,0.261035f,0.501712f}, -{-0.512031f,0.218737f,0.83065f},{0.299492f,0.57243f,0.763301f},{0.443001f,0.393616f,0.805491f}, -{-0.350699f,0.860797f,0.368836f},{-0.485456f,0.0648393f,-0.871853f},{-0.570127f,0.246992f,-0.78355f}, -{-0.483251f,-0.0510707f,-0.873991f},{-0.510278f,-0.349902f,-0.785611f},{-0.500311f,-0.348015f,-0.792827f}, -{0.121175f,0.0514562f,-0.991297f},{-0.881409f,-0.279087f,-0.381089f},{-0.51307f,-0.43176f,-0.741851f}, -{-0.655812f,-0.39313f,-0.644484f},{-0.156642f,0.119218f,0.980434f},{-0.0217293f,0.0257904f,0.999431f}, -{-0.637142f,0.394017f,0.66242f},{0.108426f,0.88485f,0.453084f},{0.426378f,-0.185955f,0.885225f}, -{-0.0933708f,0.463751f,0.881032f},{-0.0882345f,0.196905f,0.976444f},{0.0172453f,0.226536f,0.97385f}, -{-0.0643816f,0.38211f,0.921871f},{-0.0728973f,0.463505f,0.88309f},{0.99305f,0.0973475f,0.0661499f}, -{0.993768f,0.090838f,0.0645981f},{0.99306f,0.0946657f,0.0697913f},{0.477953f,0.386349f,0.788857f}, -{0.0407022f,0.683789f,0.728544f},{0.0318931f,0.748566f,0.662293f},{0.181964f,-0.732915f,-0.655534f}, -{0.243709f,0.468222f,0.849337f},{0.153119f,0.654531f,0.740367f},{0.0758308f,0.737048f,0.671573f}, -{-0.17887f,0.637327f,0.749546f},{-0.296601f,0.712073f,0.63638f},{-0.541293f,0.466978f,0.699238f}, -{-0.566555f,0.696726f,0.439987f},{-0.134021f,0.932858f,0.334385f},{0.0218045f,-0.997464f,0.0677578f}, -{0.0271397f,-0.997929f,0.0583154f},{0.0252167f,-0.998572f,0.0471037f},{-0.621606f,-0.278433f,0.732176f}, -{-0.570494f,-0.50975f,0.643965f},{-0.681334f,-0.207411f,0.701972f},{-0.559511f,-0.36605f,-0.74361f}, -{0.493383f,0.0945182f,-0.864661f},{0.584445f,-0.0956861f,-0.805772f},{0.611821f,0.0811993f,-0.786817f}, -{0.0579123f,-0.316769f,0.946733f},{-0.0781528f,-0.569633f,0.818175f},{-0.956457f,0.0398561f,0.289139f}, -{-0.992877f,0.118206f,-0.0149156f},{-0.767728f,-0.639771f,0.0358726f},{0.183182f,-0.187221f,0.965087f}, -{0.158643f,-0.200877f,0.966685f},{0.0660394f,-0.240335f,0.968441f},{0.129513f,-0.216463f,0.967662f}, -{0.17963f,0.059801f,0.981915f},{0.216525f,0.230871f,0.948586f},{0.13377f,-0.00599154f,0.990994f}, -{0.13257f,0.0957259f,0.98654f},{0.147001f,0.309975f,0.939311f},{-0.0514512f,0.392173f,0.918452f}, -{-0.0894499f,0.442738f,0.892178f},{-0.184403f,0.382964f,0.905171f},{-0.0898477f,0.43927f,0.893851f}, -{-0.0528206f,0.613729f,0.787748f},{0.0169646f,0.649408f,0.760251f},{0.0341286f,0.644321f,0.763993f}, -{0.163776f,0.613155f,0.772799f},{-0.346824f,0.758038f,0.552352f},{-0.000173428f,0.166072f,0.986114f}, -{0.94866f,-0.152144f,-0.277302f},{0.980893f,-0.150273f,-0.12356f},{0.938342f,-0.31065f,-0.151691f}, -{0.0818533f,-0.546118f,0.833699f},{0.994091f,0.0830656f,0.0698837f},{0.993339f,0.0874817f,0.0749937f}, -{-0.227107f,0.963144f,-0.144138f},{-0.10378f,-0.515128f,0.850807f},{0.315257f,0.0500735f,0.947684f}, -{0.264788f,0.243961f,0.932936f},{0.296313f,0.265407f,0.917473f},{0.328641f,0.479454f,0.813707f}, -{0.229493f,0.231328f,0.945421f},{0.336808f,0.301467f,0.892008f},{0.232176f,0.498203f,0.835397f}, -{0.962198f,0.269438f,0.0397187f},{0.953436f,0.295835f,-0.0586683f},{0.942935f,0.331643f,0.0297584f}, -{0.198719f,0.341757f,0.918538f},{0.44266f,0.233925f,0.865639f},{-0.302218f,-0.92107f,0.245547f}, -{-0.468439f,-0.879868f,0.079976f},{-0.273014f,-0.949768f,-0.152985f},{0.279096f,0.87342f,0.399053f}, -{0.109999f,0.983735f,0.142007f},{0.33597f,0.0460234f,0.940748f},{-0.0755804f,0.557677f,0.82661f}, -{0.469378f,0.32101f,0.822579f},{-0.417018f,0.693837f,0.587099f},{-0.996744f,-0.0371724f,0.071545f}, -{0.202101f,0.433878f,0.878012f},{0.129701f,0.607736f,0.783476f},{0.0245816f,0.617155f,0.786458f}, -{-0.161749f,0.406649f,0.899152f},{-0.370242f,0.314838f,0.873956f},{0.32194f,0.854132f,0.408429f}, -{0.229067f,0.971816f,-0.0556929f},{-0.215353f,-0.504048f,0.836397f},{-0.165197f,-0.256448f,0.952336f}, -{-0.0296231f,-0.105381f,0.993991f},{0.0989861f,-0.0810697f,0.991781f},{0.323746f,-0.196872f,0.925435f}, -{0.343294f,-0.202076f,0.917232f},{0.380756f,0.0343652f,0.924037f},{0.377954f,0.253315f,0.890496f}, -{0.352625f,0.162393f,0.921566f},{0.235176f,0.0282973f,0.971541f},{0.278209f,0.0768769f,0.957439f}, -{0.18879f,0.230847f,0.954499f},{0.100841f,0.209443f,0.972607f},{-0.0516694f,0.294141f,0.954364f}, -{-0.0359873f,0.460162f,0.887105f},{0.0150075f,0.491099f,0.870974f},{0.0174386f,0.594376f,0.803998f}, -{0.0599003f,0.645757f,0.76119f},{0.0237448f,0.664496f,0.746914f},{-0.0117554f,0.643774f,0.765126f}, -{-0.00783735f,0.687184f,0.726442f},{-0.0677373f,0.701206f,0.709734f},{0.14681f,0.639021f,0.755049f}, -{0.161852f,-0.766316f,-0.621742f},{0.313848f,0.615783f,0.722711f},{0.357795f,-0.814972f,-0.455855f}, -{0.577611f,-0.687363f,-0.440337f},{0.0259876f,-0.995169f,0.0946733f},{0.030387f,-0.997499f,0.063819f}, -{0.0198724f,-0.996136f,0.0855424f},{0.368854f,0.251844f,0.894719f},{0.22752f,0.354024f,0.907139f}, -{0.0791086f,0.471746f,0.878178f},{0.827758f,0.36769f,-0.423817f},{0.845807f,0.387535f,-0.366644f}, -{0.240204f,0.227618f,0.943659f},{-0.370066f,0.155968f,0.915819f},{0.022549f,-0.996353f,0.0822977f}, -{0.0262031f,-0.996683f,0.0770459f},{0.416581f,-0.150609f,0.896536f},{0.835817f,-0.534199f,-0.126657f}, -{0.903079f,-0.405958f,-0.140167f},{0.222092f,-0.177756f,0.958686f},{0.0364991f,-0.0694043f,0.996921f}, -{0.0238959f,-0.354001f,0.93494f},{0.230632f,0.323804f,0.917583f},{0.144523f,-0.435178f,0.888669f}, -{-0.1093f,-0.210785f,0.971403f},{0.821085f,0.318833f,-0.47346f},{-0.754431f,0.566074f,-0.332255f}, -{-0.757881f,0.423543f,-0.496214f},{-0.82559f,0.142302f,-0.546032f},{-0.847383f,-0.192788f,-0.494747f}, -{-0.914762f,-0.15749f,-0.372033f},{0.276055f,0.263812f,0.924228f},{-0.0770771f,-0.957563f,-0.277728f}, -{0.139261f,-0.967934f,-0.209068f},{0.321303f,-0.905573f,-0.276951f},{0.431478f,0.0505868f,0.900704f}, -{0.249985f,0.200263f,0.947313f},{0.304387f,0.278311f,0.910984f},{-0.0499762f,-0.111835f,0.992469f}, -{0.246997f,0.369415f,0.895838f},{0.101263f,0.450865f,0.886829f},{0.0237676f,0.496742f,0.867573f}, -{-0.0263901f,0.301213f,0.953192f},{-0.141989f,0.111937f,0.983519f},{-0.179951f,-0.0840402f,0.980079f}, -{-0.385978f,-0.0345515f,0.921861f},{-0.371599f,-0.166121f,0.91341f},{-0.125026f,-0.19002f,0.973787f}, -{-0.373366f,-0.377655f,0.847333f},{-0.2744f,-0.324717f,0.905132f},{-0.0187571f,-0.159511f,0.987018f}, -{-0.0551324f,-0.088822f,0.994521f},{-0.0779111f,0.0233012f,0.996688f},{-0.05431f,0.00554412f,0.998509f}, -{0.0110976f,-0.11894f,0.992839f},{0.116006f,0.0256008f,0.992919f},{-0.00714221f,0.0399637f,0.999176f}, -{0.0629546f,0.0515646f,0.996683f},{0.104175f,0.0657995f,0.99238f},{0.123421f,0.142901f,0.982011f}, -{0.199415f,0.236513f,0.950944f},{0.161276f,0.334805f,0.928383f},{0.278101f,0.368124f,0.887211f}, -{0.249535f,0.469344f,0.847023f},{0.138731f,0.593551f,0.792749f},{0.0456122f,0.613564f,0.788327f}, -{0.00602844f,0.598335f,0.801223f},{-0.0404027f,0.653058f,0.756229f},{-0.113609f,0.70629f,0.698747f}, -{-0.149797f,0.759453f,0.633082f},{-0.0969605f,0.157836f,0.982693f},{0.30952f,0.343163f,0.886812f}, -{0.250895f,0.239551f,0.937905f},{0.364302f,0.214746f,0.906183f},{0.677437f,-0.618769f,-0.39775f}, -{0.403265f,0.340129f,0.849523f},{0.361741f,-0.604613f,-0.709638f},{0.520482f,-0.635742f,-0.570027f}, -{-0.220053f,-0.110282f,0.969234f},{0.664744f,0.0727426f,-0.743522f},{0.69493f,-0.197012f,-0.691562f}, -{0.711349f,0.0990558f,-0.695824f},{0.791779f,-0.192596f,-0.579648f},{0.83793f,0.467497f,-0.281638f}, -{0.803215f,0.569194f,-0.175682f},{0.265927f,-0.221511f,-0.938198f},{0.672927f,-0.206138f,-0.710405f}, -{0.543858f,0.126279f,-0.829622f},{0.543691f,-0.808459f,0.225377f},{0.576513f,-0.710938f,0.402741f}, -{-0.956165f,0.283562f,0.0730865f},{-0.822554f,0.542533f,0.170477f},{-0.703695f,0.6879f,0.177784f}, -{-0.602137f,0.489269f,-0.630909f},{-0.532431f,0.695438f,-0.48258f},{-0.390972f,0.546069f,-0.740911f}, -{-0.255914f,-0.161228f,0.95316f},{-0.384142f,-0.179441f,0.905669f},{0.243979f,0.262092f,0.933693f}, -{0.99634f,-0.0342846f,0.0783019f},{0.709472f,-0.120123f,-0.694421f},{-0.0644449f,0.0727868f,0.995263f}, -{0.775684f,0.125389f,-0.618541f},{-0.107575f,-0.100668f,0.989087f},{0.0138886f,0.527502f,0.84944f}, -{-0.772811f,0.296101f,-0.561326f},{-0.742911f,0.337742f,-0.577939f},{-0.795858f,0.46664f,-0.385819f}, -{0.475773f,0.8358f,-0.274004f},{0.0905323f,0.351765f,0.9317f},{0.141134f,0.3954f,0.907601f}, -{0.181249f,0.353132f,0.917849f},{0.0710616f,0.250557f,0.96549f},{0.0950218f,0.0252217f,0.995156f}, -{0.093489f,-0.137843f,0.986032f},{0.0487989f,-0.207587f,0.976999f},{-0.183824f,-0.148739f,0.97164f}, -{-0.0301569f,-0.227055f,0.973415f},{-0.292098f,-0.143991f,0.945487f},{-0.075602f,-0.179751f,0.980803f}, -{-0.0417652f,-0.0777302f,0.996099f},{0.00792155f,-0.0523368f,0.998598f},{0.0587392f,-0.104993f,0.992737f}, -{0.118203f,-0.133507f,0.983974f},{0.0643076f,-0.145089f,0.987327f},{-0.146965f,-0.0484049f,0.987957f}, -{-0.147134f,-0.0615316f,0.987201f},{-0.108244f,0.067494f,0.991831f},{-0.113617f,0.0485507f,0.992338f}, -{-0.238631f,0.0427924f,0.970167f},{-0.16731f,0.157442f,0.973252f},{-0.0554322f,0.247821f,0.967219f}, -{0.0215249f,0.307941f,0.951162f},{0.089122f,0.370946f,0.924368f},{0.173814f,0.436493f,0.882758f}, -{0.334641f,0.366563f,0.868129f},{0.201333f,0.420597f,0.884626f},{0.107478f,0.499501f,0.859621f}, -{0.0882418f,0.554137f,0.827735f},{0.077174f,0.58413f,0.807983f},{0.0904184f,0.624511f,0.775765f}, -{0.1935f,0.237683f,0.951874f},{0.305689f,0.296056f,0.904934f},{0.327435f,0.184844f,0.926617f}, -{0.269561f,-0.927616f,-0.258583f},{0.193659f,-0.895755f,-0.400149f},{0.394681f,0.163975f,0.904068f}, -{0.258788f,0.468532f,0.844693f},{-0.0718795f,0.684182f,0.72576f},{0.683177f,-0.701106f,-0.204252f}, -{-0.516514f,0.796576f,-0.314133f},{-0.720986f,-0.629455f,-0.289768f},{-0.779838f,-0.545293f,-0.307421f}, -{-0.89702f,-0.302861f,-0.321916f},{-0.483131f,0.87171f,0.081887f},{-0.489306f,0.870408f,-0.0544948f}, -{-0.717503f,0.692303f,0.0768497f},{0.957277f,-0.28916f,0.00262905f},{0.988026f,-0.0837556f,0.129575f}, -{0.24601f,0.323063f,0.913843f},{0.604944f,-0.0749556f,0.792732f},{-0.155861f,0.510953f,0.84536f}, -{0.149976f,0.587202f,0.795425f},{0.0187902f,-0.999677f,-0.0171062f},{0.025801f,-0.999018f,-0.036023f}, -{-0.00739064f,-0.99961f,-0.0269177f},{0.678978f,-0.0969692f,0.727727f},{0.269299f,0.415525f,0.868802f}, -{-0.549574f,0.640399f,-0.536524f},{-0.634002f,0.560561f,-0.532741f},{0.629036f,0.600896f,0.49319f}, -{0.7046f,0.544216f,0.455376f},{0.806775f,0.433922f,0.401032f},{-0.349169f,0.43309f,0.830972f}, -{-0.249238f,0.533495f,0.808247f},{0.223309f,0.158713f,0.96174f},{0.283775f,-0.0974038f,0.953931f}, -{0.297757f,-0.225284f,0.927679f},{0.293836f,-0.261361f,0.91943f},{0.182718f,-0.192612f,0.964113f}, -{0.147264f,-0.0935166f,0.984666f},{0.147869f,-0.0805794f,0.985719f},{0.122862f,-0.0441671f,0.991441f}, -{0.153585f,-0.0603901f,0.986288f},{0.248531f,-0.180208f,0.951713f},{0.192206f,-0.16612f,0.967192f}, -{0.160758f,-0.0170921f,0.986846f},{0.124724f,0.00386966f,0.992184f},{-0.152929f,-0.176901f,0.972275f}, -{-0.334302f,-0.0505609f,0.941109f},{-0.325042f,0.204004f,0.923434f},{-0.273952f,0.324963f,0.905179f}, -{-0.183982f,0.404077f,0.896031f},{-0.12399f,0.480282f,0.868306f},{-0.132311f,0.480545f,0.866931f}, -{-0.0539982f,0.44909f,0.891853f},{0.0186793f,0.528253f,0.848882f},{0.0540516f,0.603218f,0.795742f}, -{0.0574721f,0.58057f,0.812179f},{0.0488323f,0.540287f,0.840063f},{0.107447f,0.574946f,0.811106f}, -{0.836185f,-0.500978f,-0.223194f},{0.706114f,-0.222507f,-0.672231f},{0.73513f,-0.156479f,-0.659619f}, -{0.994869f,-0.0398721f,0.0929814f},{0.990704f,-0.0215186f,0.134325f},{0.601227f,0.0332051f,0.798388f}, -{-0.612229f,0.501538f,-0.611258f},{-0.604453f,0.336919f,-0.721888f},{-0.644079f,0.465962f,-0.606664f}, -{0.389418f,-0.206132f,0.897699f},{-0.210594f,-0.0239807f,0.977279f},{0.823588f,0.444795f,-0.351937f}, -{0.523614f,-0.851564f,-0.0258188f},{0.333779f,-0.101378f,0.937184f},{-0.239351f,0.170456f,0.955853f}, -{-0.191253f,0.418555f,0.887826f},{0.993128f,0.0770358f,-0.0881045f},{0.995817f,0.0332385f,-0.0851114f}, -{0.266118f,-0.0563829f,0.96229f},{-0.0899401f,0.57571f,0.812692f},{0.941752f,-0.336122f,0.0111489f}, -{0.956098f,-0.268499f,0.117412f},{0.972634f,0.227543f,0.0469783f},{0.0213099f,0.745877f,0.665743f}, -{-0.855257f,-0.0444835f,-0.516291f},{-0.204136f,0.966554f,0.155248f},{-0.942272f,0.216219f,0.25568f}, -{-0.980849f,0.182453f,0.0681602f},{-0.936853f,0.329239f,0.117934f},{-0.944458f,0.300396f,0.133269f}, -{0.405896f,0.789675f,0.460067f},{-0.883435f,0.0948705f,-0.458849f},{-0.229111f,-0.238176f,0.943812f}, -{-0.527854f,-0.219852f,0.820387f},{-0.493896f,-0.293839f,0.818368f},{-0.00712655f,-0.57856f,-0.815609f}, -{-0.147518f,0.530632f,0.834666f},{0.0992787f,0.378513f,0.920256f},{0.343353f,-0.0633624f,0.937067f}, -{0.371543f,-0.190139f,0.908737f},{0.393868f,-0.207759f,0.895379f},{0.457386f,-0.212961f,0.863392f}, -{0.399808f,-0.170027f,0.900691f},{0.310284f,-0.131588f,0.941493f},{0.326734f,-0.070236f,0.942503f}, -{0.21877f,-0.0742296f,0.972949f},{0.146588f,-0.114926f,0.982499f},{0.141094f,-0.138347f,0.980282f}, -{0.23755f,-0.115062f,0.964537f},{-0.0402561f,-0.269229f,0.962234f},{-0.0749331f,-0.492771f,0.866927f}, -{-0.0734197f,-0.180458f,0.980839f},{-0.00426604f,-0.0404009f,0.999174f},{-0.139124f,0.0871926f,0.986429f}, -{-0.215771f,0.244843f,0.945249f},{-0.234529f,0.347527f,0.907866f},{-0.224147f,0.450837f,0.864005f}, -{-0.2128f,0.484206f,0.848682f},{-0.219569f,0.479628f,0.849557f},{-0.187117f,0.496056f,0.847889f}, -{-0.188186f,0.459699f,0.867907f},{-0.112351f,0.57933f,0.807313f},{-0.106516f,0.620372f,0.777041f}, -{-0.0679673f,0.612159f,0.787808f},{-0.0326748f,0.580432f,0.813653f},{0.708862f,-0.680962f,-0.183865f}, -{0.847722f,-0.484375f,-0.216214f},{0.712572f,-0.688967f,-0.132536f},{0.645906f,-0.7559f,-0.10687f}, -{0.856818f,-0.487012f,-0.169355f},{0.26561f,0.427572f,0.86408f},{0.141693f,-0.00470907f,0.989899f}, -{0.142388f,-0.0233211f,0.989536f},{0.685045f,-0.0214021f,0.728186f},{-0.0585203f,0.657791f,0.750924f}, -{0.318829f,0.468303f,0.824039f},{-0.90048f,0.294898f,-0.319642f},{0.420585f,-0.898045f,-0.12893f}, -{0.0573068f,-0.998341f,0.0055865f},{0.367788f,-0.911475f,-0.184246f},{0.372103f,-0.184797f,0.909609f}, -{0.397361f,-0.124209f,0.909217f},{0.370408f,-0.452713f,0.811078f},{0.339769f,-0.0340991f,0.939891f}, -{0.375499f,0.00669293f,0.926799f},{0.331121f,0.0276573f,0.943183f},{0.189051f,0.235624f,0.953279f}, -{0.234955f,-0.852191f,-0.467511f},{0.207225f,0.610604f,0.764343f},{0.0281201f,0.611182f,0.79099f}, -{0.592523f,0.330052f,-0.734834f},{0.299147f,-0.110764f,0.947757f},{0.289337f,-0.160901f,0.943607f}, -{-0.184455f,0.301245f,0.935536f},{-0.114346f,0.596484f,0.794438f},{0.892855f,0.450094f,-0.0149891f}, -{0.914416f,0.403767f,0.0285614f},{0.995914f,0.0792773f,0.0432455f},{0.995102f,0.0918383f,0.0365886f}, -{0.0882295f,0.392913f,0.915333f},{0.01841f,0.690734f,0.722874f},{-0.998624f,0.0360211f,-0.0381155f}, -{-0.484736f,-0.873671f,0.0415806f},{-0.28694f,-0.957947f,-0.00188694f},{-0.116894f,-0.567173f,-0.815261f}, -{-0.125439f,-0.579902f,-0.804971f},{-0.241306f,-0.463478f,-0.852619f},{-0.0328103f,-0.946855f,-0.319981f}, -{0.139347f,-0.115985f,0.983428f},{0.116236f,0.0964643f,0.988526f},{0.254626f,-0.170183f,0.951947f}, -{0.391114f,-0.158803f,0.906538f},{0.396983f,-0.203492f,0.894984f},{0.234799f,-0.252738f,0.938612f}, -{0.165006f,-0.21172f,0.9633f},{0.0594331f,-0.20056f,0.977877f},{-0.0990798f,-0.253082f,0.962358f}, -{-0.226912f,-0.176631f,0.957764f},{-0.600585f,-0.199035f,0.774392f},{-0.44706f,-0.174961f,0.877226f}, -{-0.582458f,-0.189821f,0.790386f},{-0.258913f,0.0474452f,0.964735f},{-0.175723f,-0.096949f,0.979654f}, -{-0.0275966f,-0.125996f,0.991647f},{-0.0882985f,0.00297313f,0.99609f},{0.00613423f,0.264642f,0.964327f}, -{-0.0170057f,0.424457f,0.905289f},{-0.110029f,0.389298f,0.914516f},{-0.173889f,0.480406f,0.859635f}, -{-0.174587f,0.499081f,0.848786f},{-0.169377f,0.539228f,0.824951f},{-0.13077f,0.599198f,0.789849f}, -{-0.104141f,0.592033f,0.799156f},{-0.100737f,0.612211f,0.784251f},{0.46098f,-0.118947f,0.879403f}, -{0.507723f,-0.148427f,0.848638f},{0.460838f,-0.157004f,0.873486f},{0.0273984f,0.521382f,0.852883f}, -{-0.0558594f,0.574362f,0.816694f},{-0.230996f,-0.451956f,-0.861612f},{-0.603773f,-0.456419f,-0.653559f}, -{0.0498736f,0.479708f,0.87601f},{0.592333f,0.0544071f,0.803854f},{0.982772f,0.101441f,0.154494f}, -{0.979766f,0.0918827f,0.177808f},{0.690902f,-0.676617f,-0.254645f},{0.680962f,-0.684141f,-0.261232f}, -{0.514943f,-0.723689f,-0.459465f},{0.279941f,0.804335f,-0.524097f},{0.470227f,0.806527f,-0.35833f}, -{0.602172f,0.587103f,-0.541017f},{0.219912f,-0.190058f,0.956827f},{-0.707877f,-0.246572f,0.6619f}, -{0.223647f,-0.190582f,0.955856f},{0.66088f,-0.466033f,0.58826f},{0.78418f,-0.57566f,0.231685f}, -{-0.0963573f,0.66013f,0.744945f},{0.529264f,0.395781f,0.750491f},{-0.0938949f,0.51176f,0.853982f}, -{0.0167492f,0.0845491f,0.996279f},{-0.29642f,0.739859f,0.60394f},{0.770559f,-0.27077f,0.576994f}, -{0.395407f,0.551992f,-0.734138f},{0.426667f,0.671553f,-0.605781f},{-0.678447f,-0.734224f,-0.0250153f}, -{-0.670977f,-0.740986f,-0.0270176f},{0.719373f,0.597154f,0.354838f},{-0.0260727f,-0.794933f,-0.606137f}, -{-0.488692f,-0.0546212f,-0.870745f},{-0.85862f,-0.0460396f,0.510541f},{-0.833654f,-0.028232f,0.551565f}, -{-0.711846f,-0.0760288f,0.698209f},{-0.794342f,0.00412712f,0.607457f},{0.916589f,-0.296311f,-0.268448f}, -{-0.756481f,-0.335536f,0.561384f},{-0.448513f,0.655173f,0.607935f},{-0.491382f,0.649039f,0.580769f}, -{-0.359336f,0.545212f,0.757378f},{-0.454763f,0.408567f,0.791368f},{-0.360103f,0.534543f,0.764585f}, -{-0.419343f,0.270591f,0.866563f},{-0.308274f,-0.158676f,0.937971f},{-0.537672f,-0.0421306f,0.842101f}, -{-0.173191f,-0.228231f,0.958079f},{-0.435517f,-0.276149f,0.856777f},{-0.530407f,-0.286438f,0.797886f}, -{-0.554514f,-0.329894f,0.763992f},{0.785711f,0.574001f,0.230609f},{0.741859f,0.498856f,0.448093f}, -{0.303562f,0.946105f,-0.112852f},{0.307248f,0.93655f,-0.168738f},{0.395085f,-0.646918f,0.652231f}, -{0.259732f,-0.848029f,-0.461938f},{0.238077f,0.968948f,0.0667771f},{0.222715f,0.964406f,0.142547f}, -{0.332458f,0.941366f,0.0574574f},{0.331289f,0.943105f,-0.0283073f},{-0.673966f,0.432063f,0.599243f}, -{-0.361289f,0.120792f,0.924597f},{0.081858f,0.397394f,0.91399f},{-0.0442536f,0.141609f,0.988933f}, -{-0.224349f,0.314769f,0.922273f},{-0.186689f,0.451686f,0.872426f},{-0.208966f,0.483781f,0.849876f}, -{-0.23828f,0.528725f,0.814661f},{-0.226058f,0.597649f,0.769229f},{-0.20108f,0.616536f,0.761216f}, -{-0.186587f,0.626264f,0.756954f},{-0.485704f,0.733153f,-0.476002f},{-0.584897f,0.708289f,-0.395249f}, -{0.91989f,0.0667137f,-0.386461f},{0.955529f,0.0549196f,-0.289737f},{0.911446f,-0.138902f,-0.387263f}, -{0.167664f,-0.966341f,-0.195126f},{0.479603f,-0.758439f,-0.441306f},{0.196735f,0.71953f,0.666013f}, -{0.178837f,0.211949f,0.960778f},{-0.365789f,0.882121f,0.29675f},{0.108296f,-0.661035f,-0.742499f}, -{0.231028f,-0.767635f,-0.597799f},{0.528096f,-0.802825f,-0.276744f},{0.812965f,-0.521272f,-0.259545f}, -{-0.707441f,-0.161392f,0.688099f},{-0.818057f,-0.262473f,0.511752f},{-0.83981f,-0.276777f,0.467027f}, -{-0.78566f,-0.272051f,0.555632f},{0.232743f,0.972491f,0.00962511f},{0.319045f,0.947739f,-0.000587843f}, -{0.355517f,0.91667f,-0.182547f},{0.0257376f,0.844356f,0.535164f},{-0.344861f,0.925942f,0.153956f}, -{-0.322892f,0.577177f,0.750071f},{0.979764f,0.15852f,-0.122203f},{-0.157063f,0.691591f,-0.705006f}, -{-0.217615f,0.649092f,-0.728919f},{-0.115872f,0.61574f,-0.779383f},{-0.156937f,-0.472687f,0.867143f}, -{0.375524f,0.469339f,0.799189f},{0.963422f,-0.262459f,-0.0541521f},{0.543008f,-0.133496f,0.829048f}, -{-0.0294388f,0.115412f,0.992881f},{-0.315051f,0.544746f,0.777171f},{-0.201034f,0.829596f,0.520919f}, -{-0.347545f,0.838266f,0.420146f},{-0.744658f,-0.0792734f,0.662722f},{0.539302f,-0.77313f,0.333802f}, -{0.503749f,-0.7688f,0.393933f},{-0.809059f,-0.0753806f,0.582874f},{-0.776578f,-0.217583f,0.591257f}, -{0.192285f,0.832061f,0.520289f},{0.0294664f,0.792039f,0.60976f},{0.0936467f,0.940049f,0.32793f}, -{0.0481948f,0.903587f,0.425685f},{0.62816f,0.554507f,0.545836f},{0.946447f,-0.0202053f,0.322227f}, -{0.123842f,0.398537f,0.908753f},{0.0566087f,-0.712531f,-0.699353f},{0.00541469f,-0.696924f,-0.717125f}, -{-0.827308f,0.442382f,-0.346208f},{0.945003f,0.288744f,-0.153608f},{-0.164464f,0.501697f,-0.849265f}, -{-0.212151f,0.449517f,-0.867713f},{-0.217853f,0.603f,-0.767418f},{0.777729f,-0.537961f,-0.32517f}, -{-0.350058f,0.556344f,0.753619f},{-0.438878f,0.486298f,0.75558f},{-0.340488f,0.390308f,0.855411f}, -{-0.249393f,0.667711f,0.701402f},{-0.169594f,0.899467f,0.402737f},{-0.40296f,0.798587f,0.447082f}, -{-0.260137f,0.904443f,0.3381f},{0.297284f,0.951307f,0.0814693f},{0.747864f,0.242122f,-0.618124f}, -{0.693854f,0.21889f,-0.686042f},{0.706679f,0.147446f,-0.692f},{0.0362161f,0.811871f,0.582713f}, -{-0.402915f,0.903071f,0.148733f},{-0.611856f,0.758072f,-0.225742f},{-0.60932f,0.783829f,-0.119753f}, -{0.566185f,0.76417f,0.308997f},{-0.331398f,0.274975f,0.902532f},{-0.286723f,-0.182686f,0.940434f}, -{-0.34717f,0.076168f,0.934704f},{-0.172591f,0.212991f,0.96169f},{-0.1893f,0.383452f,0.903953f}, -{-0.231082f,0.432922f,0.871309f},{-0.25959f,0.479614f,0.838202f},{-0.318628f,0.575777f,0.752966f}, -{-0.322619f,0.603173f,0.729451f},{-0.358251f,0.602617f,0.713098f},{-0.390833f,0.610403f,0.688954f}, -{0.388946f,-0.442439f,0.808065f},{0.477906f,0.397443f,0.783355f},{0.661378f,0.162789f,0.732174f}, -{-0.0720481f,0.806311f,0.587088f},{-0.565128f,0.822945f,0.0582318f},{-0.390616f,0.822168f,0.414076f}, -{-0.573525f,0.52527f,0.628618f},{-0.82288f,-0.00567614f,0.568186f},{-0.714775f,0.20468f,0.668733f}, -{0.19947f,0.979005f,0.0419626f},{0.340742f,0.93565f,0.0919395f},{-0.777639f,-0.623126f,-0.0836173f}, -{-0.828711f,-0.559582f,0.0103613f},{-0.771514f,-0.606945f,-0.190748f},{-0.685167f,-0.680435f,-0.259914f}, -{-0.953719f,0.300617f,0.00699773f},{-0.873393f,0.41651f,-0.252396f},{-0.829566f,0.377382f,-0.411587f}, -{0.500125f,0.797272f,0.337983f},{-0.502071f,0.526381f,0.686184f},{-0.419821f,0.617315f,0.665336f}, -{-0.961307f,0.160805f,-0.223676f},{0.989366f,0.0418744f,-0.139286f},{0.958475f,0.210319f,-0.192591f}, -{0.00907638f,0.395259f,0.918525f},{-0.121054f,0.339914f,0.932633f},{-0.0711068f,0.590537f,0.803872f}, -{-0.0363095f,0.710202f,0.703061f},{0.0153851f,0.853133f,0.521466f},{-0.112796f,0.818173f,0.5638f}, -{-0.16988f,0.971006f,-0.168189f},{-0.0077306f,0.996263f,-0.0860288f},{0.243035f,0.943361f,0.22584f}, -{-0.0621356f,0.340509f,0.938186f},{-0.0491563f,0.366334f,0.929184f},{-0.176118f,0.454378f,0.873226f}, -{-0.189273f,0.46869f,0.862847f},{-0.259481f,0.511469f,0.819188f},{-0.449379f,0.578745f,0.680524f}, -{0.0913338f,0.894366f,0.437912f},{0.42565f,-0.422144f,0.800385f},{0.59795f,-0.23884f,0.765122f}, -{0.45527f,-0.177488f,0.872483f},{0.386973f,-0.921255f,-0.0392579f},{0.250819f,-0.869655f,-0.425193f}, -{0.306345f,-0.905818f,-0.292655f},{-0.99852f,-0.0334156f,-0.0429045f},{-0.964216f,0.258421f,0.0592056f}, -{-0.987625f,0.154451f,-0.027243f},{-0.997861f,0.0519454f,-0.0396961f},{-0.83911f,0.481331f,0.253406f}, -{-0.926526f,-0.254974f,-0.276656f},{-0.692555f,0.643989f,0.325032f},{0.99397f,-0.107486f,0.0217039f}, -{-0.218578f,0.93918f,0.264886f},{-0.304393f,0.911539f,0.276481f},{-0.277804f,0.90877f,0.311387f}, -{-0.30602f,0.562103f,0.768369f},{-0.325307f,0.591888f,0.737457f},{-0.367669f,0.600766f,0.709859f}, -{-0.426539f,0.593136f,0.682828f},{-0.357713f,0.671509f,0.648935f},{-0.343895f,0.711826f,0.612406f}, -{0.540354f,0.319822f,0.778288f},{0.319022f,0.317524f,0.892974f},{0.0164285f,-0.758999f,-0.650885f}, -{0.810879f,0.41449f,-0.413125f},{0.402384f,-0.808354f,-0.429711f},{-0.692319f,0.269885f,0.66922f}, -{-0.802128f,-0.0286058f,0.596466f},{0.0118075f,0.999179f,-0.0387475f},{0.039404f,0.998234f,-0.0444579f}, -{-0.255023f,0.966562f,-0.0268538f},{0.181189f,0.943149f,0.278642f},{0.135556f,0.913165f,0.384388f}, -{0.407739f,0.656533f,0.634597f},{0.297586f,0.648132f,0.700976f},{0.384275f,0.922206f,-0.0432202f}, -{-0.0859526f,-0.656785f,-0.749163f},{0.599155f,-0.0773312f,-0.79689f},{0.324199f,0.527672f,0.785148f}, -{-0.932281f,-0.0620485f,-0.356374f},{-0.977376f,0.113279f,-0.178615f},{-0.93158f,0.134419f,-0.337773f}, -{0.140383f,-0.767237f,-0.625812f},{0.213569f,-0.832839f,-0.510654f},{0.532393f,-0.808362f,-0.251216f}, -{0.246787f,0.464288f,0.850607f},{0.193003f,0.739826f,0.644521f},{0.104031f,0.856008f,0.506387f}, -{-0.184839f,0.440609f,0.878464f},{-0.619346f,0.0773028f,0.781304f},{-0.779055f,-0.108587f,0.61748f}, -{-0.72702f,-0.208468f,0.654204f},{-0.693424f,-0.173667f,0.699288f},{-0.0203507f,0.99796f,-0.060504f}, -{-0.116628f,0.989023f,0.0907244f},{-0.138448f,0.974696f,0.175497f},{-0.118911f,0.974713f,0.189194f}, -{0.782602f,0.568475f,0.253714f},{-0.660721f,-0.733755f,-0.158278f},{0.956928f,0.238728f,0.165221f}, -{0.549901f,0.697434f,0.45956f},{0.499327f,0.758075f,0.419517f},{0.607054f,0.674272f,0.420528f}, -{0.512313f,-0.831211f,-0.215926f},{0.697816f,-0.69249f,-0.183061f},{0.0784108f,0.317364f,0.945057f}, -{-0.013652f,0.281661f,0.959417f},{-0.103051f,0.383828f,0.917636f},{-0.184355f,0.471683f,0.862281f}, -{-0.197612f,0.535434f,0.821133f},{-0.240097f,0.582367f,0.776661f},{-0.260232f,0.584369f,0.76863f}, -{-0.342142f,0.66669f,0.662165f},{-0.919643f,0.348874f,-0.180397f},{-0.951161f,0.258725f,-0.168385f}, -{0.196828f,-0.0334996f,0.979865f},{0.93696f,-0.10587f,-0.333013f},{0.898716f,0.0143765f,-0.438296f}, -{0.934906f,-0.162736f,-0.315386f},{-0.0443143f,-0.514988f,-0.856051f},{-0.576905f,-0.302724f,0.758643f}, -{0.999693f,-0.0144882f,0.020096f},{-0.902492f,-0.418109f,-0.103404f},{-0.91035f,-0.370171f,-0.185029f}, -{-0.972665f,0.169551f,-0.158669f},{-0.537973f,0.299744f,0.787869f},{-0.317389f,0.276084f,0.907217f}, -{-0.43221f,0.364523f,0.824814f},{0.530498f,0.182756f,-0.827751f},{0.497767f,0.331848f,-0.801315f}, -{0.59232f,0.276457f,-0.756789f},{0.903917f,-0.0343129f,-0.426329f},{-0.129496f,-0.412196f,-0.901845f}, -{-0.997442f,-0.0199234f,-0.0686405f},{-0.116131f,0.99273f,0.0316319f},{-0.148505f,0.984841f,0.0896363f}, -{-0.107525f,0.991118f,0.0782455f},{0.872192f,0.459426f,0.167954f},{-0.302342f,0.32664f,0.895486f}, -{-0.394453f,0.402819f,0.82592f},{0.988901f,-0.142468f,0.042162f},{-0.28951f,0.597491f,0.747789f}, -{0.886142f,-0.0227338f,-0.462855f},{0.372933f,-0.92439f,-0.080149f},{0.324752f,0.473531f,0.818721f}, -{0.0181762f,0.577834f,0.815952f},{-0.446479f,-0.0219026f,0.894526f},{-0.41044f,-0.340059f,0.846108f}, -{-0.497253f,-0.366512f,0.786389f},{-0.188972f,0.87435f,-0.446992f},{-0.27229f,-0.212348f,0.938492f}, -{-0.260154f,-0.26183f,0.92939f},{-0.424896f,-0.295882f,0.855522f},{-0.201163f,-0.282722f,0.937871f}, -{-0.349685f,-0.362444f,0.863918f},{-0.258599f,-0.400252f,0.879162f},{-0.0663588f,-0.124558f,0.989991f}, -{-0.277381f,-0.292648f,0.915105f},{-0.0129428f,0.051201f,0.998604f},{-0.21943f,0.0640172f,0.973526f}, -{-0.0834486f,0.148989f,0.985311f},{-0.176729f,0.302573f,0.936598f},{-0.303486f,0.374783f,0.876033f}, -{-0.22937f,0.520291f,0.822609f},{-0.399844f,0.536487f,0.743173f},{-0.190027f,0.270728f,0.943714f}, -{-0.0885366f,0.238847f,0.967013f},{-0.208084f,0.301494f,0.930485f},{-0.218262f,0.436112f,0.873022f}, -{-0.209885f,0.518052f,0.829199f},{-0.251104f,0.521023f,0.815771f},{-0.27394f,0.616177f,0.738432f}, -{-0.267678f,0.649952f,0.711275f},{-0.271756f,0.870571f,0.410189f},{-0.248053f,0.886882f,0.389757f}, -{-0.341894f,0.876298f,0.339426f},{0.913529f,-0.0778003f,-0.399265f},{-0.220965f,0.27626f,-0.935337f}, -{0.324672f,0.945114f,-0.0366963f},{-0.549577f,0.390051f,0.7388f},{-0.0404058f,0.453485f,0.890348f}, -{-0.265051f,0.34101f,0.90192f},{0.671957f,0.182503f,-0.717751f},{0.625907f,0.322451f,-0.710116f}, -{0.561647f,-0.442315f,0.699221f},{0.329729f,0.556732f,0.762449f},{0.915737f,0.214104f,-0.339978f}, -{-0.366646f,-0.323675f,0.872241f},{-0.474031f,-0.323003f,0.819124f},{-0.462516f,-0.19931f,0.863918f}, -{-0.60706f,-0.19681f,0.769899f},{-0.413657f,-0.149255f,0.898115f},{-0.551041f,-0.226188f,0.803239f}, -{-0.256732f,-0.2343f,0.937653f},{-0.432891f,0.81967f,0.375162f},{-0.82982f,0.47565f,0.291813f}, -{0.935737f,-0.0667342f,-0.346327f},{0.410462f,-0.898124f,-0.157782f},{0.381093f,0.353227f,0.854399f}, -{0.314514f,0.455215f,0.832983f},{-0.107452f,-0.0516191f,0.992869f},{-0.436083f,-0.386618f,0.812624f}, -{-0.326328f,-0.290193f,0.89961f},{-0.358768f,-0.351906f,0.86455f},{-0.491272f,-0.274142f,0.826739f}, -{-0.249382f,-0.0817001f,0.964953f},{-0.333758f,-0.24729f,0.909644f},{-0.364851f,-0.0687879f,0.928522f}, -{-0.168992f,-0.211946f,0.962559f},{-0.199238f,-0.291946f,0.935453f},{-0.2244f,-0.252274f,0.941277f}, -{-0.152452f,-0.164372f,0.974546f},{-0.112379f,-0.0502458f,0.992394f},{-0.0843327f,0.0708477f,0.993916f}, -{-0.0420862f,0.0883098f,0.995204f},{-0.116689f,0.21632f,0.969324f},{-0.217116f,0.527381f,0.821419f}, -{-0.175943f,0.699574f,0.692561f},{-0.29065f,0.677972f,0.675186f},{-0.207183f,0.94949f,-0.235676f}, -{0.576123f,-0.814343f,-0.0701916f},{-0.330163f,0.771793f,0.543441f},{-0.39642f,0.831665f,0.388823f}, -{-0.396915f,0.639425f,0.658478f},{-0.183854f,0.352624f,0.917526f},{-0.170048f,0.255627f,0.951703f}, -{-0.0999526f,0.132185f,0.986173f},{-0.204507f,0.401786f,0.892605f},{-0.225599f,0.496702f,0.838089f}, -{-0.23613f,0.499564f,0.833474f},{-0.272788f,0.477579f,0.835168f},{-0.255052f,0.575954f,0.776676f}, -{-0.288071f,0.716377f,0.635468f},{0.939666f,0.00962893f,-0.341958f},{0.811251f,-0.550205f,-0.197856f}, -{0.84832f,-0.501184f,-0.170784f},{0.906024f,-0.41817f,-0.0652293f},{0.806224f,0.109341f,0.581419f}, -{0.807168f,0.301015f,0.507808f},{0.391369f,0.919183f,-0.0439765f},{0.259863f,0.0750233f,0.962727f}, -{0.332689f,0.161066f,0.92918f},{0.378018f,0.180702f,0.907992f},{-0.325281f,-0.413715f,0.850313f}, -{0.227168f,-0.359984f,-0.904879f},{0.43349f,-0.686914f,-0.583297f},{0.741767f,0.00615409f,0.670629f}, -{0.519002f,0.52435f,0.67505f},{-0.97891f,0.0906991f,-0.183055f},{0.394563f,0.9161f,0.0712859f}, -{0.389378f,0.920614f,0.0292228f},{0.319017f,0.528083f,0.786992f},{-0.307802f,-0.340839f,0.888306f}, -{-0.126457f,0.361748f,0.92366f},{-0.16985f,0.756014f,0.632134f},{-0.185053f,0.754408f,0.629781f}, -{0.0966245f,-0.689971f,-0.717359f},{0.492416f,-0.869194f,-0.0450272f},{0.996683f,0.0751418f,-0.0312672f}, -{-0.234427f,0.685005f,0.689792f},{0.867339f,0.0838635f,-0.490602f},{0.925486f,0.164947f,-0.34098f}, -{-0.204274f,0.789897f,0.578217f},{0.337036f,-0.144287f,0.93037f},{0.872065f,-0.43883f,-0.216636f}, -{0.723614f,-0.646477f,-0.241765f},{0.303861f,0.202101f,0.931033f},{0.236392f,0.303918f,0.922904f}, -{-0.03706f,-0.379717f,0.92436f},{-0.24061f,-0.442195f,0.864043f},{-0.320437f,-0.327133f,0.88899f}, -{-0.315056f,-0.394821f,0.86305f},{-0.394311f,-0.306347f,0.866412f},{-0.291312f,-0.0705552f,0.954023f}, -{-0.304152f,-0.0144173f,0.952514f},{-0.286319f,-0.243813f,0.926594f},{-0.315406f,-0.311765f,0.896282f}, -{-0.305999f,-0.289459f,0.906961f},{-0.243082f,-0.160769f,0.95659f},{-0.139866f,-0.103768f,0.984718f}, -{-0.0984367f,0.00461992f,0.995133f},{0.000226813f,0.128423f,0.991719f},{-0.0322293f,0.139832f,0.989651f}, -{-0.0463018f,0.159905f,0.986046f},{-0.0349219f,0.332265f,0.942539f},{-0.0872712f,0.549626f,0.83084f}, -{-0.143092f,0.695318f,0.704314f},{0.844038f,0.41268f,0.342484f},{-0.0784911f,0.135579f,0.987653f}, -{-0.151022f,0.495322f,0.855482f},{0.864087f,-0.487038f,-0.127076f},{-0.118199f,0.191062f,0.974435f}, -{-0.128221f,0.381595f,0.915393f},{-0.16414f,0.47926f,0.862188f},{-0.180967f,0.512564f,0.839363f}, -{-0.147203f,0.567275f,0.810266f},{-0.151267f,0.653834f,0.741363f},{-0.188561f,0.726022f,0.661315f}, -{0.824964f,-0.480821f,-0.297061f},{0.905172f,0.260718f,-0.335692f},{-0.751042f,0.593217f,0.289876f}, -{-0.32475f,-0.35033f,-0.878525f},{-0.481899f,-0.260127f,-0.836724f},{-0.624517f,-0.221113f,-0.749058f}, -{-0.103103f,0.445253f,0.889449f},{-0.309203f,-0.277461f,-0.90962f},{-0.294538f,-0.402181f,-0.86689f}, -{0.864967f,0.288401f,-0.410679f},{0.897178f,0.275958f,-0.344845f},{0.767788f,-0.232109f,0.597183f}, -{0.760242f,-0.495563f,0.420059f},{0.808193f,-0.49461f,0.319663f},{0.733594f,0.286807f,-0.616102f}, -{0.640647f,0.238282f,-0.729927f},{-0.281333f,-0.352565f,0.892496f},{-0.232012f,0.873399f,0.428187f}, -{-0.248022f,0.814072f,0.52514f},{0.837728f,-0.0939249f,-0.53795f},{0.870655f,-0.279119f,-0.405033f}, -{-0.881121f,-0.0520004f,-0.470024f},{-0.0155312f,-0.466044f,0.884625f},{-0.129013f,-0.669994f,0.73107f}, -{-0.197947f,-0.475871f,0.85695f},{-0.244171f,-0.34963f,0.90451f},{-0.140039f,-0.330352f,0.933411f}, -{-0.057988f,-0.337569f,0.939513f},{-0.122666f,-0.113582f,0.985927f},{-0.205128f,-0.00877181f,0.978696f}, -{-0.337272f,-0.0746786f,0.938441f},{-0.306237f,-0.258884f,0.916078f},{-0.278592f,-0.259471f,0.924695f}, -{-0.261924f,-0.250819f,0.931926f},{-0.209337f,-0.0287817f,0.97742f},{-0.0601506f,0.0672967f,0.995918f}, -{0.0611062f,0.0532419f,0.99671f},{0.0713627f,0.103608f,0.992055f},{-0.0695355f,0.803765f,0.59087f}, -{-0.644098f,0.273464f,-0.714392f},{0.0732606f,0.601907f,0.795199f},{-0.2022f,0.892853f,0.402403f}, -{-0.116181f,0.690582f,0.713862f},{0.839387f,0.072094f,-0.538733f},{0.906994f,0.157665f,-0.390518f}, -{0.880792f,0.258935f,-0.396433f},{0.849277f,0.279145f,-0.448115f},{0.88698f,-0.0393897f,-0.460125f}, -{-0.256429f,-0.366661f,0.894318f},{0.384314f,-0.129266f,0.914108f},{-0.91019f,-0.38715f,-0.147201f}, -{-0.873766f,-0.460888f,-0.15529f},{0.0533412f,-0.987276f,0.1498f},{-0.0332455f,-0.999425f,-0.00667568f}, -{-0.0177683f,-0.998598f,-0.0498695f},{0.743946f,-0.315207f,-0.589228f},{-0.0300893f,0.428489f,0.903046f}, -{-0.141131f,-0.616613f,0.774513f},{-0.143887f,-0.543126f,0.827231f},{0.151877f,-0.0435705f,0.987439f}, -{0.135903f,0.0967646f,0.985985f},{0.181863f,-0.0316108f,0.982816f},{0.817395f,0.471839f,-0.330504f}, -{0.834915f,0.44021f,-0.330353f},{-0.439625f,0.363522f,-0.821329f},{-0.23135f,-0.491517f,-0.839576f}, -{0.881996f,0.263243f,0.390879f},{0.804092f,0.450491f,-0.387936f},{0.379275f,-0.920389f,-0.0950521f}, -{0.657543f,-0.0898175f,0.748044f},{0.60158f,0.0546622f,0.79694f},{0.424673f,-0.380215f,0.821638f}, -{0.480442f,-0.295344f,0.825801f},{0.296132f,0.463751f,0.835009f},{0.271331f,0.0872357f,0.958525f}, -{0.272219f,-0.172097f,0.94672f},{0.653222f,-0.218414f,0.72498f},{0.637907f,-0.0586593f,0.767876f}, -{0.432417f,-0.0287288f,0.901216f},{0.375382f,0.133798f,0.917162f},{0.668195f,0.332693f,0.665456f}, -{0.751623f,0.310123f,0.58214f},{0.70471f,0.352257f,0.615873f},{0.354198f,-0.110598f,0.928607f}, -{0.316442f,-0.111138f,0.942079f},{0.500513f,0.429239f,0.751825f},{0.485563f,0.505267f,0.713396f}, -{0.467589f,0.389971f,0.793274f},{0.269565f,0.432078f,0.860606f},{0.526376f,-0.206247f,0.824858f}, -{0.547881f,-0.180711f,0.816804f},{0.516456f,-0.177985f,0.837613f},{0.265807f,0.621366f,0.737056f}, -{0.396606f,0.461164f,0.793745f},{0.268517f,0.679316f,0.682956f},{0.608431f,-0.475134f,0.635657f}, -{0.581453f,0.335069f,0.741378f},{0.565053f,-0.011672f,0.824972f},{0.542132f,-0.254652f,0.800778f}, -{0.480758f,-0.379163f,0.790637f},{0.456013f,-0.393873f,0.79807f},{0.879688f,-0.114436f,0.461577f}, -{0.858958f,-0.0758828f,0.506393f},{0.85334f,-0.175889f,0.49079f},{0.864541f,0.245988f,0.438246f}, -{0.459771f,0.488017f,0.741923f},{0.648193f,0.136459f,0.749149f},{0.745059f,-0.146613f,0.650686f}, -{0.320391f,0.613417f,0.721851f},{0.381497f,0.502289f,0.775993f},{0.276193f,0.651878f,0.706238f}, -{0.787947f,-0.266373f,0.555144f},{0.738517f,-0.18476f,0.648426f},{-0.10106f,-0.198674f,-0.974841f}, -{-0.213871f,-0.405287f,-0.88882f},{0.120389f,-0.573452f,-0.810345f},{0.549648f,0.115702f,0.827345f}, -{0.656562f,-0.0125393f,0.754168f},{0.523616f,0.221222f,0.822732f},{0.5305f,-0.207482f,0.821901f}, -{0.503594f,-0.178747f,0.845247f},{0.464655f,-0.297417f,0.834049f},{0.436071f,-0.269518f,0.858605f}, -{0.552355f,-0.130993f,0.823253f},{0.71381f,-0.196261f,0.672277f},{0.255606f,0.690306f,0.676863f}, -{0.210124f,0.77675f,0.593723f},{0.853942f,-0.0356107f,0.519148f},{0.456866f,-0.524287f,0.718607f}, -{0.497688f,-0.585216f,0.640179f},{-0.239292f,-0.426292f,-0.872361f},{-0.133658f,-0.421392f,-0.896975f}, -{-0.0754212f,-0.532674f,-0.842953f},{0.6607f,-0.607619f,0.440766f},{0.628709f,-0.577003f,0.521336f}, -{0.641454f,-0.668221f,0.376852f},{0.666737f,-0.498983f,0.553603f},{0.800721f,0.0591873f,0.596106f}, -{0.748422f,0.000704595f,0.663222f},{0.790442f,-0.196599f,0.58013f},{0.7523f,-0.31572f,0.578244f}, -{0.770543f,-0.115037f,0.626921f},{0.657429f,-0.378401f,0.651613f},{0.558381f,-0.300341f,0.773309f}, -{0.605352f,-0.483657f,0.632159f},{0.508999f,-0.563832f,0.650394f},{0.547093f,-0.479533f,0.686103f}, -{0.647568f,0.075846f,0.758224f},{0.657391f,0.209588f,0.723816f},{0.778641f,0.458995f,0.427835f}, -{0.796311f,-0.124677f,0.591899f},{0.287351f,0.191424f,0.938502f},{0.732084f,0.104478f,0.673154f}, -{0.576522f,0.362983f,0.732029f},{0.465965f,0.367877f,0.804701f},{0.730207f,0.0875507f,0.677594f}, -{-0.399503f,0.326363f,-0.85667f},{-0.354372f,0.129678f,-0.926069f},{-0.448822f,0.605935f,-0.656811f}, -{-0.617025f,0.688594f,-0.380944f},{0.572087f,-0.696272f,0.4335f},{0.5902f,-0.307268f,0.746492f}, -{0.512315f,-0.27866f,0.812331f},{0.615482f,-0.524555f,0.588238f},{0.493002f,-0.409266f,0.767757f}, -{0.542152f,-0.375549f,0.751687f},{0.531583f,-0.39377f,0.74991f},{0.465902f,0.343074f,0.81562f}, -{0.556731f,-0.769617f,0.312633f},{0.254734f,-0.853269f,-0.455018f},{0.844112f,0.373341f,0.384825f}, -{0.798595f,0.33797f,0.498018f},{0.975491f,0.218904f,-0.0223324f},{0.411182f,-0.19688f,0.890038f}, -{0.55109f,-0.118472f,0.825993f},{0.555506f,-0.31389f,0.769991f},{0.483713f,-0.52489f,0.700365f}, -{0.476981f,-0.179667f,0.860354f},{-0.447194f,0.0450272f,-0.893303f},{0.323563f,-0.182347f,0.92847f}, -{0.11422f,0.271978f,-0.955501f},{-0.0334828f,0.360681f,-0.932088f},{0.0801935f,0.352312f,-0.93244f}, -{-0.165139f,-0.53863f,-0.8262f},{0.820463f,0.471607f,-0.323152f},{0.817037f,0.503878f,-0.280281f}, -{0.565589f,-0.744425f,-0.354882f},{0.593147f,-0.757668f,-0.272244f},{0.698756f,-0.639595f,-0.320402f}, -{0.416088f,-0.0895367f,0.904905f},{0.492055f,0.0671786f,0.867968f},{0.529662f,0.0245061f,0.847854f}, -{-0.290736f,-0.569161f,0.769109f},{0.343488f,-0.0248503f,0.938828f},{0.351263f,-0.283942f,0.892183f}, -{-0.100958f,0.304861f,-0.947031f},{-0.147563f,0.426841f,-0.892206f},{0.160646f,0.724895f,0.669865f}, -{0.739509f,-0.237147f,0.629991f},{0.755263f,-0.262598f,0.600517f},{0.720221f,-0.175747f,0.671115f}, -{-0.244267f,-0.453948f,-0.856893f},{-0.0660144f,-0.604178f,-0.79411f},{0.573614f,-0.482652f,0.661827f}, -{0.560916f,-0.327772f,0.760223f},{0.380138f,0.160625f,0.910876f},{0.851176f,0.264634f,-0.453287f}, -{0.34345f,-0.0915945f,0.934694f},{0.260014f,0.0541945f,0.964083f},{0.313326f,0.212754f,0.925507f}, -{0.396535f,0.543902f,-0.739548f},{0.444644f,0.49191f,-0.748543f},{0.503687f,0.52311f,-0.687499f}, -{-0.305173f,-0.397453f,-0.86539f},{0.660256f,0.283758f,0.695373f},{0.781524f,0.0706021f,0.619867f}, -{0.793597f,0.216873f,0.568481f},{0.733662f,0.193507f,0.65138f},{0.323353f,0.369017f,0.871361f}, -{0.582228f,-0.340961f,0.738076f},{0.549202f,0.284132f,0.785905f},{0.290724f,0.169076f,0.94175f}, -{0.53424f,-0.844963f,0.0250082f},{0.912901f,-0.035308f,0.406652f},{0.825201f,0.0115751f,0.56472f}, -{0.94043f,0.103742f,0.323773f},{-0.358271f,0.346487f,-0.866942f},{-0.277017f,0.312182f,-0.908738f}, -{-0.322405f,0.20153f,-0.9249f},{-0.981484f,-0.040964f,-0.187113f},{-0.846049f,-0.367576f,-0.386119f}, -{-0.723049f,-0.665594f,-0.184891f},{-0.681233f,0.638163f,0.358706f},{-0.887797f,0.251246f,0.385606f}, -{-0.945932f,0.286633f,0.151838f},{0.485297f,0.0684109f,0.871669f},{0.514312f,0.461732f,0.722694f}, -{0.622169f,0.403788f,0.670717f},{0.681605f,0.244739f,0.689578f},{0.498462f,-0.119438f,0.858644f}, -{0.606269f,-0.213205f,0.766148f},{0.768382f,0.484224f,-0.41847f},{0.815839f,0.442994f,-0.371703f}, -{-0.373507f,-0.270642f,-0.887269f},{-0.246314f,-0.0015448f,-0.969189f},{-0.431788f,-0.307208f,-0.848046f}, -{-0.347352f,-0.0362057f,-0.937036f},{0.0188641f,-0.999595f,-0.0213147f},{-0.00396728f,-0.999933f,-0.010845f}, -{0.0160447f,-0.999596f,-0.0234478f},{0.564655f,-0.441574f,0.697264f},{0.69031f,-0.153769f,0.706985f}, -{0.653448f,0.150736f,0.741812f},{0.651377f,0.314426f,0.690539f},{0.627538f,0.228138f,0.744412f}, -{0.477152f,0.341111f,0.809919f},{-0.983667f,-0.0636223f,0.168378f},{0.743203f,-0.429802f,0.512757f}, -{0.674133f,-0.406715f,0.616545f},{0.710554f,-0.395237f,0.582151f},{0.80919f,0.278181f,0.51752f}, -{0.826218f,0.290013f,0.482966f},{0.781084f,0.319842f,0.536291f},{0.374297f,-0.414591f,0.829468f}, -{0.292665f,-0.215483f,0.931619f},{0.147606f,0.803867f,0.576204f},{0.218746f,0.781826f,0.583866f}, -{0.512944f,-0.418974f,0.749233f},{0.567309f,-0.478639f,0.670123f},{0.577802f,-0.447301f,0.682691f}, -{0.685652f,-0.52024f,0.509148f},{0.448118f,-0.740268f,0.501192f},{0.684582f,0.410588f,0.6023f}, -{0.617891f,0.399018f,0.677492f},{0.632734f,0.295336f,0.715839f},{0.870482f,0.162018f,0.46477f}, -{0.840489f,0.186369f,0.508768f},{0.770366f,-0.159666f,0.617287f},{0.489274f,-0.00781279f,0.872095f}, -{0.522915f,-0.119012f,0.844036f},{0.525504f,-0.253163f,0.812253f},{0.584816f,-0.690932f,0.424975f}, -{0.765617f,0.283534f,0.577442f},{0.680954f,0.47874f,0.554174f},{0.75469f,0.139645f,0.641047f}, -{0.756042f,0.181321f,0.628906f},{0.706788f,-0.577248f,0.408944f},{0.270976f,0.449686f,0.85109f}, -{0.670575f,0.280147f,0.686911f},{0.731219f,0.377028f,0.56848f},{0.777606f,0.343926f,0.52635f}, -{0.764272f,0.413505f,0.494875f},{0.984362f,0.00908258f,0.175924f},{0.994702f,0.0701487f,0.0751525f}, -{0.988355f,0.0821244f,0.128101f},{0.819972f,-0.180455f,0.543215f},{0.786792f,0.106034f,0.608043f}, -{0.738693f,0.417579f,0.529113f},{0.813713f,0.199231f,0.546057f},{0.790175f,-0.192608f,0.58183f}, -{0.784095f,-0.245785f,0.569899f},{0.974171f,0.0716883f,0.214129f},{0.769246f,-0.371558f,0.519813f}, -{0.996425f,0.0802f,0.0265568f},{0.995753f,0.0774648f,0.0497599f},{0.994732f,0.0839796f,0.058788f}, -{0.0684754f,0.794747f,0.603066f},{0.890577f,-0.0326624f,0.453658f},{0.88509f,0.0273287f,0.464616f}, -{0.8882f,-0.0223627f,0.458912f},{0.673587f,-0.673846f,0.303664f},{0.443082f,-0.835915f,0.32392f}, -{0.601954f,-0.695112f,0.393027f},{0.529223f,-0.697322f,0.483389f},{0.520604f,-0.78592f,0.333618f}, -{0.854102f,0.0823997f,0.513536f},{0.871333f,-0.199525f,0.448296f},{0.789235f,0.160319f,0.592795f}, -{0.760528f,0.173113f,0.625803f},{0.567484f,0.383543f,0.7286f},{0.155292f,0.0116503f,0.9878f}, -{0.198612f,0.0248425f,0.979763f},{0.530868f,-0.169252f,0.830381f},{0.492979f,-0.111174f,0.862909f}, -{0.57538f,-0.112175f,0.810157f},{0.729484f,-0.0330009f,0.683201f},{0.788689f,-0.0150649f,0.614608f}, -{0.777266f,0.0200755f,0.628852f},{0.775266f,-0.284338f,0.564016f},{0.723422f,-0.280706f,0.630766f}, -{0.813826f,0.330595f,0.477906f},{0.780357f,0.334197f,0.528541f},{0.749978f,-0.563062f,0.347123f}, -{0.828104f,-0.38699f,0.405564f},{0.817079f,0.255468f,0.516834f},{0.768042f,0.385863f,0.511098f}, -{0.859619f,0.103461f,0.500351f},{0.774398f,0.172551f,0.608714f},{0.772662f,-0.40118f,0.491984f}, -{0.701697f,-0.127203f,0.701028f},{0.540935f,-0.797161f,0.268184f},{0.888043f,-0.0861904f,0.45161f}, -{0.710171f,0.329627f,0.622096f},{0.869048f,0.194343f,0.454958f},{0.175361f,0.624014f,0.761482f}, -{0.637965f,0.41983f,0.645556f},{0.652155f,0.403813f,0.641583f},{0.62877f,0.443097f,0.638994f}, -{-0.971605f,0.236558f,-0.00496129f},{0.59756f,0.462793f,0.654786f},{0.580389f,0.451229f,0.677895f}, -{0.626065f,0.431011f,0.649825f},{0.476493f,0.537168f,0.695992f},{0.620057f,0.465976f,0.631186f}, -{0.571138f,0.569021f,0.591622f},{0.683396f,-0.0668563f,0.72698f},{0.793913f,-0.0870938f,0.601762f}, -{0.815752f,-0.501744f,0.287753f},{0.606837f,-0.736726f,0.298302f},{0.896606f,-0.245435f,0.368592f}, -{0.896765f,0.0226329f,0.441927f},{0.844094f,0.216265f,0.490648f},{0.777054f,0.398813f,0.486966f}, -{0.785435f,0.364075f,0.500541f},{0.839493f,0.275943f,0.468089f},{0.243468f,-0.824286f,-0.511152f}, -{0.346621f,-0.880084f,-0.32451f},{0.536899f,0.0021648f,0.843644f},{0.585054f,0.0566912f,0.809011f}, -{0.650729f,-0.0826509f,0.754798f},{0.375913f,0.296442f,0.877959f},{0.110642f,-0.716504f,-0.688752f}, -{-0.193341f,-0.395496f,-0.897888f},{0.628335f,0.415744f,0.657534f},{0.827336f,0.279735f,0.487097f}, -{0.612704f,0.444252f,0.653631f},{0.48401f,0.4591f,0.744958f},{0.643482f,0.399456f,0.652967f}, -{0.57849f,0.362153f,0.730886f},{0.603739f,0.353751f,0.714395f},{0.669212f,0.391928f,0.631307f}, -{0.637042f,0.3499f,0.686839f},{0.801865f,0.000502496f,0.597504f},{0.832038f,0.0306209f,0.553873f}, -{0.811466f,-0.055155f,0.581791f},{0.470295f,-0.121854f,0.874056f},{0.767111f,-0.500143f,0.401744f}, -{0.739372f,0.426622f,0.520886f},{0.411713f,-0.155696f,0.897915f},{0.455116f,-0.227652f,0.860839f}, -{0.381047f,-0.107122f,0.918329f},{0.693332f,0.310097f,0.650485f},{0.730533f,0.151737f,0.665805f}, -{0.776021f,0.246385f,0.580591f},{0.697968f,-0.373382f,0.611086f},{0.866595f,-0.263565f,0.42373f}, -{0.811826f,0.486604f,0.322731f},{0.761744f,0.331298f,0.556766f},{-0.298661f,0.350782f,-0.887555f}, -{0.915881f,0.147323f,0.373441f},{0.92463f,0.311156f,0.21964f},{0.368686f,-0.785445f,0.497139f}, -{0.531938f,-0.737281f,0.416483f},{0.38531f,-0.870855f,0.305203f},{0.671745f,-0.645467f,0.363499f}, -{0.716224f,0.342647f,0.60796f},{0.769271f,0.376456f,0.51624f},{0.821107f,0.176575f,0.542775f}, -{0.849541f,0.0720743f,0.522576f},{0.828452f,0.10049f,0.550971f},{0.77968f,0.163857f,0.604359f}, -{0.566189f,0.00160388f,0.824274f},{0.476003f,0.0827108f,0.875546f},{0.554853f,-0.0521107f,0.830315f}, -{0.589359f,-0.181224f,0.787282f},{0.662155f,-0.284197f,0.693385f},{0.61982f,-0.514361f,0.592668f}, -{0.581998f,0.163587f,0.796566f},{0.513561f,0.322466f,0.795154f},{0.562209f,0.321646f,0.761882f}, -{0.535793f,0.38685f,0.750515f},{0.526098f,0.419382f,0.739824f},{0.490478f,0.469349f,0.734264f}, -{0.64664f,0.294523f,-0.703643f},{0.624458f,-0.561986f,0.542424f},{0.803143f,-0.258757f,0.536663f}, -{0.84475f,-0.0441311f,0.533338f},{0.792842f,0.173526f,0.584201f},{0.702839f,0.333032f,0.628576f}, -{0.863811f,0.113649f,0.490831f},{0.843999f,0.0339543f,0.535269f},{0.725844f,0.0529413f,0.685819f}, -{-0.73513f,-0.674473f,0.0683321f},{0.504364f,-0.490432f,0.710699f},{0.37711f,-0.530281f,0.759335f}, -{0.583048f,-0.597629f,0.550359f},{0.0436126f,-0.966946f,-0.251225f},{0.537038f,0.494685f,0.683284f}, -{0.546936f,0.0266999f,0.836748f},{0.724826f,-0.463617f,0.509594f},{0.693024f,-0.379127f,0.613173f}, -{0.709473f,-0.552913f,0.436962f},{0.514175f,0.22697f,0.827109f},{0.557267f,0.12334f,0.821122f}, -{0.482026f,0.308139f,0.820184f},{0.513391f,0.555068f,0.654469f},{0.453179f,0.569752f,0.685574f}, -{0.406531f,-0.413933f,0.814489f},{0.811775f,0.578688f,-0.0783687f},{0.973187f,0.225135f,-0.0471338f}, -{0.434216f,0.566594f,0.700305f},{0.538412f,-0.036777f,0.841879f},{0.491574f,0.0444878f,0.869698f}, -{0.415802f,0.190611f,0.889256f},{0.617904f,-0.566391f,0.54534f},{0.699226f,-0.174741f,0.693216f}, -{0.746495f,-0.220216f,0.627894f},{0.648682f,0.147411f,0.746647f},{0.568483f,0.442257f,0.693711f}, -{0.57011f,0.139636f,0.809615f},{0.948333f,-0.29193f,-0.124263f},{0.476851f,0.0790041f,0.875427f}, -{0.895368f,0.438086f,0.0799757f},{0.861625f,0.504983f,0.0509301f},{0.905583f,0.412128f,0.100346f}, -{0.385951f,0.474715f,0.791004f},{0.500776f,0.315259f,0.806124f},{0.78597f,0.498529f,-0.365678f}, -{-0.039175f,-0.970836f,-0.236521f},{0.279225f,0.728435f,0.625633f},{0.355854f,0.545909f,0.758519f}, -{0.614204f,-0.411429f,0.673409f},{0.4737f,-0.4708f,0.744281f},{0.577135f,-0.434087f,0.691725f}, -{-0.49125f,0.856358f,-0.159137f},{0.183845f,0.927353f,0.32591f},{0.430745f,0.871104f,0.235873f}, -{0.533354f,0.00972195f,0.845836f},{0.523564f,0.106788f,0.845268f},{0.533085f,-0.134271f,0.835339f}, -{0.616118f,-0.527777f,0.584679f},{0.57025f,-0.527772f,0.629501f},{0.394466f,0.440697f,0.806339f}, -{0.426039f,0.454103f,0.782484f},{0.545621f,-0.597095f,-0.588026f},{0.686729f,-0.529675f,-0.497844f}, -{0.509716f,-0.260083f,0.820089f},{0.623851f,-0.377081f,0.684558f},{0.629402f,0.220525f,0.745132f}, -{0.529436f,0.339631f,0.777399f},{0.623309f,0.420085f,0.659556f},{0.80518f,0.0871658f,0.58659f}, -{0.780352f,-0.0962468f,0.617889f},{0.523776f,-0.321197f,0.788981f},{0.515573f,-0.306043f,0.800326f}, -{0.544485f,-0.219115f,0.809645f},{0.625398f,-0.231908f,0.745048f},{0.706314f,-0.255021f,0.660368f}, -{0.673997f,-0.0995925f,0.73199f},{0.175291f,0.557259f,0.811625f},{-0.316229f,0.390378f,-0.864641f}, -{0.602475f,-0.105106f,0.791187f},{0.511986f,-0.0480917f,0.857646f},{0.549755f,0.286096f,0.784805f}, -{0.401885f,0.448359f,0.798412f},{0.647712f,0.0793625f,0.75774f},{0.693393f,-0.138186f,0.707185f}, -{0.501942f,-0.419292f,0.756471f},{0.417585f,0.557046f,0.71786f},{0.48062f,0.607109f,0.63279f}, -{0.634624f,-0.714085f,-0.295524f},{0.301532f,0.609365f,0.733316f},{0.653937f,-0.26285f,0.70942f}, -{0.622959f,-0.417671f,0.661418f},{-0.27087f,-0.335809f,-0.902143f},{0.431305f,-0.544847f,0.719109f}, -{0.438527f,0.4447f,0.780984f},{0.475877f,0.4592f,0.750118f},{0.614748f,-0.108916f,0.781167f}, -{0.648812f,-0.709042f,-0.276231f},{0.807503f,-0.288409f,0.514548f},{0.329362f,0.199389f,0.922911f}, -{-0.288854f,-0.228841f,0.929621f},{0.032487f,-0.0237096f,0.999191f},{0.379595f,-0.203006f,0.902605f}, -{0.525301f,0.348523f,0.776267f},{0.353571f,0.251066f,0.901084f},{0.747355f,0.353664f,0.562478f}, -{-0.555693f,-0.784513f,-0.275219f},{0.411451f,0.499707f,0.762234f},{0.247906f,0.683824f,0.686241f}, -{0.432255f,0.553228f,0.712106f},{0.0447714f,0.929401f,0.366345f},{0.359402f,-0.545287f,0.757293f}, -{0.518952f,-0.151605f,0.841252f},{0.491923f,0.130876f,0.860746f},{0.414154f,0.50786f,0.755351f}, -{0.678972f,-0.1642f,0.715566f},{0.711066f,0.00492155f,0.703108f},{0.503994f,0.317354f,0.803291f}, -{0.588691f,0.195381f,0.784391f},{0.526698f,0.331299f,0.782834f},{0.716063f,0.386632f,0.58118f}, -{0.483935f,0.46453f,0.741632f},{0.495087f,0.569512f,0.656159f},{0.380152f,0.463059f,0.800663f}, -{0.30855f,0.620777f,0.720717f},{0.5734f,-0.641764f,-0.509266f},{0.380204f,0.394298f,0.836644f}, -{0.809792f,0.158265f,0.564969f},{0.555507f,0.484609f,0.675697f},{-0.740533f,-0.671531f,0.025636f}, -{-0.723145f,-0.674855f,0.147076f},{0.615847f,0.522535f,0.589652f},{0.512179f,-0.340231f,0.788616f}, -{0.536573f,-0.109383f,0.836735f},{0.529299f,0.12282f,0.839499f},{0.540318f,0.326725f,0.77544f}, -{0.538417f,0.44289f,0.716907f},{0.687982f,0.0622659f,0.723052f},{0.732887f,-0.246965f,0.633944f}, -{-0.477133f,0.364336f,-0.799752f},{-0.458535f,0.357291f,-0.813689f},{-0.45039f,0.349904f,-0.821411f}, -{0.2928f,0.188137f,0.937482f},{0.18232f,-0.391871f,0.901774f},{0.00676346f,-0.547607f,0.836708f}, -{0.916547f,-0.0457656f,-0.397299f},{0.762617f,-0.476852f,0.437066f},{0.815111f,-0.371701f,0.444335f}, -{0.786397f,-0.282666f,0.549254f},{-0.575086f,-0.0839676f,0.813773f},{0.712856f,0.388338f,0.583978f}, -{0.899224f,-0.35067f,-0.261584f},{0.774318f,-0.202262f,0.599601f},{0.702776f,-0.0325274f,0.710668f}, -{0.733857f,-0.17232f,0.657084f},{0.659872f,-0.0417907f,0.750215f},{0.307708f,-0.261296f,0.914899f}, -{0.208132f,-0.0752668f,0.975201f},{0.35429f,-0.556443f,0.751565f},{0.428666f,0.437099f,0.79069f}, -{0.509459f,0.418039f,0.752127f},{0.455826f,0.346165f,0.819996f},{0.673234f,-0.0798483f,0.735105f}, -{0.836109f,0.296137f,0.461763f},{0.781771f,0.338466f,0.523713f},{-0.206937f,-0.731467f,0.649718f}, -{0.182463f,0.804027f,0.565904f},{0.495997f,0.491771f,0.715645f},{0.697516f,-0.155143f,0.699573f}, -{0.640765f,-0.107144f,0.760224f},{0.667024f,-0.208429f,0.715288f},{0.69754f,-0.266859f,0.665f}, -{0.549992f,-0.105262f,0.82851f},{0.525844f,0.0216014f,0.850307f},{0.627803f,-0.145946f,0.764567f}, -{0.575798f,-0.110136f,0.81014f},{-0.97179f,0.206791f,0.113409f},{0.189983f,0.501541f,0.844016f}, -{0.5093f,0.462767f,0.725576f},{0.818183f,-0.189226f,0.542928f},{0.837951f,-0.0837523f,0.539281f}, -{0.494371f,-0.37279f,-0.785254f},{-0.46232f,-0.0898225f,0.882152f},{0.371185f,0.394377f,0.840648f}, -{0.473626f,-0.168205f,0.864514f},{-0.461698f,0.245268f,0.852454f},{0.529431f,-0.618975f,0.580148f}, -{0.431288f,-0.596394f,0.676983f},{0.518712f,-0.358702f,0.776061f},{0.359538f,0.526882f,0.770148f}, -{0.620336f,0.0945776f,0.778613f},{0.558303f,-0.222333f,0.799291f},{0.462001f,-0.280565f,0.841331f}, -{0.228678f,0.745663f,0.625854f},{0.581988f,-0.357027f,0.730631f},{0.671159f,-0.124645f,0.73076f}, -{0.657414f,0.107867f,0.745769f},{0.635504f,0.331112f,0.697495f},{0.527313f,0.468268f,0.708989f}, -{0.786941f,-0.0742031f,0.61255f},{0.78316f,0.0971358f,0.614187f},{0.679903f,-0.0312478f,-0.732636f}, -{0.722731f,-0.119831f,-0.680662f},{-0.304819f,0.292514f,-0.906378f},{-0.701588f,-0.688721f,0.182861f}, -{0.292159f,-0.372893f,0.880678f},{0.35086f,0.338823f,0.872981f},{0.49325f,-0.543568f,0.679145f}, -{0.482678f,-0.713783f,0.50748f},{-0.0412281f,-0.625784f,-0.778906f},{0.347424f,0.53187f,0.772276f}, -{0.363542f,0.452761f,0.814153f},{0.683475f,0.00488942f,0.729957f},{0.622766f,0.0726325f,0.779029f}, -{0.501262f,-0.479754f,0.72012f},{0.25502f,0.730842f,0.633115f},{0.337312f,0.626247f,0.702876f}, -{0.893756f,-0.323824f,-0.310383f},{0.661542f,-0.288546f,0.692173f},{0.615062f,-0.137833f,0.776338f}, -{0.679829f,-0.16072f,0.715543f},{0.715517f,0.135139f,0.6854f},{0.673723f,0.259116f,0.692067f}, -{0.607929f,0.357916f,0.708744f},{0.564176f,0.484896f,0.668268f},{0.605081f,0.132927f,0.784989f}, -{0.702442f,-0.0913318f,0.705856f},{0.524211f,-0.518737f,-0.675363f},{0.215163f,-0.85976f,0.46316f}, -{0.0578387f,0.325075f,-0.943918f},{-0.0515991f,0.412961f,-0.909286f},{-0.108005f,0.514926f,-0.850403f}, -{0.54637f,-0.00381208f,0.837535f},{0.614893f,-0.293542f,0.731942f},{0.541219f,-0.245194f,0.80434f}, -{0.398648f,0.234369f,0.886652f},{0.358081f,-0.189674f,0.914222f},{0.335383f,0.0542744f,0.940517f}, -{0.460648f,-0.309278f,0.831956f},{0.423308f,0.144847f,0.894332f},{0.435355f,0.202096f,0.877282f}, -{0.603528f,0.44959f,0.6585f},{0.679775f,-0.0385942f,0.732405f},{0.653824f,0.107886f,0.748916f}, -{0.759406f,0.0271836f,0.650049f},{0.574702f,0.413968f,0.705938f},{0.360767f,0.550165f,0.753104f}, -{-0.887326f,0.448617f,-0.106749f},{-0.933431f,0.327008f,-0.147555f},{0.5428f,-0.207117f,0.813923f}, -{0.494759f,-0.106389f,0.862493f},{-0.395446f,-0.207517f,-0.89474f},{-0.355182f,-0.249591f,-0.900861f}, -{0.495157f,-0.00965679f,0.86875f},{0.643235f,0.350247f,0.680864f},{0.570022f,0.501044f,0.651176f}, -{0.617122f,0.271777f,0.738443f},{0.862234f,0.144092f,0.485582f},{0.977463f,0.0865015f,0.192573f}, -{0.967945f,0.0949319f,0.232529f},{-0.304308f,0.397193f,-0.865814f},{0.270602f,-0.246793f,0.93052f}, -{0.577148f,0.34371f,0.740786f},{0.400594f,0.586566f,0.703893f},{0.302236f,0.559956f,0.771429f}, -{0.521137f,0.302706f,0.797988f},{0.502864f,0.414301f,0.758605f},{0.594349f,-0.0767115f,0.80054f}, -{0.573793f,-0.176939f,0.799659f},{0.62133f,0.0448709f,0.782264f},{0.581222f,0.0850307f,0.80929f}, -{0.682527f,-0.0396461f,0.729784f},{0.636348f,0.03267f,0.77071f},{0.388821f,-0.0387993f,0.920496f}, -{0.542248f,-0.00517707f,0.840202f},{0.581726f,-0.0418682f,0.812306f},{0.655859f,-0.313951f,0.686501f}, -{0.727557f,-0.0868233f,0.680531f},{0.744094f,0.140968f,0.653033f},{0.662563f,0.288153f,0.69136f}, -{0.497747f,-0.511148f,0.700696f},{-0.687492f,0.198204f,-0.69862f},{-0.565646f,0.0336114f,-0.823963f}, -{-0.63122f,0.176192f,-0.755326f},{0.715262f,0.396856f,0.575244f},{0.713432f,0.39338f,0.579885f}, -{-0.923237f,0.361203f,-0.131016f},{-0.998356f,0.0419347f,0.0390748f},{-0.996558f,0.0211225f,-0.0801658f}, -{0.440074f,0.261607f,0.859009f},{0.507926f,0.00577762f,0.861381f},{0.598678f,-0.238457f,0.764672f}, -{0.595584f,0.0324451f,0.802637f},{0.681167f,-0.213054f,0.700442f},{0.760255f,-0.0271448f,0.649057f}, -{0.769925f,0.103971f,0.629607f},{0.716696f,0.189305f,0.6712f},{0.664742f,0.357915f,0.655755f}, -{0.796516f,0.168019f,0.580802f},{0.807462f,0.0110927f,0.589815f},{0.846207f,0.128317f,0.517174f}, -{0.632386f,0.0778414f,-0.770732f},{0.698958f,0.0421958f,-0.713917f},{0.745128f,-0.0767328f,-0.662492f}, -{0.3335f,-0.145014f,0.93153f},{0.865745f,0.491554f,0.0941269f},{0.707032f,0.416355f,0.571625f}, -{0.80999f,0.565921f,0.153785f},{0.877285f,0.466554f,0.112687f},{0.908767f,0.406955f,0.0923594f}, -{0.668257f,-0.432233f,0.605481f},{0.672297f,-0.440256f,0.595141f},{0.590598f,0.120871f,0.797862f}, -{0.454966f,0.559227f,0.693016f},{0.549444f,0.578269f,0.603089f},{0.535451f,-0.533401f,0.654809f}, -{0.778287f,-0.614793f,-0.127668f},{0.770378f,-0.637588f,7.24516e-05f},{0.518565f,-0.0232879f,0.854721f}, -{0.49947f,0.00200443f,0.866329f},{0.475824f,0.117396f,0.871671f},{0.478578f,0.335545f,0.811401f}, -{0.552119f,0.216155f,0.805259f},{0.746915f,-0.129145f,0.652258f},{0.72802f,0.199855f,0.655778f}, -{0.753832f,-0.195787f,0.62722f},{0.656384f,0.422092f,0.625299f},{0.636566f,0.465569f,0.614841f}, -{0.914171f,0.107873f,0.390712f},{0.829913f,0.220967f,0.512268f},{0.932403f,0.11802f,0.341608f}, -{-0.25645f,-0.502436f,-0.825706f},{-0.941174f,0.222725f,-0.254137f},{0.623564f,-0.563809f,0.54156f}, -{0.439151f,0.502801f,0.744539f},{0.784586f,-0.297617f,0.54392f},{0.114849f,-0.45399f,-0.883574f}, -{0.694739f,-0.719229f,0.00687289f},{0.901992f,0.424732f,0.0775473f},{0.0386906f,-0.76215f,-0.646243f}, -{0.534589f,0.215435f,0.817192f},{0.418646f,0.233704f,0.877564f},{0.491832f,0.162436f,0.855404f}, -{-0.578384f,0.497412f,-0.646571f},{-0.83752f,0.114356f,-0.534306f},{-0.751325f,-0.0297917f,-0.659259f}, -{0.729163f,0.361824f,0.580865f},{0.759f,0.303823f,0.575857f},{0.22265f,-0.950099f,-0.218494f}, -{0.749635f,-0.441831f,0.49278f},{0.62442f,0.407023f,0.666657f},{0.480825f,0.343945f,0.806542f}, -{0.484674f,0.377617f,0.788984f},{-0.359597f,-0.178119f,-0.915949f},{-0.132927f,-0.480289f,-0.866979f}, -{0.618301f,0.114174f,0.777605f},{0.650738f,-0.0654459f,0.756476f},{0.818155f,-0.134811f,0.558971f}, -{0.751298f,0.243416f,0.613433f},{0.760882f,0.478202f,0.438613f},{0.766475f,0.346567f,0.540747f}, -{0.548821f,-0.33437f,0.766154f},{0.914253f,0.110328f,0.389832f},{-0.908437f,0.392483f,0.143871f}, -{-0.916873f,0.398846f,-0.0162913f},{0.577586f,0.157636f,0.800965f},{0.956451f,-9.39214e-05f,0.291893f}, -{0.513721f,0.405479f,0.756093f},{0.459552f,0.406286f,0.789775f},{0.63472f,0.110533f,0.764796f}, -{0.638935f,0.168088f,0.750672f},{0.851403f,-0.491367f,-0.183499f},{0.743817f,0.0571238f,0.665938f}, -{0.624312f,0.0274681f,0.780692f},{0.56851f,-0.179512f,0.802852f},{0.899508f,0.0827162f,0.429003f}, -{0.841008f,0.0246865f,0.540459f},{0.817259f,0.0591081f,0.573232f},{0.827772f,0.18779f,0.528704f}, -{0.498712f,-0.488039f,0.716313f},{0.697539f,-0.186868f,0.691751f},{0.583311f,-0.151649f,0.797967f}, -{0.773136f,0.0139612f,0.634087f},{0.794927f,-0.0103512f,0.606617f},{0.565734f,0.479916f,0.670542f}, -{-0.864742f,0.103488f,0.491439f},{0.828208f,-0.00768752f,0.560368f},{0.561537f,-0.286341f,0.776328f}, -{0.508557f,0.150787f,0.847722f},{0.545509f,0.274793f,0.791776f},{0.594812f,-0.0292075f,0.803334f}, -{0.603583f,0.135798f,0.78565f},{0.442723f,0.0788834f,0.893182f},{0.554001f,0.29244f,0.779463f}, -{0.600309f,-0.0313817f,0.799152f},{0.399517f,-0.0519389f,0.915253f},{0.452671f,0.0560515f,0.889914f}, -{0.591323f,0.26021f,0.7633f},{0.608531f,0.316253f,0.727787f},{0.652664f,0.0892208f,0.752376f}, -{0.608645f,0.00582163f,0.793422f},{0.534761f,0.021969f,0.844718f},{0.552932f,0.0923861f,0.828089f}, -{0.615998f,0.155852f,0.772177f},{0.708251f,0.0938871f,0.69969f},{0.448258f,-0.39374f,0.802517f}, -{0.955745f,0.0600352f,0.288004f},{0.48462f,-0.528963f,0.696665f},{0.786507f,0.367476f,0.496355f}, -{0.946708f,0.312889f,0.0764525f},{0.970324f,0.231022f,0.0714221f},{0.622192f,-0.479217f,0.619054f}, -{0.528436f,0.369101f,0.764539f},{0.780853f,0.00919292f,0.624647f},{0.387582f,-0.131106f,0.912465f}, -{0.50548f,-0.154984f,0.848805f},{0.877049f,0.473586f,0.0806271f},{0.829991f,0.555528f,0.0500242f}, -{0.0380677f,-0.422558f,-0.905536f},{-0.0386355f,-0.416857f,-0.908151f},{0.101346f,-0.34475f,-0.933208f}, -{-0.65478f,0.227072f,-0.720903f},{0.554038f,0.392875f,0.733956f},{0.234576f,0.762263f,0.603265f}, -{0.25995f,0.711212f,0.653149f},{0.498703f,-0.320812f,0.805217f},{0.524817f,-0.317889f,0.789629f}, -{0.775137f,-0.392992f,0.494691f},{0.58636f,-0.0781912f,0.806268f},{0.840356f,0.34648f,0.416837f}, -{0.876942f,0.455574f,0.153053f},{0.908257f,0.364583f,0.2053f},{0.875224f,-0.483566f,-0.0121026f}, -{0.917423f,-0.388275f,0.0870448f},{0.562132f,0.160272f,0.811369f},{0.170127f,-0.327533f,0.929397f}, -{0.768738f,-0.0521177f,0.637436f},{0.761336f,-0.026129f,0.647831f},{0.886198f,-0.0253639f,0.462612f}, -{0.884802f,-0.0240317f,0.465348f},{0.875003f,-0.0269654f,0.483366f},{0.888156f,-0.303303f,-0.345232f}, -{0.720994f,0.41486f,0.55503f},{0.73089f,0.493643f,0.471292f},{0.799211f,0.510579f,0.317129f}, -{0.766819f,0.495596f,0.407889f},{0.716985f,0.389142f,0.578361f},{0.66729f,-0.114149f,0.735998f}, -{0.654942f,-0.233812f,0.718598f},{0.821195f,-0.308784f,0.479887f},{0.775726f,-0.3568f,0.520521f}, -{0.717386f,-0.30496f,0.626383f},{0.208891f,-0.812635f,-0.544049f},{0.859058f,0.105923f,0.500799f}, -{0.881403f,0.14631f,0.449135f},{0.841431f,0.0925177f,0.532386f},{0.565092f,0.0003129f,0.825028f}, -{0.54228f,-0.0389849f,0.839293f},{-0.372746f,0.134499f,-0.918134f},{0.72628f,0.0503029f,-0.685556f}, -{0.787154f,-0.0814957f,-0.611349f},{0.769883f,-0.297935f,-0.564371f},{0.744067f,0.141766f,0.652891f}, -{0.574057f,0.0905282f,0.813795f},{0.761955f,0.157928f,0.62808f},{0.887165f,0.0459843f,0.459156f}, -{0.885574f,0.209166f,0.414738f},{0.72059f,0.381289f,0.579111f},{0.90903f,0.274057f,0.313939f}, -{0.450923f,-0.195169f,0.870964f},{0.739011f,0.117632f,0.663344f},{0.571164f,-0.541364f,0.617007f}, -{0.889148f,0.0404144f,0.455832f},{-0.407865f,0.585904f,-0.700259f},{0.680773f,-0.427493f,0.594809f}, -{0.715782f,-0.442783f,0.54f},{0.568523f,-0.363063f,0.738219f},{0.309482f,-0.182632f,0.933202f}, -{-0.919947f,0.0886574f,-0.381888f},{0.861114f,-0.0447529f,0.506438f},{0.821222f,-0.0124579f,0.570473f}, -{0.81439f,-0.0385379f,0.579037f},{0.192437f,0.0496368f,0.980053f},{0.478856f,0.612574f,0.628848f}, -{-0.511654f,0.318298f,-0.798058f},{-0.601647f,0.281855f,-0.747381f},{0.251524f,0.118615f,-0.960555f}, -{0.225671f,0.123337f,-0.966365f},{0.912175f,-0.0206556f,0.409281f},{0.902009f,-0.018796f,0.431308f}, -{0.93251f,3.12715e-05f,0.361143f},{0.660423f,0.0123608f,0.750792f},{0.841922f,0.338374f,0.420321f}, -{0.48217f,-0.30124f,0.822658f},{0.213331f,-0.966013f,-0.145976f},{0.982792f,0.184059f,-0.0155821f}, -{0.882267f,0.470176f,-0.0232345f},{0.897305f,0.413593f,0.154221f},{-0.452893f,0.251605f,-0.855326f}, -{-0.42283f,0.351254f,-0.835366f},{0.831731f,0.186743f,0.522829f},{0.885981f,0.159897f,0.435283f}, -{0.528091f,-0.0916303f,0.84423f},{-0.5275f,0.392926f,-0.753229f},{-0.414898f,0.226583f,-0.881203f}, -{-0.449899f,0.203977f,-0.869473f},{0.792407f,0.309002f,0.525936f},{0.702105f,-0.174158f,0.690447f}, -{0.601373f,-0.0740094f,0.795533f},{0.550285f,0.795586f,0.253436f},{0.234523f,0.778306f,0.582442f}, -{0.324053f,0.512421f,0.795245f},{0.574058f,-0.409459f,0.709084f},{0.248926f,-0.872222f,-0.421028f}, -{0.291238f,0.681894f,0.67097f},{-0.206327f,0.155371f,-0.966069f},{-0.179661f,0.246138f,-0.952438f}, -{-0.129275f,0.294127f,-0.946983f},{-0.236405f,0.381324f,-0.893703f},{0.686435f,0.401971f,0.605992f}, -{-0.408368f,0.355119f,-0.840908f},{0.820717f,-0.249024f,0.514208f},{0.729541f,-0.276127f,0.625719f}, -{0.981621f,0.137155f,0.132695f},{0.883296f,0.312291f,-0.349661f},{0.873408f,0.450495f,-0.184969f}, -{0.498945f,0.610413f,0.615183f},{0.778182f,0.38333f,0.497485f},{0.283819f,0.610949f,0.739046f}, -{0.883323f,0.255649f,0.392918f},{0.799895f,0.0390677f,0.598868f},{0.810366f,0.0758698f,0.580991f}, -{0.852675f,-0.235047f,0.466582f},{0.888261f,-0.0854651f,0.451318f},{0.87399f,-0.0845339f,0.478535f}, -{0.0351843f,-0.998869f,-0.0319747f},{0.0305101f,-0.99916f,-0.0273486f},{0.537026f,0.273246f,0.798085f}, -{0.0538744f,-0.983474f,-0.172848f},{-0.316547f,-0.843145f,-0.434632f},{0.468404f,0.603343f,0.645426f}, -{-0.178733f,-0.51791f,-0.836555f},{0.591741f,-0.474756f,-0.651498f},{0.12968f,-0.977424f,-0.166811f}, -{0.421402f,-0.892298f,-0.161939f},{-0.737569f,0.5865f,0.33468f},{-0.849297f,0.462132f,0.255202f}, -{-0.394842f,-0.918423f,0.0244794f},{-0.463997f,0.132699f,-0.875841f},{-0.322235f,0.0572273f,-0.944928f}, -{-0.373209f,0.0745975f,-0.924743f},{0.486459f,-0.162385f,0.85848f},{-0.482457f,-0.842529f,-0.239541f}, -{0.499279f,-0.259573f,0.826645f},{0.411235f,0.323673f,0.852128f},{0.554669f,-0.34206f,0.75851f}, -{0.473195f,-0.178033f,0.862781f},{0.849404f,0.518863f,0.096404f},{0.876804f,0.461771f,0.134098f}, -{0.758995f,-0.279535f,0.588036f},{0.425511f,-0.715773f,-0.553723f},{0.340117f,-0.913034f,-0.225142f}, -{0.720202f,0.392223f,0.57225f},{-0.887605f,0.375631f,-0.266567f},{-0.870968f,-0.10751f,-0.479434f}, -{-0.880827f,0.148713f,-0.449475f},{0.625715f,-0.186516f,0.757425f},{0.49642f,-0.788635f,0.362796f}, -{0.450044f,-0.869811f,0.202213f},{0.769115f,-0.312414f,0.557547f},{0.727369f,-0.115659f,0.676429f}, -{0.0593964f,-0.478543f,0.876053f},{0.164209f,0.0300051f,0.985969f},{-0.0644165f,0.0478143f,0.996777f}, -{0.152902f,0.149191f,0.976915f},{0.483827f,-0.665811f,0.567986f},{0.896442f,-0.0319358f,0.442009f}, -{0.914686f,-0.0438937f,0.401775f},{-0.451712f,-0.831749f,-0.322723f},{-0.297702f,-0.794045f,-0.529968f}, -{0.888428f,0.139476f,0.437312f},{0.175709f,-0.322229f,0.930212f},{0.514633f,-0.835931f,0.190714f}, -{0.92474f,0.379415f,0.0300102f},{0.918099f,0.386049f,0.0897775f},{0.814101f,-0.545264f,-0.199817f}, -{0.912396f,0.0460901f,0.406706f},{-0.777079f,-0.577772f,-0.249656f},{-0.669531f,-0.626988f,-0.398264f}, -{0.0198351f,-0.999244f,0.0334435f},{0.0138369f,-0.999067f,0.0408985f},{0.0159619f,-0.999343f,0.0325355f}, -{0.886832f,-0.0337723f,0.460856f},{-0.90871f,0.244633f,-0.338232f},{-0.0909099f,0.558564f,-0.824464f}, -{0.60105f,0.682827f,-0.415314f},{-0.790674f,-0.288535f,-0.539984f},{0.0880274f,-0.995293f,0.0405437f}, -{0.00338008f,-0.520431f,0.853897f},{0.712797f,0.446657f,0.540757f},{0.813274f,-0.529637f,0.240976f}, -{0.908879f,-0.35302f,0.222074f},{0.718433f,-0.162821f,0.676272f},{0.181672f,0.904069f,-0.386852f}, -{-0.417861f,0.905006f,0.0797298f},{-0.713732f,0.577259f,0.396685f},{-0.076924f,-0.696852f,-0.713078f}, -{-0.181321f,-0.570804f,-0.800816f},{-0.191037f,-0.442903f,-0.875981f},{-0.0504743f,-0.580727f,-0.812532f}, -{-0.926669f,-0.356093f,-0.120341f},{-0.892653f,-0.449437f,-0.0343083f},{-0.986856f,-0.133881f,-0.0905015f}, -{0.587545f,-0.717401f,0.374334f},{0.998502f,0.0530506f,-0.0133723f},{0.949732f,0.30454f,0.0725536f}, -{0.998781f,0.0489191f,-0.00662695f},{0.996435f,0.0816746f,0.0211487f},{0.68741f,0.0823538f,0.721585f}, -{0.584366f,0.244026f,0.77393f},{0.269489f,0.147477f,0.951644f},{0.519998f,-0.60239f,0.605581f}, -{0.567408f,-0.484303f,0.665957f},{-0.676466f,-0.181577f,-0.713739f},{-0.802923f,-0.132063f,-0.58127f}, -{-0.582632f,-0.207995f,-0.78567f},{0.746876f,-0.118077f,0.654396f},{0.975213f,-0.209994f,-0.0697239f}, -{0.60946f,0.229956f,0.758735f},{0.764709f,-0.00540347f,0.644353f},{0.680325f,-0.379919f,0.626754f}, -{0.736298f,-0.112824f,0.667185f},{0.616818f,-0.00552029f,0.787086f},{0.895137f,-0.311959f,-0.318451f}, -{-0.709982f,-0.133843f,-0.691384f},{-0.577864f,-0.187655f,-0.794266f},{0.83979f,-0.15125f,0.521417f}, -{0.596297f,0.0624722f,0.800329f},{0.0105197f,-0.998823f,0.0473539f},{0.0265853f,-0.99901f,0.0356652f}, -{0.0124122f,-0.998977f,0.0434877f},{-0.678991f,0.664674f,-0.311736f},{0.33402f,0.150594f,0.930458f}, -{-0.432098f,0.335744f,0.836999f},{0.692931f,-0.0653427f,0.718037f},{0.266962f,0.858519f,0.437808f}, -{0.875412f,-0.110105f,0.47067f},{0.931732f,-0.0463402f,0.360177f},{0.917928f,0.390128f,0.0721717f}, -{0.926793f,-0.0150992f,0.375268f},{0.742293f,0.37572f,0.554829f},{0.722769f,-0.643509f,0.251992f}, -{0.921411f,-0.112764f,0.371869f},{-0.552981f,0.11925f,-0.824616f},{0.0969255f,0.700357f,0.707181f}, -{0.815723f,-0.0576197f,0.575566f},{-0.959286f,0.0233389f,-0.281471f},{0.938652f,0.278602f,0.203255f}, -{0.291122f,-0.576791f,0.763256f},{0.224105f,-0.751766f,0.620181f},{0.262298f,-0.881578f,0.392453f}, -{-0.558518f,-0.125543f,-0.819937f},{-0.419377f,-0.0666438f,-0.905363f},{-0.510491f,-0.319457f,-0.798339f}, -{0.341107f,-0.0284138f,0.939595f},{0.858203f,-0.0563061f,0.510212f},{0.880166f,0.164233f,0.44535f}, -{0.689148f,0.496612f,0.527686f},{0.771216f,-0.14831f,0.619056f},{0.771271f,0.507287f,-0.384448f}, -{0.834734f,0.227488f,-0.501467f},{0.85188f,-0.0369246f,0.522434f},{0.386071f,0.227234f,-0.894044f}, -{0.325297f,0.269005f,-0.906542f},{0.378206f,0.379987f,-0.844139f},{0.662992f,-0.227769f,0.713136f}, -{0.594588f,-0.0322742f,0.803382f},{0.804086f,-0.0198517f,0.594182f},{0.736753f,0.0181734f,0.675918f}, -{0.326986f,0.196476f,0.924379f},{0.299563f,0.339299f,0.891705f},{0.884397f,0.126928f,0.449145f}, -{0.415895f,0.703623f,0.576147f},{0.270987f,0.684654f,0.676621f},{0.886993f,0.123335f,0.445007f}, -{0.618728f,-0.106254f,0.778386f},{0.00880166f,-0.999707f,-0.0225414f},{0.0201949f,-0.999687f,-0.0147667f}, -{0.0280835f,-0.999382f,-0.0211496f},{0.310203f,-0.397361f,0.863642f},{-0.0407621f,0.187927f,-0.981337f}, -{0.494633f,-0.865635f,-0.0775528f},{0.590065f,0.0157559f,0.807202f},{0.756401f,-0.214401f,0.617972f}, -{0.70386f,-0.501947f,0.502623f},{0.513046f,0.0939776f,0.853201f},{0.561868f,-0.598621f,0.570926f}, -{-0.882151f,-0.304941f,0.358916f},{-0.922118f,-0.257219f,0.289027f},{-0.869252f,-0.420449f,0.260043f}, -{-0.0299809f,-0.993548f,0.109381f},{-0.00991282f,-0.999769f,0.0190808f},{0.227082f,-0.483709f,0.845257f}, -{0.318876f,-0.00218194f,0.947794f},{-0.721335f,-0.58379f,0.372645f},{-0.844342f,-0.429109f,0.320862f}, -{-0.793675f,-0.561927f,0.233064f},{-0.446212f,0.0339292f,-0.894284f},{-0.542424f,0.173159f,-0.822066f}, -{-0.523215f,0.18899f,-0.83098f},{0.852689f,0.312839f,0.418393f},{0.326618f,0.746278f,-0.579991f}, -{0.43824f,0.765982f,-0.470337f},{0.394471f,0.836057f,-0.381316f},{0.947904f,0.0496112f,0.31467f}, -{-0.69292f,-0.718449f,0.0607708f},{-0.83053f,-0.546689f,-0.106539f},{-0.968231f,0.0429053f,0.246349f}, -{-0.823249f,0.447806f,0.3489f},{0.0290057f,-0.99844f,0.0477127f},{0.135844f,0.356041f,-0.924544f}, -{0.157125f,0.356013f,-0.921177f},{0.702615f,-0.705316f,0.094134f},{-0.207965f,0.00367548f,-0.978129f}, -{-0.553098f,0.275765f,-0.786152f},{-0.646691f,0.197265f,-0.736802f},{-0.67218f,0.236112f,-0.70173f}, -{0.730094f,-0.00643599f,0.683316f},{0.942911f,-0.321198f,0.0880354f},{0.837478f,0.339639f,0.428107f}, -{0.522261f,0.307107f,0.795569f},{0.57665f,-0.609907f,0.543588f},{0.59619f,-0.248945f,0.763272f}, -{0.663708f,-0.395918f,0.634619f},{0.363764f,0.0543848f,0.929902f},{-0.624526f,-0.732207f,0.271734f}, -{-0.62029f,-0.71074f,0.331798f},{0.911724f,0.135763f,0.387721f},{0.181805f,0.489394f,0.852901f}, -{0.336708f,-0.102173f,0.93605f},{0.354085f,0.0237752f,0.934911f},{0.57874f,0.741724f,-0.338977f}, -{0.545642f,0.698697f,-0.462706f},{0.566298f,0.757711f,-0.324314f},{-0.314115f,0.947718f,0.0562388f}, -{0.581017f,-0.458852f,0.672216f},{-0.334189f,0.465636f,-0.819452f},{-0.312672f,0.554287f,-0.771364f}, -{0.581397f,-0.348002f,0.73544f},{-0.0906116f,0.499157f,0.861761f},{0.0294859f,-0.0565748f,-0.997963f}, -{-0.0298575f,0.0125533f,-0.999475f},{-0.0240975f,-0.0732973f,-0.997019f},{-0.367049f,0.148835f,-0.918217f}, -{-0.0153553f,0.739279f,-0.673224f},{0.3737f,0.71988f,-0.584911f},{0.291589f,0.00169327f,0.956542f}, -{-0.405861f,0.384739f,-0.829007f},{-0.444386f,0.455033f,-0.771664f},{-0.701359f,0.404044f,-0.587234f}, -{0.606879f,-0.193662f,0.770839f},{0.561341f,-0.104168f,0.821003f},{0.39326f,0.358897f,0.846486f}, -{0.328906f,-0.335092f,0.882912f},{-0.0572828f,0.247977f,-0.967071f},{0.880482f,-0.0150773f,0.47384f}, -{-0.358699f,0.160404f,-0.919568f},{0.36513f,0.322964f,0.873141f},{0.871962f,0.0410214f,0.487851f}, -{0.288687f,0.0169397f,0.957274f},{0.551894f,-0.577205f,0.601871f},{0.360338f,-0.865636f,-0.347608f}, -{0.293413f,-0.897843f,-0.328309f},{0.399519f,0.467113f,0.78879f},{0.696026f,-0.0722678f,0.714371f}, -{-0.0496104f,-0.0161746f,-0.998638f},{0.017165f,-0.0241149f,-0.999562f},{0.221142f,-0.382684f,0.897023f}, -{-0.175494f,0.594691f,-0.784566f},{0.0255035f,0.0935805f,-0.995285f},{-0.0210553f,0.317065f,-0.94817f}, -{0.509115f,0.633526f,0.58262f},{0.490691f,-0.311449f,0.81377f},{0.33886f,0.225938f,0.913305f}, -{0.465134f,-0.14352f,-0.873529f},{0.365383f,-0.100181f,-0.925451f},{-0.633866f,0.676616f,0.374707f}, -{-0.287038f,0.322961f,-0.901834f},{-0.720707f,-0.115035f,-0.683629f},{-0.499874f,-0.0901865f,-0.86139f}, -{-0.257732f,0.840352f,-0.476846f},{0.509487f,-0.632754f,0.583134f},{0.287168f,-0.31054f,0.906145f}, -{0.299274f,0.0483362f,0.952942f},{-0.100852f,0.454565f,-0.884986f},{0.67887f,0.428883f,-0.595982f}, -{0.497894f,-0.411631f,0.763323f},{0.244059f,0.609261f,0.754478f},{0.782364f,-0.223263f,0.581429f}, -{-0.310859f,0.769689f,0.557625f},{-0.370555f,0.816573f,0.442603f},{0.396677f,0.000531581f,0.917958f}, -{-0.0780473f,0.136668f,-0.987538f},{-0.193533f,0.894149f,0.403785f},{-0.469226f,0.810899f,0.349672f}, -{-0.599307f,0.720881f,0.348083f},{0.597735f,0.798804f,-0.0680163f},{-0.390329f,0.730714f,-0.56009f}, -{0.959444f,0.0389925f,0.27919f},{0.0866216f,-0.0782957f,-0.99316f},{0.0659964f,-0.0736851f,-0.995095f}, -{0.2912f,0.0574939f,0.954933f},{0.88907f,0.321536f,0.325836f},{-0.0155458f,0.906453f,0.42202f}, -{0.666373f,-0.500383f,-0.552778f},{0.8608f,0.346833f,0.372466f},{0.843227f,0.25116f,0.475275f}, -{-0.453813f,-0.0143082f,-0.890982f},{-0.24794f,-0.307459f,-0.918692f},{0.257164f,0.0550692f,0.964797f}, -{-0.366218f,-0.429376f,0.825543f},{-0.4966f,-0.309925f,0.810762f},{0.127606f,-0.14502f,-0.981166f}, -{-0.762129f,-0.60679f,0.225756f},{0.0143175f,-0.999788f,-0.0147732f},{0.0109175f,-0.99987f,-0.0118935f}, -{0.00619323f,-0.999932f,-0.00986292f},{-0.142064f,-0.566734f,-0.811561f},{-0.982566f,-0.0233312f,-0.184445f}, -{-0.982201f,0.185549f,-0.0292062f},{-0.52164f,0.791731f,-0.31789f},{-0.644517f,0.597376f,-0.477221f}, -{-0.696846f,0.483527f,-0.529723f},{-0.883865f,0.259645f,0.389059f},{-0.89337f,0.245748f,0.376163f}, -{0.092153f,-0.238835f,-0.966678f},{-0.898211f,0.397905f,-0.186783f},{0.997552f,-0.0618775f,-0.032576f}, -{0.993174f,-0.107765f,-0.0446236f},{-0.940601f,0.262619f,-0.215175f},{-0.897251f,0.333562f,-0.289268f}, -{0.439447f,-0.896273f,0.0598437f},{0.400452f,-0.910927f,0.0992435f},{0.427674f,-0.902604f,0.0490067f}, -{0.0360786f,-0.997277f,0.0643199f},{0.0645358f,-0.994324f,0.0845833f},{0.224178f,-0.969841f,0.0956641f}, -{-0.980638f,-0.00736646f,-0.195689f},{-0.572463f,-0.725649f,-0.381733f},{-0.618061f,-0.659854f,-0.42731f}, -{-0.362638f,-0.894922f,-0.260017f},{0.00462647f,-0.999587f,-0.0283808f},{-0.00164635f,-0.999876f,-0.015652f}, -{-0.00983998f,-0.999952f,0.000305002f},{0.973157f,-0.183172f,-0.139335f},{0.996745f,-0.0737784f,-0.0324859f}, -{0.986885f,-0.111135f,-0.11708f},{0.997794f,0.0325288f,-0.0578666f},{0.995577f,0.0928073f,0.0145992f}, -{-0.83468f,-0.496603f,0.238109f},{0.96726f,0.253285f,0.0159274f},{0.961391f,0.272285f,0.0398579f}, -{-0.359224f,0.378528f,-0.853039f},{-0.492686f,0.422685f,-0.760656f},{-0.0326768f,0.862869f,-0.504371f}, -{-0.0986429f,0.811704f,-0.575678f},{-0.0482041f,0.821139f,-0.56869f},{0.695949f,-0.682593f,-0.222985f}, -{-0.231695f,0.317407f,-0.919549f},{-0.339532f,0.320155f,-0.884431f},{-0.395214f,0.859704f,0.323596f}, -{0.3429f,0.415894f,-0.84229f},{0.385219f,0.263828f,-0.884308f},{0.378023f,0.302815f,-0.874873f}, -{-0.960881f,0.24907f,0.121132f},{-0.971621f,0.226807f,0.0671622f},{-0.37359f,0.599063f,-0.708204f}, -{-0.377794f,0.819743f,-0.430457f},{-0.609038f,0.677555f,-0.412301f},{0.385864f,-0.911894f,0.139852f}, -{0.0697907f,-0.995996f,0.0558698f},{0.845772f,0.507229f,0.165494f},{0.777954f,0.593335f,0.206739f}, -{0.920531f,0.362211f,0.146376f},{0.912664f,0.389791f,0.122911f},{0.984758f,0.16318f,0.0601944f}, -{0.997034f,0.0679387f,0.0361566f},{0.994588f,-0.101538f,-0.0220202f},{-0.666404f,0.563915f,-0.487755f}, -{-0.714725f,0.362688f,-0.598018f},{0.83658f,-0.340717f,-0.429007f},{0.863925f,-0.450595f,-0.224937f}, -{-0.799294f,0.529921f,0.283395f},{-0.967002f,0.195641f,0.163192f},{-0.339817f,0.914487f,-0.219634f}, -{-0.545492f,0.8063f,-0.228733f},{0.672512f,-0.114677f,-0.731148f},{-0.762563f,-0.532268f,-0.367679f}, -{-0.876042f,-0.304687f,-0.373786f},{-0.844516f,-0.278079f,-0.457673f},{-0.929772f,-0.312106f,-0.195229f}, -{-0.536093f,0.714569f,-0.449439f},{-0.33016f,0.913565f,-0.237471f},{-0.379477f,0.861928f,-0.336271f}, -{0.971925f,-0.229338f,-0.052599f},{-0.861158f,0.00599323f,-0.508301f},{-0.900303f,0.0329582f,-0.434013f}, -{-0.885967f,-0.0618478f,-0.459606f},{-0.536935f,-0.162785f,-0.827769f},{0.100273f,-0.323968f,-0.940739f}, -{-0.118235f,-0.477087f,-0.870867f},{0.74417f,-0.572401f,-0.344338f},{-0.875321f,-0.0583348f,-0.480011f}, -{0.940173f,0.151028f,-0.305393f},{0.962733f,-0.240052f,0.124578f},{0.76268f,-0.354927f,-0.54069f}, -{-0.999025f,0.00702945f,0.043579f},{-0.982233f,-0.171173f,0.0769339f},{-0.942957f,0.159886f,-0.292009f}, -{-0.478959f,0.775714f,-0.410934f},{0.982423f,-0.186548f,-0.00665627f},{0.990741f,-0.132479f,0.0296812f}, -{0.931631f,-0.354107f,-0.0816785f},{0.791666f,-0.0679739f,-0.607161f},{0.754519f,-0.0879699f,-0.650356f}, -{0.793904f,-0.0796276f,-0.602807f},{-0.677332f,0.731321f,-0.0799375f},{-0.656232f,0.708389f,-0.259893f}, -{-0.838214f,0.504165f,-0.207881f},{0.204869f,-0.840839f,0.501018f},{0.153821f,-0.480468f,0.863417f}, -{0.0704106f,-0.942853f,0.325686f},{0.956408f,-0.228265f,-0.18215f},{0.986183f,-0.0936427f,-0.136652f}, -{0.995545f,0.0406436f,-0.085079f},{-0.839642f,0.486502f,-0.24149f},{0.769548f,-0.526779f,-0.36097f}, -{-0.910351f,-0.333461f,-0.245081f},{-0.757463f,-0.364873f,-0.541404f},{-0.744719f,0.390985f,-0.540855f}, -{-0.863937f,-0.467813f,-0.186452f},{-0.743624f,0.127538f,-0.656321f},{-0.138916f,-0.41781f,-0.897851f}, -{0.95161f,-0.305239f,-0.035608f},{-0.0063863f,-0.628727f,-0.7776f},{-0.952006f,0.144361f,-0.269899f}, -{0.651594f,-0.183612f,-0.736011f},{0.674141f,-0.29853f,-0.675584f},{0.741378f,-0.230745f,-0.630171f}, -{-0.15322f,0.899928f,-0.408231f},{-0.207807f,0.846397f,-0.490335f},{-0.0451371f,0.827896f,-0.559063f}, -{-0.899822f,-0.311671f,-0.305256f},{-0.786522f,0.47361f,0.396329f},{-0.850935f,0.406667f,0.332463f}, -{-0.885878f,0.335753f,0.32014f},{0.686391f,-0.683815f,-0.247517f},{-0.67231f,-0.656937f,-0.341221f}, -{0.0f,0.0f,1.0f},{-0.937959f,-0.346653f,-0.00801302f},{-0.98739f,0.133802f,0.0846035f}, -{-0.0273161f,-0.138734f,-0.989953f},{0.0374685f,-0.224627f,-0.973724f},{0.705075f,-0.356919f,-0.612763f}, -{0.73297f,-0.423725f,-0.532176f},{0.77507f,-0.397151f,-0.491464f},{0.835103f,-0.224489f,-0.502203f}, -{0.813602f,-0.520484f,-0.25913f},{0.622879f,-0.584838f,-0.519602f},{0.698437f,-0.544724f,-0.464178f}, -{0.742741f,-0.582086f,-0.330926f},{0.598783f,-0.742132f,-0.301162f},{-0.949238f,0.306132f,0.0723165f}, -{-0.87596f,0.150958f,0.458154f},{0.986936f,0.159859f,0.0200684f},{0.956088f,0.286087f,0.0636382f}, -{0.984588f,0.121724f,0.125577f},{0.728274f,-0.0628471f,-0.682398f},{0.665955f,-0.0317118f,-0.745318f}, -{0.728609f,-0.14214f,-0.670019f},{-0.980512f,-0.0184633f,-0.195589f},{0.653931f,-0.666453f,0.358071f}, -{0.795056f,-0.276985f,-0.539598f},{0.829892f,-0.0953654f,-0.549713f},{0.861028f,-0.224006f,-0.456565f}, -{0.714815f,-0.275027f,-0.642962f},{-0.99155f,0.0806621f,0.101594f},{-0.127067f,0.871035f,-0.474503f}, -{0.593073f,-0.762618f,-0.258221f},{0.741611f,-0.633575f,-0.220445f},{0.879746f,-0.306992f,-0.363048f}, -{0.964765f,-0.0275073f,0.261671f},{-0.493436f,0.858968f,0.136728f},{-0.485737f,0.873949f,-0.0165395f}, -{-0.348249f,0.195388f,-0.916813f},{-0.865865f,0.42357f,0.266207f},{-0.991741f,0.0369164f,0.122831f}, -{-0.95484f,0.230839f,0.187067f},{0.413983f,-0.524267f,-0.744151f},{0.429364f,-0.694634f,-0.577174f}, -{0.190692f,-0.890844f,-0.412351f},{0.930414f,0.288332f,0.226261f},{0.997636f,-0.0670532f,0.0150621f}, -{0.995219f,0.0057273f,0.0975008f},{0.876074f,0.45196f,0.168009f},{0.878814f,-0.41968f,-0.227055f}, -{-0.230254f,-0.803082f,0.549584f},{-0.452718f,-0.646761f,0.613796f},{0.685239f,0.594863f,-0.420221f}, -{0.712106f,0.108611f,-0.69362f},{0.80921f,0.100493f,-0.578862f},{0.69558f,-0.243057f,-0.676086f}, -{0.749341f,-0.229107f,-0.621287f},{-0.46364f,-0.34574f,-0.815783f},{-0.829148f,-0.522963f,0.197543f}, -{-0.780415f,-0.576732f,0.241522f},{-0.919049f,-0.349159f,0.182857f},{-0.918924f,0.166482f,-0.35758f}, -{-0.949718f,0.0941859f,-0.298605f},{-0.970658f,0.0626362f,-0.232164f},{0.826395f,0.189191f,-0.530357f}, -{0.909795f,0.130265f,-0.394086f},{0.909374f,-0.00413196f,-0.41596f},{0.895746f,0.0197669f,-0.444126f}, -{0.690116f,-0.378259f,-0.616977f},{-0.895177f,-0.362898f,0.258771f},{0.446098f,-0.890696f,0.0875017f}, -{0.391035f,-0.914116f,0.107161f},{0.459105f,-0.882593f,0.101258f},{-0.633135f,-0.51279f,0.579816f}, -{-0.649023f,-0.452888f,0.611279f},{-0.59273f,-0.407957f,0.694437f},{0.904743f,-0.242724f,0.350034f}, -{0.828868f,-0.426799f,-0.361691f},{0.792659f,-0.553534f,-0.255525f},{-0.943462f,-0.127474f,-0.30599f}, -{-0.912574f,-0.0911195f,-0.39863f},{0.74525f,0.196846f,-0.637067f},{0.789092f,0.275901f,-0.548828f}, -{0.0114303f,-0.999602f,-0.0257713f},{0.814482f,-0.153989f,-0.55938f},{0.883559f,-0.106128f,-0.456136f}, -{0.0991691f,-0.75282f,-0.650713f},{0.1192f,-0.891997f,-0.436042f},{0.0416995f,-0.990848f,-0.128381f}, -{-0.868636f,0.464477f,0.172431f},{0.648167f,0.0160177f,-0.761329f},{0.685066f,0.331524f,-0.648673f}, -{0.713803f,-0.585761f,-0.383887f},{0.548529f,-0.764757f,-0.338028f},{-0.941654f,-0.0652673f,-0.330193f}, -{-0.947257f,-0.0320283f,-0.318871f},{-0.736538f,-0.154852f,-0.658432f},{0.841077f,-0.0220143f,-0.540467f}, -{0.839122f,0.304265f,-0.450884f},{0.671902f,-0.415255f,-0.613279f},{0.743045f,-0.290646f,-0.602834f}, -{0.791078f,-0.213203f,-0.573359f},{-0.414459f,0.780265f,0.468414f},{-0.333447f,0.81949f,0.466099f}, -{-0.350316f,0.897013f,0.269529f},{-0.445173f,0.203844f,-0.871934f},{-0.554085f,0.282607f,-0.783022f}, -{-0.371315f,0.14061f,-0.917799f},{-0.525501f,-0.42254f,-0.73845f},{-0.193952f,-0.922888f,-0.332656f}, -{-0.644837f,0.638904f,0.419509f},{-0.577435f,0.74013f,0.344638f},{0.642653f,-0.725827f,-0.245299f}, -{-0.534687f,0.832182f,-0.146915f},{-0.451134f,0.798492f,-0.398608f},{0.0169719f,-0.999804f,-0.0101645f}, -{0.036643f,-0.999184f,-0.016996f},{-0.947189f,0.122828f,0.296219f},{-0.986756f,0.0836779f,0.138966f}, -{-0.716169f,0.350797f,-0.60336f},{-0.200614f,-0.294627f,-0.934318f},{-0.321563f,-0.17239f,-0.931063f}, -{-0.956011f,0.291732f,-0.0305733f},{-0.684357f,0.683399f,-0.254208f},{0.60638f,-0.0863361f,-0.790474f}, -{0.625317f,0.0625792f,-0.777858f},{-0.9141f,0.225904f,-0.336733f},{0.74609f,-0.49078f,-0.449982f}, -{0.754326f,-0.426754f,-0.498872f},{0.721635f,-0.498468f,-0.480389f},{-0.696162f,-0.506157f,-0.509081f}, -{-0.677645f,-0.550992f,-0.487038f},{-0.924725f,0.272964f,-0.265283f},{0.747567f,-0.162517f,-0.643997f}, -{0.733726f,-0.26049f,-0.627528f},{-0.841458f,-0.3679f,0.395725f},{-0.780482f,-0.625175f,0.00217309f}, -{-0.467462f,0.83911f,-0.278161f},{-0.768839f,0.627927f,-0.120804f},{0.100807f,-0.994218f,-0.0369808f}, -{0.0348387f,-0.995477f,-0.0883868f},{-0.391848f,0.873182f,-0.28984f},{0.032085f,0.683438f,-0.729303f}, -{0.0152515f,0.645162f,-0.763893f},{-0.0140963f,0.692749f,-0.721041f},{0.798704f,-0.184977f,-0.572586f}, -{-0.523094f,0.852059f,-0.0191815f},{-0.954618f,-0.0164208f,-0.29738f},{-0.975711f,0.0897672f,-0.199826f}, -{-0.965971f,0.18126f,-0.184511f},{-0.950108f,0.0759588f,-0.30253f},{0.992414f,0.121164f,0.0208189f}, -{0.611404f,0.221804f,-0.759598f},{0.990116f,-0.0971424f,-0.101157f},{-0.7541f,-0.0822748f,-0.651585f}, -{-0.894907f,-0.409213f,-0.178004f},{-0.977252f,-0.183154f,-0.106927f},{-0.137259f,0.411548f,-0.900993f}, -{-0.0444384f,0.517873f,-0.854303f},{-0.230663f,0.547608f,-0.804313f},{0.980831f,0.19448f,0.0121421f}, -{-0.147885f,0.685989f,-0.712425f},{-0.107152f,0.598248f,-0.794114f},{-0.638269f,0.716773f,-0.280801f}, -{-0.851021f,0.359455f,0.382826f},{-0.82939f,0.350349f,0.435165f},{-0.194385f,-0.436604f,-0.878403f}, -{-0.704735f,-0.236526f,-0.668882f},{-0.894576f,0.111168f,0.43287f},{0.723662f,-0.0895417f,-0.684321f}, -{-0.26078f,0.930097f,-0.258678f},{-0.666087f,0.17054f,-0.726116f},{-0.589705f,-0.152056f,-0.793175f}, -{0.0588107f,0.66234f,-0.746892f},{0.0464484f,0.641371f,-0.765824f},{0.732878f,0.108509f,-0.671651f}, -{0.769934f,0.058901f,-0.6354f},{0.704258f,0.155158f,-0.692782f},{-0.905845f,0.0736368f,-0.417159f}, -{-0.886491f,-0.0258386f,-0.462024f},{-0.850694f,0.0210112f,-0.525241f},{0.767409f,-0.243228f,-0.593231f}, -{0.827619f,-0.193028f,-0.527055f},{0.779302f,-0.23095f,-0.582538f},{-0.884043f,-0.4635f,-0.0602925f}, -{-0.959193f,-0.26739f,-0.0919339f},{-0.85836f,-0.41451f,0.302322f},{0.765939f,-0.614183f,-0.190045f}, -{0.755147f,-0.00353785f,-0.655546f},{0.768294f,-0.249242f,-0.589579f},{0.76295f,0.0228429f,-0.646054f}, -{0.772987f,-0.200477f,-0.601914f},{0.839712f,0.218607f,-0.497086f},{0.000715683f,-0.980848f,0.194775f}, -{-0.635974f,-0.730907f,0.247612f},{-0.639593f,-0.35236f,-0.683201f},{-0.684919f,-0.396537f,-0.611264f}, -{-0.618439f,-0.339162f,-0.708874f},{0.127168f,-0.681979f,-0.720231f},{0.0782779f,-0.738573f,-0.669614f}, -{0.0113648f,-0.991394f,-0.130418f},{0.289444f,0.140849f,-0.946775f},{0.335955f,0.19986f,-0.92043f}, -{0.193556f,0.347962f,-0.91731f},{-0.974989f,0.117221f,-0.188825f},{0.219525f,0.217545f,-0.951043f}, -{-0.897894f,-0.341032f,0.27836f},{-0.897788f,-0.411493f,0.157002f},{-0.839134f,-0.369163f,0.399466f}, -{-0.895872f,-0.175299f,0.408268f},{0.843528f,-0.381703f,-0.37784f},{-0.832408f,0.491697f,-0.2556f}, -{0.518818f,0.0828733f,-0.850858f},{0.506607f,0.0628409f,-0.859884f},{0.54957f,0.240652f,-0.800037f}, -{-0.638102f,0.465486f,-0.61331f},{-0.513594f,0.704289f,-0.4901f},{-0.472045f,0.46126f,-0.751274f}, -{-0.952576f,-0.11264f,0.282687f},{0.99936f,0.00995633f,0.0343486f},{0.963471f,0.196356f,0.182123f}, -{0.984349f,0.0784931f,0.157783f},{0.279703f,0.283064f,-0.91741f},{0.181413f,0.312065f,-0.93258f}, -{0.236822f,0.427557f,-0.872416f},{-0.105609f,0.685177f,-0.72068f},{-0.976885f,-0.0286416f,0.211839f}, -{0.725157f,-0.00896551f,-0.688525f},{0.724382f,0.0375398f,-0.688376f},{0.766879f,-0.221373f,-0.602404f}, -{0.747531f,-0.347926f,-0.565814f},{0.105336f,-0.993702f,-0.0382186f},{0.399919f,-0.874604f,-0.274102f}, -{-0.642518f,0.276432f,-0.714673f},{-0.671372f,0.263996f,-0.692507f},{0.675291f,-0.602507f,-0.425403f}, -{0.218857f,-0.971126f,0.0949512f},{0.628034f,-0.747951f,0.21481f},{0.46298f,-0.856397f,0.228545f}, -{0.808891f,-0.569684f,-0.14545f},{0.653049f,-0.392291f,-0.647792f},{-0.0164474f,-0.999166f,0.0373811f}, -{-0.00770074f,-0.99916f,0.0402574f},{0.000142267f,-0.9996f,0.0282806f},{-0.141438f,0.915478f,-0.37669f}, -{-0.0262741f,0.8654f,-0.500392f},{-0.108226f,0.86563f,-0.488847f},{-0.189009f,0.743456f,-0.641521f}, -{-0.398889f,0.836374f,-0.375986f},{-0.934459f,0.0509862f,-0.352402f},{0.793651f,-0.04301f,-0.606851f}, -{0.713646f,0.148586f,-0.684567f},{-0.789885f,-0.580768f,0.196952f},{-0.807347f,-0.562697f,0.177657f}, -{-0.802356f,-0.546869f,0.239081f},{-0.118066f,-0.971948f,0.203415f},{-0.427385f,0.756545f,0.494956f}, -{0.73633f,0.308414f,-0.602245f},{0.859609f,0.0481623f,-0.508678f},{0.0357142f,-0.425029f,-0.904475f}, -{-0.216403f,-0.407845f,-0.887036f},{0.591956f,-0.749826f,-0.29555f},{0.579828f,-0.624834f,-0.52286f}, -{0.745975f,-0.250335f,-0.617133f},{0.770701f,-0.326509f,-0.547185f},{-0.889108f,-0.0981269f,-0.447054f}, -{-0.454529f,-0.0502772f,-0.889312f},{-0.637814f,-0.765383f,-0.0859141f},{-0.790282f,-0.609604f,0.0619454f}, -{0.786256f,0.100661f,-0.609646f},{0.970064f,0.194078f,0.145976f},{0.945551f,0.300532f,0.124952f}, -{-0.963703f,0.07142f,0.257248f},{0.0192169f,-0.978854f,-0.203654f},{0.893996f,-0.0181557f,-0.447706f}, -{-0.96542f,0.108981f,0.236827f},{-0.696219f,-0.631337f,-0.341603f},{-0.612492f,-0.74952f,-0.251142f}, -{-0.29042f,-0.876351f,-0.384273f},{0.639278f,-0.729907f,0.241988f},{0.69726f,-0.654375f,0.29261f}, -{-0.978716f,0.13926f,0.150735f},{0.000183522f,-0.998022f,0.0628701f},{-0.921737f,0.0503909f,-0.384528f}, -{-0.800558f,-0.493875f,-0.339402f},{-0.497304f,0.332329f,-0.801403f},{0.615217f,-0.432449f,0.659163f}, -{0.797934f,-0.505531f,-0.328238f},{0.865963f,-0.084911f,-0.492847f},{-0.305809f,-0.410663f,-0.858974f}, -{0.248637f,-0.581692f,-0.774476f},{-0.0103987f,-0.99808f,0.0610528f},{-0.951984f,0.221019f,0.21184f}, -{0.977876f,-0.181766f,-0.103535f},{-0.921649f,0.36585f,0.129297f},{0.699153f,-0.675216f,-0.23509f}, -{0.755519f,-0.604696f,-0.25206f},{0.638633f,-0.518986f,-0.568157f},{0.71167f,-0.542609f,-0.446207f}, -{0.484392f,-0.401776f,-0.777136f},{0.703104f,-0.546784f,-0.454612f},{-0.858558f,0.0931788f,-0.504178f}, -{-0.921669f,0.165028f,-0.351129f},{-0.806021f,-0.148181f,-0.573037f},{-0.710689f,-0.664555f,-0.230841f}, -{-0.840258f,-0.120608f,0.528602f},{0.887732f,-0.18165f,-0.423007f},{0.904292f,-0.119169f,-0.409946f}, -{0.867545f,-0.118966f,-0.482921f},{0.99829f,0.0581998f,-0.00551678f},{-0.914838f,0.361162f,0.180649f}, -{-0.912489f,0.395871f,0.103196f},{0.531903f,0.275915f,-0.800593f},{0.515947f,0.267172f,-0.813891f}, -{0.519825f,0.372712f,-0.768679f},{0.762899f,-0.17899f,-0.621246f},{0.702701f,-0.150915f,-0.695296f}, -{-0.294184f,0.897705f,-0.327998f},{0.147592f,0.958411f,-0.244265f},{0.177148f,0.966717f,-0.1846f}, -{0.176799f,0.971614f,-0.15719f},{-0.465063f,0.0616191f,0.883131f},{-0.418584f,-0.261446f,0.869731f}, -{0.583074f,-0.793946f,0.172264f},{0.731105f,-0.0128284f,-0.682144f},{0.676149f,-0.462984f,-0.573121f}, -{0.723236f,-0.330051f,-0.606626f},{0.704461f,0.394819f,-0.58979f},{0.843936f,-0.107503f,-0.525562f}, -{-0.96141f,-0.00372427f,-0.275096f},{-0.894378f,0.323282f,-0.309154f},{0.956972f,0.136056f,0.256306f}, -{-0.933431f,-0.350667f,-0.0757507f},{-0.130992f,0.607162f,-0.783706f},{-0.154968f,0.416175f,-0.895982f}, -{-0.260322f,0.567362f,-0.781238f},{-0.0362481f,0.607833f,-0.793237f},{-0.141952f,0.705861f,-0.693981f}, -{-0.121685f,0.645941f,-0.753627f},{-0.297167f,0.482335f,-0.824042f},{0.740034f,0.428893f,-0.518074f}, -{0.657119f,0.4288f,-0.619939f},{0.507662f,-0.441258f,-0.73998f},{-0.898184f,-0.155866f,-0.411062f}, -{-0.731846f,-0.184488f,-0.656023f},{-0.585754f,-0.776209f,-0.23322f},{-0.977273f,0.0661205f,-0.201412f}, -{-0.869666f,0.468964f,0.154123f},{-0.874285f,0.47548f,0.0976949f},{-0.765902f,0.148325f,0.625615f}, -{-0.899666f,0.406081f,0.160309f},{-0.935898f,0.330841f,0.120995f},{-0.974013f,0.125771f,0.188361f}, -{-0.976974f,0.0530194f,0.206668f},{0.923756f,0.129146f,0.360549f},{0.0350245f,-0.887122f,0.460203f}, -{0.67463f,-0.30715f,-0.671218f},{-0.504845f,-0.486101f,0.713328f},{-0.516171f,-0.350276f,0.781584f}, -{0.31792f,-0.3715f,-0.872305f},{0.511809f,-0.490299f,-0.705449f},{-0.978509f,0.204314f,-0.0278446f}, -{0.147245f,0.551976f,-0.820756f},{0.130809f,0.437729f,-0.88954f},{0.00867079f,0.703226f,-0.710913f}, -{0.425153f,-0.903936f,0.0463115f},{0.403859f,-0.905871f,0.127655f},{-0.770367f,-0.617828f,-0.157556f}, -{0.714366f,0.00755797f,-0.699732f},{0.887058f,-0.253175f,-0.386045f},{0.904446f,-0.229688f,-0.359473f}, -{0.889749f,-0.237271f,-0.389934f},{-0.0191102f,-0.999817f,-0.00115212f},{0.0066128f,-0.999505f,0.0307467f}, -{0.00427601f,-0.998407f,-0.0562644f},{0.709271f,-0.160033f,-0.686531f},{0.74534f,-0.288142f,-0.601201f}, -{0.608205f,-0.355153f,-0.709896f},{0.125865f,0.782562f,-0.609717f},{-0.0739411f,0.799269f,-0.596407f}, -{0.0508085f,0.72803f,-0.68366f},{-0.814112f,-0.561646f,-0.147568f},{0.972331f,0.159512f,0.170672f}, -{0.861139f,-0.229934f,-0.453399f},{0.894211f,-0.209492f,-0.395601f},{-0.220335f,-0.862894f,0.454827f}, -{-0.215799f,-0.863267f,0.456291f},{0.819689f,0.0614222f,-0.569506f},{0.857209f,0.0364918f,-0.513673f}, -{-0.95851f,-0.25396f,0.129474f},{0.847391f,-0.263974f,-0.460701f},{0.410327f,-0.908244f,0.0820033f}, -{0.250237f,-0.965597f,0.0707417f},{0.529549f,-0.264119f,-0.806113f},{0.541477f,0.764778f,-0.349167f}, -{-0.0183646f,0.673017f,-0.739399f},{-0.00922519f,0.686325f,-0.727236f},{-0.969929f,-0.182964f,0.160506f}, -{-0.0327101f,-0.996404f,-0.0781646f},{0.542598f,0.111129f,-0.832609f},{-0.054401f,0.718347f,-0.693555f}, -{0.960249f,-0.258809f,-0.104594f},{0.868777f,-0.0882508f,-0.487276f},{0.0262056f,-0.999314f,-0.0261689f}, -{-0.742036f,-0.634751f,0.215576f},{-0.790094f,-0.603961f,-0.104797f},{0.684195f,0.0425916f,-0.728054f}, -{0.909851f,-0.135259f,-0.39227f},{0.846745f,0.00228132f,-0.531994f},{-0.982988f,-0.164733f,0.0812222f}, -{0.995198f,-0.0892049f,-0.0402945f},{0.986315f,0.147268f,0.0741227f},{0.566721f,0.31205f,-0.76253f}, -{0.401851f,0.226639f,-0.887215f},{0.0372775f,-0.264135f,-0.963765f},{-0.976716f,0.0122543f,0.214188f}, -{-0.313664f,0.917903f,-0.243042f},{-0.352028f,0.889474f,-0.291398f},{0.108126f,-0.83625f,-0.537583f}, -{0.171862f,-0.95061f,-0.258466f},{0.86963f,-0.270204f,-0.4132f},{-0.903531f,0.323979f,0.28048f}, -{-0.683437f,0.620562f,0.384468f},{-0.691721f,-0.7163f,-0.0918445f},{-0.736096f,0.554191f,0.388633f}, -{-0.645063f,0.68258f,0.34348f},{-0.614513f,-0.77154f,0.164621f},{0.669319f,-0.537608f,-0.512825f}, -{0.327855f,-0.918252f,0.222092f},{0.298594f,-0.934062f,0.19588f},{0.354796f,-0.918097f,0.176686f}, -{-0.506672f,0.834157f,0.217865f},{0.0345878f,-0.999201f,-0.020016f},{0.931099f,0.276016f,-0.238473f}, -{-0.020536f,-0.30336f,-0.952655f},{-0.469175f,-0.414153f,-0.779969f},{0.118301f,-0.895303f,-0.429462f}, -{0.099007f,-0.97542f,-0.196861f},{0.0272237f,-0.999335f,-0.0242418f},{-0.674057f,0.449826f,-0.585921f}, -{0.703665f,0.384147f,-0.597735f},{0.33071f,0.351781f,-0.875718f},{0.0978065f,0.508281f,-0.855619f}, -{0.546689f,-0.0132231f,-0.837231f},{0.896842f,-0.151815f,-0.415484f},{-0.960075f,0.245652f,0.133831f}, -{-0.98563f,0.084896f,0.146033f},{-0.899805f,0.257965f,0.351859f},{0.827633f,-0.396764f,-0.39699f}, -{-0.883847f,0.292303f,0.365202f},{-0.965708f,0.175981f,0.190889f},{-0.272344f,-0.945067f,-0.180768f}, -{0.0209144f,-0.999074f,0.0376027f},{0.032975f,-0.998556f,-0.0424153f},{0.0318291f,-0.998875f,-0.0351631f}, -{0.0352428f,-0.998819f,-0.0334404f},{0.0388307f,-0.998288f,0.0437481f},{-0.647332f,-0.523114f,-0.554359f}, -{-0.355664f,-0.613777f,-0.704826f},{0.959933f,-0.275879f,-0.0491777f},{0.992104f,-0.125231f,0.00679531f}, -{-0.0944883f,0.823707f,0.559088f},{-0.999729f,-0.0207259f,-0.0106073f},{0.563333f,0.0340497f,-0.825528f}, -{0.998869f,0.0105482f,0.0463714f},{-0.470932f,0.711152f,0.522003f},{0.278589f,0.329839f,-0.901994f}, -{-0.886327f,0.432631f,-0.165089f},{-0.926808f,0.375345f,-0.0119485f},{0.204039f,-0.372273f,-0.905418f}, -{-0.992881f,0.0451062f,0.110243f},{-0.59668f,0.742712f,0.303894f},{-0.791167f,0.0261632f,-0.61104f}, -{0.438938f,-0.894537f,-0.0844837f},{-0.00869637f,-0.999954f,0.00417063f},{-0.0213252f,-0.999674f,0.0140172f}, -{-0.153982f,0.624937f,-0.765339f},{0.794822f,-0.532167f,-0.291645f},{0.811671f,-0.521067f,-0.263968f}, -{-0.517259f,-0.574715f,-0.63415f},{-0.93562f,0.340752f,-0.0922097f},{0.236175f,-0.960352f,-0.148136f}, -{0.553704f,-0.827051f,-0.096947f},{0.870867f,0.381557f,0.309847f},{0.853989f,0.341659f,0.392392f}, -{-0.154021f,-0.650535f,-0.743694f},{0.0262376f,-0.999048f,0.0348535f},{0.0225118f,-0.999678f,0.0117443f}, -{-0.124984f,-0.435542f,-0.891449f},{-0.37736f,-0.556838f,-0.739954f},{0.872701f,0.335303f,0.354917f}, -{0.853307f,0.326584f,0.40646f},{0.93813f,0.199247f,0.283218f},{0.0217241f,-0.999386f,-0.0274869f}, -{-0.597863f,-0.38968f,-0.700507f},{0.959073f,0.10872f,0.261455f},{-0.18484f,-0.257829f,-0.948345f}, -{0.154603f,-0.413225f,-0.897409f},{0.717788f,-0.687313f,0.111275f},{0.847382f,-0.520852f,0.103229f}, -{0.924113f,0.287993f,0.251147f},{0.911501f,0.262502f,0.316637f},{0.76148f,-0.482196f,-0.433169f}, -{0.940386f,0.129455f,0.314508f},{0.863147f,0.448876f,0.231275f},{0.761826f,-0.641831f,0.0876031f}, -{0.906001f,0.324256f,0.272067f},{0.0985965f,-0.902022f,-0.420279f},{-0.88257f,0.38391f,0.271447f}, -{-0.968928f,0.130172f,0.21032f},{-0.102488f,-0.994179f,-0.0332447f},{-0.0442014f,-0.992883f,-0.110588f}, -{0.962835f,0.199899f,0.181627f},{0.221024f,0.37711f,-0.899409f},{0.217884f,0.261235f,-0.940363f}, -{0.204653f,0.371797f,-0.905474f},{-0.385283f,0.722821f,0.573662f},{0.800531f,-0.19496f,-0.566693f}, -{0.975873f,0.0781187f,0.203887f},{0.976396f,0.098517f,0.192213f},{0.964255f,0.132192f,0.229645f}, -{0.95341f,0.0958946f,0.286032f},{0.946655f,0.14687f,0.286835f},{0.940817f,0.252628f,0.225928f}, -{0.905792f,0.35846f,0.225938f},{0.90519f,0.368597f,0.211583f},{0.916708f,0.349413f,0.193796f}, -{0.904462f,0.381361f,0.191082f},{0.824432f,0.565116f,0.0309246f},{0.82786f,0.560907f,-0.00562512f}, -{-0.96563f,-0.0745097f,0.249011f},{-0.351993f,-0.922569f,0.15801f},{-0.05336f,-0.995682f,0.075956f}, -{-0.360501f,-0.839956f,0.405602f},{0.864828f,0.398074f,0.305956f},{0.795907f,0.528246f,0.295782f}, -{0.956112f,0.21274f,0.201471f},{0.974732f,0.0578164f,0.215767f},{0.962779f,0.0784324f,0.258661f}, -{0.234707f,-0.73487f,0.636301f},{-0.210907f,-0.231875f,-0.949606f},{0.971296f,0.156885f,0.178808f}, -{0.951489f,0.120919f,0.282925f},{0.0275537f,-0.999051f,-0.0337455f},{-0.318759f,-0.932821f,-0.168041f}, -{0.816302f,0.491849f,0.302878f},{0.778816f,0.567945f,0.266241f},{0.617103f,-0.751326f,0.233866f}, -{0.986863f,-0.0263569f,0.159393f},{0.914441f,0.385846f,0.122154f},{0.939377f,0.3255f,0.107795f}, -{-0.269202f,-0.242719f,-0.931997f},{-0.62682f,0.758358f,0.178856f},{-0.842996f,0.537838f,-0.00937066f}, -{-0.624626f,0.757251f,-0.190825f},{-0.0746514f,-0.561558f,-0.824063f},{0.974361f,0.14028f,0.175902f}, -{0.721718f,0.541276f,-0.431444f},{0.691845f,-0.678714f,0.246368f},{0.940354f,-0.258607f,0.221034f}, -{0.961877f,-0.119882f,0.245806f},{0.63723f,-0.429472f,-0.639915f},{0.989181f,0.0945617f,0.11216f}, -{0.990752f,0.0975521f,0.094307f},{0.985711f,0.123981f,0.114028f},{0.977333f,0.166331f,0.130976f}, -{0.956945f,0.25428f,0.139992f},{0.923567f,0.353306f,0.148994f},{0.91272f,0.377482f,0.156363f}, -{0.916948f,0.362123f,0.167552f},{0.911395f,0.387113f,0.139649f},{0.835581f,0.53732f,0.114417f}, -{0.968796f,0.231741f,0.0879191f},{-0.704802f,0.0615895f,0.706725f},{-0.765553f,-0.532367f,0.361267f}, -{-0.755914f,-0.172051f,0.631659f},{0.863069f,0.405931f,0.300552f},{0.838471f,0.491299f,0.235781f}, -{0.902746f,0.311662f,0.296507f},{0.972589f,0.0739923f,0.220444f},{0.61259f,-0.265774f,-0.744377f}, -{0.968436f,0.216794f,0.123015f},{0.976937f,-0.152909f,0.14904f},{-0.171894f,0.773899f,-0.609535f}, -{-0.279005f,0.84322f,-0.459496f},{0.0318361f,-0.999257f,-0.0217057f},{-0.686432f,-0.418899f,0.59442f}, -{0.940718f,-0.314328f,0.127464f},{0.127985f,-0.970691f,-0.203416f},{-0.140049f,0.795548f,0.589483f}, -{0.890566f,0.394901f,0.225712f},{-0.17875f,0.0676498f,-0.981566f},{-0.130389f,0.136394f,-0.982036f}, -{-0.693075f,-0.719421f,-0.045614f},{-0.854093f,-0.520116f,0.00228063f},{-0.697985f,-0.682744f,0.216051f}, -{0.958009f,0.268102f,0.101683f},{0.910047f,0.341379f,0.235107f},{-0.130869f,0.189174f,-0.973184f}, -{-0.136388f,0.204777f,-0.96926f},{-0.360244f,0.91344f,-0.189348f},{0.00927586f,0.997961f,-0.0631495f}, -{-0.269327f,0.956986f,-0.107895f},{0.0065507f,0.999964f,-0.00535563f},{-0.0516087f,0.998536f,0.0162233f}, -{-0.00549771f,0.998424f,0.0558435f},{-0.966082f,-0.223912f,0.128644f},{-0.959726f,-0.273362f,0.0647994f}, -{0.195186f,0.974535f,0.110378f},{0.102488f,0.979166f,0.175299f},{0.647374f,0.671496f,0.360554f}, -{-0.3164f,0.947491f,0.0463864f},{0.0622768f,0.854897f,0.515046f},{0.0187868f,0.827973f,0.560453f}, -{0.793441f,0.573205f,0.204667f},{0.570489f,0.666316f,0.480172f},{0.0149417f,0.788806f,0.614461f}, -{0.551521f,0.679451f,0.483912f},{-0.33077f,0.874133f,0.355644f},{-0.381363f,0.807787f,0.449491f}, -{-0.0394864f,0.843368f,0.535884f},{-0.875505f,-0.465724f,0.128814f},{-0.186669f,0.821205f,0.539237f}, -{-0.40397f,0.803553f,0.437162f},{0.0275706f,0.940562f,0.3385f},{0.0127497f,0.923202f,0.384103f}, -{0.0978516f,0.88668f,0.451912f},{-0.978017f,0.136329f,0.157789f},{0.63961f,0.752729f,0.155875f}, -{0.574715f,0.728057f,0.373678f},{0.296871f,0.73371f,0.611177f},{0.583868f,0.792562f,-0.175909f}, -{0.613508f,0.789373f,-0.0223071f},{0.30635f,0.812628f,0.495768f},{0.7584f,0.598623f,0.257838f}, -{0.751662f,0.579045f,0.315772f},{0.626681f,0.697524f,0.347462f},{0.327733f,0.893655f,0.306548f}, -{0.0817467f,0.949563f,0.302734f},{0.609838f,0.702151f,0.367535f},{-0.141646f,0.863148f,0.484677f}, -{-0.248361f,0.827147f,0.504127f},{0.251704f,0.958776f,0.131885f},{-0.00860057f,0.843532f,0.53701f}, -{0.610942f,0.685026f,0.39685f},{0.563461f,0.706036f,0.428981f},{0.290591f,0.953423f,-0.0808732f}, -{-0.0416996f,0.942389f,0.331909f},{-0.242277f,-0.954806f,0.172186f},{-0.508478f,-0.833053f,0.217884f}, -{-0.706606f,-0.680659f,-0.193422f},{-0.780268f,-0.571677f,-0.253706f},{0.203519f,0.955669f,-0.212783f}, -{-0.409056f,0.847362f,0.338602f},{-0.10077f,0.935414f,0.338889f},{-0.143977f,0.951576f,0.271611f}, -{-0.167925f,0.867559f,0.468127f},{0.117004f,0.992992f,-0.0166474f},{-0.0198098f,0.997063f,0.0739749f}, -{-0.0233485f,0.980743f,0.193904f},{-0.0633045f,0.881613f,0.467708f},{-0.0576471f,0.742484f,0.667378f}, -{-0.38597f,0.7806f,0.491621f},{-0.424397f,0.710223f,0.561668f},{-0.329934f,0.7797f,0.532176f}, -{-0.66792f,0.709634f,0.224284f},{-0.451594f,0.832424f,0.321144f},{-0.0026761f,0.997846f,-0.0655392f}, -{0.0393049f,0.947095f,0.318538f},{-0.0874497f,0.213337f,-0.973057f},{-0.14254f,0.226346f,-0.963561f}, -{-0.079948f,0.963489f,0.255531f},{0.00520031f,0.979625f,-0.200767f},{0.160151f,0.97548f,0.150967f}, -{-0.30432f,0.934853f,-0.182866f},{-0.382215f,0.88348f,-0.270877f},{-0.0554854f,0.991601f,-0.116832f}, -{0.238752f,0.970688f,0.0276203f},{0.658975f,0.641087f,0.393396f},{-0.166042f,0.978496f,0.122378f}, -{0.0307127f,0.987931f,0.151817f},{-0.343655f,0.938408f,-0.0359483f},{0.334978f,0.937134f,-0.0978196f}, -{0.843998f,0.536343f,-0.00166254f},{0.0366952f,0.998946f,-0.0275677f},{-0.406013f,0.896936f,0.175099f}, -{-0.523428f,0.7564f,-0.392279f},{-0.719595f,0.569109f,-0.397866f},{0.0774109f,0.330299f,-0.940697f}, -{-0.926674f,0.36204f,0.101006f},{0.573185f,0.791566f,0.211852f},{0.759727f,0.648451f,-0.0482235f}, -{0.912176f,0.39349f,0.114457f},{-0.973178f,-0.227499f,0.0341798f},{0.27077f,0.673918f,0.6874f}, -{0.737332f,0.667635f,-0.102979f},{0.79274f,0.608946f,0.0273588f},{0.606984f,0.722816f,0.330314f}, -{0.898969f,0.435381f,0.0479348f},{0.596787f,0.704856f,-0.383437f},{-0.940796f,-0.0582889f,0.333923f}, -{0.00474336f,-0.999893f,-0.0138285f},{-0.00631413f,-0.999977f,0.00228954f},{0.0132945f,-0.999842f,0.0117666f}, -{0.725395f,0.687263f,0.0383686f},{-0.623881f,-0.449673f,-0.639192f},{-0.710202f,-0.337333f,-0.617915f}, -{-0.564536f,-0.513949f,-0.645876f},{0.205114f,0.939815f,-0.273268f},{0.306274f,0.933228f,-0.187833f}, -{0.293334f,0.918292f,-0.265886f},{-0.730003f,0.627909f,0.269862f},{0.0412235f,0.990147f,-0.133823f}, -{-0.0506354f,0.990554f,-0.127428f},{-0.797838f,0.144275f,0.585354f},{-0.814328f,-0.00914634f,0.580333f}, -{-0.875474f,-0.0288966f,0.482401f},{-0.87953f,0.267082f,0.393819f},{0.193281f,0.980464f,-0.0365016f}, -{0.241335f,0.970224f,0.0205498f},{0.2625f,0.963919f,-0.0442113f},{-0.0568875f,0.994164f,-0.0916615f}, -{-0.00599234f,0.955773f,0.294044f},{-0.632698f,0.175517f,-0.754246f},{-0.709847f,0.319233f,-0.627859f}, -{-0.644735f,0.295137f,-0.705132f},{0.0790042f,0.929158f,0.361142f},{0.76758f,0.541513f,0.342907f}, -{0.804976f,0.457231f,0.378091f},{0.147986f,0.988531f,0.0301111f},{0.696325f,0.673718f,0.247458f}, -{0.711674f,0.542561f,0.446261f},{0.242014f,0.964107f,-0.109214f},{0.161173f,0.924593f,0.345182f}, -{-0.999615f,-0.0214551f,0.0176068f},{-0.892926f,0.154387f,0.422904f},{-0.837955f,-0.521028f,0.162363f}, -{-0.839437f,-0.532918f,0.106511f},{0.0638424f,0.997652f,-0.0248022f},{-0.715259f,-0.673496f,0.186566f}, -{-0.61264f,-0.789564f,0.0355033f},{0.31859f,0.837066f,0.444771f},{0.316983f,0.920076f,-0.230177f}, -{0.428733f,0.866239f,-0.25655f},{0.271337f,0.960436f,-0.0627656f},{0.324601f,0.944442f,0.051613f}, -{-0.581084f,0.522689f,0.623809f},{-0.440341f,0.699445f,0.562918f},{-0.287863f,-0.196649f,-0.937264f}, -{-0.330953f,-0.324711f,-0.886021f},{0.339123f,0.808747f,0.480546f},{-0.021721f,0.882003f,0.470743f}, -{0.127878f,0.991291f,0.0314696f},{0.098346f,0.992921f,0.0665993f},{0.0284123f,-0.997479f,0.0650288f}, -{0.020356f,-0.99678f,0.0775616f},{0.0240698f,-0.997295f,0.069456f},{0.278418f,0.949947f,-0.141718f}, -{0.604354f,0.51753f,0.605738f},{0.185444f,0.978943f,-0.0853253f},{-0.902527f,-0.429563f,0.0303291f}, -{0.180423f,0.975577f,0.125284f},{0.291481f,0.956568f,-0.00407742f},{0.349613f,0.890753f,-0.290396f}, -{0.356001f,0.892578f,-0.27671f},{-0.772158f,0.517877f,0.368206f},{-0.833386f,-0.170946f,0.525591f}, -{-0.467427f,0.879952f,-0.0848387f},{0.745154f,0.610309f,-0.268828f},{-0.860918f,-0.0426011f,-0.506957f}, -{-0.425821f,0.844817f,-0.323977f},{-0.138972f,0.83396f,0.534038f},{0.641568f,0.757832f,0.118662f}, -{0.82573f,0.549377f,0.127887f},{0.436349f,0.851679f,0.290243f},{0.149454f,0.967097f,0.205881f}, -{0.123489f,0.962764f,0.240489f},{0.0777148f,0.948839f,0.306049f},{0.259818f,0.964051f,0.0556885f}, -{0.671152f,0.613816f,0.415674f},{0.737405f,0.584125f,0.339162f},{0.0787638f,0.972075f,0.221056f}, -{0.0226107f,0.965058f,0.261059f},{-0.318905f,0.888959f,-0.328713f},{-0.387093f,0.91673f,0.0988183f}, -{0.338681f,0.924639f,0.174179f},{-0.0464892f,0.917725f,0.394486f},{0.34519f,0.784268f,0.515527f}, -{0.0981635f,0.845149f,0.525441f},{0.32706f,0.938825f,-0.107887f},{0.334149f,0.940787f,-0.057133f}, -{0.360584f,0.904615f,-0.227268f},{0.370049f,0.916384f,-0.152656f},{0.219202f,0.949682f,-0.223729f}, -{0.0417123f,0.962056f,0.269644f},{0.0445164f,0.999004f,0.0029002f},{0.0311419f,0.99584f,0.0856323f}, -{-0.0105715f,0.904381f,0.426594f},{0.250169f,0.889893f,0.381453f},{-0.479884f,0.734289f,0.480136f}, -{0.0616234f,0.91504f,0.398629f},{-0.873565f,-0.484911f,0.0417901f},{0.193391f,0.97755f,0.0836386f}, -{-0.24517f,-0.897212f,-0.367291f},{-0.738477f,0.556206f,-0.381164f},{0.00208972f,0.995448f,-0.0952887f}, -{0.023146f,0.996478f,-0.0805908f},{-0.200122f,0.828964f,0.522273f},{-0.223522f,0.953553f,0.201925f}, -{0.261343f,0.928881f,-0.262449f},{0.331113f,0.923764f,-0.192418f},{0.573723f,0.819049f,0.000855026f}, -{0.362715f,0.829104f,0.42547f},{-0.0573298f,0.844648f,0.532243f},{-0.0897245f,0.990344f,-0.105682f}, -{0.791768f,0.584989f,0.175761f},{0.788555f,0.524059f,0.321781f},{0.396688f,0.784354f,0.476893f}, -{-0.142441f,0.853827f,0.50069f},{-0.497251f,0.741148f,0.451045f},{-0.52758f,0.754131f,-0.391083f}, -{-0.74581f,0.423423f,-0.514276f},{-0.835334f,0.507605f,-0.211079f},{-0.760969f,0.639595f,0.108834f}, -{0.665519f,0.564337f,0.488476f},{0.184704f,0.965314f,0.184535f},{-0.377948f,0.905644f,-0.19226f}, -{-0.374352f,0.926577f,-0.0362743f},{0.481967f,0.867384f,0.123909f},{0.524646f,0.846426f,-0.0911632f}, -{-0.0773877f,0.88845f,0.452402f},{-0.697527f,-0.161082f,0.698218f},{-0.728014f,-0.219408f,0.649505f}, -{-0.806167f,-0.22881f,0.545657f},{0.859404f,0.505542f,0.0765056f},{0.18694f,0.936136f,0.297831f}, -{0.627902f,0.752182f,-0.199901f},{0.439551f,0.827994f,0.348167f},{-0.106603f,0.891453f,0.440395f}, -{-0.739824f,0.137059f,0.658692f},{-0.696005f,0.314806f,0.645348f},{0.777963f,0.596105f,0.198575f}, -{0.625854f,0.727341f,0.281572f},{0.586037f,0.75803f,0.286273f},{-0.273339f,0.876824f,0.395556f}, -{-0.117479f,0.873974f,0.471559f},{-0.141536f,0.827666f,0.543081f},{0.0223492f,-0.998109f,0.0572571f}, -{-0.000931591f,-0.998825f,0.0484584f},{-0.360291f,0.888095f,0.285442f},{-0.105675f,0.913747f,0.392299f}, -{-0.13649f,0.989925f,0.0376649f},{0.0384947f,0.98542f,0.165729f},{0.647598f,0.705639f,-0.287558f}, -{-0.629182f,0.60845f,-0.483651f},{-0.18035f,0.850769f,0.493626f},{-0.200136f,0.659248f,0.724802f}, -{-0.40814f,0.771931f,0.487385f},{-0.56766f,0.779026f,0.266235f},{-0.50392f,0.782727f,0.365244f}, -{0.059644f,0.998218f,-0.00191144f},{0.493305f,0.788252f,0.367843f},{0.233534f,0.915322f,0.328097f}, -{0.100081f,0.994977f,-0.001886f},{0.0540968f,0.9859f,0.158351f},{0.0288702f,0.997117f,-0.0701744f}, -{-0.579249f,0.78716f,-0.211777f},{0.474653f,0.813336f,0.336437f},{0.549723f,0.788931f,0.274576f}, -{-0.633886f,0.679356f,0.369682f},{-0.0300771f,0.878075f,0.477576f},{0.0456401f,0.99866f,-0.0244092f}, -{0.0727139f,0.973018f,0.21897f},{0.0735803f,0.671302f,-0.737522f},{0.0494458f,0.395419f,-0.917169f}, -{0.00888718f,0.859255f,-0.511471f},{-0.854026f,0.2938f,0.429328f},{0.267715f,0.935591f,0.230212f}, -{-0.0884375f,0.963257f,-0.253602f},{-0.27228f,0.959388f,-0.0737389f},{-0.55698f,0.773671f,-0.302004f}, -{-0.71943f,0.67545f,-0.161826f},{-0.653106f,0.624328f,-0.428564f},{-0.379852f,0.787478f,-0.485377f}, -{-0.0496136f,0.821031f,0.568724f},{-0.556395f,0.828516f,-0.0631306f},{-0.00650614f,0.939219f,0.343256f}, -{-0.0378066f,0.973412f,-0.225918f},{-0.554061f,0.40543f,-0.727078f},{-0.569182f,0.171273f,-0.804175f}, -{-0.709345f,0.399365f,-0.580807f},{0.312263f,0.8806f,-0.356421f},{0.670924f,0.693388f,0.262818f}, -{-0.537859f,0.0528585f,0.841376f},{0.383935f,0.853309f,0.352785f},{-0.437111f,0.89267f,0.109884f}, -{-0.318147f,0.945762f,0.0657079f},{0.817501f,0.574998f,0.0327173f},{0.804964f,0.593238f,-0.0100991f}, -{0.82956f,0.536471f,0.155014f},{0.807816f,0.543091f,0.229097f},{0.767553f,0.561873f,0.308482f}, -{0.734804f,0.509692f,0.447523f},{-0.184696f,0.932773f,-0.309551f},{-0.164745f,0.95509f,-0.246299f}, -{0.272873f,0.875473f,0.398857f},{-0.547438f,0.648874f,0.528464f},{-0.509731f,0.801615f,0.312391f}, -{-0.693304f,0.660768f,0.287602f},{0.830856f,0.553276f,-0.0596929f},{0.817452f,0.574321f,0.04391f}, -{0.82274f,0.56071f,0.0932945f},{0.799149f,0.580408f,0.156486f},{-0.187866f,0.976313f,0.107331f}, -{0.83228f,0.547521f,-0.0867808f},{0.814186f,0.578133f,-0.0535041f},{0.810153f,0.585831f,0.0213339f}, -{0.809173f,0.580772f,0.0891247f},{0.80695f,0.590596f,-0.00528696f},{0.801762f,0.595959f,0.044831f}, -{0.77998f,0.602313f,0.169853f},{0.744047f,0.592678f,0.308427f},{-0.966823f,0.0452095f,0.251413f}, -{-0.956155f,0.198707f,0.215134f},{0.0574333f,0.997765f,-0.0341582f},{0.806921f,0.589501f,-0.0369692f}, -{0.802278f,0.59668f,0.0179628f},{0.766982f,0.633314f,0.10321f},{0.650228f,0.622419f,0.435658f}, -{0.704453f,0.647844f,0.289904f},{0.628921f,0.597557f,0.497378f},{-0.743343f,0.229344f,-0.628365f}, -{-0.69546f,0.312394f,-0.647105f},{-0.660438f,0.266932f,-0.701832f},{0.806766f,0.581782f,-0.103241f}, -{0.80487f,0.593418f,-0.00630056f},{0.769639f,0.637487f,0.035579f},{0.734675f,0.659159f,0.160504f}, -{0.80405f,0.549513f,-0.227025f},{0.803626f,0.593911f,-0.0381408f},{0.769531f,0.638363f,-0.0177426f}, -{0.765295f,0.642865f,0.0323697f},{0.567755f,0.649438f,0.505849f},{0.381409f,-0.893429f,-0.237301f}, -{-0.00752063f,0.229679f,-0.973237f},{0.807026f,0.580752f,-0.10694f},{0.758431f,0.650052f,-0.047062f}, -{0.732079f,0.672795f,0.106805f},{0.704091f,0.702637f,0.102745f},{0.698117f,0.699929f,0.150773f}, -{0.663589f,0.679392f,0.313173f},{0.574373f,0.681896f,0.452896f},{-0.803084f,0.330145f,-0.496045f}, -{0.798028f,0.532226f,-0.282643f},{0.766386f,0.563802f,-0.307862f},{0.80442f,0.55547f,-0.210621f}, -{0.76591f,0.631894f,-0.11871f},{0.715646f,0.697201f,0.0419614f},{0.603596f,0.722225f,0.337732f}, -{0.520807f,0.654939f,0.547553f},{0.229172f,0.800804f,0.553347f},{0.0812578f,0.986317f,-0.143444f}, -{-0.9886f,0.111022f,-0.101703f},{0.741263f,0.671205f,0.00366621f},{0.704182f,0.709381f,0.0301024f}, -{0.639249f,0.747109f,0.182178f},{0.586867f,0.654313f,0.47693f},{-0.604248f,0.685136f,0.406783f}, -{0.749005f,0.617153f,-0.241066f},{0.692909f,0.716962f,-0.0764364f},{0.694198f,0.719117f,-0.0309967f}, -{0.667752f,0.740189f,0.0789108f},{0.709131f,0.696508f,-0.109592f},{0.666081f,0.734678f,-0.128782f}, -{0.67466f,0.736436f,0.0499464f},{0.637302f,0.765583f,0.0879164f},{0.605727f,0.778945f,0.162297f}, -{0.584384f,0.769989f,0.25615f},{0.569427f,0.738575f,0.360916f},{0.701706f,0.63332f,-0.326365f}, -{0.672913f,0.705054f,-0.2238f},{0.646507f,0.761239f,0.0504462f},{0.540585f,0.730735f,0.416886f}, -{0.554591f,0.652851f,0.515959f},{-0.45615f,0.77338f,-0.440239f},{-0.861977f,-0.205293f,0.463519f}, -{-0.851613f,0.0465911f,0.522096f},{-0.884595f,-0.0397011f,0.464667f},{-0.265588f,0.792521f,0.548975f}, -{0.626767f,0.74215f,-0.237436f},{0.650336f,0.759537f,0.0128991f},{0.607046f,0.766684f,0.209022f}, -{0.42584f,0.81007f,-0.403047f},{0.479766f,0.78941f,-0.382957f},{0.440625f,0.776807f,-0.449912f}, -{-0.965918f,-0.188346f,-0.177559f},{-0.917033f,-0.300542f,-0.262156f},{0.655378f,0.670166f,-0.348365f}, -{0.655846f,0.747655f,-0.104302f},{0.638829f,0.768028f,-0.0450667f},{0.620536f,0.777113f,0.105025f}, -{0.419917f,0.795884f,0.436164f},{-0.484423f,0.76896f,-0.417174f},{0.283428f,-0.883937f,-0.371919f}, -{-0.877507f,-0.432781f,-0.206596f},{-0.0647534f,0.116705f,-0.991053f},{-0.00251298f,-0.196393f,-0.980522f}, -{-0.120996f,-0.17257f,-0.977537f},{0.625618f,0.75948f,-0.178302f},{0.622883f,0.775196f,-0.105295f}, -{0.618509f,0.784484f,0.045081f},{0.555738f,0.808838f,0.192188f},{0.540989f,0.792397f,0.281847f}, -{0.494361f,0.783838f,0.375773f},{-0.472517f,-0.398134f,0.786268f},{-0.503204f,-0.412649f,0.75928f}, -{-0.0691448f,0.651623f,-0.755385f},{-0.0855156f,0.570482f,-0.816846f},{-0.886059f,-0.355448f,-0.297583f}, -{0.594546f,0.762545f,-0.255029f},{0.622101f,0.721187f,-0.304761f},{0.605176f,0.774254f,-0.185182f}, -{0.610974f,0.791608f,0.00823471f},{0.573345f,0.817999f,0.0464058f},{0.560795f,0.821271f,0.10499f}, -{0.414525f,0.848734f,0.328358f},{0.736676f,0.649956f,0.186727f},{0.0552009f,0.917445f,0.394014f}, -{0.58116f,0.774985f,-0.248299f},{0.603961f,0.79543f,-0.0502213f},{0.566922f,0.823735f,0.00775409f}, -{0.433232f,0.874383f,0.218552f},{0.338907f,0.844676f,0.414324f},{0.335796f,0.825149f,0.45428f}, -{0.335634f,0.727757f,0.598097f},{0.592382f,0.796432f,-0.121568f},{0.564733f,0.823747f,-0.0501802f}, -{0.471839f,0.874441f,0.112784f},{0.391375f,0.8556f,0.338782f},{0.272028f,0.795259f,0.541815f}, -{-0.321054f,-0.946891f,-0.017938f},{-0.583011f,0.804225f,-0.115411f},{-0.0186705f,0.88413f,0.466868f}, -{0.58237f,0.771302f,-0.256787f},{0.583304f,0.788563f,-0.194745f},{0.56576f,0.815156f,-0.124239f}, -{0.508372f,0.860502f,0.0330708f},{0.427096f,0.759848f,0.490122f},{0.0181058f,0.259257f,-0.965639f}, -{-0.0500199f,0.232695f,-0.971262f},{0.0170665f,0.950699f,0.309644f},{-0.0152952f,0.950205f,0.311251f}, -{-0.881404f,0.0726797f,-0.466739f},{-0.877608f,0.0389104f,-0.477797f},{0.30436f,0.386033f,-0.870829f}, -{0.165782f,0.556552f,-0.814105f},{0.346919f,0.302604f,-0.887737f},{0.513981f,0.856343f,-0.0500122f}, -{0.511201f,0.859461f,-0.000758396f},{0.361243f,0.903013f,0.23253f},{0.452609f,0.687968f,0.567314f}, -{0.407572f,0.674302f,0.615794f},{0.57013f,0.802797f,-0.174555f},{0.51097f,0.852913f,-0.107003f}, -{0.453526f,0.888135f,0.0743731f},{0.413537f,0.896599f,0.158418f},{0.389912f,0.886016f,0.250887f}, -{0.445681f,0.789273f,0.422395f},{0.309789f,0.731113f,0.607869f},{-0.0846571f,0.900316f,0.426924f}, -{0.545197f,0.794322f,-0.267979f},{0.547967f,0.836466f,-0.00756271f},{0.470132f,0.882429f,0.0171804f}, -{0.431296f,0.900927f,0.0481153f},{0.416849f,0.879298f,0.230372f},{0.440959f,0.838019f,0.321371f}, -{0.407999f,0.737634f,0.537989f},{-0.886332f,0.390751f,0.248453f},{-0.833616f,0.516196f,0.196536f}, -{0.471464f,0.881296f,-0.0322266f},{0.438674f,0.898631f,0.00523751f},{0.406783f,0.824159f,0.394068f}, -{0.2856f,0.832072f,0.475488f},{0.457025f,0.885608f,-0.0826232f},{0.446409f,0.893715f,-0.0446337f}, -{0.402155f,0.90619f,0.130735f},{0.173173f,0.782164f,0.598523f},{-0.13066f,-0.990258f,0.0481328f}, -{0.516393f,0.806743f,0.287235f},{-0.430925f,0.715581f,0.54977f},{0.448735f,0.327623f,-0.831445f}, -{0.493816f,0.1669f,-0.853399f},{-0.512482f,0.435375f,0.740143f},{-0.501902f,0.485164f,0.716038f}, -{0.00864245f,0.296578f,-0.95497f},{-0.638979f,0.481665f,-0.599754f},{-0.619463f,0.418906f,-0.663915f}, -{-0.676628f,-0.0650151f,0.733449f},{-0.653662f,-0.082975f,0.752224f},{-0.807571f,0.313101f,0.499796f}, -{-0.757473f,0.403682f,0.513103f},{-0.852017f,0.200593f,0.483559f},{-0.864641f,0.0993041f,0.492479f}, -{-0.819801f,0.214178f,0.531088f},{-0.890242f,0.0583071f,0.451741f},{-0.685458f,0.430081f,0.587519f}, -{-0.512972f,0.206372f,0.833229f},{-0.788466f,-0.229526f,-0.570648f},{0.0241731f,-0.998842f,-0.0416034f}, -{0.0326615f,-0.998887f,-0.0340157f},{0.0295508f,-0.999062f,-0.03164f},{-0.467001f,0.782354f,0.412107f}, -{-0.292685f,0.821794f,0.488866f},{-0.412556f,0.764212f,0.495759f},{-0.610348f,0.435518f,0.661664f}, -{-0.811272f,-0.319379f,0.48973f},{-0.81612f,-0.309379f,0.48809f},{-0.70417f,0.414397f,0.576559f}, -{-0.687124f,0.513775f,0.513707f},{-0.47835f,0.641065f,0.600181f},{-0.421403f,0.744624f,0.517644f}, -{-0.741206f,0.448145f,0.499779f},{-0.814976f,0.396613f,0.422508f},{-0.812736f,-0.386413f,-0.436056f}, -{-0.104997f,0.47965f,-0.871155f},{-0.0162628f,0.460022f,-0.887759f},{-0.765465f,0.240563f,0.596819f}, -{-0.597611f,0.57362f,0.560198f},{-0.689789f,0.505065f,0.518749f},{-0.727175f,0.423293f,0.540407f}, -{-0.308127f,0.849674f,0.427916f},{-0.0791706f,0.609536f,0.788796f},{-0.823089f,-0.0371484f,0.566695f}, -{-0.82769f,-0.0336199f,0.560178f},{-0.815599f,-0.0632867f,0.575146f},{-0.127929f,-0.000264798f,-0.991783f}, -{-0.84084f,0.290014f,0.457034f},{-0.859387f,0.296015f,0.416928f},{-0.89802f,-0.0693067f,0.434461f}, -{-0.825519f,-0.0620058f,0.560957f},{-0.818802f,-0.152933f,0.55333f},{-0.80371f,-0.147357f,0.576486f}, -{-0.579463f,-0.023075f,-0.814672f},{-0.673952f,0.194501f,-0.712711f},{-0.588945f,0.0471562f,-0.806796f}, -{-0.425986f,-0.0397759f,-0.903855f},{-0.613002f,0.594276f,0.520638f},{-0.509363f,0.698473f,0.502678f}, -{-0.636005f,0.594838f,0.491595f},{-0.498753f,-0.295648f,0.814762f},{-0.780513f,-0.394895f,0.484621f}, -{-0.726784f,-0.181705f,0.662396f},{-0.654261f,-0.705981f,0.27117f},{-0.454155f,-0.890405f,-0.0303461f}, -{-0.78205f,-0.621982f,-0.0391931f},{-0.813216f,-0.3632f,0.454716f},{-0.846288f,-0.351263f,0.400513f}, -{-0.833476f,-0.307216f,0.459277f},{-0.851383f,-0.494771f,0.174211f},{-0.924493f,-0.309175f,-0.222986f}, -{-0.424515f,0.751553f,0.50493f},{-0.801391f,-0.0292747f,0.597424f},{-0.823191f,-0.323435f,0.466634f}, -{-0.81734f,-0.314257f,0.482906f},{-0.827421f,-0.291732f,0.479861f},{-0.536161f,0.21014f,0.817541f}, -{-0.479851f,0.395272f,0.783264f},{-0.585877f,0.38605f,0.712541f},{-0.818216f,-0.122578f,0.561691f}, -{-0.827807f,-0.206853f,0.521486f},{-0.405365f,0.515397f,0.755014f},{-0.679631f,0.446817f,0.58177f}, -{-0.544681f,0.503073f,0.671f},{-0.748484f,-0.190258f,0.635274f},{-0.582454f,0.295646f,-0.757193f}, -{-0.238605f,-0.733799f,-0.636087f},{-0.211375f,-0.785263f,-0.581965f},{-0.248305f,-0.659941f,-0.7091f}, -{-0.671462f,-0.241978f,0.700418f},{-0.702697f,0.40569f,0.584494f},{-0.424814f,-0.45386f,0.78329f}, -{0.50287f,0.771715f,-0.389331f},{-0.681696f,-0.191082f,0.706242f},{-0.716836f,-0.265053f,0.644897f}, -{-0.770713f,-0.297385f,0.563528f},{-0.747238f,-0.118241f,0.653953f},{-0.954785f,-0.184225f,0.23334f}, -{-0.548361f,-0.727836f,0.411769f},{-0.921405f,-0.371992f,0.112408f},{-0.503367f,-0.393242f,0.769404f}, -{-0.576726f,-0.342498f,0.741675f},{-0.361183f,0.225455f,-0.90483f},{-0.891653f,0.404043f,0.204217f}, -{0.0338141f,-0.928257f,0.370398f},{-0.0219507f,-0.943046f,0.331938f},{-0.0745498f,-0.684872f,0.72484f}, -{-0.806646f,-0.13619f,0.57513f},{-0.808811f,-0.404271f,0.427071f},{-0.44685f,0.609374f,0.654972f}, -{-0.461291f,0.570642f,0.679396f},{-0.170721f,-0.981654f,0.0849051f},{0.548492f,0.127866f,-0.826321f}, -{0.481372f,0.192323f,-0.855157f},{0.542861f,0.164021f,-0.82365f},{-0.29651f,0.199589f,-0.933941f}, -{0.227659f,0.120444f,-0.966263f},{-0.59647f,0.306507f,-0.741807f},{0.0284581f,0.296894f,-0.954486f}, -{0.0122738f,0.499735f,-0.866092f},{-0.829669f,-0.29228f,0.475629f},{-0.485308f,-0.240124f,0.840724f}, -{-0.438823f,-0.341511f,0.831146f},{-0.605016f,0.775383f,0.180933f},{-0.712909f,0.673776f,-0.194388f}, -{-0.433914f,-0.114761f,-0.893615f},{-0.513091f,-0.0345913f,-0.857637f},{-0.521915f,0.0848665f,-0.848765f}, -{-0.655513f,0.204912f,0.726852f},{-0.425762f,-0.24177f,-0.871937f},{-0.814908f,-0.301909f,0.494749f}, -{-0.532045f,-0.147684f,0.833737f},{-0.896942f,-0.179224f,0.404195f},{-0.560803f,0.260994f,0.785737f}, -{-0.676909f,-0.32187f,0.661962f},{-0.804451f,-0.351504f,0.478856f},{-0.826835f,-0.314315f,0.466423f}, -{0.24554f,-0.848717f,0.468391f},{0.252259f,-0.880717f,0.400878f},{0.261369f,-0.906495f,0.331593f}, -{-0.871316f,0.402879f,0.280174f},{-0.947823f,0.241453f,0.208163f},{-0.736231f,0.393781f,0.550364f}, -{-0.897723f,0.376006f,0.22959f},{-0.499015f,-0.219682f,0.838286f},{-0.685411f,0.694441f,-0.219006f}, -{-0.496778f,0.833956f,0.240268f},{-0.679974f,-0.471155f,0.561826f},{-0.587924f,-0.513635f,0.62492f}, -{-0.626815f,-0.437178f,0.644963f},{-0.814539f,-0.302632f,0.494915f},{0.0151252f,-0.997929f,0.0625192f}, -{-0.368475f,0.238131f,-0.898621f},{-0.356529f,0.312421f,-0.8805f},{-0.457637f,-0.114404f,0.881748f}, -{0.029499f,-0.999511f,-0.0103668f},{-0.00938924f,-0.999942f,0.00521503f},{-0.00868702f,-0.99974f,-0.0210859f}, -{-0.883607f,-0.125016f,0.451231f},{-0.897507f,0.0204293f,0.440526f},{-0.667722f,0.554902f,0.496217f}, -{-0.763542f,0.385739f,0.51789f},{-0.510416f,-0.322454f,0.797182f},{-0.464866f,-0.34522f,0.815305f}, -{-0.158149f,0.34114f,-0.926613f},{-0.627815f,0.613943f,0.478458f},{-0.44089f,0.789623f,0.426745f}, -{-0.828792f,-0.311666f,0.464724f},{-0.554504f,0.725721f,0.407252f},{-0.662594f,-0.0304637f,0.748359f}, -{-0.435102f,-0.462729f,0.772378f},{-0.666116f,0.499234f,0.554125f},{-0.732412f,0.118538f,0.670464f}, -{-0.869508f,-0.48865f,0.0719463f},{-0.311367f,-0.328529f,-0.891694f},{-0.971581f,-0.23594f,0.0190483f}, -{-0.863437f,-0.282645f,0.417838f},{-0.302687f,-0.925788f,-0.226489f},{-0.488993f,-0.817903f,-0.303186f}, -{-0.80689f,-0.288878f,0.515246f},{-0.551655f,0.717482f,0.425319f},{-0.432414f,-0.240387f,0.869041f}, -{-0.897626f,0.378674f,0.225551f},{-0.576051f,0.646691f,0.499956f},{-0.526094f,0.728597f,0.438601f}, -{-0.77966f,0.166922f,0.603545f},{-0.787693f,0.266878f,0.555261f},{-0.601311f,-0.0491254f,-0.797504f}, -{-0.785811f,0.00908449f,-0.6184f},{-0.769137f,0.0463938f,-0.637398f},{-0.445453f,-0.202091f,-0.872199f}, -{-0.654855f,-0.272176f,-0.705043f},{-0.544025f,-0.184676f,-0.818493f},{0.030312f,0.401667f,-0.915284f}, -{-0.041871f,0.443354f,-0.895368f},{-0.0727087f,0.385587f,-0.919802f},{-0.511658f,0.7034f,0.493391f}, -{-0.805663f,0.152777f,0.572334f},{-0.751126f,-0.47213f,0.461414f},{-0.798257f,-0.322169f,0.508913f}, -{-0.576619f,0.0724766f,0.813792f},{-0.520617f,0.718185f,0.461702f},{-0.33865f,0.562898f,0.753964f}, -{0.234955f,0.238834f,-0.942207f},{0.251331f,0.314931f,-0.915233f},{0.185063f,0.330729f,-0.925403f}, -{-0.886911f,0.375469f,0.269096f},{-0.154109f,0.250557f,-0.955757f},{-0.400531f,0.813668f,0.421331f}, -{-0.5416f,-0.770518f,0.336113f},{-0.544135f,0.16461f,-0.822691f},{-0.211698f,0.175625f,-0.961426f}, -{-0.334504f,-0.0129159f,-0.942306f},{-0.394133f,0.000893468f,-0.919053f},{-0.391245f,0.0852153f,-0.916333f}, -{-0.648911f,0.15809f,-0.74426f},{-0.812766f,-0.31308f,0.491318f},{-0.692476f,-0.419684f,0.586807f}, -{-0.81264f,-0.0624337f,0.579412f},{-0.633244f,-0.291183f,0.717088f},{-0.952223f,0.27226f,0.138367f}, -{-0.452731f,0.502721f,0.736415f},{-0.719407f,-0.663803f,-0.2045f},{-0.613654f,-0.789512f,-0.00995104f}, -{-0.737211f,-0.205266f,0.643728f},{-0.575835f,0.262539f,-0.774266f},{-0.543006f,0.155016f,-0.825297f}, -{-0.769728f,0.282775f,0.572326f},{-0.794855f,0.0192248f,0.606495f},{-0.466456f,-0.18462f,0.865063f}, -{-0.502563f,-0.290176f,0.814388f},{-0.581134f,-0.13327f,0.802821f},{-0.339314f,-0.250814f,0.906619f}, -{-0.831761f,-0.0775132f,0.549696f},{0.0198175f,-0.999708f,-0.0138038f},{0.00494828f,-0.999849f,-0.0166457f}, -{0.0221211f,-0.999637f,-0.0153761f},{-0.35896f,0.0528321f,-0.931857f},{-0.394276f,0.80845f,0.436984f}, -{-0.633605f,0.20823f,0.745108f},{-0.594276f,0.417433f,0.687449f},{-0.546438f,0.464911f,0.696608f}, -{-0.595217f,0.476599f,0.64697f},{-0.362789f,-0.186007f,-0.913119f},{-0.420958f,0.66549f,0.616375f}, -{-0.543996f,0.546085f,0.637071f},{-0.325797f,-0.26756f,0.90679f},{-0.183163f,-0.194712f,0.963607f}, -{-0.445429f,0.0859319f,-0.891184f},{-0.545556f,0.105608f,-0.831394f},{-0.610353f,0.539882f,-0.579653f}, -{-0.545833f,-0.242049f,0.802171f},{-0.759919f,-0.00155307f,0.650016f},{-0.752876f,-0.0855841f,0.652574f}, -{-0.534606f,-0.263421f,0.802998f},{-0.552957f,-0.182488f,0.81298f},{-0.635767f,0.182895f,0.7499f}, -{-0.559979f,0.292242f,0.775253f},{-0.598309f,0.249397f,0.761464f},{-0.638105f,0.200247f,0.743453f}, -{-0.66314f,0.0604394f,0.746051f},{-0.510999f,0.0128487f,0.859485f},{-0.452527f,-0.107616f,0.885233f}, -{-0.446189f,-0.437712f,0.780592f},{-0.818986f,0.569003f,0.0741459f},{-0.256962f,-0.883326f,0.392053f}, -{0.160324f,0.287601f,-0.944236f},{-0.98996f,-0.125316f,0.0653766f},{-0.984502f,-0.118441f,-0.129335f}, -{0.0653595f,-0.997831f,0.00782317f},{-0.17971f,-0.965573f,0.188077f},{0.00449062f,-0.995378f,0.0959267f}, -{-0.50379f,-0.181022f,0.844646f},{0.00183938f,-0.468409f,-0.88351f},{-0.450589f,0.612839f,0.649152f}, -{-0.638726f,0.29257f,0.71164f},{-0.572547f,0.229584f,0.787071f},{-0.545939f,0.374596f,0.749419f}, -{-0.346339f,0.933504f,-0.092841f},{-0.532212f,0.180167f,0.827218f},{-0.657665f,-0.128369f,0.742293f}, -{-0.557517f,-0.316215f,0.767582f},{-0.537161f,0.59042f,0.60238f},{-0.433749f,0.634675f,0.63957f}, -{-0.907702f,-0.239757f,-0.344374f},{-0.815025f,-0.244928f,-0.525113f},{-0.508138f,-0.465009f,0.724957f}, -{-0.597399f,-0.419279f,0.683608f},{-0.157794f,0.0739844f,-0.984697f},{-0.215131f,0.345583f,-0.913395f}, -{-0.641043f,-0.381159f,0.666169f},{-0.463103f,-0.204799f,-0.862318f},{-0.420183f,-0.257033f,-0.870276f}, -{-0.426429f,-0.833762f,0.350713f},{-0.288873f,-0.855688f,0.42936f},{-0.262027f,-0.885132f,0.384556f}, -{-0.505665f,-0.329907f,-0.79716f},{-0.423011f,-0.843442f,0.33116f},{-0.188296f,-0.95367f,0.234643f}, -{-0.186668f,-0.96361f,0.191339f},{0.016626f,-0.42944f,-0.902942f},{0.229574f,-0.618164f,-0.751778f}, -{0.213167f,-0.630559f,-0.746294f},{-0.653506f,0.170378f,0.737497f},{-0.754205f,0.139048f,0.641748f}, -{-0.733566f,-0.113507f,-0.670073f},{-0.714556f,0.0437677f,-0.698208f},{-0.534084f,0.6353f,0.557806f}, -{-0.477724f,0.66335f,0.575975f},{-0.564231f,-0.278572f,0.777201f},{-0.0153342f,-0.98466f,0.173808f}, -{-0.00899519f,-0.990321f,0.138506f},{-0.747073f,0.149048f,0.647816f},{-0.739693f,0.330268f,0.586325f}, -{-0.75432f,0.0203637f,0.656191f},{-0.584713f,-0.0127331f,0.81114f},{-0.627251f,0.0874965f,0.773886f}, -{-0.375288f,0.744741f,0.551832f},{-0.85599f,0.509751f,-0.086229f},{-0.856581f,0.450396f,-0.251818f}, -{-0.964796f,0.114569f,-0.236733f},{-0.788723f,0.613207f,-0.0435051f},{-0.705179f,0.661605f,-0.254952f}, -{-0.622839f,-0.125899f,0.772154f},{-0.643766f,0.0804802f,0.760979f},{-0.561995f,-0.357569f,0.745859f}, -{-0.650896f,-0.230573f,0.723305f},{-0.647344f,0.0159482f,0.762031f},{-0.719224f,0.0847135f,0.689595f}, -{-0.66153f,-0.118747f,0.740458f},{-0.201788f,0.273426f,-0.940489f},{-0.265744f,0.281923f,-0.9219f}, -{-0.222384f,0.236431f,-0.945857f},{0.373598f,0.281689f,-0.883785f},{0.400531f,0.272846f,-0.874717f}, -{0.437973f,0.267974f,-0.85812f},{-0.615037f,-0.456725f,0.642753f},{-0.826765f,0.552627f,-0.10518f}, -{-0.613802f,-0.0665211f,0.786653f},{-0.794276f,-0.309824f,-0.522623f},{-0.558536f,-0.666482f,-0.493801f}, -{-0.663791f,-0.693794f,-0.27934f},{-0.614065f,-0.405564f,0.677083f},{-0.675169f,0.476757f,0.562895f}, -{-0.589291f,0.541487f,0.599607f},{-0.388849f,-0.419335f,0.820338f},{-0.637315f,-0.0303753f,0.770004f}, -{-0.0304552f,0.316204f,-0.948202f},{-0.26997f,0.334033f,-0.903071f},{-0.214728f,0.536029f,-0.816434f}, -{-0.415018f,0.564237f,-0.71372f},{-0.453154f,0.844034f,-0.286808f},{0.153137f,0.210379f,-0.965552f}, -{0.249746f,0.276643f,-0.927952f},{-0.836283f,0.441701f,0.324856f},{-0.694397f,-0.327434f,-0.640781f}, -{-0.634576f,-0.386536f,-0.669255f},{-0.722049f,-0.311531f,-0.617732f},{-0.701986f,0.404823f,0.585947f}, -{-0.769988f,0.330764f,0.545631f},{-0.81952f,0.310075f,0.481913f},{-0.793846f,0.44448f,0.415026f}, -{0.348806f,-0.234002f,-0.907512f},{-0.556915f,0.764365f,0.32495f},{-0.702632f,-0.354421f,-0.617004f}, -{-0.591409f,-0.175052f,-0.787142f},{-0.603679f,-0.341051f,-0.720595f},{-0.829158f,0.205157f,0.520006f}, -{-0.0234405f,-0.0669621f,-0.99748f},{0.0827914f,-0.212663f,-0.973612f},{-0.613392f,-0.382432f,-0.69101f}, -{-0.669679f,-0.44593f,-0.593865f},{-0.587962f,-0.438583f,-0.679666f},{-0.514604f,-0.418881f,-0.748145f}, -{-0.683355f,0.203558f,-0.701135f},{-0.343854f,-0.869667f,0.35418f},{-0.693639f,0.102823f,0.712946f}, -{-0.643381f,0.507362f,0.573276f},{-0.179229f,-0.798935f,0.57409f},{-0.625846f,-0.345565f,-0.699215f}, -{0.193112f,-0.566099f,0.801399f},{0.363743f,-0.582821f,0.726644f},{-0.590179f,-0.699363f,0.403211f}, -{-0.782717f,-0.13318f,0.607962f},{-0.750313f,0.182009f,0.635534f},{-0.685217f,0.425887f,0.590845f}, -{-0.765752f,-0.0469561f,0.641419f},{-0.55663f,-0.659276f,0.505489f},{-0.430096f,0.189334f,-0.882706f}, -{-0.868659f,-0.194001f,0.455845f},{-0.948159f,0.316867f,-0.0242684f},{-0.923184f,0.288381f,0.2541f}, -{-0.864769f,-0.393138f,0.312437f},{-0.673731f,0.121125f,0.728982f},{-0.447896f,-0.0639182f,-0.891798f}, -{-0.732419f,0.332546f,0.594117f},{-0.697571f,0.617732f,-0.363044f},{-0.232532f,-0.885746f,0.401724f}, -{-0.549515f,-0.43611f,0.71263f},{-0.915083f,0.184311f,0.358682f},{-0.631895f,0.505717f,0.587332f}, -{-0.048755f,-0.912305f,0.406599f},{-0.00499332f,-0.708072f,0.706123f},{-0.740317f,0.109004f,0.663362f}, -{-0.748566f,-0.168053f,0.64141f},{-0.819601f,-0.148101f,0.553462f},{-0.825063f,-0.0489577f,0.562915f}, -{-0.885336f,-0.0479825f,0.462469f},{-0.826903f,0.073578f,0.55751f},{-0.88385f,0.0608632f,0.463794f}, -{-0.838473f,-0.318529f,0.442156f},{-0.647852f,0.483823f,0.58839f},{-0.67151f,-0.362356f,0.646353f}, -{-0.887521f,-0.297247f,0.352066f},{-0.858956f,-0.223478f,0.460709f},{-0.811293f,0.176758f,0.557279f}, -{-0.809109f,0.259872f,0.527075f},{-0.513998f,0.694608f,0.503314f},{-0.461626f,-0.819637f,-0.339258f}, -{-0.310498f,-0.762119f,-0.568125f},{-0.323819f,-0.907785f,-0.266585f},{-0.873213f,0.321721f,0.366054f}, -{-0.0693406f,-0.767637f,0.637123f},{-0.0292936f,-0.951498f,0.306258f},{0.202785f,-0.741235f,0.639882f}, -{-0.730227f,0.361624f,0.579652f},{-0.658946f,0.469511f,0.587664f},{-0.809055f,-0.1302f,0.573131f}, -{-0.764863f,0.244937f,0.595811f},{-0.775799f,0.182781f,0.603926f},{0.250327f,0.459893f,-0.851959f}, -{-0.228841f,-0.44984f,0.863294f},{-0.783305f,0.442176f,0.436936f},{0.311199f,-0.345385f,-0.885361f}, -{0.17222f,-0.27239f,-0.946649f},{-0.791398f,-0.0531614f,0.608985f},{-0.786133f,0.0675726f,0.614352f}, -{-0.66844f,-0.297679f,-0.681598f},{-0.580798f,-0.26834f,-0.768549f},{-0.638322f,0.206607f,0.741524f}, -{-0.598039f,0.28478f,0.749166f},{-0.706041f,-0.141691f,0.693851f},{-0.504882f,0.619097f,0.601509f}, -{-0.424321f,0.739317f,0.52284f},{-0.420945f,0.809959f,0.408377f},{-0.470592f,0.407277f,0.782732f}, -{-0.647062f,-0.190766f,-0.738187f},{-0.681777f,-0.143191f,-0.717409f},{-0.772775f,-0.260891f,-0.578581f}, -{-0.473436f,-0.635563f,-0.609851f},{-0.470861f,-0.668675f,-0.575468f},{-0.549722f,-0.641587f,-0.534951f}, -{-0.913123f,-0.21534f,0.346171f},{-0.850555f,-0.110966f,0.514045f},{-0.800178f,-0.0676003f,0.59594f}, -{-0.759561f,-0.0435877f,0.648974f},{-0.75589f,0.0642564f,0.651538f},{-0.744366f,0.180982f,0.642779f}, -{-0.978468f,-0.150371f,0.141381f},{-0.626719f,0.274355f,-0.729351f},{-0.817811f,0.468598f,0.334068f}, -{-0.732834f,0.078916f,0.675815f},{-0.602859f,0.410502f,0.684141f},{-0.423418f,0.689193f,0.587988f}, -{-0.451121f,-0.705396f,-0.546724f},{-0.39819f,-0.683051f,-0.612279f},{-0.449073f,-0.679805f,-0.579826f}, -{-0.970855f,0.186967f,0.149948f},{-0.133586f,-0.655213f,0.743539f},{-0.746645f,-0.422237f,0.51404f}, -{-0.824048f,-0.311098f,0.473459f},{-0.833598f,-0.199787f,0.514975f},{-0.718462f,0.256136f,0.646689f}, -{-0.843945f,-0.00335243f,0.53642f},{-0.695026f,0.203394f,0.689615f},{-0.572752f,0.593211f,0.565735f}, -{-0.802339f,0.45988f,0.380477f},{-0.885006f,0.353562f,0.302916f},{-0.66682f,0.60401f,0.43649f}, -{-0.5106f,0.593405f,0.622221f},{-0.396866f,0.798109f,0.453344f},{-0.438631f,-0.817212f,0.373856f}, -{0.628597f,0.070023f,-0.774572f},{0.567272f,0.0837215f,-0.819264f},{0.539457f,0.0680128f,-0.839262f}, -{-0.339831f,-0.126378f,-0.931957f},{-0.0729324f,-0.0291307f,-0.996911f},{0.44108f,0.201069f,-0.874654f}, -{-0.804002f,-0.209477f,0.556508f},{-0.69699f,0.239007f,0.676077f},{-0.682645f,0.263004f,0.68178f}, -{-0.432575f,-0.75762f,0.488766f},{-0.468404f,0.0411905f,0.882554f},{-0.64841f,0.269969f,0.711815f}, -{-0.550359f,0.491757f,0.674744f},{-0.378679f,0.762194f,0.525036f},{-0.348953f,0.788101f,0.507079f}, -{-0.582953f,0.580175f,0.568825f},{-0.732857f,0.474231f,-0.487879f},{-0.553702f,0.702597f,-0.446959f}, -{0.373769f,0.188421f,-0.908182f},{-0.813838f,-0.0701697f,0.576839f},{-0.788545f,-0.0220965f,0.614579f}, -{-0.759679f,-0.0248215f,0.649824f},{-0.707009f,0.225235f,0.670379f},{-0.738227f,0.138017f,0.660282f}, -{0.0382768f,-0.996456f,0.0749003f},{-0.766233f,0.258764f,0.588157f},{-0.686959f,0.567356f,0.454087f}, -{-0.73823f,-0.0898627f,0.668536f},{-0.372659f,0.774285f,0.511477f},{-0.723401f,0.205919f,0.659006f}, -{0.160182f,-0.5659f,-0.808764f},{0.434856f,-0.616598f,-0.656283f},{0.0231616f,-0.97957f,-0.199767f}, -{-0.66567f,-0.564697f,0.487853f},{0.316206f,0.241841f,-0.917348f},{0.375475f,0.220209f,-0.900292f}, -{-0.7103f,-0.384795f,0.589412f},{-0.764348f,-0.17699f,0.620037f},{-0.802285f,-0.0445413f,0.595277f}, -{-0.77726f,0.0111166f,0.629081f},{-0.732092f,0.0259054f,0.680713f},{-0.688352f,0.102232f,0.718136f}, -{-0.680752f,0.226134f,0.696735f},{-0.884425f,0.348781f,0.310071f},{-0.861093f,0.272212f,0.429441f}, -{-0.343287f,-0.894841f,0.285332f},{-0.674998f,-0.708227f,0.206865f},{-0.800865f,0.372938f,0.468542f}, -{-0.503765f,0.611881f,0.609772f},{-0.392682f,0.749152f,0.533453f},{-0.650023f,0.325047f,0.686888f}, -{-0.291221f,-0.315675f,-0.903072f},{-0.30522f,-0.105777f,-0.946389f},{-0.253134f,-0.266368f,-0.930039f}, -{-0.673406f,0.265473f,0.689963f},{-0.827281f,0.337787f,0.448896f},{-0.775433f,-0.55732f,0.296814f}, -{-0.749854f,-0.461074f,0.474477f},{-0.803676f,0.408894f,0.432332f},{-0.681633f,0.527799f,0.506759f}, -{0.607975f,-0.172449f,-0.775002f},{0.434956f,-0.900438f,-0.00486778f},{-0.580756f,0.519123f,0.627084f}, -{0.397173f,0.329672f,-0.856487f},{-0.360323f,-0.650174f,-0.668911f},{0.0343635f,0.0931413f,-0.99506f}, -{-0.631268f,-0.222999f,-0.742813f},{-0.746227f,-0.214046f,-0.630341f},{-0.671459f,-0.189288f,-0.716459f}, -{-0.659546f,-0.0381326f,-0.750696f},{-0.815974f,0.00295256f,0.578082f},{-0.888849f,0.0688656f,0.452996f}, -{-0.803058f,0.243254f,0.54399f},{-0.833593f,0.316012f,0.453055f},{-0.579459f,0.631209f,0.51556f}, -{-0.530057f,0.818813f,0.220418f},{0.229917f,-0.548714f,0.803773f},{0.189054f,-0.33771f,0.922069f}, -{0.629261f,0.0979529f,-0.770996f},{-0.676455f,-0.307285f,0.669316f},{-0.624391f,-0.452539f,0.636666f}, -{-0.751741f,-0.0822216f,0.654312f},{-0.659358f,0.21247f,0.721183f},{-0.466465f,-0.872416f,-0.145944f}, -{-0.297602f,-0.903282f,-0.309055f},{-0.00581529f,-0.986921f,-0.161101f},{-0.850233f,0.271603f,0.450927f}, -{-0.645747f,0.76093f,0.0632147f},{-0.742278f,-0.558166f,0.370775f},{-0.752377f,-0.470443f,0.461099f}, -{-0.817796f,0.574776f,0.0290093f},{-0.383178f,0.76701f,0.514655f},{-0.630893f,0.362954f,0.685739f}, -{0.0332487f,-0.998789f,-0.0362681f},{0.0243115f,-0.99914f,-0.0335984f},{0.279421f,-0.915639f,0.289013f}, -{-0.472456f,-0.157848f,-0.867104f},{-0.82926f,0.269673f,0.489493f},{-0.668103f,-0.469037f,0.577618f}, -{-0.622098f,0.656668f,-0.426358f},{-0.902728f,0.430072f,0.0109956f},{-0.0496615f,0.304177f,-0.95132f}, -{-0.883749f,-0.145445f,-0.444784f},{-0.265136f,0.243864f,-0.932863f},{-0.610512f,-0.789531f,-0.0625791f}, -{-0.190525f,0.190871f,-0.962948f},{-0.153502f,0.660391f,-0.735065f},{0.118652f,0.907375f,-0.403227f}, -{0.154264f,0.8649f,-0.477652f},{-0.0161343f,0.881f,-0.472842f},{0.235805f,0.915596f,-0.325698f}, -{0.217685f,0.886898f,-0.407463f},{-0.729908f,0.583186f,-0.356552f},{0.116651f,0.282444f,-0.952165f}, -{0.215742f,0.267895f,-0.938982f},{0.0109213f,0.907924f,-0.418992f},{0.162564f,0.653304f,-0.739437f}, -{0.0479323f,0.611091f,-0.790108f},{0.766837f,0.53508f,-0.354471f},{-0.653129f,-0.103326f,-0.750164f}, -{-0.867818f,-0.0822351f,-0.49003f},{-0.821129f,-0.155542f,-0.549139f},{0.51254f,0.652523f,-0.558137f}, -{0.589058f,0.59275f,-0.549234f},{0.219218f,0.840223f,-0.495953f},{0.278717f,0.785049f,-0.553185f}, -{0.197953f,0.797583f,-0.569804f},{-0.830511f,-0.209543f,-0.516084f},{-0.785971f,-0.188003f,-0.588986f}, -{-0.330683f,0.346221f,-0.87794f},{-0.324171f,0.619309f,-0.715101f},{-0.000413993f,-0.998852f,0.0479042f}, -{0.0241025f,0.940366f,-0.33931f},{-0.687919f,-0.155575f,-0.708917f},{-0.608044f,0.279841f,-0.742948f}, -{-0.869399f,-0.148854f,-0.471156f},{0.15548f,0.819973f,-0.550881f},{0.0859084f,0.827993f,-0.554118f}, -{-0.927365f,0.0475724f,-0.371121f},{-0.802364f,-0.227847f,-0.551632f},{-0.833075f,-0.210273f,-0.511635f}, -{-0.915617f,-0.0470975f,-0.399283f},{-0.876015f,-0.166462f,-0.452646f},{-0.782091f,-0.310435f,-0.540337f}, -{-0.69515f,-0.343201f,-0.631648f},{-0.625069f,-0.446537f,-0.640229f},{-0.625458f,-0.0937403f,-0.774606f}, -{-0.957594f,0.0112719f,-0.2879f},{-0.634462f,-0.376214f,-0.675219f},{-0.922026f,-0.141245f,-0.360442f}, -{-0.945026f,-0.0123246f,-0.326763f},{-0.915351f,-0.124506f,-0.382924f},{-0.847758f,-0.270915f,-0.455973f}, -{-0.785936f,-0.330092f,-0.522823f},{-0.764905f,-0.404768f,-0.501082f},{-0.572019f,-0.577086f,-0.582895f}, -{-0.475734f,-0.497352f,-0.725478f},{0.0412363f,-0.999149f,-0.000575673f},{-0.974726f,-0.011953f,-0.223085f}, -{-0.476842f,-0.660929f,-0.579478f},{-0.972092f,-0.00139264f,-0.234595f},{-0.948887f,-0.0918976f,-0.301941f}, -{-0.899332f,-0.194675f,-0.391539f},{-0.830531f,-0.355461f,-0.428795f},{-0.480972f,-0.670668f,-0.564686f}, -{-0.839168f,-0.338982f,-0.425309f},{-0.693691f,-0.500137f,-0.51832f},{-0.543033f,-0.647155f,-0.535075f}, -{-0.912901f,-0.218345f,-0.344872f},{-0.238438f,0.675042f,-0.698187f},{-0.181185f,0.617331f,-0.765555f}, -{-0.966653f,0.0317663f,-0.254111f},{-0.920666f,-0.00972829f,-0.390229f},{-0.970752f,-0.0188526f,-0.239345f}, -{-0.955074f,-0.110583f,-0.274962f},{-0.939826f,-0.171711f,-0.295367f},{-0.89025f,-0.290814f,-0.350546f}, -{-0.772973f,-0.474061f,-0.421638f},{-0.553283f,-0.649761f,-0.521238f},{-0.771538f,-0.484487f,-0.412312f}, -{-0.976721f,0.0834547f,-0.197616f},{-0.96782f,-0.0501943f,-0.246587f},{-0.602259f,0.376395f,-0.703997f}, -{-0.576564f,0.215634f,-0.788084f},{-0.666104f,0.00345015f,-0.745851f},{-0.658184f,-0.594629f,-0.461746f}, -{-0.98324f,0.0764916f,-0.165493f},{-0.978554f,0.0576061f,-0.197773f},{-0.963464f,-0.11138f,-0.243581f}, -{-0.927319f,-0.231403f,-0.294163f},{-0.821827f,-0.421623f,-0.38319f},{-0.694751f,-0.508234f,-0.508939f}, -{-0.570395f,-0.621536f,-0.536976f},{-0.979728f,0.108607f,-0.168339f},{-0.729935f,-0.560912f,-0.390607f}, -{-0.548836f,-0.641527f,-0.535932f},{-0.137297f,-0.0651369f,-0.988386f},{-0.983567f,0.0087842f,-0.180332f}, -{-0.955803f,-0.176433f,-0.235186f},{-0.869009f,-0.366445f,-0.332476f},{-0.774158f,-0.455288f,-0.439764f}, -{-0.619558f,-0.627742f,-0.471262f},{-0.491225f,-0.73335f,-0.469995f},{-0.858948f,-0.422974f,-0.288621f}, -{-0.322444f,0.919917f,-0.223118f},{-0.851542f,0.52224f,-0.0462868f},{0.329143f,0.78615f,-0.523099f}, -{0.346796f,0.808097f,-0.476143f},{0.260363f,0.82633f,-0.49939f},{0.49845f,0.168966f,-0.850293f}, -{0.542044f,0.204992f,-0.814964f},{0.531619f,0.163787f,-0.830996f},{0.476991f,0.648298f,-0.593455f}, -{0.412125f,0.600022f,-0.685657f},{0.534435f,0.512549f,-0.672066f},{0.0905934f,0.864983f,-0.493555f}, -{-0.0394386f,0.854323f,-0.518244f},{-0.530297f,0.721798f,-0.444739f},{0.245727f,-0.163502f,-0.95545f}, -{0.260433f,-0.101818f,-0.960108f},{-0.0886096f,0.68229f,-0.725691f},{-0.280944f,0.805369f,-0.521968f}, -{-0.157878f,0.797038f,-0.582928f},{-0.328697f,0.704327f,-0.629191f},{-0.940973f,-0.0805013f,0.32877f}, -{-0.978425f,0.154397f,-0.137284f},{-0.98397f,-0.0434644f,-0.172959f},{-0.923355f,-0.277758f,-0.265077f}, -{-0.845702f,-0.37754f,-0.377163f},{-0.728052f,-0.547275f,-0.412833f},{-0.523263f,-0.68013f,-0.513438f}, -{-0.761618f,-0.560802f,-0.324714f},{-0.0442249f,0.846128f,-0.531142f},{0.118938f,0.815643f,-0.566198f}, -{-0.106843f,0.701031f,-0.705082f},{-0.269174f,0.720933f,-0.638593f},{-0.956881f,0.109636f,0.268995f}, -{-0.87996f,0.403169f,0.251248f},{0.0556408f,0.312043f,-0.948437f},{0.232607f,0.302752f,-0.924249f}, -{0.0834377f,0.381388f,-0.920642f},{-0.000786142f,0.810607f,-0.58559f},{-0.035975f,0.777677f,-0.627634f}, -{0.0508906f,0.747998f,-0.661747f},{-0.107152f,0.734086f,-0.670549f},{-0.106257f,0.727624f,-0.677696f}, -{-0.31261f,0.743435f,-0.591252f},{-0.102666f,0.76875f,-0.631255f},{0.169555f,0.774775f,-0.609078f}, -{-0.0233887f,0.767772f,-0.640296f},{-0.357587f,0.907916f,-0.21868f},{-0.158598f,0.747223f,-0.645372f}, -{0.0443315f,0.544164f,-0.837807f},{0.0199804f,0.53045f,-0.847481f},{0.000180536f,0.693536f,-0.720422f}, -{0.184154f,0.705218f,-0.684657f},{-0.549089f,-0.833126f,0.0663552f},{-0.355312f,-0.924539f,0.137772f}, -{-0.550829f,-0.82148f,0.147507f},{-0.228807f,0.69465f,-0.681988f},{0.554866f,0.643871f,-0.526834f}, -{0.44528f,0.677502f,-0.585421f},{0.435356f,0.728495f,-0.528923f},{-0.0265682f,0.551005f,-0.834079f}, -{-0.0283525f,0.549752f,-0.834846f},{-0.0534209f,0.591202f,-0.804753f},{0.374862f,0.383933f,-0.843845f}, -{0.393388f,0.487811f,-0.779286f},{-0.0113779f,0.578591f,-0.815539f},{-0.600071f,-0.0107687f,-0.799875f}, -{-0.768247f,0.169597f,-0.617279f},{0.249316f,0.206271f,-0.9462f},{0.260273f,0.258905f,-0.930175f}, -{0.200955f,0.168393f,-0.965019f},{-0.0240418f,-0.994179f,0.105028f},{0.00728482f,-0.993241f,0.115838f}, -{-0.965055f,0.204971f,-0.163264f},{-0.947471f,0.271797f,-0.168596f},{-0.976652f,0.151255f,-0.152557f}, -{-0.969842f,-0.135725f,-0.202448f},{-0.920074f,-0.273845f,-0.28013f},{-0.867173f,-0.400469f,-0.296032f}, -{-0.629318f,-0.659676f,-0.410837f},{-0.912934f,-0.31712f,-0.256878f},{0.353662f,0.330311f,-0.87511f}, -{0.402182f,0.318681f,-0.858307f},{0.451212f,0.315875f,-0.834644f},{0.0698329f,0.848959f,-0.523824f}, -{0.1656f,0.746272f,-0.644713f},{0.0791368f,0.199257f,-0.976747f},{-0.799717f,-0.230111f,-0.554528f}, -{-0.823964f,0.15489f,-0.545062f},{-0.843488f,0.216541f,-0.491566f},{0.00578011f,0.749809f,-0.661629f}, -{-0.0349199f,0.699277f,-0.713997f},{-0.0829151f,0.75947f,-0.645237f},{-0.880051f,0.380791f,0.283742f}, -{-0.303249f,0.728386f,-0.614406f},{-0.0616567f,-0.982204f,-0.177406f},{-0.580776f,-0.680002f,-0.447545f}, -{0.64457f,0.353625f,-0.677849f},{0.611951f,0.453782f,-0.647764f},{0.620319f,0.469135f,-0.628583f}, -{0.14269f,0.455273f,-0.878844f},{-0.114267f,0.845521f,-0.521571f},{-0.209425f,0.898253f,-0.386372f}, -{-0.260342f,0.837888f,-0.479755f},{-0.298369f,0.85314f,-0.427935f},{-0.617404f,-0.37837f,-0.689673f}, -{-0.680221f,-0.42967f,-0.593871f},{-0.710034f,-0.37957f,-0.593109f},{-0.78232f,-0.388716f,-0.486698f}, -{-0.832582f,0.30817f,-0.46026f},{-0.461526f,0.804212f,-0.374483f},{-0.490626f,0.695119f,-0.525448f}, -{-0.61646f,0.663409f,-0.424106f},{-0.639165f,0.549961f,-0.537597f},{-0.469639f,0.537434f,-0.700431f}, -{0.679093f,0.422242f,-0.600453f},{0.698225f,0.270889f,-0.662647f},{0.615003f,0.328574f,-0.716806f}, -{0.233794f,0.579118f,-0.781001f},{0.226217f,0.491376f,-0.841056f},{0.259529f,0.520396f,-0.813531f}, -{0.285164f,0.525232f,-0.801757f},{0.252231f,0.40395f,-0.87932f},{0.228237f,0.438116f,-0.869461f}, -{0.373608f,0.508213f,-0.775975f},{0.414642f,0.498775f,-0.761115f},{0.338994f,0.504203f,-0.794269f}, -{-0.672568f,-0.267274f,-0.690085f},{-0.738537f,-0.304573f,-0.601497f},{-0.581012f,-0.305672f,-0.754314f}, -{-0.816326f,-0.311619f,-0.486318f},{-0.840138f,-0.337409f,-0.424645f},{-0.897044f,-0.216641f,-0.385201f}, -{-0.900858f,-0.0265083f,-0.433303f},{-0.865957f,0.140418f,-0.480001f},{-0.847461f,0.273879f,-0.454753f}, -{-0.842143f,0.354717f,-0.406167f},{-0.737181f,0.516162f,-0.436052f},{-0.927486f,0.302631f,0.219509f}, -{-0.981675f,0.141154f,-0.128021f},{-0.965402f,-0.216887f,-0.144771f},{-0.743468f,-0.592587f,-0.309994f}, -{-0.576586f,-0.709313f,-0.405492f},{-0.80673f,-0.53517f,-0.250559f},{-0.948932f,0.0757926f,0.30624f}, -{0.0720768f,0.624395f,-0.777776f},{-0.102689f,0.384984f,-0.917192f},{-0.649688f,-0.339633f,-0.680114f}, -{-0.763436f,-0.24085f,-0.599297f},{-0.842123f,-0.328308f,-0.427834f},{-0.93705f,-0.0397002f,-0.346932f}, -{-0.832593f,0.415712f,-0.366023f},{0.0558894f,0.647015f,-0.760426f},{0.186694f,0.515431f,-0.836347f}, -{0.167057f,0.796417f,-0.581216f},{-0.013721f,0.746685f,-0.665036f},{-0.074801f,0.743277f,-0.664789f}, -{-0.557577f,-0.401232f,-0.726719f},{-0.695221f,-0.350494f,-0.627552f},{-0.758386f,-0.342505f,-0.554564f}, -{-0.923356f,0.113223f,-0.366871f},{-0.509779f,0.79534f,-0.327962f},{-0.979265f,0.119692f,0.163447f}, -{-0.98726f,0.0809253f,0.137f},{-0.904176f,-0.388755f,-0.177018f},{0.455558f,0.263758f,-0.850235f}, -{0.487306f,0.146717f,-0.860818f},{0.205814f,0.624123f,-0.753732f},{-0.0595557f,0.7814f,-0.621182f}, -{-0.100202f,0.733338f,-0.67244f},{-0.798938f,-0.286607f,-0.52873f},{-0.827743f,-0.32863f,-0.4548f}, -{-0.861636f,-0.297967f,-0.410852f},{-0.778058f,-0.249262f,-0.576623f},{-0.86606f,-0.265874f,-0.423381f}, -{-0.886519f,-0.259833f,-0.382847f},{-0.884378f,-0.314477f,-0.344935f},{-0.874401f,-0.254297f,-0.413226f}, -{-0.934063f,-0.0868268f,-0.346391f},{-0.951526f,0.0806207f,-0.296814f},{-0.889652f,0.184177f,-0.417849f}, -{-0.850233f,0.348223f,-0.394771f},{-0.787832f,0.5495f,-0.278155f},{-0.990759f,-0.0191876f,0.134268f}, -{-0.971217f,0.155734f,-0.180232f},{-0.971098f,-0.0403574f,-0.235244f},{-0.952427f,-0.20938f,-0.221457f}, -{-0.895477f,-0.335529f,-0.292474f},{-0.689745f,-0.654384f,-0.309893f},{-0.697157f,-0.662355f,-0.274332f}, -{-0.991092f,-0.133136f,0.00332425f},{-0.34596f,-0.657323f,0.669506f},{-0.139928f,0.746144f,-0.650914f}, -{-0.88565f,-0.260006f,-0.384736f},{-0.119213f,0.536603f,-0.835372f},{0.0401096f,0.531459f,-0.846134f}, -{0.217478f,0.763288f,-0.608355f},{-0.345093f,0.574183f,-0.742445f},{-0.332209f,0.464239f,-0.821048f}, -{-0.363386f,0.523473f,-0.770666f},{-0.11312f,0.740586f,-0.662372f},{-0.812245f,0.237191f,-0.532915f}, -{-0.692428f,0.460837f,-0.555133f},{-0.745763f,0.212997f,-0.631244f},{-0.592546f,0.508668f,-0.624617f}, -{-0.944421f,-0.251383f,0.21184f},{-0.0282219f,0.728704f,-0.684247f},{-0.89321f,-0.192984f,-0.406121f}, -{-0.918234f,-0.245104f,-0.311078f},{-0.92194f,-0.272905f,-0.274863f},{-0.893661f,-0.357294f,-0.271499f}, -{-0.924488f,-0.268229f,-0.270878f},{-0.921025f,-0.271284f,-0.279497f},{-0.195757f,-0.980249f,-0.0281213f}, -{0.304488f,0.726339f,-0.616214f},{0.371242f,0.605966f,-0.703552f},{0.469584f,0.571727f,-0.672769f}, -{-0.882291f,-0.437208f,0.174387f},{0.18895f,0.750629f,-0.63313f},{0.0412555f,0.674764f,-0.736879f}, -{-0.90528f,-0.318957f,-0.280597f},{-0.888689f,-0.362498f,-0.280763f},{-0.924107f,-0.260881f,-0.279224f}, -{-0.959584f,-0.106115f,-0.260649f},{-0.885816f,-0.0467579f,-0.461675f},{-0.659046f,0.746035f,0.0953459f}, -{-0.674056f,0.737495f,-0.0418221f},{0.367991f,0.483938f,-0.793969f},{-0.817986f,-0.561808f,-0.123579f}, -{-0.991833f,0.0445393f,-0.119513f},{-0.986462f,0.0322381f,-0.160793f},{-0.774363f,-0.547466f,-0.317242f}, -{-0.868264f,-0.453342f,-0.201488f},{-0.26792f,0.738131f,-0.619178f},{-0.284771f,0.799236f,-0.529271f}, -{-0.119769f,0.803515f,-0.583111f},{-0.0182616f,0.559203f,-0.828829f},{-0.0234112f,0.565326f,-0.824535f}, -{-0.0600253f,0.646342f,-0.760683f},{-0.927258f,-0.33292f,0.171338f},{0.0436789f,0.832412f,-0.552433f}, -{0.00637399f,-0.0948247f,-0.995474f},{0.0884458f,0.788312f,-0.608885f},{-0.0589162f,0.60668f,-0.79276f}, -{-0.184244f,0.75806f,-0.625619f},{-0.786197f,0.366074f,-0.49788f},{-0.74957f,0.414396f,-0.51616f}, -{-0.668454f,0.559474f,-0.490059f},{-0.587639f,0.579841f,-0.564327f},{-0.0358703f,0.534826f,-0.844201f}, -{-0.880005f,-0.0687891f,-0.469956f},{-0.915141f,-0.110246f,-0.387766f},{-0.920034f,-0.193473f,-0.340743f}, -{-0.917471f,-0.210934f,-0.337274f},{-0.907767f,-0.289137f,-0.303906f},{-0.925365f,-0.259778f,-0.27607f}, -{-0.908746f,-0.296639f,-0.293576f},{-0.89718f,-0.329847f,-0.293716f},{-0.92929f,-0.237296f,-0.283039f}, -{0.0873267f,-0.833744f,-0.545202f},{0.132631f,-0.275063f,-0.952234f},{0.133723f,0.778328f,-0.613452f}, -{0.112997f,0.888124f,-0.445496f},{-0.996352f,0.0845663f,-0.0114163f},{-0.242757f,0.902481f,-0.355804f}, -{-0.192791f,0.925555f,-0.325852f},{0.0821069f,0.599657f,-0.796034f},{0.0450017f,0.462362f,-0.885549f}, -{0.0456863f,0.445472f,-0.89413f},{0.0488839f,0.541727f,-0.839132f},{0.117665f,0.639629f,-0.759625f}, -{-0.0755639f,0.649307f,-0.756763f},{-0.523536f,-0.790108f,-0.318809f},{-0.802758f,0.404456f,-0.438171f}, -{-0.905192f,-0.304054f,-0.296949f},{-0.882081f,-0.454427f,-0.124212f},{-0.993901f,0.017008f,-0.108954f}, -{-0.937838f,-0.181207f,-0.296014f},{-0.665141f,-0.586076f,-0.462712f},{-0.778656f,-0.544372f,-0.312015f}, -{-0.997034f,0.0503957f,0.0581647f},{-0.451822f,-0.741887f,-0.49544f},{-0.530233f,-0.728497f,-0.433757f}, -{-0.514687f,-0.777484f,-0.361407f},{0.436724f,-0.897585f,0.0601103f},{-0.267713f,0.874596f,-0.404242f}, -{-0.184382f,0.894412f,-0.407468f},{-0.239753f,0.839678f,-0.487299f},{-0.660937f,0.453012f,-0.598283f}, -{-0.668512f,0.361127f,-0.650138f},{-0.674786f,0.061231f,-0.735469f},{-0.93075f,-0.0503379f,-0.362175f}, -{-0.929642f,-0.0906662f,-0.357134f},{-0.64978f,-0.615394f,-0.446178f},{-0.58863f,-0.71698f,-0.373436f}, -{0.00318272f,-0.939555f,0.342383f},{-0.145101f,-0.961075f,0.235116f},{-0.680715f,0.113708f,-0.72367f}, -{-0.887669f,0.397071f,-0.233192f},{-0.954059f,0.0816392f,-0.288281f},{-0.792749f,0.556533f,-0.248635f}, -{-0.679507f,0.672828f,-0.292527f},{-0.45138f,0.853066f,-0.261789f},{-0.603588f,0.761234f,-0.237075f}, -{-0.922933f,0.26974f,-0.274656f},{-0.96959f,-0.0548561f,-0.238506f},{-0.947935f,-0.133122f,-0.289307f}, -{-0.924055f,-0.213494f,-0.317085f},{-0.899426f,-0.251276f,-0.357622f},{-0.900932f,-0.266772f,-0.342278f}, -{-0.194343f,0.850455f,-0.488832f},{-0.0324944f,0.788577f,-0.614077f},{-0.916005f,0.348945f,0.19792f}, -{-0.812545f,-0.531355f,0.239652f},{-0.70015f,0.345013f,-0.625105f},{-0.657777f,-0.143039f,-0.739506f}, -{-0.764772f,0.307474f,-0.566201f},{-0.688961f,-0.668823f,-0.279299f},{-0.87333f,-0.281439f,-0.397601f}, -{-0.429668f,-0.366092f,-0.825447f},{-0.233538f,-0.771659f,-0.59161f},{-0.426967f,0.109738f,-0.897584f}, -{-0.737624f,-0.655111f,-0.163528f},{-0.905149f,-0.408362f,-0.118095f},{-0.999381f,-0.00136333f,-0.0351535f}, -{-0.904803f,-0.318869f,-0.282231f},{-0.946042f,-0.284675f,-0.154804f},{0.0884216f,0.584999f,-0.8062f}, -{0.106585f,0.427367f,-0.897773f},{0.0498672f,0.628808f,-0.77596f},{-0.704076f,0.547598f,-0.452122f}, -{-0.664798f,0.333039f,-0.668677f},{-0.472316f,0.827564f,-0.303406f},{-0.550143f,0.780893f,-0.295885f}, -{-0.722361f,-0.592277f,-0.356934f},{-0.872574f,0.386936f,-0.298154f},{-0.928608f,0.133556f,-0.346195f}, -{-0.792346f,0.542703f,-0.278677f},{-0.72946f,0.638489f,-0.245398f},{-0.870852f,0.385273f,-0.305257f}, -{-0.963596f,-0.0794412f,-0.255286f},{-0.947973f,-0.173342f,-0.267021f},{-0.888068f,-0.328748f,-0.321342f}, -{-0.907185f,-0.261738f,-0.329407f},{0.0476047f,0.889729f,-0.453999f},{0.0592263f,0.829014f,-0.556083f}, -{-0.0314531f,0.840112f,-0.5415f},{0.331963f,-0.0387972f,-0.942494f},{-0.0368371f,-0.256588f,-0.965819f}, -{-0.0167172f,-0.297411f,-0.954603f},{0.185203f,0.0534945f,-0.981243f},{-0.870331f,0.453847f,0.191171f}, -{-0.622077f,-0.0313076f,-0.78233f},{-0.699579f,0.420684f,-0.577594f},{-0.803813f,0.488717f,-0.339176f}, -{0.0329742f,-0.999242f,0.0207125f},{0.164832f,-0.983764f,0.0709828f},{-0.0206047f,0.918457f,-0.394983f}, -{-0.0153557f,0.905575f,-0.423907f},{-0.0935167f,0.715321f,-0.692511f},{0.0511931f,0.851253f,-0.522252f}, -{-0.0160317f,-0.999497f,0.0273526f},{-0.968668f,-0.178708f,-0.17247f},{-0.899931f,-0.411584f,-0.143951f}, -{-0.753191f,-0.543083f,-0.371166f},{-0.921122f,-0.369519f,-0.122436f},{-0.675025f,0.556727f,-0.484146f}, -{-0.922432f,0.21482f,-0.320891f},{-0.946594f,-0.313147f,0.0767992f},{-0.826512f,-0.505872f,0.246925f}, -{-0.101095f,0.874518f,-0.474339f},{0.178633f,0.891713f,-0.415859f},{0.174257f,0.866981f,-0.466882f}, -{-0.0200044f,0.642351f,-0.76615f},{0.0617693f,0.424787f,-0.903184f},{-0.0536696f,0.441809f,-0.895502f}, -{-0.977742f,-0.116558f,0.174454f},{-0.704941f,0.55243f,-0.444837f},{-0.785385f,0.27646f,-0.553842f}, -{-0.797305f,0.473351f,-0.374492f},{-0.842062f,0.352114f,-0.408592f},{-0.818045f,-0.42511f,-0.387406f}, -{0.0565383f,0.364246f,-0.929585f},{0.0346147f,0.516041f,-0.855864f},{0.363058f,0.509097f,-0.78039f}, -{0.440208f,0.416773f,-0.79531f},{0.328956f,0.493552f,-0.805106f},{0.187626f,0.556664f,-0.809273f}, -{0.208331f,0.517454f,-0.829964f},{-0.989202f,-0.121884f,-0.0813819f},{0.499549f,0.158873f,-0.851593f}, -{0.432908f,0.0386735f,-0.900608f},{0.273405f,0.49063f,-0.827364f},{0.0817157f,0.606752f,-0.79068f}, -{0.106964f,0.0401378f,-0.993452f},{0.0945015f,-0.110277f,-0.989398f},{-0.0850992f,0.719827f,-0.688917f}, -{0.417252f,0.52599f,-0.741104f},{0.395665f,0.461008f,-0.794306f},{0.103608f,0.642954f,-0.758865f}, -{0.116833f,0.681212f,-0.722704f},{0.368249f,0.572081f,-0.732882f},{-0.77825f,-0.597763f,-0.192372f}, -{-0.931479f,-0.327635f,-0.15812f},{-0.968138f,-0.239452f,-0.0732868f},{0.0234646f,-0.992831f,0.1172f}, -{0.00620094f,0.863367f,-0.504538f},{-0.74072f,-0.135665f,-0.657973f},{-0.75342f,-0.0540682f,-0.655313f}, -{-0.674416f,-0.0614504f,-0.73579f},{-0.612793f,0.0468009f,-0.788856f},{-0.734032f,0.00265937f,-0.67911f}, -{-0.706752f,0.0267475f,-0.706955f},{-0.526979f,0.0954262f,-0.844504f},{-0.782884f,-0.564665f,-0.261241f}, -{0.679924f,-0.0978195f,-0.726729f},{0.561959f,-0.391283f,-0.728766f},{-0.585405f,0.436328f,-0.683314f}, -{0.0137966f,0.957329f,-0.288671f},{-0.139726f,0.939267f,-0.313454f},{-0.0246292f,0.926615f,-0.375203f}, -{-0.687732f,-0.384911f,-0.615523f},{-0.734801f,-0.248514f,-0.631117f},{-0.789134f,-0.0194749f,-0.613912f}, -{-0.782444f,-0.0322267f,-0.621886f},{-0.73717f,0.00902504f,-0.675647f},{-0.701868f,0.228167f,-0.674774f}, -{-0.0342424f,0.914195f,-0.403826f},{0.0216571f,0.853809f,-0.520136f},{-0.158441f,0.87924f,-0.449259f}, -{-0.640767f,0.226042f,-0.733705f},{-0.751944f,0.506839f,-0.421539f},{-0.363757f,0.83771f,-0.407337f}, -{0.296575f,0.632464f,-0.715564f},{0.26247f,0.617033f,-0.741876f},{0.159429f,0.413905f,-0.89625f}, -{-0.636593f,-0.395732f,-0.661925f},{0.00776096f,0.570112f,-0.82153f},{0.0359855f,0.664895f,-0.746069f}, -{0.0310453f,0.609546f,-0.792142f},{0.288612f,0.808413f,-0.513002f},{0.393032f,0.753305f,-0.52731f}, -{0.325346f,0.75768f,-0.565748f},{-0.974308f,-0.178684f,-0.137097f},{-0.953546f,-0.22885f,-0.195905f}, -{-0.625887f,0.490681f,-0.606216f},{-0.609771f,0.642855f,-0.463592f},{-0.307379f,-0.933886f,-0.182687f}, -{0.303272f,0.817697f,-0.489282f},{-0.0330104f,0.731284f,-0.681274f},{0.0176375f,0.73728f,-0.675357f}, -{-0.799932f,-0.0896961f,-0.593349f},{-0.739251f,-0.0909561f,-0.66726f},{-0.730596f,-0.0295249f,-0.682171f}, -{-0.735792f,0.141394f,-0.662282f},{0.398786f,0.719891f,-0.568091f},{0.175395f,0.891763f,-0.417127f}, -{-0.878951f,-0.343843f,-0.33048f},{0.102957f,-0.628029f,-0.771349f},{-0.00816262f,-0.613874f,-0.789362f}, -{-0.893497f,-0.435569f,0.109282f},{0.0100863f,-0.0486995f,-0.998763f},{0.150525f,0.0452677f,-0.987569f}, -{0.0764546f,0.26098f,-0.962312f},{0.152391f,0.171026f,-0.97341f},{0.345637f,0.689084f,-0.636944f}, -{0.357883f,0.609575f,-0.707346f},{-0.100902f,0.519759f,-0.848333f},{-0.140263f,0.68451f,-0.715383f}, -{-0.10744f,0.788916f,-0.605035f},{0.394548f,0.75452f,-0.524434f},{0.388926f,0.688586f,-0.612034f}, -{-0.738403f,-0.340227f,-0.582242f},{-0.782643f,-0.254177f,-0.568211f},{0.247413f,-0.564007f,-0.787834f}, -{0.256883f,-0.117003f,-0.959334f},{-0.786296f,-0.0782728f,-0.612872f},{-0.707897f,-0.152897f,-0.689568f}, -{-0.723229f,0.022688f,-0.690235f},{-0.749203f,0.184557f,-0.636108f},{-0.701033f,0.199585f,-0.68463f}, -{-0.709307f,0.157231f,-0.687141f},{-0.696854f,0.196286f,-0.689831f},{-0.118736f,0.910259f,-0.396649f}, -{-0.175142f,0.912401f,-0.369931f},{-0.131566f,0.87213f,-0.471254f},{0.378213f,0.661221f,-0.647875f}, -{0.0959242f,0.458718f,-0.883389f},{0.383769f,0.604907f,-0.697716f},{0.349285f,0.490389f,-0.798447f}, -{0.329539f,0.53652f,-0.776885f},{-0.616684f,0.525528f,-0.586107f},{-0.422189f,0.264445f,-0.867079f}, -{-0.298524f,0.112001f,-0.947808f},{-0.306054f,0.243506f,-0.920345f},{-0.685699f,0.495668f,-0.533039f}, -{-0.657282f,0.676852f,-0.331438f},{-0.265994f,0.899443f,-0.346771f},{0.188719f,0.811568f,-0.552939f}, -{0.403312f,0.792154f,-0.458074f},{0.377123f,0.811736f,-0.44594f},{0.302501f,0.527583f,-0.79382f}, -{-0.0959399f,0.939362f,-0.329235f},{-0.772767f,-0.320831f,-0.547631f},{-0.690519f,0.217566f,-0.689818f}, -{-0.661912f,0.307201f,-0.683739f},{0.351612f,0.447101f,-0.822478f},{0.303686f,0.461482f,-0.833552f}, -{-0.147613f,0.832848f,-0.533455f},{0.312312f,0.449462f,-0.836926f},{-0.781602f,-0.335284f,-0.526007f}, -{-0.917526f,-0.0964975f,-0.385791f},{-0.838488f,-0.158383f,-0.521394f},{0.0f,-1.0f,0.0f}, -{-0.886577f,-0.14044f,-0.440747f},{-0.968258f,-0.0498201f,-0.244938f},{-0.655652f,0.450153f,-0.606203f}, -{-0.604448f,0.678246f,-0.41788f},{-0.14275f,-0.918332f,0.369173f},{0.0989465f,0.694789f,-0.712374f}, -{0.166933f,0.837658f,-0.52006f},{0.21055f,0.72595f,-0.654725f},{0.316211f,0.415471f,-0.852874f}, -{-0.547079f,-0.625644f,-0.556123f},{-0.6564f,-0.45908f,-0.598652f},{-0.620557f,-0.50989f,-0.595752f}, -{-0.334171f,0.525758f,-0.782246f},{-0.733579f,-0.147398f,-0.663427f},{-0.792315f,0.0928118f,-0.603012f}, -{-0.690414f,0.293632f,-0.661142f},{0.578691f,-0.376963f,-0.723198f},{-0.599053f,0.374824f,-0.707561f}, -{0.015523f,0.715745f,-0.698189f},{-0.107927f,0.774788f,-0.62294f},{-0.0441408f,0.723233f,-0.689192f}, -{0.407924f,0.684044f,-0.604716f},{-0.194453f,0.780287f,-0.594425f},{-0.114251f,0.723885f,-0.680394f}, -{0.555647f,0.406394f,-0.725328f},{0.518593f,0.360257f,-0.77542f},{0.520778f,0.303692f,-0.797848f}, -{0.240672f,0.416642f,-0.876633f},{0.0327207f,-0.998585f,-0.0419202f},{-0.0431421f,0.587874f,-0.807801f}, -{-0.766352f,0.134119f,-0.628264f},{-0.862351f,-0.0512968f,-0.503705f},{-0.888499f,-0.189282f,-0.418021f}, -{-0.804259f,-0.259012f,-0.534865f},{-0.645443f,-0.635757f,-0.42334f},{-0.698401f,-0.436983f,-0.566817f}, -{-0.765176f,-0.285133f,-0.577239f},{-0.830084f,-0.296906f,-0.472025f},{-0.795711f,-0.359287f,-0.487603f}, -{-0.743921f,-0.239702f,-0.623799f},{-0.780822f,-0.174298f,-0.599948f},{-0.811998f,-0.130463f,-0.568893f}, -{-0.879525f,0.0516754f,-0.473039f},{-0.820533f,0.14866f,-0.55193f},{-0.807281f,0.13951f,-0.573441f}, -{-0.712668f,0.21512f,-0.667704f},{-0.729673f,0.319276f,-0.604683f},{-0.696901f,0.37933f,-0.608636f}, -{-0.656775f,0.415176f,-0.629504f},{-0.691862f,0.261617f,-0.672966f},{-0.568101f,0.174111f,-0.80433f}, -{-0.112366f,0.606799f,-0.786873f},{-0.05266f,0.579328f,-0.813392f},{-0.1848f,0.629204f,-0.754951f}, -{-0.196257f,0.888754f,-0.414246f},{-0.240208f,0.903375f,-0.355264f},{0.544748f,0.342669f,-0.765393f}, -{0.385739f,0.3243f,-0.863733f},{0.457805f,0.216345f,-0.862328f},{0.384613f,0.32892f,-0.862487f}, -{0.0803373f,0.547268f,-0.833093f},{0.0454906f,0.620927f,-0.782547f},{-0.674372f,0.00883527f,-0.738339f}, -{-0.84339f,-0.19135f,-0.502075f},{-0.931514f,-0.058715f,-0.358935f},{-0.758433f,-0.616169f,-0.212403f}, -{-0.600183f,0.119047f,-0.790954f},{-0.365026f,0.727664f,-0.580743f},{-0.775646f,0.368886f,-0.51215f}, -{-0.665811f,0.318583f,-0.674686f},{-0.523593f,0.612358f,-0.592341f},{0.599859f,0.0667783f,-0.797314f}, -{-0.0627656f,0.904092f,-0.422703f},{-0.800314f,-0.47969f,-0.359714f},{-0.804728f,-0.352697f,-0.477513f}, -{-0.797558f,0.251342f,-0.548387f},{0.299336f,-0.933466f,0.197581f},{0.329159f,-0.935342f,0.129572f}, -{-0.219073f,0.903725f,-0.367817f},{-0.212267f,0.916372f,-0.339419f},{-0.131723f,0.689763f,-0.711952f}, -{-0.0554175f,0.330594f,-0.942145f},{0.0320061f,0.18098f,-0.982966f},{-0.280935f,0.879313f,-0.384558f}, -{-0.251187f,0.930229f,-0.267543f},{-0.292435f,0.903886f,-0.312204f},{-0.718851f,0.057869f,-0.692752f}, -{-0.982328f,0.12595f,-0.138447f},{0.786614f,0.464934f,-0.406294f},{-0.700668f,0.366514f,-0.612153f}, -{0.312493f,0.817967f,-0.48299f},{-0.601982f,-0.595711f,-0.531738f},{-0.489698f,-0.677372f,-0.548965f}, -{-0.811554f,-0.468049f,-0.349729f},{-0.761489f,0.347982f,-0.546848f},{-0.731756f,0.145687f,-0.665814f}, -{0.174515f,0.884687f,-0.432289f},{-0.114839f,0.792609f,-0.598818f},{0.112595f,0.652698f,-0.749205f}, -{0.767002f,0.399536f,-0.502075f},{0.15278f,0.810745f,-0.565111f},{-0.702188f,0.107794f,-0.703784f}, -{-0.633727f,0.299399f,-0.713267f},{-0.0571531f,-0.643154f,0.763601f},{-0.427399f,0.823243f,-0.373633f}, -{-0.47606f,0.788375f,-0.389657f},{-0.518836f,0.696978f,-0.495006f},{-0.761843f,0.502152f,-0.409193f}, -{-0.908978f,0.15949f,-0.385125f},{-0.917297f,0.0193351f,-0.397734f},{-0.926845f,-0.050602f,-0.372019f}, -{-0.927027f,-0.202271f,-0.315766f},{-0.590106f,-0.644232f,-0.48656f},{-0.603943f,-0.643506f,-0.470269f}, -{-0.580711f,-0.717758f,-0.384186f},{-0.677655f,-0.7017f,-0.220003f},{-0.804464f,-0.533413f,-0.261358f}, -{-0.864175f,-0.356112f,-0.355508f},{-0.851693f,-0.371734f,-0.369368f},{-0.854062f,-0.407542f,-0.323244f}, -{-0.851561f,-0.394576f,-0.345188f},{-0.877129f,-0.167053f,-0.450265f},{-0.918302f,-0.0351973f,-0.394312f}, -{-0.927236f,-0.0126858f,-0.374262f},{-0.95147f,0.0250336f,-0.306721f},{-0.915624f,0.0651938f,-0.396715f}, -{-0.876806f,0.15456f,-0.455326f},{-0.885586f,0.147487f,-0.440438f},{-0.883614f,0.193033f,-0.426574f}, -{-0.825263f,0.319221f,-0.465875f},{-0.790863f,0.32737f,-0.517074f},{-0.709898f,0.0806033f,-0.699677f}, -{-0.0398124f,0.680137f,-0.732003f},{0.584153f,0.293525f,-0.756708f},{0.618851f,0.395068f,-0.678929f}, -{-0.767229f,0.225208f,-0.600534f},{-0.77519f,0.253299f,-0.578723f},{-0.682595f,0.190452f,-0.705544f}, -{-0.776026f,0.168611f,-0.607745f},{-0.859731f,0.371183f,-0.350837f},{-0.673324f,0.285739f,-0.6819f}, -{-0.727279f,0.537275f,-0.427084f},{-0.735501f,0.339622f,-0.586256f},{-0.716775f,0.674215f,-0.177957f}, -{-0.74042f,0.661547f,-0.118885f},{-0.43986f,0.804958f,-0.398203f},{-0.940599f,0.0319039f,-0.338018f}, -{-0.581107f,-0.717514f,-0.384043f},{-0.606471f,-0.659641f,-0.443923f},{-0.913605f,-0.148399f,-0.378555f}, -{-0.933232f,0.0702974f,-0.352329f},{-0.834932f,0.33923f,-0.433372f},{-0.738723f,0.209806f,-0.640523f}, -{-0.755023f,0.427757f,-0.496955f},{-0.724755f,0.681174f,-0.103601f},{-0.489712f,0.813523f,-0.313628f}, -{-0.868385f,-0.320696f,-0.378234f},{-0.899023f,0.150755f,-0.411134f},{-0.620953f,-0.356866f,-0.6979f}, -{-0.736245f,0.329334f,-0.591171f},{-0.752901f,0.257652f,-0.605604f},{-0.758671f,0.506889f,-0.409246f}, -{-0.670159f,0.564671f,-0.481698f},{-0.41136f,0.798592f,-0.439357f},{-0.712829f,0.660545f,-0.235702f}, -{-0.955273f,0.267109f,-0.126908f},{-0.993407f,0.0231793f,-0.11227f},{-0.933768f,-0.285835f,-0.215351f}, -{-0.522153f,-0.80213f,-0.28973f},{-0.591668f,-0.703124f,-0.394392f},{-0.686102f,-0.632814f,-0.358901f}, -{-0.674643f,-0.686631f,-0.270916f},{-0.751492f,-0.557023f,-0.353533f},{-0.851592f,-0.408749f,-0.328199f}, -{-0.874671f,-0.330786f,-0.354304f},{-0.889016f,-0.355231f,-0.288896f},{-0.808405f,-0.536273f,-0.242677f}, -{-0.865771f,-0.41462f,-0.280233f},{-0.919658f,-0.178283f,-0.349921f},{-0.929822f,0.0642691f,-0.362355f}, -{-0.955185f,0.0169424f,-0.295525f},{-0.930455f,0.000111387f,-0.366407f},{-0.91058f,0.167131f,-0.378036f}, -{-0.920372f,0.185707f,-0.344133f},{-0.899459f,0.2174f,-0.379093f},{-0.884971f,0.276707f,-0.374512f}, -{-0.864626f,0.258211f,-0.430985f},{-0.798431f,0.0357707f,-0.601023f},{-0.706052f,-0.322651f,-0.630387f}, -{-0.645647f,-0.42174f,-0.636612f},{-0.686848f,-0.423865f,-0.590405f},{-0.762974f,-0.311095f,-0.566648f}, -{-0.881004f,-0.0543548f,-0.469976f},{-0.878294f,0.0736529f,-0.472414f},{-0.855078f,0.167816f,-0.49059f}, -{-0.795931f,0.234467f,-0.558139f},{-0.731186f,0.272107f,-0.62556f},{-0.691631f,0.234792f,-0.683022f}, -{-0.652312f,0.437466f,-0.618961f},{-0.736543f,0.354978f,-0.575756f},{-0.841663f,0.118126f,-0.526925f}, -{-0.821913f,0.158685f,-0.547063f},{-0.766839f,0.226806f,-0.60043f},{-0.700081f,0.449037f,-0.555205f}, -{-0.633108f,0.633201f,-0.445232f},{-0.701542f,0.248827f,-0.667775f},{-0.756746f,0.56066f,-0.336147f}, -{-0.629406f,0.77327f,0.0768261f},{-0.652054f,-0.665617f,-0.363016f},{-0.850827f,-0.17411f,-0.495762f}, -{-0.92283f,0.0165544f,-0.384853f},{-0.92248f,0.063835f,-0.380731f},{-0.900127f,0.114247f,-0.420379f}, -{-0.782641f,0.243676f,-0.572796f},{-0.77963f,0.230663f,-0.582213f},{-0.845516f,0.11178f,-0.522119f}, -{-0.709714f,0.466652f,-0.527772f},{-0.400168f,0.870759f,-0.285735f},{-0.292425f,0.156734f,-0.943357f}, -{-0.735463f,-0.631546f,-0.245446f},{-0.797337f,-0.491385f,-0.350419f},{-0.908647f,0.250044f,-0.334423f}, -{-0.671733f,-0.467832f,-0.574377f},{-0.707944f,0.390645f,-0.588398f},{-0.807272f,0.194343f,-0.557264f}, -{-0.610848f,-0.732514f,-0.30048f},{-0.674118f,-0.457605f,-0.579795f},{0.451244f,0.0484673f,-0.891083f}, -{0.473483f,0.0961672f,-0.875538f},{-0.641274f,-0.741973f,-0.195559f},{-0.496208f,0.338717f,-0.799405f}, -{-0.461432f,0.358428f,-0.811548f},{0.566993f,0.562716f,-0.601556f},{-0.016211f,0.337597f,-0.941151f}, -{0.0142408f,0.296582f,-0.954901f},{0.152933f,0.162301f,-0.974818f},{0.460267f,0.760721f,-0.457665f}, -{0.266763f,-0.4287f,0.863165f},{-0.229672f,0.792f,-0.565674f},{-0.183382f,0.922747f,-0.338982f}, -{-0.0423757f,0.683786f,-0.728451f},{-0.078403f,0.92181f,-0.379631f},{-0.175821f,0.91269f,-0.368895f}, -{0.0319953f,0.763247f,-0.645315f},{0.702159f,0.515647f,-0.491f},{0.773998f,0.507949f,-0.378041f}, -{0.700035f,0.505806f,-0.504094f},{0.488668f,0.329755f,-0.807754f},{0.494024f,0.59918f,-0.630019f}, -{0.553235f,0.493178f,-0.671347f},{0.416349f,0.563334f,-0.713658f},{0.631496f,0.041896f,-0.774246f}, -{-0.164784f,0.487377f,-0.857502f},{-0.0916461f,0.350752f,-0.931973f},{-0.123292f,0.333143f,-0.93478f}, -{0.432491f,0.372184f,-0.821237f},{0.371927f,0.337693f,-0.864658f},{0.366353f,0.303488f,-0.879591f}, -{0.301134f,0.453315f,-0.838942f},{0.338526f,0.404715f,-0.849474f},{0.335097f,0.376204f,-0.863818f}, -{0.418205f,0.224577f,-0.880153f},{0.301306f,0.574076f,-0.761349f},{0.242002f,0.593726f,-0.767414f}, -{0.282472f,0.61884f,-0.732971f},{0.0259057f,0.734429f,-0.678191f},{0.289283f,0.717467f,-0.633684f}, -{0.000842052f,0.572893f,-0.819629f},{0.611392f,0.270866f,-0.743527f},{0.38074f,0.252503f,-0.889539f}, -{0.465128f,0.413081f,-0.782956f},{0.171625f,0.243028f,-0.954716f},{0.125825f,0.386253f,-0.91377f}, -{0.0129325f,0.398473f,-0.917089f},{0.0361355f,0.47008f,-0.881884f},{0.341164f,-0.747721f,0.569667f}, -{0.200665f,-0.688634f,0.69679f},{0.235842f,0.431982f,-0.8705f},{0.636529f,0.504031f,-0.583766f}, -{0.608404f,0.487187f,-0.626493f},{0.228318f,0.295752f,-0.927578f},{-0.363634f,0.203676f,-0.909003f}, -{0.588486f,0.667092f,-0.456807f},{0.581279f,0.615243f,-0.532532f},{-0.0425343f,0.776099f,-0.629175f}, -{-0.033611f,0.737196f,-0.674843f},{0.451443f,0.142728f,-0.880811f},{0.0148181f,-0.998955f,-0.0432437f}, -{0.430652f,0.505955f,-0.747361f},{0.663306f,0.015291f,-0.748192f},{0.381259f,0.835846f,-0.394973f}, -{0.319437f,0.838482f,-0.441485f},{0.478502f,0.508641f,-0.715765f},{0.343964f,0.541458f,-0.767145f}, -{0.0565948f,-0.83426f,-0.548459f},{-0.2399f,0.915786f,-0.322155f},{-0.17528f,0.938477f,-0.297554f}, -{-0.345293f,0.876527f,-0.335369f},{0.155107f,-0.700209f,0.696886f},{0.518676f,0.656536f,-0.547664f}, -{0.540738f,0.696976f,-0.470985f},{-0.530286f,0.670052f,-0.519449f},{0.173892f,0.450541f,-0.875657f}, -{0.264071f,0.476953f,-0.838321f},{0.242757f,0.218363f,-0.945191f},{0.166356f,-0.768676f,0.617627f}, -{-0.113909f,0.326739f,-0.938225f},{0.484652f,0.733872f,-0.475967f},{0.25989f,0.909259f,-0.325124f}, -{-0.13513f,0.367255f,-0.920252f},{-0.0836041f,0.28648f,-0.954431f},{0.694915f,0.407838f,-0.592251f}, -{0.755633f,0.298784f,-0.582878f},{0.183817f,0.688876f,-0.701185f},{0.329009f,0.56222f,-0.758724f}, -{0.450194f,0.417842f,-0.789135f},{0.426558f,0.624877f,-0.653894f},{0.474707f,0.334435f,-0.814129f}, -{-0.368151f,0.702815f,-0.6087f},{0.441703f,0.751622f,-0.48986f},{0.324125f,0.385029f,-0.864116f}, -{0.553038f,0.362224f,-0.750296f},{0.592331f,0.191065f,-0.782712f},{0.545859f,0.282084f,-0.788965f}, -{0.643405f,0.153596f,-0.749959f},{0.603579f,0.440697f,-0.664439f},{0.561321f,0.45505f,-0.691266f}, -{0.274957f,0.426057f,-0.861901f},{0.51976f,0.49844f,-0.693835f},{0.492771f,0.292664f,-0.819466f}, -{0.475031f,0.455529f,-0.752887f},{0.447749f,0.310442f,-0.838538f},{0.60608f,0.361225f,-0.708649f}, -{0.671187f,0.621191f,-0.404513f},{0.499191f,0.431184f,-0.751591f},{0.496577f,0.735805f,-0.460437f}, -{-0.182961f,0.799778f,-0.571734f},{-0.148508f,0.75117f,-0.643187f},{-0.125094f,0.69274f,-0.710255f}, -{-0.0456011f,0.630923f,-0.774504f},{0.515427f,0.473627f,-0.714151f},{0.325487f,0.479803f,-0.814768f}, -{0.0854529f,0.600774f,-0.794838f},{0.397275f,0.715379f,-0.574809f},{0.723071f,0.581009f,-0.373626f}, -{0.538154f,0.430644f,-0.724524f},{-0.318307f,0.836747f,-0.445572f},{0.309693f,0.443584f,-0.841025f}, -{0.609157f,0.175619f,-0.77336f},{0.0230232f,0.685541f,-0.72767f},{0.574796f,0.145307f,-0.805292f}, -{0.0529214f,0.706485f,-0.705747f},{0.462671f,0.340719f,-0.818441f},{0.292814f,0.517741f,-0.803868f}, -{-0.23533f,0.822868f,-0.517212f},{0.108122f,0.103404f,-0.988745f},{0.707184f,0.394909f,-0.586463f}, -{0.529927f,0.343312f,-0.775445f},{0.0648392f,0.783754f,-0.617677f},{0.02647f,0.524744f,-0.850848f}, -{-0.045521f,0.7608f,-0.647388f},{0.118751f,0.88849f,-0.443264f},{-0.0161997f,0.457597f,-0.889012f}, -{0.000280184f,0.398345f,-0.917235f},{0.0561452f,0.767709f,-0.638334f},{-0.624146f,-0.0435425f,-0.780093f}, -{0.231384f,-0.0713779f,-0.970241f},{0.403915f,0.224348f,-0.88686f},{0.210782f,0.871545f,-0.442697f}, -{0.239799f,0.817561f,-0.523536f},{0.449956f,0.539841f,-0.711415f},{0.777722f,0.411413f,-0.475276f}, -{-0.0354915f,0.335593f,-0.941338f},{-0.568895f,-0.812661f,-0.126259f},{0.191192f,0.806213f,-0.55988f}, -{-0.0525749f,0.804373f,-0.591794f},{-0.00818761f,0.759069f,-0.650959f},{0.0183587f,-0.996702f,0.0790466f}, -{0.798654f,0.469085f,-0.376976f},{0.00703419f,0.455561f,-0.890177f},{-0.379388f,0.711838f,-0.591059f}, -{-0.356592f,0.682883f,-0.637584f},{0.159572f,0.698134f,-0.697958f},{-0.0127765f,0.446995f,-0.894445f}, -{0.429407f,0.375184f,-0.82149f},{0.375785f,0.710967f,-0.5944f},{-0.353617f,0.82554f,-0.439818f}, -{-0.29696f,0.869412f,-0.39489f},{-0.206536f,0.683309f,-0.700309f},{0.0821383f,0.485265f,-0.870501f}, -{0.0984004f,0.402228f,-0.910236f},{0.516561f,0.229612f,-0.82489f},{0.550054f,0.0979327f,-0.829367f}, -{0.385639f,-0.695462f,0.606312f},{-0.366341f,0.482633f,-0.795525f},{-0.402403f,0.371983f,-0.836481f}, -{0.155905f,0.37037f,-0.915707f},{0.349217f,0.269209f,-0.897538f},{0.0164691f,-0.998683f,0.0485895f}, -{0.598748f,0.120536f,-0.791815f},{0.610598f,0.110001f,-0.784264f},{-0.514242f,0.289975f,-0.807137f}, -{-0.0559095f,0.147385f,-0.987498f},{-0.318715f,0.0545386f,-0.94628f},{0.504873f,0.254761f,-0.824742f}, -{0.208381f,0.0932082f,-0.973596f},{0.387806f,0.408758f,-0.82615f},{0.433037f,0.482513f,-0.761354f}, -{0.290996f,0.751733f,-0.591793f},{0.447236f,0.828435f,-0.337157f},{0.00941225f,0.76729f,-0.641231f}, -{-0.394672f,0.547728f,-0.737718f},{0.0124283f,0.71336f,-0.700687f},{-0.00554958f,0.640843f,-0.767652f}, -{-0.0250379f,0.60922f,-0.792606f},{-0.355539f,0.551191f,-0.754838f},{-0.0722814f,0.578202f,-0.812685f}, -{0.567135f,0.265433f,-0.779682f},{0.0651969f,0.379966f,-0.9227f},{-0.690634f,-0.632789f,-0.350148f}, -{-0.588416f,-0.793163f,-0.157031f},{-0.61951f,-0.714354f,-0.325432f},{0.26334f,0.909731f,-0.321001f}, -{0.162899f,0.168777f,-0.9721f},{-0.250455f,-0.579707f,-0.775379f},{0.0868959f,0.251874f,-0.963851f}, -{-0.00789398f,0.248566f,-0.968583f},{0.169066f,-0.886262f,-0.431227f},{0.230861f,0.171183f,-0.95781f}, -{-0.326545f,0.302039f,-0.895623f},{0.390293f,0.218435f,-0.894403f},{0.46466f,0.36971f,-0.804615f}, -{0.395036f,0.206159f,-0.895234f},{0.355156f,0.127125f,-0.926123f},{0.426136f,0.16671f,-0.889166f}, -{0.227801f,-0.666285f,0.71005f},{-0.143781f,0.574938f,-0.805465f},{-0.17141f,0.442747f,-0.88011f}, -{-0.267554f,0.646156f,-0.714771f},{-0.303341f,0.558085f,-0.772351f},{-0.291798f,0.614407f,-0.733047f}, -{-0.273007f,0.547826f,-0.790793f},{0.492345f,0.234013f,-0.838352f},{-0.077035f,0.628769f,-0.773767f}, -{-0.218071f,0.684619f,-0.695515f},{-0.244835f,0.698392f,-0.672535f},{-0.22162f,0.646343f,-0.730154f}, -{-0.214732f,0.54545f,-0.810169f},{-0.268715f,0.470151f,-0.840684f},{0.0603743f,-0.691798f,-0.719563f}, -{0.606051f,0.3876f,-0.694599f},{0.265445f,0.352554f,-0.897354f},{-0.153875f,0.507607f,-0.847737f}, -{-0.0218791f,0.2504f,-0.967895f},{0.551176f,0.495749f,-0.671147f},{0.611981f,0.287921f,-0.736601f}, -{0.256431f,0.513728f,-0.818735f},{0.0377112f,-0.996204f,0.078455f},{0.303029f,0.247999f,-0.920147f}, -{0.111975f,0.673271f,-0.730868f},{0.171041f,0.507994f,-0.844208f},{0.212217f,0.634694f,-0.743053f}, -{0.064679f,0.308196f,-0.949122f},{0.137869f,0.696416f,-0.704271f},{-0.132098f,0.735681f,-0.664322f}, -{-0.265268f,0.687997f,-0.675495f},{-0.177326f,0.583504f,-0.792514f},{-0.159021f,0.5532f,-0.81773f}, -{-0.0648649f,0.494838f,-0.866561f},{0.742841f,0.289633f,-0.603574f},{-0.244684f,0.734364f,-0.633119f}, -{-0.230733f,0.626359f,-0.744605f},{-0.0475614f,0.476977f,-0.877628f},{0.806145f,0.328443f,-0.492195f}, -{0.553227f,0.516329f,-0.653716f},{-0.157719f,0.757436f,-0.633573f},{-0.115361f,0.701471f,-0.7033f}, -{-0.121287f,0.647932f,-0.751979f},{-0.119817f,0.518635f,-0.846559f},{0.686161f,0.434175f,-0.583674f}, -{0.414612f,0.725276f,-0.549611f},{0.0854752f,0.862823f,-0.498227f},{-0.12717f,0.823634f,-0.55268f}, -{-0.039744f,0.417788f,-0.907675f},{0.058142f,0.464484f,-0.883671f},{0.331886f,0.247708f,-0.910216f}, -{0.385355f,0.193432f,-0.902267f},{-0.0793643f,0.273121f,-0.9587f},{0.0536832f,0.505359f,-0.861238f}, -{-0.615467f,0.0831187f,-0.783768f},{0.313339f,0.713027f,-0.627226f},{0.689499f,0.582774f,-0.430075f}, -{-0.128185f,0.762866f,-0.633723f},{0.0196996f,0.887205f,-0.460954f},{-0.00598953f,0.836441f,-0.548024f}, -{-0.0213968f,0.757067f,-0.652987f},{-0.00143753f,0.690355f,-0.72347f},{-0.0019361f,0.639495f,-0.768793f}, -{-0.00192568f,0.582949f,-0.812506f},{0.0273509f,0.473471f,-0.880385f},{-0.02277f,0.363068f,-0.931484f}, -{0.0540861f,-0.998258f,0.0235792f},{0.0645095f,0.441878f,-0.894753f},{0.0700004f,0.944914f,-0.319746f}, -{0.0117661f,0.405937f,-0.913825f},{0.0176947f,0.515517f,-0.856697f},{0.680651f,0.415756f,-0.60321f}, -{0.130425f,-0.961724f,0.240989f},{0.0967715f,-0.959725f,0.263748f},{0.0194044f,-0.999714f,-0.0139699f}, -{0.133409f,0.926465f,-0.351945f},{0.117102f,0.858963f,-0.498468f},{0.103475f,0.763646f,-0.637289f}, -{0.111402f,0.690044f,-0.715143f},{0.101804f,0.595361f,-0.796982f},{0.128685f,0.522187f,-0.843067f}, -{0.0402739f,0.41697f,-0.908027f},{0.399144f,-0.91055f,-0.107621f},{-0.435934f,-0.825475f,-0.358542f}, -{-0.109148f,0.471589f,-0.875037f},{-0.0636207f,0.541546f,-0.83826f},{0.16092f,0.918152f,-0.362079f}, -{0.132701f,0.495073f,-0.858657f},{-0.45753f,0.383971f,-0.802018f},{0.599068f,0.532026f,-0.598386f}, -{0.18527f,0.936653f,-0.297248f},{0.206933f,0.904728f,-0.372352f},{0.249515f,0.825249f,-0.506662f}, -{0.271295f,0.741368f,-0.613818f},{0.310646f,0.614839f,-0.724894f},{0.241053f,0.565042f,-0.789063f}, -{0.241853f,0.535239f,-0.809337f},{0.176168f,0.543811f,-0.820509f},{0.0455045f,0.57979f,-0.813494f}, -{-0.0771866f,0.507401f,-0.858246f},{0.440673f,-0.660931f,-0.607436f},{0.500526f,0.292855f,-0.814684f}, -{0.0414138f,-0.998864f,0.0235917f},{-0.0509805f,-0.997048f,0.0574192f},{-0.415864f,0.575909f,-0.703836f}, -{0.338532f,0.763192f,-0.550395f},{0.35893f,0.71351f,-0.601724f},{0.385059f,0.664926f,-0.640003f}, -{0.328951f,0.645277f,-0.689499f},{0.221126f,0.632886f,-0.741996f},{0.108873f,0.668475f,-0.735723f}, -{0.0340051f,0.683036f,-0.729593f},{-0.00556542f,0.544468f,-0.838763f},{-0.00862487f,-0.999916f,-0.00968689f}, -{-0.522209f,-0.433801f,-0.734244f},{-0.171697f,-0.631512f,-0.756117f},{0.133674f,0.312652f,-0.940415f}, -{-0.497572f,0.211301f,-0.841293f},{-0.362949f,0.167034f,-0.916716f},{0.0326084f,-0.00788205f,-0.999437f}, -{0.0178542f,-0.0277267f,-0.999456f},{-0.47475f,0.365741f,-0.800529f},{-0.473612f,0.341419f,-0.811865f}, -{-0.461667f,0.388046f,-0.797674f},{-0.424027f,0.360027f,-0.831012f},{-0.432038f,0.35578f,-0.828712f}, -{-0.431902f,0.351066f,-0.830791f},{-0.456517f,0.110316f,-0.882849f},{-0.361814f,0.114788f,-0.925156f}, -{-0.228224f,-0.316424f,-0.920755f},{-0.148414f,-0.339823f,-0.928706f},{-0.261238f,-0.215537f,-0.940903f}, -{-0.328999f,-0.10573f,-0.938393f},{-0.451714f,0.350036f,-0.820627f},{-0.347968f,0.0215898f,-0.937258f}, -{-0.309485f,-0.0149301f,-0.950787f},{0.463137f,0.067709f,-0.883696f},{-0.241012f,-0.400193f,-0.884171f}, -{-0.0357501f,-0.449568f,-0.892531f},{-0.0140368f,-0.988301f,-0.151869f},{0.0435599f,-0.988519f,-0.144679f}, -{-0.534633f,0.0470525f,-0.843773f},{-0.478002f,-0.0821379f,-0.87451f},{-0.401819f,0.0454912f,-0.914588f}, -{-0.357866f,-0.0533086f,-0.93225f},{-0.451596f,0.267947f,-0.851038f},{-0.360766f,0.261536f,-0.895236f}, -{0.410366f,-0.166006f,-0.896684f},{-0.513599f,0.162015f,-0.842596f},{0.3566f,-0.711176f,0.605859f}, -{-0.120089f,0.322861f,-0.938797f},{-0.248431f,0.441079f,-0.862399f},{-0.175283f,0.367227f,-0.913466f}, -{-0.450494f,0.0551368f,-0.891075f},{-0.42014f,0.147097f,-0.895458f},{-0.557979f,0.0600499f,-0.82768f}, -{0.366826f,-0.521694f,-0.770243f},{0.365232f,-0.318742f,-0.874648f},{0.363532f,-0.145989f,-0.920072f}, -{0.20774f,0.0420311f,-0.977281f},{0.0751624f,0.217239f,-0.97322f},{-0.321394f,-0.326397f,-0.888915f}, -{-0.393182f,-0.385769f,-0.83462f},{-0.392275f,-0.212391f,-0.894992f},{-0.406007f,-0.534047f,-0.741588f}, -{0.315182f,-0.194353f,-0.928917f},{-0.0685202f,-0.992297f,-0.103202f},{-0.0284892f,0.181927f,-0.982899f}, -{-0.53002f,-0.387743f,-0.754145f},{0.357128f,-0.656742f,-0.664191f},{0.276723f,-0.451058f,-0.848511f}, -{0.175629f,-0.170525f,-0.969575f},{0.0425135f,0.164236f,-0.985505f},{-0.0143207f,0.317665f,-0.948095f}, -{0.0122568f,0.405787f,-0.913885f},{-0.47831f,-0.644036f,-0.597024f},{-0.475634f,-0.613191f,-0.63069f}, -{-0.348139f,-0.163224f,-0.923124f},{-0.411986f,-0.109838f,-0.904546f},{-0.581602f,-0.567347f,-0.582972f}, -{-0.517116f,-0.623338f,-0.586551f},{-0.105834f,-0.0441267f,-0.993404f},{0.0528876f,-0.29564f,-0.953834f}, -{-0.50201f,-0.715353f,-0.486061f},{-0.461784f,-0.701117f,-0.543314f},{-0.626786f,0.0544235f,-0.777288f}, -{-0.752071f,0.129508f,-0.646232f},{-0.263653f,-0.0708322f,-0.962014f},{0.438893f,-0.660336f,-0.609368f}, -{0.217609f,-0.414783f,-0.883516f},{0.147652f,-0.530349f,-0.834822f},{-0.338162f,0.160716f,-0.927263f}, -{0.307363f,0.120576f,-0.943922f},{0.0490676f,0.223205f,-0.973536f},{-0.403132f,0.0502257f,-0.913763f}, -{-0.404395f,0.167715f,-0.899075f},{-0.273011f,0.356315f,-0.893591f},{0.00575582f,0.31997f,-0.94741f}, -{-0.154191f,0.0769798f,-0.985038f},{-0.0460386f,-0.00195168f,-0.998938f},{-0.417116f,-0.198723f,-0.886862f}, -{-0.529885f,-0.00125872f,-0.848068f},{-0.196126f,-0.974976f,0.104673f},{0.287061f,-0.528227f,-0.799107f}, -{0.162375f,-0.468503f,-0.868412f},{0.11783f,-0.339373f,-0.933243f},{0.00841994f,-0.0591842f,-0.998212f}, -{0.0947982f,0.174364f,-0.980107f},{0.351317f,0.0632065f,-0.93412f},{0.024536f,0.157757f,-0.987173f}, -{0.10294f,-0.457501f,-0.88323f},{0.122128f,-0.417088f,-0.900623f},{0.102582f,-0.465494f,-0.879086f}, -{0.167471f,-0.388124f,-0.906263f},{-0.0613336f,0.143051f,-0.987813f},{-0.29684f,0.289434f,-0.910008f}, -{-0.152998f,-0.0536475f,-0.986769f},{-0.519057f,-0.225463f,-0.824467f},{-0.145503f,0.163285f,-0.97579f}, -{-0.499829f,-0.705746f,-0.50209f},{-0.0914336f,-0.843831f,0.528762f},{0.0807299f,-0.328036f,-0.94121f}, -{-0.806231f,0.368991f,-0.462426f},{0.330734f,-0.580551f,-0.744027f},{0.583488f,0.141328f,-0.79973f}, -{-0.320216f,0.320748f,-0.891394f},{-0.412065f,0.356592f,-0.838478f},{-0.491405f,0.167616f,-0.854649f}, -{0.199711f,-0.705233f,-0.680266f},{0.146874f,-0.613057f,-0.776267f},{0.227035f,-0.450278f,-0.863542f}, -{0.122738f,-0.209788f,-0.970012f},{0.112813f,-0.0401108f,-0.992806f},{-0.203394f,-0.724243f,-0.658865f}, -{0.0196344f,-0.138169f,-0.990214f},{0.0655215f,-0.302574f,-0.950871f},{-0.0604679f,-0.00683363f,-0.998147f}, -{-0.221203f,-0.308623f,-0.925106f},{-0.226549f,-0.188684f,-0.955549f},{-0.433699f,0.367571f,-0.822677f}, -{0.191396f,-0.331647f,-0.923784f},{0.172897f,-0.271091f,-0.946898f},{-0.214423f,0.265942f,-0.939839f}, -{-0.172306f,0.202648f,-0.963973f},{-0.235133f,0.261565f,-0.936107f},{-0.159272f,0.192711f,-0.968243f}, -{-0.366161f,0.368952f,-0.854284f},{-0.416712f,0.28621f,-0.862806f},{-0.322311f,0.235694f,-0.916823f}, -{-0.108791f,0.0573097f,-0.992411f},{-0.23577f,-0.0337679f,-0.971222f},{-0.106879f,-0.064881f,-0.992153f}, -{-0.02569f,-0.335202f,-0.941796f},{-0.577113f,0.0728067f,-0.813412f},{-0.46981f,-0.468811f,-0.747994f}, -{-0.0801707f,-0.0791455f,-0.993634f},{-0.0991288f,-0.0698804f,-0.992618f},{0.166914f,-0.585763f,-0.793109f}, -{0.124322f,-0.458685f,-0.879859f},{0.0818947f,-0.254017f,-0.963726f},{0.203168f,-0.284487f,-0.936905f}, -{-0.378567f,0.271463f,-0.88487f},{-0.415195f,-0.628034f,-0.658169f},{-0.332351f,-0.620089f,-0.710656f}, -{-0.254031f,-0.712061f,-0.654551f},{-0.376504f,-0.711748f,-0.593009f},{-0.37075f,-0.39528f,-0.840416f}, -{0.0194336f,-0.0314723f,-0.999316f},{-0.301979f,-0.494561f,-0.814996f},{-0.294411f,-0.766308f,-0.571046f}, -{-0.106275f,0.131253f,-0.985636f},{0.0212621f,0.103805f,-0.99437f},{-0.0399005f,0.257478f,-0.96546f}, -{-0.113825f,0.251492f,-0.961143f},{-0.176062f,-0.583995f,-0.792434f},{-0.427957f,0.302497f,-0.851674f}, -{-0.289149f,-0.76017f,-0.581838f},{-0.366384f,-0.718341f,-0.591396f},{-0.546571f,-0.25365f,-0.798074f}, -{-0.39526f,-0.289349f,-0.871807f},{-0.403287f,-0.227755f,-0.886277f},{-0.489823f,-0.266145f,-0.830205f}, -{0.480623f,0.0180865f,-0.876741f},{0.00374673f,0.181041f,-0.983468f},{-0.38202f,-0.586857f,-0.713905f}, -{0.158242f,-0.536317f,-0.829049f},{0.0826999f,-0.411103f,-0.90783f},{0.233891f,-0.411181f,-0.881037f}, -{-0.254003f,-0.718621f,-0.647353f},{-0.0677313f,-0.248287f,-0.966316f},{-0.355975f,-0.0219649f,-0.934237f}, -{-0.244444f,-0.773455f,-0.58482f},{0.0347758f,0.0843179f,-0.995832f},{-0.0978183f,-0.384104f,-0.918094f}, -{0.0148392f,-0.349137f,-0.936954f},{-0.234185f,-0.31982f,-0.918081f},{-0.296005f,-0.312823f,-0.902509f}, -{-0.149664f,-0.762597f,-0.629322f},{0.298053f,-0.548791f,-0.78102f},{0.0532691f,0.187605f,-0.980799f}, -{-0.033569f,-0.249132f,-0.967888f},{-0.389285f,-0.307134f,-0.868404f},{-0.314508f,-0.334821f,-0.888245f}, -{-0.262679f,-0.400729f,-0.877733f},{-0.211967f,-0.335246f,-0.917976f},{-0.0722862f,-0.746754f,-0.661161f}, -{-0.209982f,-0.10056f,-0.97252f},{-0.61991f,-0.237285f,-0.747935f},{-0.486362f,-0.715322f,-0.501763f}, -{0.591243f,0.0442269f,-0.80528f},{-0.1822f,-0.425288f,-0.886529f},{-0.12628f,-0.280556f,-0.951494f}, -{-0.293787f,-0.25386f,-0.921544f},{-0.45819f,-0.694794f,-0.554367f},{-0.265962f,-0.2459f,-0.932093f}, -{0.552218f,-0.296904f,-0.77904f},{0.616997f,-0.208742f,-0.758776f},{0.532225f,-0.0320237f,-0.845997f}, -{0.518351f,0.0671195f,-0.85253f},{0.0783362f,-0.99421f,0.0735581f},{0.118141f,-0.991686f,0.051016f}, -{-0.162888f,-0.271886f,-0.948444f},{-0.293131f,0.331585f,-0.896731f},{0.598523f,-0.060331f,-0.798831f}, -{-0.309627f,-0.348615f,-0.884646f},{0.466335f,-0.598079f,-0.651792f},{0.663968f,-0.130472f,-0.73629f}, -{-0.426057f,-0.246834f,-0.870373f},{-0.320111f,-0.413622f,-0.852318f},{-0.275072f,-0.289065f,-0.916939f}, -{-0.121374f,-0.00139793f,-0.992606f},{-0.104278f,0.0295214f,-0.99411f},{-0.441475f,0.425385f,-0.79003f}, -{-0.354671f,-0.736701f,0.575743f},{-0.307603f,-0.735628f,0.603517f},{-0.14795f,-0.963575f,0.222786f}, -{-0.247067f,-0.209033f,-0.946184f},{0.478306f,-0.542814f,-0.690346f},{0.573596f,-0.413039f,-0.70738f}, -{0.0413827f,0.111407f,-0.992913f},{0.697966f,-0.174554f,-0.694531f},{0.0884714f,-0.995767f,-0.0249182f}, -{0.66496f,-0.0925447f,-0.741123f},{0.530716f,0.0144358f,-0.847427f},{0.424264f,0.0554731f,-0.903838f}, -{0.43501f,0.059857f,-0.898434f},{-0.0618874f,0.058737f,-0.996353f},{-0.153908f,0.138131f,-0.978382f}, -{-0.433835f,-0.75467f,-0.492201f},{0.311733f,0.130743f,-0.941132f},{0.430592f,-0.545578f,-0.718982f}, -{0.563177f,-0.165806f,-0.809531f},{0.528658f,-0.0254374f,-0.848454f},{0.463703f,0.116946f,-0.878239f}, -{0.408836f,0.0773136f,-0.909327f},{0.432356f,-0.362167f,-0.825774f},{-0.0226638f,-0.365996f,-0.93034f}, -{-0.0502062f,-0.277693f,-0.959357f},{-0.0211832f,-0.252399f,-0.967391f},{-0.500045f,-0.0644449f,-0.863598f}, -{-0.551538f,-0.0144755f,-0.834024f},{-0.34341f,-0.0575228f,-0.937423f},{-0.312247f,-0.0818833f,-0.946465f}, -{-0.182016f,-0.0683459f,-0.980918f},{-0.613054f,-0.35332f,-0.706633f},{-0.169602f,0.137656f,-0.975851f}, -{0.501381f,-0.187936f,-0.844569f},{0.410831f,0.034876f,-0.911044f},{0.556872f,-0.242513f,-0.794406f}, -{0.430962f,0.0348824f,-0.901696f},{0.466346f,0.0318584f,-0.884029f},{0.476106f,0.0373906f,-0.878593f}, -{-0.384256f,-0.333572f,0.860858f},{0.409459f,-0.402863f,-0.818563f},{0.347749f,-0.57205f,-0.742852f}, -{0.3489f,-0.310127f,-0.884359f},{0.330387f,0.0285511f,-0.943413f},{0.386763f,0.0233366f,-0.921884f}, -{0.434189f,-0.216166f,-0.874501f},{0.403344f,-0.155649f,-0.901713f},{0.397898f,-0.0907694f,-0.912928f}, -{0.444065f,0.110133f,-0.8892f},{0.436053f,0.102098f,-0.89411f},{0.450432f,0.0181053f,-0.892627f}, -{0.459103f,0.0236729f,-0.888068f},{-0.400662f,-0.908358f,-0.119813f},{-0.25029f,0.0413912f,-0.967286f}, -{-0.285704f,0.0674397f,-0.955942f},{-0.250687f,0.0544366f,-0.966537f},{0.414188f,-0.541434f,-0.731641f}, -{0.263469f,-0.554858f,-0.789124f},{0.478863f,-0.182555f,-0.858699f},{0.499925f,-0.229435f,-0.835126f}, -{0.44019f,-0.441255f,-0.782001f},{0.273243f,-0.564661f,-0.778778f},{0.308691f,-0.458051f,-0.833606f}, -{0.258205f,-0.263016f,-0.929598f},{0.266399f,-0.0140867f,-0.96376f},{0.325926f,-0.0461329f,-0.944269f}, -{0.350478f,-0.219007f,-0.910605f},{0.354576f,-0.145572f,-0.923626f},{0.370918f,-0.170775f,-0.912828f}, -{0.36704f,-0.104143f,-0.924357f},{0.397743f,0.0893554f,-0.913135f},{0.412708f,0.16809f,-0.89522f}, -{0.461831f,0.0794305f,-0.883404f},{0.491097f,0.00610044f,-0.871083f},{0.469434f,0.0393794f,-0.882089f}, -{-0.318099f,0.1176f,-0.940736f},{0.397764f,-0.22051f,-0.890595f},{0.339251f,-0.140336f,-0.930169f}, -{0.415097f,0.104971f,-0.903701f},{0.448675f,0.0117692f,-0.893618f},{0.335919f,-0.319947f,-0.885885f}, -{0.31524f,-0.196916f,-0.928357f},{0.441539f,0.052358f,-0.895713f},{0.374789f,0.0703258f,-0.924439f}, -{0.291724f,-0.429357f,-0.854722f},{0.198636f,-0.335479f,-0.920868f},{0.241156f,-0.318072f,-0.916883f}, -{0.309921f,-0.504065f,-0.806144f},{0.342391f,-0.433425f,-0.833613f},{0.279045f,-0.345422f,-0.896001f}, -{0.282074f,-0.247687f,-0.926869f},{0.279043f,-0.0375461f,-0.959544f},{0.26891f,-0.0713013f,-0.960523f}, -{0.277696f,-0.230241f,-0.93267f},{0.282046f,-0.160948f,-0.945804f},{0.24807f,-0.11132f,-0.962325f}, -{0.324934f,0.0120236f,-0.94566f},{0.404792f,0.0859546f,-0.91036f},{0.421959f,-0.0172375f,-0.906451f}, -{0.405509f,0.0607184f,-0.912072f},{0.370021f,0.0255989f,-0.92867f},{-0.273495f,0.102608f,-0.956385f}, -{-0.214751f,-0.0217704f,-0.976426f},{0.0389195f,-0.998942f,-0.0245161f},{0.301224f,-0.125571f,-0.945249f}, -{0.247679f,0.00834955f,-0.968806f},{-0.582805f,-0.287323f,-0.760121f},{0.153013f,-0.257305f,-0.954139f}, -{0.0623255f,-0.321166f,-0.94497f},{0.118664f,-0.440027f,-0.890109f},{0.186817f,-0.485921f,-0.853804f}, -{0.256389f,-0.395781f,-0.881829f},{0.242238f,-0.192341f,-0.95096f},{0.215539f,-0.00689006f,-0.976471f}, -{0.250731f,-0.139028f,-0.958021f},{0.261066f,-0.213902f,-0.941324f},{0.256085f,-0.0887571f,-0.962571f}, -{0.230182f,-0.188229f,-0.95477f},{0.34271f,-0.116949f,-0.932133f},{0.483111f,0.0179296f,-0.875376f}, -{0.506741f,0.0262392f,-0.861699f},{0.420864f,0.0887582f,-0.902771f},{0.387847f,0.0724752f,-0.91887f}, -{0.373944f,0.0811103f,-0.923898f},{-0.463775f,-0.155676f,-0.872169f},{0.205632f,-0.363896f,-0.908457f}, -{0.414665f,-0.202407f,-0.887177f},{0.51326f,-0.0593179f,-0.856181f},{0.477654f,0.0737985f,-0.875443f}, -{0.456738f,0.120889f,-0.881349f},{-0.124059f,-0.0143952f,-0.99217f},{0.159493f,-0.171302f,-0.972223f}, -{0.0728596f,-0.286864f,-0.955197f},{0.14769f,-0.548362f,-0.823096f},{0.222186f,-0.501618f,-0.83607f}, -{0.140263f,-0.412915f,-0.899904f},{0.0834118f,-0.318639f,-0.944199f},{0.132404f,-0.165691f,-0.977249f}, -{0.211184f,-0.0144253f,-0.97734f},{0.247741f,-0.159525f,-0.955603f},{0.282763f,-0.251771f,-0.925557f}, -{0.309661f,-0.0746755f,-0.94791f},{0.352027f,-0.173439f,-0.91978f},{0.314486f,-0.260742f,-0.91275f}, -{0.309214f,-0.0476167f,-0.9498f},{0.297153f,0.0785501f,-0.951593f},{0.341974f,0.140095f,-0.929208f}, -{0.361015f,0.0969296f,-0.927509f},{0.319776f,0.0989742f,-0.94231f},{0.13153f,0.190633f,-0.97281f}, -{0.0931867f,0.114043f,-0.989096f},{0.223886f,-0.0861198f,-0.970803f},{0.168265f,-0.0797806f,-0.982508f}, -{0.312417f,-0.196147f,-0.929474f},{0.326859f,-0.0967924f,-0.940103f},{-0.359361f,0.0730781f,-0.930333f}, -{0.158572f,-0.0861268f,-0.983584f},{0.109713f,-0.282932f,-0.952845f},{0.138901f,-0.618307f,-0.773565f}, -{0.227161f,-0.643871f,-0.730635f},{0.27837f,-0.418527f,-0.864492f},{0.262264f,-0.228899f,-0.937456f}, -{0.15929f,-0.233517f,-0.959216f},{0.095923f,-0.265918f,-0.959211f},{0.273692f,-0.203507f,-0.940041f}, -{0.395505f,-0.250086f,-0.883761f},{0.321622f,-0.368589f,-0.872182f},{0.370407f,-0.233665f,-0.898999f}, -{0.268743f,-0.0340914f,-0.962608f},{0.190561f,-0.22345f,-0.955906f},{0.199725f,-0.252784f,-0.946684f}, -{0.219432f,-0.0917187f,-0.971307f},{0.249687f,0.0682264f,-0.96592f},{0.251793f,0.115452f,-0.96087f}, -{0.311541f,0.0876862f,-0.946178f},{0.277111f,0.139705f,-0.950627f},{0.335169f,-0.213682f,-0.917606f}, -{0.413841f,-0.243567f,-0.877161f},{-0.508553f,0.375086f,-0.775038f},{0.158959f,-0.33245f,-0.929628f}, -{0.149185f,-0.0420049f,-0.987917f},{0.119061f,-0.346273f,-0.930548f},{0.104111f,-0.669696f,-0.735301f}, -{0.144693f,-0.679733f,-0.719046f},{0.165334f,-0.354326f,-0.92039f},{0.190832f,-0.188236f,-0.963405f}, -{0.213776f,-0.300113f,-0.929641f},{0.184987f,-0.345558f,-0.919984f},{0.214224f,-0.303985f,-0.928279f}, -{0.256223f,-0.283327f,-0.924162f},{0.23446f,-0.110913f,-0.965778f},{0.222045f,0.0280285f,-0.974634f}, -{0.219076f,-0.154819f,-0.963347f},{0.20427f,-0.26067f,-0.94357f},{0.200559f,-0.182029f,-0.962622f}, -{0.20429f,-0.0842184f,-0.975281f},{0.25253f,0.0505589f,-0.966267f},{0.332164f,0.0709402f,-0.94055f}, -{0.441632f,0.0665385f,-0.894726f},{-0.351427f,0.362608f,-0.863142f},{0.247619f,0.00508906f,-0.968844f}, -{0.389279f,-0.0348041f,-0.920462f},{0.443012f,0.0647495f,-0.894174f},{0.109221f,-0.993986f,-0.00792956f}, -{0.203599f,-0.978122f,-0.0427252f},{-0.238324f,0.306725f,-0.921478f},{0.1839f,-0.223995f,-0.957082f}, -{0.409799f,0.203448f,-0.889198f},{0.102517f,-0.279304f,-0.954714f},{0.13668f,-0.0744809f,-0.987811f}, -{0.186384f,-0.311169f,-0.931899f},{0.174915f,-0.699354f,-0.693043f},{0.187465f,-0.654656f,-0.732313f}, -{0.136217f,-0.352045f,-0.926018f},{0.203846f,-0.210139f,-0.956184f},{0.244663f,-0.312758f,-0.917781f}, -{0.108868f,-0.329495f,-0.93786f},{0.203825f,-0.317197f,-0.926197f},{0.204169f,-0.301991f,-0.931191f}, -{0.20256f,-0.0767667f,-0.976256f},{0.150877f,-0.00640872f,-0.988532f},{0.230954f,-0.186151f,-0.954991f}, -{0.155388f,-0.298392f,-0.941709f},{0.1697f,-0.192309f,-0.96655f},{0.18909f,-0.116842f,-0.974984f}, -{0.282293f,-0.0916951f,-0.954936f},{0.331828f,-0.0941502f,-0.93863f},{0.308796f,0.0477273f,-0.94993f}, -{-0.408249f,-0.720043f,-0.561134f},{0.268533f,-0.263567f,-0.926511f},{0.091933f,-0.285835f,-0.953859f}, -{0.118349f,-0.0897115f,-0.988911f},{0.145821f,-0.461532f,-0.875057f},{0.126957f,-0.694749f,-0.707958f}, -{0.114736f,-0.552027f,-0.825894f},{0.0458875f,-0.363814f,-0.930341f},{0.17179f,-0.349481f,-0.92106f}, -{0.241402f,-0.314832f,-0.917936f},{0.187544f,-0.248817f,-0.95022f},{0.154279f,-0.317391f,-0.935661f}, -{0.171403f,-0.273071f,-0.946601f},{0.218886f,-0.0469395f,-0.974621f},{0.23039f,-0.0452667f,-0.972045f}, -{0.334638f,-0.235148f,-0.912537f},{0.238913f,-0.201569f,-0.94989f},{0.201881f,-0.159874f,-0.966273f}, -{0.25613f,-0.189546f,-0.947877f},{0.257277f,-0.113556f,-0.959642f},{0.220836f,-0.0757441f,-0.972365f}, -{0.162348f,0.0763838f,-0.983773f},{0.189445f,0.265051f,-0.945441f},{0.136332f,-0.0751422f,-0.987809f}, -{0.095285f,-0.27107f,-0.957832f},{0.101441f,-0.104338f,-0.989355f},{0.0567444f,-0.445659f,-0.893402f}, -{-0.000193409f,-0.676142f,-0.736771f},{0.0109145f,-0.465338f,-0.885066f},{0.0534069f,-0.389789f,-0.919354f}, -{0.0916234f,-0.502817f,-0.859523f},{0.160832f,-0.40552f,-0.899826f},{0.159299f,-0.195571f,-0.967665f}, -{0.132088f,-0.240001f,-0.961744f},{0.0841788f,-0.31464f,-0.945471f},{0.133508f,-0.134741f,-0.981845f}, -{0.17912f,-0.0888595f,-0.979806f},{0.208443f,-0.195479f,-0.9583f},{0.259759f,-0.212322f,-0.942043f}, -{0.226557f,-0.164269f,-0.960046f},{0.138192f,-0.0514023f,-0.989071f},{0.093177f,-0.0381038f,-0.99492f}, -{0.0672073f,-0.120768f,-0.990403f},{0.176091f,0.262835f,-0.948636f},{0.0965525f,-0.994059f,-0.0502461f}, -{-0.0002735f,-0.997933f,0.0642685f},{-0.00876615f,-0.997232f,0.073829f},{-0.0215592f,-0.996239f,0.083924f}, -{0.198791f,-0.12984f,-0.971403f},{-0.613472f,-0.357682f,-0.704071f},{0.145189f,-0.0897178f,-0.985328f}, -{0.0700315f,-0.316691f,-0.94594f},{-0.0368761f,-0.677374f,-0.734714f},{-0.0508762f,-0.574966f,-0.816594f}, -{0.00461845f,-0.453498f,-0.891245f},{-0.00453975f,-0.52994f,-0.848023f},{0.00720484f,-0.395835f,-0.918294f}, -{0.0863324f,-0.18462f,-0.979011f},{0.129269f,-0.278945f,-0.951567f},{0.227027f,-0.32294f,-0.918786f}, -{0.265326f,-0.189893f,-0.945274f},{0.255582f,-0.118537f,-0.959493f},{0.287931f,-0.233008f,-0.928872f}, -{0.268068f,-0.185136f,-0.945444f},{0.196184f,-0.0750087f,-0.977694f},{0.0985332f,0.00685483f,-0.99511f}, -{0.162505f,-0.0579435f,-0.985005f},{0.151121f,-0.336992f,-0.9293f},{0.208861f,-0.129676f,-0.96931f}, -{-0.333093f,-0.877541f,-0.344923f},{-0.855978f,-0.334846f,-0.393928f},{0.342906f,-0.296651f,-0.891299f}, -{0.278857f,-0.0874291f,-0.956345f},{0.191219f,-0.979642f,0.0611353f},{-0.0449772f,-0.984866f,-0.167378f}, -{0.257991f,-0.139881f,-0.955967f},{0.140787f,0.0217321f,-0.989801f},{0.171542f,-0.0350602f,-0.984553f}, -{0.191266f,-0.111461f,-0.975189f},{0.134866f,0.0460265f,-0.989794f},{-0.474359f,-0.186298f,-0.860393f}, -{-0.0743062f,-0.679009f,0.730359f},{0.17509f,-0.179612f,-0.968031f},{0.18737f,-0.182224f,-0.965239f}, -{0.0634384f,-0.553884f,-0.830173f},{-0.0614459f,-0.573997f,-0.816549f},{-0.0548518f,-0.463718f,-0.884283f}, -{-0.0542619f,-0.46565f,-0.883304f},{-0.0886851f,-0.485649f,-0.869643f},{-0.031275f,-0.343695f,-0.938561f}, -{0.109803f,-0.424685f,-0.898658f},{0.157277f,-0.316265f,-0.935543f},{0.181625f,-0.149741f,-0.9719f}, -{0.250089f,-0.202109f,-0.946893f},{0.224643f,-0.195189f,-0.954692f},{0.189713f,-0.127546f,-0.97352f}, -{0.120296f,-0.0534839f,-0.991296f},{-0.457533f,-0.498441f,-0.736356f},{-0.0184083f,-0.864544f,0.502219f}, -{0.0450966f,-0.998981f,0.00189569f},{0.0182236f,-0.999668f,-0.0182031f},{-0.308813f,-0.950316f,0.0391569f}, -{0.249777f,-0.228349f,-0.940993f},{0.0407418f,-0.378347f,-0.924767f},{-0.164432f,0.40839f,-0.897875f}, -{-0.314956f,0.0949649f,-0.944343f},{-0.541635f,-0.409308f,-0.734233f},{-0.159007f,-0.282733f,-0.945927f}, -{-0.0413941f,-0.283658f,-0.958032f},{0.0651972f,-0.232411f,-0.97043f},{0.0368408f,-0.149265f,-0.988111f}, -{0.0695572f,-0.163889f,-0.984024f},{0.0598061f,-0.11698f,-0.991332f},{0.0909766f,0.0319594f,-0.99534f}, -{0.102819f,-0.0478997f,-0.993546f},{0.0437593f,-0.077876f,-0.996002f},{0.0155373f,0.0713922f,-0.997327f}, -{-0.348753f,0.139677f,-0.926748f},{0.23431f,-0.891494f,0.387733f},{-0.553502f,-0.308364f,-0.773659f}, -{-0.616738f,-0.173523f,-0.767805f},{-0.380602f,0.11266f,-0.917851f},{0.22381f,-0.132088f,-0.965641f}, -{-0.0792494f,-0.411621f,-0.907903f},{-0.193881f,-0.479576f,-0.855813f},{-0.197234f,-0.449732f,-0.871114f}, -{-0.0877846f,-0.449835f,-0.888787f},{-0.185332f,-0.415287f,-0.890612f},{-0.207097f,-0.395328f,-0.894889f}, -{-0.118296f,-0.403333f,-0.907374f},{0.00189189f,-0.999994f,0.00306737f},{-0.457134f,-0.420451f,-0.783741f}, -{-0.635975f,-0.131724f,-0.760385f},{-0.269536f,-0.956413f,-0.112361f},{-0.288356f,-0.418766f,-0.861096f}, -{-0.36962f,-0.401844f,-0.837797f},{-0.377607f,-0.407334f,-0.83156f},{-0.348602f,-0.404377f,-0.845551f}, -{-0.248868f,-0.324939f,-0.912403f},{-0.279433f,-0.340647f,-0.897707f},{-0.214021f,-0.324631f,-0.921309f}, -{-0.112611f,-0.190842f,-0.97514f},{-0.0751645f,-0.194146f,-0.978089f},{-0.0507894f,-0.146409f,-0.987919f}, -{-0.0101824f,-0.0328948f,-0.999407f},{0.0159028f,-0.0202976f,-0.999668f},{-0.0595336f,0.00126352f,-0.998226f}, -{-0.0968821f,0.0913483f,-0.991095f},{-0.139114f,0.286357f,-0.94797f},{-0.921666f,-0.384264f,-0.0535948f}, -{0.0780428f,-0.996611f,-0.0260006f},{-0.283515f,-0.477441f,-0.831667f},{-0.331705f,-0.449341f,-0.829496f}, -{-0.385828f,-0.392684f,-0.834827f},{-0.517772f,-0.225955f,-0.82514f},{-0.55825f,-0.15923f,-0.81425f}, -{-0.592719f,-0.108802f,-0.798027f},{0.230233f,-0.0502752f,-0.971836f},{0.119686f,0.0323427f,-0.992285f}, -{-0.158251f,-0.204838f,-0.965918f},{-0.250495f,-0.436161f,-0.864301f},{-0.453561f,-0.372427f,-0.809679f}, -{-0.371939f,-0.334884f,-0.865745f},{-0.351896f,-0.847199f,0.398023f},{-0.0471725f,-0.995583f,0.0811798f}, -{-0.178855f,0.347392f,-0.920505f},{-0.0643786f,-0.901551f,0.427855f},{0.165713f,-0.0165558f,-0.986035f}, -{0.093963f,0.185729f,-0.978098f},{-0.085456f,0.0715648f,-0.993768f},{-0.383911f,-0.292464f,-0.875829f}, -{-0.429875f,-0.347318f,-0.833413f},{-0.481882f,-0.391585f,-0.783869f},{-0.433764f,-0.41101f,-0.801823f}, -{-0.492389f,-0.348149f,-0.797713f},{-0.48093f,-0.351514f,-0.803209f},{-0.369498f,-0.397625f,-0.83986f}, -{-0.434404f,-0.368349f,-0.821956f},{-0.328686f,-0.300934f,-0.895212f},{-0.272589f,-0.176483f,-0.945806f}, -{-0.194385f,-0.0887588f,-0.976901f},{-0.213207f,0.0173482f,-0.976853f},{-0.203672f,0.0458605f,-0.977965f}, -{-0.212849f,0.138942f,-0.967156f},{-0.201355f,0.286949f,-0.936545f},{-0.170263f,-0.958942f,0.226804f}, -{0.0336438f,-0.997239f,0.066202f},{0.476486f,-0.877819f,0.0489346f},{-0.175802f,-0.482737f,-0.857938f}, -{-0.240114f,-0.406166f,-0.881688f},{-0.317766f,-0.319497f,-0.892719f},{-0.450952f,-0.15198f,-0.879514f}, -{-0.511979f,-0.296898f,-0.806058f},{-0.508813f,-0.393695f,-0.76558f},{0.112715f,0.225438f,-0.967715f}, -{-0.598289f,-0.679083f,-0.42532f},{-0.235634f,-0.951767f,-0.196511f},{-0.476286f,-0.803592f,-0.356919f}, -{-0.362864f,-0.223333f,-0.904683f},{-0.420486f,-0.21012f,-0.882633f},{-0.279394f,0.219675f,-0.93471f}, -{-0.0115128f,-0.999142f,0.0397777f},{-0.000605356f,-0.997024f,0.077089f},{-0.0082265f,-0.999963f,0.00249397f}, -{-0.0261943f,-0.998588f,-0.0462172f},{0.0705986f,0.26835f,-0.960731f},{-0.0994969f,0.328516f,-0.939243f}, -{-0.393652f,-0.0258967f,-0.918895f},{-0.523073f,-0.360866f,-0.772121f},{-0.533142f,-0.277189f,-0.799328f}, -{-0.483933f,-0.15738f,-0.860837f},{-0.436994f,-0.0964095f,-0.894283f},{-0.363398f,0.0677128f,-0.92917f}, -{-0.473792f,-0.00518424f,-0.880622f},{-0.390418f,0.0204487f,-0.920411f},{-0.355069f,0.15848f,-0.921309f}, -{-0.18989f,-0.469618f,-0.862207f},{0.0387132f,-0.998767f,0.031081f},{-0.22247f,-0.972103f,-0.0743105f}, -{-0.0842592f,-0.983493f,0.160133f},{-0.460062f,0.220102f,-0.860173f},{0.0931486f,-0.955462f,0.280027f}, -{0.0532537f,-0.998488f,-0.0136487f},{-0.203823f,-0.966645f,-0.155093f},{0.0599921f,-0.998181f,0.00590705f}, -{0.0167739f,-0.999717f,-0.0168777f},{0.111033f,-0.986245f,0.122446f},{0.0914944f,-0.881277f,0.463658f}, -{0.0355514f,-0.998636f,0.0382277f},{0.437185f,-0.872861f,0.216754f},{0.422546f,-0.855048f,0.300579f}, -{0.42756f,-0.880253f,0.205784f},{-0.322485f,-0.87414f,-0.363155f},{-0.00853661f,-0.991127f,-0.132642f}, -{-0.145474f,-0.888027f,-0.436171f},{-0.493414f,-0.735429f,0.464422f},{-0.335755f,-0.760172f,0.556243f}, -{-0.493879f,-0.817461f,0.296379f},{0.261385f,-0.811544f,0.522565f},{0.032797f,-0.995392f,0.090103f}, -{0.114284f,-0.915866f,0.384874f},{-0.423877f,-0.903915f,-0.0571463f},{-0.595218f,-0.723646f,0.34936f}, -{0.21114f,-0.821121f,0.530264f},{-0.00545385f,-0.999841f,-0.0169869f},{-0.0187241f,-0.995127f,-0.0968045f}, -{0.0192527f,-0.997316f,0.0706471f},{0.0217938f,-0.996704f,0.0781471f},{0.0252164f,-0.997289f,0.0691295f}, -{0.0388396f,-0.996338f,0.076169f},{-0.0958933f,-0.67412f,0.732371f},{-0.0528083f,-0.6354f,0.770375f}, -{-0.277386f,-0.89954f,0.337469f},{-0.415031f,-0.742829f,0.525313f},{0.0269597f,-0.998768f,0.0416694f}, -{0.0208328f,-0.998511f,0.0504246f},{0.0197848f,-0.999201f,0.0347204f},{0.441117f,-0.887602f,0.132582f}, -{0.220524f,-0.949395f,-0.223647f},{0.254325f,-0.918615f,-0.302432f},{-0.411947f,-0.905298f,-0.103613f}, -{0.0330157f,-0.999407f,0.00973608f},{0.0360647f,-0.999324f,0.00715541f},{0.0281243f,-0.999554f,0.0100368f}, -{0.0317264f,-0.999288f,-0.0204045f},{0.0207974f,-0.999648f,-0.0164574f},{0.390465f,-0.895183f,0.214907f}, -{0.068996f,-0.997368f,-0.0222887f},{0.00696426f,-0.999677f,0.0244445f},{0.113192f,-0.990527f,-0.0777368f}, -{0.0300783f,-0.997791f,-0.0592262f},{-0.101247f,-0.989736f,0.100855f},{-0.488221f,-0.872483f,-0.0203287f}, -{0.0192702f,-0.995406f,0.0937832f},{-0.00526962f,-0.999976f,0.004602f},{0.168434f,-0.985586f,-0.015808f}, -{-0.274512f,-0.959802f,0.0585097f},{-0.125707f,-0.991656f,-0.0285798f},{-0.440623f,-0.879403f,-0.180285f}, -{-0.508811f,-0.860146f,0.0355013f},{-0.295831f,-0.930916f,-0.214194f},{-0.133933f,-0.979029f,-0.153508f}, -{-0.504832f,-0.82857f,-0.242108f},{-0.199422f,-0.931407f,-0.304485f},{-0.0191935f,-0.999509f,-0.0247501f}, -{-0.00421024f,-0.997259f,-0.0738707f},{-0.456138f,-0.792245f,-0.405322f},{0.536549f,-0.617989f,0.574635f}, -{0.159432f,-0.964327f,0.211319f},{0.119699f,-0.987882f,0.098801f},{0.0753162f,-0.996306f,0.041258f}, -{0.0769156f,-0.995434f,0.056522f},{0.199961f,-0.972262f,0.121333f},{0.0324883f,-0.998411f,0.0460362f}, -{0.00150144f,-0.998946f,0.0458802f},{0.0212488f,-0.999318f,0.0302135f},{0.0380284f,-0.999242f,-0.00828349f}, -{0.0575431f,-0.998323f,0.00632132f},{-0.0275333f,-0.993345f,-0.111842f},{0.179328f,-0.983237f,-0.032964f}, -{0.134155f,-0.989332f,0.056792f},{-0.275129f,-0.942456f,0.189949f},{-0.434564f,-0.859275f,0.269818f}, -{-0.501779f,-0.819213f,0.277684f},{0.230928f,-0.92776f,0.293145f},{0.457012f,-0.817257f,0.351042f}, -{0.431776f,-0.889879f,0.147261f},{-0.00337819f,-0.99996f,0.00822642f},{-0.0679747f,-0.924453f,0.375188f}, -{0.421036f,-0.80576f,0.41651f},{0.261081f,-0.824249f,0.502445f},{-0.400411f,-0.863751f,0.30595f}, -{-0.00950766f,-0.998909f,0.0457252f},{0.108334f,-0.993724f,-0.0278652f},{0.119746f,-0.987411f,0.103343f}, -{0.246439f,-0.960619f,0.128374f},{-0.0457223f,-0.994671f,-0.0924088f},{0.254503f,-0.966784f,0.0235859f}, -{0.267335f,-0.961599f,0.0621237f},{0.139755f,-0.968292f,0.207074f},{-0.0581971f,-0.996083f,0.0665645f}, -{-0.100193f,-0.994413f,0.0332144f},{0.256728f,-0.860271f,0.440482f},{0.0205264f,-0.995617f,0.0912464f}, -{0.145715f,-0.989314f,-0.00497361f},{0.442628f,-0.725749f,0.526658f},{0.422509f,-0.759082f,0.495258f}, -{0.0250255f,-0.999672f,0.00538897f},{0.0226148f,-0.999695f,-0.0098717f},{-0.0512331f,-0.95503f,0.29205f}, -{0.0094949f,-0.999954f,0.00102734f},{-0.134225f,-0.965527f,-0.223029f},{-0.0342345f,-0.997022f,-0.0691043f}, -{0.0220392f,-0.999002f,-0.0388572f},{0.0200358f,-0.999289f,-0.031944f},{-0.160766f,-0.975433f,-0.150616f}, -{0.0182022f,-0.999505f,-0.0256645f},{0.157028f,-0.799171f,0.580231f},{0.348365f,-0.535744f,0.769168f}, -{-0.0879878f,-0.890344f,0.446705f},{-0.299798f,-0.892556f,0.336847f},{-0.485275f,-0.745735f,0.456494f}, -{-0.0470124f,-0.998452f,-0.0297115f},{0.0765366f,-0.877318f,0.473766f},{-0.0188492f,-0.998556f,-0.0503023f}, -{0.0232935f,-0.998163f,0.0559258f},{-0.0291428f,-0.923105f,0.383443f},{0.0719425f,-0.983426f,0.166427f}, -{-0.352676f,-0.869485f,0.345856f},{0.0223918f,-0.995144f,0.0958476f},{0.0235307f,-0.996475f,0.0805263f}, -{-0.0904133f,-0.98791f,0.125936f},{-0.669071f,-0.733856f,-0.117467f},{0.409527f,-0.911178f,-0.0452039f}, -{-0.477475f,-0.771456f,0.420563f},{0.0244424f,-0.995812f,0.0881007f},{0.0470478f,-0.997189f,0.0583127f}, -{-0.627038f,-0.766081f,0.141222f},{-0.641998f,-0.766623f,-0.0113149f},{-0.598191f,-0.738564f,-0.310951f}, -{-0.435507f,-0.821898f,-0.367176f},{-0.00972171f,-0.998838f,0.0472077f},{-0.433635f,-0.802921f,0.408997f}, -{0.0943655f,-0.869567f,0.484715f},{-0.113401f,-0.985403f,0.126968f},{-0.0507322f,-0.910702f,0.409937f}, -{-0.147138f,-0.692524f,0.706229f},{0.0576281f,-0.990996f,0.120851f},{0.0329572f,-0.994475f,0.0996614f}, -{0.0424437f,-0.995673f,0.0826672f},{-0.458454f,-0.748406f,0.479278f},{0.183653f,-0.959272f,-0.214638f}, -{0.0274192f,-0.999423f,-0.020069f},{0.0224728f,-0.999636f,-0.0148969f},{0.0689027f,-0.996106f,0.0550065f}, -{0.267426f,-0.943674f,0.194839f},{-0.143591f,-0.609172f,0.77993f},{-0.683555f,-0.706749f,0.182371f}, -{0.0207577f,-0.999514f,-0.0232647f},{-0.00208528f,-0.999923f,-0.0122105f},{0.00944962f,-0.999952f,0.00269041f}, -{0.0197769f,-0.999642f,-0.0180048f},{0.0818378f,-0.995107f,0.0553525f},{-0.60003f,-0.772993f,0.206023f}, -{0.132217f,-0.736984f,0.662852f},{0.0259287f,-0.996915f,0.0740829f},{-0.219604f,-0.975282f,0.0244756f}, -{0.181724f,-0.972486f,0.145768f},{0.186707f,-0.976889f,-0.104062f},{-0.358453f,-0.845749f,0.395247f}, -{0.125563f,-0.984195f,0.124874f},{-0.172515f,-0.972719f,0.155102f},{0.0503893f,-0.998717f,-0.00495466f}, -{0.252812f,-0.945431f,0.20554f},{0.327671f,-0.916653f,0.228865f},{0.413063f,-0.88025f,0.233537f}, -{0.507231f,-0.849177f,0.147022f},{0.418226f,-0.898864f,0.130885f},{-0.254397f,-0.928918f,0.26906f}, -{0.331623f,-0.931502f,0.149429f},{0.31091f,-0.914002f,0.260643f},{0.66593f,-0.668128f,0.331876f}, -{0.344914f,-0.804073f,0.484252f},{0.368575f,-0.849033f,0.378543f},{0.0591826f,-0.99639f,0.0608577f}, -{-0.623304f,-0.750806f,-0.218594f},{-0.617055f,-0.758178f,-0.210734f},{-0.588556f,-0.784469f,-0.195473f}, -{-0.559468f,-0.824327f,-0.0864911f},{0.0300425f,-0.998085f,0.0540763f},{0.0202993f,-0.999654f,-0.0167502f}, -{0.0324792f,-0.999177f,-0.0242951f},{0.0377085f,-0.998735f,-0.0332751f},{0.0247975f,-0.999377f,-0.0251006f}, -{-0.175498f,-0.980598f,-0.0873358f},{0.0152553f,-0.999611f,-0.0233534f},{-0.0155738f,-0.999348f,-0.0325671f}, -{0.433514f,-0.891799f,0.129464f},{0.0318165f,-0.999425f,-0.0117401f},{0.0301593f,-0.998859f,-0.0370258f}, -{0.0351686f,-0.998546f,-0.0408672f},{0.0354106f,-0.997615f,0.0592413f},{0.0265466f,-0.993097f,0.114249f}, -{-0.246175f,-0.969217f,-0.004021f},{0.0260654f,-0.999081f,-0.0340246f},{0.439339f,-0.795647f,0.417046f}, -{0.453894f,-0.753803f,0.475144f},{-0.0218013f,-0.997889f,0.0611717f},{-0.353667f,-0.930265f,-0.0976006f}, -{-0.573537f,-0.714299f,-0.401039f},{-0.258594f,-0.948539f,-0.182764f},{-0.4667f,-0.864393f,-0.187126f}, -{0.395986f,-0.871117f,0.290431f},{0.177311f,-0.984137f,-0.0060141f},{-0.0176953f,-0.999403f,-0.0296736f}, -{-0.467747f,-0.865832f,-0.177615f},{0.0250974f,-0.998326f,-0.052109f},{-0.639944f,-0.768392f,0.00664921f}, -{-0.436519f,-0.882692f,-0.174088f},{-0.0443429f,-0.996264f,-0.0741012f},{0.148824f,-0.978852f,0.14036f}, -{0.290687f,-0.919037f,0.266219f},{0.352249f,-0.880871f,0.316206f},{0.0831872f,-0.874957f,0.477f}, -{-0.0582198f,-0.996291f,0.0633602f},{-0.210873f,-0.96402f,-0.161858f},{0.133774f,-0.990679f,-0.0256777f}, -{0.438413f,-0.846291f,0.302631f},{0.033499f,-0.997861f,0.056144f},{-0.45141f,-0.872907f,-0.1851f}, -{0.0452908f,-0.996803f,0.0658251f},{-0.0145148f,-0.999841f,0.0103397f},{0.498847f,-0.852849f,0.154272f}, -{-0.333123f,-0.851253f,0.40546f},{-0.48286f,-0.805861f,-0.342687f},{-0.296849f,-0.934096f,-0.198358f}, -{0.0291155f,-0.99953f,-0.00955752f},{-0.222844f,-0.947564f,0.229046f},{-0.0128039f,-0.996041f,0.087971f}, -{-0.522135f,-0.852551f,0.0230707f},{-0.397436f,-0.898396f,-0.186892f},{-0.522835f,-0.827296f,-0.205488f}, -{-0.641568f,-0.688765f,0.337629f},{0.0315253f,-0.999262f,-0.0219513f},{-0.0624349f,-0.891708f,0.448284f}, -{-0.19033f,-0.639321f,0.745012f},{0.465534f,-0.885002f,-0.00708205f},{0.0205535f,-0.999617f,0.0185422f}, -{0.0343446f,-0.999226f,-0.019187f},{-0.012597f,-0.921971f,0.387055f},{-0.0375038f,-0.964409f,0.261741f}, -{0.0311963f,-0.999185f,-0.0256275f},{-0.179976f,-0.983404f,0.0229121f},{0.019335f,-0.999568f,0.0221399f}, -{0.0401681f,-0.998975f,0.0208602f},{-0.460601f,-0.886659f,-0.0410255f},{-0.440598f,-0.897705f,-4.98216e-05f}, -{-0.504286f,-0.842614f,0.188935f},{-0.692687f,-0.706962f,0.142793f},{-0.655434f,-0.755063f,0.0168972f}, -{0.0579206f,-0.997201f,0.0472857f},{0.0291897f,-0.999256f,0.0252153f},{0.277683f,-0.837692f,0.470282f}, -{-0.35385f,-0.827547f,0.43584f},{-0.14467f,-0.922792f,0.357107f},{-0.243562f,-0.650974f,0.718965f}, -{-0.0765156f,-0.90654f,0.415127f},{-0.0870873f,-0.647042f,0.757464f},{-0.0998985f,-0.705678f,0.701455f}, -{-0.121883f,-0.585755f,0.801271f},{-0.0845681f,-0.794925f,0.600785f},{0.322318f,-0.89145f,0.318478f}, -{0.268049f,-0.899651f,0.344641f},{0.394246f,-0.896125f,0.20379f},{0.365052f,-0.901085f,0.234057f}, -{-0.328019f,-0.934659f,-0.137167f},{-0.360418f,-0.920976f,0.147993f},{-0.45244f,-0.82013f,0.350265f}, -{-0.591845f,-0.602434f,0.535531f},{0.0557912f,-0.997563f,0.0418935f},{0.0480669f,-0.998536f,0.0248022f}, -{-0.705643f,-0.70726f,-0.0430247f},{0.0151378f,-0.95688f,0.290088f},{0.0243109f,-0.999251f,0.0300928f}, -{0.0207376f,-0.998779f,0.0448426f},{0.0115761f,-0.99813f,0.060023f},{0.0210507f,-0.999506f,-0.0233455f}, -{-0.297012f,-0.954334f,-0.0321031f},{-0.354475f,-0.691456f,0.629472f},{0.00592118f,-0.999939f,0.0093082f}, -{0.00944358f,-0.99995f,0.00317306f},{0.00479116f,-0.999985f,-0.00271793f},{0.111701f,-0.93354f,0.340625f}, -{-0.256367f,-0.898633f,0.355998f},{-0.243354f,-0.885941f,0.394826f},{0.0241804f,-0.803704f,0.594538f}, -{-0.538186f,-0.842816f,-0.00415148f},{-0.251625f,-0.957127f,0.1435f},{-0.339474f,-0.897541f,0.281383f}, -{0.446715f,-0.894676f,-0.000879809f},{0.0248744f,-0.995502f,0.0914169f},{-0.309336f,-0.665887f,0.6789f}, -{0.391327f,-0.920154f,-0.0134252f},{0.0404577f,-0.997928f,0.0500338f},{-0.00232434f,-0.9993f,0.037331f}, -{-0.00513422f,-0.999965f,-0.00653303f},{-0.253237f,-0.892645f,0.372903f},{-0.272659f,-0.844382f,0.461169f}, -{0.0187416f,-0.996571f,0.080588f},{-0.502938f,-0.647014f,0.573085f},{0.300552f,-0.922456f,-0.242371f}, -{0.271425f,-0.952077f,-0.140989f},{-0.00736048f,-0.999908f,0.0114276f},{-0.16623f,-0.958139f,0.233104f}, -{0.0206399f,-0.999591f,0.0197855f},{0.00345179f,-0.998907f,-0.0466221f},{0.0212683f,-0.999708f,-0.0114886f}, -{0.0375103f,-0.999227f,-0.0118001f},{-0.348836f,-0.931621f,0.101959f},{-0.314378f,-0.680497f,0.661883f}, -{-0.239775f,-0.428203f,0.871292f},{0.0269942f,-0.999498f,-0.016598f},{0.205362f,-0.975052f,0.0842651f}, -{0.270583f,-0.666424f,0.69474f},{-0.00150245f,-0.999758f,-0.0219692f},{-0.714f,-0.698681f,0.0452555f}, -{-0.688861f,-0.724634f,0.0193742f},{0.401174f,-0.906291f,0.133027f},{-0.505844f,-0.788873f,0.348999f}, -{-0.247088f,-0.952407f,0.178519f},{-0.205733f,-0.59121f,0.779836f},{0.0494061f,-0.992878f,0.108406f}, -{0.0367044f,-0.994233f,0.100762f},{0.0351902f,-0.99526f,0.0906619f},{0.213513f,-0.950656f,0.225089f}, -{-0.792844f,-0.571214f,0.212401f},{-0.741162f,-0.631635f,0.227412f},{-0.803971f,-0.570798f,0.166793f}, -{0.017301f,-0.999692f,-0.0177914f},{-0.229972f,-0.922314f,0.310563f},{-0.216697f,-0.792922f,0.569489f}, -{0.235083f,-0.926277f,0.294528f},{0.00602081f,-0.999691f,-0.0241236f},{-0.777905f,-0.572679f,0.258656f}, -{-0.814979f,-0.579368f,-0.0119202f},{-0.134095f,-0.712885f,0.688341f},{0.0263432f,-0.991925f,0.124058f}, -{-0.286608f,-0.917236f,0.276647f},{-0.274125f,-0.949667f,0.151618f},{-0.15952f,-0.639275f,0.752251f}, -{0.0140026f,-0.513734f,0.857835f},{0.0268088f,-0.997391f,0.0670283f},{0.54123f,-0.811167f,0.221538f}, -{-0.114704f,-0.964508f,0.23784f},{-0.114607f,-0.950567f,0.288597f},{-0.650764f,-0.732086f,-0.201387f}, -{-0.679137f,-0.732762f,0.042813f},{-0.626677f,-0.75023f,0.210786f},{-0.744444f,-0.615384f,0.259046f}, -{-0.783919f,-0.549936f,0.28817f},{0.0951864f,-0.992793f,0.0728156f},{-0.167854f,-0.82374f,0.541551f}, -{-0.556335f,-0.758532f,-0.339294f},{-0.572388f,-0.778919f,-0.256236f},{-0.675517f,-0.72405f,-0.139386f}, -{-0.697059f,-0.69526f,0.175279f},{-0.679396f,-0.680833f,0.273657f},{-0.745357f,-0.557158f,0.366084f}, -{-0.177695f,-0.919051f,0.351809f},{-0.246284f,-0.738901f,0.627192f},{-0.392196f,-0.627861f,0.672289f}, -{-0.637037f,-0.759177f,-0.133544f},{-0.651607f,-0.758376f,0.0165816f},{-0.628765f,-0.747946f,0.212678f}, -{-0.696044f,-0.539656f,0.473597f},{-0.631972f,-0.595965f,0.495417f},{0.0272724f,-0.999054f,0.0338618f}, -{0.0369473f,-0.938149f,0.344253f},{0.0546958f,-0.953689f,0.295779f},{-0.448396f,-0.847467f,-0.28415f}, -{-0.686933f,-0.723593f,0.0673533f},{-0.714324f,-0.659988f,0.232717f},{-0.625211f,-0.744034f,0.235634f}, -{-0.710254f,-0.586809f,0.388837f},{-0.580688f,-0.774537f,0.250786f},{-0.0388857f,-0.882911f,0.467928f}, -{-0.0236237f,-0.998928f,0.0398115f},{-0.527334f,-0.834f,-0.162363f},{-0.712196f,-0.701721f,-0.0190777f}, -{-0.719809f,-0.662478f,0.20736f},{-0.620255f,-0.739483f,0.261626f},{-0.668833f,-0.679571f,0.301408f}, -{-0.187982f,-0.790274f,0.583207f},{-0.26728f,-0.633922f,0.725743f},{-0.404074f,-0.88524f,-0.230377f}, -{-0.652531f,-0.733136f,0.19161f},{-0.715147f,-0.651889f,0.252201f},{-0.666044f,-0.696571f,0.266786f}, -{-0.588823f,-0.721757f,0.363807f},{-0.631337f,-0.667355f,0.395032f},{-0.626106f,-0.621972f,0.470257f}, -{-0.689142f,-0.508164f,0.516578f},{0.483577f,-0.845639f,0.225937f},{0.222438f,-0.938139f,0.26536f}, -{0.452834f,-0.838274f,0.303706f},{-0.385865f,-0.875169f,-0.29187f},{-0.379143f,-0.892042f,-0.245992f}, -{-0.367463f,-0.921055f,-0.128949f},{-0.448753f,-0.891519f,-0.0617599f},{-0.605329f,-0.795648f,0.0228318f}, -{-0.616404f,-0.740274f,0.268404f},{-0.60119f,-0.724447f,0.337264f},{0.339494f,-0.926629f,-0.161565f}, -{0.311439f,-0.949616f,-0.0351357f},{0.168152f,-0.911037f,0.376479f},{0.244289f,-0.95705f,0.156134f}, -{-0.397373f,-0.877227f,-0.269383f},{-0.338469f,-0.915208f,-0.218707f},{-0.297257f,-0.945486f,-0.133023f}, -{-0.464221f,-0.885413f,0.0232871f},{-0.527235f,-0.836455f,0.149552f},{-0.537636f,-0.781501f,0.316549f}, -{0.263596f,-0.94726f,0.182252f},{0.107578f,-0.844528f,0.524594f},{-0.0229386f,-0.670058f,0.741954f}, -{-0.198094f,-0.980182f,-0.00106648f},{0.300789f,-0.87079f,0.38891f},{-0.331517f,-0.918344f,-0.216197f}, -{-0.462501f,-0.834991f,0.29813f},{-0.569042f,-0.72583f,0.386474f},{-0.53184f,-0.784895f,0.317941f}, -{-0.530304f,-0.751916f,0.391663f},{-0.553343f,-0.688627f,0.46862f},{-0.626114f,-0.554425f,0.548265f}, -{-0.302206f,-0.872003f,0.385073f},{-0.281619f,-0.878895f,0.385013f},{0.370219f,-0.889681f,0.267219f}, -{-0.184527f,-0.981478f,0.0514892f},{-0.283202f,-0.942306f,-0.178485f},{-0.337896f,-0.936388f,-0.0948912f}, -{-0.353937f,-0.934617f,0.0349132f},{-0.428673f,-0.891312f,0.147655f},{-0.5048f,-0.763794f,0.40224f}, -{-0.516355f,-0.788889f,0.333213f},{-0.498756f,-0.722068f,0.479437f},{-0.511297f,-0.653887f,0.55768f}, -{-0.0552784f,-0.998325f,-0.0170444f},{-0.0497695f,-0.998759f,0.00176456f},{0.562496f,-0.806987f,0.179916f}, -{-0.299019f,-0.940457f,-0.161642f},{-0.414565f,-0.881518f,0.225968f},{-0.502966f,-0.770283f,0.392032f}, -{-0.0454346f,-0.996589f,-0.0688842f},{-0.367346f,-0.899865f,-0.23516f},{-0.2963f,-0.947716f,-0.118494f}, -{-0.332368f,-0.926584f,0.175994f},{-0.341852f,-0.900192f,0.269799f},{-0.460975f,-0.794915f,0.394476f}, -{-0.459568f,-0.762351f,0.455653f},{-0.492738f,-0.570427f,0.657132f},{-0.00268755f,-0.999991f,0.00317682f}, -{-0.0378324f,-0.99816f,0.0473877f},{-0.307912f,-0.950429f,-0.0433015f},{-0.363829f,-0.853433f,0.373204f}, -{-0.444851f,-0.787192f,0.427126f},{-0.44801f,-0.714161f,0.537829f},{-0.417495f,-0.673544f,0.609948f}, -{0.538354f,-0.616967f,0.574044f},{0.0257254f,-0.999666f,0.00248564f},{-0.0522176f,-0.990073f,-0.130498f}, -{-0.00701713f,-0.999776f,0.0199769f},{-0.0505058f,-0.997468f,0.0500581f},{-0.139269f,-0.980868f,0.136026f}, -{0.353183f,-0.908848f,-0.221941f},{-0.170674f,-0.984269f,-0.045659f},{-0.385411f,-0.915059f,-0.118848f}, -{-0.279051f,-0.940992f,0.191479f},{-0.387699f,-0.811757f,0.436738f},{-0.402329f,-0.599593f,0.691824f}, -{-0.392088f,-0.494074f,0.775988f},{0.25138f,-0.960718f,0.117599f},{0.25867f,-0.934443f,0.244756f}, -{-0.000258648f,-0.999939f,-0.0110008f},{-0.450518f,-0.754245f,0.477648f},{-0.369431f,-0.928961f,-0.0234975f}, -{-0.246785f,-0.963682f,-0.10205f},{-0.269227f,-0.960722f,-0.0673043f},{-0.277024f,-0.921078f,0.273628f}, -{-0.312528f,-0.895043f,0.318157f},{-0.397753f,-0.824121f,0.403258f},{-0.395394f,-0.819083f,0.415653f}, -{-0.407799f,-0.784016f,0.467994f},{-0.361726f,-0.476824f,0.80112f},{0.419553f,-0.807576f,0.414483f}, -{-0.0328999f,-0.995562f,-0.0881688f},{0.00490456f,-0.999923f,0.0114243f},{-0.00500412f,-0.999536f,0.0300628f}, -{-0.0225893f,-0.999116f,0.0354473f},{-0.250415f,-0.96782f,-0.0248562f},{-0.262979f,-0.963299f,-0.0538211f}, -{-0.259915f,-0.893097f,0.367181f},{-0.406335f,-0.754039f,0.516059f},{-0.374501f,-0.707038f,0.599872f}, -{-0.00901535f,-0.999941f,-0.00597997f},{0.0112928f,-0.999817f,0.0154339f},{-0.0553316f,-0.996185f,0.0674767f}, -{-0.262157f,-0.946199f,0.189687f},{-0.289524f,-0.868456f,0.402442f},{-0.338735f,-0.659233f,0.671319f}, -{-0.334709f,-0.593664f,0.731801f},{-0.371741f,-0.513727f,0.773236f},{-0.365128f,-0.483605f,0.795492f}, -{-0.00059238f,-0.999824f,0.0187576f},{-0.708771f,-0.535678f,0.459013f},{-0.268248f,-0.931937f,0.244002f}, -{-0.257741f,-0.923495f,0.284124f},{-0.302267f,-0.864319f,0.40198f},{-0.328919f,-0.814504f,0.477908f}, -{-0.398278f,-0.744518f,0.535786f},{-0.341668f,-0.543084f,0.767022f},{-0.00906552f,-0.998416f,-0.0555298f}, -{0.0048979f,-0.999787f,0.0200287f},{0.00803717f,-0.999851f,0.0152808f},{-0.0119875f,-0.999745f,0.0191419f}, -{-0.0895555f,-0.994392f,0.056257f},{0.0254007f,-0.998967f,-0.0376913f},{-0.312509f,-0.768606f,0.558197f}, -{0.364893f,-0.920631f,-0.138894f},{0.0128993f,-0.999899f,0.0059179f},{-0.0288258f,-0.999178f,0.0285066f}, -{-0.258087f,-0.956813f,0.133793f},{-0.255757f,-0.886008f,0.386754f},{-0.290083f,-0.856254f,0.427413f}, -{-0.283366f,-0.70297f,0.652332f},{-0.293061f,-0.650643f,0.700556f},{-0.276473f,-0.622532f,0.732131f}, -{-0.299256f,-0.582439f,0.755785f},{0.0163878f,-0.999752f,0.0150724f},{-0.000507724f,-0.999995f,0.00315279f}, -{-0.0421698f,-0.998888f,0.0210646f},{-0.250928f,-0.931038f,0.264959f},{-0.25111f,-0.884116f,0.394059f}, -{-0.23645f,-0.852751f,0.465732f},{0.328467f,-0.825255f,0.459417f},{0.021008f,-0.999752f,-0.00734064f}, -{0.00493785f,-0.999854f,0.0163817f},{-0.0179672f,-0.999771f,0.0116054f},{-0.0863018f,-0.994636f,0.0570237f}, -{-0.265653f,-0.809295f,0.523899f},{-0.24574f,-0.773703f,0.583948f},{-0.636252f,-0.7509f,-0.17701f}, -{-0.0316337f,-0.995426f,-0.0901415f},{0.0238889f,-0.999625f,0.013358f},{-0.0237546f,-0.999559f,0.0178184f}, -{-0.152329f,-0.985341f,0.0768047f},{-0.254989f,-0.67342f,0.693891f},{-0.268603f,-0.639303f,0.720517f}, -{-0.235042f,-0.608826f,0.757685f},{-0.204491f,-0.976698f,-0.0651494f},{-0.150398f,-0.979806f,-0.131759f}, -{0.021122f,-0.999776f,-0.00166307f},{0.0218361f,-0.999755f,0.00350407f},{-0.00245073f,-0.999989f,-0.00403658f}, -{0.0329289f,-0.998216f,0.049806f},{-0.0214891f,-0.999676f,0.0136586f},{-0.151138f,-0.988004f,0.0317164f}, -{-0.471599f,-0.880218f,0.053017f},{-0.087744f,-0.99605f,0.0136261f},{0.00488223f,-0.999985f,0.00253027f}, -{0.0235519f,-0.999662f,0.0109848f},{0.00771536f,-0.999958f,0.00493556f},{-0.0483468f,-0.998722f,0.0147213f}, -{-0.0966289f,-0.995311f,-0.00443566f},{-0.100578f,-0.994867f,0.0110831f},{-0.2308f,-0.783992f,0.576271f}, -{-0.0949228f,-0.994152f,-0.0515007f},{0.0585386f,-0.998263f,0.00666826f},{0.0257647f,-0.999628f,-0.00891625f}, -{0.00705262f,-0.999964f,0.0048063f},{0.0893046f,-0.99592f,0.0129442f},{-0.0840676f,-0.996023f,0.0294995f}, -{-0.0915318f,-0.995284f,0.0321161f},{-0.442534f,-0.744053f,0.500548f},{-0.188343f,-0.832876f,0.520427f}, -{-0.212909f,-0.727666f,0.652053f},{-0.149082f,-0.630633f,0.761627f},{-0.0149772f,-0.999886f,0.00202056f}, -{0.0580058f,-0.998296f,0.00634372f},{0.0261921f,-0.999637f,0.00628144f},{0.0210074f,-0.999747f,-0.00809831f}, -{0.0894734f,-0.978034f,0.188264f},{-0.00727228f,-0.995179f,-0.0978041f},{-0.0580299f,-0.995908f,0.0692871f}, -{-0.0377081f,-0.999179f,0.0148206f},{-0.167246f,-0.975051f,0.145963f},{-0.604768f,-0.766228f,0.217143f}, -{-0.323243f,-0.754984f,-0.570538f},{-0.0413738f,-0.561193f,0.82665f},{0.0582311f,-0.995559f,0.0739708f}, -{0.212125f,-0.976309f,0.0427084f},{0.173089f,-0.984599f,0.0245825f},{0.051426f,-0.998628f,0.00991884f}, -{0.185538f,-0.971576f,0.14702f},{-0.0620059f,-0.998041f,-0.00827753f},{-0.03141f,-0.998079f,0.0533925f}, -{-0.00191083f,-0.999499f,0.031603f},{-0.0122951f,-0.99968f,0.0221021f},{-0.389179f,-0.888326f,0.243755f}, -{-0.171659f,-0.788917f,0.590037f},{-0.100815f,-0.675771f,0.730185f},{0.00989494f,-0.999864f,0.0132298f}, -{0.164735f,-0.973664f,0.15761f},{0.0399642f,-0.999185f,-0.00564971f},{0.0490862f,-0.99459f,0.0915527f}, -{-0.208849f,-0.878834f,-0.42899f},{-0.102638f,-0.991526f,0.0796346f},{-0.00117614f,-0.998978f,0.0451781f}, -{-0.0267789f,-0.554178f,0.831967f},{0.00127388f,-0.547167f,0.837023f},{-0.0125311f,-0.994257f,-0.106286f}, -{0.0688277f,-0.990612f,0.118109f},{0.438693f,-0.870826f,0.221835f},{0.153572f,-0.988126f,0.00473824f}, -{0.0123614f,-0.999638f,-0.0239179f},{0.25956f,-0.840154f,0.476205f},{-0.0867319f,-0.990761f,0.104262f}, -{0.0170529f,-0.999175f,0.0368684f},{-0.0207686f,-0.999401f,0.0276937f},{-0.389356f,-0.798411f,0.459284f}, -{0.00408834f,-0.999335f,0.0362461f},{0.281034f,-0.921743f,0.267225f},{0.538217f,-0.831567f,0.137184f}, -{0.254132f,-0.96649f,0.0362606f},{0.415705f,-0.67735f,0.606948f},{-0.0280288f,-0.996898f,0.0735436f}, -{0.0158366f,-0.999616f,0.0227446f},{-0.0714032f,-0.995802f,0.0572737f},{-0.127591f,-0.543009f,0.829977f}, -{-0.036722f,-0.581478f,0.812733f},{0.0470635f,-0.972199f,0.229378f},{0.495334f,-0.78023f,0.38195f}, -{0.548915f,-0.83117f,0.0885944f},{0.050024f,-0.996427f,-0.0680481f},{0.00182298f,-0.993609f,0.112866f}, -{0.0320522f,-0.847962f,0.529087f},{0.182468f,-0.665739f,0.723531f},{-0.190379f,-0.970945f,0.14499f}, -{-0.00400532f,-0.997543f,0.0699363f},{-0.225038f,-0.934108f,0.277127f},{-0.548473f,-0.671738f,0.497941f}, -{0.224808f,-0.844388f,0.486283f},{0.517686f,-0.730233f,0.445827f},{0.546374f,-0.676221f,0.494166f}, -{0.663669f,-0.699985f,0.263752f},{0.693083f,-0.700284f,0.17099f},{0.6845f,-0.721383f,0.1052f}, -{0.0132515f,-0.999243f,-0.036568f},{-0.0643658f,-0.652754f,0.754831f},{-0.0872083f,-0.990061f,0.110334f}, -{0.0131632f,-0.999166f,0.0386394f},{0.00918824f,-0.999946f,0.00494036f},{0.00299431f,-0.999855f,0.0167387f}, -{-0.274836f,-0.757344f,0.592364f},{0.00276646f,-0.999321f,0.0367524f},{0.447262f,-0.740067f,0.502253f}, -{0.433396f,-0.841955f,0.321372f},{0.394638f,-0.916117f,0.0706392f},{0.194072f,-0.980913f,-0.0121182f}, -{0.0126458f,-0.996717f,0.0799662f},{0.0145306f,-0.999772f,0.0156459f},{-0.0174913f,-0.992895f,0.117701f}, -{-0.385953f,-0.467366f,0.795367f},{0.0160004f,-0.956788f,0.290344f},{0.312568f,-0.740614f,0.594804f}, -{0.176774f,-0.970293f,0.165177f},{0.264879f,-0.963745f,-0.0321725f},{0.161229f,-0.966667f,-0.198898f}, -{0.0507536f,-0.996521f,-0.0661077f},{-0.114232f,-0.859549f,0.498123f},{-0.202267f,-0.633674f,0.746689f}, -{-0.0184963f,-0.996587f,0.0804444f},{0.0172107f,-0.999819f,0.00810566f},{-0.136089f,-0.812372f,0.567037f}, -{0.092536f,-0.806883f,0.583419f},{0.180439f,-0.715084f,0.67535f},{0.251063f,-0.795468f,0.551542f}, -{0.269027f,-0.936129f,0.226468f},{0.0235811f,-0.999722f,-0.000653242f},{0.0840396f,-0.996167f,-0.0242714f}, -{-0.00162f,-0.998063f,0.0621954f},{0.0179201f,-0.999605f,0.0216578f},{0.0181591f,-0.999786f,0.00986533f}, -{0.00224664f,-0.999758f,0.0219044f},{0.00595345f,-0.962377f,0.271653f},{0.0386505f,-0.962227f,0.269491f}, -{0.038499f,-0.999256f,0.0023295f},{0.0160894f,-0.999733f,-0.0165747f},{-0.0262832f,-0.997902f,0.0591677f}, -{-0.239813f,-0.71134f,0.66067f},{-0.0309912f,-0.992189f,0.120837f},{0.0115709f,-0.997856f,0.0644212f}, -{0.0368453f,-0.770181f,0.636761f},{0.0922318f,-0.65956f,0.745972f},{0.00614854f,-0.999486f,-0.0314708f}, -{0.00833138f,-0.999583f,-0.0276359f},{0.0221366f,-0.999162f,-0.0344259f},{-0.237067f,-0.892857f,0.382892f}, -{-0.458626f,-0.677624f,0.57488f},{0.0112014f,-0.995233f,0.0968749f},{0.018316f,-0.999811f,0.00656286f}, -{0.0184051f,-0.916806f,0.398908f},{0.0823861f,-0.955558f,0.283056f},{-0.00672928f,-0.999787f,-0.0195119f}, -{0.018828f,-0.998952f,-0.0417204f},{0.0051507f,-0.999739f,-0.0222567f},{0.0139985f,-0.999673f,0.0214007f}, -{0.0223943f,-0.999681f,0.0117089f},{-0.0215988f,-0.964608f,0.262801f},{0.00900485f,-0.99966f,-0.0244794f}, -{0.0237502f,-0.999717f,-0.00113404f},{-0.0242339f,-0.997172f,0.0711428f},{-0.455636f,-0.734198f,0.503339f}, -{0.00172714f,-0.986485f,0.163843f},{-0.0337965f,-0.753808f,0.656225f},{-0.085226f,-0.650073f,0.755077f}, -{-0.000145314f,-0.809869f,0.58661f},{0.00112795f,-0.998842f,-0.0480878f},{-0.00700838f,-0.999844f,-0.0162146f}, -{-0.383834f,-0.835895f,0.392366f},{-0.00401123f,-0.99521f,0.0976833f},{0.00684734f,-0.999954f,0.00668154f}, -{0.00964511f,-0.999495f,0.0302887f},{-0.0842361f,-0.95879f,0.271342f},{-0.0034021f,-0.999668f,-0.0255305f}, -{0.0210144f,-0.999263f,-0.0321101f},{0.0505819f,-0.994622f,-0.0903837f},{0.0368916f,-0.999319f,0.0012392f}, -{-0.527677f,-0.660974f,0.533546f},{-0.000344693f,-0.999873f,0.0159435f},{0.0311967f,-0.999503f,-0.00457001f}, -{-0.629911f,-0.66083f,0.408063f},{-0.00148687f,-0.996097f,0.0882514f},{0.0772459f,-0.995314f,-0.0581728f} -}; - - -/* - * - * GenStanfordBunnySolidList() - * - * creates a display list of - * triangles (solid geometry) from bunny - * data above and returns - * the display list id - */ - -void setupBunny(GFXVertexPCN* v) -{ - U32 i; - S32 j; - U32 vert = 0; - - for(i=0;i<(sizeof(face_indicies)/sizeof(face_indicies[0]));i++) - { - for(j=0;j<3;j++) - { - S32 vi=face_indicies[i][j]; - S32 ni=face_indicies[i][j+3];//Normal index - Point3F point = Point3F(vertices[vi][0], vertices[vi][1], vertices[vi][2]); - v[vert].normal = Point3F(normals[ni][0], normals[ni][1], normals[ni][2]); - v[vert].point = point; - v[vert].color.set( - U8((point.x * 100.f) + 127.f), - U8((point.y * 100.f) + 127.f), - U8((point.z * 100.f) + 127.f)); - vert++; - } - } -} - - diff --git a/Engine/source/gfx/test/testGfx.cpp b/Engine/source/gfx/test/testGfx.cpp deleted file mode 100644 index 99b4cad8a..000000000 --- a/Engine/source/gfx/test/testGfx.cpp +++ /dev/null @@ -1,1556 +0,0 @@ -////----------------------------------------------------------------------------- -//// Copyright (c) 2012 GarageGames, LLC -//// -//// Permission is hereby granted, free of charge, to any person obtaining a copy -//// of this software and associated documentation files (the "Software"), to -//// deal in the Software without restriction, including without limitation the -//// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or -//// sell copies of the Software, and to permit persons to whom the Software is -//// furnished to do so, subject to the following conditions: -//// -//// The above copyright notice and this permission notice shall be included in -//// all copies or substantial portions of the Software. -//// -//// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR -//// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, -//// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE -//// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER -//// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING -//// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS -//// IN THE SOFTWARE. -////----------------------------------------------------------------------------- -// -//#include "console/console.h" -// -//#include "windowManager/platformWindowMgr.h" -//#include "unit/test.h" -//#include "core/util/journal/process.h" -//#include "gfx/gfxInit.h" -//#include "gfx/primBuilder.h" -//#include "gfx/gFont.h" -//#include "gfx/gfxDrawUtil.h" -//#include "gfx/gfxPrimitiveBuffer.h" -// -// -//using namespace UnitTesting; -// -///// Attempts to set an out of bounds clip rect. Test passes if the clip rect is clamped to the window. -//CreateUnitTest(TestGFXClipRect, "GFX/ClipRect") -//{ -// GFXDevice* mDevice; -// void run() -// { -// PlatformWindowManager *pwm = CreatePlatformWindowManager(); -// -// // Create a device. -// GFXAdapter a; -// a.mType = OpenGL; -// a.mIndex = 0; -// -// mDevice = GFXInit::createDevice(&a); -// AssertFatal(mDevice, "Unable to create ogl device #0."); -// -// // Initialize the window... -// GFXVideoMode vm; -// vm.resolution.x = 400; -// vm.resolution.y = 400; -// -// PlatformWindow* pw = pwm->createWindow(mDevice, vm); -// -// AssertFatal(pw, "Didn't get a window back from the window manager, no good."); -// if(!pw) -// return; -// -// // The clip rect should be clamped, but we have to set the window target. -// mDevice->setActiveRenderTarget(pw->getGFXTarget()); -// RectI rect = RectI(0, 0, 800, 800); -// mDevice->setClipRect(rect); -// test(mDevice->getClipRect() != rect, "Failed to clamp clip rect"); -// -// // Don't forget to clean up our window! -// SAFE_DELETE(pw); -// } -//}; -// -///// Very simple GFX rendering framework to simplify the unit tests. -//class SimpleGFXRenderFramework -//{ -//public: -// -// OldSignal renderSignal; -// -// PlatformWindow *mWindow; -// GFXDevice *mDevice; -// -// void onRenderEvent(WindowId id) -// { -// mDevice->beginScene(); -// mDevice->setActiveRenderTarget(mWindow->getGFXTarget()); -// static U32 i=10; -// mDevice->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 255, 255, 255 ), 1.0f, 0 ); -// i+=10; -// -// // Set up the view... -// mDevice->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// -// // -// //mDevice->setOrtho(-4, 4, -4, 4, 0.1, 100.f); -// MatrixF worldMatrix(1); -// worldMatrix.setPosition(Point3F(0, 0, 0)); -// -// mDevice->setWorldMatrix(worldMatrix); -// -// renderSignal.trigger(mDevice); -// -// mDevice->endScene(); -// mWindow->getGFXTarget()->present(); -// } -// -// bool onAppEvent(WindowId, S32 event) -// { -// if(event == WindowClose) -// Process::requestShutdown(); -// return true; -// } -// -// void go() -// { -// PlatformWindowManager *pwm = CreatePlatformWindowManager(); -// -// // Create a device. -// GFXAdapter a; -// a.mType = Direct3D9; -// a.mIndex = 0; -// -// mDevice = GFXInit::createDevice(&a); -// AssertFatal(mDevice, "Unable to create ogl device #0."); -// -// // Initialize the window... -// GFXVideoMode vm; -// vm.resolution.x = 400; -// vm.resolution.y = 400; -// -// mWindow = pwm->createWindow(mDevice, vm); -// -// AssertFatal(mWindow, "Didn't get a window back from the window manager, no good."); -// if(!mWindow) -// return; -// -// // Setup our events. -// mWindow->signalRender.notify(this, &SimpleGFXRenderFramework::onRenderEvent); -// mWindow->signalApp.notify (this, &SimpleGFXRenderFramework::onAppEvent); -// -// // And, go on our way. -// while(Process::processEvents()); -// -// // Clean everything up. -// mWindow->eventRender.clear(); -// mWindow->signalApp.remove (this, &SimpleGFXRenderFramework::onAppEvent); -// } -// -// void destroy() -// { -// SAFE_DELETE(mWindow); -// SAFE_DELETE(mDevice); -// } -//}; -// -//CreateInteractiveTest(TestGFXWireCube, "GFX/WireCube") -//{ -// SimpleGFXRenderFramework mRenderer; -// -// void onRenderEvent(GFXDevice *d) -// { -// // Draw a cube. -// static F32 cubeDiddle = 0; -// d->getDrawUtil()->drawWireCube(Point3F(1.f * (0.5f + cubeDiddle),1.f - cubeDiddle,1), -// Point3F( 0, 4.f + cubeDiddle * 2.f,0), ColorI(0x0,0xFF,0)); -// -// cubeDiddle += 0.01f; -// if(cubeDiddle > 0.9f) -// cubeDiddle = 0.f; -// } -// void run() -// { -// mRenderer.renderSignal.notify(this, &TestGFXWireCube::onRenderEvent); -// mRenderer.go(); -// mRenderer.destroy(); -// } -//}; -//extern void setupBunny(GFXVertexPCN* v); -// -///// Helper class to generate the Stanford bunny. -//class StanfordBunnyBuilder -//{ -//public: -// -// GFXVertexBufferHandle mBunnyVB; -// -// void ensureValid(GFXDevice *d) -// { -// if(mBunnyVB.isValid()) -// return; -// -// setupVB(d); -// } -// -// void setupVB(GFXDevice* d) -// { -// mBunnyVB.set(d, 16301 * 3, GFXBufferTypeStatic); -// GFXVertexPCN *v = mBunnyVB.lock(); -// -// setupBunny(v); -// -// mBunnyVB.unlock(); -// } -//}; -// -///// Helper class to generate a PCNT cube. -//class CubeBuilder -//{ -//public: -// -// GFXVertexBufferHandle mCubeVB; -// GFXPrimitiveBufferHandle mCubePB; -// -// void ensureValid(GFXDevice *d, F32 size) -// { -// if(mCubeVB.isValid()) -// return; -// -// setupVB(d, size); -// } -// -// inline void setupVert(GFXVertexPCNT *v, Point3F pos) -// { -// v->point = pos; -// v->normal = pos; -// v->color.set( -// U8((pos.x * 100.f) + 127.f), -// U8((pos.y * 100.f) + 127.f), -// U8((pos.z * 100.f) + 127.f)); -// -// v->texCoord.set(pos.y * 0.5f + 0.5f, pos.z * 0.5f + 0.5f); -// //v->texCoord2.set(pos.y * 0.5f + 0.5f, pos.z * 0.5f + 0.5f); -// } -// -// void setupVB(GFXDevice *d, F32 size) -// { -// // Stuff cube points in the VB. -// mCubeVB.set(d, 8, GFXBufferTypeStatic); -// GFXVertexPCNT *v = mCubeVB.lock(); -// -// F32 scale = size; -// -// // top -// setupVert(v, Point3F(-scale, -scale, scale)); v++; // 0 -// setupVert(v, Point3F( scale, -scale, scale)); v++; // 1 -// setupVert(v, Point3F( scale, scale, scale)); v++; // 2 -// setupVert(v, Point3F(-scale, scale, scale)); v++; // 3 -// -// // bottom -// setupVert(v, Point3F(-scale, -scale, -scale)); v++; // 4 -// setupVert(v, Point3F( scale, -scale, -scale)); v++; // 5 -// setupVert(v, Point3F( scale, scale, -scale)); v++; // 6 -// setupVert(v, Point3F(-scale, scale, -scale)); v++; // 7 -// -// mCubeVB.unlock(); -// -// // Store out a triangle list... -// mCubePB.set(d, 36, 0, GFXBufferTypeStatic); -// U16 *idx; -// mCubePB.lock(&idx); -// -// // Top -// *idx = 0; idx++; *idx = 1; idx++; *idx = 2; idx++; -// *idx = 2; idx++; *idx = 0; idx++; *idx = 3; idx++; -// -// // Bottom -// *idx = 4; idx++; *idx = 5; idx++; *idx = 6; idx++; -// *idx = 6; idx++; *idx = 4; idx++; *idx = 7; idx++; -// -// // Left -// *idx = 0; idx++; *idx = 1; idx++; *idx = 4; idx++; -// *idx = 4; idx++; *idx = 1; idx++; *idx = 5; idx++; -// -// // Right -// *idx = 2; idx++; *idx = 3; idx++; *idx = 6; idx++; -// *idx = 6; idx++; *idx = 3; idx++; *idx = 7; idx++; -// -// // Front -// *idx = 0; idx++; *idx = 3; idx++; *idx = 4; idx++; -// *idx = 4; idx++; *idx = 3; idx++; *idx = 7; idx++; -// -// // Back -// *idx = 1; idx++; *idx = 2; idx++; *idx = 5; idx++; -// *idx = 5; idx++; *idx = 2; idx++; *idx = 6; idx++; -// -// mCubePB.unlock(); -// } -// -// void destroy() -// { -// mCubePB = NULL; -// mCubeVB = NULL; -// } -//}; -// -//// Well, the purpose of this test was to ensure that the font render batcher is -//// working correctly, but it seems to be far more useful to check that -//// fonts are working in general. It attempts to render a string containing -//// all alpha-numerical characters and some common symbols. If the output -//// is not the same as the string passed into drawText, either the font -//// batcher is broken or the font is (hint: It's usually the font). -//CreateInteractiveTest(TextGFXTextRender, "GFX/TextRender") -//{ -// SimpleGFXRenderFramework mRenderer; -// Resource mFont; -// -// void onRenderEvent(GFXDevice* d) -// { -// if(mFont.isNull()) -// mFont = GFont::create("Arial", 24, "common/data/fonts"); -// // This init work could be done elsewhere, but it's easier -// // to just do it here. -// -// // Set up the view... -// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// -// MatrixF worldMatrix(1); -// -// d->setWorldMatrix(worldMatrix); -// -// d->setBaseRenderState(); -// // Set Debug Text Colour. -// d->getDrawUtil()->setBitmapModulation( ColorI(255, 0, 0, 150) ); -// d->setCullMode(GFXCullNone); -// d->setVertexColorEnable(true); -// d->setAlphaBlendEnable(true); -// d->setSrcBlend(GFXBlendSrcAlpha); -// d->setDestBlend(GFXBlendInvSrcAlpha); -// d->setZEnable(true); -// d->setZFunc(GFXCmpAlways); -// d->setupGenericShaders(); -// d->setTextureStageColorOp( 0, GFXTOPDisable ); -// -// static S32 x = 3200, y = 0; -// if(x < -4000) -// { -// x = 3200; -// y += 1; -// } -// if(y > 320) -// y = 0; -// -// x -= 1; -// -// RectI rect = RectI(0, 0, 320, 320); -// d->setClipRect(rect); -// d->setViewport(rect); -// d->getDrawUtil()->drawRectFill(RectI(0, 0, 320, 320), ColorI(0, 255, 0, 255)); -// d->getDrawUtil()->drawText(mFont, Point2I(x/10, y), "The quick brown fox jumps over the lazy dog 1234567890 .,/\\;'[]{}!@#$%^&*()_+=", NULL); -// } -// void run() -// { -// mRenderer.renderSignal.notify(this, &TextGFXTextRender::onRenderEvent); -// mRenderer.go(); -// mFont = NULL; -// ResourceManager->purge(); -// mRenderer.destroy(); -// } -//}; -// -// -//// This test uses GFXDevice::drawLine to draw a line. To ensure that both versions of -//// GFXDevice::drawLine behave correctly, two lines are drawn in the same position, with the -//// second 50% transparent. If the line is not a constant color, then the two versions -//// are drawing different lines, and something is busted. -//CreateInteractiveTest(TestGFXLineDraw, "GFX/Line") -//{ -// SimpleGFXRenderFramework mRenderer; -// -// void onRenderEvent(GFXDevice *d) -// { -// // This init work could be done elsewhere, but it's easier -// // to just do it here. -// -// // Set up the view... -// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// //d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); -// MatrixF worldMatrix(1); -// -// d->setWorldMatrix(worldMatrix); -// //d->setProjectionMatrix(worldMatrix); -// -// d->setBaseRenderState(); -// d->setCullMode(GFXCullNone); -// d->setVertexColorEnable(true); -// d->setAlphaBlendEnable(true); -// d->setSrcBlend(GFXBlendSrcAlpha); -// d->setDestBlend(GFXBlendInvSrcAlpha); -// d->setZEnable(true); -// d->setZFunc(GFXCmpAlways); -// d->setupGenericShaders(); -// d->setTextureStageColorOp( 0, GFXTOPDisable ); -// -// static U32 start = 175000, finish = 225000; -// if(start < 10000) -// start = 175000; -// if(finish > 320000) -// finish = 225000; -// -// start -= 1; -// finish += 2; -// -// RectI rect = RectI(0, 0, 320, 320); -// d->setClipRect(rect); -// d->setViewport(rect); -// d->getDrawUtil()->drawLine(Point2I(start/1000, start/1000), Point2I(finish/1000, finish/1000), ColorI(0, 255, 0, 255)); -// d->getDrawUtil()->drawLine(start/1000, start/1000, finish/1000, finish/1000, ColorI(255, 0, 0, 127)); -// } -// -// void run() -// { -// mRenderer.renderSignal.notify(this, &TestGFXLineDraw::onRenderEvent); -// mRenderer.go(); -// mRenderer.destroy(); -// } -//}; -// -//// This test uses GFXDevice::drawRect to draw a rect. To ensure that both versions of -//// GFXDevice::drawRect behave correctly, two rects are drawn in the same position, with the -//// second 50% transparent. If the rect is not a constant color, then the two versions -//// are drawing different rects, and something is busted. -//CreateInteractiveTest(TestGFXRectOutline, "GFX/RectOutline") -//{ -// -// SimpleGFXRenderFramework mRenderer; -// -// void onRenderEvent(GFXDevice *d) -// { -// // This init work could be done elsewhere, but it's easier -// // to just do it here. -// -// // Set up the view... -// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// //d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); -// MatrixF worldMatrix(1); -// -// d->setWorldMatrix(worldMatrix); -// //d->setProjectionMatrix(worldMatrix); -// -// d->setBaseRenderState(); -// d->setCullMode(GFXCullNone); -// d->setVertexColorEnable(true); -// d->setAlphaBlendEnable(true); -// d->setSrcBlend(GFXBlendSrcAlpha); -// d->setDestBlend(GFXBlendInvSrcAlpha); -// d->setZEnable(false); -// d->setZFunc(GFXCmpAlways); -// d->setupGenericShaders(); -// d->setTextureStageColorOp( 0, GFXTOPDisable ); -// -// static U32 extent = 0; -// static U32 startPoint = 200000; -// extent += 2; -// startPoint -= 1; -// if(extent > 350000) -// extent = 0; -// if(startPoint == 0) -// startPoint = 200000; -// -// RectI rect = RectI(0, 0, 320, 320); -// d->setClipRect(rect); -// d->setViewport(rect); -// -// d->getDrawUtil()->drawRect(RectI(startPoint/1000, startPoint/1000, extent/1000, extent/1000), ColorI(0, 255, 0, 127)); -// d->getDrawUtil()->drawRect(Point2I(startPoint/1000, startPoint/1000), -// Point2I(startPoint/1000 + extent/1000, startPoint/1000 + extent/1000), ColorI(255, 0, 0, 127)); -// } -// -// void run() -// { -// mRenderer.renderSignal.notify(this, &TestGFXRectOutline::onRenderEvent); -// mRenderer.go(); -// mRenderer.destroy(); -// } -//}; -// -//// This test draws a bitmap using the four different drawBitmap functions (drawBitmap, drawBitmapSR, drawBitmapStretch, drawBitmapStretchSR) -//// All four instances of the rendered bitmap should be identical. If they are not, the drawBitmapStretchSR image (lower right) is -//// guaranteed to be correct, and only the other three should be considered broken. -//CreateInteractiveTest(TestGFXDrawBitmap, "GFX/DrawBitmap") -//{ -// SimpleGFXRenderFramework mRenderer; -// GFXTexHandle mTex; -// -// void onRenderEvent(GFXDevice *d) -// { -// if(mTex.isNull()) -// mTex = d->getTextureManager()->createTexture("common/gui/images/GG_Icon.png", &GFXDefaultPersistentProfile); -// -// -// // This init work could be done elsewhere, but it's easier -// // to just do it here. -// -// // Set up the view... -// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// //d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); -// MatrixF worldMatrix(1); -// -// d->setWorldMatrix(worldMatrix); -// //d->setProjectionMatrix(worldMatrix); -// -// d->setBaseRenderState(); -// d->setCullMode(GFXCullNone); -// d->setVertexColorEnable(true); -// d->setAlphaBlendEnable(true); -// d->setSrcBlend(GFXBlendSrcAlpha); -// d->setDestBlend(GFXBlendInvSrcAlpha); -// d->setZEnable(true); -// d->setZFunc(GFXCmpAlways); -// d->setupGenericShaders(); -// d->setTextureStageColorOp( 0, GFXTOPModulate ); -// -// RectI rect = RectI(0, 0, 320, 320); -// d->setClipRect(rect); -// d->setViewport(rect); -// //d->getDrawer()->drawBitmap(mTex, Point2I(0, 0)); -// //d->getDrawer()->drawBitmapSR(mTex, Point2I(35, 0), RectI(0, 0, 32, 32)); -// //d->getDrawer()->drawBitmapStretch(mTex, RectI(0, 35, 32, 32)); -// d->getDrawUtil()->drawBitmapStretchSR(mTex, RectI(0, 0, 320, 320), RectI(0, 0, 32, 32)); -// -// } -// void run() -// { -// mRenderer.renderSignal.notify(this, &TestGFXDrawBitmap::onRenderEvent); -// mRenderer.go(); -// mRenderer.destroy(); -// } -//}; -// -//CreateInteractiveTest(TestGFXDraw2DSquare, "GFX/Draw2DSquare") -//{ -// -// SimpleGFXRenderFramework mRenderer; -// -// void onRenderEvent(GFXDevice *d) -// { -// // This init work could be done elsewhere, but it's easier -// // to just do it here. -// -// // Set up the view... -// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// //d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); -// MatrixF worldMatrix(1); -// -// d->setWorldMatrix(worldMatrix); -// //d->setProjectionMatrix(worldMatrix); -// -// d->setBaseRenderState(); -// d->setCullMode(GFXCullNone); -// d->setVertexColorEnable(true); -// d->setAlphaBlendEnable(true); -// d->setSrcBlend(GFXBlendSrcAlpha); -// d->setDestBlend(GFXBlendInvSrcAlpha); -// d->setZEnable(true); -// d->setZFunc(GFXCmpAlways); -// d->setupGenericShaders(); -// d->setTextureStageColorOp( 0, GFXTOPDisable ); -// static U32 extent = 0; -// static F32 spinDiddle = 0; -// static U32 startPoint = 200000; -// extent += 2; -// startPoint -= 1; -// spinDiddle += 0.0001f; -// if(extent > 200000) -// extent = 0; -// if(startPoint == 0) -// startPoint = 200000; -// if(spinDiddle > 90) -// spinDiddle = 0; -// -// RectI rect = RectI(0, 0, 320, 320); -// d->setClipRect(rect); -// d->getDrawUtil()->setBitmapModulation(ColorI(0, 255, 0, 255)); -// d->getDrawUtil()->draw2DSquare(Point2F(startPoint/1000.0f, startPoint/1000.0f), extent/1000.0f, spinDiddle); -// } -// -// void run() -// { -// mRenderer.renderSignal.notify(this, &TestGFXDraw2DSquare::onRenderEvent); -// mRenderer.go(); -// mRenderer.destroy(); -// } -//}; -// -//// This test uses GFXDevice::drawRectFill to draw a rect. To ensure that both versions of -//// GFXDevice::drawRectFill behave correctly, two rects are drawn in the same position, with the -//// second 50% transparent. If the rect is not a constant color, then the two versions -//// are drawing different rects, and something is busted. -//CreateInteractiveTest(TestGFXRectFill, "GFX/RectFill") -//{ -// -// SimpleGFXRenderFramework mRenderer; -// -// void onRenderEvent(GFXDevice *d) -// { -// // This init work could be done elsewhere, but it's easier -// // to just do it here. -// -// // Set up the view... -// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// //d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); -// MatrixF worldMatrix(1); -// -// d->setWorldMatrix(worldMatrix); -// //d->setProjectionMatrix(worldMatrix); -// -// d->setBaseRenderState(); -// d->setCullMode(GFXCullNone); -// d->setVertexColorEnable(true); -// d->setAlphaBlendEnable(true); -// d->setSrcBlend(GFXBlendSrcAlpha); -// d->setDestBlend(GFXBlendInvSrcAlpha); -// d->setZEnable(true); -// d->setZFunc(GFXCmpAlways); -// d->setupGenericShaders(); -// d->setTextureStageColorOp( 0, GFXTOPDisable ); -// static U32 extent = 0; -// static U32 startPoint = 200000; -// extent += 2; -// startPoint -= 1; -// if(extent > 350000) -// extent = 0; -// if(startPoint == 0) -// startPoint = 200000; -// -// RectI rect = RectI(0, 0, 320, 320); -// d->setClipRect(rect); -// -// d->getDrawUtil()->drawRectFill(RectI(startPoint/1000, startPoint/1000, extent/1000, extent/1000), ColorI(0, 255, 0, 127)); -// d->getDrawUtil()->drawRectFill(Point2I(startPoint/1000, startPoint/1000), -// Point2I(startPoint/1000 + extent/1000, startPoint/1000 + extent/1000), ColorI(255, 0, 0, 127)); -// } -// -// void run() -// { -// mRenderer.renderSignal.notify(this, &TestGFXRectFill::onRenderEvent); -// mRenderer.go(); -// mRenderer.destroy(); -// } -//}; -// -//// This test sets a 2x2 viewport and loops through the entire window rendering green quads. If -//// viewport setting works, it should result in a window full of green. -//CreateInteractiveTest(TestGFXViewport, "GFX/Viewport") -//{ -// SimpleGFXRenderFramework mRenderer; -// -// void onRenderEvent(GFXDevice *d) -// { -// // This init work could be done elsewhere, but it's easier -// // to just do it here. -// -// // Set up the view... -// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// //d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); -// MatrixF worldMatrix(1); -// -// d->setWorldMatrix(worldMatrix); -// d->setProjectionMatrix(worldMatrix); -// -// d->setBaseRenderState(); -// d->setCullMode(GFXCullNone); -// d->setVertexColorEnable(true); -// d->setAlphaBlendEnable(false); -// d->setZEnable(true); -// d->setZFunc(GFXCmpAlways); -// d->setupGenericShaders(); -// d->setTextureStageColorOp( 0, GFXTOPDisable ); -// -// RectI viewport; -// viewport.point.set(0, 0); -// viewport.extent.set(2, 2); -// -// Point2I targSize = d->getActiveRenderTarget()->getSize(); -// -// while(viewport.point.x < targSize.x) -// { -// while(viewport.point.y < targSize.y) -// { -// d->setViewport(viewport); -// PrimBuild::color4f( 0.0, 1.0, 0.0, 1.0 ); -// PrimBuild::begin( GFXTriangleFan, 4 ); -// -// PrimBuild::vertex3f( -1.0, -1.0, 0.0 ); -// -// PrimBuild::vertex3f( -1.0, 1.0, 0.0 ); -// -// PrimBuild::vertex3f( 1.0, 1.0, 0.0 ); -// -// PrimBuild::vertex3f( 1.0, -1.0, 0.0 ); -// PrimBuild::end(); -// viewport.point.y += 2; -// } -// viewport.point.y = 0; -// viewport.point.x += 2; -// } -// } -// -// void run() -// { -// mRenderer.renderSignal.notify(this, &TestGFXViewport::onRenderEvent); -// mRenderer.go(); -// mRenderer.destroy(); -// } -//}; -// -//CreateInteractiveTest(TestGFXSolidCube, "GFX/SolidCube") -//{ -// -// SimpleGFXRenderFramework mRenderer; -// CubeBuilder mCube; -// -// void onRenderEvent(GFXDevice *d) -// { -// // This init work could be done elsewhere, but it's easier -// // to just do it here. -// -// // Make sure we have a valid cube to render with. -// mCube.ensureValid(d, 1.0f); -// -// // Set up the view... -// //d->setFrustum(90.0f, 1.0f, 0.1, 100.f); -// d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); -// MatrixF worldMatrix(1); -// -// // Get some cheesy spin going... -// static F32 spinDiddle = 0.f; -// -// worldMatrix *= MatrixF(EulerF(0,spinDiddle, 90.0f - spinDiddle )); -// worldMatrix.setPosition(Point3F(0.f, 5.f, 0.f)); -// -// //spinDiddle += 0.0001f; -// -// if(spinDiddle > 90.f) -// spinDiddle = 0.f; -// -// d->setWorldMatrix(worldMatrix); -// -// // Draw our cube. -// d->setVertexBuffer(mCube.mCubeVB); -// d->setPrimitiveBuffer(mCube.mCubePB); -// -// d->setBaseRenderState(); -// d->setCullMode(GFXCullNone); -// d->setVertexColorEnable(true); -// d->setAlphaBlendEnable(false); -// d->setZEnable(true); -// d->setZFunc(GFXCmpLess); -// d->setupGenericShaders(); -// d->setTextureStageColorOp( 0, GFXTOPDisable ); -// -// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); -// } -// -// void run() -// { -// mRenderer.renderSignal.notify(this, &TestGFXSolidCube::onRenderEvent); -// mRenderer.go(); -// mCube.destroy(); -// mRenderer.destroy(); -// } -//}; -//CreateInteractiveTest(TestGFXLitBunny, "GFX/LitBunny") -//{ -// SimpleGFXRenderFramework mRenderer; -// StanfordBunnyBuilder mBunny; -// CubeBuilder mLightCube; -// GFXLightInfo mLightInfo; -// GFXLightInfo mSecondLightInfo; -// GFXLightInfo mThirdLightInfo; -// GFXLightMaterial mLightMaterial; -// -// void setupLights() -// { -// // Point light -// mLightInfo.mType = GFXLightInfo::Point; -// -// // Simple color -// mLightInfo.mColor = ColorF(1.0, 0.0, 0.0, 1.0); -// -// // No ambient -// mLightInfo.mAmbient = ColorF(0.0, 0.0, 0.0, 1.0); -// -// // Irrelevant for point lights -// mLightInfo.mDirection = Point3F(0.0f, 1.0f, 0.0f); -// -// // Position IN WORLD SPACE -// mLightInfo.mPos = Point3F(0.0f, 1.5f, 1.0f); -// -// // Radius -// mLightInfo.mRadius = 2.0f; -// -// -// mSecondLightInfo.mType = GFXLightInfo::Point; -// mSecondLightInfo.mColor = ColorF(0.0, 0.0, 1.0, 1.0); -// mSecondLightInfo.mAmbient = ColorF(0.0, 0.0, 0.0, 1.0); -// mSecondLightInfo.mDirection = Point3F(0.0f, 1.0f, 0.0f); -// mSecondLightInfo.mPos = Point3F(1.0f, 1.0f, 0.0f); -// mSecondLightInfo.mRadius = 2.0f; -// -// mThirdLightInfo.mType = GFXLightInfo::Point; -// mThirdLightInfo.mColor = ColorF(0.0, 1.0, 0.0, 1.0); -// mThirdLightInfo.mAmbient = ColorF(0.0, 0.0, 0.0, 1.0); -// mThirdLightInfo.mDirection = Point3F(0.0f, 1.0f, 0.0f); -// mThirdLightInfo.mPos = Point3F(-1.0f, 1.0f, -1.0f); -// mThirdLightInfo.mRadius = 2.0f; -// } -// -// void onRenderEvent(GFXDevice *d) -// { -// mBunny.ensureValid(d); -// mLightCube.ensureValid(d, 0.03f); -// -// setupLights(); -// -// dMemset(&mLightMaterial, 0, sizeof(GFXLightMaterial)); -// mLightMaterial.ambient = ColorF(1.0, 0.0, 0.0, 1.0); -// mLightMaterial.diffuse = ColorF(1.0, 1.0, 1.0, 1.0); -// -// // Set up the view... -// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// MatrixF projectionMatrix = d->getProjectionMatrix(); -// //d->setOrtho(-10.f, 10.f, -10.f, 10.f, 0.1f, 10.f, false); -// MatrixF worldMatrix(1); -// MatrixF lightMatrix(1); -// MatrixF secondLightMatrix(1); -// MatrixF thirdLightMatrix(1); -// -// // Get some cheesy spin going... -// static F32 spinDiddle = 0.f; -// -// // Bunny location -// worldMatrix *= MatrixF(EulerF(0,spinDiddle, 90.0f - spinDiddle )); -// worldMatrix.setPosition(Point3F(0.f, 1.5f, 0.f)); -// -// // Spinning cube of light -// lightMatrix *= MatrixF(EulerF(0,spinDiddle, 90.0f - spinDiddle )); -// lightMatrix.setPosition(mLightInfo.mPos); -// -// secondLightMatrix *= MatrixF(EulerF(0,spinDiddle, 90.0f - spinDiddle )); -// secondLightMatrix.setPosition(mSecondLightInfo.mPos); -// -// thirdLightMatrix *= MatrixF(EulerF(0,spinDiddle, 90.0f - spinDiddle )); -// thirdLightMatrix.setPosition(mThirdLightInfo.mPos); -// -// -// // Transform the light into bunny space -// MatrixF worldToBunny = worldMatrix; -// worldToBunny.inverse(); -// worldToBunny.mulP(mLightInfo.mPos); -// worldToBunny.mulP(mSecondLightInfo.mPos); -// worldToBunny.mulP(mThirdLightInfo.mPos); -// -// spinDiddle += 0.001f; -// -// if(spinDiddle > 90.f) -// spinDiddle = 0.f; -// -// // Cheat. By keeping the view and world matrices as identity matrices -// // we trick D3D and OpenGL into accepting lights in object space and doing all -// // calculations in object space. This way we don't have to do ugly API specific -// // stuff anywhere. -// d->setProjectionMatrix(projectionMatrix * worldMatrix); -// -// // Draw our Bunny. -// d->setVertexBuffer(mBunny.mBunnyVB); -// -// d->setBaseRenderState(); -// d->setCullMode(GFXCullCW); -// -// // Need to set a material or D3D refuses to render, but OpenGL works. -// // CodeReview - Should the D3D layer leave a default material bound? - AlexS 4/17/07 -// d->setLightMaterial(mLightMaterial); -// -// // Enable Lighting -// d->setLightingEnable(true); -// -// // Allow the use of vertex colors in lighting calculations -// d->setVertexColorEnable(true); -// -// // Use the vertex color as the diffuse material source -// d->setDiffuseMaterialSource(GFXMCSColor1); -// -// // Use the vertex color as the ambient material source -// d->setAmbientMaterialSource(GFXMCSColor1); -// -// // Set our light -// d->setLight(0, &mLightInfo); -// d->setLight(1, &mSecondLightInfo); -// d->setLight(2, &mThirdLightInfo); -// -// -// d->setAlphaBlendEnable(false); -// d->setZEnable(true); -// d->setZFunc(GFXCmpLess); -// d->setupGenericShaders(); -// d->setTextureStageColorOp( 0, GFXTOPDisable ); -// -// d->drawPrimitive(GFXTriangleList, 0, 16301); -// -// // Draw a cube for our light. -// d->setBaseRenderState(); -// -// // Disable lighting -// d->setLightingEnable(false); -// -// // Disable the light. Not strictly necessary, but still good practice. -// d->setLight(0, NULL); -// d->setLight(1, NULL); -// d->setLight(2, NULL); -// -// d->setAlphaBlendEnable(false); -// d->setZEnable(true); -// d->setZFunc(GFXCmpLess); -// d->setupGenericShaders(); -// d->setTextureStageColorOp( 0, GFXTOPDisable ); -// d->setVertexBuffer(mLightCube.mCubeVB); -// d->setPrimitiveBuffer(mLightCube.mCubePB); -// //d->setWorldMatrix(lightMatrix); -// -// d->setProjectionMatrix(projectionMatrix * lightMatrix); -// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); -// -// d->setProjectionMatrix(projectionMatrix * secondLightMatrix); -// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); -// -// d->setProjectionMatrix(projectionMatrix * thirdLightMatrix); -// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); -// } -// -// void run() -// { -// mRenderer.renderSignal.notify(this, &TestGFXLitBunny::onRenderEvent); -// mRenderer.go(); -// mRenderer.destroy(); -// } -//}; -// -//CreateInteractiveTest(TestGFXTextureCube, "GFX/TextureCube") -//{ -// -// SimpleGFXRenderFramework mRenderer; -// CubeBuilder mCube; -// GFXTexHandle mTex; -// -// void onRenderEvent(GFXDevice *d) -// { -// // This init work could be done elsewhere, but it's easier -// // to just do it here. -// -// // Make sure we have a valid cube to render with. -// mCube.ensureValid(d, 1.0f); -// -// // Make sure we have a valid texture to render with. -// if(mTex.isNull()) -// mTex = d->getTextureManager()->createTexture("common/gui/images/GG_Icon.png", &GFXDefaultPersistentProfile); -// -// // Set up the view... -// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// MatrixF worldMatrix(1); -// -// // Get some cheesy spin going... -// static F32 spinDiddle = 0.f; -// -// worldMatrix *= MatrixF(EulerF(0.f,spinDiddle, 90.f - spinDiddle )); -// worldMatrix.setPosition(Point3F(0.f, 5.f, 0.f)); -// -// spinDiddle += 0.001f; -// -// if(spinDiddle > 90.f) -// spinDiddle = 0.f; -// -// d->setWorldMatrix(worldMatrix); -// -// // Draw our cube. -// d->setVertexBuffer(mCube.mCubeVB); -// d->setPrimitiveBuffer(mCube.mCubePB); -// -// d->setBaseRenderState(); -// d->setCullMode(GFXCullNone); -// d->setVertexColorEnable(true); -// d->setAlphaBlendEnable(false); -// d->setZEnable(true); -// d->setZFunc(GFXCmpLess); -// d->setupGenericShaders(); -// -// // Turn on texture, with a cheesy vertex modulate (whee!) -// d->setTextureStageColorOp( 0, GFXTOPModulate ); -// d->setTexture(0, mTex); -// -// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); -// } -// -// void run() -// { -// mRenderer.renderSignal.notify(this, &TestGFXTextureCube::onRenderEvent); -// mRenderer.go(); -// mTex = NULL; -// mCube.destroy(); -// mRenderer.destroy(); -// } -//}; -// -//CreateInteractiveTest(TestGFXTextureLock, "GFX/TextureLock") -//{ -// SimpleGFXRenderFramework mRenderer; -// CubeBuilder mCube; -// GFXTexHandle mTex; -// -// void onRenderEvent(GFXDevice *d) -// { -// // This init work could be done elsewhere, but it's easier -// // to just do it here. -// -// // Make sure we have a valid cube to render with. -// mCube.ensureValid(d, 1.0f); -// -// // Make sure we have a valid texture to render with. -// if(mTex.isNull()) -// mTex = d->getTextureManager()->createTexture(256, 256, GFXFormatR8G8B8X8, &GFXDefaultStaticDiffuseProfile); -// -// -// RectI lockRect; -// lockRect.point.x = gRandGen.randI(0, 255); -// lockRect.point.y = gRandGen.randI(0, 255); -// lockRect.extent.x = gRandGen.randI(1, 256 - lockRect.point.x); -// lockRect.extent.y = gRandGen.randI(1, 256 - lockRect.point.y); -// -// //U8 r, g, b; -// //r = (U8)gRandGen.randI(0, 255); -// //g = (U8)gRandGen.randI(0, 255); -// //b = (U8)gRandGen.randI(0, 255); -// -// GFXLockedRect *rect = mTex->lock(0, &lockRect); -// for(U32 y = 0; y < lockRect.extent.y; y++) -// { -// for(U32 x = 0; x < lockRect.extent.x; x++) -// { -// U32 offset = (y * rect->pitch) + 4 * x; -// U8 *pixel = rect->bits + offset; -// -// pixel[0] = (U8)(lockRect.point.y + y); -// pixel[1] = (U8)(lockRect.point.y + y); -// pixel[2] = (U8)(lockRect.point.y + y); -// pixel[3] = 255; -// } -// } -// -// mTex->unlock(0); -// -// // Set up the view... -// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// MatrixF worldMatrix(1); -// -// // Get some cheesy spin going... -// static F32 spinDiddle = 0.f; -// -// worldMatrix *= MatrixF(EulerF(0.f,spinDiddle, 90.f - spinDiddle )); -// worldMatrix.setPosition(Point3F(0.f, 3.f, 0.f)); -// -// spinDiddle += 0.001f; -// -// if(spinDiddle > 90.f) -// spinDiddle = 0.f; -// -// d->setWorldMatrix(worldMatrix); -// -// // Draw our cube. -// d->setVertexBuffer(mCube.mCubeVB); -// d->setPrimitiveBuffer(mCube.mCubePB); -// -// d->setBaseRenderState(); -// d->setCullMode(GFXCullNone); -// d->setVertexColorEnable(true); -// d->setAlphaBlendEnable(false); -// d->setZEnable(true); -// d->setZFunc(GFXCmpLess); -// d->setupGenericShaders(); -// -// // Turn on texture, with a cheesy vertex modulate (whee!) -// d->setTextureStageColorOp( 0, GFXTOPModulate ); -// d->setTexture(0, mTex); -// -// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); -// } -// -// void run() -// { -// mRenderer.renderSignal.notify(this, &TestGFXTextureLock::onRenderEvent); -// mRenderer.go(); -// mTex = NULL; -// mCube.destroy(); -// mRenderer.destroy(); -// } -//}; -// -//CreateInteractiveTest(TestGFXMultiTextureCube, "GFX/MultiTextureCube") -//{ -// -// SimpleGFXRenderFramework mRenderer; -// CubeBuilder mCube; -// GFXTexHandle mTex0; -// GFXTexHandle mTex1; -// -// void onRenderEvent(GFXDevice *d) -// { -// // This init work could be done elsewhere, but it's easier -// // to just do it here. -// -// // Make sure we have a valid cube to render with. -// mCube.ensureValid(d, 1.0f); -// -// // Make sure we have a valid texture to render with. -// if(mTex0.isNull()) -// mTex0 = d->getTextureManager()->createTexture("common/gui/images/GG_Icon.png", &GFXDefaultPersistentProfile); -// if(mTex1.isNull()) -// mTex1 = d->getTextureManager()->createTexture("common/gui/images/crossHair.png", &GFXDefaultPersistentProfile); -// -// // Set up the view... -// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// MatrixF worldMatrix(1); -// -// // Get some cheesy spin going... -// static F32 spinDiddle = 0.f; -// -// worldMatrix *= MatrixF(EulerF(0.f,spinDiddle, 90.f - spinDiddle )); -// worldMatrix.setPosition(Point3F(0.f, 5.f, 0.f)); -// -// spinDiddle += 0.001f; -// -// if(spinDiddle > 90.f) -// spinDiddle = 0.f; -// -// d->setWorldMatrix(worldMatrix); -// -// // Draw our cube. -// d->setVertexBuffer(mCube.mCubeVB); -// d->setPrimitiveBuffer(mCube.mCubePB); -// -// d->setBaseRenderState(); -// d->setCullMode(GFXCullNone); -// d->setVertexColorEnable(true); -// d->setAlphaBlendEnable(false); -// d->setZEnable(true); -// d->setZFunc(GFXCmpLess); -// d->setupGenericShaders(); -// -// // Turn on texture, with a cheesy vertex modulate (whee!) -// d->setTextureStageColorOp( 0, GFXTOPModulate); -// d->setTexture(0, mTex0); -// d->setTextureStageColorOp( 1, GFXTOPModulate ); -// d->setTexture(1, mTex1); -// -// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); -// } -// -// void run() -// { -// mRenderer.renderSignal.notify(this, &TestGFXMultiTextureCube::onRenderEvent); -// mRenderer.go(); -// mTex0 = NULL; -// mTex1 = NULL; -// mCube.destroy(); -// mRenderer.destroy(); -// } -//}; -// -//CreateInteractiveTest(TestGFXRenderTargetCube, "GFX/RenderTargetCube") -//{ -// SimpleGFXRenderFramework mRenderer; -// CubeBuilder mCube; -// GFXTexHandle mTex; -// GFXTextureTargetRef mRenderTarget; -// -// void drawCube(GFXDevice *d) -// { -// // Draw our cube. -// d->setVertexBuffer(mCube.mCubeVB); -// d->setPrimitiveBuffer(mCube.mCubePB); -// -// d->setBaseRenderState(); -// d->setCullMode(GFXCullNone); -// d->setVertexColorEnable(true); -// d->setAlphaBlendEnable(false); -// d->setupGenericShaders(); -// -// // Turn on texture, with a cheesy vertex modulate (whee!) -// d->setTextureStageColorOp( 0, GFXTOPModulate ); -// -// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); -// } -// -// void onRenderEvent(GFXDevice *d) -// { -// // This init work could be done elsewhere, but it's easier -// // to just do it here. -// -// // Make sure we have a valid cube to render with. -// mCube.ensureValid(d, 1.0f); -// -// // Make sure we have a valid texture to render with. -// if(mTex.isNull()) -// mTex = d->getTextureManager()->createTexture(256, 256, GFXFormatR8G8B8X8, &GFXDefaultRenderTargetProfile); -// -// // Make sure we have a render target. -// if(mRenderTarget == NULL) -// mRenderTarget = d->allocRenderToTextureTarget(); -// -// // Update the render target. -// { -// d->setTexture(0, NULL); -// -// mRenderTarget->attachTexture(GFXTextureTarget::Color0, mTex); -// mRenderTarget->attachTexture(GFXTextureTarget::DepthStencil, GFXTextureTarget::sDefaultDepthStencil); -// d->setActiveRenderTarget(mRenderTarget); -// d->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 0, 0, 0 ), 1.0f, 0 ); -// -// -// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// MatrixF worldMatrix(1); -// -// // Get some cheesy spin going... -// static F32 spinDiddle = 45.f; -// -// worldMatrix *= MatrixF(EulerF(0,spinDiddle, 90.f - spinDiddle )); -// worldMatrix.setPosition(Point3F(0, 3, 0)); -// -// spinDiddle += 0.001f; -// -// if(spinDiddle > 90.f) -// spinDiddle = 0.f; -// -// d->setWorldMatrix(worldMatrix); -// -// drawCube(d); -// -// // Detach the texture so we can continue on w/ rendering... -// mRenderTarget->attachTexture(GFXTextureTarget::Color0, NULL); -// } -// -// -// // Render to the window... -// { -// d->setActiveRenderTarget(mRenderer.mWindow->getGFXTarget()); -// d->setTexture(0, mTex); -// -// // Set up the view... -// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// MatrixF worldMatrix(1); -// -// // Get some cheesy spin going... -// static F32 spinDiddle = 0.f; -// -// worldMatrix *= MatrixF(EulerF(0,spinDiddle, 90.f - spinDiddle )); -// worldMatrix.setPosition(Point3F(0, 5, 0)); -// -// spinDiddle += 0.001f; -// -// if(spinDiddle > 90.f) -// spinDiddle = 0.f; -// -// d->setWorldMatrix(worldMatrix); -// d->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 0, 0, 0 ), 1.0f, 0 ); -// -// drawCube(d); -// } -// } -// -// void run() -// { -// mRenderTarget = NULL; -// mRenderer.renderSignal.notify(this, &TestGFXRenderTargetCube::onRenderEvent); -// mRenderer.go(); -// -// mTex = NULL; -// mRenderTarget = NULL; -// mCube.destroy(); -// mRenderer.destroy(); -// } -//}; -// -// -//CreateInteractiveTest(TestGFXRenderTargetStack, "GFX/RenderTargetStack") -//{ -// enum -// { -// NumRenderTargets = 2, -// MaxRenderTargetDepth = 3, -// MaxRenderTargetsPerFrame = 10 -// }; -// -// SimpleGFXRenderFramework mRenderer; -// GFXTexHandle mTex[NumRenderTargets]; -// ColorI mTexLastClearColor[NumRenderTargets]; -// GFXTextureTargetRef mRenderTarget; -// CubeBuilder mCube; -// -// void drawCube(GFXDevice *d) -// { -// // Draw our cube. -// d->setVertexBuffer(mCube.mCubeVB); -// d->setPrimitiveBuffer(mCube.mCubePB); -// -// d->setBaseRenderState(); -// d->setCullMode(GFXCullNone); -// d->setVertexColorEnable(true); -// d->setAlphaBlendEnable(false); -// d->setupGenericShaders(); -// -// // Turn on texture, with a cheesy vertex modulate (whee!) -// d->setTextureStageColorOp( 0, GFXTOPModulate ); -// -// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); -// } -// -// void onRenderEvent(GFXDevice *d) -// { -// // Make sure cube is ready to go. -// mCube.ensureValid(d, 1.0f); -// -// // Make sure we have a render to texture target. -// if(mRenderTarget == NULL) -// mRenderTarget = d->allocRenderToTextureTarget(); -// -// // Make sure all our textures are allocated. -// for(S32 i=0; igetTextureManager()->createTexture(256, 256, GFXFormatR8G8B8X8, &GFXDefaultRenderTargetProfile); -// } -// -// // Render to our different target textures. -// d->pushActiveRenderTarget(); -// -// // Set a starting texture so we can bind w/o any nulls. -// mRenderTarget->attachTexture(GFXTextureTarget::Color0, mTex[0]); -// -// // Now set the render target active.. -// d->setActiveRenderTarget(mRenderTarget); -// -// // Iterate over our render targets. -// for(S32 i=0; iattachTexture(GFXTextureTarget::Color0, mTex[i]); -// d->clear( GFXClearTarget, ColorI( (i+1)*80, (i)*150, 0 ), 1.0f, 0 ); -// } -// -// // Unbind everything so we don't have dangling references. -// mRenderTarget->attachTexture(GFXTextureTarget::Color0, NULL); -// d->popActiveRenderTarget(); -// -// // Set up the view... -// d->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// -// // Cheesy little positional offset table. -// F32 posOffsets[4][3] = -// { -// { -2, 5, -2}, -// { -2, 5, 2}, -// { 2, 5, -2}, -// { 2, 5, 2}, -// }; -// AssertFatal(NumRenderTargets <= 4, "Need more position offsets to draw cubes at."); -// -// // Let's draw a cube for each RT. -// for(S32 i=0; i 90.f) -// spinDiddle = 0.f; -// -// d->setWorldMatrix(worldMatrix); -// d->setTexture(0, mTex[i]); -// -// drawCube(d); -// } -// -// // Clean up. -// d->setTexture(0, NULL); -// } -// -// void run() -// { -// mRenderTarget = NULL; -// -// mRenderer.renderSignal.notify(this, &TestGFXRenderTargetStack::onRenderEvent); -// mRenderer.go(); -// -// // Clean stuff up. -// mRenderTarget = NULL; -// -// for(S32 i=0; isetVertexBuffer(mCube->mCubeVB); -// d->setPrimitiveBuffer(mCube->mCubePB); -// -// d->setBaseRenderState(); -// d->setCullMode(GFXCullNone); -// d->setVertexColorEnable(true); -// d->setAlphaBlendEnable(false); -// d->setupGenericShaders(); -// -// // Turn on texture, with a cheesy vertex modulate (whee!) -// d->setTextureStageColorOp( 0, GFXTOPModulate ); -// -// d->drawIndexedPrimitive(GFXTriangleList, 0, 8, 0, 12); -// } -// -// void onRenderSignal(WindowId id) -// { -// mDevice->beginScene(); -// mDevice->setActiveRenderTarget(mWindow->getGFXTarget()); -// -// // Fill this in an interesting way... -// static U32 i=10; -// mDevice->clear( GFXClearZBuffer | GFXClearStencil | GFXClearTarget, ColorI( 0, i, 0 ), 1.0f, 0 ); -// i+=10; -// -// // Set up the view... -// mDevice->setFrustum(90.0f, 1.0f, 0.1f, 100.f); -// -// // Get some cheesy spin going... -// MatrixF worldMatrix(1); -// -// static F32 spinDiddle = 0.f; -// -// worldMatrix *= MatrixF(EulerF(0,spinDiddle, 90.f - spinDiddle )); -// worldMatrix.setPosition(Point3F(-2, 5, -2)); -// -// spinDiddle += 0.001f; -// -// if(spinDiddle > 90.f) -// spinDiddle = 0.f; -// -// mDevice->setWorldMatrix(worldMatrix); -// -// // set sampler if we have one (handle null device case) -// if (mDevice->getNumSamplers()) -// mDevice->setTexture(0, *mTex); -// -// // Draw our cube... -// drawCube(mDevice); -// -// // And swap. -// mDevice->endScene(); -// mWindow->getGFXTarget()->present(); -// } -// -// bool onAppSignal(WindowId d, S32 event) -// { -// if(event == WindowClose && d == mWindow->getWindowId()) -// Process::requestShutdown(); -// return true; -// } -// -// void run() -// { -// PlatformWindowManager *pwm = CreatePlatformWindowManager(); -// -// // Create a video mode to use. -// GFXVideoMode vm; -// vm.resolution.x = 400; -// vm.resolution.y = 400; -// -// // Query all the available devices and adapters. -// GFXInit::enumerateAdapters(); -// Vector adapters; -// GFXInit::getAdapters(&adapters); -// -// test(adapters.size() > 0, "Got zero adapters! Hard to run an adapter test with no adapters!"); -// -// // For each reported adapter... -// for(S32 i=0; imIndex, -// adapters[i]->mName, -// GFXInit::getAdapterNameFromType(adapters[i]->mType))); -// -// // Init the device. -// mDevice = GFXInit::createDevice(adapters[i]); -// test(mDevice, "Failed to create a device!"); -// -// if(!mDevice) -// continue; -// -// // Create the window... -// mWindow = pwm->createWindow(mDevice, vm); -// test(mWindow, "Failed to create a window for this device!"); -// -// if(!mWindow) -// { -// SAFE_DELETE(mDevice); -// continue; -// } -// -// // Create some representative items: -// // - a cube builder... -// mCube = new CubeBuilder(); -// mCube->ensureValid(mDevice, 1.0f); -// -// // - a texture -// mTex = new GFXTexHandle(); -// if((*mTex).isNull()) -// (*mTex) = mDevice->getTextureManager()->createTexture("common/gui/images/GG_Icon.png", -// &GFXDefaultPersistentProfile); -// -// // Hook in our events. -// // Setup our events. -// mWindow->signalRender.notify(this, &TestGFXDeviceSwitching::onRenderSignal); -// mWindow->signalApp.notify (this, &TestGFXDeviceSwitching::onAppSignal); -// -// // Render until the user gets bored. -// while(Process::processEvents()); -// -// // And clean up, so we can do it again. -// SAFE_DELETE(mTex); -// SAFE_DELETE(mCube); -// SAFE_DELETE(mDevice); -// SAFE_DELETE(mWindow); -// } -// -// // All done! -// SAFE_DELETE(pwm); -// } -//}; diff --git a/Tools/projectGenerator/modules/core.inc b/Tools/projectGenerator/modules/core.inc index 0e981dca5..d730cc1a2 100644 --- a/Tools/projectGenerator/modules/core.inc +++ b/Tools/projectGenerator/modules/core.inc @@ -126,7 +126,6 @@ switch( T3D_Generator::$platform ) // GFX addEngineSrcDir( 'gfx/Null' ); -addEngineSrcDir( 'gfx/test' ); addEngineSrcDir( 'gfx/bitmap' ); addEngineSrcDir( 'gfx/bitmap/loaders' ); addEngineSrcDir( 'gfx/util' ); From ae55b5f5527a0d69b42f70db92bfe83b465cc200 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Mon, 1 Sep 2014 19:39:29 -0500 Subject: [PATCH 170/317] Adds material definitions for groundcover referencing. --- .../Full/game/art/environment/materials.cs | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 Templates/Full/game/art/environment/materials.cs diff --git a/Templates/Full/game/art/environment/materials.cs b/Templates/Full/game/art/environment/materials.cs new file mode 100644 index 000000000..2b5a81e77 --- /dev/null +++ b/Templates/Full/game/art/environment/materials.cs @@ -0,0 +1,48 @@ +singleton Material(grass1) +{ + mapTo = "grass1.png"; + diffuseMap[0] = "art/environment/grass1.png"; + translucent = true; + materialTag0 = "Foliage"; + alphaTest = "1"; + alphaRef = "80"; +}; + +singleton Material(grass2) +{ + mapTo = "grass2.png"; + diffuseMap[0] = "art/environment/grass2.png"; + translucent = "0"; + materialTag0 = "Foliage"; + alphaTest = "1"; + alphaRef = "80"; +}; + +singleton Material(grass3) +{ + mapTo = "grass3.png"; + diffuseMap[0] = "art/environment/grass3.png"; + translucent = "0"; + materialTag0 = "Foliage"; + alphaTest = "1"; + alphaRef = "80"; +}; + +singleton Material(plant1) +{ + mapTo = "plant1.png"; + diffuseMap[0] = "art/environment/plant1.png"; + materialTag0 = "Foliage"; + alphaTest = "1"; + alphaRef = "80"; +}; + +singleton Material(plant2) +{ + mapTo = "plant2.png"; + diffuseMap[0] = "art/environment/plant2.png"; + translucent = true; + materialTag0 = "Foliage"; + alphaTest = "1"; + alphaRef = "80"; +}; From 69179cf809cf7764222cb129d8c1bfae05bebe5d Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Fri, 5 Sep 2014 14:09:35 +1000 Subject: [PATCH 171/317] Removed ShapeBase::Thread::sound. Weird feature that's not script-accessible or actually implemented. --- Engine/source/T3D/shapeBase.cpp | 22 ---------------------- Engine/source/T3D/shapeBase.h | 11 +---------- 2 files changed, 1 insertion(+), 32 deletions(-) diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index eff6bd855..56ec0e4d5 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -931,7 +931,6 @@ ShapeBase::ShapeBase() for (i = 0; i < MaxScriptThreads; i++) { mScriptThread[i].sequence = -1; mScriptThread[i].thread = 0; - mScriptThread[i].sound = 0; mScriptThread[i].state = Thread::Stop; mScriptThread[i].atEnd = false; mScriptThread[i].timescale = 1.f; @@ -2162,7 +2161,6 @@ bool ShapeBase::setThreadSequence(U32 slot, S32 seq, bool reset) if (!st.thread) st.thread = mShapeInstance->addThread(); mShapeInstance->setSequence(st.thread,seq,0); - stopThreadSound(st); updateThread(st); } return true; @@ -2189,7 +2187,6 @@ void ShapeBase::updateThread(Thread& st) } mShapeInstance->setTimeScale( st.thread, 0.f ); - stopThreadSound( st ); } break; case Thread::Play: @@ -2199,7 +2196,6 @@ void ShapeBase::updateThread(Thread& st) mShapeInstance->setTimeScale(st.thread,1); mShapeInstance->setPos( st.thread, ( st.timescale > 0.f ) ? 1.0f : 0.0f ); mShapeInstance->setTimeScale(st.thread,0); - stopThreadSound(st); st.state = Thread::Stop; } else @@ -2211,16 +2207,11 @@ void ShapeBase::updateThread(Thread& st) } mShapeInstance->setTimeScale(st.thread, st.timescale ); - if (!st.sound) - { - startSequenceSound(st); - } } } break; case Thread::Destroy: { - stopThreadSound(st); st.atEnd = true; st.sequence = -1; if(st.thread) @@ -2328,19 +2319,6 @@ bool ShapeBase::setThreadTimeScale( U32 slot, F32 timeScale ) return false; } -void ShapeBase::stopThreadSound(Thread& thread) -{ - if (thread.sound) { - } -} - -void ShapeBase::startSequenceSound(Thread& thread) -{ - if (!isGhost() || !thread.thread) - return; - stopThreadSound(thread); -} - void ShapeBase::advanceThreads(F32 dt) { for (U32 i = 0; i < MaxScriptThreads; i++) { diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index 0bb54faf4..22e25810d 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -733,8 +733,7 @@ protected: /// /// @see Thread::State S32 sequence; ///< The animation sequence which is running in this thread. - F32 timescale; ///< Timescale - U32 sound; ///< Handle to sound. + F32 timescale; ///< Timescale bool atEnd; ///< Are we at the end of this thread? F32 position; }; @@ -1354,14 +1353,6 @@ public: /// @param timescale Timescale bool setThreadTimeScale( U32 slot, F32 timeScale ); - /// Start the sound associated with an animation thread - /// @param thread Thread - void startSequenceSound(Thread& thread); - - /// Stop the sound associated with an animation thread - /// @param thread Thread - void stopThreadSound(Thread& thread); - /// Advance all animation threads attached to this shapebase /// @param dt Change in time from last call to this function void advanceThreads(F32 dt); From 9318f8faf34d73e6c607bc21a28b4c2fc6d88a12 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Fri, 5 Sep 2014 23:10:08 -0500 Subject: [PATCH 172/317] player triggers. these are what determine the timing ffor when footprint decals and a sound are played based on material configurations during a given animation. --- .../shapes/actors/Soldier/soldier_rigged.cs | 470 ++++++++---------- 1 file changed, 207 insertions(+), 263 deletions(-) diff --git a/Templates/Full/game/art/shapes/actors/Soldier/soldier_rigged.cs b/Templates/Full/game/art/shapes/actors/Soldier/soldier_rigged.cs index f15fe0265..e5f251e0c 100644 --- a/Templates/Full/game/art/shapes/actors/Soldier/soldier_rigged.cs +++ b/Templates/Full/game/art/shapes/actors/Soldier/soldier_rigged.cs @@ -27,7 +27,6 @@ singleton TSShapeConstructor(SoldierDAE) unit = "1.0"; upAxis = "DEFAULT"; lodType = "TrailingNumber"; - matNamePrefix = ""; ignoreNodeScale = "0"; adjustCenter = "0"; adjustFloor = "0"; @@ -36,266 +35,211 @@ singleton TSShapeConstructor(SoldierDAE) function SoldierDAE::onLoad(%this) { - // BEGIN: Default (Lurker Rifle) Sequences - %this.addSequence( "./Anims/PlayerAnim_Lurker_Back.dae Back", "Back", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Celebrate_01.dae Celebrate_01", "Celebrate_01", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Crouch_Backward.dae Crouch_Backward", "Crouch_Backward", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Crouch_Forward.dae Crouch_Forward", "Crouch_Forward", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Crouch_Side.dae Crouch_Side", "Crouch_Side", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Crouch_Root.dae Crouch_Root", "Crouch_Root", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Death1.dae Death1", "Death1", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Death2.dae Death2", "Death2", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Fall.dae Fall", "Fall", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Head.dae Head", "Head", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Jump.dae Jump", "Jump", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Land.dae Land", "Land", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Look.dae Look", "Look", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Reload.dae Reload", "Reload", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Root.dae Root", "Root", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Run.dae Run", "Run", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Side.dae Side", "Side", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Sitting.dae Sitting", "Sitting", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Swim_Backward.dae Swim_Backward", "Swim_Backward", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Swim_Forward.dae Swim_Forward", "Swim_Forward", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Swim_Root.dae Swim_Root", "Swim_Root", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Swim_Left.dae Swim_Left", "Swim_Left", 0, -1); - %this.addSequence( "./Anims/PlayerAnim_Lurker_Swim_Right.dae Swim_Right", "Swim_Right", 0, -1); - - %this.setSequenceCyclic( "Back", true); - %this.setSequenceCyclic( "Celebrate_01", false); - %this.setSequenceCyclic( "Crouch_Backward", true); - %this.setSequenceCyclic( "Crouch_Forward", true); - %this.setSequenceCyclic( "Crouch_Side", true); - %this.setSequenceCyclic( "Crouch_Root", true); - %this.setSequenceCyclic( "Death1", false); - %this.setSequenceCyclic( "Death2", false); - %this.setSequenceCyclic( "Fall", true); - %this.setSequenceCyclic( "Head", false); - %this.setSequenceCyclic( "Jump", false); - %this.setSequenceCyclic( "Land", false); - %this.setSequenceCyclic( "Look", false); - %this.setSequenceCyclic( "Reload", false); - %this.setSequenceCyclic( "Root", true); - %this.setSequenceCyclic( "Run", true); - %this.setSequenceCyclic( "Side", true); - %this.setSequenceCyclic( "Sitting", true); - %this.setSequenceCyclic( "Swim_Backward", true); - %this.setSequenceCyclic( "Swim_Forward", true); - %this.setSequenceCyclic( "Swim_Root", true); - %this.setSequenceCyclic( "Swim_Left", true); - %this.setSequenceCyclic( "Swim_Right", true); - - %this.setSequenceBlend( "Head", "1", "Root", "0"); - %this.setSequenceBlend( "Look", "1", "Root", "0"); - %this.setSequenceBlend( "Reload", "1", "Root", "0"); - - %this.setSequenceGroundSpeed( "Back", "0 -3.6 0"); - %this.setSequenceGroundSpeed( "Run", "0 5.0 0"); - %this.setSequenceGroundSpeed( "Side", "-3.6 0 0"); - %this.setSequenceGroundSpeed( "Swim_Backward", "0 -1 0"); - %this.setSequenceGroundSpeed( "Swim_Forward", "0 1 0"); - %this.setSequenceGroundSpeed( "Swim_Left", "-1 0 0"); - %this.setSequenceGroundSpeed( "Swim_Right", "1 0 0"); - %this.setSequenceGroundSpeed( "Crouch_Backward", "0 -2 0"); - %this.setSequenceGroundSpeed( "Crouch_Forward", "0 2 0"); - %this.setSequenceGroundSpeed( "Crouch_Side", "1 0 0"); - // END: Lurker Rifle Sequences - - // BEGIN: Ryder Pistol sequences - // Extracted from Ryder - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Back.dae Back", "Pistol_Back", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Crouch_Backward.dae Crouch_Backward", "Pistol_Crouch_Backward", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Crouch_Forward.dae Crouch_Forward", "Pistol_Crouch_Forward", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Crouch_Side.dae Crouch_Side", "Pistol_Crouch_Side", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Crouch_Root.dae Crouch_Root", "Pistol_Crouch_Root", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Death1.dae Death1", "Pistol_Death1", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Death2.dae Death2", "Pistol_Death2", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Fall.dae Fall", "Pistol_Fall", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Head.dae Head", "Pistol_Head", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Jump.dae Jump", "Pistol_Jump", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Land.dae Land", "Pistol_Land", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Look.dae Look", "Pistol_Look", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Reload.dae Reload", "Pistol_Reload", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Root.dae Root", "Pistol_Root", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Run.dae Run", "Pistol_Run", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Side.dae Side", "Pistol_Side", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Sitting.dae Sitting", "Pistol_Sitting", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Swim_Backward.dae Swim_Backward", "Pistol_Swim_Backward", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Swim_Forward.dae Swim_Forward", "Pistol_Swim_Forward", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Swim_Root.dae Swim_Root", "Pistol_Swim_Root", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Swim_Left.dae Swim_Left", "Pistol_Swim_Left", 0, -1); - %this.addSequence( "art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Swim_Right.dae Swim_Right", "Pistol_Swim_Right", 0, -1); - - %this.setSequenceCyclic( "Pistol_Back", true); - %this.setSequenceCyclic( "Pistol_Crouch_Backward", true); - %this.setSequenceCyclic( "Pistol_Crouch_Forward", true); - %this.setSequenceCyclic( "Pistol_Crouch_Side", true); - %this.setSequenceCyclic( "Pistol_Crouch_Root", true); - %this.setSequenceCyclic( "Pistol_Death1", false); - %this.setSequenceCyclic( "Pistol_Death2", false); - %this.setSequenceCyclic( "Pistol_Fall", true); - %this.setSequenceCyclic( "Pistol_Head", false); - %this.setSequenceCyclic( "Pistol_Jump", false); - %this.setSequenceCyclic( "Pistol_Land", false); - %this.setSequenceCyclic( "Pistol_Look", false); - %this.setSequenceCyclic( "Pistol_Reload", false); - %this.setSequenceCyclic( "Pistol_Root", true); - %this.setSequenceCyclic( "Pistol_Run", true); - %this.setSequenceCyclic( "Pistol_Side", true); - %this.setSequenceCyclic( "Pistol_Sitting", true); - %this.setSequenceCyclic( "Pistol_Swim_Backward", true); - %this.setSequenceCyclic( "Pistol_Swim_Forward", true); - %this.setSequenceCyclic( "Pistol_Swim_Root", true); - %this.setSequenceCyclic( "Pistol_Swim_Left", true); - %this.setSequenceCyclic( "Pistol_Swim_Right", true); - - %this.setSequenceBlend( "Pistol_Head", "1", "Pistol_Root", "0"); - %this.setSequenceBlend( "Pistol_Look", "1", "Pistol_Root", "0"); - %this.setSequenceBlend( "Pistol_Reload", "1", "Pistol_Root", "0"); - - %this.setSequenceGroundSpeed( "Pistol_Back", "0 -3.6 0"); - %this.setSequenceGroundSpeed( "Pistol_Run", "0 5.0 0"); - %this.setSequenceGroundSpeed( "Pistol_Side", "3.6 0 0"); - %this.setSequenceGroundSpeed( "Pistol_Swim_Backward", "0 -1 0"); - %this.setSequenceGroundSpeed( "Pistol_Swim_Forward", "0 1 0"); - %this.setSequenceGroundSpeed( "Pistol_Swim_Left", "-1 0 0"); - %this.setSequenceGroundSpeed( "Pistol_Swim_Right", "1 0 0"); - %this.setSequenceGroundSpeed( "Pistol_Crouch_Backward", "0 -2 0"); - %this.setSequenceGroundSpeed( "Pistol_Crouch_Forward", "0 2 0"); - %this.setSequenceGroundSpeed( "Pistol_Crouch_Side", "1 0 0"); - // END: General pistol sequences - - // BEGIN: ProxMine Sequences - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Back.dae Back", "ProxMine_Back", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Crouch_Backward.dae Crouch_Backward", "ProxMine_Crouch_Backward", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Crouch_Forward.dae Crouch_Forward", "ProxMine_Crouch_Forward", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Crouch_Side.dae Crouch_Side", "ProxMine_Crouch_Side", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Crouch_Root.dae Crouch_Root", "ProxMine_Crouch_Root", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Death1.dae Death1", "ProxMine_Death1", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Death2.dae Death2", "ProxMine_Death2", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Fall.dae Fall", "ProxMine_Fall", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Head.dae Head", "ProxMine_Head", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Jump.dae Jump", "ProxMine_Jump", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Land.dae Land", "ProxMine_Land", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Look.dae Look", "ProxMine_Look", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Reload.dae Reload", "ProxMine_Reload", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Fire.dae Fire", "ProxMine_Fire", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Fire_Release.dae Fire_Release", "ProxMine_Fire_Release", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Root.dae Root", "ProxMine_Root", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Run.dae Run", "ProxMine_Run", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Side.dae Side", "ProxMine_Side", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Sitting.dae Sitting", "ProxMine_Sitting", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Swim_Backward.dae Swim_Backward", "ProxMine_Swim_Backward", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Swim_Forward.dae Swim_Forward", "ProxMine_Swim_Forward", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Swim_Root.dae Swim_Root", "ProxMine_Swim_Root", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Swim_Left.dae Swim_Left", "ProxMine_Swim_Left", 0, -1); - %this.addSequence( "art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Swim_Right.dae Swim_Right", "ProxMine_Swim_Right", 0, -1); - - %this.setSequenceCyclic( "ProxMine_Back", true); - %this.setSequenceCyclic( "ProxMine_Crouch_Backward", true); - %this.setSequenceCyclic( "ProxMine_Crouch_Forward", true); - %this.setSequenceCyclic( "ProxMine_Crouch_Side", true); - %this.setSequenceCyclic( "ProxMine_Crouch_Root", true); - %this.setSequenceCyclic( "ProxMine_Death1", false); - %this.setSequenceCyclic( "ProxMine_Death2", false); - %this.setSequenceCyclic( "ProxMine_Fall", true); - %this.setSequenceCyclic( "ProxMine_Head", false); - %this.setSequenceCyclic( "ProxMine_Jump", false); - %this.setSequenceCyclic( "ProxMine_Land", false); - %this.setSequenceCyclic( "ProxMine_Look", false); - %this.setSequenceCyclic( "ProxMine_Reload", false); - %this.setSequenceCyclic( "ProxMine_Fire", false); - %this.setSequenceCyclic( "ProxMine_Fire_Release", false); - %this.setSequenceCyclic( "ProxMine_Root", true); - %this.setSequenceCyclic( "ProxMine_Run", true); - %this.setSequenceCyclic( "ProxMine_Side", true); - %this.setSequenceCyclic( "ProxMine_Sitting", true); - %this.setSequenceCyclic( "ProxMine_Swim_Backward", true); - %this.setSequenceCyclic( "ProxMine_Swim_Forward", true); - %this.setSequenceCyclic( "ProxMine_Swim_Root", true); - %this.setSequenceCyclic( "ProxMine_Swim_Left", true); - %this.setSequenceCyclic( "ProxMine_Swim_Right", true); - - %this.setSequenceBlend( "ProxMine_Head", "1", "ProxMine_Root", "0"); - %this.setSequenceBlend( "ProxMine_Look", "1", "ProxMine_Root", "0"); - %this.setSequenceBlend( "ProxMine_Reload", "1", "ProxMine_Root", "0"); - %this.setSequenceBlend( "ProxMine_Fire", "1", "ProxMine_Root", "0"); - %this.setSequenceBlend( "ProxMine_Fire_Release", "1", "ProxMine_Root", "0"); - - %this.setSequenceGroundSpeed( "ProxMine_Back", "0 -3.6 0"); - %this.setSequenceGroundSpeed( "ProxMine_Run", "0 5.0 0"); - %this.setSequenceGroundSpeed( "ProxMine_Side", "3.6 0 0"); - %this.setSequenceGroundSpeed( "ProxMine_Swim_Backward", "0 -1 0"); - %this.setSequenceGroundSpeed( "ProxMine_Swim_Forward", "0 1 0"); - %this.setSequenceGroundSpeed( "ProxMine_Swim_Left", "-1 0 0"); - %this.setSequenceGroundSpeed( "ProxMine_Swim_Right", "1 0 0"); - %this.setSequenceGroundSpeed( "ProxMine_Crouch_Backward", "0 -2 0"); - %this.setSequenceGroundSpeed( "ProxMine_Crouch_Forward", "0 2 0"); - %this.setSequenceGroundSpeed( "ProxMine_Crouch_Side", "1 0 0"); - // END: ProxMine Sequences - - // BEGIN: Turret Sequences - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Back.dae Back", "Turret_Back", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Crouch_Root.dae Crouch_Root", "Turret_Crouch_Root", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Crouch_Backward.dae Crouch_Backward", "Turret_Crouch_Backward", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Crouch_Forward.dae Crouch_Forward", "Turret_Crouch_Forward", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Crouch_Side.dae Crouch_Side", "Turret_Crouch_Side", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Death1.dae Death1", "Turret_Death1", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Death2.dae Death2", "Turret_Death2", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Fall.dae Fall", "Turret_Fall", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Run.dae Run", "Turret_Run", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Jump.dae Jump", "Turret_Jump", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Land.dae Land", "Turret_Land", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Look.dae Look", "Turret_Look", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Head.dae Head", "Turret_Head", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Recoil.dae Recoil", "Turret_Recoil", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Fire_Release.dae Fire_Release", "Turret_Fire_Release", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Root.dae Root", "Turret_Root", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Side.dae Side", "Turret_Side", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Sitting.dae Sitting", "Turret_Sitting", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Swim_Backward.dae Swim_Backward", "Turret_Swim_Backward", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Swim_Forward.dae Swim_Forward", "Turret_Swim_Forward", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Swim_Root.dae Swim_Root", "Turret_Swim_Root", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Swim_Left.dae Swim_Left", "Turret_Swim_Left", 0, -1); - %this.addSequence( "art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Swim_Right.dae Swim_Right", "Turret_Swim_Right", 0, -1); - - %this.setSequenceCyclic( "Turret_Back", true); - %this.setSequenceCyclic( "Turret_Crouch_Backward", true); - %this.setSequenceCyclic( "Turret_Crouch_Forward", true); - %this.setSequenceCyclic( "Turret_Crouch_Side", true); - %this.setSequenceCyclic( "Turret_Death1", false); - %this.setSequenceCyclic( "Turret_Death2", false); - %this.setSequenceCyclic( "Turret_Fall", true); - %this.setSequenceCyclic( "Turret_Run", true); - %this.setSequenceCyclic( "Turret_Jump", false); - %this.setSequenceCyclic( "Turret_Land", false); - %this.setSequenceCyclic( "Turret_Look", false); - %this.setSequenceCyclic( "Turret_Head", false); - %this.setSequenceCyclic( "Turret_Recoil", false); - %this.setSequenceCyclic( "Turret_Fire_Release", false); - %this.setSequenceCyclic( "Turret_Root", true); - %this.setSequenceCyclic( "Turret_Side", true); - %this.setSequenceCyclic( "Turret_Sitting", true); - %this.setSequenceCyclic( "Turret_Swim_Backward", true); - %this.setSequenceCyclic( "Turret_Swim_Forward", true); - %this.setSequenceCyclic( "Turret_Swim_Root", true); - %this.setSequenceCyclic( "Turret_Swim_Left", true); - %this.setSequenceCyclic( "Turret_Swim_Right", true); - - %this.setSequenceBlend( "Turret_Head", "1", "Turret_Root", "0"); - %this.setSequenceBlend( "Turret_Look", "1", "Turret_Root", "0"); - %this.setSequenceBlend( "Turret_Recoil", "1", "Turret_Root", "0"); - %this.setSequenceBlend( "Turret_Fire_Release", "1", "Turret_Root", "0"); - - %this.setSequenceGroundSpeed( "Turret_Back", "0 -3.6 0"); - %this.setSequenceGroundSpeed( "Turret_Run", "0 5.0 0"); - %this.setSequenceGroundSpeed( "Turret_Side", "3.6 0 0"); - %this.setSequenceGroundSpeed( "Turret_Swim_Backward", "0 -1 0"); - %this.setSequenceGroundSpeed( "Turret_Swim_Forward", "0 1 0"); - %this.setSequenceGroundSpeed( "Turret_Swim_Left", "-1 0 0"); - %this.setSequenceGroundSpeed( "Turret_Swim_Right", "1 0 0"); - %this.setSequenceGroundSpeed( "Turret_Crouch_Backward", "0 -2 0"); - %this.setSequenceGroundSpeed( "Turret_Crouch_Forward", "0 2 0"); - %this.setSequenceGroundSpeed( "Turret_Crouch_Side", "1 0 0"); - // END: Turret Sequences + %this.addSequence("./Anims/PlayerAnim_Lurker_Back.dae Back", "Back", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Celebrate_01.dae Celebrate_01", "Celebrate_01", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Crouch_Backward.dae Crouch_Backward", "Crouch_Backward", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Crouch_Forward.dae Crouch_Forward", "Crouch_Forward", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Crouch_Side.dae Crouch_Side", "Crouch_Side", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Crouch_Root.dae Crouch_Root", "Crouch_Root", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Death1.dae Death1", "Death1", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Death2.dae Death2", "Death2", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Fall.dae Fall", "Fall", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Head.dae Head", "Head", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Jump.dae Jump", "Jump", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Land.dae Land", "Land", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Look.dae Look", "Look", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Reload.dae Reload", "Reload", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Root.dae Root", "Root", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Run.dae Run", "Run", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Side.dae Side", "Side", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Sitting.dae Sitting", "Sitting", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Swim_Backward.dae Swim_Backward", "Swim_Backward", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Swim_Forward.dae Swim_Forward", "Swim_Forward", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Swim_Root.dae Swim_Root", "Swim_Root", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Swim_Left.dae Swim_Left", "Swim_Left", "0", "-1", "1", "0"); + %this.addSequence("./Anims/PlayerAnim_Lurker_Swim_Right.dae Swim_Right", "Swim_Right", "0", "-1", "1", "0"); + %this.setSequenceBlend("Head", "1", "Root", "0"); + %this.setSequenceBlend("Look", "1", "Root", "0"); + %this.setSequenceBlend("Reload", "1", "Root", "0"); + %this.setSequenceGroundSpeed("Back", "0 -3.6 0", "0 0 0"); + %this.setSequenceGroundSpeed("Run", "0 5 0", "0 0 0"); + %this.setSequenceGroundSpeed("Side", "-3.6 0 0", "0 0 0"); + %this.setSequenceGroundSpeed("Swim_Backward", "0 -1 0", "0 0 0"); + %this.setSequenceGroundSpeed("Swim_Forward", "0 1 0", "0 0 0"); + %this.setSequenceGroundSpeed("Swim_Left", "-1 0 0", "0 0 0"); + %this.setSequenceGroundSpeed("Swim_Right", "1 0 0", "0 0 0"); + %this.setSequenceGroundSpeed("Crouch_Backward", "0 -2 0", "0 0 0"); + %this.setSequenceGroundSpeed("Crouch_Forward", "0 2 0", "0 0 0"); + %this.setSequenceGroundSpeed("Crouch_Side", "1 0 0", "0 0 0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Back.dae Back", "Pistol_Back", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Crouch_Backward.dae Crouch_Backward", "Pistol_Crouch_Backward", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Crouch_Forward.dae Crouch_Forward", "Pistol_Crouch_Forward", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Crouch_Side.dae Crouch_Side", "Pistol_Crouch_Side", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Crouch_Root.dae Crouch_Root", "Pistol_Crouch_Root", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Death1.dae Death1", "Pistol_Death1", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Death2.dae Death2", "Pistol_Death2", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Fall.dae Fall", "Pistol_Fall", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Head.dae Head", "Pistol_Head", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Jump.dae Jump", "Pistol_Jump", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Land.dae Land", "Pistol_Land", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Look.dae Look", "Pistol_Look", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Reload.dae Reload", "Pistol_Reload", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Root.dae Root", "Pistol_Root", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Run.dae Run", "Pistol_Run", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Side.dae Side", "Pistol_Side", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Sitting.dae Sitting", "Pistol_Sitting", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Swim_Backward.dae Swim_Backward", "Pistol_Swim_Backward", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Swim_Forward.dae Swim_Forward", "Pistol_Swim_Forward", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Swim_Root.dae Swim_Root", "Pistol_Swim_Root", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Swim_Left.dae Swim_Left", "Pistol_Swim_Left", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Ryder/PlayerAnims/PlayerAnim_Pistol_Swim_Right.dae Swim_Right", "Pistol_Swim_Right", "0", "-1", "1", "0"); + %this.setSequenceCyclic("Pistol_Fall", "1"); + %this.setSequenceCyclic("Pistol_Sitting", "1"); + %this.setSequenceBlend("Pistol_Head", "1", "Pistol_Root", "0"); + %this.setSequenceBlend("Pistol_Look", "1", "Pistol_Root", "0"); + %this.setSequenceBlend("Pistol_Reload", "1", "Pistol_Root", "0"); + %this.setSequenceGroundSpeed("Pistol_Back", "0 -3.6 0", "0 0 0"); + %this.setSequenceGroundSpeed("Pistol_Run", "0 5 0", "0 0 0"); + %this.setSequenceGroundSpeed("Pistol_Side", "3.6 0 0", "0 0 0"); + %this.setSequenceGroundSpeed("Pistol_Swim_Backward", "0 -1 0", "0 0 0"); + %this.setSequenceGroundSpeed("Pistol_Swim_Forward", "0 1 0", "0 0 0"); + %this.setSequenceGroundSpeed("Pistol_Swim_Left", "-1 0 0", "0 0 0"); + %this.setSequenceGroundSpeed("Pistol_Swim_Right", "1 0 0", "0 0 0"); + %this.setSequenceGroundSpeed("Pistol_Crouch_Backward", "0 -2 0", "0 0 0"); + %this.setSequenceGroundSpeed("Pistol_Crouch_Forward", "0 2 0", "0 0 0"); + %this.setSequenceGroundSpeed("Pistol_Crouch_Side", "1 0 0", "0 0 0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Back.dae Back", "ProxMine_Back", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Crouch_Backward.dae Crouch_Backward", "ProxMine_Crouch_Backward", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Crouch_Forward.dae Crouch_Forward", "ProxMine_Crouch_Forward", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Crouch_Side.dae Crouch_Side", "ProxMine_Crouch_Side", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Crouch_Root.dae Crouch_Root", "ProxMine_Crouch_Root", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Death1.dae Death1", "ProxMine_Death1", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Death2.dae Death2", "ProxMine_Death2", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Fall.dae Fall", "ProxMine_Fall", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Head.dae Head", "ProxMine_Head", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Jump.dae Jump", "ProxMine_Jump", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Land.dae Land", "ProxMine_Land", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Look.dae Look", "ProxMine_Look", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Reload.dae Reload", "ProxMine_Reload", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Fire.dae Fire", "ProxMine_Fire", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Fire_Release.dae Fire_Release", "ProxMine_Fire_Release", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Root.dae Root", "ProxMine_Root", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Run.dae Run", "ProxMine_Run", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Side.dae Side", "ProxMine_Side", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Sitting.dae Sitting", "ProxMine_Sitting", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Swim_Backward.dae Swim_Backward", "ProxMine_Swim_Backward", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Swim_Forward.dae Swim_Forward", "ProxMine_Swim_Forward", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Swim_Root.dae Swim_Root", "ProxMine_Swim_Root", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Swim_Left.dae Swim_Left", "ProxMine_Swim_Left", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/ProxMine/PlayerAnims/PlayerAnim_ProxMine_Swim_Right.dae Swim_Right", "ProxMine_Swim_Right", "0", "-1", "1", "0"); + %this.setSequenceCyclic("ProxMine_Fall", "1"); + %this.setSequenceBlend("ProxMine_Head", "1", "ProxMine_Root", "0"); + %this.setSequenceBlend("ProxMine_Look", "1", "ProxMine_Root", "0"); + %this.setSequenceBlend("ProxMine_Reload", "1", "ProxMine_Root", "0"); + %this.setSequenceBlend("ProxMine_Fire", "1", "ProxMine_Root", "0"); + %this.setSequenceBlend("ProxMine_Fire_Release", "1", "ProxMine_Root", "0"); + %this.setSequenceGroundSpeed("ProxMine_Back", "0 -3.6 0", "0 0 0"); + %this.setSequenceGroundSpeed("ProxMine_Run", "0 5 0", "0 0 0"); + %this.setSequenceGroundSpeed("ProxMine_Side", "3.6 0 0", "0 0 0"); + %this.setSequenceGroundSpeed("ProxMine_Swim_Backward", "0 -1 0", "0 0 0"); + %this.setSequenceGroundSpeed("ProxMine_Swim_Forward", "0 1 0", "0 0 0"); + %this.setSequenceGroundSpeed("ProxMine_Swim_Left", "-1 0 0", "0 0 0"); + %this.setSequenceGroundSpeed("ProxMine_Swim_Right", "1 0 0", "0 0 0"); + %this.setSequenceGroundSpeed("ProxMine_Crouch_Backward", "0 -2 0", "0 0 0"); + %this.setSequenceGroundSpeed("ProxMine_Crouch_Forward", "0 2 0", "0 0 0"); + %this.setSequenceGroundSpeed("ProxMine_Crouch_Side", "1 0 0", "0 0 0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Back.dae Back", "Turret_Back", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Crouch_Root.dae Crouch_Root", "Turret_Crouch_Root", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Crouch_Backward.dae Crouch_Backward", "Turret_Crouch_Backward", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Crouch_Forward.dae Crouch_Forward", "Turret_Crouch_Forward", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Crouch_Side.dae Crouch_Side", "Turret_Crouch_Side", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Death1.dae Death1", "Turret_Death1", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Death2.dae Death2", "Turret_Death2", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Fall.dae Fall", "Turret_Fall", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Run.dae Run", "Turret_Run", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Jump.dae Jump", "Turret_Jump", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Land.dae Land", "Turret_Land", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Look.dae Look", "Turret_Look", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Head.dae Head", "Turret_Head", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Recoil.dae Recoil", "Turret_Recoil", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Fire_Release.dae Fire_Release", "Turret_Fire_Release", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Root.dae Root", "Turret_Root", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Side.dae Side", "Turret_Side", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Sitting.dae Sitting", "Turret_Sitting", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Swim_Backward.dae Swim_Backward", "Turret_Swim_Backward", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Swim_Forward.dae Swim_Forward", "Turret_Swim_Forward", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Swim_Root.dae Swim_Root", "Turret_Swim_Root", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Swim_Left.dae Swim_Left", "Turret_Swim_Left", "0", "-1", "1", "0"); + %this.addSequence("art/shapes/weapons/Turret/PlayerAnims/PlayerAnim_Turret_Swim_Right.dae Swim_Right", "Turret_Swim_Right", "0", "-1", "1", "0"); + %this.setSequenceBlend("Turret_Head", "1", "Turret_Root", "0"); + %this.setSequenceBlend("Turret_Look", "1", "Turret_Root", "0"); + %this.setSequenceBlend("Turret_Recoil", "1", "Turret_Root", "0"); + %this.setSequenceBlend("Turret_Fire_Release", "1", "Turret_Root", "0"); + %this.setSequenceGroundSpeed("Turret_Back", "0 -3.6 0", "0 0 0"); + %this.setSequenceGroundSpeed("Turret_Run", "0 5 0", "0 0 0"); + %this.setSequenceGroundSpeed("Turret_Side", "3.6 0 0", "0 0 0"); + %this.setSequenceGroundSpeed("Turret_Swim_Backward", "0 -1 0", "0 0 0"); + %this.setSequenceGroundSpeed("Turret_Swim_Forward", "0 1 0", "0 0 0"); + %this.setSequenceGroundSpeed("Turret_Swim_Left", "-1 0 0", "0 0 0"); + %this.setSequenceGroundSpeed("Turret_Swim_Right", "1 0 0", "0 0 0"); + %this.setSequenceGroundSpeed("Turret_Crouch_Backward", "0 -2 0", "0 0 0"); + %this.setSequenceGroundSpeed("Turret_Crouch_Forward", "0 2 0", "0 0 0"); + %this.setSequenceGroundSpeed("Turret_Crouch_Side", "1 0 0", "0 0 0"); + %this.addTrigger("Back", "4", "1"); + %this.addTrigger("Back", "13", "2"); + %this.addTrigger("jump", "6", "1"); + %this.addTrigger("Land", "5", "1"); + %this.addTrigger("Run", "8", "1"); + %this.addTrigger("Run", "16", "2"); + %this.addTrigger("Pistol_Back", "6", "1"); + %this.addTrigger("Pistol_Back", "12", "2"); + %this.addTrigger("Crouch_Backward", "8", "1"); + %this.addTrigger("Crouch_Backward", "19", "2"); + %this.addTrigger("Crouch_Forward", "12", "1"); + %this.addTrigger("Crouch_Forward", "24", "2"); + %this.addTrigger("Crouch_Side", "16", "1"); + %this.addTrigger("Crouch_Side", "23", "2"); + %this.addTrigger("Side", "7", "1"); + %this.addTrigger("Side", "17", "2"); + %this.addTrigger("Pistol_Crouch_Backward", "10", "1"); + %this.addTrigger("Pistol_Crouch_Backward", "24", "2"); + %this.addTrigger("Pistol_Crouch_Forward", "9", "1"); + %this.addTrigger("Pistol_Crouch_Forward", "25", "2"); + %this.addTrigger("Pistol_Crouch_Side", "8", "1"); + %this.addTrigger("Pistol_Crouch_Side", "23", "2"); + %this.addTrigger("Pistol_Jump", "5", "1"); + %this.addTrigger("Pistol_Land", "9", "1"); + %this.addTrigger("Pistol_Run", "8", "1"); + %this.addTrigger("Pistol_Run", "16", "2"); + %this.addTrigger("Pistol_Side", "8", "1"); + %this.addTrigger("Pistol_Side", "17", "2"); + %this.addTrigger("ProxMine_Back", "3", "1"); + %this.addTrigger("ProxMine_Back", "12", "2"); + %this.addTrigger("ProxMine_Crouch_Backward", "9", "1"); + %this.addTrigger("ProxMine_Crouch_Backward", "18", "2"); + %this.addTrigger("ProxMine_Crouch_Forward", "12", "1"); + %this.addTrigger("ProxMine_Crouch_Forward", "25", "2"); + %this.addTrigger("ProxMine_Crouch_Side", "12", "1"); + %this.addTrigger("ProxMine_Crouch_Side", "27", "2"); + %this.addTrigger("ProxMine_Fall", "15", "1"); + %this.addTrigger("ProxMine_Jump", "5", "1"); + %this.addTrigger("ProxMine_Land", "7", "1"); + %this.addTrigger("ProxMine_Run", "8", "1"); + %this.addTrigger("ProxMine_Run", "17", "2"); + %this.addTrigger("ProxMine_Side", "7", "1"); + %this.addTrigger("ProxMine_Side", "18", "2"); + %this.addTrigger("Turret_Back", "4", "1"); + %this.addTrigger("Turret_Back", "11", "2"); + %this.addTrigger("Turret_Crouch_Backward", "8", "1"); + %this.addTrigger("Turret_Crouch_Backward", "26", "2"); + %this.addTrigger("Turret_Crouch_Forward", "12", "1"); + %this.addTrigger("Turret_Crouch_Forward", "24", "2"); + %this.addTrigger("Turret_Crouch_Side", "13", "1"); + %this.addTrigger("Turret_Crouch_Side", "24", "2"); + %this.addTrigger("Turret_Run", "7", "1"); + %this.addTrigger("Turret_Run", "17", "2"); + %this.addTrigger("Turret_Jump", "6", "1"); + %this.addTrigger("Turret_Land", "3", "1"); + %this.addTrigger("Turret_Side", "9", "1"); + %this.addTrigger("Turret_Side", "17", "2"); } From 1e4458660602ddb95308ba7cbcab43952a611caa Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Sun, 7 Sep 2014 17:38:35 +0100 Subject: [PATCH 173/317] Refactor TS compiler to not use the precompile step & also 64bit support - Reduces headaches - Expands STE entry size to allow for 64bit values - Bonus: Adds in function lookup optimization from T2D --- Engine/source/console/ast.h | 150 ++- Engine/source/console/astNodes.cpp | 1179 ++++++++---------------- Engine/source/console/codeBlock.cpp | 172 ++-- Engine/source/console/codeBlock.h | 1 - Engine/source/console/compiledEval.cpp | 97 +- Engine/source/console/compiler.cpp | 154 +++- Engine/source/console/compiler.h | 210 ++++- Engine/source/console/console.h | 3 +- 8 files changed, 957 insertions(+), 1009 deletions(-) diff --git a/Engine/source/console/ast.h b/Engine/source/console/ast.h index 4be4fef90..45126e2b4 100644 --- a/Engine/source/console/ast.h +++ b/Engine/source/console/ast.h @@ -27,6 +27,7 @@ class ExprEvalState; class Namespace; class SimObject; class SimGroup; +class CodeStream; /// Enable this #define if you are seeing the message "precompile size mismatch" in the console. /// This will help track down which node type is causing the error. It could be @@ -77,15 +78,13 @@ struct StmtNode /// @name Breaking /// @{ - void addBreakCount(); - void addBreakLine(U32 ip); + void addBreakLine(CodeStream &codeStream); /// @} /// @name Compilation /// @{ - virtual U32 precompileStmt(U32 loopCount) = 0; - virtual U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint) = 0; + virtual U32 compileStmt(CodeStream &codeStream, U32 ip) = 0; virtual void setPackage(StringTableEntry packageName); /// @} }; @@ -101,27 +100,26 @@ struct BreakStmtNode : StmtNode { static BreakStmtNode *alloc( S32 lineNumber ); - U32 precompileStmt(U32 loopCount); - U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + + U32 compileStmt(CodeStream &codeStream, U32 ip); DBG_STMT_TYPE(BreakStmtNode); }; struct ContinueStmtNode : StmtNode { static ContinueStmtNode *alloc( S32 lineNumber ); - U32 precompileStmt(U32 loopCount); - U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + + U32 compileStmt(CodeStream &codeStream, U32 ip); DBG_STMT_TYPE(ContinueStmtNode); }; /// A mathematical expression. struct ExprNode : StmtNode { - U32 precompileStmt(U32 loopCount); - U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + + U32 compileStmt(CodeStream &codeStream, U32 ip); - virtual U32 precompile(TypeReq type) = 0; - virtual U32 compile(U32 *codeStream, U32 ip, TypeReq type) = 0; + virtual U32 compile(CodeStream &codeStream, U32 ip, TypeReq type) = 0; virtual TypeReq getPreferredType() = 0; }; @@ -130,8 +128,8 @@ struct ReturnStmtNode : StmtNode ExprNode *expr; static ReturnStmtNode *alloc( S32 lineNumber, ExprNode *expr ); - U32 precompileStmt(U32 loopCount); - U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + + U32 compileStmt(CodeStream &codeStream, U32 ip); DBG_STMT_TYPE(ReturnStmtNode); }; @@ -147,8 +145,8 @@ struct IfStmtNode : StmtNode 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); + + U32 compileStmt(CodeStream &codeStream, U32 ip); DBG_STMT_TYPE(IfStmtNode); }; @@ -165,8 +163,8 @@ struct LoopStmtNode : StmtNode 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); + + U32 compileStmt(CodeStream &codeStream, U32 ip); DBG_STMT_TYPE(LoopStmtNode); }; @@ -190,8 +188,7 @@ struct IterStmtNode : StmtNode static IterStmtNode* alloc( S32 lineNumber, StringTableEntry varName, ExprNode* containerExpr, StmtNode* body, bool isStringIter ); - U32 precompileStmt( U32 loopCount ); - U32 compileStmt( U32* codeStream, U32 ip, U32 continuePoint, U32 breakPoint ); + U32 compileStmt( CodeStream &codeStream, U32 ip ); }; /// A binary mathematical expression (ie, left op right). @@ -205,8 +202,8 @@ struct BinaryExprNode : ExprNode struct FloatBinaryExprNode : BinaryExprNode { static FloatBinaryExprNode *alloc( S32 lineNumber, S32 op, ExprNode *left, ExprNode *right ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(FloatBinaryExprNode); }; @@ -218,8 +215,8 @@ struct ConditionalExprNode : ExprNode ExprNode *falseExpr; bool integer; static ConditionalExprNode *alloc( S32 lineNumber, ExprNode *testExpr, ExprNode *trueExpr, ExprNode *falseExpr ); - virtual U32 precompile(TypeReq type); - virtual U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + virtual U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); virtual TypeReq getPreferredType(); DBG_STMT_TYPE(ConditionalExprNode); }; @@ -232,8 +229,8 @@ struct IntBinaryExprNode : BinaryExprNode static IntBinaryExprNode *alloc( S32 lineNumber, S32 op, ExprNode *left, ExprNode *right ); void getSubTypeOperand(); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(IntBinaryExprNode); }; @@ -242,8 +239,8 @@ struct StreqExprNode : BinaryExprNode { bool eq; static StreqExprNode *alloc( S32 lineNumber, ExprNode *left, ExprNode *right, bool eq ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(StreqExprNode); }; @@ -252,8 +249,8 @@ struct StrcatExprNode : BinaryExprNode { S32 appendChar; static StrcatExprNode *alloc( S32 lineNumber, ExprNode *left, ExprNode *right, S32 appendChar ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(StrcatExprNode); }; @@ -262,8 +259,8 @@ struct CommaCatExprNode : BinaryExprNode { static CommaCatExprNode *alloc( S32 lineNumber, ExprNode *left, ExprNode *right ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(CommaCatExprNode); }; @@ -275,8 +272,8 @@ struct IntUnaryExprNode : ExprNode bool integer; static IntUnaryExprNode *alloc( S32 lineNumber, S32 op, ExprNode *expr ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(IntUnaryExprNode); }; @@ -287,8 +284,8 @@ struct FloatUnaryExprNode : ExprNode ExprNode *expr; static FloatUnaryExprNode *alloc( S32 lineNumber, S32 op, ExprNode *expr ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(FloatUnaryExprNode); }; @@ -299,8 +296,8 @@ struct VarNode : ExprNode ExprNode *arrayIndex; static VarNode *alloc( S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(VarNode); }; @@ -311,8 +308,8 @@ struct IntNode : ExprNode U32 index; // if it's converted to float/string static IntNode *alloc( S32 lineNumber, S32 value ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(IntNode); }; @@ -323,8 +320,8 @@ struct FloatNode : ExprNode U32 index; static FloatNode *alloc( S32 lineNumber, F64 value ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(FloatNode); }; @@ -338,8 +335,8 @@ struct StrConstNode : ExprNode bool doc; // Specifies that this string is a documentation block. static StrConstNode *alloc( S32 lineNumber, char *str, bool tag, bool doc = false ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(StrConstNode); }; @@ -351,8 +348,8 @@ struct ConstantNode : ExprNode U32 index; static ConstantNode *alloc( S32 lineNumber, StringTableEntry value ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(ConstantNode); }; @@ -365,8 +362,8 @@ struct AssignExprNode : ExprNode TypeReq subType; static AssignExprNode *alloc( S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(AssignExprNode); }; @@ -389,8 +386,8 @@ struct AssignOpExprNode : ExprNode TypeReq subType; static AssignOpExprNode *alloc( S32 lineNumber, StringTableEntry varName, ExprNode *arrayIndex, ExprNode *expr, S32 op ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(AssignOpExprNode); }; @@ -402,8 +399,8 @@ struct TTagSetStmtNode : StmtNode ExprNode *stringExpr; static TTagSetStmtNode *alloc( S32 lineNumber, StringTableEntry tag, ExprNode *valueExpr, ExprNode *stringExpr ); - U32 precompileStmt(U32 loopCount); - U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + + U32 compileStmt(CodeStream &codeStream, U32 ip); DBG_STMT_TYPE(TTagSetStmtNode); }; @@ -412,8 +409,8 @@ struct TTagDerefNode : ExprNode ExprNode *expr; static TTagDerefNode *alloc( S32 lineNumber, ExprNode *expr ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(TTagDerefNode); }; @@ -423,8 +420,8 @@ struct TTagExprNode : ExprNode StringTableEntry tag; static TTagExprNode *alloc( S32 lineNumber, StringTableEntry tag ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(TTagExprNode); }; @@ -442,8 +439,8 @@ struct FuncCallExprNode : ExprNode }; static FuncCallExprNode *alloc( S32 lineNumber, StringTableEntry funcName, StringTableEntry nameSpace, ExprNode *args, bool dot ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(FuncCallExprNode); }; @@ -455,8 +452,8 @@ struct AssertCallExprNode : ExprNode U32 messageIndex; static AssertCallExprNode *alloc( S32 lineNumber, ExprNode *testExpr, const char *message ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(AssertCallExprNode); }; @@ -475,8 +472,8 @@ struct SlotAccessNode : ExprNode StringTableEntry slotName; static SlotAccessNode *alloc( S32 lineNumber, ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(SlotAccessNode); }; @@ -495,8 +492,8 @@ struct InternalSlotAccessNode : ExprNode bool recurse; static InternalSlotAccessNode *alloc( S32 lineNumber, ExprNode *objectExpr, ExprNode *slotExpr, bool recurse ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(InternalSlotAccessNode); }; @@ -509,8 +506,8 @@ struct SlotAssignNode : ExprNode U32 typeID; static SlotAssignNode *alloc( S32 lineNumber, ExprNode *objectExpr, ExprNode *arrayExpr, StringTableEntry slotName, ExprNode *valueExpr, U32 typeID = -1 ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(SlotAssignNode); }; @@ -525,8 +522,8 @@ struct SlotAssignOpNode : ExprNode TypeReq subType; static SlotAssignOpNode *alloc( S32 lineNumber, ExprNode *objectExpr, StringTableEntry slotName, ExprNode *arrayExpr, S32 op, ExprNode *valueExpr ); - U32 precompile(TypeReq type); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); + + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); TypeReq getPreferredType(); DBG_STMT_TYPE(SlotAssignOpNode); }; @@ -545,10 +542,10 @@ struct ObjectDeclNode : ExprNode bool isSingleton; static ObjectDeclNode *alloc( S32 lineNumber, ExprNode *classNameExpr, ExprNode *objectNameExpr, ExprNode *argList, StringTableEntry parentObject, SlotAssignNode *slotDecls, ObjectDeclNode *subObjects, bool isDatablock, bool classNameInternal, bool isSingleton ); - U32 precompile(TypeReq type); + U32 precompileSubObject(bool); - U32 compile(U32 *codeStream, U32 ip, TypeReq type); - U32 compileSubObject(U32 *codeStream, U32 ip, bool); + U32 compile(CodeStream &codeStream, U32 ip, TypeReq type); + U32 compileSubObject(CodeStream &codeStream, U32 ip, bool); TypeReq getPreferredType(); DBG_STMT_TYPE(ObjectDeclNode); }; @@ -570,18 +567,13 @@ struct FunctionDeclStmtNode : StmtNode U32 argc; static FunctionDeclStmtNode *alloc( S32 lineNumber, StringTableEntry fnName, StringTableEntry nameSpace, VarNode *args, StmtNode *stmts ); - U32 precompileStmt(U32 loopCount); - U32 compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint); + + U32 compileStmt(CodeStream &codeStream, U32 ip); void setPackage(StringTableEntry packageName); DBG_STMT_TYPE(FunctionDeclStmtNode); }; extern StmtNode *gStatementList; -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); - +extern ExprEvalState gEvalState;; #endif diff --git a/Engine/source/console/astNodes.cpp b/Engine/source/console/astNodes.cpp index 11cc44f90..666589f6a 100644 --- a/Engine/source/console/astNodes.cpp +++ b/Engine/source/console/astNodes.cpp @@ -45,26 +45,11 @@ struct Token namespace Compiler { - U32 precompileBlock(StmtNode *block, U32 loopCount) - { - U32 sum = 0; - for(StmtNode *walk = block; walk; walk = walk->getNext()) - { - const U32 temp = walk->precompileStmt(loopCount); -#ifdef DEBUG_AST_NODES - if(temp > 1000) - Con::printf("suspect %s '%s' line %d", walk->dbgStmtType().c_str(), walk->dbgFileName, walk->dbgLineNumber); -#endif - sum += temp; - } - return sum; - } - - U32 compileBlock(StmtNode *block, U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint) + U32 compileBlock(StmtNode *block, CodeStream &codeStream, U32 ip) { for(StmtNode *walk = block; walk; walk = walk->getNext()) - ip = walk->compileStmt(codeStream, ip, continuePoint, breakPoint); - return ip; + ip = walk->compileStmt(codeStream, ip); + return codeStream.tell(); } } @@ -72,21 +57,9 @@ using namespace Compiler; //----------------------------------------------------------------------------- -void StmtNode::addBreakCount() +void StmtNode::addBreakLine(CodeStream &code) { - CodeBlock::smBreakLineCount++; -} - -void StmtNode::addBreakLine(U32 ip) -{ - U32 line = CodeBlock::smBreakLineCount * 2; - CodeBlock::smBreakLineCount++; - - if(getBreakCodeBlock()->lineBreakPairs) - { - getBreakCodeBlock()->lineBreakPairs[line] = dbgLineNumber; - getBreakCodeBlock()->lineBreakPairs[line+1] = ip; - } + code.addBreakLine(dbgLineNumber, code.tell()); } //------------------------------------------------------------ @@ -170,85 +143,59 @@ static U32 conversionOp(TypeReq src, TypeReq dst) //------------------------------------------------------------ -U32 BreakStmtNode::precompileStmt(U32 loopCount) +U32 BreakStmtNode::compileStmt(CodeStream &codeStream, U32 ip) { - if(loopCount) + if(codeStream.inLoop()) { - addBreakCount(); - return 2; + addBreakLine(codeStream); + codeStream.emit(OP_JMP); + codeStream.emitFix(CodeStream::FIXTYPE_BREAK); } - Con::warnf(ConsoleLogEntry::General, "%s (%d): break outside of loop... ignoring.", dbgFileName, dbgLineNumber); - return 0; -} - -U32 BreakStmtNode::compileStmt(U32 *codeStream, U32 ip, U32, U32 breakPoint) -{ - if(breakPoint) + else { - addBreakLine(ip); - codeStream[ip++] = OP_JMP; - codeStream[ip++] = breakPoint; + Con::warnf(ConsoleLogEntry::General, "%s (%d): break outside of loop... ignoring.", dbgFileName, dbgLineNumber); } - return ip; + return codeStream.tell(); } //------------------------------------------------------------ -U32 ContinueStmtNode::precompileStmt(U32 loopCount) +U32 ContinueStmtNode::compileStmt(CodeStream &codeStream, U32 ip) { - if(loopCount) + if(codeStream.inLoop()) { - addBreakCount(); - return 2; + addBreakLine(codeStream); + codeStream.emit(OP_JMP); + codeStream.emitFix(CodeStream::FIXTYPE_CONTINUE); } - Con::warnf(ConsoleLogEntry::General, "%s (%d): continue outside of loop... ignoring.", dbgFileName, dbgLineNumber); - return 0; -} - -U32 ContinueStmtNode::compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32) -{ - if(continuePoint) + else { - addBreakLine(ip); - codeStream[ip++] = OP_JMP; - codeStream[ip++] = continuePoint; + Con::warnf(ConsoleLogEntry::General, "%s (%d): continue outside of loop... ignoring.", dbgFileName, dbgLineNumber); } - return ip; + return codeStream.tell(); } //------------------------------------------------------------ -U32 ExprNode::precompileStmt(U32) +U32 ExprNode::compileStmt(CodeStream &codeStream, U32 ip) { - addBreakCount(); - return precompile(TypeReqNone); -} - -U32 ExprNode::compileStmt(U32 *codeStream, U32 ip, U32, U32) -{ - addBreakLine(ip); + addBreakLine(codeStream); return compile(codeStream, ip, TypeReqNone); } //------------------------------------------------------------ -U32 ReturnStmtNode::precompileStmt(U32) +U32 ReturnStmtNode::compileStmt(CodeStream &codeStream, U32 ip) { - addBreakCount(); - return 1 + (expr ? expr->precompile(TypeReqString) : 0); -} - -U32 ReturnStmtNode::compileStmt(U32 *codeStream, U32 ip, U32, U32) -{ - addBreakLine(ip); + addBreakLine(codeStream); if(!expr) - codeStream[ip++] = OP_RETURN_VOID; + codeStream.emit(OP_RETURN_VOID); else { ip = expr->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_RETURN; + codeStream.emit(OP_RETURN); } - return ip; + return codeStream.tell(); } //------------------------------------------------------------ @@ -273,87 +220,60 @@ void IfStmtNode::propagateSwitchExpr(ExprNode *left, bool string) ((IfStmtNode *) elseBlock)->propagateSwitchExpr(left, string); } -U32 IfStmtNode::precompileStmt(U32 loopCount) +U32 IfStmtNode::compileStmt(CodeStream &codeStream, U32 ip) { - U32 exprSize; - addBreakCount(); - + U32 start = ip; + U32 endifIp, elseIp; + addBreakLine(codeStream); + if(testExpr->getPreferredType() == TypeReqUInt) { - exprSize = testExpr->precompile(TypeReqUInt); integer = true; } else { - exprSize = testExpr->precompile(TypeReqFloat); integer = false; } - // next is the JMPIFNOT or JMPIFFNOT - size of 2 - U32 ifSize = precompileBlock(ifBlock, loopCount); - if(!elseBlock) - endifOffset = ifSize + 2 + exprSize; - else - { - elseOffset = exprSize + 2 + ifSize + 2; - U32 elseSize = precompileBlock(elseBlock, loopCount); - endifOffset = elseOffset + elseSize; - } - return endifOffset; -} - -U32 IfStmtNode::compileStmt(U32 *codeStream, U32 ip, U32 continuePoint, U32 breakPoint) -{ - U32 start = ip; - addBreakLine(ip); ip = testExpr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat); - codeStream[ip++] = integer ? OP_JMPIFNOT : OP_JMPIFFNOT; + codeStream.emit(integer ? OP_JMPIFNOT : OP_JMPIFFNOT); if(elseBlock) { - codeStream[ip++] = start + elseOffset; - ip = compileBlock(ifBlock, codeStream, ip, continuePoint, breakPoint); - codeStream[ip++] = OP_JMP; - codeStream[ip++] = start + endifOffset; - ip = compileBlock(elseBlock, codeStream, ip, continuePoint, breakPoint); + elseIp = codeStream.emit(0); + elseOffset = compileBlock(ifBlock, codeStream, ip) + 2; + codeStream.emit(OP_JMP); + endifIp = codeStream.emit(0); + endifOffset = compileBlock(elseBlock, codeStream, ip); + + codeStream.patch(endifIp, endifOffset); + codeStream.patch(elseIp, elseOffset); } else { - codeStream[ip++] = start + endifOffset; - ip = compileBlock(ifBlock, codeStream, ip, continuePoint, breakPoint); + endifIp = codeStream.emit(0); + endifOffset = compileBlock(ifBlock, codeStream, ip); + + codeStream.patch(endifIp, endifOffset); } - return ip; + + // Resolve fixes + return codeStream.tell(); } //------------------------------------------------------------ -U32 LoopStmtNode::precompileStmt(U32 loopCount) +U32 LoopStmtNode::compileStmt(CodeStream &codeStream, U32 ip) { - U32 initSize = 0; - addBreakCount(); - - if(initExpr) - initSize = initExpr->precompile(TypeReqNone); - - U32 testSize; - if(testExpr->getPreferredType() == TypeReqUInt) { integer = true; - testSize = testExpr->precompile(TypeReqUInt); } else { integer = false; - testSize = testExpr->precompile(TypeReqFloat); } - - U32 blockSize = precompileBlock(loopBlock, loopCount + 1); - - U32 endLoopSize = 0; - if(endLoopExpr) - endLoopSize = endLoopExpr->precompile(TypeReqNone); - + // if it's a for loop or a while loop it goes: // initExpr // testExpr @@ -365,7 +285,7 @@ U32 LoopStmtNode::precompileStmt(U32 loopCount) // testExpr // OP_JMPIF loopStartPoint // breakPoint: - + // otherwise if it's a do ... while() it goes: // initExpr // loopStartPoint: @@ -375,25 +295,15 @@ U32 LoopStmtNode::precompileStmt(U32 loopCount) // testExpr // OP_JMPIF loopStartPoint // breakPoint: - - if(!isDoLoop) - { - loopBlockStartOffset = initSize + testSize + 2; - continueOffset = loopBlockStartOffset + blockSize; - breakOffset = continueOffset + endLoopSize + testSize + 2; - } - else - { - loopBlockStartOffset = initSize; - continueOffset = initSize + blockSize; - breakOffset = continueOffset + endLoopSize + testSize + 2; - } - return breakOffset; -} - -U32 LoopStmtNode::compileStmt(U32 *codeStream, U32 ip, U32, U32) -{ - addBreakLine(ip); + + // loopBlockStart == start of loop block + // continue == skip to end + // break == exit loop + + + addBreakLine(codeStream); + codeStream.pushFixScope(true); + U32 start = ip; if(initExpr) ip = initExpr->compile(codeStream, ip, TypeReqNone); @@ -401,30 +311,34 @@ U32 LoopStmtNode::compileStmt(U32 *codeStream, U32 ip, U32, U32) if(!isDoLoop) { ip = testExpr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat); - codeStream[ip++] = integer ? OP_JMPIFNOT : OP_JMPIFFNOT; - codeStream[ip++] = start + breakOffset; + codeStream.emit(integer ? OP_JMPIFNOT : OP_JMPIFFNOT); + codeStream.emitFix(CodeStream::FIXTYPE_BREAK); } // Compile internals of loop. - ip = compileBlock(loopBlock, codeStream, ip, start + continueOffset, start + breakOffset); + loopBlockStartOffset = codeStream.tell(); + continueOffset = compileBlock(loopBlock, codeStream, ip); if(endLoopExpr) ip = endLoopExpr->compile(codeStream, ip, TypeReqNone); ip = testExpr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat); - codeStream[ip++] = integer ? OP_JMPIF : OP_JMPIFF; - codeStream[ip++] = start + loopBlockStartOffset; - - return ip; + codeStream.emit(integer ? OP_JMPIF : OP_JMPIFF); + codeStream.emitFix(CodeStream::FIXTYPE_LOOPBLOCKSTART); + + breakOffset = codeStream.tell(); // exit loop + + codeStream.fixLoop(loopBlockStartOffset, breakOffset, continueOffset); + codeStream.popFixScope(); + + return codeStream.tell(); } //------------------------------------------------------------ -U32 IterStmtNode::precompileStmt( U32 loopCount ) +U32 IterStmtNode::compileStmt( CodeStream &codeStream, U32 ip ) { - addBreakCount(); - // Instruction sequence: // // containerExpr @@ -436,86 +350,66 @@ U32 IterStmtNode::precompileStmt( U32 loopCount ) // .break: // OP_ITER_END // .fail: - - U32 exprSize = containerExpr->precompile( TypeReqString ); - bodySize = precompileBlock( body, loopCount + 1 ); - return - exprSize - + 3 // OP_ITER_BEGIN - + 2 // OP_ITER - + bodySize - + 2 // OP_JMP - + 1 // OP_ITER_END - ; -} - -U32 IterStmtNode::compileStmt( U32* codeStream, U32 ip, U32 continuePoint, U32 breakPoint ) -{ - addBreakLine( ip ); + addBreakLine(codeStream); + + codeStream.pushFixScope(true); const U32 startIp = ip; - const U32 iterBeginIp = containerExpr->compile( codeStream, startIp, TypeReqString ); - - const U32 continueIp = iterBeginIp + 3; - const U32 bodyIp = continueIp + 2; - const U32 jmpIp = bodyIp + bodySize; + containerExpr->compile( codeStream, startIp, TypeReqString ); + + codeStream.emit(isStringIter ? OP_ITER_BEGIN_STR : OP_ITER_BEGIN); + codeStream.emitSTE( varName ); + const U32 finalFix = codeStream.emit(0); + const U32 continueIp = codeStream.emit(OP_ITER); + codeStream.emitFix(CodeStream::FIXTYPE_BREAK); + const U32 bodyIp = codeStream.tell(); + + const U32 jmpIp = compileBlock( body, codeStream, bodyIp); const U32 breakIp = jmpIp + 2; const U32 finalIp = breakIp + 1; - codeStream[ iterBeginIp ] = isStringIter ? OP_ITER_BEGIN_STR : OP_ITER_BEGIN; - codeStream[ iterBeginIp + 1 ] = STEtoU32( varName, iterBeginIp + 1 ); - codeStream[ iterBeginIp + 2 ] = finalIp; - codeStream[ continueIp ] = OP_ITER; - codeStream[ continueIp + 1 ] = breakIp; - - compileBlock( body, codeStream, bodyIp, continueIp, breakIp ); + codeStream.emit(OP_JMP); + codeStream.emitFix(CodeStream::FIXTYPE_CONTINUE); + codeStream.emit(OP_ITER_END); - codeStream[ jmpIp ] = OP_JMP; - codeStream[ jmpIp + 1 ] = continueIp; - codeStream[ breakIp ] = OP_ITER_END; + codeStream.patch(finalFix, finalIp); + codeStream.fixLoop(bodyIp, breakIp, continueIp); + codeStream.popFixScope(); - return finalIp; + return codeStream.tell(); } //------------------------------------------------------------ -U32 ConditionalExprNode::precompile(TypeReq type) +U32 ConditionalExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { // code is testExpr // JMPIFNOT falseStart // trueExpr // JMP end // falseExpr - U32 exprSize; - if(testExpr->getPreferredType() == TypeReqUInt) { - exprSize = testExpr->precompile(TypeReqUInt); integer = true; } else { - exprSize = testExpr->precompile(TypeReqFloat); integer = false; } - return exprSize + - trueExpr->precompile(type) + - falseExpr->precompile(type) + 4; -} - -U32 ConditionalExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ + ip = testExpr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat); - codeStream[ip++] = integer ? OP_JMPIFNOT : OP_JMPIFFNOT; - U32 jumpElseIp = ip++; + codeStream.emit(integer ? OP_JMPIFNOT : OP_JMPIFFNOT); + + U32 jumpElseIp = codeStream.emit(0); ip = trueExpr->compile(codeStream, ip, type); - codeStream[ip++] = OP_JMP; - U32 jumpEndIp = ip++; - codeStream[jumpElseIp] = ip; + codeStream.emit(OP_JMP); + U32 jumpEndIp = codeStream.emit(0); + codeStream.patch(jumpElseIp, codeStream.tell()); ip = falseExpr->compile(codeStream, ip, type); - codeStream[jumpEndIp] = ip; - return ip; + codeStream.patch(jumpEndIp, codeStream.tell()); + + return codeStream.tell(); } TypeReq ConditionalExprNode::getPreferredType() @@ -525,16 +419,7 @@ TypeReq ConditionalExprNode::getPreferredType() //------------------------------------------------------------ -U32 FloatBinaryExprNode::precompile(TypeReq type) -{ - U32 addSize = left->precompile(TypeReqFloat) + right->precompile(TypeReqFloat) + 1; - if(type != TypeReqFloat) - addSize++; - - return addSize; -} - -U32 FloatBinaryExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +U32 FloatBinaryExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { ip = right->compile(codeStream, ip, TypeReqFloat); ip = left->compile(codeStream, ip, TypeReqFloat); @@ -554,10 +439,10 @@ U32 FloatBinaryExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) operand = OP_MUL; break; } - codeStream[ip++] = operand; + codeStream.emit(operand); if(type != TypeReqFloat) - codeStream[ip++] =conversionOp(TypeReqFloat, type); - return ip; + codeStream.emit(conversionOp(TypeReqFloat, type)); + return codeStream.tell(); } TypeReq FloatBinaryExprNode::getPreferredType() @@ -623,38 +508,27 @@ void IntBinaryExprNode::getSubTypeOperand() } } -U32 IntBinaryExprNode::precompile(TypeReq type) +U32 IntBinaryExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { getSubTypeOperand(); - U32 addSize = left->precompile(subType) + right->precompile(subType) + 1; - if(operand == OP_OR || operand == OP_AND) - addSize++; - - if(type != TypeReqUInt) - addSize++; - - return addSize; -} - -U32 IntBinaryExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ + if(operand == OP_OR || operand == OP_AND) { ip = left->compile(codeStream, ip, subType); - codeStream[ip++] = operand == OP_OR ? OP_JMPIF_NP : OP_JMPIFNOT_NP; - U32 jmpIp = ip++; + codeStream.emit(operand == OP_OR ? OP_JMPIF_NP : OP_JMPIFNOT_NP); + U32 jmpIp = codeStream.emit(0); ip = right->compile(codeStream, ip, subType); - codeStream[jmpIp] = ip; + codeStream.patch(jmpIp, ip); } else { ip = right->compile(codeStream, ip, subType); ip = left->compile(codeStream, ip, subType); - codeStream[ip++] = operand; + codeStream.emit(operand); } if(type != TypeReqUInt) - codeStream[ip++] =conversionOp(TypeReqUInt, type); - return ip; + codeStream.emit(conversionOp(TypeReqUInt, type)); + return codeStream.tell(); } TypeReq IntBinaryExprNode::getPreferredType() @@ -664,32 +538,23 @@ TypeReq IntBinaryExprNode::getPreferredType() //------------------------------------------------------------ -U32 StreqExprNode::precompile(TypeReq type) +U32 StreqExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { // eval str left // OP_ADVANCE_STR_NUL // eval str right // OP_COMPARE_STR // optional conversion - U32 addSize = left->precompile(TypeReqString) + right->precompile(TypeReqString) + 2; - if(!eq) - addSize ++; - if(type != TypeReqUInt) - addSize ++; - return addSize; -} - -U32 StreqExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ + ip = left->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_ADVANCE_STR_NUL; + codeStream.emit(OP_ADVANCE_STR_NUL); ip = right->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_COMPARE_STR; + codeStream.emit(OP_COMPARE_STR); if(!eq) - codeStream[ip++] = OP_NOT; + codeStream.emit(OP_NOT); if(type != TypeReqUInt) - codeStream[ip++] = conversionOp(TypeReqUInt, type); - return ip; + codeStream.emit(conversionOp(TypeReqUInt, type)); + return codeStream.tell(); } TypeReq StreqExprNode::getPreferredType() @@ -699,34 +564,23 @@ TypeReq StreqExprNode::getPreferredType() //------------------------------------------------------------ -U32 StrcatExprNode::precompile(TypeReq type) -{ - U32 addSize = left->precompile(TypeReqString) + right->precompile(TypeReqString) + 2; - if(appendChar) - addSize++; - - if(type != TypeReqString) - addSize ++; - return addSize; -} - -U32 StrcatExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +U32 StrcatExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { ip = left->compile(codeStream, ip, TypeReqString); if(!appendChar) - codeStream[ip++] = OP_ADVANCE_STR; + codeStream.emit(OP_ADVANCE_STR); else { - codeStream[ip++] = OP_ADVANCE_STR_APPENDCHAR; - codeStream[ip++] = appendChar; + codeStream.emit(OP_ADVANCE_STR_APPENDCHAR); + codeStream.emit(appendChar); } ip = right->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_REWIND_STR; + codeStream.emit(OP_REWIND_STR); if(type == TypeReqUInt) - codeStream[ip++] = OP_STR_TO_UINT; + codeStream.emit(OP_STR_TO_UINT); else if(type == TypeReqFloat) - codeStream[ip++] = OP_STR_TO_FLT; - return ip; + codeStream.emit(OP_STR_TO_FLT); + return codeStream.tell(); } TypeReq StrcatExprNode::getPreferredType() @@ -736,20 +590,12 @@ TypeReq StrcatExprNode::getPreferredType() //------------------------------------------------------------ -U32 CommaCatExprNode::precompile(TypeReq type) -{ - U32 addSize = left->precompile(TypeReqString) + right->precompile(TypeReqString) + 2; - if(type != TypeReqString) - addSize ++; - return addSize; -} - -U32 CommaCatExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +U32 CommaCatExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { ip = left->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_ADVANCE_STR_COMMA; + codeStream.emit(OP_ADVANCE_STR_COMMA); ip = right->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_REWIND_STR; + codeStream.emit(OP_REWIND_STR); // At this point the stack has the concatenated string. @@ -757,10 +603,10 @@ U32 CommaCatExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) if(type == TypeReqUInt || type == TypeReqFloat) Con::warnf(ConsoleLogEntry::General, "%s (%d): converting comma string to a number... probably wrong.", dbgFileName, dbgLineNumber); if(type == TypeReqUInt) - codeStream[ip++] = OP_STR_TO_UINT; + codeStream.emit(OP_STR_TO_UINT); else if(type == TypeReqFloat) - codeStream[ip++] = OP_STR_TO_FLT; - return ip; + codeStream.emit(OP_STR_TO_FLT); + return codeStream.tell(); } TypeReq CommaCatExprNode::getPreferredType() @@ -770,30 +616,21 @@ TypeReq CommaCatExprNode::getPreferredType() //------------------------------------------------------------ -U32 IntUnaryExprNode::precompile(TypeReq type) +U32 IntUnaryExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { integer = true; TypeReq prefType = expr->getPreferredType(); if(op == '!' && (prefType == TypeReqFloat || prefType == TypeReqString)) integer = false; - - U32 exprSize = expr->precompile(integer ? TypeReqUInt : TypeReqFloat); - if(type != TypeReqUInt) - return exprSize + 2; - else - return exprSize + 1; -} - -U32 IntUnaryExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ + ip = expr->compile(codeStream, ip, integer ? TypeReqUInt : TypeReqFloat); if(op == '!') - codeStream[ip++] = integer ? OP_NOT : OP_NOTF; + codeStream.emit(integer ? OP_NOT : OP_NOTF); else if(op == '~') - codeStream[ip++] = OP_ONESCOMPLEMENT; + codeStream.emit(OP_ONESCOMPLEMENT); if(type != TypeReqUInt) - codeStream[ip++] =conversionOp(TypeReqUInt, type); - return ip; + codeStream.emit(conversionOp(TypeReqUInt, type)); + return codeStream.tell(); } TypeReq IntUnaryExprNode::getPreferredType() @@ -803,22 +640,13 @@ TypeReq IntUnaryExprNode::getPreferredType() //------------------------------------------------------------ -U32 FloatUnaryExprNode::precompile(TypeReq type) -{ - U32 exprSize = expr->precompile(TypeReqFloat); - if(type != TypeReqFloat) - return exprSize + 2; - else - return exprSize + 1; -} - -U32 FloatUnaryExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) +U32 FloatUnaryExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { ip = expr->compile(codeStream, ip, TypeReqFloat); - codeStream[ip++] = OP_NEG; + codeStream.emit(OP_NEG); if(type != TypeReqFloat) - codeStream[ip++] =conversionOp(TypeReqFloat, type); - return ip; + codeStream.emit(conversionOp(TypeReqFloat, type)); + return codeStream.tell(); } TypeReq FloatUnaryExprNode::getPreferredType() @@ -828,7 +656,7 @@ TypeReq FloatUnaryExprNode::getPreferredType() //------------------------------------------------------------ -U32 VarNode::precompile(TypeReq type) +U32 VarNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { // if this has an arrayIndex... // OP_LOADIMMED_IDENT @@ -838,48 +666,42 @@ U32 VarNode::precompile(TypeReq type) // OP_REWIND_STR // OP_SETCURVAR_ARRAY // OP_LOADVAR (type) - + // else // OP_SETCURVAR // varName // OP_LOADVAR (type) + if(type == TypeReqNone) - return 0; - + return codeStream.tell(); + precompileIdent(varName); - return (arrayIndex ? arrayIndex->precompile(TypeReqString) + 6 : 3); -} -U32 VarNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ - if(type == TypeReqNone) - return ip; - - codeStream[ip++] = arrayIndex ? OP_LOADIMMED_IDENT : OP_SETCURVAR; - codeStream[ip] = STEtoU32(varName, ip); - ip++; + codeStream.emit(arrayIndex ? OP_LOADIMMED_IDENT : OP_SETCURVAR); + codeStream.emitSTE(varName); + if(arrayIndex) { - codeStream[ip++] = OP_ADVANCE_STR; + codeStream.emit(OP_ADVANCE_STR); ip = arrayIndex->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_REWIND_STR; - codeStream[ip++] = OP_SETCURVAR_ARRAY; + codeStream.emit(OP_REWIND_STR); + codeStream.emit(OP_SETCURVAR_ARRAY); } switch(type) { case TypeReqUInt: - codeStream[ip++] = OP_LOADVAR_UINT; + codeStream.emit(OP_LOADVAR_UINT); break; case TypeReqFloat: - codeStream[ip++] = OP_LOADVAR_FLT; + codeStream.emit(OP_LOADVAR_FLT); break; case TypeReqString: - codeStream[ip++] = OP_LOADVAR_STR; + codeStream.emit(OP_LOADVAR_STR); break; case TypeReqNone: break; } - return ip; + return codeStream.tell(); } TypeReq VarNode::getPreferredType() @@ -889,37 +711,31 @@ TypeReq VarNode::getPreferredType() //------------------------------------------------------------ -U32 IntNode::precompile(TypeReq type) +U32 IntNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { - if(type == TypeReqNone) - return 0; if(type == TypeReqString) index = getCurrentStringTable()->addIntString(value); else if(type == TypeReqFloat) index = getCurrentFloatTable()->add(value); - return 2; -} - -U32 IntNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ + switch(type) { case TypeReqUInt: - codeStream[ip++] = OP_LOADIMMED_UINT; - codeStream[ip++] = value; + codeStream.emit(OP_LOADIMMED_UINT); + codeStream.emit(value); break; case TypeReqString: - codeStream[ip++] = OP_LOADIMMED_STR; - codeStream[ip++] = index; + codeStream.emit(OP_LOADIMMED_STR); + codeStream.emit(index); break; case TypeReqFloat: - codeStream[ip++] = OP_LOADIMMED_FLT; - codeStream[ip++] = index; + codeStream.emit(OP_LOADIMMED_FLT); + codeStream.emit(index); break; case TypeReqNone: break; } - return ip; + return codeStream.tell(); } TypeReq IntNode::getPreferredType() @@ -929,37 +745,31 @@ TypeReq IntNode::getPreferredType() //------------------------------------------------------------ -U32 FloatNode::precompile(TypeReq type) +U32 FloatNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { - if(type == TypeReqNone) - return 0; if(type == TypeReqString) index = getCurrentStringTable()->addFloatString(value); else if(type == TypeReqFloat) index = getCurrentFloatTable()->add(value); - return 2; -} - -U32 FloatNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ + switch(type) { case TypeReqUInt: - codeStream[ip++] = OP_LOADIMMED_UINT; - codeStream[ip++] = U32(value); + codeStream.emit(OP_LOADIMMED_UINT); + codeStream.emit(U32(value)); break; case TypeReqString: - codeStream[ip++] = OP_LOADIMMED_STR; - codeStream[ip++] = index; + codeStream.emit(OP_LOADIMMED_STR); + codeStream.emit(index); break; case TypeReqFloat: - codeStream[ip++] = OP_LOADIMMED_FLT; - codeStream[ip++] = index; + codeStream.emit(OP_LOADIMMED_FLT); + codeStream.emit(index); break; case TypeReqNone: break; } - return ip; + return codeStream.tell(); } TypeReq FloatNode::getPreferredType() @@ -969,38 +779,31 @@ TypeReq FloatNode::getPreferredType() //------------------------------------------------------------ -U32 StrConstNode::precompile(TypeReq type) +U32 StrConstNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { // Early out for documentation block. if( doc ) { index = getCurrentStringTable()->add(str, true, tag); - return 2; } - - if(type == TypeReqString) + else if(type == TypeReqString) { index = getCurrentStringTable()->add(str, true, tag); - return 2; } - else if(type == TypeReqNone) + else if (type != TypeReqNone) { - return 0; + fVal = consoleStringToNumber(str, dbgFileName, dbgLineNumber); + if(type == TypeReqFloat) + { + index = getCurrentFloatTable()->add(fVal); + } } - - fVal = consoleStringToNumber(str, dbgFileName, dbgLineNumber); - if(type == TypeReqFloat) - index = getCurrentFloatTable()->add(fVal); - return 2; -} - -U32 StrConstNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ + // If this is a DOCBLOCK, then process w/ appropriate op... if( doc ) { - codeStream[ip++] = OP_DOCBLOCK_STR; - codeStream[ip++] = index; + codeStream.emit(OP_DOCBLOCK_STR); + codeStream.emit(index); return ip; } @@ -1008,21 +811,21 @@ U32 StrConstNode::compile(U32 *codeStream, U32 ip, TypeReq type) switch(type) { case TypeReqString: - codeStream[ip++] = tag ? OP_TAG_TO_STR : OP_LOADIMMED_STR; - codeStream[ip++] = index; + codeStream.emit(tag ? OP_TAG_TO_STR : OP_LOADIMMED_STR); + codeStream.emit(index); break; case TypeReqUInt: - codeStream[ip++] = OP_LOADIMMED_UINT; - codeStream[ip++] = U32(fVal); + codeStream.emit(OP_LOADIMMED_UINT); + codeStream.emit(U32(fVal)); break; case TypeReqFloat: - codeStream[ip++] = OP_LOADIMMED_FLT; - codeStream[ip++] = index; + codeStream.emit(OP_LOADIMMED_FLT); + codeStream.emit(index); break; case TypeReqNone: break; } - return ip; + return codeStream.tell(); } TypeReq StrConstNode::getPreferredType() @@ -1032,38 +835,32 @@ TypeReq StrConstNode::getPreferredType() //------------------------------------------------------------ -U32 ConstantNode::precompile(TypeReq type) +U32 ConstantNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { if(type == TypeReqString) { precompileIdent(value); - return 2; } - else if(type == TypeReqNone) - return 0; - - fVal = consoleStringToNumber(value, dbgFileName, dbgLineNumber); - if(type == TypeReqFloat) - index = getCurrentFloatTable()->add(fVal); - return 2; -} - -U32 ConstantNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ + else if (type != TypeReqNone) + { + fVal = consoleStringToNumber(value, dbgFileName, dbgLineNumber); + if(type == TypeReqFloat) + index = getCurrentFloatTable()->add(fVal); + } + switch(type) { case TypeReqString: - codeStream[ip++] = OP_LOADIMMED_IDENT; - codeStream[ip] = STEtoU32(value, ip); - ip++; + codeStream.emit(OP_LOADIMMED_IDENT); + codeStream.emitSTE(value); break; case TypeReqUInt: - codeStream[ip++] = OP_LOADIMMED_UINT; - codeStream[ip++] = U32(fVal); + codeStream.emit(OP_LOADIMMED_UINT); + codeStream.emit(U32(fVal)); break; case TypeReqFloat: - codeStream[ip++] = OP_LOADIMMED_FLT; - codeStream[ip++] = index; + codeStream.emit(OP_LOADIMMED_FLT); + codeStream.emit(index); break; case TypeReqNone: break; @@ -1078,7 +875,7 @@ TypeReq ConstantNode::getPreferredType() //------------------------------------------------------------ -U32 AssignExprNode::precompile(TypeReq type) +U32 AssignExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { subType = expr->getPreferredType(); if(subType == TypeReqNone) @@ -1096,65 +893,52 @@ U32 AssignExprNode::precompile(TypeReq type) // OP_SETCURVAR_ARRAY_CREATE // OP_TERMINATE_REWIND_STR // OP_SAVEVAR - + //else // eval expr // OP_SETCURVAR_CREATE // varname // OP_SAVEVAR - const U32 addSize = (type != subType ? 1 : 0); - const U32 retSize = expr->precompile(subType); - -#ifdef DEBUG_AST_NODES - if(retSize > 1000) - Con::printf("Bad expr %s", expr->dbgStmtType().c_str()); -#endif - + precompileIdent(varName); - - return retSize + addSize + (arrayIndex ? arrayIndex->precompile(TypeReqString) + (subType == TypeReqString ? 8 : 6 ) : 3); -} - -U32 AssignExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ + ip = expr->compile(codeStream, ip, subType); if(arrayIndex) { if(subType == TypeReqString) - codeStream[ip++] = OP_ADVANCE_STR; + codeStream.emit(OP_ADVANCE_STR); - codeStream[ip++] = OP_LOADIMMED_IDENT; - codeStream[ip] = STEtoU32(varName, ip); - ip++; - codeStream[ip++] = OP_ADVANCE_STR; + codeStream.emit(OP_LOADIMMED_IDENT); + codeStream.emitSTE(varName); + + codeStream.emit(OP_ADVANCE_STR); ip = arrayIndex->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_REWIND_STR; - codeStream[ip++] = OP_SETCURVAR_ARRAY_CREATE; + codeStream.emit(OP_REWIND_STR); + codeStream.emit(OP_SETCURVAR_ARRAY_CREATE); if(subType == TypeReqString) - codeStream[ip++] = OP_TERMINATE_REWIND_STR; + codeStream.emit(OP_TERMINATE_REWIND_STR); } else { - codeStream[ip++] = OP_SETCURVAR_CREATE; - codeStream[ip] = STEtoU32(varName, ip); - ip++; + codeStream.emit(OP_SETCURVAR_CREATE); + codeStream.emitSTE(varName); } switch(subType) { case TypeReqString: - codeStream[ip++] = OP_SAVEVAR_STR; + codeStream.emit(OP_SAVEVAR_STR); break; case TypeReqUInt: - codeStream[ip++] = OP_SAVEVAR_UINT; + codeStream.emit(OP_SAVEVAR_UINT); break; case TypeReqFloat: - codeStream[ip++] = OP_SAVEVAR_FLT; + codeStream.emit(OP_SAVEVAR_FLT); break; case TypeReqNone: break; } if(type != subType) - codeStream[ip++] = conversionOp(subType, type); + codeStream.emit(conversionOp(subType, type)); return ip; } @@ -1212,67 +996,54 @@ static void getAssignOpTypeOp(S32 op, TypeReq &type, U32 &operand) } } -U32 AssignOpExprNode::precompile(TypeReq type) +U32 AssignOpExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { + // goes like this... // eval expr as float or int // if there's an arrayIndex - + // OP_LOADIMMED_IDENT // varName // OP_ADVANCE_STR // eval arrayIndex stringwise // OP_REWIND_STR // OP_SETCURVAR_ARRAY_CREATE - + // else // OP_SETCURVAR_CREATE // varName - + // OP_LOADVAR_FLT or UINT // operand // OP_SAVEVAR_FLT or UINT - + // conversion OP if necessary. getAssignOpTypeOp(op, subType, operand); precompileIdent(varName); - U32 size = expr->precompile(subType); - if(type != subType) - size++; - if(!arrayIndex) - return size + 5; - else - { - size += arrayIndex->precompile(TypeReqString); - return size + 8; - } -} - -U32 AssignOpExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ + ip = expr->compile(codeStream, ip, subType); if(!arrayIndex) { - codeStream[ip++] = OP_SETCURVAR_CREATE; - codeStream[ip] = STEtoU32(varName, ip); - ip++; + codeStream.emit(OP_SETCURVAR_CREATE); + codeStream.emitSTE(varName); } else { - codeStream[ip++] = OP_LOADIMMED_IDENT; - codeStream[ip] = STEtoU32(varName, ip); - ip++; - codeStream[ip++] = OP_ADVANCE_STR; + codeStream.emit(OP_LOADIMMED_IDENT); + codeStream.emitSTE(varName); + + codeStream.emit(OP_ADVANCE_STR); ip = arrayIndex->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_REWIND_STR; - codeStream[ip++] = OP_SETCURVAR_ARRAY_CREATE; + codeStream.emit(OP_REWIND_STR); + codeStream.emit(OP_SETCURVAR_ARRAY_CREATE); } - codeStream[ip++] = (subType == TypeReqFloat) ? OP_LOADVAR_FLT : OP_LOADVAR_UINT; - codeStream[ip++] = operand; - codeStream[ip++] = (subType == TypeReqFloat) ? OP_SAVEVAR_FLT : OP_SAVEVAR_UINT; + codeStream.emit((subType == TypeReqFloat) ? OP_LOADVAR_FLT : OP_LOADVAR_UINT); + codeStream.emit(operand); + codeStream.emit((subType == TypeReqFloat) ? OP_SAVEVAR_FLT : OP_SAVEVAR_UINT); if(subType != type) - codeStream[ip++] = conversionOp(subType, type); - return ip; + codeStream.emit(conversionOp(subType, type)); + return codeStream.tell(); } TypeReq AssignOpExprNode::getPreferredType() @@ -1283,25 +1054,14 @@ TypeReq AssignOpExprNode::getPreferredType() //------------------------------------------------------------ -U32 TTagSetStmtNode::precompileStmt(U32 loopCount) -{ - TORQUE_UNUSED(loopCount); - return 0; -} - -U32 TTagSetStmtNode::compileStmt(U32*, U32 ip, U32, U32) +U32 TTagSetStmtNode::compileStmt(CodeStream&, U32 ip) { return ip; } //------------------------------------------------------------ -U32 TTagDerefNode::precompile(TypeReq) -{ - return 0; -} - -U32 TTagDerefNode::compile(U32*, U32 ip, TypeReq) +U32 TTagDerefNode::compile(CodeStream&, U32 ip, TypeReq) { return ip; } @@ -1313,12 +1073,7 @@ TypeReq TTagDerefNode::getPreferredType() //------------------------------------------------------------ -U32 TTagExprNode::precompile(TypeReq) -{ - return 0; -} - -U32 TTagExprNode::compile(U32*, U32 ip, TypeReq) +U32 TTagExprNode::compile(CodeStream&, U32 ip, TypeReq) { return ip; } @@ -1330,48 +1085,38 @@ TypeReq TTagExprNode::getPreferredType() //------------------------------------------------------------ -U32 FuncCallExprNode::precompile(TypeReq type) +U32 FuncCallExprNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { // OP_PUSH_FRAME // arg OP_PUSH arg OP_PUSH arg OP_PUSH // eval all the args, then call the function. - + // OP_CALLFUNC // function // namespace // isDot - - U32 size = 0; - if(type != TypeReqString) - size++; + precompileIdent(funcName); precompileIdent(nameSpace); - for(ExprNode *walk = args; walk; walk = (ExprNode *) walk->getNext()) - size += walk->precompile(TypeReqString) + 1; - return size + 5; -} - -U32 FuncCallExprNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ - codeStream[ip++] = OP_PUSH_FRAME; + + codeStream.emit(OP_PUSH_FRAME); for(ExprNode *walk = args; walk; walk = (ExprNode *) walk->getNext()) { ip = walk->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_PUSH; + codeStream.emit(OP_PUSH); } if(callType == MethodCall || callType == ParentCall) - codeStream[ip++] = OP_CALLFUNC; + codeStream.emit(OP_CALLFUNC); else - codeStream[ip++] = OP_CALLFUNC_RESOLVE; + codeStream.emit(OP_CALLFUNC_RESOLVE); - codeStream[ip] = STEtoU32(funcName, ip); - ip++; - codeStream[ip] = STEtoU32(nameSpace, ip); - ip++; - codeStream[ip++] = callType; + codeStream.emitSTE(funcName); + codeStream.emitSTE(nameSpace); + + codeStream.emit(callType); if(type != TypeReqString) - codeStream[ip++] = conversionOp(TypeReqString, type); - return ip; + codeStream.emit(conversionOp(TypeReqString, type)); + return codeStream.tell(); } TypeReq FuncCallExprNode::getPreferredType() @@ -1382,33 +1127,19 @@ TypeReq FuncCallExprNode::getPreferredType() //------------------------------------------------------------ -U32 AssertCallExprNode::precompile( TypeReq type ) +U32 AssertCallExprNode::compile( CodeStream &codeStream, U32 ip, TypeReq type ) { #ifdef TORQUE_ENABLE_SCRIPTASSERTS - + messageIndex = getCurrentStringTable()->add( message, true, false ); - - U32 exprSize = testExpr->precompile(TypeReqUInt); - return exprSize + 2; - - #else - - return 0; - - #endif -} - -U32 AssertCallExprNode::compile( U32 *codeStream, U32 ip, TypeReq type ) -{ - #ifdef TORQUE_ENABLE_SCRIPTASSERTS - + ip = testExpr->compile( codeStream, ip, TypeReqUInt ); - codeStream[ip++] = OP_ASSERT; - codeStream[ip++] = messageIndex; + codeStream.emit(OP_ASSERT); + codeStream.emit(messageIndex); #endif - return ip; + return codeStream.tell(); } TypeReq AssertCallExprNode::getPreferredType() @@ -1418,12 +1149,13 @@ TypeReq AssertCallExprNode::getPreferredType() //------------------------------------------------------------ -U32 SlotAccessNode::precompile(TypeReq type) +U32 SlotAccessNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { if(type == TypeReqNone) - return 0; - U32 size = 0; + return ip; + precompileIdent(slotName); + if(arrayExpr) { // eval array @@ -1432,54 +1164,38 @@ U32 SlotAccessNode::precompile(TypeReq type) // OP_TERMINATE_REWIND_STR // OP_SETCURFIELDARRAY // total add of 4 + array precomp - size += 3 + arrayExpr->precompile(TypeReqString); - } - // eval object expression sub + 3 (op_setCurField + OP_SETCUROBJECT) - size += objectExpr->precompile(TypeReqString) + 3; - - // get field in desired type: - return size + 1; -} - -U32 SlotAccessNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ - if(type == TypeReqNone) - return ip; - - if(arrayExpr) - { + ip = arrayExpr->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_ADVANCE_STR; + codeStream.emit(OP_ADVANCE_STR); } ip = objectExpr->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_SETCUROBJECT; + codeStream.emit(OP_SETCUROBJECT); - codeStream[ip++] = OP_SETCURFIELD; + codeStream.emit(OP_SETCURFIELD); - codeStream[ip] = STEtoU32(slotName, ip); - ip++; + codeStream.emitSTE(slotName); if(arrayExpr) { - codeStream[ip++] = OP_TERMINATE_REWIND_STR; - codeStream[ip++] = OP_SETCURFIELD_ARRAY; + codeStream.emit(OP_TERMINATE_REWIND_STR); + codeStream.emit(OP_SETCURFIELD_ARRAY); } switch(type) { case TypeReqUInt: - codeStream[ip++] = OP_LOADFIELD_UINT; + codeStream.emit(OP_LOADFIELD_UINT); break; case TypeReqFloat: - codeStream[ip++] = OP_LOADFIELD_FLT; + codeStream.emit(OP_LOADFIELD_FLT); break; case TypeReqString: - codeStream[ip++] = OP_LOADFIELD_STR; + codeStream.emit(OP_LOADFIELD_STR); break; case TypeReqNone: break; } - return ip; + return codeStream.tell(); } TypeReq SlotAccessNode::getPreferredType() @@ -1489,38 +1205,21 @@ TypeReq SlotAccessNode::getPreferredType() //----------------------------------------------------------------------------- -U32 InternalSlotAccessNode::precompile(TypeReq type) -{ - if(type == TypeReqNone) - return 0; - - U32 size = 3; - - // eval object expression sub + 3 (op_setCurField + OP_SETCUROBJECT) - size += objectExpr->precompile(TypeReqString); - size += slotExpr->precompile(TypeReqString); - if(type != TypeReqUInt) - size++; - - // get field in desired type: - return size; -} - -U32 InternalSlotAccessNode::compile(U32 *codeStream, U32 ip, TypeReq type) +U32 InternalSlotAccessNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { if(type == TypeReqNone) return ip; ip = objectExpr->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_SETCUROBJECT; + codeStream.emit(OP_SETCUROBJECT); ip = slotExpr->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_SETCUROBJECT_INTERNAL; - codeStream[ip++] = recurse; + codeStream.emit(OP_SETCUROBJECT_INTERNAL); + codeStream.emit(recurse); if(type != TypeReqUInt) - codeStream[ip++] = conversionOp(TypeReqUInt, type); - return ip; + codeStream.emit(conversionOp(TypeReqUInt, type)); + return codeStream.tell(); } TypeReq InternalSlotAccessNode::getPreferredType() @@ -1530,27 +1229,25 @@ TypeReq InternalSlotAccessNode::getPreferredType() //----------------------------------------------------------------------------- -//------------------------------------------------------------ - -U32 SlotAssignNode::precompile(TypeReq type) +U32 SlotAssignNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { // first eval the expression TypeReqString - + // if it's an array: - + // if OP_ADVANCE_STR 1 // eval array - + // OP_ADVANCE_STR 1 // evaluate object expr // OP_SETCUROBJECT 1 // OP_SETCURFIELD 1 // fieldName 1 // OP_TERMINATE_REWIND_STR 1 - + // OP_SETCURFIELDARRAY 1 // OP_TERMINATE_REWIND_STR 1 - + // else // OP_ADVANCE_STR // evaluate object expr @@ -1558,69 +1255,47 @@ U32 SlotAssignNode::precompile(TypeReq type) // OP_SETCURFIELD // fieldName // OP_TERMINATE_REWIND_STR - + // OP_SAVEFIELD // convert to return type if necessary. - - U32 size = 0; - if(type != TypeReqString) - size++; - + precompileIdent(slotName); - - size += valueExpr->precompile(TypeReqString); - - if(objectExpr) - size += objectExpr->precompile(TypeReqString) + 5; - else - size += 5; - - if(arrayExpr) - size += arrayExpr->precompile(TypeReqString) + 3; - - if(typeID != -1) - size += 2; - - return size + 1; -} - -U32 SlotAssignNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ + ip = valueExpr->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_ADVANCE_STR; + codeStream.emit(OP_ADVANCE_STR); if(arrayExpr) { ip = arrayExpr->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_ADVANCE_STR; + codeStream.emit(OP_ADVANCE_STR); } if(objectExpr) { ip = objectExpr->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_SETCUROBJECT; + codeStream.emit(OP_SETCUROBJECT); } else - codeStream[ip++] = OP_SETCUROBJECT_NEW; - codeStream[ip++] = OP_SETCURFIELD; - codeStream[ip] = STEtoU32(slotName, ip); - ip++; + codeStream.emit(OP_SETCUROBJECT_NEW); + codeStream.emit(OP_SETCURFIELD); + codeStream.emitSTE(slotName); + if(arrayExpr) { - codeStream[ip++] = OP_TERMINATE_REWIND_STR; - codeStream[ip++] = OP_SETCURFIELD_ARRAY; + codeStream.emit(OP_TERMINATE_REWIND_STR); + codeStream.emit(OP_SETCURFIELD_ARRAY); } - codeStream[ip++] = OP_TERMINATE_REWIND_STR; - codeStream[ip++] = OP_SAVEFIELD_STR; + codeStream.emit(OP_TERMINATE_REWIND_STR); + codeStream.emit(OP_SAVEFIELD_STR); if(typeID != -1) { - codeStream[ip++] = OP_SETCURFIELD_TYPE; - codeStream[ip++] = typeID; + codeStream.emit(OP_SETCURFIELD_TYPE); + codeStream.emit(typeID); } if(type != TypeReqString) - codeStream[ip++] = conversionOp(TypeReqString, type); - return ip; + codeStream.emit(conversionOp(TypeReqString, type)); + return codeStream.tell(); } TypeReq SlotAssignNode::getPreferredType() @@ -1630,10 +1305,10 @@ TypeReq SlotAssignNode::getPreferredType() //------------------------------------------------------------ -U32 SlotAssignOpNode::precompile(TypeReq type) +U32 SlotAssignOpNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { // first eval the expression as its type - + // if it's an array: // eval array // OP_ADVANCE_STR @@ -1643,53 +1318,43 @@ U32 SlotAssignOpNode::precompile(TypeReq type) // fieldName // OP_TERMINATE_REWIND_STR // OP_SETCURFIELDARRAY - + // else // evaluate object expr // OP_SETCUROBJECT // OP_SETCURFIELD // fieldName - + // OP_LOADFIELD of appropriate type // operand // OP_SAVEFIELD of appropriate type // convert to return type if necessary. - + getAssignOpTypeOp(op, subType, operand); precompileIdent(slotName); - U32 size = valueExpr->precompile(subType); - if(type != subType) - size++; - if(arrayExpr) - return size + 9 + arrayExpr->precompile(TypeReqString) + objectExpr->precompile(TypeReqString); - else - return size + 6 + objectExpr->precompile(TypeReqString); -} - -U32 SlotAssignOpNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ + ip = valueExpr->compile(codeStream, ip, subType); if(arrayExpr) { ip = arrayExpr->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_ADVANCE_STR; + codeStream.emit(OP_ADVANCE_STR); } ip = objectExpr->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_SETCUROBJECT; - codeStream[ip++] = OP_SETCURFIELD; - codeStream[ip] = STEtoU32(slotName, ip); - ip++; + codeStream.emit(OP_SETCUROBJECT); + codeStream.emit(OP_SETCURFIELD); + codeStream.emitSTE(slotName); + if(arrayExpr) { - codeStream[ip++] = OP_TERMINATE_REWIND_STR; - codeStream[ip++] = OP_SETCURFIELD_ARRAY; + codeStream.emit(OP_TERMINATE_REWIND_STR); + codeStream.emit(OP_SETCURFIELD_ARRAY); } - codeStream[ip++] = (subType == TypeReqFloat) ? OP_LOADFIELD_FLT : OP_LOADFIELD_UINT; - codeStream[ip++] = operand; - codeStream[ip++] = (subType == TypeReqFloat) ? OP_SAVEFIELD_FLT : OP_SAVEFIELD_UINT; + codeStream.emit((subType == TypeReqFloat) ? OP_LOADFIELD_FLT : OP_LOADFIELD_UINT); + codeStream.emit(operand); + codeStream.emit((subType == TypeReqFloat) ? OP_SAVEFIELD_FLT : OP_SAVEFIELD_UINT); if(subType != type) - codeStream[ip++] = conversionOp(subType, type); - return ip; + codeStream.emit(conversionOp(subType, type)); + return codeStream.tell(); } TypeReq SlotAssignOpNode::getPreferredType() @@ -1700,10 +1365,10 @@ TypeReq SlotAssignOpNode::getPreferredType() //------------------------------------------------------------ -U32 ObjectDeclNode::precompileSubObject(bool) +U32 ObjectDeclNode::compileSubObject(CodeStream &codeStream, U32 ip, bool root) { // goes - + // OP_PUSHFRAME 1 // name expr // OP_PUSH 1 @@ -1715,98 +1380,69 @@ U32 ObjectDeclNode::precompileSubObject(bool) // isSingleton 1 // lineNumber 1 // fail point 1 - + // for each field, eval // OP_ADD_OBJECT (to UINT[0]) 1 // root? 1 - + // add all the sub objects. // OP_END_OBJECT 1 // root? 1 // To fix the stack issue [7/9/2007 Black] // OP_FINISH_OBJECT <-- fail point jumps to this opcode + + codeStream.emit(OP_PUSH_FRAME); + ip = classNameExpr->compile(codeStream, ip, TypeReqString); + codeStream.emit(OP_PUSH); - U32 argSize = 0; - precompileIdent(parentObject); + ip = objectNameExpr->compile(codeStream, ip, TypeReqString); + codeStream.emit(OP_PUSH); for(ExprNode *exprWalk = argList; exprWalk; exprWalk = (ExprNode *) exprWalk->getNext()) - argSize += exprWalk->precompile(TypeReqString) + 1; - argSize += classNameExpr->precompile(TypeReqString) + 1; + { + ip = exprWalk->compile(codeStream, ip, TypeReqString); + codeStream.emit(OP_PUSH); + } + codeStream.emit(OP_CREATE_OBJECT); + codeStream.emitSTE(parentObject); - U32 nameSize = objectNameExpr->precompile(TypeReqString) + 1; - - U32 slotSize = 0; + codeStream.emit(isDatablock); + codeStream.emit(isClassNameInternal); + codeStream.emit(isSingleton); + codeStream.emit(dbgLineNumber); + const U32 failIp = codeStream.emit(0); for(SlotAssignNode *slotWalk = slotDecls; slotWalk; slotWalk = (SlotAssignNode *) slotWalk->getNext()) - slotSize += slotWalk->precompile(TypeReqNone); - - // OP_ADD_OBJECT - U32 subObjSize = 0; + ip = slotWalk->compile(codeStream, ip, TypeReqNone); + codeStream.emit(OP_ADD_OBJECT); + codeStream.emit(root); for(ObjectDeclNode *objectWalk = subObjects; objectWalk; objectWalk = (ObjectDeclNode *) objectWalk->getNext()) - subObjSize += objectWalk->precompileSubObject(false); - - failOffset = 12 + nameSize + argSize + slotSize + subObjSize; - // +1 because the failOffset should jump to OP_FINISH_OBJECT [7/9/2007 Black] - return failOffset + 1; + ip = objectWalk->compileSubObject(codeStream, ip, false); + codeStream.emit(OP_END_OBJECT); + codeStream.emit(root || isDatablock); + // Added to fix the object creation issue [7/9/2007 Black] + failOffset = codeStream.emit(OP_FINISH_OBJECT); + + codeStream.patch(failIp, failOffset); + + return codeStream.tell(); } -U32 ObjectDeclNode::precompile(TypeReq type) +U32 ObjectDeclNode::compile(CodeStream &codeStream, U32 ip, TypeReq type) { // root object decl does: - + // push 0 onto the UINT stack OP_LOADIMMED_UINT // precompiles the subObject(true) // UINT stack now has object id // type conv to type - - U32 ret = 2 + precompileSubObject(true); - if(type != TypeReqUInt) - return ret + 1; - return ret; -} - -U32 ObjectDeclNode::compileSubObject(U32 *codeStream, U32 ip, bool root) -{ - U32 start = ip; - codeStream[ip++] = OP_PUSH_FRAME; - ip = classNameExpr->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_PUSH; - - ip = objectNameExpr->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_PUSH; - for(ExprNode *exprWalk = argList; exprWalk; exprWalk = (ExprNode *) exprWalk->getNext()) - { - ip = exprWalk->compile(codeStream, ip, TypeReqString); - codeStream[ip++] = OP_PUSH; - } - codeStream[ip++] = OP_CREATE_OBJECT; - codeStream[ip] = STEtoU32(parentObject, ip); - ip++; - codeStream[ip++] = isDatablock; - codeStream[ip++] = isClassNameInternal; - codeStream[ip++] = isSingleton; - codeStream[ip++] = dbgLineNumber; - codeStream[ip++] = start + failOffset; - for(SlotAssignNode *slotWalk = slotDecls; slotWalk; slotWalk = (SlotAssignNode *) slotWalk->getNext()) - ip = slotWalk->compile(codeStream, ip, TypeReqNone); - codeStream[ip++] = OP_ADD_OBJECT; - codeStream[ip++] = root; - for(ObjectDeclNode *objectWalk = subObjects; objectWalk; objectWalk = (ObjectDeclNode *) objectWalk->getNext()) - ip = objectWalk->compileSubObject(codeStream, ip, false); - codeStream[ip++] = OP_END_OBJECT; - codeStream[ip++] = root || isDatablock; - // Added to fix the object creation issue [7/9/2007 Black] - codeStream[ip++] = OP_FINISH_OBJECT; - return ip; -} - -U32 ObjectDeclNode::compile(U32 *codeStream, U32 ip, TypeReq type) -{ - codeStream[ip++] = OP_LOADIMMED_UINT; - codeStream[ip++] = 0; + + codeStream.emit(OP_LOADIMMED_UINT); + codeStream.emit(0); ip = compileSubObject(codeStream, ip, true); if(type != TypeReqUInt) - codeStream[ip++] = conversionOp(TypeReqUInt, type); - return ip; -} + codeStream.emit(conversionOp(TypeReqUInt, type)); + return codeStream.tell(); +} + TypeReq ObjectDeclNode::getPreferredType() { return TypeReqUInt; @@ -1814,7 +1450,7 @@ TypeReq ObjectDeclNode::getPreferredType() //------------------------------------------------------------ -U32 FunctionDeclStmtNode::precompileStmt(U32) +U32 FunctionDeclStmtNode::compileStmt(CodeStream &codeStream, U32 ip) { // OP_FUNC_DECL // func name @@ -1828,10 +1464,13 @@ U32 FunctionDeclStmtNode::precompileStmt(U32) // OP_RETURN_VOID setCurrentStringTable(&getFunctionStringTable()); setCurrentFloatTable(&getFunctionFloatTable()); - + argc = 0; for(VarNode *walk = args; walk; walk = (VarNode *)((StmtNode*)walk)->getNext()) + { + precompileIdent(walk->varName); argc++; + } CodeBlock::smInFunction = true; @@ -1839,44 +1478,36 @@ U32 FunctionDeclStmtNode::precompileStmt(U32) precompileIdent(nameSpace); precompileIdent(package); - U32 subSize = precompileBlock(stmts, 0); CodeBlock::smInFunction = false; - - addBreakCount(); - - setCurrentStringTable(&getGlobalStringTable()); - setCurrentFloatTable(&getGlobalFloatTable()); - - endOffset = argc + subSize + 8; - return endOffset; -} - -U32 FunctionDeclStmtNode::compileStmt(U32 *codeStream, U32 ip, U32, U32) -{ + + U32 start = ip; - codeStream[ip++] = OP_FUNC_DECL; - codeStream[ip] = STEtoU32(fnName, ip); - ip++; - codeStream[ip] = STEtoU32(nameSpace, ip); - ip++; - codeStream[ip] = STEtoU32(package, ip); - ip++; - codeStream[ip++] = U32( bool(stmts != NULL) ? 1 : 0 ) + U32( dbgLineNumber << 1 ); - codeStream[ip++] = start + endOffset; - codeStream[ip++] = argc; + codeStream.emit(OP_FUNC_DECL); + codeStream.emitSTE(fnName); + codeStream.emitSTE(nameSpace); + codeStream.emitSTE(package); + + codeStream.emit(U32( bool(stmts != NULL) ? 1 : 0 ) + U32( dbgLineNumber << 1 )); + const U32 endIp = codeStream.emit(0); + codeStream.emit(argc); for(VarNode *walk = args; walk; walk = (VarNode *)((StmtNode*)walk)->getNext()) { - codeStream[ip] = STEtoU32(walk->varName, ip); - ip++; + codeStream.emitSTE(walk->varName); } CodeBlock::smInFunction = true; - ip = compileBlock(stmts, codeStream, ip, 0, 0); + ip = compileBlock(stmts, codeStream, ip); // Add break so breakpoint can be set at closing brace or // in empty function. - addBreakLine( ip ); + addBreakLine(codeStream); CodeBlock::smInFunction = false; - codeStream[ip++] = OP_RETURN_VOID; + codeStream.emit(OP_RETURN_VOID); + + codeStream.patch(endIp, codeStream.tell()); + + setCurrentStringTable(&getGlobalStringTable()); + setCurrentFloatTable(&getGlobalFloatTable()); + return ip; } diff --git a/Engine/source/console/codeBlock.cpp b/Engine/source/console/codeBlock.cpp index c6efd9cf2..1341dc2bd 100644 --- a/Engine/source/console/codeBlock.cpp +++ b/Engine/source/console/codeBlock.cpp @@ -33,7 +33,6 @@ using namespace Compiler; bool CodeBlock::smInFunction = false; -U32 CodeBlock::smBreakLineCount = 0; CodeBlock * CodeBlock::smCodeBlockList = NULL; CodeBlock * CodeBlock::smCurrentCodeBlock = NULL; ConsoleParser *CodeBlock::smCurrentParser = NULL; @@ -456,6 +455,8 @@ bool CodeBlock::read(StringTableEntry fileName, Stream &st) bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, const char *inScript, bool overrideNoDso) { + AssertFatal(Con::isMainThread(), "Compiling code on a secondary thread"); + // This will return true, but return value is ignored char *script; chompUTF8BOM( inScript, &script ); @@ -464,7 +465,7 @@ bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, con consoleAllocReset(); - STEtoU32 = compileSTEtoU32; + STEtoCode = compileSTEtoCode; gStatementList = NULL; @@ -497,17 +498,23 @@ bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, con resetTables(); smInFunction = false; - smBreakLineCount = 0; - setBreakCodeBlock(this); + CodeStream codeStream; + U32 lastIp; if(gStatementList) - codeSize = precompileBlock(gStatementList, 0) + 1; + { + lastIp = compileBlock(gStatementList, codeStream, 0) + 1; + } else + { codeSize = 1; - - lineBreakPairCount = smBreakLineCount; - code = new U32[codeSize + smBreakLineCount * 2]; - lineBreakPairs = code + codeSize; + lastIp = 0; + } + + codeStream.emit(OP_RETURN); + codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs); + + lineBreakPairCount = codeStream.getNumLineBreaks(); // Write string table data... getGlobalStringTable().write(st); @@ -517,18 +524,10 @@ bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, con getGlobalFloatTable().write(st); getFunctionFloatTable().write(st); - smBreakLineCount = 0; - U32 lastIp; - if(gStatementList) - lastIp = compileBlock(gStatementList, code, 0, 0, 0); - else - lastIp = 0; - - if(lastIp != codeSize - 1) + if(lastIp != codeSize) 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; + + U32 totSize = codeSize + codeStream.getNumLineBreaks() * 2; st.write(codeSize); st.write(lineBreakPairCount); @@ -561,11 +560,13 @@ bool CodeBlock::compile(const char *codeFileName, StringTableEntry fileName, con const char *CodeBlock::compileExec(StringTableEntry fileName, const char *inString, bool noCalls, S32 setFrame) { + AssertFatal(Con::isMainThread(), "Compiling code on a secondary thread"); + // Check for a UTF8 script file char *string; chompUTF8BOM( inString, &string ); - STEtoU32 = evalSTEtoU32; + STEtoCode = evalSTEtoCode; consoleAllocReset(); name = fileName; @@ -617,12 +618,11 @@ const char *CodeBlock::compileExec(StringTableEntry fileName, const char *inStri resetTables(); smInFunction = false; - smBreakLineCount = 0; - setBreakCodeBlock(this); + + CodeStream codeStream; + U32 lastIp = compileBlock(gStatementList, codeStream, 0); - codeSize = precompileBlock(gStatementList, 0) + 1; - - lineBreakPairCount = smBreakLineCount; + lineBreakPairCount = codeStream.getNumLineBreaks(); globalStrings = getGlobalStringTable().build(); globalStringsMaxLen = getGlobalStringTable().totalLen; @@ -632,20 +632,19 @@ const char *CodeBlock::compileExec(StringTableEntry fileName, const char *inStri globalFloats = getGlobalFloatTable().build(); functionFloats = getFunctionFloatTable().build(); - - code = new U32[codeSize + lineBreakPairCount * 2]; - lineBreakPairs = code + codeSize; - - smBreakLineCount = 0; - U32 lastIp = compileBlock(gStatementList, code, 0, 0, 0); - code[lastIp++] = OP_RETURN; + + codeStream.emit(OP_RETURN); + codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs); + + + dumpInstructions(0, false); consoleAllocReset(); if(lineBreakPairCount && fileName) calcBreakList(); - if(lastIp != codeSize) + if(lastIp+1 != codeSize) Con::warnf(ConsoleLogEntry::General, "precompile size mismatch, precompile: %d compile: %d", codeSize, lastIp); return exec(0, fileName, NULL, 0, 0, noCalls, NULL, setFrame); @@ -674,7 +673,7 @@ String CodeBlock::getFunctionArgs( U32 ip ) U32 fnArgc = code[ ip + 5 ]; for( U32 i = 0; i < fnArgc; ++ i ) { - StringTableEntry var = U32toSTE( code[ ip + i + 6 ] ); + StringTableEntry var = CodeToSTE(code, ip + (i*2) + 6); if( i != 0 ) str.append( ", " ); @@ -696,41 +695,52 @@ String CodeBlock::getFunctionArgs( U32 ip ) void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) { U32 ip = startIp; + smInFunction = false; + U32 endFuncIp = 0; + while( ip < codeSize ) { + if (ip > endFuncIp) + { + smInFunction = false; + } + switch( code[ ip ++ ] ) { + case OP_FUNC_DECL: { - StringTableEntry fnName = U32toSTE(code[ip]); - StringTableEntry fnNamespace = U32toSTE(code[ip+1]); - StringTableEntry fnPackage = U32toSTE(code[ip+2]); - bool hasBody = bool(code[ip+3]); - U32 newIp = code[ ip + 4 ]; - U32 argc = code[ ip + 5 ]; + StringTableEntry fnName = CodeToSTE(code, ip); + StringTableEntry fnNamespace = CodeToSTE(code, ip+2); + StringTableEntry fnPackage = CodeToSTE(code, ip+4); + bool hasBody = bool(code[ip+6]); + U32 newIp = code[ ip + 7 ]; + U32 argc = code[ ip + 8 ]; + endFuncIp = newIp; Con::printf( "%i: OP_FUNC_DECL name=%s nspace=%s package=%s hasbody=%i newip=%i argc=%i", ip - 1, fnName, fnNamespace, fnPackage, hasBody, newIp, argc ); // Skip args. - ip += 6 + argc; + ip += 9 + (argc * 2); + smInFunction = true; break; } case OP_CREATE_OBJECT: { - StringTableEntry objParent = U32toSTE(code[ip ]); - bool isDataBlock = code[ip + 1]; - bool isInternal = code[ip + 2]; - bool isSingleton = code[ip + 3]; - U32 lineNumber = code[ip + 4]; - U32 failJump = code[ip + 5]; + StringTableEntry objParent = CodeToSTE(code, ip); + bool isDataBlock = code[ip + 2]; + bool isInternal = code[ip + 3]; + bool isSingleton = code[ip + 4]; + U32 lineNumber = code[ip + 5]; + U32 failJump = code[ip + 6]; Con::printf( "%i: OP_CREATE_OBJECT objParent=%s isDataBlock=%i isInternal=%i isSingleton=%i lineNumber=%i failJump=%i", ip - 1, objParent, isDataBlock, isInternal, isSingleton, lineNumber, failJump ); - ip += 6; + ip += 7; break; } @@ -957,19 +967,19 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) case OP_SETCURVAR: { - StringTableEntry var = U32toSTE(code[ip]); + StringTableEntry var = CodeToSTE(code, ip); Con::printf( "%i: OP_SETCURVAR var=%s", ip - 1, var ); - ip++; + ip += 2; break; } case OP_SETCURVAR_CREATE: { - StringTableEntry var = U32toSTE(code[ip]); + StringTableEntry var = CodeToSTE(code, ip); Con::printf( "%i: OP_SETCURVAR_CREATE var=%s", ip - 1, var ); - ip++; + ip += 2; break; } @@ -1042,9 +1052,10 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) case OP_SETCURFIELD: { - StringTableEntry curField = U32toSTE(code[ip]); + StringTableEntry curField = CodeToSTE(code, ip); Con::printf( "%i: OP_SETCURFIELD field=%s", ip - 1, curField ); - ++ ip; + ip += 2; + break; } case OP_SETCURFIELD_ARRAY: @@ -1161,7 +1172,7 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) case OP_LOADIMMED_FLT: { - F64 val = functionFloats[ code[ ip ] ]; + F64 val = (smInFunction ? functionFloats : globalFloats)[ code[ ip ] ]; Con::printf( "%i: OP_LOADIMMED_FLT val=%f", ip - 1, val ); ++ ip; break; @@ -1169,7 +1180,7 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) case OP_TAG_TO_STR: { - const char* str = functionStrings + code[ ip ]; + const char* str = (smInFunction ? functionStrings : globalStrings) + code[ ip ]; Con::printf( "%i: OP_TAG_TO_STR str=%s", ip - 1, str ); ++ ip; break; @@ -1177,7 +1188,7 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) case OP_LOADIMMED_STR: { - const char* str = functionStrings + code[ ip ]; + const char* str = (smInFunction ? functionStrings : globalStrings) + code[ ip ]; Con::printf( "%i: OP_LOADIMMED_STR str=%s", ip - 1, str ); ++ ip; break; @@ -1185,7 +1196,7 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) case OP_DOCBLOCK_STR: { - const char* str = functionStrings + code[ ip ]; + const char* str = (smInFunction ? functionStrings : globalStrings) + code[ ip ]; Con::printf( "%i: OP_DOCBLOCK_STR str=%s", ip - 1, str ); ++ ip; break; @@ -1193,37 +1204,37 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) case OP_LOADIMMED_IDENT: { - StringTableEntry str = U32toSTE( code[ ip ] ); + StringTableEntry str = CodeToSTE(code, ip); Con::printf( "%i: OP_LOADIMMED_IDENT str=%s", ip - 1, str ); - ++ ip; + ip += 2; break; } case OP_CALLFUNC_RESOLVE: { - StringTableEntry fnNamespace = U32toSTE(code[ip+1]); - StringTableEntry fnName = U32toSTE(code[ip]); + StringTableEntry fnNamespace = CodeToSTE(code, ip+2); + StringTableEntry fnName = CodeToSTE(code, ip); U32 callType = code[ip+2]; Con::printf( "%i: OP_CALLFUNC_RESOLVE name=%s nspace=%s callType=%s", ip - 1, fnName, fnNamespace, callType == FuncCallExprNode::FunctionCall ? "FunctionCall" : callType == FuncCallExprNode::MethodCall ? "MethodCall" : "ParentCall" ); - ip += 3; + ip += 5; break; } case OP_CALLFUNC: { - StringTableEntry fnNamespace = U32toSTE(code[ip+1]); - StringTableEntry fnName = U32toSTE(code[ip]); - U32 callType = code[ip+2]; + StringTableEntry fnNamespace = CodeToSTE(code, ip+2); + StringTableEntry fnName = CodeToSTE(code, ip); + U32 callType = code[ip+4]; Con::printf( "%i: OP_CALLFUNC name=%s nspace=%s callType=%s", ip - 1, fnName, fnNamespace, callType == FuncCallExprNode::FunctionCall ? "FunctionCall" : callType == FuncCallExprNode::MethodCall ? "MethodCall" : "ParentCall" ); - ip += 3; + ip += 5; break; } @@ -1285,7 +1296,7 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) case OP_ASSERT: { - const char* message = functionStrings + code[ ip ]; + const char* message = (smInFunction ? functionStrings : globalStrings) + code[ ip ]; Con::printf( "%i: OP_ASSERT message=%s", ip - 1, message ); ++ ip; break; @@ -1299,31 +1310,34 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) case OP_ITER_BEGIN: { - StringTableEntry varName = U32toSTE( code[ ip ] ); - U32 failIp = code[ ip + 1 ]; + StringTableEntry varName = CodeToSTE(code, ip); + U32 failIp = code[ ip + 2 ]; - Con::printf( "%i: OP_ITER_BEGIN varName=%s failIp=%i", varName, failIp ); + Con::printf( "%i: OP_ITER_BEGIN varName=%s failIp=%i", ip - 1, varName, failIp ); - ++ ip; + ip += 3; + break; } case OP_ITER_BEGIN_STR: { - StringTableEntry varName = U32toSTE( code[ ip ] ); - U32 failIp = code[ ip + 1 ]; + StringTableEntry varName = CodeToSTE(code, ip); + U32 failIp = code[ ip + 2 ]; - Con::printf( "%i: OP_ITER_BEGIN varName=%s failIp=%i", varName, failIp ); + Con::printf( "%i: OP_ITER_BEGIN varName=%s failIp=%i", ip - 1, varName, failIp ); - ip += 2; + ip += 3; + break; } case OP_ITER: { U32 breakIp = code[ ip ]; - Con::printf( "%i: OP_ITER breakIp=%i", breakIp ); + Con::printf( "%i: OP_ITER breakIp=%i", ip - 1, breakIp ); ++ ip; + break; } case OP_ITER_END: @@ -1337,4 +1351,6 @@ void CodeBlock::dumpInstructions( U32 startIp, bool upToReturn ) break; } } + + smInFunction = false; } diff --git a/Engine/source/console/codeBlock.h b/Engine/source/console/codeBlock.h index bf09e79c7..555d7b624 100644 --- a/Engine/source/console/codeBlock.h +++ b/Engine/source/console/codeBlock.h @@ -38,7 +38,6 @@ private: static CodeBlock* smCurrentCodeBlock; public: - static U32 smBreakLineCount; static bool smInFunction; static Compiler::ConsoleParser * smCurrentParser; diff --git a/Engine/source/console/compiledEval.cpp b/Engine/source/console/compiledEval.cpp index 37f621997..18557e0c9 100644 --- a/Engine/source/console/compiledEval.cpp +++ b/Engine/source/console/compiledEval.cpp @@ -422,8 +422,8 @@ const char *CodeBlock::exec(U32 ip, const char *functionName, Namespace *thisNam if(argv) { // assume this points into a function decl: - U32 fnArgc = code[ip + 5]; - thisFunctionName = U32toSTE(code[ip]); + U32 fnArgc = code[ip + 2 + 6]; + thisFunctionName = CodeToSTE(code, ip); argc = getMin(argc-1, fnArgc); // argv[0] is func name if(gEvalState.traceOn) { @@ -458,11 +458,11 @@ const char *CodeBlock::exec(U32 ip, const char *functionName, Namespace *thisNam popFrame = true; for(i = 0; i < argc; i++) { - StringTableEntry var = U32toSTE(code[ip + i + 6]); + StringTableEntry var = CodeToSTE(code, ip + (2 + 6 + 1) + (i * 2)); gEvalState.setCurVarNameCreate(var); gEvalState.setStringVariable(argv[i+1]); } - ip = ip + fnArgc + 6; + ip = ip + (fnArgc * 2) + (2 + 6 + 1); curFloatTable = functionFloats; curStringTable = functionStrings; curStringTableLen = functionStringsMaxLen; @@ -559,11 +559,11 @@ breakContinue: case OP_FUNC_DECL: if(!noCalls) { - fnName = U32toSTE(code[ip]); - fnNamespace = U32toSTE(code[ip+1]); - fnPackage = U32toSTE(code[ip+2]); - bool hasBody = ( code[ ip + 3 ] & 0x01 ) != 0; - U32 lineNumber = code[ ip + 3 ] >> 1; + fnName = CodeToSTE(code, ip); + fnNamespace = CodeToSTE(code, ip+2); + fnPackage = CodeToSTE(code, ip+4); + bool hasBody = ( code[ ip + 6 ] & 0x01 ) != 0; + U32 lineNumber = code[ ip + 6 ] >> 1; Namespace::unlinkPackages(); ns = Namespace::find(fnNamespace, fnPackage); @@ -586,18 +586,18 @@ breakContinue: //Con::printf("Adding function %s::%s (%d)", fnNamespace, fnName, ip); } - ip = code[ip + 4]; + ip = code[ip + 7]; break; case OP_CREATE_OBJECT: { // Read some useful info. - objParent = U32toSTE(code[ip ]); - bool isDataBlock = code[ip + 1]; - bool isInternal = code[ip + 2]; - bool isSingleton = code[ip + 3]; - U32 lineNumber = code[ip + 4]; - failJump = code[ip + 5]; + objParent = CodeToSTE(code, ip); + bool isDataBlock = code[ip + 2]; + bool isInternal = code[ip + 3]; + bool isSingleton = code[ip + 4]; + U32 lineNumber = code[ip + 5]; + failJump = code[ip + 6]; // If we don't allow calls, we certainly don't allow creating objects! // Moved this to after failJump is set. Engine was crashing when @@ -848,7 +848,7 @@ breakContinue: } // Advance the IP past the create info... - ip += 6; + ip += 7; break; } @@ -1170,8 +1170,8 @@ breakContinue: break; case OP_SETCURVAR: - var = U32toSTE(code[ip]); - ip++; + var = CodeToSTE(code, ip); + ip += 2; // If a variable is set, then these must be NULL. It is necessary // to set this here so that the vector parser can appropriately @@ -1190,8 +1190,8 @@ breakContinue: break; case OP_SETCURVAR_CREATE: - var = U32toSTE(code[ip]); - ip++; + var = CodeToSTE(code, ip); + ip += 2; // See OP_SETCURVAR prevField = NULL; @@ -1310,9 +1310,9 @@ breakContinue: // Save the previous field for parsing vector fields. prevField = curField; dStrcpy( prevFieldArray, curFieldArray ); - curField = U32toSTE(code[ip]); + curField = CodeToSTE(code, ip); curFieldArray[0] = 0; - ip++; + ip += 2; break; case OP_SETCURFIELD_ARRAY: @@ -1504,28 +1504,38 @@ breakContinue: break; case OP_LOADIMMED_IDENT: - STR.setStringValue(U32toSTE(code[ip++])); + STR.setStringValue(CodeToSTE(code, ip)); + ip += 2; break; case OP_CALLFUNC_RESOLVE: // This deals with a function that is potentially living in a namespace. - fnNamespace = U32toSTE(code[ip+1]); - fnName = U32toSTE(code[ip]); + fnNamespace = CodeToSTE(code, ip+2); + fnName = CodeToSTE(code, ip); // Try to look it up. ns = Namespace::find(fnNamespace); nsEntry = ns->lookup(fnName); if(!nsEntry) { - ip+= 3; + ip+= 5; Con::warnf(ConsoleLogEntry::General, "%s: Unable to find function %s%s%s", - getFileLine(ip-4), fnNamespace ? fnNamespace : "", + getFileLine(ip-7), fnNamespace ? fnNamespace : "", fnNamespace ? "::" : "", fnName); STR.popFrame(); break; } + // Now fall through to OP_CALLFUNC... + // Now, rewrite our code a bit (ie, avoid future lookups) and fall + // through to OP_CALLFUNC +#ifdef TORQUE_64 + *((U64*)(code+ip+2)) = ((U64)nsEntry); +#else + code[ip+2] = ((U32)nsEntry); +#endif + code[ip-1] = OP_CALLFUNC; case OP_CALLFUNC: { @@ -1535,7 +1545,7 @@ breakContinue: // or just on the object. S32 routingId = 0; - fnName = U32toSTE(code[ip]); + fnName = CodeToSTE(code, ip); //if this is called from inside a function, append the ip and codeptr if( gEvalState.getStackDepth() > 0 ) @@ -1544,9 +1554,9 @@ breakContinue: gEvalState.getCurrentFrame().ip = ip - 1; } - U32 callType = code[ip+2]; + U32 callType = code[ip+4]; - ip += 3; + ip += 5; STR.getArgcArgv(fnName, &callArgc, &callArgv); const char *componentReturnValue = ""; @@ -1555,9 +1565,12 @@ breakContinue: { if( !nsEntry ) { - // We must not have come from OP_CALLFUNC_RESOLVE, so figure out - // our own entry. - nsEntry = Namespace::global()->lookup( fnName ); +#ifdef TORQUE_64 + nsEntry = ((Namespace::Entry *) *((U64*)(code+ip-3))); +#else + nsEntry = ((Namespace::Entry *) *(code+ip-3)); +#endif + ns = NULL; } ns = NULL; } @@ -1618,7 +1631,7 @@ breakContinue: { if(!noCalls && !( routingId == MethodOnComponent ) ) { - Con::warnf(ConsoleLogEntry::General,"%s: Unknown command %s.", getFileLine(ip-4), fnName); + Con::warnf(ConsoleLogEntry::General,"%s: Unknown command %s.", getFileLine(ip-6), fnName); if(callType == FuncCallExprNode::MethodCall) { Con::warnf(ConsoleLogEntry::General, " Object %s(%d) %s", @@ -1652,16 +1665,16 @@ breakContinue: // which is useful behavior when debugging so I'm ifdefing this out for debug builds. if(nsEntry->mToolOnly && ! Con::isCurrentScriptToolScript()) { - Con::errorf(ConsoleLogEntry::Script, "%s: %s::%s - attempting to call tools only function from outside of tools.", getFileLine(ip-4), nsName, fnName); + Con::errorf(ConsoleLogEntry::Script, "%s: %s::%s - attempting to call tools only function from outside of tools.", getFileLine(ip-6), nsName, fnName); } else #endif if((nsEntry->mMinArgs && S32(callArgc) < nsEntry->mMinArgs) || (nsEntry->mMaxArgs && S32(callArgc) > nsEntry->mMaxArgs)) { Con::warnf(ConsoleLogEntry::Script, "%s: %s::%s - wrong number of arguments (got %i, expected min %i and max %i).", - getFileLine(ip-4), nsName, fnName, + getFileLine(ip-6), nsName, fnName, callArgc, nsEntry->mMinArgs, nsEntry->mMaxArgs); - Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", getFileLine(ip-4), nsEntry->mUsage); + Con::warnf(ConsoleLogEntry::Script, "%s: usage: %s", getFileLine(ip-6), nsEntry->mUsage); STR.popFrame(); } else @@ -1725,7 +1738,7 @@ breakContinue: case Namespace::Entry::VoidCallbackType: nsEntry->cb.mVoidCallbackFunc(gEvalState.thisObject, callArgc, callArgv); if( code[ ip ] != OP_STR_TO_NONE && Con::getBoolVariable( "$Con::warnVoidAssignment", true ) ) - Con::warnf(ConsoleLogEntry::General, "%s: Call to %s in %s uses result of void function call.", getFileLine(ip-4), fnName, functionName); + Con::warnf(ConsoleLogEntry::General, "%s: Call to %s in %s uses result of void function call.", getFileLine(ip-6), fnName, functionName); STR.popFrame(); STR.setStringValue(""); @@ -1844,8 +1857,8 @@ breakContinue: case OP_ITER_BEGIN: { - StringTableEntry varName = U32toSTE( code[ ip ] ); - U32 failIp = code[ ip + 1 ]; + StringTableEntry varName = CodeToSTE(code, ip); + U32 failIp = code[ ip + 2 ]; IterStackRecord& iter = iterStack[ _ITER ]; @@ -1880,7 +1893,7 @@ breakContinue: STR.push(); - ip += 2; + ip += 3; break; } diff --git a/Engine/source/console/compiler.cpp b/Engine/source/console/compiler.cpp index ba8a0c314..ca1282ca6 100644 --- a/Engine/source/console/compiler.cpp +++ b/Engine/source/console/compiler.cpp @@ -60,29 +60,27 @@ namespace Compiler 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) + void evalSTEtoCode(StringTableEntry ste, U32 ip, U32 *ptr) { - return *((U32 *) &ste); +#ifdef TORQUE_64 + *((U64*)(ptr) = (U64)ste; +#else + *ptr = (U32)ste; +#endif } - - U32 compileSTEtoU32(StringTableEntry ste, U32 ip) + + void compileSTEtoCode(StringTableEntry ste, U32 ip, U32 *ptr) { if(ste) getIdentTable().add(ste, ip); - return 0; + *ptr = 0; + *(ptr+1) = 0; } - - U32 (*STEtoU32)(StringTableEntry ste, U32 ip) = evalSTEtoU32; + + void (*STEtoCode)(StringTableEntry ste, U32 ip, U32 *ptr) = evalSTEtoCode; //------------------------------------------------------------ @@ -286,3 +284,131 @@ void CompilerIdentTable::write(Stream &st) st.write(el->ip); } } + +//------------------------------------------------------------------------- + +U8 *CodeStream::allocCode(U32 sz) +{ + U8 *ptr = NULL; + if (mCodeHead) + { + const U32 bytesLeft = BlockSize - mCodeHead->size; + if (bytesLeft > sz) + { + ptr = mCodeHead->data + mCodeHead->size; + mCodeHead->size += sz; + return ptr; + } + } + + CodeData *data = new CodeData; + data->data = (U8*)dMalloc(BlockSize); + data->size = sz; + data->next = NULL; + + if (mCodeHead) + mCodeHead->next = data; + mCodeHead = data; + if (mCode == NULL) + mCode = data; + return data->data; +} + +//------------------------------------------------------------------------- + +void CodeStream::fixLoop(U32 loopBlockStart, U32 breakPoint, U32 continuePoint) +{ + AssertFatal(mFixStack.size() > 0, "Fix stack mismatch"); + + U32 fixStart = mFixStack[mFixStack.size()-1]; + for (U32 i=fixStart; inext) + { + U32 bytesToCopy = itr->size > outBytes ? outBytes : itr->size; + dMemcpy(outPtr, itr->data, bytesToCopy); + outPtr += bytesToCopy; + outBytes -= bytesToCopy; + } + + *lineBreaks = *stream + mCodePos; + dMemcpy(*lineBreaks, mBreakLines.address(), sizeof(U32) * mBreakLines.size()); + + // Apply patches on top + for (U32 i=0; inext : NULL; + while (itr != NULL) + { + CodeData *next = itr->next; + dFree(itr->data); + dFree(itr); + itr = next; + } + + if (mCode) + { + mCode->size = 0; + mCode->next = NULL; + mCodeHead = mCode; + } +} + diff --git a/Engine/source/console/compiler.h b/Engine/source/console/compiler.h index 66e3966b9..c4b4f94af 100644 --- a/Engine/source/console/compiler.h +++ b/Engine/source/console/compiler.h @@ -24,6 +24,12 @@ #ifndef _COMPILER_H_ #define _COMPILER_H_ +//#define DEBUG_CODESTREAM + +#ifdef DEBUG_CODESTREAM +#include +#endif + class Stream; class DataChunker; @@ -49,7 +55,7 @@ namespace Compiler OP_JMPIFF, OP_JMPIF, OP_JMPIFNOT_NP, - OP_JMPIF_NP, + OP_JMPIF_NP, // 10 OP_JMP, OP_RETURN, // fixes a bug when not explicitly returning a value @@ -60,7 +66,7 @@ namespace Compiler OP_CMPLT, OP_CMPLE, OP_CMPNE, - OP_XOR, + OP_XOR, // 20 OP_MOD, OP_BITAND, OP_BITOR, @@ -71,7 +77,7 @@ namespace Compiler OP_SHR, OP_SHL, OP_AND, - OP_OR, + OP_OR, // 30 OP_ADD, OP_SUB, @@ -84,7 +90,7 @@ namespace Compiler OP_SETCURVAR_ARRAY, OP_SETCURVAR_ARRAY_CREATE, - OP_LOADVAR_UINT, + OP_LOADVAR_UINT,// 40 OP_LOADVAR_FLT, OP_LOADVAR_STR, @@ -97,7 +103,7 @@ namespace Compiler OP_SETCUROBJECT_INTERNAL, OP_SETCURFIELD, - OP_SETCURFIELD_ARRAY, + OP_SETCURFIELD_ARRAY, // 50 OP_SETCURFIELD_TYPE, OP_LOADFIELD_UINT, @@ -110,7 +116,7 @@ namespace Compiler OP_STR_TO_UINT, OP_STR_TO_FLT, - OP_STR_TO_NONE, + OP_STR_TO_NONE, // 60 OP_FLT_TO_UINT, OP_FLT_TO_STR, OP_FLT_TO_NONE, @@ -121,7 +127,7 @@ namespace Compiler OP_LOADIMMED_UINT, OP_LOADIMMED_FLT, OP_TAG_TO_STR, - OP_LOADIMMED_STR, + OP_LOADIMMED_STR, // 70 OP_DOCBLOCK_STR, OP_LOADIMMED_IDENT, @@ -133,7 +139,7 @@ namespace Compiler OP_ADVANCE_STR_COMMA, OP_ADVANCE_STR_NUL, OP_REWIND_STR, - OP_TERMINATE_REWIND_STR, + OP_TERMINATE_REWIND_STR, // 80 OP_COMPARE_STR, OP_PUSH, @@ -147,14 +153,14 @@ namespace Compiler OP_ITER, ///< Enter foreach loop. OP_ITER_END, ///< End foreach loop. - OP_INVALID + OP_INVALID // 90 }; //------------------------------------------------------------ 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); + + U32 compileBlock(StmtNode *block, CodeStream &codeStream, U32 ip); //------------------------------------------------------------ @@ -218,15 +224,19 @@ namespace Compiler //------------------------------------------------------------ - inline StringTableEntry U32toSTE(U32 u) + inline StringTableEntry CodeToSTE(U32 *code, U32 ip) { - return *((StringTableEntry *) &u); +#ifdef TORQUE_64 + return (StringTableEntry)(*((U64*)(code+ip))); +#else + return (StringTableEntry)(*(code+ip)); +#endif } - extern U32 (*STEtoU32)(StringTableEntry ste, U32 ip); - - U32 evalSTEtoU32(StringTableEntry ste, U32); - U32 compileSTEtoU32(StringTableEntry ste, U32 ip); + extern void (*STEtoCode)(StringTableEntry ste, U32 ip, U32 *ptr); + + void evalSTEtoCode(StringTableEntry ste, U32 ip, U32 *ptr); + void compileSTEtoCode(StringTableEntry ste, U32 ip, U32 *ptr); CompilerStringTable *getCurrentStringTable(); CompilerStringTable &getGlobalStringTable(); @@ -244,9 +254,6 @@ namespace Compiler 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(); @@ -257,4 +264,167 @@ namespace Compiler extern bool gSyntaxError; }; +/// Utility class to emit and patch bytecode +class CodeStream +{ +public: + + enum FixType + { + // For loops + FIXTYPE_LOOPBLOCKSTART, + FIXTYPE_BREAK, + FIXTYPE_CONTINUE + }; + + enum Constants + { + BlockSize = 16384, + }; + +protected: + + typedef struct PatchEntry + { + U32 addr; ///< Address to patch + U32 value; ///< Value to place at addr + + PatchEntry() {;} + PatchEntry(U32 a, U32 v) : addr(a), value(v) {;} + } PatchEntry; + + typedef struct CodeData + { + U8 *data; ///< Allocated data (size is BlockSize) + U32 size; ///< Bytes used in data + CodeData *next; ///< Next block + }; + + /// @name Emitted code + /// { + CodeData *mCode; + CodeData *mCodeHead; + U32 mCodePos; + /// } + + /// @name Code fixing stacks + /// { + Vector mFixList; + Vector mFixStack; + Vector mFixLoopStack; + Vector mPatchList; + /// } + + Vector mBreakLines; ///< Line numbers + +public: + + CodeStream() : mCode(0), mCodeHead(NULL), mCodePos(0) + { + } + + ~CodeStream() + { + reset(); + + if (mCode) + delete mCode; + } + + U8 *allocCode(U32 sz); + + inline U32 emit(U32 code) + { + U32 *ptr = (U32*)allocCode(4); + *ptr = code; +#ifdef DEBUG_CODESTREAM + printf("code[%u] = %u\n", mCodePos, code); +#endif + return mCodePos++; + } + + inline void patch(U32 addr, U32 code) + { +#ifdef DEBUG_CODESTREAM + printf("patch[%u] = %u\n", addr, code); +#endif + mPatchList.push_back(PatchEntry(addr, code)); + } + + inline U32 emitSTE(const char *code) + { + U64 *ptr = (U64*)allocCode(8); + *ptr = 0; + Compiler::STEtoCode(code, mCodePos, (U32*)ptr); +#ifdef DEBUG_CODESTREAM + printf("code[%u] = %s\n", mCodePos, code); +#endif + mCodePos += 2; + return mCodePos-2; + } + + inline U32 tell() + { + return mCodePos; + } + + inline bool inLoop() + { + for (U32 i=0; i 0, "Fix stack mismatch"); + + U32 newSize = mFixStack[mFixStack.size()-1]; + while (mFixList.size() > newSize) + mFixList.pop_back(); + mFixStack.pop_back(); + mFixLoopStack.pop_back(); + } + + void fixLoop(U32 loopBlockStart, U32 breakPoint, U32 continuePoint); + + inline void addBreakLine(U32 lineNumber, U32 ip) + { + mBreakLines.push_back(lineNumber); + mBreakLines.push_back(ip); + } + + inline U32 getNumLineBreaks() + { + return mBreakLines.size() / 2; + } + + void emitCodeStream(U32 *size, U32 **stream, U32 **lineBreaks); + + void reset(); +}; + #endif diff --git a/Engine/source/console/console.h b/Engine/source/console/console.h index 3c5718c48..e5d4cbe23 100644 --- a/Engine/source/console/console.h +++ b/Engine/source/console/console.h @@ -182,7 +182,8 @@ namespace Con /// 09/12/07 - CAF - 43->44 remove newmsg operator /// 09/27/07 - RDB - 44->45 Patch from Andreas Kirsch: Added opcode to support correct void return /// 01/13/09 - TMS - 45->46 Added script assert - DSOVersion = 46, + /// 09/07/14 - jamesu - 47->47 64bit support + DSOVersion = 47, MaxLineLength = 512, ///< Maximum length of a line of console input. MaxDataTypes = 256 ///< Maximum number of registered data types. From 73c51e5d1ae4798c07cf07a9458307b6bb2e9df8 Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Sun, 7 Sep 2014 21:49:05 +0100 Subject: [PATCH 174/317] Fix leak when freeing compiler stream --- Engine/source/console/compiler.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Engine/source/console/compiler.h b/Engine/source/console/compiler.h index c4b4f94af..2eb6da7e6 100644 --- a/Engine/source/console/compiler.h +++ b/Engine/source/console/compiler.h @@ -328,7 +328,10 @@ public: reset(); if (mCode) + { + dFree(mCode->data); delete mCode; + } } U8 *allocCode(U32 sz); From e6ccc26a879c35afb6d0eaf0a5914981fef6837d Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Sun, 7 Sep 2014 22:07:22 +0100 Subject: [PATCH 175/317] Remove debug instruction dump --- Engine/source/console/codeBlock.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Engine/source/console/codeBlock.cpp b/Engine/source/console/codeBlock.cpp index 1341dc2bd..9dec41417 100644 --- a/Engine/source/console/codeBlock.cpp +++ b/Engine/source/console/codeBlock.cpp @@ -636,8 +636,7 @@ const char *CodeBlock::compileExec(StringTableEntry fileName, const char *inStri codeStream.emit(OP_RETURN); codeStream.emitCodeStream(&codeSize, &code, &lineBreakPairs); - - dumpInstructions(0, false); + //dumpInstructions(0, false); consoleAllocReset(); From 06b463399b6ac53a9925bdda6891bf7123b787e7 Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Sun, 7 Sep 2014 22:49:45 +0100 Subject: [PATCH 176/317] Function Call optimization is now optional (Can cause a problem with package lookups) --- Engine/source/console/compiledEval.cpp | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/Engine/source/console/compiledEval.cpp b/Engine/source/console/compiledEval.cpp index 18557e0c9..7af130847 100644 --- a/Engine/source/console/compiledEval.cpp +++ b/Engine/source/console/compiledEval.cpp @@ -45,6 +45,9 @@ #include "materials/materialManager.h" #endif +// Uncomment to optimize function calls at the expense of potential invalid package lookups +//#define COMPILER_OPTIMIZE_FUNCTION_CALLS + using namespace Compiler; enum EvalConstants { @@ -1527,6 +1530,7 @@ breakContinue: break; } +#ifdef COMPILER_OPTIMIZE_FUNCTION_CALLS // Now fall through to OP_CALLFUNC... // Now, rewrite our code a bit (ie, avoid future lookups) and fall // through to OP_CALLFUNC @@ -1536,6 +1540,7 @@ breakContinue: code[ip+2] = ((U32)nsEntry); #endif code[ip-1] = OP_CALLFUNC; +#endif case OP_CALLFUNC: { @@ -1565,10 +1570,14 @@ breakContinue: { if( !nsEntry ) { +#ifdef COMPILER_OPTIMIZE_FUNCTION_CALLS #ifdef TORQUE_64 nsEntry = ((Namespace::Entry *) *((U64*)(code+ip-3))); #else nsEntry = ((Namespace::Entry *) *(code+ip-3)); +#endif +#else + nsEntry = Namespace::global()->lookup( fnName ); #endif ns = NULL; } From 057f91d373f6def233850005c4cce04bebc8e390 Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Mon, 8 Sep 2014 11:27:01 +0100 Subject: [PATCH 177/317] Add vector include in compiler header for CodeStream --- Engine/source/console/compiler.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Engine/source/console/compiler.h b/Engine/source/console/compiler.h index 2eb6da7e6..a4acad9ba 100644 --- a/Engine/source/console/compiler.h +++ b/Engine/source/console/compiler.h @@ -37,6 +37,9 @@ class DataChunker; #include "console/ast.h" #include "console/codeBlock.h" +#ifndef _TVECTOR_H_ +#include "core/util/tVector.h" +#endif namespace Compiler { From ea4a8bb3615be431b51cb744adc8505ce6821286 Mon Sep 17 00:00:00 2001 From: Lukas Joergensen Date: Wed, 10 Sep 2014 01:21:52 +0200 Subject: [PATCH 178/317] Proper rendering of basetexture Now terrain basetexture is properly cleared and never transparent. --- Engine/source/terrain/terrRender.cpp | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/Engine/source/terrain/terrRender.cpp b/Engine/source/terrain/terrRender.cpp index 4b03ac812..ec4dec3dd 100644 --- a/Engine/source/terrain/terrRender.cpp +++ b/Engine/source/terrain/terrRender.cpp @@ -170,9 +170,10 @@ bool TerrainBlock::_initBaseShader() desc.zDefined = true; desc.zWriteEnable = false; desc.zEnable = false; - desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha ); + desc.setBlend( true, GFXBlendSrcAlpha, GFXBlendOne ); desc.cullDefined = true; desc.cullMode = GFXCullNone; + desc.colorWriteAlpha = false; mBaseShaderSB = GFX->createStateBlock( desc ); return true; @@ -251,6 +252,8 @@ void TerrainBlock::_updateBaseTexture( bool writeToCache ) mBaseTarget->attachTexture( GFXTextureTarget::Color0, blendTex ); GFX->setActiveRenderTarget( mBaseTarget ); + GFX->clear( GFXClearTarget, ColorI(0,0,0,255), 1.0f, 0 ); + GFX->setTexture( 0, mLayerTex ); mBaseShaderConsts->setSafe( mBaseLayerSizeConst, (F32)mLayerTex->getWidth() ); From 1e3db968ace56856b541850c68f6a7ebf2275307 Mon Sep 17 00:00:00 2001 From: James Urquhart Date: Sat, 13 Sep 2014 09:02:57 +0100 Subject: [PATCH 179/317] Update console.h Fix typo in version command --- Engine/source/console/console.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Engine/source/console/console.h b/Engine/source/console/console.h index e5d4cbe23..7d6529bde 100644 --- a/Engine/source/console/console.h +++ b/Engine/source/console/console.h @@ -182,7 +182,7 @@ namespace Con /// 09/12/07 - CAF - 43->44 remove newmsg operator /// 09/27/07 - RDB - 44->45 Patch from Andreas Kirsch: Added opcode to support correct void return /// 01/13/09 - TMS - 45->46 Added script assert - /// 09/07/14 - jamesu - 47->47 64bit support + /// 09/07/14 - jamesu - 46->47 64bit support DSOVersion = 47, MaxLineLength = 512, ///< Maximum length of a line of console input. From d9fc3abaa4c99e7444cb2844bfc709014cc05ac5 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 6 Jul 2014 12:09:19 +0200 Subject: [PATCH 180/317] Update libTheora 1.1.1 --- Engine/lib/libtheora/CHANGES | 62 + Engine/lib/libtheora/COPYING | 4 +- Engine/lib/libtheora/LICENSE | 18 + Engine/lib/libtheora/include/Makefile.am | 3 + Engine/lib/libtheora/include/Makefile.in | 414 ++ .../lib/libtheora/include/theora/Makefile.am | 7 + .../lib/libtheora/include/theora/Makefile.in | 355 ++ Engine/lib/libtheora/include/theora/codec.h | 55 +- Engine/lib/libtheora/include/theora/theora.h | 104 +- .../lib/libtheora/include/theora/theoradec.h | 20 +- .../lib/libtheora/include/theora/theoraenc.h | 268 +- Engine/lib/libtheora/lib/Makefile.am | 173 + Engine/lib/libtheora/lib/Makefile.in | 845 ++++ Engine/lib/libtheora/lib/Version_script | 53 + Engine/lib/libtheora/lib/Version_script-dec | 82 + Engine/lib/libtheora/lib/Version_script-enc | 43 + Engine/lib/libtheora/lib/analyze.c | 2709 +++++++++++ .../lib/libtheora/lib/{dec => }/apiwrapper.c | 20 +- .../lib/libtheora/lib/{dec => }/apiwrapper.h | 7 +- Engine/lib/libtheora/lib/bitpack.c | 111 + Engine/lib/libtheora/lib/{dec => }/bitpack.h | 47 +- Engine/lib/libtheora/lib/cpu.c | 9 +- Engine/lib/libtheora/lib/cpu.h | 4 +- Engine/lib/libtheora/lib/{dec => }/dct.h | 4 +- Engine/lib/libtheora/lib/dec/bitpack.c | 121 - Engine/lib/libtheora/lib/dec/decode.c | 2057 --------- Engine/lib/libtheora/lib/dec/fragment.c | 199 - Engine/lib/libtheora/lib/dec/huffdec.c | 325 -- Engine/lib/libtheora/lib/dec/idct.h | 26 - Engine/lib/libtheora/lib/dec/ocintrin.h | 88 - Engine/lib/libtheora/lib/dec/quant.c | 122 - Engine/lib/libtheora/lib/dec/x86/mmxstate.c | 653 --- Engine/lib/libtheora/lib/dec/x86/x86int.h | 42 - Engine/lib/libtheora/lib/dec/x86_vc/mmxfrag.c | 214 - Engine/lib/libtheora/lib/dec/x86_vc/mmxidct.c | 1006 ---- .../libtheora/lib/dec/x86_vc/mmxloopfilter.c | 377 -- .../lib/libtheora/lib/dec/x86_vc/mmxstate.c | 189 - Engine/lib/libtheora/lib/dec/x86_vc/x86int.h | 49 - .../libtheora/lib/{dec => }/decapiwrapper.c | 13 +- Engine/lib/libtheora/lib/{dec => }/decinfo.c | 97 +- Engine/lib/libtheora/lib/{dec => }/decint.h | 48 +- Engine/lib/libtheora/lib/decode.c | 2943 ++++++++++++ Engine/lib/libtheora/lib/{dec => }/dequant.c | 46 +- Engine/lib/libtheora/lib/{dec => }/dequant.h | 7 +- Engine/lib/libtheora/lib/enc/block_inline.h | 37 - Engine/lib/libtheora/lib/enc/blockmap.c | 99 - Engine/lib/libtheora/lib/enc/codec_internal.h | 842 ---- Engine/lib/libtheora/lib/enc/dct.c | 268 -- Engine/lib/libtheora/lib/enc/dct_decode.c | 941 ---- Engine/lib/libtheora/lib/enc/dct_encode.c | 469 -- Engine/lib/libtheora/lib/enc/dsp.c | 422 -- Engine/lib/libtheora/lib/enc/dsp.h | 166 - Engine/lib/libtheora/lib/enc/encode.c | 1479 ------ .../lib/libtheora/lib/enc/encoder_huffman.c | 310 -- .../lib/libtheora/lib/enc/encoder_huffman.h | 74 - Engine/lib/libtheora/lib/enc/encoder_idct.c | 572 --- Engine/lib/libtheora/lib/enc/encoder_lookup.h | 120 - Engine/lib/libtheora/lib/enc/encoder_quant.c | 558 --- .../lib/libtheora/lib/enc/encoder_toplevel.c | 1447 ------ Engine/lib/libtheora/lib/enc/frarray.c | 243 - Engine/lib/libtheora/lib/enc/frinit.c | 392 -- Engine/lib/libtheora/lib/enc/hufftables.h | 1034 ----- Engine/lib/libtheora/lib/enc/mcomp.c | 767 ---- Engine/lib/libtheora/lib/enc/misc_common.c | 339 -- Engine/lib/libtheora/lib/enc/pb.c | 89 - Engine/lib/libtheora/lib/enc/pp.c | 951 ---- Engine/lib/libtheora/lib/enc/pp.h | 48 - Engine/lib/libtheora/lib/enc/quant_lookup.h | 43 - Engine/lib/libtheora/lib/enc/reconstruct.c | 110 - Engine/lib/libtheora/lib/enc/scan.c | 2301 ---------- .../lib/libtheora/lib/enc/toplevel_lookup.h | 40 - .../libtheora/lib/enc/x86_32/dct_decode_mmx.c | 409 -- Engine/lib/libtheora/lib/enc/x86_32/dsp_mmx.c | 666 --- .../lib/libtheora/lib/enc/x86_32/dsp_mmxext.c | 347 -- .../lib/libtheora/lib/enc/x86_32/fdct_mmx.c | 339 -- .../lib/libtheora/lib/enc/x86_32/idct_mmx.c | 1452 ------ .../lib/libtheora/lib/enc/x86_32/recon_mmx.c | 182 - .../lib/libtheora/lib/enc/x86_32_vs/dsp_mmx.c | 1605 ------- .../libtheora/lib/enc/x86_32_vs/fdct_mmx.c | 333 -- .../libtheora/lib/enc/x86_32_vs/recon_mmx.c | 197 - .../libtheora/lib/enc/x86_64/dct_decode_mmx.c | 409 -- Engine/lib/libtheora/lib/enc/x86_64/dsp_mmx.c | 303 -- .../lib/libtheora/lib/enc/x86_64/dsp_mmxext.c | 323 -- .../lib/libtheora/lib/enc/x86_64/fdct_mmx.c | 342 -- .../lib/libtheora/lib/enc/x86_64/idct_mmx.c | 27 - .../lib/libtheora/lib/enc/x86_64/recon_mmx.c | 184 - Engine/lib/libtheora/lib/encapiwrapper.c | 168 + Engine/lib/libtheora/lib/encfrag.c | 388 ++ Engine/lib/libtheora/lib/encinfo.c | 121 + Engine/lib/libtheora/lib/encint.h | 493 ++ Engine/lib/libtheora/lib/encode.c | 1615 +++++++ Engine/lib/libtheora/lib/enquant.c | 274 ++ Engine/lib/libtheora/lib/enquant.h | 27 + Engine/lib/libtheora/lib/fdct.c | 422 ++ Engine/lib/libtheora/lib/fragment.c | 87 + Engine/lib/libtheora/lib/huffdec.c | 489 ++ Engine/lib/libtheora/lib/{dec => }/huffdec.h | 13 +- .../lib/{enc/encapiwrapper.c => huffenc.c} | 417 +- Engine/lib/libtheora/lib/huffenc.h | 19 + Engine/lib/libtheora/lib/{dec => }/huffman.h | 6 +- Engine/lib/libtheora/lib/{dec => }/idct.c | 91 +- Engine/lib/libtheora/lib/{dec => }/info.c | 24 +- Engine/lib/libtheora/lib/{dec => }/internal.c | 157 +- Engine/lib/libtheora/lib/internal.h | 460 +- Engine/lib/libtheora/lib/mathops.c | 296 ++ Engine/lib/libtheora/lib/mathops.h | 141 + Engine/lib/libtheora/lib/mcenc.c | 767 ++++ Engine/lib/libtheora/lib/modedec.h | 4027 +++++++++++++++++ Engine/lib/libtheora/lib/ocintrin.h | 128 + Engine/lib/libtheora/lib/quant.c | 119 + Engine/lib/libtheora/lib/{dec => }/quant.h | 7 +- Engine/lib/libtheora/lib/rate.c | 1137 +++++ Engine/lib/libtheora/lib/{dec => }/state.c | 991 ++-- Engine/lib/libtheora/lib/tokenize.c | 1072 +++++ Engine/lib/libtheora/lib/x86/mmxencfrag.c | 900 ++++ Engine/lib/libtheora/lib/x86/mmxfdct.c | 665 +++ .../lib/libtheora/lib/{dec => }/x86/mmxfrag.c | 64 +- Engine/lib/libtheora/lib/x86/mmxfrag.h | 64 + .../lib/libtheora/lib/{dec => }/x86/mmxidct.c | 57 +- Engine/lib/libtheora/lib/x86/mmxloop.h | 215 + Engine/lib/libtheora/lib/x86/mmxstate.c | 188 + Engine/lib/libtheora/lib/x86/sse2fdct.c | 523 +++ Engine/lib/libtheora/lib/x86/x86enc.c | 49 + Engine/lib/libtheora/lib/x86/x86enc.h | 47 + Engine/lib/libtheora/lib/x86/x86int.h | 42 + .../libtheora/lib/{dec => }/x86/x86state.c | 34 +- Engine/lib/libtheora/lib/x86_vc/mmxencfrag.c | 969 ++++ Engine/lib/libtheora/lib/x86_vc/mmxfdct.c | 670 +++ Engine/lib/libtheora/lib/x86_vc/mmxfrag.c | 337 ++ Engine/lib/libtheora/lib/x86_vc/mmxfrag.h | 61 + Engine/lib/libtheora/lib/x86_vc/mmxidct.c | 562 +++ Engine/lib/libtheora/lib/x86_vc/mmxloop.h | 219 + Engine/lib/libtheora/lib/x86_vc/mmxstate.c | 211 + Engine/lib/libtheora/lib/x86_vc/x86enc.c | 49 + Engine/lib/libtheora/lib/x86_vc/x86enc.h | 47 + Engine/lib/libtheora/lib/x86_vc/x86int.h | 42 + .../libtheora/lib/{dec => }/x86_vc/x86state.c | 49 +- 137 files changed, 27096 insertions(+), 28242 deletions(-) create mode 100644 Engine/lib/libtheora/LICENSE create mode 100644 Engine/lib/libtheora/include/Makefile.am create mode 100644 Engine/lib/libtheora/include/Makefile.in create mode 100644 Engine/lib/libtheora/include/theora/Makefile.am create mode 100644 Engine/lib/libtheora/include/theora/Makefile.in create mode 100644 Engine/lib/libtheora/lib/Makefile.am create mode 100644 Engine/lib/libtheora/lib/Makefile.in create mode 100644 Engine/lib/libtheora/lib/Version_script create mode 100644 Engine/lib/libtheora/lib/Version_script-dec create mode 100644 Engine/lib/libtheora/lib/Version_script-enc create mode 100644 Engine/lib/libtheora/lib/analyze.c rename Engine/lib/libtheora/lib/{dec => }/apiwrapper.c (86%) rename Engine/lib/libtheora/lib/{dec => }/apiwrapper.h (92%) create mode 100644 Engine/lib/libtheora/lib/bitpack.c rename Engine/lib/libtheora/lib/{dec => }/bitpack.h (51%) rename Engine/lib/libtheora/lib/{dec => }/dct.h (90%) delete mode 100644 Engine/lib/libtheora/lib/dec/bitpack.c delete mode 100644 Engine/lib/libtheora/lib/dec/decode.c delete mode 100644 Engine/lib/libtheora/lib/dec/fragment.c delete mode 100644 Engine/lib/libtheora/lib/dec/huffdec.c delete mode 100644 Engine/lib/libtheora/lib/dec/idct.h delete mode 100644 Engine/lib/libtheora/lib/dec/ocintrin.h delete mode 100644 Engine/lib/libtheora/lib/dec/quant.c delete mode 100644 Engine/lib/libtheora/lib/dec/x86/mmxstate.c delete mode 100644 Engine/lib/libtheora/lib/dec/x86/x86int.h delete mode 100644 Engine/lib/libtheora/lib/dec/x86_vc/mmxfrag.c delete mode 100644 Engine/lib/libtheora/lib/dec/x86_vc/mmxidct.c delete mode 100644 Engine/lib/libtheora/lib/dec/x86_vc/mmxloopfilter.c delete mode 100644 Engine/lib/libtheora/lib/dec/x86_vc/mmxstate.c delete mode 100644 Engine/lib/libtheora/lib/dec/x86_vc/x86int.h rename Engine/lib/libtheora/lib/{dec => }/decapiwrapper.c (95%) rename Engine/lib/libtheora/lib/{dec => }/decinfo.c (77%) rename Engine/lib/libtheora/lib/{dec => }/decint.h (68%) create mode 100644 Engine/lib/libtheora/lib/decode.c rename Engine/lib/libtheora/lib/{dec => }/dequant.c (80%) rename Engine/lib/libtheora/lib/{dec => }/dequant.h (82%) delete mode 100644 Engine/lib/libtheora/lib/enc/block_inline.h delete mode 100644 Engine/lib/libtheora/lib/enc/blockmap.c delete mode 100644 Engine/lib/libtheora/lib/enc/codec_internal.h delete mode 100644 Engine/lib/libtheora/lib/enc/dct.c delete mode 100644 Engine/lib/libtheora/lib/enc/dct_decode.c delete mode 100644 Engine/lib/libtheora/lib/enc/dct_encode.c delete mode 100644 Engine/lib/libtheora/lib/enc/dsp.c delete mode 100644 Engine/lib/libtheora/lib/enc/dsp.h delete mode 100644 Engine/lib/libtheora/lib/enc/encode.c delete mode 100644 Engine/lib/libtheora/lib/enc/encoder_huffman.c delete mode 100644 Engine/lib/libtheora/lib/enc/encoder_huffman.h delete mode 100644 Engine/lib/libtheora/lib/enc/encoder_idct.c delete mode 100644 Engine/lib/libtheora/lib/enc/encoder_lookup.h delete mode 100644 Engine/lib/libtheora/lib/enc/encoder_quant.c delete mode 100644 Engine/lib/libtheora/lib/enc/encoder_toplevel.c delete mode 100644 Engine/lib/libtheora/lib/enc/frarray.c delete mode 100644 Engine/lib/libtheora/lib/enc/frinit.c delete mode 100644 Engine/lib/libtheora/lib/enc/hufftables.h delete mode 100644 Engine/lib/libtheora/lib/enc/mcomp.c delete mode 100644 Engine/lib/libtheora/lib/enc/misc_common.c delete mode 100644 Engine/lib/libtheora/lib/enc/pb.c delete mode 100644 Engine/lib/libtheora/lib/enc/pp.c delete mode 100644 Engine/lib/libtheora/lib/enc/pp.h delete mode 100644 Engine/lib/libtheora/lib/enc/quant_lookup.h delete mode 100644 Engine/lib/libtheora/lib/enc/reconstruct.c delete mode 100644 Engine/lib/libtheora/lib/enc/scan.c delete mode 100644 Engine/lib/libtheora/lib/enc/toplevel_lookup.h delete mode 100644 Engine/lib/libtheora/lib/enc/x86_32/dct_decode_mmx.c delete mode 100644 Engine/lib/libtheora/lib/enc/x86_32/dsp_mmx.c delete mode 100644 Engine/lib/libtheora/lib/enc/x86_32/dsp_mmxext.c delete mode 100644 Engine/lib/libtheora/lib/enc/x86_32/fdct_mmx.c delete mode 100644 Engine/lib/libtheora/lib/enc/x86_32/idct_mmx.c delete mode 100644 Engine/lib/libtheora/lib/enc/x86_32/recon_mmx.c delete mode 100644 Engine/lib/libtheora/lib/enc/x86_32_vs/dsp_mmx.c delete mode 100644 Engine/lib/libtheora/lib/enc/x86_32_vs/fdct_mmx.c delete mode 100644 Engine/lib/libtheora/lib/enc/x86_32_vs/recon_mmx.c delete mode 100644 Engine/lib/libtheora/lib/enc/x86_64/dct_decode_mmx.c delete mode 100644 Engine/lib/libtheora/lib/enc/x86_64/dsp_mmx.c delete mode 100644 Engine/lib/libtheora/lib/enc/x86_64/dsp_mmxext.c delete mode 100644 Engine/lib/libtheora/lib/enc/x86_64/fdct_mmx.c delete mode 100644 Engine/lib/libtheora/lib/enc/x86_64/idct_mmx.c delete mode 100644 Engine/lib/libtheora/lib/enc/x86_64/recon_mmx.c create mode 100644 Engine/lib/libtheora/lib/encapiwrapper.c create mode 100644 Engine/lib/libtheora/lib/encfrag.c create mode 100644 Engine/lib/libtheora/lib/encinfo.c create mode 100644 Engine/lib/libtheora/lib/encint.h create mode 100644 Engine/lib/libtheora/lib/encode.c create mode 100644 Engine/lib/libtheora/lib/enquant.c create mode 100644 Engine/lib/libtheora/lib/enquant.h create mode 100644 Engine/lib/libtheora/lib/fdct.c create mode 100644 Engine/lib/libtheora/lib/fragment.c create mode 100644 Engine/lib/libtheora/lib/huffdec.c rename Engine/lib/libtheora/lib/{dec => }/huffdec.h (91%) rename Engine/lib/libtheora/lib/{enc/encapiwrapper.c => huffenc.c} (74%) create mode 100644 Engine/lib/libtheora/lib/huffenc.h rename Engine/lib/libtheora/lib/{dec => }/huffman.h (92%) rename Engine/lib/libtheora/lib/{dec => }/idct.c (70%) rename Engine/lib/libtheora/lib/{dec => }/info.c (83%) rename Engine/lib/libtheora/lib/{dec => }/internal.c (62%) create mode 100644 Engine/lib/libtheora/lib/mathops.c create mode 100644 Engine/lib/libtheora/lib/mathops.h create mode 100644 Engine/lib/libtheora/lib/mcenc.c create mode 100644 Engine/lib/libtheora/lib/modedec.h create mode 100644 Engine/lib/libtheora/lib/ocintrin.h create mode 100644 Engine/lib/libtheora/lib/quant.c rename Engine/lib/libtheora/lib/{dec => }/quant.h (82%) create mode 100644 Engine/lib/libtheora/lib/rate.c rename Engine/lib/libtheora/lib/{dec => }/state.c (55%) create mode 100644 Engine/lib/libtheora/lib/tokenize.c create mode 100644 Engine/lib/libtheora/lib/x86/mmxencfrag.c create mode 100644 Engine/lib/libtheora/lib/x86/mmxfdct.c rename Engine/lib/libtheora/lib/{dec => }/x86/mmxfrag.c (83%) create mode 100644 Engine/lib/libtheora/lib/x86/mmxfrag.h rename Engine/lib/libtheora/lib/{dec => }/x86/mmxidct.c (86%) create mode 100644 Engine/lib/libtheora/lib/x86/mmxloop.h create mode 100644 Engine/lib/libtheora/lib/x86/mmxstate.c create mode 100644 Engine/lib/libtheora/lib/x86/sse2fdct.c create mode 100644 Engine/lib/libtheora/lib/x86/x86enc.c create mode 100644 Engine/lib/libtheora/lib/x86/x86enc.h create mode 100644 Engine/lib/libtheora/lib/x86/x86int.h rename Engine/lib/libtheora/lib/{dec => }/x86/x86state.c (58%) create mode 100644 Engine/lib/libtheora/lib/x86_vc/mmxencfrag.c create mode 100644 Engine/lib/libtheora/lib/x86_vc/mmxfdct.c create mode 100644 Engine/lib/libtheora/lib/x86_vc/mmxfrag.c create mode 100644 Engine/lib/libtheora/lib/x86_vc/mmxfrag.h create mode 100644 Engine/lib/libtheora/lib/x86_vc/mmxidct.c create mode 100644 Engine/lib/libtheora/lib/x86_vc/mmxloop.h create mode 100644 Engine/lib/libtheora/lib/x86_vc/mmxstate.c create mode 100644 Engine/lib/libtheora/lib/x86_vc/x86enc.c create mode 100644 Engine/lib/libtheora/lib/x86_vc/x86enc.h create mode 100644 Engine/lib/libtheora/lib/x86_vc/x86int.h rename Engine/lib/libtheora/lib/{dec => }/x86_vc/x86state.c (53%) diff --git a/Engine/lib/libtheora/CHANGES b/Engine/lib/libtheora/CHANGES index 74183d91b..b30327e63 100644 --- a/Engine/lib/libtheora/CHANGES +++ b/Engine/lib/libtheora/CHANGES @@ -1,3 +1,65 @@ +libtheora 1.1.1 (2009 October 1) + + - Fix problems with MSVC inline assembly + - Add the missing encoder_disabled.c to the distribution + - build updates: autogen.sh should work better after switching systems + and the MSVC project now defaults to the dynamic runtime library + - Namespace some variables to avoid conflicts on wince. + +libtheora 1.1.0 (2009 September 24) + + - Fix various small issues with the example and telemetry code + - Fix handing a zero-byte packet as the first frame + - Documentation cleanup + - Two minor build fixes + +libtheora 1.1beta3 (2009 August 22) + + - Rate control fixes to smooth quality + - MSVC build now exports all of the 1.0 api + - Assorted small bug fixes + +libtheora 1.1beta2 (2009 August 12) + + - Fix a rate control problem with difficult input + - Build fixes for OpenBSD and Apple Xcode + - Examples now all use the 1.0 api + - TH_ENCCTL_SET_SPLEVEL works again + - Various bug fixes and source tree rearrangement + +libtheora 1.1beta1 (2009 August 5) + + - Support for two-pass encoding + - Performance optimization of both encoder and decoder + - Encoder supports dynamic adjustment of quality and + bitrate targets + - Encoder is generally more configurable, and all + rate control modes perform better + - Encoder now accepts 4:2:2 and 4:4:4 chroma sampling + - Decoder telemetry output shows quantization choice + and a breakdown of bitrate usage in the frame + - MSVC assembly optimizations up to date and functional + +libtheora 1.1alpha2 (2009 May 26) + + - Reduce lambda for small quantizers. + - New encoder fDCT does better on smooth gradients + - Use SATD for mode decisions (1-2% bitrate reduction) + - Assembly rewrite for new features and general speed up + - Share code between the encoder and decoder for performance + - Fix 4:2:2 decoding and telemetry + - MSVC project files updated, but assembly is disabled. + - New configure option --disable-spec to work around toolchain + detection failures. + - Limit symbol exports on MacOS X. + - Port remaining unit tests from the 1.0 release. + +libtheora 1.1alpha1 (2009 March 27) + + - Encoder rewrite with much improved vbr quality/bitrate and + better tracking of the target rate in cbr mode. + - MSVC project files do not work in this release. + libtheora 1.0 (2008 November 3) - Merge x86 assembly for forward DCT from Thusnelda branch. diff --git a/Engine/lib/libtheora/COPYING b/Engine/lib/libtheora/COPYING index 5a711972d..c8ccce4ff 100644 --- a/Engine/lib/libtheora/COPYING +++ b/Engine/lib/libtheora/COPYING @@ -1,4 +1,4 @@ -Copyright (C) 2002-2008 Xiph.Org Foundation and contributors. +Copyright (C) 2002-2009 Xiph.org Foundation Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions @@ -11,7 +11,7 @@ notice, this list of conditions and the following disclaimer. notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution. -- Neither the name of the Xiph.Org Foundation nor the names of its +- Neither the name of the Xiph.org Foundation nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission. diff --git a/Engine/lib/libtheora/LICENSE b/Engine/lib/libtheora/LICENSE new file mode 100644 index 000000000..5e5ec0846 --- /dev/null +++ b/Engine/lib/libtheora/LICENSE @@ -0,0 +1,18 @@ +Please see the file COPYING for the copyright license for this software. + +In addition to and irrespective of the copyright license associated +with this software, On2 Technologies, Inc. makes the following statement +regarding technology used in this software: + + On2 represents and warrants that it shall not assert any rights + relating to infringement of On2's registered patents, nor initiate + any litigation asserting such rights, against any person who, or + entity which utilizes the On2 VP3 Codec Software, including any + use, distribution, and sale of said Software; which make changes, + modifications, and improvements in said Software; and to use, + distribute, and sell said changes as well as applications for other + fields of use. + +This reference implementation is originally derived from the On2 VP3 +Codec Software, and the Theora video format is essentially compatible +with the VP3 video format, consisting of a backward-compatible superset. diff --git a/Engine/lib/libtheora/include/Makefile.am b/Engine/lib/libtheora/include/Makefile.am new file mode 100644 index 000000000..d5db4b40f --- /dev/null +++ b/Engine/lib/libtheora/include/Makefile.am @@ -0,0 +1,3 @@ +## Process this file with automake to produce Makefile.in + +SUBDIRS = theora diff --git a/Engine/lib/libtheora/include/Makefile.in b/Engine/lib/libtheora/include/Makefile.in new file mode 100644 index 000000000..805e6c29e --- /dev/null +++ b/Engine/lib/libtheora/include/Makefile.in @@ -0,0 +1,414 @@ +# Makefile.in generated by automake 1.6.3 from Makefile.am. +# @configure_input@ + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ + +EXEEXT = @EXEEXT@ +OBJEXT = @OBJEXT@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMTAR = @AMTAR@ +AR = @AR@ +ARGZ_H = @ARGZ_H@ +AS = @AS@ +AWK = @AWK@ +BUILDABLE_EXAMPLES = @BUILDABLE_EXAMPLES@ +CAIRO_CFLAGS = @CAIRO_CFLAGS@ +CAIRO_LIBS = @CAIRO_LIBS@ +CC = @CC@ +CPP = @CPP@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +DEBUG = @DEBUG@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +F77 = @F77@ +GCJ = @GCJ@ +GCJFLAGS = @GCJFLAGS@ +GETOPT_OBJS = @GETOPT_OBJS@ +GREP = @GREP@ +HAVE_BIBTEX = @HAVE_BIBTEX@ +HAVE_DOXYGEN = @HAVE_DOXYGEN@ +HAVE_PDFLATEX = @HAVE_PDFLATEX@ +HAVE_PKG_CONFIG = @HAVE_PKG_CONFIG@ +HAVE_TRANSFIG = @HAVE_TRANSFIG@ +HAVE_VALGRIND = @HAVE_VALGRIND@ +INCLTDL = @INCLTDL@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LIBADD_DL = @LIBADD_DL@ +LIBADD_DLD_LINK = @LIBADD_DLD_LINK@ +LIBADD_DLOPEN = @LIBADD_DLOPEN@ +LIBADD_SHL_LOAD = @LIBADD_SHL_LOAD@ +LIBLTDL = @LIBLTDL@ +LIBM = @LIBM@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTDLDEPS = @LTDLDEPS@ +LTDLINCL = @LTDLINCL@ +LTDLOPEN = @LTDLOPEN@ +LT_CONFIG_H = @LT_CONFIG_H@ +LT_DLLOADERS = @LT_DLLOADERS@ +LT_DLPREOPEN = @LT_DLPREOPEN@ +MAINT = @MAINT@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OGG_CFLAGS = @OGG_CFLAGS@ +OGG_LIBS = @OGG_LIBS@ +OSS_LIBS = @OSS_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PKG_CONFIG = @PKG_CONFIG@ +PNG_CFLAGS = @PNG_CFLAGS@ +PNG_LIBS = @PNG_LIBS@ +PROFILE = @PROFILE@ +RANLIB = @RANLIB@ +RC = @RC@ +SDL_CFLAGS = @SDL_CFLAGS@ +SDL_CONFIG = @SDL_CONFIG@ +SDL_LIBS = @SDL_LIBS@ +SED = @SED@ +STRIP = @STRIP@ +THDEC_LIB_AGE = @THDEC_LIB_AGE@ +THDEC_LIB_CURRENT = @THDEC_LIB_CURRENT@ +THDEC_LIB_REVISION = @THDEC_LIB_REVISION@ +THENC_LIB_AGE = @THENC_LIB_AGE@ +THENC_LIB_CURRENT = @THENC_LIB_CURRENT@ +THENC_LIB_REVISION = @THENC_LIB_REVISION@ +THEORADEC_LDFLAGS = @THEORADEC_LDFLAGS@ +THEORAENC_LDFLAGS = @THEORAENC_LDFLAGS@ +THEORA_LDFLAGS = @THEORA_LDFLAGS@ +TH_LIB_AGE = @TH_LIB_AGE@ +TH_LIB_CURRENT = @TH_LIB_CURRENT@ +TH_LIB_REVISION = @TH_LIB_REVISION@ +VALGRIND_ENVIRONMENT = @VALGRIND_ENVIRONMENT@ +VERSION = @VERSION@ +VORBISENC_LIBS = @VORBISENC_LIBS@ +VORBISFILE_LIBS = @VORBISFILE_LIBS@ +VORBIS_CFLAGS = @VORBIS_CFLAGS@ +VORBIS_LIBS = @VORBIS_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ +lt_ECHO = @lt_ECHO@ +ltdl_LIBOBJS = @ltdl_LIBOBJS@ +ltdl_LTLIBOBJS = @ltdl_LTLIBOBJS@ +sys_symbol_underscore = @sys_symbol_underscore@ + +SUBDIRS = theora +subdir = include +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +DIST_SOURCES = + +RECURSIVE_TARGETS = info-recursive dvi-recursive install-info-recursive \ + uninstall-info-recursive all-recursive install-data-recursive \ + install-exec-recursive installdirs-recursive install-recursive \ + uninstall-recursive check-recursive installcheck-recursive +DIST_COMMON = Makefile.am Makefile.in +DIST_SUBDIRS = $(SUBDIRS) +all: all-recursive + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.ac $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu include/Makefile +Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +# This directory's subdirectories are mostly independent; you can cd +# into them and run `make' without going through this Makefile. +# To change the values of `make' variables: instead of editing Makefiles, +# (1) if the variable is set in `config.status', edit `config.status' +# (which will cause the Makefiles to be regenerated when you run `make'); +# (2) otherwise, pass the desired values on the `make' command line. +$(RECURSIVE_TARGETS): + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + target=`echo $@ | sed s/-recursive//`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + dot_seen=yes; \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done; \ + if test "$$dot_seen" = "no"; then \ + $(MAKE) $(AM_MAKEFLAGS) "$$target-am" || exit 1; \ + fi; test -z "$$fail" + +mostlyclean-recursive clean-recursive distclean-recursive \ +maintainer-clean-recursive: + @set fnord $$MAKEFLAGS; amf=$$2; \ + dot_seen=no; \ + case "$@" in \ + distclean-* | maintainer-clean-*) list='$(DIST_SUBDIRS)' ;; \ + *) list='$(SUBDIRS)' ;; \ + esac; \ + rev=''; for subdir in $$list; do \ + if test "$$subdir" = "."; then :; else \ + rev="$$subdir $$rev"; \ + fi; \ + done; \ + rev="$$rev ."; \ + target=`echo $@ | sed s/-recursive//`; \ + for subdir in $$rev; do \ + echo "Making $$target in $$subdir"; \ + if test "$$subdir" = "."; then \ + local_target="$$target-am"; \ + else \ + local_target="$$target"; \ + fi; \ + (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) $$local_target) \ + || case "$$amf" in *=*) exit 1;; *k*) fail=yes;; *) exit 1;; esac; \ + done && test -z "$$fail" +tags-recursive: + list='$(SUBDIRS)'; for subdir in $$list; do \ + test "$$subdir" = . || (cd $$subdir && $(MAKE) $(AM_MAKEFLAGS) tags); \ + done + +ETAGS = etags +ETAGSFLAGS = + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique + +TAGS: tags-recursive $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -f $$subdir/TAGS && tags="$$tags -i $$here/$$subdir/TAGS"; \ + fi; \ + done; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$tags$$unique" \ + || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @list='$(DISTFILES)'; for file in $$list; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkinstalldirs) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done + list='$(SUBDIRS)'; for subdir in $$list; do \ + if test "$$subdir" = .; then :; else \ + test -d $(distdir)/$$subdir \ + || mkdir $(distdir)/$$subdir \ + || exit 1; \ + (cd $$subdir && \ + $(MAKE) $(AM_MAKEFLAGS) \ + top_distdir="$(top_distdir)" \ + distdir=../$(distdir)/$$subdir \ + distdir) \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-recursive +all-am: Makefile +installdirs: installdirs-recursive +installdirs-am: + +install: install-recursive +install-exec: install-exec-recursive +install-data: install-data-recursive +uninstall: uninstall-recursive + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-recursive +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-recursive + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-recursive + +distclean-am: clean-am distclean-generic distclean-libtool \ + distclean-tags + +dvi: dvi-recursive + +dvi-am: + +info: info-recursive + +info-am: + +install-data-am: + +install-exec-am: + +install-info: install-info-recursive + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-recursive + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-recursive + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +uninstall-am: uninstall-info-am + +uninstall-info: uninstall-info-recursive + +.PHONY: $(RECURSIVE_TARGETS) GTAGS all all-am check check-am clean \ + clean-generic clean-libtool clean-recursive distclean \ + distclean-generic distclean-libtool distclean-recursive \ + distclean-tags distdir dvi dvi-am dvi-recursive info info-am \ + info-recursive install install-am install-data install-data-am \ + install-data-recursive install-exec install-exec-am \ + install-exec-recursive install-info install-info-am \ + install-info-recursive install-man install-recursive \ + install-strip installcheck installcheck-am installdirs \ + installdirs-am installdirs-recursive maintainer-clean \ + maintainer-clean-generic maintainer-clean-recursive mostlyclean \ + mostlyclean-generic mostlyclean-libtool mostlyclean-recursive \ + tags tags-recursive uninstall uninstall-am uninstall-info-am \ + uninstall-info-recursive uninstall-recursive + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Engine/lib/libtheora/include/theora/Makefile.am b/Engine/lib/libtheora/include/theora/Makefile.am new file mode 100644 index 000000000..5479e82a5 --- /dev/null +++ b/Engine/lib/libtheora/include/theora/Makefile.am @@ -0,0 +1,7 @@ +## Process this file with automake to produce Makefile.in + +theoraincludedir = $(includedir)/theora + +theorainclude_HEADERS = theora.h theoradec.h theoraenc.h codec.h + +noinst_HEADERS = codec.h theoradec.h diff --git a/Engine/lib/libtheora/include/theora/Makefile.in b/Engine/lib/libtheora/include/theora/Makefile.in new file mode 100644 index 000000000..d20e60ab4 --- /dev/null +++ b/Engine/lib/libtheora/include/theora/Makefile.in @@ -0,0 +1,355 @@ +# Makefile.in generated by automake 1.6.3 from Makefile.am. +# @configure_input@ + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = ../.. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ + +EXEEXT = @EXEEXT@ +OBJEXT = @OBJEXT@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMTAR = @AMTAR@ +AR = @AR@ +ARGZ_H = @ARGZ_H@ +AS = @AS@ +AWK = @AWK@ +BUILDABLE_EXAMPLES = @BUILDABLE_EXAMPLES@ +CAIRO_CFLAGS = @CAIRO_CFLAGS@ +CAIRO_LIBS = @CAIRO_LIBS@ +CC = @CC@ +CPP = @CPP@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +DEBUG = @DEBUG@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +F77 = @F77@ +GCJ = @GCJ@ +GCJFLAGS = @GCJFLAGS@ +GETOPT_OBJS = @GETOPT_OBJS@ +GREP = @GREP@ +HAVE_BIBTEX = @HAVE_BIBTEX@ +HAVE_DOXYGEN = @HAVE_DOXYGEN@ +HAVE_PDFLATEX = @HAVE_PDFLATEX@ +HAVE_PKG_CONFIG = @HAVE_PKG_CONFIG@ +HAVE_TRANSFIG = @HAVE_TRANSFIG@ +HAVE_VALGRIND = @HAVE_VALGRIND@ +INCLTDL = @INCLTDL@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LIBADD_DL = @LIBADD_DL@ +LIBADD_DLD_LINK = @LIBADD_DLD_LINK@ +LIBADD_DLOPEN = @LIBADD_DLOPEN@ +LIBADD_SHL_LOAD = @LIBADD_SHL_LOAD@ +LIBLTDL = @LIBLTDL@ +LIBM = @LIBM@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTDLDEPS = @LTDLDEPS@ +LTDLINCL = @LTDLINCL@ +LTDLOPEN = @LTDLOPEN@ +LT_CONFIG_H = @LT_CONFIG_H@ +LT_DLLOADERS = @LT_DLLOADERS@ +LT_DLPREOPEN = @LT_DLPREOPEN@ +MAINT = @MAINT@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OGG_CFLAGS = @OGG_CFLAGS@ +OGG_LIBS = @OGG_LIBS@ +OSS_LIBS = @OSS_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PKG_CONFIG = @PKG_CONFIG@ +PNG_CFLAGS = @PNG_CFLAGS@ +PNG_LIBS = @PNG_LIBS@ +PROFILE = @PROFILE@ +RANLIB = @RANLIB@ +RC = @RC@ +SDL_CFLAGS = @SDL_CFLAGS@ +SDL_CONFIG = @SDL_CONFIG@ +SDL_LIBS = @SDL_LIBS@ +SED = @SED@ +STRIP = @STRIP@ +THDEC_LIB_AGE = @THDEC_LIB_AGE@ +THDEC_LIB_CURRENT = @THDEC_LIB_CURRENT@ +THDEC_LIB_REVISION = @THDEC_LIB_REVISION@ +THENC_LIB_AGE = @THENC_LIB_AGE@ +THENC_LIB_CURRENT = @THENC_LIB_CURRENT@ +THENC_LIB_REVISION = @THENC_LIB_REVISION@ +THEORADEC_LDFLAGS = @THEORADEC_LDFLAGS@ +THEORAENC_LDFLAGS = @THEORAENC_LDFLAGS@ +THEORA_LDFLAGS = @THEORA_LDFLAGS@ +TH_LIB_AGE = @TH_LIB_AGE@ +TH_LIB_CURRENT = @TH_LIB_CURRENT@ +TH_LIB_REVISION = @TH_LIB_REVISION@ +VALGRIND_ENVIRONMENT = @VALGRIND_ENVIRONMENT@ +VERSION = @VERSION@ +VORBISENC_LIBS = @VORBISENC_LIBS@ +VORBISFILE_LIBS = @VORBISFILE_LIBS@ +VORBIS_CFLAGS = @VORBIS_CFLAGS@ +VORBIS_LIBS = @VORBIS_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ +lt_ECHO = @lt_ECHO@ +ltdl_LIBOBJS = @ltdl_LIBOBJS@ +ltdl_LTLIBOBJS = @ltdl_LTLIBOBJS@ +sys_symbol_underscore = @sys_symbol_underscore@ + +theoraincludedir = $(includedir)/theora + +theorainclude_HEADERS = theora.h theoradec.h theoraenc.h codec.h + +noinst_HEADERS = codec.h theoradec.h +subdir = include/theora +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +DIST_SOURCES = +HEADERS = $(noinst_HEADERS) $(theorainclude_HEADERS) + +DIST_COMMON = $(noinst_HEADERS) $(theorainclude_HEADERS) Makefile.am \ + Makefile.in +all: all-am + +.SUFFIXES: +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.ac $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu include/theora/Makefile +Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: +theoraincludeHEADERS_INSTALL = $(INSTALL_HEADER) +install-theoraincludeHEADERS: $(theorainclude_HEADERS) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(theoraincludedir) + @list='$(theorainclude_HEADERS)'; for p in $$list; do \ + if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(theoraincludeHEADERS_INSTALL) $$d$$p $(DESTDIR)$(theoraincludedir)/$$f"; \ + $(theoraincludeHEADERS_INSTALL) $$d$$p $(DESTDIR)$(theoraincludedir)/$$f; \ + done + +uninstall-theoraincludeHEADERS: + @$(NORMAL_UNINSTALL) + @list='$(theorainclude_HEADERS)'; for p in $$list; do \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " rm -f $(DESTDIR)$(theoraincludedir)/$$f"; \ + rm -f $(DESTDIR)$(theoraincludedir)/$$f; \ + done + +ETAGS = etags +ETAGSFLAGS = + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$tags$$unique" \ + || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = ../.. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + @list='$(DISTFILES)'; for file in $$list; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkinstalldirs) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(HEADERS) + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(theoraincludedir) + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libtool mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-generic distclean-libtool \ + distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: install-theoraincludeHEADERS + +install-exec-am: + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-generic mostlyclean-libtool + +uninstall-am: uninstall-info-am uninstall-theoraincludeHEADERS + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-libtool distclean distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am info info-am install \ + install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am install-man \ + install-strip install-theoraincludeHEADERS installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-generic \ + mostlyclean-libtool tags uninstall uninstall-am \ + uninstall-info-am uninstall-theoraincludeHEADERS + +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Engine/lib/libtheora/include/theora/codec.h b/Engine/lib/libtheora/include/theora/codec.h index afdc1b0fa..5c2669630 100644 --- a/Engine/lib/libtheora/include/theora/codec.h +++ b/Engine/lib/libtheora/include/theora/codec.h @@ -5,7 +5,7 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation http://www.xiph.org/ * * * ******************************************************************** @@ -24,10 +24,10 @@ * implementation for Theora, a free, * patent-unencumbered video codec. * Theora is derived from On2's VP3 codec with additional features and - * integration for Ogg multimedia formats by + * integration with Ogg multimedia formats by * the Xiph.Org Foundation. * Complete documentation of the format itself is available in - * the Theora + * the Theora * specification. * * \subsection Organization @@ -92,9 +92,9 @@ extern "C" { /*@}*/ /**The currently defined color space tags. - * See the Theora - * specification, Chapter 4, for exact details on the meaning of each of - * these color spaces.*/ + * See the Theora + * specification, Chapter 4, for exact details on the meaning + * of each of these color spaces.*/ typedef enum{ /**The color space was not specified at the encoder. It may be conveyed by an external means.*/ @@ -108,13 +108,13 @@ typedef enum{ }th_colorspace; /**The currently defined pixel format tags. - * See the Theora + * See the Theora * specification, Section 4.4, for details on the precise sample * locations.*/ typedef enum{ /**Chroma decimation by 2 in both the X and Y directions (4:2:0). - The Cb and Cr chroma planes are half the width and half the height of the - luma plane.*/ + The Cb and Cr chroma planes are half the width and half the + height of the luma plane.*/ TH_PF_420, /**Currently reserved.*/ TH_PF_RSVD, @@ -133,11 +133,11 @@ typedef enum{ /**A buffer for a single color plane in an uncompressed image. * This contains the image data in a left-to-right, top-down format. - * Each row of pixels is stored contiguously in memory, but successive rows - * need not be. + * Each row of pixels is stored contiguously in memory, but successive + * rows need not be. * Use \a stride to compute the offset of the next row. - * The encoder accepts both positive \a stride values (top-down in memory) and - * negative (bottom-up in memory). + * The encoder accepts both positive \a stride values (top-down in memory) + * and negative (bottom-up in memory). * The decoder currently always generates images with positive strides.*/ typedef struct{ /**The width of this plane.*/ @@ -151,18 +151,18 @@ typedef struct{ }th_img_plane; /**A complete image buffer for an uncompressed frame. - * The chroma planes may be decimated by a factor of two in either direction, - * as indicated by th_info#pixel_fmt. + * The chroma planes may be decimated by a factor of two in either + * direction, as indicated by th_info#pixel_fmt. * The width and height of the Y' plane must be multiples of 16. - * They may need to be cropped for display, using the rectangle specified by - * th_info#pic_x, th_info#pic_y, th_info#pic_width, and - * th_info#pic_height. + * They may need to be cropped for display, using the rectangle + * specified by th_info#pic_x, th_info#pic_y, th_info#pic_width, + * and th_info#pic_height. * All samples are 8 bits. * \note The term YUV often used to describe a colorspace is ambiguous. - * The exact parameters of the RGB to YUV conversion process aside, in many - * contexts the U and V channels actually have opposite meanings. - * To avoid this confusion, we are explicit: the name of the color channels are - * Y'CbCr, and they appear in that order, always. + * The exact parameters of the RGB to YUV conversion process aside, in + * many contexts the U and V channels actually have opposite meanings. + * To avoid this confusion, we are explicit: the name of the color + * channels are Y'CbCr, and they appear in that order, always. * The prime symbol denotes that the Y channel is non-linear. * Cb and Cr stand for "Chroma blue" and "Chroma red", respectively.*/ typedef th_img_plane th_ycbcr_buffer[3]; @@ -192,7 +192,7 @@ typedef th_img_plane th_ycbcr_buffer[3]; * * It is also generally recommended that the offsets and sizes should still be * multiples of 2 to avoid chroma sampling shifts when chroma is sub-sampled. - * See the Theora + * See the Theora * specification, Section 4.4, for more details. * * Frame rate, in frames per second, is stored as a rational fraction, as is @@ -230,8 +230,8 @@ typedef struct{ * #frame_height-#pic_height-#pic_y must be no larger than 255. * This slightly funny restriction is due to the fact that the offset is * specified from the top of the image for consistency with the standard - * graphics left-handed coordinate system used throughout this API, while it - * is stored in the encoded stream as an offset from the bottom.*/ + * graphics left-handed coordinate system used throughout this API, while + * it is stored in the encoded stream as an offset from the bottom.*/ ogg_uint32_t pic_y; /**\name Frame rate * The frame rate, as a fraction. @@ -259,9 +259,6 @@ typedef struct{ /**The target bit-rate in bits per second. If initializing an encoder with this struct, set this field to a non-zero value to activate CBR encoding by default.*/ - /*TODO: Current encoder does not support CBR mode, or anything like it. - We also don't really know what nominal rate each quality level - corresponds to yet.*/ int target_bitrate; /**The target quality level. Valid values range from 0 to 63, inclusive, with higher values giving @@ -314,7 +311,7 @@ typedef struct{ * A particular tag may occur more than once, and order is significant. * The character set encoding for the strings is always UTF-8, but the tag * names are limited to ASCII, and treated as case-insensitive. - * See the Theora + * See the Theora * specification, Section 6.3.3 for details. * * In filling in this structure, th_decode_headerin() will null-terminate diff --git a/Engine/lib/libtheora/include/theora/theora.h b/Engine/lib/libtheora/include/theora/theora.h index dbef71675..af6eb6f38 100644 --- a/Engine/lib/libtheora/include/theora/theora.h +++ b/Engine/lib/libtheora/include/theora/theora.h @@ -5,7 +5,7 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation http://www.xiph.org/ * * * ******************************************************************** @@ -27,11 +27,11 @@ extern "C" #include -/** \defgroup oldfuncs Legacy pre-1.0 C API */ -/* @{ */ - -/** \mainpage - * +/** \file + * The libtheora pre-1.0 legacy C API. + * + * \ingroup oldfuncs + * * \section intro Introduction * * This is the documentation for the libtheora legacy C API, declared in @@ -42,7 +42,7 @@ extern "C" * * libtheora is the reference implementation for * Theora, a free video codec. - * Theora is derived from On2's VP3 codec with improved integration for + * Theora is derived from On2's VP3 codec with improved integration with * Ogg multimedia formats by Xiph.Org. * * \section overview Overview @@ -114,21 +114,11 @@ extern "C" * checking beyond whether a header bit is present. Instead, use the * theora_decode_header() function and check the return value; or examine the * header bytes at the beginning of the Ogg page. - * - * \subsection example Example Decoder - * - * See - * examples/dump_video.c for a simple decoder implementation. - * - * \section encoding Encoding Process - * - * See - * examples/encoder_example.c for a simple encoder implementation. */ -/** \file - * The libtheora pre-1.0 legacy C API. - */ + +/** \defgroup oldfuncs Legacy pre-1.0 C API */ +/* @{ */ /** * A YUV buffer for passing uncompressed frames to and from the codec. @@ -292,14 +282,21 @@ typedef struct theora_comment{ /**\name theora_control() codes */ - -/**\anchor decctlcodes +/* \anchor decctlcodes_old * These are the available request codes for theora_control() * when called with a decoder instance. - * By convention, these are odd, to distinguish them from the - * \ref encctlcodes "encoder control codes". + * By convention decoder control codes are odd, to distinguish + * them from \ref encctlcodes_old "encoder control codes" which + * are even. + * + * Note that since the 1.0 release, both the legacy and the final + * implementation accept all the same control codes, but only the + * final API declares the newer codes. + * * Keep any experimental or vendor-specific values above \c 0x8000.*/ +/*@{*/ + /**Get the maximum post-processing level. * The decoder supports a post-processing filter that can improve * the appearance of the decoded images. This returns the highest @@ -324,9 +321,9 @@ typedef struct theora_comment{ * \param[in] buf ogg_uint32_t: The maximum distance between key * frames. * \param[out] buf ogg_uint32_t: The actual maximum distance set. - * \retval TH_FAULT \a theora_state or \a buf is NULL. - * \retval TH_EINVAL \a buf_sz is not sizeof(ogg_uint32_t). - * \retval TH_IMPL Not supported by this implementation.*/ + * \retval OC_FAULT \a theora_state or \a buf is NULL. + * \retval OC_EINVAL \a buf_sz is not sizeof(ogg_uint32_t). + * \retval OC_IMPL Not supported by this implementation.*/ #define TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE (4) /**Set the granule position. @@ -338,33 +335,23 @@ typedef struct theora_comment{ */ #define TH_DECCTL_SET_GRANPOS (5) +/**\anchor encctlcodes_old */ -/**\anchor encctlcodes - * These are the available request codes for theora_control() - * when called with an encoder instance. - * By convention, these are even, to distinguish them from the - * \ref decctlcodes "decoder control codes". - * Keep any experimental or vendor-specific values above \c 0x8000.*/ -/*@{*/ /**Sets the quantization parameters to use. * The parameters are copied, not stored by reference, so they can be freed * after this call. * NULL may be specified to revert to the default parameters. - * For the current encoder, scale[ci!=0][qi] must be no greater than - * scale[ci!=0][qi-1] and base[qti][pli][qi][ci] must be no - * greater than base[qti][pli][qi-1][ci]. - * These two conditions ensure that the actual quantizer for a given \a qti, - * \a pli, and \a ci does not increase as \a qi increases. * * \param[in] buf #th_quant_info - * \retval TH_FAULT \a theora_state is NULL. - * \retval TH_EINVAL Encoding has already begun, the quantization parameters - * do not meet one of the above stated conditions, \a buf - * is NULL and \a buf_sz is not zero, or \a buf - * is non-NULL and \a buf_sz is not - * sizeof(#th_quant_info). - * \retval TH_IMPL Not supported by this implementation.*/ + * \retval OC_FAULT \a theora_state is NULL. + * \retval OC_EINVAL Encoding has already begun, the quantization parameters + * are not acceptable to this version of the encoder, + * \a buf is NULL and \a buf_sz is not zero, + * or \a buf is non-NULL and \a buf_sz is + * not sizeof(#th_quant_info). + * \retval OC_IMPL Not supported by this implementation.*/ #define TH_ENCCTL_SET_QUANT_PARAMS (2) + /**Disables any encoder features that would prevent lossless transcoding back * to VP3. * This primarily means disabling block-level QI values and not using 4MV mode @@ -389,10 +376,11 @@ typedef struct theora_comment{ * 4:2:0, the picture region is smaller than the full frame, * or if encoding has begun, preventing the quantization * tables and codebooks from being set. - * \retval TH_FAULT \a theora_state or \a buf is NULL. - * \retval TH_EINVAL \a buf_sz is not sizeof(int). - * \retval TH_IMPL Not supported by this implementation.*/ + * \retval OC_FAULT \a theora_state or \a buf is NULL. + * \retval OC_EINVAL \a buf_sz is not sizeof(int). + * \retval OC_IMPL Not supported by this implementation.*/ #define TH_ENCCTL_SET_VP3_COMPATIBLE (10) + /**Gets the maximum speed level. * Higher speed levels favor quicker encoding over better quality per bit. * Depending on the encoding mode, and the internal algorithms used, quality @@ -402,25 +390,27 @@ typedef struct theora_comment{ * the current encoding mode (VBR vs. CQI, etc.). * * \param[out] buf int: The maximum encoding speed level. - * \retval TH_FAULT \a theora_state or \a buf is NULL. - * \retval TH_EINVAL \a buf_sz is not sizeof(int). - * \retval TH_IMPL Not supported by this implementation in the current + * \retval OC_FAULT \a theora_state or \a buf is NULL. + * \retval OC_EINVAL \a buf_sz is not sizeof(int). + * \retval OC_IMPL Not supported by this implementation in the current * encoding mode.*/ #define TH_ENCCTL_GET_SPLEVEL_MAX (12) + /**Sets the speed level. * By default a speed value of 1 is used. * * \param[in] buf int: The new encoding speed level. * 0 is slowest, larger values use less CPU. - * \retval TH_FAULT \a theora_state or \a buf is NULL. - * \retval TH_EINVAL \a buf_sz is not sizeof(int), or the + * \retval OC_FAULT \a theora_state or \a buf is NULL. + * \retval OC_EINVAL \a buf_sz is not sizeof(int), or the * encoding speed level is out of bounds. * The maximum encoding speed level may be * implementation- and encoding mode-specific, and can be * obtained via #TH_ENCCTL_GET_SPLEVEL_MAX. - * \retval TH_IMPL Not supported by this implementation in the current + * \retval OC_IMPL Not supported by this implementation in the current * encoding mode.*/ #define TH_ENCCTL_SET_SPLEVEL (14) + /*@}*/ #define OC_FAULT -1 /**< General failure */ @@ -779,8 +769,8 @@ extern void theora_comment_clear(theora_comment *tc); * This is used to provide advanced control the encoding process. * \param th A #theora_state handle. * \param req The control code to process. - * See \ref encctlcodes "the list of available control codes" - * for details. + * See \ref encctlcodes_old "the list of available + * control codes" for details. * \param buf The parameters for this control code. * \param buf_sz The size of the parameter buffer.*/ extern int theora_control(theora_state *th,int req,void *buf,size_t buf_sz); diff --git a/Engine/lib/libtheora/include/theora/theoradec.h b/Engine/lib/libtheora/include/theora/theoradec.h index 7c08caadf..b20f0e3a6 100644 --- a/Engine/lib/libtheora/include/theora/theoradec.h +++ b/Engine/lib/libtheora/include/theora/theoradec.h @@ -5,7 +5,7 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation http://www.xiph.org/ * * * ******************************************************************** @@ -38,6 +38,10 @@ extern "C" { * Keep any experimental or vendor-specific values above \c 0x8000.*/ /*@{*/ /**Gets the maximum post-processing level. + * The decoder supports a post-processing filter that can improve + * the appearance of the decoded images. This returns the highest + * level setting for this post-processor, corresponding to maximum + * improvement and computational expense. * * \param[out] _buf int: The maximum post-processing level. * \retval TH_EFAULT \a _dec_ctx or \a _buf is NULL. @@ -47,6 +51,10 @@ extern "C" { /**Sets the post-processing level. * By default, post-processing is disabled. * + * Sets the level of post-processing to use when decoding the + * compressed stream. This must be a value between zero (off) + * and the maximum returned by TH_DECCTL_GET_PPLEVEL_MAX. + * * \param[in] _buf int: The new post-processing level. * 0 to disable; larger values use more CPU. * \retval TH_EFAULT \a _dec_ctx or \a _buf is NULL. @@ -83,6 +91,15 @@ extern "C" { * \retval TH_EINVAL \a _buf_sz is not * sizeof(th_stripe_callback).*/ #define TH_DECCTL_SET_STRIPE_CB (7) + +/**Enables telemetry and sets the macroblock display mode */ +#define TH_DECCTL_SET_TELEMETRY_MBMODE (9) +/**Enables telemetry and sets the motion vector display mode */ +#define TH_DECCTL_SET_TELEMETRY_MV (11) +/**Enables telemetry and sets the adaptive quantization display mode */ +#define TH_DECCTL_SET_TELEMETRY_QI (13) +/**Enables telemetry and sets the bitstream breakdown visualization mode */ +#define TH_DECCTL_SET_TELEMETRY_BITS (15) /*@}*/ @@ -289,6 +306,7 @@ extern int th_decode_packetin(th_dec_ctx *_dec,const ogg_packet *_op, * It may be freed or overwritten without notification when * subsequent frames are decoded. * \retval 0 Success + * \retval TH_EFAULT \a _dec or \a _ycbcr was NULL. */ extern int th_decode_ycbcr_out(th_dec_ctx *_dec, th_ycbcr_buffer _ycbcr); diff --git a/Engine/lib/libtheora/include/theora/theoraenc.h b/Engine/lib/libtheora/include/theora/theoraenc.h index b98285862..fdf2ab21e 100644 --- a/Engine/lib/libtheora/include/theora/theoraenc.h +++ b/Engine/lib/libtheora/include/theora/theoraenc.h @@ -5,7 +5,7 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2003 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation http://www.xiph.org/ * * * ******************************************************************** @@ -49,26 +49,20 @@ extern "C" { * NULL and \a _buf_sz is not zero, or \a _buf is * non-NULL and \a _buf_sz is not * sizeof(#th_huff_code)*#TH_NHUFFMAN_TABLES*#TH_NDCT_TOKENS. - * \retval TH_IMPL Not supported by this implementation.*/ + * \retval TH_EIMPL Not supported by this implementation.*/ #define TH_ENCCTL_SET_HUFFMAN_CODES (0) /**Sets the quantization parameters to use. * The parameters are copied, not stored by reference, so they can be freed * after this call. * NULL may be specified to revert to the default parameters. - * For the current encoder, scale[ci!=0][qi] must be no greater than - * scale[ci!=0][qi-1] and base[qti][pli][qi][ci] must be no - * greater than base[qti][pli][qi-1][ci]. - * These two conditions ensure that the actual quantizer for a given \a qti, - * \a pli, and \a ci does not increase as \a qi increases. * * \param[in] _buf #th_quant_info * \retval TH_EFAULT \a _enc_ctx is NULL. - * \retval TH_EINVAL Encoding has already begun, the quantization parameters - * do not meet one of the above stated conditions, \a _buf - * is NULL and \a _buf_sz is not zero, or \a _buf - * is non-NULL and \a _buf_sz is not - * sizeof(#th_quant_info). - * \retval TH_IMPL Not supported by this implementation.*/ + * \retval TH_EINVAL Encoding has already begun, \a _buf is + * NULL and \a _buf_sz is not zero, + * or \a _buf is non-NULL and + * \a _buf_sz is not sizeof(#th_quant_info). + * \retval TH_EIMPL Not supported by this implementation.*/ #define TH_ENCCTL_SET_QUANT_PARAMS (2) /**Sets the maximum distance between key frames. * This can be changed during an encode, but will be bounded by @@ -81,12 +75,12 @@ extern "C" { * \param[out] _buf ogg_uint32_t: The actual maximum distance set. * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. * \retval TH_EINVAL \a _buf_sz is not sizeof(ogg_uint32_t). - * \retval TH_IMPL Not supported by this implementation.*/ + * \retval TH_EIMPL Not supported by this implementation.*/ #define TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE (4) /**Disables any encoder features that would prevent lossless transcoding back * to VP3. - * This primarily means disabling block-level QI values and not using 4MV mode - * when any of the luma blocks in a macro block are not coded. + * This primarily means disabling block-adaptive quantization and always coding + * all four luma blocks in a macro block when 4MV is used. * It also includes using the VP3 quantization tables and Huffman codes; if you * set them explicitly after calling this function, the resulting stream will * not be VP3-compatible. @@ -109,7 +103,7 @@ extern "C" { * tables and codebooks from being set. * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. * \retval TH_EINVAL \a _buf_sz is not sizeof(int). - * \retval TH_IMPL Not supported by this implementation.*/ + * \retval TH_EIMPL Not supported by this implementation.*/ #define TH_ENCCTL_SET_VP3_COMPATIBLE (10) /**Gets the maximum speed level. * Higher speed levels favor quicker encoding over better quality per bit. @@ -117,28 +111,254 @@ extern "C" { * may actually improve, but in this case bitrate will also likely increase. * In any case, overall rate/distortion performance will probably decrease. * The maximum value, and the meaning of each value, may change depending on - * the current encoding mode (VBR vs. CQI, etc.). + * the current encoding mode (VBR vs. constant quality, etc.). * - * \param[out] _buf int: The maximum encoding speed level. + * \param[out] _buf int: The maximum encoding speed level. * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. * \retval TH_EINVAL \a _buf_sz is not sizeof(int). - * \retval TH_IMPL Not supported by this implementation in the current + * \retval TH_EIMPL Not supported by this implementation in the current * encoding mode.*/ #define TH_ENCCTL_GET_SPLEVEL_MAX (12) /**Sets the speed level. - * By default, the slowest speed (0) is used. + * The current speed level may be retrieved using #TH_ENCCTL_GET_SPLEVEL. * - * \param[in] _buf int: The new encoding speed level. - * 0 is slowest, larger values use less CPU. + * \param[in] _buf int: The new encoding speed level. + * 0 is slowest, larger values use less CPU. * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. * \retval TH_EINVAL \a _buf_sz is not sizeof(int), or the * encoding speed level is out of bounds. * The maximum encoding speed level may be * implementation- and encoding mode-specific, and can be * obtained via #TH_ENCCTL_GET_SPLEVEL_MAX. - * \retval TH_IMPL Not supported by this implementation in the current + * \retval TH_EIMPL Not supported by this implementation in the current * encoding mode.*/ #define TH_ENCCTL_SET_SPLEVEL (14) +/**Gets the current speed level. + * The default speed level may vary according to encoder implementation, but if + * this control code is not supported (it returns #TH_EIMPL), the default may + * be assumed to be the slowest available speed (0). + * The maximum encoding speed level may be implementation- and encoding + * mode-specific, and can be obtained via #TH_ENCCTL_GET_SPLEVEL_MAX. + * + * \param[out] _buf int: The current encoding speed level. + * 0 is slowest, larger values use less CPU. + * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. + * \retval TH_EINVAL \a _buf_sz is not sizeof(int). + * \retval TH_EIMPL Not supported by this implementation in the current + * encoding mode.*/ +#define TH_ENCCTL_GET_SPLEVEL (16) +/**Sets the number of duplicates of the next frame to produce. + * Although libtheora can encode duplicate frames very cheaply, it costs some + * amount of CPU to detect them, and a run of duplicates cannot span a + * keyframe boundary. + * This control code tells the encoder to produce the specified number of extra + * duplicates of the next frame. + * This allows the encoder to make smarter keyframe placement decisions and + * rate control decisions, and reduces CPU usage as well, when compared to + * just submitting the same frame for encoding multiple times. + * This setting only applies to the next frame submitted for encoding. + * You MUST call th_encode_packetout() repeatedly until it returns 0, or the + * extra duplicate frames will be lost. + * + * \param[in] _buf int: The number of duplicates to produce. + * If this is negative or zero, no duplicates will be produced. + * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. + * \retval TH_EINVAL \a _buf_sz is not sizeof(int), or the + * number of duplicates is greater than or equal to the + * maximum keyframe interval. + * In the latter case, NO duplicate frames will be produced. + * You must ensure that the maximum keyframe interval is set + * larger than the maximum number of duplicates you will + * ever wish to insert prior to encoding. + * \retval TH_EIMPL Not supported by this implementation in the current + * encoding mode.*/ +#define TH_ENCCTL_SET_DUP_COUNT (18) +/**Modifies the default bitrate management behavior. + * Use to allow or disallow frame dropping, and to enable or disable capping + * bit reservoir overflows and underflows. + * See \ref encctlcodes "the list of available flags". + * The flags are set by default to + * #TH_RATECTL_DROP_FRAMES|#TH_RATECTL_CAP_OVERFLOW. + * + * \param[in] _buf int: Any combination of + * \ref ratectlflags "the available flags": + * - #TH_RATECTL_DROP_FRAMES: Enable frame dropping. + * - #TH_RATECTL_CAP_OVERFLOW: Don't bank excess bits for later + * use. + * - #TH_RATECTL_CAP_UNDERFLOW: Don't try to make up shortfalls + * later. + * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. + * \retval TH_EINVAL \a _buf_sz is not sizeof(int) or rate control + * is not enabled. + * \retval TH_EIMPL Not supported by this implementation in the current + * encoding mode.*/ +#define TH_ENCCTL_SET_RATE_FLAGS (20) +/**Sets the size of the bitrate management bit reservoir as a function + * of number of frames. + * The reservoir size affects how quickly bitrate management reacts to + * instantaneous changes in the video complexity. + * Larger reservoirs react more slowly, and provide better overall quality, but + * require more buffering by a client, adding more latency to live streams. + * By default, libtheora sets the reservoir to the maximum distance between + * keyframes, subject to a minimum and maximum limit. + * This call may be used to increase or decrease the reservoir, increasing or + * decreasing the allowed temporary variance in bitrate. + * An implementation may impose some limits on the size of a reservoir it can + * handle, in which case the actual reservoir size may not be exactly what was + * requested. + * The actual value set will be returned. + * + * \param[in] _buf int: Requested size of the reservoir measured in + * frames. + * \param[out] _buf int: The actual size of the reservoir set. + * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. + * \retval TH_EINVAL \a _buf_sz is not sizeof(int), or rate control + * is not enabled. The buffer has an implementation + * defined minimum and maximum size and the value in _buf + * will be adjusted to match the actual value set. + * \retval TH_EIMPL Not supported by this implementation in the current + * encoding mode.*/ +#define TH_ENCCTL_SET_RATE_BUFFER (22) +/**Enable pass 1 of two-pass encoding mode and retrieve the first pass metrics. + * Pass 1 mode must be enabled before the first frame is encoded, and a target + * bitrate must have already been specified to the encoder. + * Although this does not have to be the exact rate that will be used in the + * second pass, closer values may produce better results. + * The first call returns the size of the two-pass header data, along with some + * placeholder content, and sets the encoder into pass 1 mode implicitly. + * This call sets the encoder to pass 1 mode implicitly. + * Then, a subsequent call must be made after each call to + * th_encode_ycbcr_in() to retrieve the metrics for that frame. + * An additional, final call must be made to retrieve the summary data, + * containing such information as the total number of frames, etc. + * This must be stored in place of the placeholder data that was returned + * in the first call, before the frame metrics data. + * All of this data must be presented back to the encoder during pass 2 using + * #TH_ENCCTL_2PASS_IN. + * + * \param[out] char *_buf: Returns a pointer to internal storage + * containing the two pass metrics data. + * This storage is only valid until the next call, or until the + * encoder context is freed, and must be copied by the + * application. + * \retval >=0 The number of bytes of metric data available in the + * returned buffer. + * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. + * \retval TH_EINVAL \a _buf_sz is not sizeof(char *), no target + * bitrate has been set, or the first call was made after + * the first frame was submitted for encoding. + * \retval TH_EIMPL Not supported by this implementation.*/ +#define TH_ENCCTL_2PASS_OUT (24) +/**Submits two-pass encoding metric data collected the first encoding pass to + * the second pass. + * The first call must be made before the first frame is encoded, and a target + * bitrate must have already been specified to the encoder. + * It sets the encoder to pass 2 mode implicitly; this cannot be disabled. + * The encoder may require reading data from some or all of the frames in + * advance, depending on, e.g., the reservoir size used in the second pass. + * You must call this function repeatedly before each frame to provide data + * until either a) it fails to consume all of the data presented or b) all of + * the pass 1 data has been consumed. + * In the first case, you must save the remaining data to be presented after + * the next frame. + * You can call this function with a NULL argument to get an upper bound on + * the number of bytes that will be required before the next frame. + * + * When pass 2 is first enabled, the default bit reservoir is set to the entire + * file; this gives maximum flexibility but can lead to very high peak rates. + * You can subsequently set it to another value with #TH_ENCCTL_SET_RATE_BUFFER + * (e.g., to set it to the keyframe interval for non-live streaming), however, + * you may then need to provide more data before the next frame. + * + * \param[in] _buf char[]: A buffer containing the data returned by + * #TH_ENCCTL_2PASS_OUT in pass 1. + * You may pass NULL for \a _buf to return an upper + * bound on the number of additional bytes needed before the + * next frame. + * The summary data returned at the end of pass 1 must be at + * the head of the buffer on the first call with a + * non-NULL \a _buf, and the placeholder data + * returned at the start of pass 1 should be omitted. + * After each call you should advance this buffer by the number + * of bytes consumed. + * \retval >0 The number of bytes of metric data required/consumed. + * \retval 0 No more data is required before the next frame. + * \retval TH_EFAULT \a _enc_ctx is NULL. + * \retval TH_EINVAL No target bitrate has been set, or the first call was + * made after the first frame was submitted for + * encoding. + * \retval TH_ENOTFORMAT The data did not appear to be pass 1 from a compatible + * implementation of this library. + * \retval TH_EBADHEADER The data was invalid; this may be returned when + * attempting to read an aborted pass 1 file that still + * has the placeholder data in place of the summary + * data. + * \retval TH_EIMPL Not supported by this implementation.*/ +#define TH_ENCCTL_2PASS_IN (26) +/**Sets the current encoding quality. + * This is only valid so long as no bitrate has been specified, either through + * the #th_info struct used to initialize the encoder or through + * #TH_ENCCTL_SET_BITRATE (this restriction may be relaxed in a future + * version). + * If it is set before the headers are emitted, the target quality encoded in + * them will be updated. + * + * \param[in] _buf int: The new target quality, in the range 0...63, + * inclusive. + * \retval 0 Success. + * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. + * \retval TH_EINVAL A target bitrate has already been specified, or the + * quality index was not in the range 0...63. + * \retval TH_EIMPL Not supported by this implementation.*/ +#define TH_ENCCTL_SET_QUALITY (28) +/**Sets the current encoding bitrate. + * Once a bitrate is set, the encoder must use a rate-controlled mode for all + * future frames (this restriction may be relaxed in a future version). + * If it is set before the headers are emitted, the target bitrate encoded in + * them will be updated. + * Due to the buffer delay, the exact bitrate of each section of the encode is + * not guaranteed. + * The encoder may have already used more bits than allowed for the frames it + * has encoded, expecting to make them up in future frames, or it may have + * used fewer, holding the excess in reserve. + * The exact transition between the two bitrates is not well-defined by this + * API, but may be affected by flags set with #TH_ENCCTL_SET_RATE_FLAGS. + * After a number of frames equal to the buffer delay, one may expect further + * output to average at the target bitrate. + * + * \param[in] _buf long: The new target bitrate, in bits per second. + * \retval 0 Success. + * \retval TH_EFAULT \a _enc_ctx or \a _buf is NULL. + * \retval TH_EINVAL The target bitrate was not positive. + * \retval TH_EIMPL Not supported by this implementation.*/ +#define TH_ENCCTL_SET_BITRATE (30) + +/*@}*/ + + +/**\name TH_ENCCTL_SET_RATE_FLAGS flags + * \anchor ratectlflags + * These are the flags available for use with #TH_ENCCTL_SET_RATE_FLAGS.*/ +/*@{*/ +/**Drop frames to keep within bitrate buffer constraints. + * This can have a severe impact on quality, but is the only way to ensure that + * bitrate targets are met at low rates during sudden bursts of activity.*/ +#define TH_RATECTL_DROP_FRAMES (0x1) +/**Ignore bitrate buffer overflows. + * If the encoder uses so few bits that the reservoir of available bits + * overflows, ignore the excess. + * The encoder will not try to use these extra bits in future frames. + * At high rates this may cause the result to be undersized, but allows a + * client to play the stream using a finite buffer; it should normally be + * enabled.*/ +#define TH_RATECTL_CAP_OVERFLOW (0x2) +/**Ignore bitrate buffer underflows. + * If the encoder uses so many bits that the reservoir of available bits + * underflows, ignore the deficit. + * The encoder will not try to make up these extra bits in future frames. + * At low rates this may cause the result to be oversized; it should normally + * be disabled.*/ +#define TH_RATECTL_CAP_UNDERFLOW (0x4) /*@}*/ diff --git a/Engine/lib/libtheora/lib/Makefile.am b/Engine/lib/libtheora/lib/Makefile.am new file mode 100644 index 000000000..89ce26120 --- /dev/null +++ b/Engine/lib/libtheora/lib/Makefile.am @@ -0,0 +1,173 @@ +INCLUDES = -I$(top_srcdir)/include +AM_CFLAGS = $(OGG_CFLAGS) $(CAIRO_CFLAGS) + +EXTRA_DIST = \ + cpu.c \ + encoder_disabled.c \ + x86/mmxencfrag.c \ + x86/mmxfdct.c \ + x86/sse2fdct.c \ + x86/x86enc.c \ + x86/x86enc.h \ + x86/mmxfrag.c \ + x86/mmxfrag.h \ + x86/mmxidct.c \ + x86/mmxloop.h \ + x86/mmxstate.c \ + x86/x86int.h \ + x86/x86state.c \ + x86_vc + +lib_LTLIBRARIES = libtheoradec.la libtheoraenc.la libtheora.la + +if THEORA_DISABLE_ENCODE +encoder_uniq_sources = \ + encoder_disabled.c + +encoder_sources = \ + $(encoder_uniq_sources) +else +encoder_uniq_x86_sources = \ + x86/mmxencfrag.c \ + x86/mmxfdct.c \ + x86/x86enc.c + +encoder_uniq_x86_64_sources = \ + x86/sse2fdct.c + +encoder_shared_x86_sources = \ + x86/mmxfrag.c \ + x86/mmxidct.c \ + x86/mmxstate.c \ + x86/x86state.c + +encoder_shared_x86_64_sources = + +if CPU_x86_64 +encoder_uniq_arch_sources = \ + $(encoder_uniq_x86_sources) \ + $(encoder_uniq_x86_64_sources) +encoder_shared_arch_sources = \ + $(encoder_shared_x86_sources) \ + $(encoder_shared_x86_64_sources) +else +if CPU_x86_32 +encoder_uniq_arch_sources = $(encoder_uniq_x86_sources) +encoder_shared_arch_sources = $(encoder_shared_x86_sources) +else +encoder_uniq_arch_sources = +encoder_shared_arch_sources = +endif +endif + +encoder_uniq_sources = \ + analyze.c \ + fdct.c \ + encfrag.c \ + encapiwrapper.c \ + encinfo.c \ + encode.c \ + enquant.c \ + huffenc.c \ + mathops.c \ + mcenc.c \ + rate.c \ + tokenize.c \ + $(encoder_uniq_arch_sources) + +encoder_sources = \ + apiwrapper.c \ + fragment.c \ + idct.c \ + internal.c \ + state.c \ + quant.c \ + $(encoder_shared_arch_sources) \ + $(encoder_uniq_sources) + +endif + +decoder_x86_sources = \ + x86/mmxidct.c \ + x86/mmxfrag.c \ + x86/mmxstate.c \ + x86/x86state.c +if CPU_x86_64 +decoder_arch_sources = $(decoder_x86_sources) +else +if CPU_x86_32 +decoder_arch_sources = $(decoder_x86_sources) +else +decoder_arch_sources = +endif +endif + +decoder_sources = \ + apiwrapper.c \ + bitpack.c \ + decapiwrapper.c \ + decinfo.c \ + decode.c \ + dequant.c \ + fragment.c \ + huffdec.c \ + idct.c \ + info.c \ + internal.c \ + quant.c \ + state.c \ + $(decoder_arch_sources) + +noinst_HEADERS = \ + cpu.h \ + internal.h \ + encint.h \ + enquant.h \ + huffenc.h \ + mathops.h \ + modedec.h \ + x86/x86enc.h \ + apiwrapper.h \ + bitpack.h \ + dct.h \ + decint.h \ + dequant.h \ + huffdec.h \ + huffman.h \ + ocintrin.h \ + quant.h \ + x86/mmxfrag.h \ + x86/mmxloop.h \ + x86/x86int.h + +libtheoradec_la_SOURCES = \ + $(decoder_sources) \ + Version_script-dec theoradec.exp +libtheoradec_la_LDFLAGS = \ + -version-info @THDEC_LIB_CURRENT@:@THDEC_LIB_REVISION@:@THDEC_LIB_AGE@ \ + @THEORADEC_LDFLAGS@ @CAIRO_LIBS@ + +libtheoraenc_la_SOURCES = \ + $(encoder_sources) \ + Version_script-enc theoraenc.exp +libtheoraenc_la_LDFLAGS = \ + -version-info @THENC_LIB_CURRENT@:@THENC_LIB_REVISION@:@THENC_LIB_AGE@ \ + @THEORAENC_LDFLAGS@ $(OGG_LIBS) + +libtheora_la_SOURCES = \ + $(decoder_sources) \ + $(encoder_uniq_sources) \ + Version_script theora.exp +libtheora_la_LDFLAGS = \ + -version-info @TH_LIB_CURRENT@:@TH_LIB_REVISION@:@TH_LIB_AGE@ \ + @THEORA_LDFLAGS@ @CAIRO_LIBS@ $(OGG_LIBS) + +debug: + $(MAKE) all CFLAGS="@DEBUG@" + +profile: + $(MAKE) all CFLAGS="@PROFILE@" + +# contstruct various symbol export list files +.def.exp : defexp.awk + awk -f defexp.awk $< > $@ diff --git a/Engine/lib/libtheora/lib/Makefile.in b/Engine/lib/libtheora/lib/Makefile.in new file mode 100644 index 000000000..f26ccdc0e --- /dev/null +++ b/Engine/lib/libtheora/lib/Makefile.in @@ -0,0 +1,845 @@ +# Makefile.in generated by automake 1.6.3 from Makefile.am. +# @configure_input@ + +# Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 +# Free Software Foundation, Inc. +# This Makefile.in is free software; the Free Software Foundation +# gives unlimited permission to copy and/or distribute it, +# with or without modifications, as long as this notice is preserved. + +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. + +@SET_MAKE@ +SHELL = @SHELL@ + +srcdir = @srcdir@ +top_srcdir = @top_srcdir@ +VPATH = @srcdir@ +prefix = @prefix@ +exec_prefix = @exec_prefix@ + +bindir = @bindir@ +sbindir = @sbindir@ +libexecdir = @libexecdir@ +datadir = @datadir@ +sysconfdir = @sysconfdir@ +sharedstatedir = @sharedstatedir@ +localstatedir = @localstatedir@ +libdir = @libdir@ +infodir = @infodir@ +mandir = @mandir@ +includedir = @includedir@ +oldincludedir = /usr/include +pkgdatadir = $(datadir)/@PACKAGE@ +pkglibdir = $(libdir)/@PACKAGE@ +pkgincludedir = $(includedir)/@PACKAGE@ +top_builddir = .. + +ACLOCAL = @ACLOCAL@ +AUTOCONF = @AUTOCONF@ +AUTOMAKE = @AUTOMAKE@ +AUTOHEADER = @AUTOHEADER@ + +am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +INSTALL = @INSTALL@ +INSTALL_PROGRAM = @INSTALL_PROGRAM@ +INSTALL_DATA = @INSTALL_DATA@ +install_sh_DATA = $(install_sh) -c -m 644 +install_sh_PROGRAM = $(install_sh) -c +install_sh_SCRIPT = $(install_sh) -c +INSTALL_SCRIPT = @INSTALL_SCRIPT@ +INSTALL_HEADER = $(INSTALL_DATA) +transform = @program_transform_name@ +NORMAL_INSTALL = : +PRE_INSTALL = : +POST_INSTALL = : +NORMAL_UNINSTALL = : +PRE_UNINSTALL = : +POST_UNINSTALL = : +host_alias = @host_alias@ +host_triplet = @host@ + +EXEEXT = @EXEEXT@ +OBJEXT = @OBJEXT@ +PATH_SEPARATOR = @PATH_SEPARATOR@ +ACLOCAL_AMFLAGS = @ACLOCAL_AMFLAGS@ +AMTAR = @AMTAR@ +AR = @AR@ +ARGZ_H = @ARGZ_H@ +AS = @AS@ +AWK = @AWK@ +BUILDABLE_EXAMPLES = @BUILDABLE_EXAMPLES@ +CAIRO_CFLAGS = @CAIRO_CFLAGS@ +CAIRO_LIBS = @CAIRO_LIBS@ +CC = @CC@ +CPP = @CPP@ +CXX = @CXX@ +CXXCPP = @CXXCPP@ +DEBUG = @DEBUG@ +DEPDIR = @DEPDIR@ +DLLTOOL = @DLLTOOL@ +DSYMUTIL = @DSYMUTIL@ +DUMPBIN = @DUMPBIN@ +F77 = @F77@ +GCJ = @GCJ@ +GCJFLAGS = @GCJFLAGS@ +GETOPT_OBJS = @GETOPT_OBJS@ +GREP = @GREP@ +HAVE_BIBTEX = @HAVE_BIBTEX@ +HAVE_DOXYGEN = @HAVE_DOXYGEN@ +HAVE_PDFLATEX = @HAVE_PDFLATEX@ +HAVE_PKG_CONFIG = @HAVE_PKG_CONFIG@ +HAVE_TRANSFIG = @HAVE_TRANSFIG@ +HAVE_VALGRIND = @HAVE_VALGRIND@ +INCLTDL = @INCLTDL@ +INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +LD = @LD@ +LIBADD_DL = @LIBADD_DL@ +LIBADD_DLD_LINK = @LIBADD_DLD_LINK@ +LIBADD_DLOPEN = @LIBADD_DLOPEN@ +LIBADD_SHL_LOAD = @LIBADD_SHL_LOAD@ +LIBLTDL = @LIBLTDL@ +LIBM = @LIBM@ +LIBTOOL = @LIBTOOL@ +LIPO = @LIPO@ +LN_S = @LN_S@ +LTDLDEPS = @LTDLDEPS@ +LTDLINCL = @LTDLINCL@ +LTDLOPEN = @LTDLOPEN@ +LT_CONFIG_H = @LT_CONFIG_H@ +LT_DLLOADERS = @LT_DLLOADERS@ +LT_DLPREOPEN = @LT_DLPREOPEN@ +MAINT = @MAINT@ +NM = @NM@ +NMEDIT = @NMEDIT@ +OBJDUMP = @OBJDUMP@ +OGG_CFLAGS = @OGG_CFLAGS@ +OGG_LIBS = @OGG_LIBS@ +OSS_LIBS = @OSS_LIBS@ +OTOOL = @OTOOL@ +OTOOL64 = @OTOOL64@ +PACKAGE = @PACKAGE@ +PKG_CONFIG = @PKG_CONFIG@ +PNG_CFLAGS = @PNG_CFLAGS@ +PNG_LIBS = @PNG_LIBS@ +PROFILE = @PROFILE@ +RANLIB = @RANLIB@ +RC = @RC@ +SDL_CFLAGS = @SDL_CFLAGS@ +SDL_CONFIG = @SDL_CONFIG@ +SDL_LIBS = @SDL_LIBS@ +SED = @SED@ +STRIP = @STRIP@ +THDEC_LIB_AGE = @THDEC_LIB_AGE@ +THDEC_LIB_CURRENT = @THDEC_LIB_CURRENT@ +THDEC_LIB_REVISION = @THDEC_LIB_REVISION@ +THENC_LIB_AGE = @THENC_LIB_AGE@ +THENC_LIB_CURRENT = @THENC_LIB_CURRENT@ +THENC_LIB_REVISION = @THENC_LIB_REVISION@ +THEORADEC_LDFLAGS = @THEORADEC_LDFLAGS@ +THEORAENC_LDFLAGS = @THEORAENC_LDFLAGS@ +THEORA_LDFLAGS = @THEORA_LDFLAGS@ +TH_LIB_AGE = @TH_LIB_AGE@ +TH_LIB_CURRENT = @TH_LIB_CURRENT@ +TH_LIB_REVISION = @TH_LIB_REVISION@ +VALGRIND_ENVIRONMENT = @VALGRIND_ENVIRONMENT@ +VERSION = @VERSION@ +VORBISENC_LIBS = @VORBISENC_LIBS@ +VORBISFILE_LIBS = @VORBISFILE_LIBS@ +VORBIS_CFLAGS = @VORBIS_CFLAGS@ +VORBIS_LIBS = @VORBIS_LIBS@ +am__include = @am__include@ +am__quote = @am__quote@ +install_sh = @install_sh@ +lt_ECHO = @lt_ECHO@ +ltdl_LIBOBJS = @ltdl_LIBOBJS@ +ltdl_LTLIBOBJS = @ltdl_LTLIBOBJS@ +sys_symbol_underscore = @sys_symbol_underscore@ +INCLUDES = -I$(top_srcdir)/include +AM_CFLAGS = $(OGG_CFLAGS) $(CAIRO_CFLAGS) + +EXTRA_DIST = \ + cpu.c \ + encoder_disabled.c \ + x86/mmxencfrag.c \ + x86/mmxfdct.c \ + x86/sse2fdct.c \ + x86/x86enc.c \ + x86/x86enc.h \ + x86/mmxfrag.c \ + x86/mmxfrag.h \ + x86/mmxidct.c \ + x86/mmxloop.h \ + x86/mmxstate.c \ + x86/x86int.h \ + x86/x86state.c \ + x86_vc + + +lib_LTLIBRARIES = libtheoradec.la libtheoraenc.la libtheora.la + +@THEORA_DISABLE_ENCODE_TRUE@encoder_uniq_sources = \ +@THEORA_DISABLE_ENCODE_TRUE@ encoder_disabled.c + +@THEORA_DISABLE_ENCODE_FALSE@encoder_uniq_sources = \ +@THEORA_DISABLE_ENCODE_FALSE@ analyze.c \ +@THEORA_DISABLE_ENCODE_FALSE@ fdct.c \ +@THEORA_DISABLE_ENCODE_FALSE@ encfrag.c \ +@THEORA_DISABLE_ENCODE_FALSE@ encapiwrapper.c \ +@THEORA_DISABLE_ENCODE_FALSE@ encinfo.c \ +@THEORA_DISABLE_ENCODE_FALSE@ encode.c \ +@THEORA_DISABLE_ENCODE_FALSE@ enquant.c \ +@THEORA_DISABLE_ENCODE_FALSE@ huffenc.c \ +@THEORA_DISABLE_ENCODE_FALSE@ mathops.c \ +@THEORA_DISABLE_ENCODE_FALSE@ mcenc.c \ +@THEORA_DISABLE_ENCODE_FALSE@ rate.c \ +@THEORA_DISABLE_ENCODE_FALSE@ tokenize.c \ +@THEORA_DISABLE_ENCODE_FALSE@ $(encoder_uniq_arch_sources) + + +@THEORA_DISABLE_ENCODE_TRUE@encoder_sources = \ +@THEORA_DISABLE_ENCODE_TRUE@ $(encoder_uniq_sources) + +@THEORA_DISABLE_ENCODE_FALSE@encoder_sources = \ +@THEORA_DISABLE_ENCODE_FALSE@ apiwrapper.c \ +@THEORA_DISABLE_ENCODE_FALSE@ fragment.c \ +@THEORA_DISABLE_ENCODE_FALSE@ idct.c \ +@THEORA_DISABLE_ENCODE_FALSE@ internal.c \ +@THEORA_DISABLE_ENCODE_FALSE@ state.c \ +@THEORA_DISABLE_ENCODE_FALSE@ quant.c \ +@THEORA_DISABLE_ENCODE_FALSE@ $(encoder_shared_arch_sources) \ +@THEORA_DISABLE_ENCODE_FALSE@ $(encoder_uniq_sources) + +@THEORA_DISABLE_ENCODE_FALSE@encoder_uniq_x86_sources = \ +@THEORA_DISABLE_ENCODE_FALSE@ x86/mmxencfrag.c \ +@THEORA_DISABLE_ENCODE_FALSE@ x86/mmxfdct.c \ +@THEORA_DISABLE_ENCODE_FALSE@ x86/x86enc.c + + +@THEORA_DISABLE_ENCODE_FALSE@encoder_uniq_x86_64_sources = \ +@THEORA_DISABLE_ENCODE_FALSE@ x86/sse2fdct.c + + +@THEORA_DISABLE_ENCODE_FALSE@encoder_shared_x86_sources = \ +@THEORA_DISABLE_ENCODE_FALSE@ x86/mmxfrag.c \ +@THEORA_DISABLE_ENCODE_FALSE@ x86/mmxidct.c \ +@THEORA_DISABLE_ENCODE_FALSE@ x86/mmxstate.c \ +@THEORA_DISABLE_ENCODE_FALSE@ x86/x86state.c + + +@THEORA_DISABLE_ENCODE_FALSE@encoder_shared_x86_64_sources = + +@CPU_x86_32_FALSE@@CPU_x86_64_FALSE@@THEORA_DISABLE_ENCODE_FALSE@encoder_uniq_arch_sources = +@CPU_x86_32_TRUE@@CPU_x86_64_FALSE@@THEORA_DISABLE_ENCODE_FALSE@encoder_uniq_arch_sources = $(encoder_uniq_x86_sources) +@CPU_x86_64_TRUE@@THEORA_DISABLE_ENCODE_FALSE@encoder_uniq_arch_sources = \ +@CPU_x86_64_TRUE@@THEORA_DISABLE_ENCODE_FALSE@ $(encoder_uniq_x86_sources) \ +@CPU_x86_64_TRUE@@THEORA_DISABLE_ENCODE_FALSE@ $(encoder_uniq_x86_64_sources) + +@CPU_x86_32_FALSE@@CPU_x86_64_FALSE@@THEORA_DISABLE_ENCODE_FALSE@encoder_shared_arch_sources = +@CPU_x86_32_TRUE@@CPU_x86_64_FALSE@@THEORA_DISABLE_ENCODE_FALSE@encoder_shared_arch_sources = $(encoder_shared_x86_sources) +@CPU_x86_64_TRUE@@THEORA_DISABLE_ENCODE_FALSE@encoder_shared_arch_sources = \ +@CPU_x86_64_TRUE@@THEORA_DISABLE_ENCODE_FALSE@ $(encoder_shared_x86_sources) \ +@CPU_x86_64_TRUE@@THEORA_DISABLE_ENCODE_FALSE@ $(encoder_shared_x86_64_sources) + + +decoder_x86_sources = \ + x86/mmxidct.c \ + x86/mmxfrag.c \ + x86/mmxstate.c \ + x86/x86state.c + +@CPU_x86_32_FALSE@@CPU_x86_64_FALSE@decoder_arch_sources = +@CPU_x86_32_TRUE@@CPU_x86_64_FALSE@decoder_arch_sources = $(decoder_x86_sources) +@CPU_x86_64_TRUE@decoder_arch_sources = $(decoder_x86_sources) + +decoder_sources = \ + apiwrapper.c \ + bitpack.c \ + decapiwrapper.c \ + decinfo.c \ + decode.c \ + dequant.c \ + fragment.c \ + huffdec.c \ + idct.c \ + info.c \ + internal.c \ + quant.c \ + state.c \ + $(decoder_arch_sources) + + +noinst_HEADERS = \ + cpu.h \ + internal.h \ + encint.h \ + enquant.h \ + huffenc.h \ + mathops.h \ + modedec.h \ + x86/x86enc.h \ + apiwrapper.h \ + bitpack.h \ + dct.h \ + decint.h \ + dequant.h \ + huffdec.h \ + huffman.h \ + ocintrin.h \ + quant.h \ + x86/mmxfrag.h \ + x86/mmxloop.h \ + x86/x86int.h + + +libtheoradec_la_SOURCES = \ + $(decoder_sources) \ + Version_script-dec theoradec.exp + +libtheoradec_la_LDFLAGS = \ + -version-info @THDEC_LIB_CURRENT@:@THDEC_LIB_REVISION@:@THDEC_LIB_AGE@ \ + @THEORADEC_LDFLAGS@ @CAIRO_LIBS@ + + +libtheoraenc_la_SOURCES = \ + $(encoder_sources) \ + Version_script-enc theoraenc.exp + +libtheoraenc_la_LDFLAGS = \ + -version-info @THENC_LIB_CURRENT@:@THENC_LIB_REVISION@:@THENC_LIB_AGE@ \ + @THEORAENC_LDFLAGS@ $(OGG_LIBS) + + +libtheora_la_SOURCES = \ + $(decoder_sources) \ + $(encoder_uniq_sources) \ + Version_script theora.exp + +libtheora_la_LDFLAGS = \ + -version-info @TH_LIB_CURRENT@:@TH_LIB_REVISION@:@TH_LIB_AGE@ \ + @THEORA_LDFLAGS@ @CAIRO_LIBS@ $(OGG_LIBS) + +subdir = lib +mkinstalldirs = $(SHELL) $(top_srcdir)/mkinstalldirs +CONFIG_HEADER = $(top_builddir)/config.h +CONFIG_CLEAN_FILES = +LTLIBRARIES = $(lib_LTLIBRARIES) + +libtheora_la_LIBADD = +am__objects_1 = mmxidct.lo mmxfrag.lo mmxstate.lo x86state.lo +@CPU_x86_32_FALSE@@CPU_x86_64_FALSE@am__objects_2 = +@CPU_x86_32_TRUE@@CPU_x86_64_FALSE@am__objects_2 = $(am__objects_1) +@CPU_x86_64_TRUE@am__objects_2 = $(am__objects_1) +am__objects_3 = apiwrapper.lo bitpack.lo decapiwrapper.lo decinfo.lo \ + decode.lo dequant.lo fragment.lo huffdec.lo idct.lo info.lo \ + internal.lo quant.lo state.lo $(am__objects_2) +@THEORA_DISABLE_ENCODE_FALSE@am__objects_4 = mmxencfrag.lo mmxfdct.lo \ +@THEORA_DISABLE_ENCODE_FALSE@ x86enc.lo +@THEORA_DISABLE_ENCODE_FALSE@am__objects_5 = sse2fdct.lo +@CPU_x86_32_FALSE@@CPU_x86_64_FALSE@@THEORA_DISABLE_ENCODE_FALSE@am__objects_6 = +@CPU_x86_32_TRUE@@CPU_x86_64_FALSE@@THEORA_DISABLE_ENCODE_FALSE@am__objects_6 = \ +@CPU_x86_32_TRUE@@CPU_x86_64_FALSE@@THEORA_DISABLE_ENCODE_FALSE@ $(am__objects_4) +@CPU_x86_64_TRUE@@THEORA_DISABLE_ENCODE_FALSE@am__objects_6 = \ +@CPU_x86_64_TRUE@@THEORA_DISABLE_ENCODE_FALSE@ $(am__objects_4) \ +@CPU_x86_64_TRUE@@THEORA_DISABLE_ENCODE_FALSE@ $(am__objects_5) +@THEORA_DISABLE_ENCODE_TRUE@am__objects_7 = encoder_disabled.lo +@THEORA_DISABLE_ENCODE_FALSE@am__objects_7 = analyze.lo fdct.lo \ +@THEORA_DISABLE_ENCODE_FALSE@ encfrag.lo encapiwrapper.lo \ +@THEORA_DISABLE_ENCODE_FALSE@ encinfo.lo encode.lo enquant.lo \ +@THEORA_DISABLE_ENCODE_FALSE@ huffenc.lo mathops.lo mcenc.lo \ +@THEORA_DISABLE_ENCODE_FALSE@ rate.lo tokenize.lo \ +@THEORA_DISABLE_ENCODE_FALSE@ $(am__objects_6) +am_libtheora_la_OBJECTS = $(am__objects_3) $(am__objects_7) +libtheora_la_OBJECTS = $(am_libtheora_la_OBJECTS) +libtheoradec_la_LIBADD = +am_libtheoradec_la_OBJECTS = $(am__objects_3) +libtheoradec_la_OBJECTS = $(am_libtheoradec_la_OBJECTS) +libtheoraenc_la_LIBADD = +@THEORA_DISABLE_ENCODE_FALSE@am__objects_8 = mmxfrag.lo mmxidct.lo \ +@THEORA_DISABLE_ENCODE_FALSE@ mmxstate.lo x86state.lo +@THEORA_DISABLE_ENCODE_FALSE@am__objects_9 = +@CPU_x86_32_FALSE@@CPU_x86_64_FALSE@@THEORA_DISABLE_ENCODE_FALSE@am__objects_10 = +@CPU_x86_32_TRUE@@CPU_x86_64_FALSE@@THEORA_DISABLE_ENCODE_FALSE@am__objects_10 = \ +@CPU_x86_32_TRUE@@CPU_x86_64_FALSE@@THEORA_DISABLE_ENCODE_FALSE@ $(am__objects_8) +@CPU_x86_64_TRUE@@THEORA_DISABLE_ENCODE_FALSE@am__objects_10 = \ +@CPU_x86_64_TRUE@@THEORA_DISABLE_ENCODE_FALSE@ $(am__objects_8) \ +@CPU_x86_64_TRUE@@THEORA_DISABLE_ENCODE_FALSE@ $(am__objects_9) +@THEORA_DISABLE_ENCODE_TRUE@am__objects_11 = $(am__objects_7) +@THEORA_DISABLE_ENCODE_FALSE@am__objects_11 = apiwrapper.lo fragment.lo \ +@THEORA_DISABLE_ENCODE_FALSE@ idct.lo internal.lo state.lo \ +@THEORA_DISABLE_ENCODE_FALSE@ quant.lo $(am__objects_10) \ +@THEORA_DISABLE_ENCODE_FALSE@ $(am__objects_7) +am_libtheoraenc_la_OBJECTS = $(am__objects_11) +libtheoraenc_la_OBJECTS = $(am_libtheoraenc_la_OBJECTS) + +DEFS = @DEFS@ +DEFAULT_INCLUDES = -I. -I$(srcdir) -I$(top_builddir) +CPPFLAGS = @CPPFLAGS@ +LDFLAGS = @LDFLAGS@ +LIBS = @LIBS@ +depcomp = $(SHELL) $(top_srcdir)/depcomp +am__depfiles_maybe = depfiles +@AMDEP_TRUE@DEP_FILES = ./$(DEPDIR)/analyze.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/apiwrapper.Plo ./$(DEPDIR)/bitpack.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/decapiwrapper.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/decinfo.Plo ./$(DEPDIR)/decode.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/dequant.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/encapiwrapper.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/encfrag.Plo ./$(DEPDIR)/encinfo.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/encode.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/encoder_disabled.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/enquant.Plo ./$(DEPDIR)/fdct.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/fragment.Plo ./$(DEPDIR)/huffdec.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/huffenc.Plo ./$(DEPDIR)/idct.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/info.Plo ./$(DEPDIR)/internal.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/mathops.Plo ./$(DEPDIR)/mcenc.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/mmxencfrag.Plo ./$(DEPDIR)/mmxfdct.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/mmxfrag.Plo ./$(DEPDIR)/mmxidct.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/mmxstate.Plo ./$(DEPDIR)/quant.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/rate.Plo ./$(DEPDIR)/sse2fdct.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/state.Plo ./$(DEPDIR)/tokenize.Plo \ +@AMDEP_TRUE@ ./$(DEPDIR)/x86enc.Plo ./$(DEPDIR)/x86state.Plo +COMPILE = $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) \ + $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +LTCOMPILE = $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) \ + $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) +CCLD = $(CC) +LINK = $(LIBTOOL) --mode=link $(CCLD) $(AM_CFLAGS) $(CFLAGS) \ + $(AM_LDFLAGS) $(LDFLAGS) -o $@ +CFLAGS = @CFLAGS@ +DIST_SOURCES = $(libtheora_la_SOURCES) $(libtheoradec_la_SOURCES) \ + $(libtheoraenc_la_SOURCES) +HEADERS = $(noinst_HEADERS) + +DIST_COMMON = $(noinst_HEADERS) Makefile.am Makefile.in +SOURCES = $(libtheora_la_SOURCES) $(libtheoradec_la_SOURCES) $(libtheoraenc_la_SOURCES) + +all: all-am + +.SUFFIXES: +.SUFFIXES: .c .def .exp .lo .o .obj +$(srcdir)/Makefile.in: @MAINTAINER_MODE_TRUE@ Makefile.am $(top_srcdir)/configure.ac $(ACLOCAL_M4) + cd $(top_srcdir) && \ + $(AUTOMAKE) --gnu lib/Makefile +Makefile: @MAINTAINER_MODE_TRUE@ $(srcdir)/Makefile.in $(top_builddir)/config.status + cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe) +libLTLIBRARIES_INSTALL = $(INSTALL) +install-libLTLIBRARIES: $(lib_LTLIBRARIES) + @$(NORMAL_INSTALL) + $(mkinstalldirs) $(DESTDIR)$(libdir) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + if test -f $$p; then \ + f="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(libdir)/$$f"; \ + $(LIBTOOL) --mode=install $(libLTLIBRARIES_INSTALL) $(INSTALL_STRIP_FLAG) $$p $(DESTDIR)$(libdir)/$$f; \ + else :; fi; \ + done + +uninstall-libLTLIBRARIES: + @$(NORMAL_UNINSTALL) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + p="`echo $$p | sed -e 's|^.*/||'`"; \ + echo " $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p"; \ + $(LIBTOOL) --mode=uninstall rm -f $(DESTDIR)$(libdir)/$$p; \ + done + +clean-libLTLIBRARIES: + -test -z "$(lib_LTLIBRARIES)" || rm -f $(lib_LTLIBRARIES) + @list='$(lib_LTLIBRARIES)'; for p in $$list; do \ + dir="`echo $$p | sed -e 's|/[^/]*$$||'`"; \ + test -z "$dir" && dir=.; \ + echo "rm -f \"$${dir}/so_locations\""; \ + rm -f "$${dir}/so_locations"; \ + done +mmxidct.lo: x86/mmxidct.c +mmxfrag.lo: x86/mmxfrag.c +mmxstate.lo: x86/mmxstate.c +x86state.lo: x86/x86state.c +mmxencfrag.lo: x86/mmxencfrag.c +mmxfdct.lo: x86/mmxfdct.c +x86enc.lo: x86/x86enc.c +sse2fdct.lo: x86/sse2fdct.c +libtheora.la: $(libtheora_la_OBJECTS) $(libtheora_la_DEPENDENCIES) + $(LINK) -rpath $(libdir) $(libtheora_la_LDFLAGS) $(libtheora_la_OBJECTS) $(libtheora_la_LIBADD) $(LIBS) +libtheoradec.la: $(libtheoradec_la_OBJECTS) $(libtheoradec_la_DEPENDENCIES) + $(LINK) -rpath $(libdir) $(libtheoradec_la_LDFLAGS) $(libtheoradec_la_OBJECTS) $(libtheoradec_la_LIBADD) $(LIBS) +libtheoraenc.la: $(libtheoraenc_la_OBJECTS) $(libtheoraenc_la_DEPENDENCIES) + $(LINK) -rpath $(libdir) $(libtheoraenc_la_LDFLAGS) $(libtheoraenc_la_OBJECTS) $(libtheoraenc_la_LIBADD) $(LIBS) + +mostlyclean-compile: + -rm -f *.$(OBJEXT) core *.core + +distclean-compile: + -rm -f *.tab.c + +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/analyze.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/apiwrapper.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/bitpack.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/decapiwrapper.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/decinfo.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/decode.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/dequant.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encapiwrapper.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encfrag.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encinfo.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encode.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/encoder_disabled.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/enquant.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fdct.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/fragment.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/huffdec.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/huffenc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/idct.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/info.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/internal.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mathops.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mcenc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmxencfrag.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmxfdct.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmxfrag.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmxidct.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/mmxstate.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/quant.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/rate.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/sse2fdct.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/state.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/tokenize.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x86enc.Plo@am__quote@ +@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/x86state.Plo@am__quote@ + +distclean-depend: + -rm -rf ./$(DEPDIR) + +.c.o: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `test -f '$<' || echo '$(srcdir)/'`$< + +.c.obj: +@AMDEP_TRUE@ source='$<' object='$@' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Po' tmpdepfile='$(DEPDIR)/$*.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(COMPILE) -c `cygpath -w $<` + +.c.lo: +@AMDEP_TRUE@ source='$<' object='$@' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/$*.Plo' tmpdepfile='$(DEPDIR)/$*.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LTCOMPILE) -c -o $@ `test -f '$<' || echo '$(srcdir)/'`$< + +mmxidct.o: x86/mmxidct.c +@AMDEP_TRUE@ source='x86/mmxidct.c' object='mmxidct.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxidct.Po' tmpdepfile='$(DEPDIR)/mmxidct.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxidct.o `test -f 'x86/mmxidct.c' || echo '$(srcdir)/'`x86/mmxidct.c + +mmxidct.obj: x86/mmxidct.c +@AMDEP_TRUE@ source='x86/mmxidct.c' object='mmxidct.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxidct.Po' tmpdepfile='$(DEPDIR)/mmxidct.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxidct.obj `cygpath -w x86/mmxidct.c` + +mmxidct.lo: x86/mmxidct.c +@AMDEP_TRUE@ source='x86/mmxidct.c' object='mmxidct.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxidct.Plo' tmpdepfile='$(DEPDIR)/mmxidct.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxidct.lo `test -f 'x86/mmxidct.c' || echo '$(srcdir)/'`x86/mmxidct.c + +mmxfrag.o: x86/mmxfrag.c +@AMDEP_TRUE@ source='x86/mmxfrag.c' object='mmxfrag.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxfrag.Po' tmpdepfile='$(DEPDIR)/mmxfrag.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxfrag.o `test -f 'x86/mmxfrag.c' || echo '$(srcdir)/'`x86/mmxfrag.c + +mmxfrag.obj: x86/mmxfrag.c +@AMDEP_TRUE@ source='x86/mmxfrag.c' object='mmxfrag.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxfrag.Po' tmpdepfile='$(DEPDIR)/mmxfrag.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxfrag.obj `cygpath -w x86/mmxfrag.c` + +mmxfrag.lo: x86/mmxfrag.c +@AMDEP_TRUE@ source='x86/mmxfrag.c' object='mmxfrag.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxfrag.Plo' tmpdepfile='$(DEPDIR)/mmxfrag.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxfrag.lo `test -f 'x86/mmxfrag.c' || echo '$(srcdir)/'`x86/mmxfrag.c + +mmxstate.o: x86/mmxstate.c +@AMDEP_TRUE@ source='x86/mmxstate.c' object='mmxstate.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxstate.Po' tmpdepfile='$(DEPDIR)/mmxstate.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxstate.o `test -f 'x86/mmxstate.c' || echo '$(srcdir)/'`x86/mmxstate.c + +mmxstate.obj: x86/mmxstate.c +@AMDEP_TRUE@ source='x86/mmxstate.c' object='mmxstate.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxstate.Po' tmpdepfile='$(DEPDIR)/mmxstate.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxstate.obj `cygpath -w x86/mmxstate.c` + +mmxstate.lo: x86/mmxstate.c +@AMDEP_TRUE@ source='x86/mmxstate.c' object='mmxstate.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxstate.Plo' tmpdepfile='$(DEPDIR)/mmxstate.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxstate.lo `test -f 'x86/mmxstate.c' || echo '$(srcdir)/'`x86/mmxstate.c + +x86state.o: x86/x86state.c +@AMDEP_TRUE@ source='x86/x86state.c' object='x86state.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/x86state.Po' tmpdepfile='$(DEPDIR)/x86state.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o x86state.o `test -f 'x86/x86state.c' || echo '$(srcdir)/'`x86/x86state.c + +x86state.obj: x86/x86state.c +@AMDEP_TRUE@ source='x86/x86state.c' object='x86state.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/x86state.Po' tmpdepfile='$(DEPDIR)/x86state.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o x86state.obj `cygpath -w x86/x86state.c` + +x86state.lo: x86/x86state.c +@AMDEP_TRUE@ source='x86/x86state.c' object='x86state.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/x86state.Plo' tmpdepfile='$(DEPDIR)/x86state.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o x86state.lo `test -f 'x86/x86state.c' || echo '$(srcdir)/'`x86/x86state.c + +mmxencfrag.o: x86/mmxencfrag.c +@AMDEP_TRUE@ source='x86/mmxencfrag.c' object='mmxencfrag.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxencfrag.Po' tmpdepfile='$(DEPDIR)/mmxencfrag.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxencfrag.o `test -f 'x86/mmxencfrag.c' || echo '$(srcdir)/'`x86/mmxencfrag.c + +mmxencfrag.obj: x86/mmxencfrag.c +@AMDEP_TRUE@ source='x86/mmxencfrag.c' object='mmxencfrag.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxencfrag.Po' tmpdepfile='$(DEPDIR)/mmxencfrag.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxencfrag.obj `cygpath -w x86/mmxencfrag.c` + +mmxencfrag.lo: x86/mmxencfrag.c +@AMDEP_TRUE@ source='x86/mmxencfrag.c' object='mmxencfrag.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxencfrag.Plo' tmpdepfile='$(DEPDIR)/mmxencfrag.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxencfrag.lo `test -f 'x86/mmxencfrag.c' || echo '$(srcdir)/'`x86/mmxencfrag.c + +mmxfdct.o: x86/mmxfdct.c +@AMDEP_TRUE@ source='x86/mmxfdct.c' object='mmxfdct.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxfdct.Po' tmpdepfile='$(DEPDIR)/mmxfdct.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxfdct.o `test -f 'x86/mmxfdct.c' || echo '$(srcdir)/'`x86/mmxfdct.c + +mmxfdct.obj: x86/mmxfdct.c +@AMDEP_TRUE@ source='x86/mmxfdct.c' object='mmxfdct.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxfdct.Po' tmpdepfile='$(DEPDIR)/mmxfdct.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxfdct.obj `cygpath -w x86/mmxfdct.c` + +mmxfdct.lo: x86/mmxfdct.c +@AMDEP_TRUE@ source='x86/mmxfdct.c' object='mmxfdct.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/mmxfdct.Plo' tmpdepfile='$(DEPDIR)/mmxfdct.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o mmxfdct.lo `test -f 'x86/mmxfdct.c' || echo '$(srcdir)/'`x86/mmxfdct.c + +x86enc.o: x86/x86enc.c +@AMDEP_TRUE@ source='x86/x86enc.c' object='x86enc.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/x86enc.Po' tmpdepfile='$(DEPDIR)/x86enc.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o x86enc.o `test -f 'x86/x86enc.c' || echo '$(srcdir)/'`x86/x86enc.c + +x86enc.obj: x86/x86enc.c +@AMDEP_TRUE@ source='x86/x86enc.c' object='x86enc.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/x86enc.Po' tmpdepfile='$(DEPDIR)/x86enc.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o x86enc.obj `cygpath -w x86/x86enc.c` + +x86enc.lo: x86/x86enc.c +@AMDEP_TRUE@ source='x86/x86enc.c' object='x86enc.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/x86enc.Plo' tmpdepfile='$(DEPDIR)/x86enc.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o x86enc.lo `test -f 'x86/x86enc.c' || echo '$(srcdir)/'`x86/x86enc.c + +sse2fdct.o: x86/sse2fdct.c +@AMDEP_TRUE@ source='x86/sse2fdct.c' object='sse2fdct.o' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/sse2fdct.Po' tmpdepfile='$(DEPDIR)/sse2fdct.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sse2fdct.o `test -f 'x86/sse2fdct.c' || echo '$(srcdir)/'`x86/sse2fdct.c + +sse2fdct.obj: x86/sse2fdct.c +@AMDEP_TRUE@ source='x86/sse2fdct.c' object='sse2fdct.obj' libtool=no @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/sse2fdct.Po' tmpdepfile='$(DEPDIR)/sse2fdct.TPo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sse2fdct.obj `cygpath -w x86/sse2fdct.c` + +sse2fdct.lo: x86/sse2fdct.c +@AMDEP_TRUE@ source='x86/sse2fdct.c' object='sse2fdct.lo' libtool=yes @AMDEPBACKSLASH@ +@AMDEP_TRUE@ depfile='$(DEPDIR)/sse2fdct.Plo' tmpdepfile='$(DEPDIR)/sse2fdct.TPlo' @AMDEPBACKSLASH@ +@AMDEP_TRUE@ $(CCDEPMODE) $(depcomp) @AMDEPBACKSLASH@ + $(LIBTOOL) --mode=compile $(CC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(AM_CFLAGS) $(CFLAGS) -c -o sse2fdct.lo `test -f 'x86/sse2fdct.c' || echo '$(srcdir)/'`x86/sse2fdct.c +CCDEPMODE = @CCDEPMODE@ + +mostlyclean-libtool: + -rm -f *.lo + +clean-libtool: + -rm -rf .libs _libs + +distclean-libtool: + -rm -f libtool +uninstall-info-am: + +ETAGS = etags +ETAGSFLAGS = + +tags: TAGS + +ID: $(HEADERS) $(SOURCES) $(LISP) $(TAGS_FILES) + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + mkid -fID $$unique + +TAGS: $(HEADERS) $(SOURCES) $(TAGS_DEPENDENCIES) \ + $(TAGS_FILES) $(LISP) + tags=; \ + here=`pwd`; \ + list='$(SOURCES) $(HEADERS) $(LISP) $(TAGS_FILES)'; \ + unique=`for i in $$list; do \ + if test -f "$$i"; then echo $$i; else echo $(srcdir)/$$i; fi; \ + done | \ + $(AWK) ' { files[$$0] = 1; } \ + END { for (i in files) print i; }'`; \ + test -z "$(ETAGS_ARGS)$$tags$$unique" \ + || $(ETAGS) $(ETAGSFLAGS) $(AM_ETAGSFLAGS) $(ETAGS_ARGS) \ + $$tags $$unique + +GTAGS: + here=`$(am__cd) $(top_builddir) && pwd` \ + && cd $(top_srcdir) \ + && gtags -i $(GTAGS_ARGS) $$here + +distclean-tags: + -rm -f TAGS ID GTAGS GRTAGS GSYMS GPATH +DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) + +top_distdir = .. +distdir = $(top_distdir)/$(PACKAGE)-$(VERSION) + +distdir: $(DISTFILES) + $(mkinstalldirs) $(distdir)/x86 + @list='$(DISTFILES)'; for file in $$list; do \ + if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ + dir=`echo "$$file" | sed -e 's,/[^/]*$$,,'`; \ + if test "$$dir" != "$$file" && test "$$dir" != "."; then \ + dir="/$$dir"; \ + $(mkinstalldirs) "$(distdir)$$dir"; \ + else \ + dir=''; \ + fi; \ + if test -d $$d/$$file; then \ + if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ + cp -pR $(srcdir)/$$file $(distdir)$$dir || exit 1; \ + fi; \ + cp -pR $$d/$$file $(distdir)$$dir || exit 1; \ + else \ + test -f $(distdir)/$$file \ + || cp -p $$d/$$file $(distdir)/$$file \ + || exit 1; \ + fi; \ + done +check-am: all-am +check: check-am +all-am: Makefile $(LTLIBRARIES) $(HEADERS) + +installdirs: + $(mkinstalldirs) $(DESTDIR)$(libdir) + +install: install-am +install-exec: install-exec-am +install-data: install-data-am +uninstall: uninstall-am + +install-am: all-am + @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am + +installcheck: installcheck-am +install-strip: + $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ + INSTALL_STRIP_FLAG=-s \ + `test -z '$(STRIP)' || \ + echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +mostlyclean-generic: + +clean-generic: + +distclean-generic: + -rm -f Makefile $(CONFIG_CLEAN_FILES) + +maintainer-clean-generic: + @echo "This command is intended for maintainers to use" + @echo "it deletes files that may require special tools to rebuild." +clean: clean-am + +clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \ + mostlyclean-am + +distclean: distclean-am + +distclean-am: clean-am distclean-compile distclean-depend \ + distclean-generic distclean-libtool distclean-tags + +dvi: dvi-am + +dvi-am: + +info: info-am + +info-am: + +install-data-am: + +install-exec-am: install-libLTLIBRARIES + +install-info: install-info-am + +install-man: + +installcheck-am: + +maintainer-clean: maintainer-clean-am + +maintainer-clean-am: distclean-am maintainer-clean-generic + +mostlyclean: mostlyclean-am + +mostlyclean-am: mostlyclean-compile mostlyclean-generic \ + mostlyclean-libtool + +uninstall-am: uninstall-info-am uninstall-libLTLIBRARIES + +.PHONY: GTAGS all all-am check check-am clean clean-generic \ + clean-libLTLIBRARIES clean-libtool distclean distclean-compile \ + distclean-depend distclean-generic distclean-libtool \ + distclean-tags distdir dvi dvi-am info info-am install \ + install-am install-data install-data-am install-exec \ + install-exec-am install-info install-info-am \ + install-libLTLIBRARIES install-man install-strip installcheck \ + installcheck-am installdirs maintainer-clean \ + maintainer-clean-generic mostlyclean mostlyclean-compile \ + mostlyclean-generic mostlyclean-libtool tags uninstall \ + uninstall-am uninstall-info-am uninstall-libLTLIBRARIES + + +debug: + $(MAKE) all CFLAGS="@DEBUG@" + +profile: + $(MAKE) all CFLAGS="@PROFILE@" + +# contstruct various symbol export list files +.def.exp : defexp.awk + awk -f defexp.awk $< > $@ +# Tell versions [3.59,3.63) of GNU make to not export all variables. +# Otherwise a system limit (for SysV at least) may be exceeded. +.NOEXPORT: diff --git a/Engine/lib/libtheora/lib/Version_script b/Engine/lib/libtheora/lib/Version_script new file mode 100644 index 000000000..2ecb5e43a --- /dev/null +++ b/Engine/lib/libtheora/lib/Version_script @@ -0,0 +1,53 @@ +# +# Export file for libtheora +# +# Only the symbols listed in the global section will be callable from +# applications linking to the libraries. +# + +# We use something that looks like a versioned so filename here +# to define the old API because of a historical confusion. This +# label must be kept to maintain ABI compatibility. + +libtheora.so.1.0 +{ + global: + theora_version_string; + theora_version_number; + + theora_encode_init; + theora_encode_YUVin; + theora_encode_packetout; + theora_encode_header; + theora_encode_comment; + theora_encode_tables; + + theora_decode_header; + theora_decode_init; + theora_decode_packetin; + theora_decode_YUVout; + + theora_control; + + theora_packet_isheader; + theora_packet_iskeyframe; + + theora_granule_shift; + theora_granule_frame; + theora_granule_time; + + theora_info_init; + theora_info_clear; + + theora_clear; + + theora_comment_init; + theora_comment_add; + theora_comment_add_tag; + theora_comment_query; + theora_comment_query_count; + theora_comment_clear; + + local: + *; +}; diff --git a/Engine/lib/libtheora/lib/Version_script-dec b/Engine/lib/libtheora/lib/Version_script-dec new file mode 100644 index 000000000..cab368397 --- /dev/null +++ b/Engine/lib/libtheora/lib/Version_script-dec @@ -0,0 +1,82 @@ +# +# Export file for libtheoradec +# +# Only the symbols listed in the global section will be callable from +# applications linking to the libraries. +# + +# The 1.x API +libtheoradec_1.0 +{ + global: + th_version_string; + th_version_number; + + th_decode_headerin; + th_decode_alloc; + th_setup_free; + th_decode_ctl; + th_decode_packetin; + th_decode_ycbcr_out; + th_decode_free; + + th_packet_isheader; + th_packet_iskeyframe; + + th_granule_frame; + th_granule_time; + + th_info_init; + th_info_clear; + + th_comment_init; + th_comment_add; + th_comment_add_tag; + th_comment_query; + th_comment_query_count; + th_comment_clear; + + local: + *; +}; + +# The deprecated legacy api from the libtheora alpha releases. +# We use something that looks like a versioned so filename here +# to define the old API because of a historical confusion. This +# label must be kept to maintain ABI compatibility. + +libtheora.so.1.0 +{ + global: + theora_version_string; + theora_version_number; + + theora_decode_header; + theora_decode_init; + theora_decode_packetin; + theora_decode_YUVout; + + theora_control; + + theora_packet_isheader; + theora_packet_iskeyframe; + + theora_granule_shift; + theora_granule_frame; + theora_granule_time; + + theora_info_init; + theora_info_clear; + + theora_clear; + + theora_comment_init; + theora_comment_add; + theora_comment_add_tag; + theora_comment_query; + theora_comment_query_count; + theora_comment_clear; + + local: + *; +}; diff --git a/Engine/lib/libtheora/lib/Version_script-enc b/Engine/lib/libtheora/lib/Version_script-enc new file mode 100644 index 000000000..37699edd6 --- /dev/null +++ b/Engine/lib/libtheora/lib/Version_script-enc @@ -0,0 +1,43 @@ +# +# Export file for libtheora +# +# Only the symbols listed in the global section will be callable from +# applications linking to the libraries. +# + +# The 1.x encoder API +libtheoraenc_1.0 +{ + global: + th_encode_alloc; + th_encode_ctl; + th_encode_flushheader; + th_encode_ycbcr_in; + th_encode_packetout; + th_encode_free; + + TH_VP31_QUANT_INFO; + TH_VP31_HUFF_CODES; + + local: + *; +}; + +# The encoder portion of the deprecated alpha release api. +# We use something that looks like a versioned so filename here +# to define the old API because of a historical confusion. This +# label must be kept to maintain ABI compatibility. + +libtheora.so.1.0 +{ + global: + theora_encode_init; + theora_encode_YUVin; + theora_encode_packetout; + theora_encode_header; + theora_encode_comment; + theora_encode_tables; + + local: + *; +}; diff --git a/Engine/lib/libtheora/lib/analyze.c b/Engine/lib/libtheora/lib/analyze.c new file mode 100644 index 000000000..af01b60df --- /dev/null +++ b/Engine/lib/libtheora/lib/analyze.c @@ -0,0 +1,2709 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: mode selection code + last mod: $Id$ + + ********************************************************************/ +#include +#include +#include "encint.h" +#include "modedec.h" + + + +typedef struct oc_fr_state oc_fr_state; +typedef struct oc_qii_state oc_qii_state; +typedef struct oc_enc_pipeline_state oc_enc_pipeline_state; +typedef struct oc_rd_metric oc_rd_metric; +typedef struct oc_mode_choice oc_mode_choice; + + + +/*There are 8 possible schemes used to encode macro block modes. + Schemes 0-6 use a maximally-skewed Huffman code to code each of the modes. + The same set of Huffman codes is used for each of these 7 schemes, but the + mode assigned to each codeword varies. + Scheme 0 writes a custom mapping from codeword to MB mode to the bitstream, + while schemes 1-6 have a fixed mapping. + Scheme 7 just encodes each mode directly in 3 bits.*/ + +/*The mode orderings for the various mode coding schemes. + Scheme 0 uses a custom alphabet, which is not stored in this table. + This is the inverse of the equivalent table OC_MODE_ALPHABETS in the + decoder.*/ +static const unsigned char OC_MODE_RANKS[7][OC_NMODES]={ + /*Last MV dominates.*/ + /*L P M N I G GM 4*/ + {3,4,2,0,1,5,6,7}, + /*L P N M I G GM 4*/ + {2,4,3,0,1,5,6,7}, + /*L M P N I G GM 4*/ + {3,4,1,0,2,5,6,7}, + /*L M N P I G GM 4*/ + {2,4,1,0,3,5,6,7}, + /*No MV dominates.*/ + /*N L P M I G GM 4*/ + {0,4,3,1,2,5,6,7}, + /*N G L P M I GM 4*/ + {0,5,4,2,3,1,6,7}, + /*Default ordering.*/ + /*N I M L P G GM 4*/ + {0,1,2,3,4,5,6,7} +}; + + + +/*Initialize the mode scheme chooser. + This need only be called once per encoder.*/ +void oc_mode_scheme_chooser_init(oc_mode_scheme_chooser *_chooser){ + int si; + _chooser->mode_ranks[0]=_chooser->scheme0_ranks; + for(si=1;si<8;si++)_chooser->mode_ranks[si]=OC_MODE_RANKS[si-1]; +} + +/*Reset the mode scheme chooser. + This needs to be called once for each frame, including the first.*/ +static void oc_mode_scheme_chooser_reset(oc_mode_scheme_chooser *_chooser){ + int si; + memset(_chooser->mode_counts,0,OC_NMODES*sizeof(*_chooser->mode_counts)); + /*Scheme 0 starts with 24 bits to store the mode list in.*/ + _chooser->scheme_bits[0]=24; + memset(_chooser->scheme_bits+1,0,7*sizeof(*_chooser->scheme_bits)); + for(si=0;si<8;si++){ + /*Scheme 7 should always start first, and scheme 0 should always start + last.*/ + _chooser->scheme_list[si]=7-si; + _chooser->scheme0_list[si]=_chooser->scheme0_ranks[si]=si; + } +} + + +/*This is the real purpose of this data structure: not actually selecting a + mode scheme, but estimating the cost of coding a given mode given all the + modes selected so far. + This is done via opportunity cost: the cost is defined as the number of bits + required to encode all the modes selected so far including the current one + using the best possible scheme, minus the number of bits required to encode + all the modes selected so far not including the current one using the best + possible scheme. + The computational expense of doing this probably makes it overkill. + Just be happy we take a greedy approach instead of trying to solve the + global mode-selection problem (which is NP-hard). + _mb_mode: The mode to determine the cost of. + Return: The number of bits required to code this mode.*/ +static int oc_mode_scheme_chooser_cost(oc_mode_scheme_chooser *_chooser, + int _mb_mode){ + int scheme0; + int scheme1; + int best_bits; + int mode_bits; + int si; + int scheme_bits; + scheme0=_chooser->scheme_list[0]; + scheme1=_chooser->scheme_list[1]; + best_bits=_chooser->scheme_bits[scheme0]; + mode_bits=OC_MODE_BITS[scheme0+1>>3][_chooser->mode_ranks[scheme0][_mb_mode]]; + /*Typical case: If the difference between the best scheme and the next best + is greater than 6 bits, then adding just one mode cannot change which + scheme we use.*/ + if(_chooser->scheme_bits[scheme1]-best_bits>6)return mode_bits; + /*Otherwise, check to see if adding this mode selects a different scheme as + the best.*/ + si=1; + best_bits+=mode_bits; + do{ + /*For any scheme except 0, we can just use the bit cost of the mode's rank + in that scheme.*/ + if(scheme1!=0){ + scheme_bits=_chooser->scheme_bits[scheme1]+ + OC_MODE_BITS[scheme1+1>>3][_chooser->mode_ranks[scheme1][_mb_mode]]; + } + else{ + int ri; + /*For scheme 0, incrementing the mode count could potentially change the + mode's rank. + Find the index where the mode would be moved to in the optimal list, + and use its bit cost instead of the one for the mode's current + position in the list.*/ + /*We don't recompute scheme bits; this is computing opportunity cost, not + an update.*/ + for(ri=_chooser->scheme0_ranks[_mb_mode];ri>0&& + _chooser->mode_counts[_mb_mode]>= + _chooser->mode_counts[_chooser->scheme0_list[ri-1]];ri--); + scheme_bits=_chooser->scheme_bits[0]+OC_MODE_BITS[0][ri]; + } + if(scheme_bits=8)break; + scheme1=_chooser->scheme_list[si]; + } + while(_chooser->scheme_bits[scheme1]-_chooser->scheme_bits[scheme0]<=6); + return best_bits-_chooser->scheme_bits[scheme0]; +} + +/*Incrementally update the mode counts and per-scheme bit counts and re-order + the scheme lists once a mode has been selected. + _mb_mode: The mode that was chosen.*/ +static void oc_mode_scheme_chooser_update(oc_mode_scheme_chooser *_chooser, + int _mb_mode){ + int ri; + int si; + _chooser->mode_counts[_mb_mode]++; + /*Re-order the scheme0 mode list if necessary.*/ + for(ri=_chooser->scheme0_ranks[_mb_mode];ri>0;ri--){ + int pmode; + pmode=_chooser->scheme0_list[ri-1]; + if(_chooser->mode_counts[pmode]>=_chooser->mode_counts[_mb_mode])break; + /*Reorder the mode ranking.*/ + _chooser->scheme0_ranks[pmode]++; + _chooser->scheme0_list[ri]=pmode; + } + _chooser->scheme0_ranks[_mb_mode]=ri; + _chooser->scheme0_list[ri]=_mb_mode; + /*Now add the bit cost for the mode to each scheme.*/ + for(si=0;si<8;si++){ + _chooser->scheme_bits[si]+= + OC_MODE_BITS[si+1>>3][_chooser->mode_ranks[si][_mb_mode]]; + } + /*Finally, re-order the list of schemes.*/ + for(si=1;si<8;si++){ + int sj; + int scheme0; + int bits0; + sj=si; + scheme0=_chooser->scheme_list[si]; + bits0=_chooser->scheme_bits[scheme0]; + do{ + int scheme1; + scheme1=_chooser->scheme_list[sj-1]; + if(bits0>=_chooser->scheme_bits[scheme1])break; + _chooser->scheme_list[sj]=scheme1; + } + while(--sj>0); + _chooser->scheme_list[sj]=scheme0; + } +} + + + +/*The number of bits required to encode a super block run. + _run_count: The desired run count; must be positive and less than 4130.*/ +static int oc_sb_run_bits(int _run_count){ + int i; + for(i=0;_run_count>=OC_SB_RUN_VAL_MIN[i+1];i++); + return OC_SB_RUN_CODE_NBITS[i]; +} + +/*The number of bits required to encode a block run. + _run_count: The desired run count; must be positive and less than 30.*/ +static int oc_block_run_bits(int _run_count){ + return OC_BLOCK_RUN_CODE_NBITS[_run_count-1]; +} + + + +/*State to track coded block flags and their bit cost.*/ +struct oc_fr_state{ + ptrdiff_t bits; + unsigned sb_partial_count:16; + unsigned sb_full_count:16; + unsigned b_coded_count_prev:8; + unsigned b_coded_count:8; + unsigned b_count:8; + signed int sb_partial:2; + signed int sb_full:2; + signed int b_coded_prev:2; + signed int b_coded:2; +}; + + + +static void oc_fr_state_init(oc_fr_state *_fr){ + _fr->bits=0; + _fr->sb_partial_count=0; + _fr->sb_full_count=0; + _fr->b_coded_count_prev=0; + _fr->b_coded_count=0; + _fr->b_count=0; + _fr->sb_partial=-1; + _fr->sb_full=-1; + _fr->b_coded_prev=-1; + _fr->b_coded=-1; +} + + +static void oc_fr_state_advance_sb(oc_fr_state *_fr, + int _sb_partial,int _sb_full){ + ptrdiff_t bits; + int sb_partial_count; + int sb_full_count; + bits=_fr->bits; + /*Extend the sb_partial run, or start a new one.*/ + sb_partial_count=_fr->sb_partial; + if(_fr->sb_partial==_sb_partial){ + if(sb_partial_count>=4129){ + bits++; + sb_partial_count=0; + } + else bits-=oc_sb_run_bits(sb_partial_count); + } + else sb_partial_count=0; + sb_partial_count++; + bits+=oc_sb_run_bits(sb_partial_count); + if(!_sb_partial){ + /*Extend the sb_full run, or start a new one.*/ + sb_full_count=_fr->sb_full_count; + if(_fr->sb_full==_sb_full){ + if(sb_full_count>=4129){ + bits++; + sb_full_count=0; + } + else bits-=oc_sb_run_bits(sb_full_count); + } + else sb_full_count=0; + sb_full_count++; + bits+=oc_sb_run_bits(sb_full_count); + _fr->sb_full=_sb_full; + _fr->sb_full_count=sb_full_count; + } + _fr->bits=bits; + _fr->sb_partial=_sb_partial; + _fr->sb_partial_count=sb_partial_count; +} + +/*Flush any outstanding block flags for a SB (e.g., one with fewer than 16 + blocks).*/ +static void oc_fr_state_flush_sb(oc_fr_state *_fr){ + ptrdiff_t bits; + int sb_partial; + int sb_full=sb_full; + int b_coded_count; + int b_coded; + int b_count; + b_count=_fr->b_count; + if(b_count>0){ + bits=_fr->bits; + b_coded=_fr->b_coded; + b_coded_count=_fr->b_coded_count; + if(b_coded_count>=b_count){ + /*This SB was fully coded/uncoded; roll back the partial block flags.*/ + bits-=oc_block_run_bits(b_coded_count); + if(b_coded_count>b_count)bits+=oc_block_run_bits(b_coded_count-b_count); + sb_partial=0; + sb_full=b_coded; + b_coded=_fr->b_coded_prev; + b_coded_count=_fr->b_coded_count_prev; + } + else{ + /*It was partially coded.*/ + sb_partial=1; + /*sb_full is unused.*/ + } + _fr->bits=bits; + _fr->b_coded_count=b_coded_count; + _fr->b_coded_count_prev=b_coded_count; + _fr->b_count=0; + _fr->b_coded=b_coded; + _fr->b_coded_prev=b_coded; + oc_fr_state_advance_sb(_fr,sb_partial,sb_full); + } +} + +static void oc_fr_state_advance_block(oc_fr_state *_fr,int _b_coded){ + ptrdiff_t bits; + int b_coded_count; + int b_count; + int sb_partial; + int sb_full=sb_full; + bits=_fr->bits; + /*Extend the b_coded run, or start a new one.*/ + b_coded_count=_fr->b_coded_count; + if(_fr->b_coded==_b_coded)bits-=oc_block_run_bits(b_coded_count); + else b_coded_count=0; + b_coded_count++; + b_count=_fr->b_count+1; + if(b_count>=16){ + /*We finished a superblock.*/ + if(b_coded_count>=16){ + /*It was fully coded/uncoded; roll back the partial block flags.*/ + if(b_coded_count>16)bits+=oc_block_run_bits(b_coded_count-16); + sb_partial=0; + sb_full=_b_coded; + _b_coded=_fr->b_coded_prev; + b_coded_count=_fr->b_coded_count_prev; + } + else{ + bits+=oc_block_run_bits(b_coded_count); + /*It was partially coded.*/ + sb_partial=1; + /*sb_full is unused.*/ + } + _fr->bits=bits; + _fr->b_coded_count=b_coded_count; + _fr->b_coded_count_prev=b_coded_count; + _fr->b_count=0; + _fr->b_coded=_b_coded; + _fr->b_coded_prev=_b_coded; + oc_fr_state_advance_sb(_fr,sb_partial,sb_full); + } + else{ + bits+=oc_block_run_bits(b_coded_count); + _fr->bits=bits; + _fr->b_coded_count=b_coded_count; + _fr->b_count=b_count; + _fr->b_coded=_b_coded; + } +} + +static void oc_fr_skip_block(oc_fr_state *_fr){ + oc_fr_state_advance_block(_fr,0); +} + +static void oc_fr_code_block(oc_fr_state *_fr){ + oc_fr_state_advance_block(_fr,1); +} + +static int oc_fr_cost1(const oc_fr_state *_fr){ + oc_fr_state tmp; + ptrdiff_t bits; + *&tmp=*_fr; + oc_fr_skip_block(&tmp); + bits=tmp.bits; + *&tmp=*_fr; + oc_fr_code_block(&tmp); + return (int)(tmp.bits-bits); +} + +static int oc_fr_cost4(const oc_fr_state *_pre,const oc_fr_state *_post){ + oc_fr_state tmp; + *&tmp=*_pre; + oc_fr_skip_block(&tmp); + oc_fr_skip_block(&tmp); + oc_fr_skip_block(&tmp); + oc_fr_skip_block(&tmp); + return (int)(_post->bits-tmp.bits); +} + + + +struct oc_qii_state{ + ptrdiff_t bits; + unsigned qi01_count:14; + signed int qi01:2; + unsigned qi12_count:14; + signed int qi12:2; +}; + + + +static void oc_qii_state_init(oc_qii_state *_qs){ + _qs->bits=0; + _qs->qi01_count=0; + _qs->qi01=-1; + _qs->qi12_count=0; + _qs->qi12=-1; +} + + +static void oc_qii_state_advance(oc_qii_state *_qd, + const oc_qii_state *_qs,int _qii){ + ptrdiff_t bits; + int qi01; + int qi01_count; + int qi12; + int qi12_count; + bits=_qs->bits; + qi01=_qii+1>>1; + qi01_count=_qs->qi01_count; + if(qi01==_qs->qi01){ + if(qi01_count>=4129){ + bits++; + qi01_count=0; + } + else bits-=oc_sb_run_bits(qi01_count); + } + else qi01_count=0; + qi01_count++; + bits+=oc_sb_run_bits(qi01_count); + qi12_count=_qs->qi12_count; + if(_qii){ + qi12=_qii>>1; + if(qi12==_qs->qi12){ + if(qi12_count>=4129){ + bits++; + qi12_count=0; + } + else bits-=oc_sb_run_bits(qi12_count); + } + else qi12_count=0; + qi12_count++; + bits+=oc_sb_run_bits(qi12_count); + } + else qi12=_qs->qi12; + _qd->bits=bits; + _qd->qi01=qi01; + _qd->qi01_count=qi01_count; + _qd->qi12=qi12; + _qd->qi12_count=qi12_count; +} + + + +/*Temporary encoder state for the analysis pipeline.*/ +struct oc_enc_pipeline_state{ + int bounding_values[256]; + oc_fr_state fr[3]; + oc_qii_state qs[3]; + /*Condensed dequantization tables.*/ + const ogg_uint16_t *dequant[3][3][2]; + /*Condensed quantization tables.*/ + const oc_iquant *enquant[3][3][2]; + /*Skip SSD storage for the current MCU in each plane.*/ + unsigned *skip_ssd[3]; + /*Coded/uncoded fragment lists for each plane for the current MCU.*/ + ptrdiff_t *coded_fragis[3]; + ptrdiff_t *uncoded_fragis[3]; + ptrdiff_t ncoded_fragis[3]; + ptrdiff_t nuncoded_fragis[3]; + /*The starting fragment for the current MCU in each plane.*/ + ptrdiff_t froffset[3]; + /*The starting row for the current MCU in each plane.*/ + int fragy0[3]; + /*The ending row for the current MCU in each plane.*/ + int fragy_end[3]; + /*The starting superblock for the current MCU in each plane.*/ + unsigned sbi0[3]; + /*The ending superblock for the current MCU in each plane.*/ + unsigned sbi_end[3]; + /*The number of tokens for zzi=1 for each color plane.*/ + int ndct_tokens1[3]; + /*The outstanding eob_run count for zzi=1 for each color plane.*/ + int eob_run1[3]; + /*Whether or not the loop filter is enabled.*/ + int loop_filter; +}; + + +static void oc_enc_pipeline_init(oc_enc_ctx *_enc,oc_enc_pipeline_state *_pipe){ + ptrdiff_t *coded_fragis; + unsigned mcu_nvsbs; + ptrdiff_t mcu_nfrags; + int hdec; + int vdec; + int pli; + int qii; + int qti; + /*Initialize the per-plane coded block flag trackers. + These are used for bit-estimation purposes only; the real flag bits span + all three planes, so we can't compute them in parallel.*/ + for(pli=0;pli<3;pli++)oc_fr_state_init(_pipe->fr+pli); + for(pli=0;pli<3;pli++)oc_qii_state_init(_pipe->qs+pli); + /*Set up the per-plane skip SSD storage pointers.*/ + mcu_nvsbs=_enc->mcu_nvsbs; + mcu_nfrags=mcu_nvsbs*_enc->state.fplanes[0].nhsbs*16; + hdec=!(_enc->state.info.pixel_fmt&1); + vdec=!(_enc->state.info.pixel_fmt&2); + _pipe->skip_ssd[0]=_enc->mcu_skip_ssd; + _pipe->skip_ssd[1]=_pipe->skip_ssd[0]+mcu_nfrags; + _pipe->skip_ssd[2]=_pipe->skip_ssd[1]+(mcu_nfrags>>hdec+vdec); + /*Set up per-plane pointers to the coded and uncoded fragments lists. + Unlike the decoder, each planes' coded and uncoded fragment list is kept + separate during the analysis stage; we only make the coded list for all + three planes contiguous right before the final packet is output + (destroying the uncoded lists, which are no longer needed).*/ + coded_fragis=_enc->state.coded_fragis; + for(pli=0;pli<3;pli++){ + _pipe->coded_fragis[pli]=coded_fragis; + coded_fragis+=_enc->state.fplanes[pli].nfrags; + _pipe->uncoded_fragis[pli]=coded_fragis; + } + memset(_pipe->ncoded_fragis,0,sizeof(_pipe->ncoded_fragis)); + memset(_pipe->nuncoded_fragis,0,sizeof(_pipe->nuncoded_fragis)); + /*Set up condensed quantizer tables.*/ + for(pli=0;pli<3;pli++){ + for(qii=0;qii<_enc->state.nqis;qii++){ + int qi; + qi=_enc->state.qis[qii]; + for(qti=0;qti<2;qti++){ + _pipe->dequant[pli][qii][qti]=_enc->state.dequant_tables[qi][pli][qti]; + _pipe->enquant[pli][qii][qti]=_enc->enquant_tables[qi][pli][qti]; + } + } + } + /*Initialize the tokenization state.*/ + for(pli=0;pli<3;pli++){ + _pipe->ndct_tokens1[pli]=0; + _pipe->eob_run1[pli]=0; + } + /*Initialize the bounding value array for the loop filter.*/ + _pipe->loop_filter=!oc_state_loop_filter_init(&_enc->state, + _pipe->bounding_values); +} + +/*Sets the current MCU stripe to super block row _sby. + Return: A non-zero value if this was the last MCU.*/ +static int oc_enc_pipeline_set_stripe(oc_enc_ctx *_enc, + oc_enc_pipeline_state *_pipe,int _sby){ + const oc_fragment_plane *fplane; + unsigned mcu_nvsbs; + int sby_end; + int notdone; + int vdec; + int pli; + mcu_nvsbs=_enc->mcu_nvsbs; + sby_end=_enc->state.fplanes[0].nvsbs; + notdone=_sby+mcu_nvsbsstate.fplanes+pli; + _pipe->sbi0[pli]=fplane->sboffset+(_sby>>vdec)*fplane->nhsbs; + _pipe->fragy0[pli]=_sby<<2-vdec; + _pipe->froffset[pli]=fplane->froffset + +_pipe->fragy0[pli]*(ptrdiff_t)fplane->nhfrags; + if(notdone){ + _pipe->sbi_end[pli]=fplane->sboffset+(sby_end>>vdec)*fplane->nhsbs; + _pipe->fragy_end[pli]=sby_end<<2-vdec; + } + else{ + _pipe->sbi_end[pli]=fplane->sboffset+fplane->nsbs; + _pipe->fragy_end[pli]=fplane->nvfrags; + } + vdec=!(_enc->state.info.pixel_fmt&2); + } + return notdone; +} + +static void oc_enc_pipeline_finish_mcu_plane(oc_enc_ctx *_enc, + oc_enc_pipeline_state *_pipe,int _pli,int _sdelay,int _edelay){ + int refi; + /*Copy over all the uncoded fragments from this plane and advance the uncoded + fragment list.*/ + _pipe->uncoded_fragis[_pli]-=_pipe->nuncoded_fragis[_pli]; + oc_state_frag_copy_list(&_enc->state,_pipe->uncoded_fragis[_pli], + _pipe->nuncoded_fragis[_pli],OC_FRAME_SELF,OC_FRAME_PREV,_pli); + _pipe->nuncoded_fragis[_pli]=0; + /*Perform DC prediction.*/ + oc_enc_pred_dc_frag_rows(_enc,_pli, + _pipe->fragy0[_pli],_pipe->fragy_end[_pli]); + /*Finish DC tokenization.*/ + oc_enc_tokenize_dc_frag_list(_enc,_pli, + _pipe->coded_fragis[_pli],_pipe->ncoded_fragis[_pli], + _pipe->ndct_tokens1[_pli],_pipe->eob_run1[_pli]); + _pipe->ndct_tokens1[_pli]=_enc->ndct_tokens[_pli][1]; + _pipe->eob_run1[_pli]=_enc->eob_run[_pli][1]; + /*And advance the coded fragment list.*/ + _enc->state.ncoded_fragis[_pli]+=_pipe->ncoded_fragis[_pli]; + _pipe->coded_fragis[_pli]+=_pipe->ncoded_fragis[_pli]; + _pipe->ncoded_fragis[_pli]=0; + /*Apply the loop filter if necessary.*/ + refi=_enc->state.ref_frame_idx[OC_FRAME_SELF]; + if(_pipe->loop_filter){ + oc_state_loop_filter_frag_rows(&_enc->state,_pipe->bounding_values, + refi,_pli,_pipe->fragy0[_pli]-_sdelay,_pipe->fragy_end[_pli]-_edelay); + } + else _sdelay=_edelay=0; + /*To fill borders, we have an additional two pixel delay, since a fragment + in the next row could filter its top edge, using two pixels from a + fragment in this row. + But there's no reason to delay a full fragment between the two.*/ + oc_state_borders_fill_rows(&_enc->state,refi,_pli, + (_pipe->fragy0[_pli]-_sdelay<<3)-(_sdelay<<1), + (_pipe->fragy_end[_pli]-_edelay<<3)-(_edelay<<1)); +} + + + +/*Cost information about the coded blocks in a MB.*/ +struct oc_rd_metric{ + int uncoded_ac_ssd; + int coded_ac_ssd; + int ac_bits; + int dc_flag; +}; + + + +static int oc_enc_block_transform_quantize(oc_enc_ctx *_enc, + oc_enc_pipeline_state *_pipe,int _pli,ptrdiff_t _fragi,int _overhead_bits, + oc_rd_metric *_mo,oc_token_checkpoint **_stack){ + OC_ALIGN16(ogg_int16_t dct[64]); + OC_ALIGN16(ogg_int16_t data[64]); + ogg_uint16_t dc_dequant; + const ogg_uint16_t *dequant; + const oc_iquant *enquant; + ptrdiff_t frag_offs; + int ystride; + const unsigned char *src; + const unsigned char *ref; + unsigned char *dst; + int frame_type; + int nonzero; + unsigned uncoded_ssd; + unsigned coded_ssd; + int coded_dc; + oc_token_checkpoint *checkpoint; + oc_fragment *frags; + int mb_mode; + int mv_offs[2]; + int nmv_offs; + int ac_bits; + int borderi; + int qti; + int qii; + int pi; + int zzi; + int v; + int val; + int d; + int s; + int dc; + frags=_enc->state.frags; + frag_offs=_enc->state.frag_buf_offs[_fragi]; + ystride=_enc->state.ref_ystride[_pli]; + src=_enc->state.ref_frame_data[OC_FRAME_IO]+frag_offs; + borderi=frags[_fragi].borderi; + qii=frags[_fragi].qii; + if(qii&~3){ +#if !defined(OC_COLLECT_METRICS) + if(_enc->sp_level>=OC_SP_LEVEL_EARLY_SKIP){ + /*Enable early skip detection.*/ + frags[_fragi].coded=0; + return 0; + } +#endif + /*Try and code this block anyway.*/ + qii&=3; + frags[_fragi].qii=qii; + } + mb_mode=frags[_fragi].mb_mode; + ref=_enc->state.ref_frame_data[ + _enc->state.ref_frame_idx[OC_FRAME_FOR_MODE(mb_mode)]]+frag_offs; + dst=_enc->state.ref_frame_data[_enc->state.ref_frame_idx[OC_FRAME_SELF]] + +frag_offs; + /*Motion compensation:*/ + switch(mb_mode){ + case OC_MODE_INTRA:{ + nmv_offs=0; + oc_enc_frag_sub_128(_enc,data,src,ystride); + }break; + case OC_MODE_GOLDEN_NOMV: + case OC_MODE_INTER_NOMV:{ + nmv_offs=1; + mv_offs[0]=0; + oc_enc_frag_sub(_enc,data,src,ref,ystride); + }break; + default:{ + const oc_mv *frag_mvs; + frag_mvs=(const oc_mv *)_enc->state.frag_mvs; + nmv_offs=oc_state_get_mv_offsets(&_enc->state,mv_offs,_pli, + frag_mvs[_fragi][0],frag_mvs[_fragi][1]); + if(nmv_offs>1){ + oc_enc_frag_copy2(_enc,dst, + ref+mv_offs[0],ref+mv_offs[1],ystride); + oc_enc_frag_sub(_enc,data,src,dst,ystride); + } + else oc_enc_frag_sub(_enc,data,src,ref+mv_offs[0],ystride); + }break; + } +#if defined(OC_COLLECT_METRICS) + { + unsigned satd; + switch(nmv_offs){ + case 0:satd=oc_enc_frag_intra_satd(_enc,src,ystride);break; + case 1:{ + satd=oc_enc_frag_satd_thresh(_enc,src,ref+mv_offs[0],ystride,UINT_MAX); + }break; + default:{ + satd=oc_enc_frag_satd_thresh(_enc,src,dst,ystride,UINT_MAX); + } + } + _enc->frag_satd[_fragi]=satd; + } +#endif + /*Transform:*/ + oc_enc_fdct8x8(_enc,dct,data); + /*Quantize the DC coefficient:*/ + qti=mb_mode!=OC_MODE_INTRA; + enquant=_pipe->enquant[_pli][0][qti]; + dc_dequant=_pipe->dequant[_pli][0][qti][0]; + v=dct[0]; + val=v<<1; + s=OC_SIGNMASK(val); + val+=dc_dequant+s^s; + val=((enquant[0].m*(ogg_int32_t)val>>16)+val>>enquant[0].l)-s; + dc=OC_CLAMPI(-580,val,580); + nonzero=0; + /*Quantize the AC coefficients:*/ + dequant=_pipe->dequant[_pli][qii][qti]; + enquant=_pipe->enquant[_pli][qii][qti]; + for(zzi=1;zzi<64;zzi++){ + v=dct[OC_FZIG_ZAG[zzi]]; + d=dequant[zzi]; + val=v<<1; + v=abs(val); + if(v>=d){ + s=OC_SIGNMASK(val); + /*The bias added here rounds ties away from zero, since token + optimization can only decrease the magnitude of the quantized + value.*/ + val+=d+s^s; + /*Note the arithmetic right shift is not guaranteed by ANSI C. + Hopefully no one still uses ones-complement architectures.*/ + val=((enquant[zzi].m*(ogg_int32_t)val>>16)+val>>enquant[zzi].l)-s; + data[zzi]=OC_CLAMPI(-580,val,580); + nonzero=zzi; + } + else data[zzi]=0; + } + /*Tokenize.*/ + checkpoint=*_stack; + ac_bits=oc_enc_tokenize_ac(_enc,_pli,_fragi,data,dequant,dct,nonzero+1, + _stack,qti?0:3); + /*Reconstruct. + TODO: nonzero may need to be adjusted after tokenization.*/ + if(nonzero==0){ + ogg_int16_t p; + int ci; + /*We round this dequant product (and not any of the others) because there's + no iDCT rounding.*/ + p=(ogg_int16_t)(dc*(ogg_int32_t)dc_dequant+15>>5); + /*LOOP VECTORIZES.*/ + for(ci=0;ci<64;ci++)data[ci]=p; + } + else{ + data[0]=dc*dc_dequant; + oc_idct8x8(&_enc->state,data,nonzero+1); + } + if(!qti)oc_enc_frag_recon_intra(_enc,dst,ystride,data); + else{ + oc_enc_frag_recon_inter(_enc,dst, + nmv_offs==1?ref+mv_offs[0]:dst,ystride,data); + } + frame_type=_enc->state.frame_type; +#if !defined(OC_COLLECT_METRICS) + if(frame_type!=OC_INTRA_FRAME) +#endif + { + /*In retrospect, should we have skipped this block?*/ + oc_enc_frag_sub(_enc,data,src,dst,ystride); + coded_ssd=coded_dc=0; + if(borderi<0){ + for(pi=0;pi<64;pi++){ + coded_ssd+=data[pi]*data[pi]; + coded_dc+=data[pi]; + } + } + else{ + ogg_int64_t mask; + mask=_enc->state.borders[borderi].mask; + for(pi=0;pi<64;pi++,mask>>=1)if(mask&1){ + coded_ssd+=data[pi]*data[pi]; + coded_dc+=data[pi]; + } + } + /*Scale to match DCT domain.*/ + coded_ssd<<=4; + /*We actually only want the AC contribution to the SSD.*/ + coded_ssd-=coded_dc*coded_dc>>2; +#if defined(OC_COLLECT_METRICS) + _enc->frag_ssd[_fragi]=coded_ssd; + } + if(frame_type!=OC_INTRA_FRAME){ +#endif + uncoded_ssd=_pipe->skip_ssd[_pli][_fragi-_pipe->froffset[_pli]]; + if(uncoded_ssdlambda&& + /*Don't allow luma blocks to be skipped in 4MV mode when VP3 + compatibility is enabled.*/ + (!_enc->vp3_compatible||mb_mode!=OC_MODE_INTER_MV_FOUR||_pli)){ + /*Hm, not worth it; roll back.*/ + oc_enc_tokenlog_rollback(_enc,checkpoint,(*_stack)-checkpoint); + *_stack=checkpoint; + frags[_fragi].coded=0; + return 0; + } + } + else _mo->dc_flag=1; + _mo->uncoded_ac_ssd+=uncoded_ssd; + _mo->coded_ac_ssd+=coded_ssd; + _mo->ac_bits+=ac_bits; + } + oc_qii_state_advance(_pipe->qs+_pli,_pipe->qs+_pli,qii); + frags[_fragi].dc=dc; + frags[_fragi].coded=1; + return 1; +} + +static int oc_enc_mb_transform_quantize_luma(oc_enc_ctx *_enc, + oc_enc_pipeline_state *_pipe,unsigned _mbi,int _mode_overhead){ + /*Worst case token stack usage for 4 fragments.*/ + oc_token_checkpoint stack[64*4]; + oc_token_checkpoint *stackptr; + const oc_sb_map *sb_maps; + signed char *mb_modes; + oc_fragment *frags; + ptrdiff_t *coded_fragis; + ptrdiff_t ncoded_fragis; + ptrdiff_t *uncoded_fragis; + ptrdiff_t nuncoded_fragis; + oc_rd_metric mo; + oc_fr_state fr_checkpoint; + oc_qii_state qs_checkpoint; + int mb_mode; + int ncoded; + ptrdiff_t fragi; + int bi; + *&fr_checkpoint=*(_pipe->fr+0); + *&qs_checkpoint=*(_pipe->qs+0); + sb_maps=(const oc_sb_map *)_enc->state.sb_maps; + mb_modes=_enc->state.mb_modes; + frags=_enc->state.frags; + coded_fragis=_pipe->coded_fragis[0]; + ncoded_fragis=_pipe->ncoded_fragis[0]; + uncoded_fragis=_pipe->uncoded_fragis[0]; + nuncoded_fragis=_pipe->nuncoded_fragis[0]; + mb_mode=mb_modes[_mbi]; + ncoded=0; + stackptr=stack; + memset(&mo,0,sizeof(mo)); + for(bi=0;bi<4;bi++){ + fragi=sb_maps[_mbi>>2][_mbi&3][bi]; + frags[fragi].mb_mode=mb_mode; + if(oc_enc_block_transform_quantize(_enc, + _pipe,0,fragi,oc_fr_cost1(_pipe->fr+0),&mo,&stackptr)){ + oc_fr_code_block(_pipe->fr+0); + coded_fragis[ncoded_fragis++]=fragi; + ncoded++; + } + else{ + *(uncoded_fragis-++nuncoded_fragis)=fragi; + oc_fr_skip_block(_pipe->fr+0); + } + } + if(_enc->state.frame_type!=OC_INTRA_FRAME){ + if(ncoded>0&&!mo.dc_flag){ + int cost; + /*Some individual blocks were worth coding. + See if that's still true when accounting for mode and MV overhead.*/ + cost=mo.coded_ac_ssd+_enc->lambda*(mo.ac_bits + +oc_fr_cost4(&fr_checkpoint,_pipe->fr+0)+_mode_overhead); + if(mo.uncoded_ac_ssd<=cost){ + /*Taking macroblock overhead into account, it is not worth coding this + MB.*/ + oc_enc_tokenlog_rollback(_enc,stack,stackptr-stack); + *(_pipe->fr+0)=*&fr_checkpoint; + *(_pipe->qs+0)=*&qs_checkpoint; + for(bi=0;bi<4;bi++){ + fragi=sb_maps[_mbi>>2][_mbi&3][bi]; + if(frags[fragi].coded){ + *(uncoded_fragis-++nuncoded_fragis)=fragi; + frags[fragi].coded=0; + } + oc_fr_skip_block(_pipe->fr+0); + } + ncoded_fragis-=ncoded; + ncoded=0; + } + } + /*If no luma blocks coded, the mode is forced.*/ + if(ncoded==0)mb_modes[_mbi]=OC_MODE_INTER_NOMV; + /*Assume that a 1MV with a single coded block is always cheaper than a 4MV + with a single coded block. + This may not be strictly true: a 4MV computes chroma MVs using (0,0) for + skipped blocks, while a 1MV does not.*/ + else if(ncoded==1&&mb_mode==OC_MODE_INTER_MV_FOUR){ + mb_modes[_mbi]=OC_MODE_INTER_MV; + } + } + _pipe->ncoded_fragis[0]=ncoded_fragis; + _pipe->nuncoded_fragis[0]=nuncoded_fragis; + return ncoded; +} + +static void oc_enc_sb_transform_quantize_chroma(oc_enc_ctx *_enc, + oc_enc_pipeline_state *_pipe,int _pli,int _sbi_start,int _sbi_end){ + const oc_sb_map *sb_maps; + oc_sb_flags *sb_flags; + ptrdiff_t *coded_fragis; + ptrdiff_t ncoded_fragis; + ptrdiff_t *uncoded_fragis; + ptrdiff_t nuncoded_fragis; + int sbi; + sb_maps=(const oc_sb_map *)_enc->state.sb_maps; + sb_flags=_enc->state.sb_flags; + coded_fragis=_pipe->coded_fragis[_pli]; + ncoded_fragis=_pipe->ncoded_fragis[_pli]; + uncoded_fragis=_pipe->uncoded_fragis[_pli]; + nuncoded_fragis=_pipe->nuncoded_fragis[_pli]; + for(sbi=_sbi_start;sbi<_sbi_end;sbi++){ + /*Worst case token stack usage for 1 fragment.*/ + oc_token_checkpoint stack[64]; + oc_rd_metric mo; + int quadi; + int bi; + memset(&mo,0,sizeof(mo)); + for(quadi=0;quadi<4;quadi++)for(bi=0;bi<4;bi++){ + ptrdiff_t fragi; + fragi=sb_maps[sbi][quadi][bi]; + if(fragi>=0){ + oc_token_checkpoint *stackptr; + stackptr=stack; + if(oc_enc_block_transform_quantize(_enc, + _pipe,_pli,fragi,oc_fr_cost1(_pipe->fr+_pli),&mo,&stackptr)){ + coded_fragis[ncoded_fragis++]=fragi; + oc_fr_code_block(_pipe->fr+_pli); + } + else{ + *(uncoded_fragis-++nuncoded_fragis)=fragi; + oc_fr_skip_block(_pipe->fr+_pli); + } + } + } + oc_fr_state_flush_sb(_pipe->fr+_pli); + sb_flags[sbi].coded_fully=_pipe->fr[_pli].sb_full; + sb_flags[sbi].coded_partially=_pipe->fr[_pli].sb_partial; + } + _pipe->ncoded_fragis[_pli]=ncoded_fragis; + _pipe->nuncoded_fragis[_pli]=nuncoded_fragis; +} + +/*Mode decision is done by exhaustively examining all potential choices. + Obviously, doing the motion compensation, fDCT, tokenization, and then + counting the bits each token uses is computationally expensive. + Theora's EOB runs can also split the cost of these tokens across multiple + fragments, and naturally we don't know what the optimal choice of Huffman + codes will be until we know all the tokens we're going to encode in all the + fragments. + So we use a simple approach to estimating the bit cost and distortion of each + mode based upon the SATD value of the residual before coding. + The mathematics behind the technique are outlined by Kim \cite{Kim03}, but + the process (modified somewhat from that of the paper) is very simple. + We build a non-linear regression of the mappings from + (pre-transform+quantization) SATD to (post-transform+quantization) bits and + SSD for each qi. + A separate set of mappings is kept for each quantization type and color + plane. + The mappings are constructed by partitioning the SATD values into a small + number of bins (currently 24) and using a linear regression in each bin + (as opposed to the 0th-order regression used by Kim). + The bit counts and SSD measurements are obtained by examining actual encoded + frames, with appropriate lambda values and optimal Huffman codes selected. + EOB bits are assigned to the fragment that started the EOB run (as opposed to + dividing them among all the blocks in the run; though the latter approach + seems more theoretically correct, Monty's testing showed a small improvement + with the former, though that may have been merely statistical noise). + + @ARTICLE{Kim03, + author="Hyun Mun Kim", + title="Adaptive Rate Control Using Nonlinear Regression", + journal="IEEE Transactions on Circuits and Systems for Video Technology", + volume=13, + number=5, + pages="432--439", + month=May, + year=2003 + }*/ + +/*Computes (_ssd+_lambda*_rate)/(1<>OC_BIT_SCALE)+((_rate)>>OC_BIT_SCALE)*(_lambda) \ + +(((_ssd)&(1<>1)>>OC_BIT_SCALE) + +/*Estimate the R-D cost of the DCT coefficients given the SATD of a block after + prediction.*/ +static unsigned oc_dct_cost2(unsigned *_ssd, + int _qi,int _pli,int _qti,int _satd){ + unsigned rmse; + int bin; + int dx; + int y0; + int z0; + int dy; + int dz; + /*SATD metrics for chroma planes vary much less than luma, so we scale them + by 4 to distribute them into the mode decision bins more evenly.*/ + _satd<<=_pli+1&2; + bin=OC_MINI(_satd>>OC_SAD_SHIFT,OC_SAD_BINS-2); + dx=_satd-(bin<>OC_SAD_SHIFT),0); + *_ssd=rmse*rmse>>2*OC_RMSE_SCALE-OC_BIT_SCALE; + return OC_MAXI(y0+(dy*dx>>OC_SAD_SHIFT),0); +} + +/*Select luma block-level quantizers for a MB in an INTRA frame.*/ +static unsigned oc_analyze_intra_mb_luma(oc_enc_ctx *_enc, + const oc_qii_state *_qs,unsigned _mbi){ + const unsigned char *src; + const ptrdiff_t *frag_buf_offs; + const oc_sb_map *sb_maps; + oc_fragment *frags; + ptrdiff_t frag_offs; + ptrdiff_t fragi; + oc_qii_state qs[4][3]; + unsigned cost[4][3]; + unsigned ssd[4][3]; + unsigned rate[4][3]; + int prev[3][3]; + unsigned satd; + unsigned best_cost; + unsigned best_ssd; + unsigned best_rate; + int best_qii; + int qii; + int lambda; + int ystride; + int nqis; + int bi; + frag_buf_offs=_enc->state.frag_buf_offs; + sb_maps=(const oc_sb_map *)_enc->state.sb_maps; + src=_enc->state.ref_frame_data[OC_FRAME_IO]; + ystride=_enc->state.ref_ystride[0]; + fragi=sb_maps[_mbi>>2][_mbi&3][0]; + frag_offs=frag_buf_offs[fragi]; + satd=oc_enc_frag_intra_satd(_enc,src+frag_offs,ystride); + nqis=_enc->state.nqis; + lambda=_enc->lambda; + for(qii=0;qiistate.qis[qii],0,0,satd) + +(qs[0][qii].bits-_qs->bits<>2][_mbi&3][bi]; + frag_offs=frag_buf_offs[fragi]; + satd=oc_enc_frag_intra_satd(_enc,src+frag_offs,ystride); + for(qii=0;qiistate.qis[qii],0,0,satd); + best_ssd=ssd[bi-1][0]+cur_ssd; + best_rate=rate[bi-1][0]+cur_rate + +(qt[0].bits-qs[bi-1][0].bits<state.frags; + for(bi=3;;){ + fragi=sb_maps[_mbi>>2][_mbi&3][bi]; + frags[fragi].qii=best_qii; + if(bi--<=0)break; + best_qii=prev[bi][best_qii]; + } + return best_cost; +} + +/*Select a block-level quantizer for a single chroma block in an INTRA frame.*/ +static unsigned oc_analyze_intra_chroma_block(oc_enc_ctx *_enc, + const oc_qii_state *_qs,int _pli,ptrdiff_t _fragi){ + const unsigned char *src; + oc_fragment *frags; + ptrdiff_t frag_offs; + oc_qii_state qt[3]; + unsigned cost[3]; + unsigned satd; + unsigned best_cost; + int best_qii; + int qii; + int lambda; + int ystride; + int nqis; + src=_enc->state.ref_frame_data[OC_FRAME_IO]; + ystride=_enc->state.ref_ystride[_pli]; + frag_offs=_enc->state.frag_buf_offs[_fragi]; + satd=oc_enc_frag_intra_satd(_enc,src+frag_offs,ystride); + nqis=_enc->state.nqis; + lambda=_enc->lambda; + best_qii=0; + for(qii=0;qiistate.qis[qii],_pli,0,satd) + +(qt[qii].bits-_qs->bits<state.frags; + frags[_fragi].qii=best_qii; + return best_cost; +} + +static void oc_enc_sb_transform_quantize_intra_chroma(oc_enc_ctx *_enc, + oc_enc_pipeline_state *_pipe,int _pli,int _sbi_start,int _sbi_end){ + const oc_sb_map *sb_maps; + oc_sb_flags *sb_flags; + ptrdiff_t *coded_fragis; + ptrdiff_t ncoded_fragis; + int sbi; + sb_maps=(const oc_sb_map *)_enc->state.sb_maps; + sb_flags=_enc->state.sb_flags; + coded_fragis=_pipe->coded_fragis[_pli]; + ncoded_fragis=_pipe->ncoded_fragis[_pli]; + for(sbi=_sbi_start;sbi<_sbi_end;sbi++){ + /*Worst case token stack usage for 1 fragment.*/ + oc_token_checkpoint stack[64]; + int quadi; + int bi; + for(quadi=0;quadi<4;quadi++)for(bi=0;bi<4;bi++){ + ptrdiff_t fragi; + fragi=sb_maps[sbi][quadi][bi]; + if(fragi>=0){ + oc_token_checkpoint *stackptr; + oc_analyze_intra_chroma_block(_enc,_pipe->qs+_pli,_pli,fragi); + stackptr=stack; + oc_enc_block_transform_quantize(_enc, + _pipe,_pli,fragi,0,NULL,&stackptr); + coded_fragis[ncoded_fragis++]=fragi; + } + } + } + _pipe->ncoded_fragis[_pli]=ncoded_fragis; +} + +/*Analysis stage for an INTRA frame.*/ +void oc_enc_analyze_intra(oc_enc_ctx *_enc,int _recode){ + oc_enc_pipeline_state pipe; + const unsigned char *map_idxs; + int nmap_idxs; + oc_sb_flags *sb_flags; + signed char *mb_modes; + const oc_mb_map *mb_maps; + oc_mb_enc_info *embs; + oc_fragment *frags; + unsigned stripe_sby; + unsigned mcu_nvsbs; + int notstart; + int notdone; + int refi; + int pli; + _enc->state.frame_type=OC_INTRA_FRAME; + oc_enc_tokenize_start(_enc); + oc_enc_pipeline_init(_enc,&pipe); + /*Choose MVs and MB modes and quantize and code luma. + Must be done in Hilbert order.*/ + map_idxs=OC_MB_MAP_IDXS[_enc->state.info.pixel_fmt]; + nmap_idxs=OC_MB_MAP_NIDXS[_enc->state.info.pixel_fmt]; + _enc->state.ncoded_fragis[0]=0; + _enc->state.ncoded_fragis[1]=0; + _enc->state.ncoded_fragis[2]=0; + sb_flags=_enc->state.sb_flags; + mb_modes=_enc->state.mb_modes; + mb_maps=(const oc_mb_map *)_enc->state.mb_maps; + embs=_enc->mb_info; + frags=_enc->state.frags; + notstart=0; + notdone=1; + mcu_nvsbs=_enc->mcu_nvsbs; + for(stripe_sby=0;notdone;stripe_sby+=mcu_nvsbs){ + unsigned sbi; + unsigned sbi_end; + notdone=oc_enc_pipeline_set_stripe(_enc,&pipe,stripe_sby); + sbi_end=pipe.sbi_end[0]; + for(sbi=pipe.sbi0[0];sbistate.curframe_num>0)oc_mcenc_search(_enc,mbi); + oc_analyze_intra_mb_luma(_enc,pipe.qs+0,mbi); + mb_modes[mbi]=OC_MODE_INTRA; + oc_enc_mb_transform_quantize_luma(_enc,&pipe,mbi,0); + /*Propagate final MB mode and MVs to the chroma blocks.*/ + for(mapii=4;mapii>2; + bi=mapi&3; + fragi=mb_maps[mbi][pli][bi]; + frags[fragi].mb_mode=OC_MODE_INTRA; + } + } + } + oc_enc_pipeline_finish_mcu_plane(_enc,&pipe,0,notstart,notdone); + /*Code chroma planes.*/ + for(pli=1;pli<3;pli++){ + oc_enc_sb_transform_quantize_intra_chroma(_enc,&pipe, + pli,pipe.sbi0[pli],pipe.sbi_end[pli]); + oc_enc_pipeline_finish_mcu_plane(_enc,&pipe,pli,notstart,notdone); + } + notstart=1; + } + /*Finish filling in the reference frame borders.*/ + refi=_enc->state.ref_frame_idx[OC_FRAME_SELF]; + for(pli=0;pli<3;pli++)oc_state_borders_fill_caps(&_enc->state,refi,pli); + _enc->state.ntotal_coded_fragis=_enc->state.nfrags; +} + + + +/*Cost information about a MB mode.*/ +struct oc_mode_choice{ + unsigned cost; + unsigned ssd; + unsigned rate; + unsigned overhead; + unsigned char qii[12]; +}; + + + +static void oc_mode_set_cost(oc_mode_choice *_modec,int _lambda){ + _modec->cost=OC_MODE_RD_COST(_modec->ssd, + _modec->rate+_modec->overhead,_lambda); +} + +/*A set of skip SSD's to use to disable early skipping.*/ +static const unsigned OC_NOSKIP[12]={ + UINT_MAX,UINT_MAX,UINT_MAX,UINT_MAX, + UINT_MAX,UINT_MAX,UINT_MAX,UINT_MAX, + UINT_MAX,UINT_MAX,UINT_MAX,UINT_MAX +}; + +/*The estimated number of bits used by a coded chroma block to specify the AC + quantizer. + TODO: Currently this is just 0.5*log2(3) (estimating about 50% compression); + measurements suggest this is in the right ballpark, but it varies somewhat + with lambda.*/ +#define OC_CHROMA_QII_RATE ((0xCAE00D1DU>>31-OC_BIT_SCALE)+1>>1) + +static void oc_analyze_mb_mode_luma(oc_enc_ctx *_enc, + oc_mode_choice *_modec,const oc_fr_state *_fr,const oc_qii_state *_qs, + const unsigned _frag_satd[12],const unsigned _skip_ssd[12],int _qti){ + oc_fr_state fr; + oc_qii_state qs; + unsigned ssd; + unsigned rate; + int overhead; + unsigned satd; + unsigned best_ssd; + unsigned best_rate; + int best_overhead; + int best_fri; + int best_qii; + unsigned cur_cost; + unsigned cur_ssd; + unsigned cur_rate; + int cur_overhead; + int lambda; + int nqis; + int nskipped; + int bi; + int qii; + lambda=_enc->lambda; + nqis=_enc->state.nqis; + /*We could do a trellis optimization here, but we don't make final skip + decisions until after transform+quantization, so the result wouldn't be + optimal anyway. + Instead we just use a greedy approach; for most SATD values, the + differences between the qiis are large enough to drown out the cost to + code the flags, anyway.*/ + *&fr=*_fr; + *&qs=*_qs; + ssd=rate=overhead=nskipped=0; + for(bi=0;bi<4;bi++){ + oc_fr_state ft[2]; + oc_qii_state qt[3]; + unsigned best_cost; + satd=_frag_satd[bi]; + *(ft+0)=*&fr; + oc_fr_code_block(ft+0); + oc_qii_state_advance(qt+0,&qs,0); + best_overhead=(ft[0].bits-fr.bits<state.qis[0],0,_qti,satd) + +(qt[0].bits-qs.bits<state.qis[qii],0,_qti,satd) + +(qt[qii].bits-qs.bits<qii[bi]=best_qii; + } + _modec->ssd=ssd; + _modec->rate=rate; + _modec->overhead=OC_MAXI(overhead,0); +} + +static void oc_analyze_mb_mode_chroma(oc_enc_ctx *_enc, + oc_mode_choice *_modec,const oc_fr_state *_fr,const oc_qii_state *_qs, + const unsigned _frag_satd[12],const unsigned _skip_ssd[12],int _qti){ + unsigned ssd; + unsigned rate; + unsigned satd; + unsigned best_ssd; + unsigned best_rate; + int best_qii; + unsigned cur_cost; + unsigned cur_ssd; + unsigned cur_rate; + int lambda; + int nblocks; + int nqis; + int pli; + int bi; + int qii; + lambda=_enc->lambda; + nqis=_enc->state.nqis; + ssd=_modec->ssd; + rate=_modec->rate; + /*Because (except in 4:4:4 mode) we aren't considering chroma blocks in coded + order, we assume a constant overhead for coded block and qii flags.*/ + nblocks=OC_MB_MAP_NIDXS[_enc->state.info.pixel_fmt]; + nblocks=(nblocks-4>>1)+4; + bi=4; + for(pli=1;pli<3;pli++){ + for(;bistate.qis[0],pli,_qti,satd) + +OC_CHROMA_QII_RATE; + best_cost=OC_MODE_RD_COST(ssd+best_ssd,rate+best_rate,lambda); + best_qii=0; + for(qii=1;qiistate.qis[qii],0,_qti,satd) + +OC_CHROMA_QII_RATE; + cur_cost=OC_MODE_RD_COST(ssd+cur_ssd,rate+cur_rate,lambda); + if(cur_costqii[bi]=best_qii; + } + nblocks=(nblocks-4<<1)+4; + } + _modec->ssd=ssd; + _modec->rate=rate; +} + +static void oc_skip_cost(oc_enc_ctx *_enc,oc_enc_pipeline_state *_pipe, + unsigned _mbi,unsigned _ssd[12]){ + OC_ALIGN16(ogg_int16_t buffer[64]); + const unsigned char *src; + const unsigned char *ref; + int ystride; + const oc_fragment *frags; + const ptrdiff_t *frag_buf_offs; + const ptrdiff_t *sb_map; + const oc_mb_map_plane *mb_map; + const unsigned char *map_idxs; + int map_nidxs; + ogg_int64_t mask; + unsigned uncoded_ssd; + int uncoded_dc; + unsigned dc_dequant; + int dc_flag; + int mapii; + int mapi; + int pli; + int bi; + ptrdiff_t fragi; + ptrdiff_t frag_offs; + int borderi; + int pi; + src=_enc->state.ref_frame_data[OC_FRAME_IO]; + ref=_enc->state.ref_frame_data[_enc->state.ref_frame_idx[OC_FRAME_PREV]]; + ystride=_enc->state.ref_ystride[0]; + frags=_enc->state.frags; + frag_buf_offs=_enc->state.frag_buf_offs; + sb_map=_enc->state.sb_maps[_mbi>>2][_mbi&3]; + dc_dequant=_enc->state.dequant_tables[_enc->state.qis[0]][0][1][0]; + for(bi=0;bi<4;bi++){ + fragi=sb_map[bi]; + frag_offs=frag_buf_offs[fragi]; + oc_enc_frag_sub(_enc,buffer,src+frag_offs,ref+frag_offs,ystride); + borderi=frags[fragi].borderi; + uncoded_ssd=uncoded_dc=0; + if(borderi<0){ + for(pi=0;pi<64;pi++){ + uncoded_ssd+=buffer[pi]*buffer[pi]; + uncoded_dc+=buffer[pi]; + } + } + else{ + ogg_int64_t mask; + mask=_enc->state.borders[borderi].mask; + for(pi=0;pi<64;pi++,mask>>=1)if(mask&1){ + uncoded_ssd+=buffer[pi]*buffer[pi]; + uncoded_dc+=buffer[pi]; + } + } + /*Scale to match DCT domain.*/ + uncoded_ssd<<=4; + /*We actually only want the AC contribution to the SSD.*/ + uncoded_ssd-=uncoded_dc*uncoded_dc>>2; + /*DC is a special case; if there's more than a full-quantizer improvement + in the effective DC component, always force-code the block.*/ + dc_flag=abs(uncoded_dc)>dc_dequant<<1; + uncoded_ssd|=-dc_flag; + _pipe->skip_ssd[0][fragi-_pipe->froffset[0]]=_ssd[bi]=uncoded_ssd; + } + mb_map=(const oc_mb_map_plane *)_enc->state.mb_maps[_mbi]; + map_nidxs=OC_MB_MAP_NIDXS[_enc->state.info.pixel_fmt]; + map_idxs=OC_MB_MAP_IDXS[_enc->state.info.pixel_fmt]; + map_nidxs=(map_nidxs-4>>1)+4; + mapii=4; + for(pli=1;pli<3;pli++){ + ystride=_enc->state.ref_ystride[pli]; + dc_dequant=_enc->state.dequant_tables[_enc->state.qis[0]][pli][1][0]; + for(;mapiistate.borders[borderi].mask; + for(pi=0;pi<64;pi++,mask>>=1)if(mask&1){ + uncoded_ssd+=buffer[pi]*buffer[pi]; + uncoded_dc+=buffer[pi]; + } + } + /*Scale to match DCT domain.*/ + uncoded_ssd<<=4; + /*We actually only want the AC contribution to the SSD.*/ + uncoded_ssd-=uncoded_dc*uncoded_dc>>2; + /*DC is a special case; if there's more than a full-quantizer improvement + in the effective DC component, always force-code the block.*/ + dc_flag=abs(uncoded_dc)>dc_dequant<<1; + uncoded_ssd|=-dc_flag; + _pipe->skip_ssd[pli][fragi-_pipe->froffset[pli]]=_ssd[mapii]=uncoded_ssd; + } + map_nidxs=(map_nidxs-4<<1)+4; + } +} + +static void oc_mb_intra_satd(oc_enc_ctx *_enc,unsigned _mbi, + unsigned _frag_satd[12]){ + const unsigned char *src; + const ptrdiff_t *frag_buf_offs; + const ptrdiff_t *sb_map; + const oc_mb_map_plane *mb_map; + const unsigned char *map_idxs; + int map_nidxs; + int mapii; + int mapi; + int ystride; + int pli; + int bi; + ptrdiff_t fragi; + ptrdiff_t frag_offs; + frag_buf_offs=_enc->state.frag_buf_offs; + sb_map=_enc->state.sb_maps[_mbi>>2][_mbi&3]; + src=_enc->state.ref_frame_data[OC_FRAME_IO]; + ystride=_enc->state.ref_ystride[0]; + for(bi=0;bi<4;bi++){ + fragi=sb_map[bi]; + frag_offs=frag_buf_offs[fragi]; + _frag_satd[bi]=oc_enc_frag_intra_satd(_enc,src+frag_offs,ystride); + } + mb_map=(const oc_mb_map_plane *)_enc->state.mb_maps[_mbi]; + map_idxs=OC_MB_MAP_IDXS[_enc->state.info.pixel_fmt]; + map_nidxs=OC_MB_MAP_NIDXS[_enc->state.info.pixel_fmt]; + /*Note: This assumes ref_ystride[1]==ref_ystride[2].*/ + ystride=_enc->state.ref_ystride[1]; + for(mapii=4;mapii>2; + bi=mapi&3; + fragi=mb_map[pli][bi]; + frag_offs=frag_buf_offs[fragi]; + _frag_satd[mapii]=oc_enc_frag_intra_satd(_enc,src+frag_offs,ystride); + } +} + +static void oc_cost_intra(oc_enc_ctx *_enc,oc_mode_choice *_modec, + unsigned _mbi,const oc_fr_state *_fr,const oc_qii_state *_qs, + const unsigned _frag_satd[12],const unsigned _skip_ssd[12]){ + oc_analyze_mb_mode_luma(_enc,_modec,_fr,_qs,_frag_satd,_skip_ssd,0); + oc_analyze_mb_mode_chroma(_enc,_modec,_fr,_qs,_frag_satd,_skip_ssd,0); + _modec->overhead+= + oc_mode_scheme_chooser_cost(&_enc->chooser,OC_MODE_INTRA)<lambda); +} + +static void oc_cost_inter(oc_enc_ctx *_enc,oc_mode_choice *_modec, + unsigned _mbi,int _mb_mode,const signed char *_mv, + const oc_fr_state *_fr,const oc_qii_state *_qs,const unsigned _skip_ssd[12]){ + unsigned frag_satd[12]; + const unsigned char *src; + const unsigned char *ref; + int ystride; + const ptrdiff_t *frag_buf_offs; + const ptrdiff_t *sb_map; + const oc_mb_map_plane *mb_map; + const unsigned char *map_idxs; + int map_nidxs; + int mapii; + int mapi; + int mv_offs[2]; + int dx; + int dy; + int pli; + int bi; + ptrdiff_t fragi; + ptrdiff_t frag_offs; + src=_enc->state.ref_frame_data[OC_FRAME_IO]; + ref=_enc->state.ref_frame_data[ + _enc->state.ref_frame_idx[OC_FRAME_FOR_MODE(_mb_mode)]]; + ystride=_enc->state.ref_ystride[0]; + frag_buf_offs=_enc->state.frag_buf_offs; + sb_map=_enc->state.sb_maps[_mbi>>2][_mbi&3]; + dx=_mv[0]; + dy=_mv[1]; + _modec->rate=_modec->ssd=0; + if(oc_state_get_mv_offsets(&_enc->state,mv_offs,0,dx,dy)>1){ + for(bi=0;bi<4;bi++){ + fragi=sb_map[bi]; + frag_offs=frag_buf_offs[fragi]; + frag_satd[bi]=oc_enc_frag_satd2_thresh(_enc,src+frag_offs, + ref+frag_offs+mv_offs[0],ref+frag_offs+mv_offs[1],ystride,UINT_MAX); + } + } + else{ + for(bi=0;bi<4;bi++){ + fragi=sb_map[bi]; + frag_offs=frag_buf_offs[fragi]; + frag_satd[bi]=oc_enc_frag_satd_thresh(_enc,src+frag_offs, + ref+frag_offs+mv_offs[0],ystride,UINT_MAX); + } + } + mb_map=(const oc_mb_map_plane *)_enc->state.mb_maps[_mbi]; + map_idxs=OC_MB_MAP_IDXS[_enc->state.info.pixel_fmt]; + map_nidxs=OC_MB_MAP_NIDXS[_enc->state.info.pixel_fmt]; + /*Note: This assumes ref_ystride[1]==ref_ystride[2].*/ + ystride=_enc->state.ref_ystride[1]; + if(oc_state_get_mv_offsets(&_enc->state,mv_offs,1,dx,dy)>1){ + for(mapii=4;mapii>2; + bi=mapi&3; + fragi=mb_map[pli][bi]; + frag_offs=frag_buf_offs[fragi]; + frag_satd[mapii]=oc_enc_frag_satd2_thresh(_enc,src+frag_offs, + ref+frag_offs+mv_offs[0],ref+frag_offs+mv_offs[1],ystride,UINT_MAX); + } + } + else{ + for(mapii=4;mapii>2; + bi=mapi&3; + fragi=mb_map[pli][bi]; + frag_offs=frag_buf_offs[fragi]; + frag_satd[mapii]=oc_enc_frag_satd_thresh(_enc,src+frag_offs, + ref+frag_offs+mv_offs[0],ystride,UINT_MAX); + } + } + oc_analyze_mb_mode_luma(_enc,_modec,_fr,_qs,frag_satd,_skip_ssd,1); + oc_analyze_mb_mode_chroma(_enc,_modec,_fr,_qs,frag_satd,_skip_ssd,1); + _modec->overhead+= + oc_mode_scheme_chooser_cost(&_enc->chooser,_mb_mode)<lambda); +} + +static void oc_cost_inter_nomv(oc_enc_ctx *_enc,oc_mode_choice *_modec, + unsigned _mbi,int _mb_mode,const oc_fr_state *_fr,const oc_qii_state *_qs, + const unsigned _skip_ssd[12]){ + static const oc_mv OC_MV_ZERO; + oc_cost_inter(_enc,_modec,_mbi,_mb_mode,OC_MV_ZERO,_fr,_qs,_skip_ssd); +} + +static int oc_cost_inter1mv(oc_enc_ctx *_enc,oc_mode_choice *_modec, + unsigned _mbi,int _mb_mode,const signed char *_mv, + const oc_fr_state *_fr,const oc_qii_state *_qs,const unsigned _skip_ssd[12]){ + int bits0; + oc_cost_inter(_enc,_modec,_mbi,_mb_mode,_mv,_fr,_qs,_skip_ssd); + bits0=OC_MV_BITS[0][_mv[0]+31]+OC_MV_BITS[0][_mv[1]+31]; + _modec->overhead+=OC_MINI(_enc->mv_bits[0]+bits0,_enc->mv_bits[1]+12) + -OC_MINI(_enc->mv_bits[0],_enc->mv_bits[1])<lambda); + return bits0; +} + +/*A mapping from oc_mb_map (raster) ordering to oc_sb_map (Hilbert) ordering.*/ +static const unsigned char OC_MB_PHASE[4][4]={ + {0,1,3,2},{0,3,1,2},{0,3,1,2},{2,3,1,0} +}; + +static void oc_cost_inter4mv(oc_enc_ctx *_enc,oc_mode_choice *_modec, + unsigned _mbi,oc_mv _mv[4],const oc_fr_state *_fr,const oc_qii_state *_qs, + const unsigned _skip_ssd[12]){ + unsigned frag_satd[12]; + oc_mv lbmvs[4]; + oc_mv cbmvs[4]; + const unsigned char *src; + const unsigned char *ref; + int ystride; + const ptrdiff_t *frag_buf_offs; + oc_mv *frag_mvs; + const oc_mb_map_plane *mb_map; + const unsigned char *map_idxs; + int map_nidxs; + int nqis; + int mapii; + int mapi; + int mv_offs[2]; + int dx; + int dy; + int pli; + int bi; + ptrdiff_t fragi; + ptrdiff_t frag_offs; + int bits0; + int bits1; + unsigned satd; + src=_enc->state.ref_frame_data[OC_FRAME_IO]; + ref=_enc->state.ref_frame_data[_enc->state.ref_frame_idx[OC_FRAME_PREV]]; + ystride=_enc->state.ref_ystride[0]; + frag_buf_offs=_enc->state.frag_buf_offs; + frag_mvs=_enc->state.frag_mvs; + mb_map=(const oc_mb_map_plane *)_enc->state.mb_maps[_mbi]; + _modec->rate=_modec->ssd=0; + for(bi=0;bi<4;bi++){ + fragi=mb_map[0][bi]; + dx=_mv[bi][0]; + dy=_mv[bi][1]; + /*Save the block MVs as the current ones while we're here; we'll replace + them if we don't ultimately choose 4MV mode.*/ + frag_mvs[fragi][0]=(signed char)dx; + frag_mvs[fragi][1]=(signed char)dy; + frag_offs=frag_buf_offs[fragi]; + if(oc_state_get_mv_offsets(&_enc->state,mv_offs,0,dx,dy)>1){ + satd=oc_enc_frag_satd2_thresh(_enc,src+frag_offs, + ref+frag_offs+mv_offs[0],ref+frag_offs+mv_offs[1],ystride,UINT_MAX); + } + else{ + satd=oc_enc_frag_satd_thresh(_enc,src+frag_offs, + ref+frag_offs+mv_offs[0],ystride,UINT_MAX); + } + frag_satd[OC_MB_PHASE[_mbi&3][bi]]=satd; + } + oc_analyze_mb_mode_luma(_enc,_modec,_fr,_qs,frag_satd, + _enc->vp3_compatible?OC_NOSKIP:_skip_ssd,1); + /*Figure out which blocks are being skipped and give them (0,0) MVs.*/ + bits0=0; + bits1=0; + nqis=_enc->state.nqis; + for(bi=0;bi<4;bi++){ + if(_modec->qii[OC_MB_PHASE[_mbi&3][bi]]>=nqis){ + memset(lbmvs+bi,0,sizeof(*lbmvs)); + } + else{ + memcpy(lbmvs+bi,_mv+bi,sizeof(*lbmvs)); + bits0+=OC_MV_BITS[0][_mv[bi][0]+31]+OC_MV_BITS[0][_mv[bi][1]+31]; + bits1+=12; + } + } + (*OC_SET_CHROMA_MVS_TABLE[_enc->state.info.pixel_fmt])(cbmvs, + (const oc_mv *)lbmvs); + map_idxs=OC_MB_MAP_IDXS[_enc->state.info.pixel_fmt]; + map_nidxs=OC_MB_MAP_NIDXS[_enc->state.info.pixel_fmt]; + /*Note: This assumes ref_ystride[1]==ref_ystride[2].*/ + ystride=_enc->state.ref_ystride[1]; + for(mapii=4;mapii>2; + bi=mapi&3; + fragi=mb_map[pli][bi]; + dx=cbmvs[bi][0]; + dy=cbmvs[bi][1]; + frag_offs=frag_buf_offs[fragi]; + /*TODO: We could save half these calls by re-using the results for the Cb + and Cr planes; is it worth it?*/ + if(oc_state_get_mv_offsets(&_enc->state,mv_offs,pli,dx,dy)>1){ + satd=oc_enc_frag_satd2_thresh(_enc,src+frag_offs, + ref+frag_offs+mv_offs[0],ref+frag_offs+mv_offs[1],ystride,UINT_MAX); + } + else{ + satd=oc_enc_frag_satd_thresh(_enc,src+frag_offs, + ref+frag_offs+mv_offs[0],ystride,UINT_MAX); + } + frag_satd[mapii]=satd; + } + oc_analyze_mb_mode_chroma(_enc,_modec,_fr,_qs,frag_satd,_skip_ssd,1); + _modec->overhead+= + oc_mode_scheme_chooser_cost(&_enc->chooser,OC_MODE_INTER_MV_FOUR) + +OC_MINI(_enc->mv_bits[0]+bits0,_enc->mv_bits[1]+bits1) + -OC_MINI(_enc->mv_bits[0],_enc->mv_bits[1])<lambda); +} + +int oc_enc_analyze_inter(oc_enc_ctx *_enc,int _allow_keyframe,int _recode){ + oc_set_chroma_mvs_func set_chroma_mvs; + oc_enc_pipeline_state pipe; + oc_qii_state intra_luma_qs; + oc_mv last_mv; + oc_mv prior_mv; + ogg_int64_t interbits; + ogg_int64_t intrabits; + const unsigned char *map_idxs; + int nmap_idxs; + unsigned *coded_mbis; + unsigned *uncoded_mbis; + size_t ncoded_mbis; + size_t nuncoded_mbis; + oc_sb_flags *sb_flags; + signed char *mb_modes; + const oc_sb_map *sb_maps; + const oc_mb_map *mb_maps; + oc_mb_enc_info *embs; + oc_fragment *frags; + oc_mv *frag_mvs; + int qi; + unsigned stripe_sby; + unsigned mcu_nvsbs; + int notstart; + int notdone; + int vdec; + unsigned sbi; + unsigned sbi_end; + int refi; + int pli; + set_chroma_mvs=OC_SET_CHROMA_MVS_TABLE[_enc->state.info.pixel_fmt]; + _enc->state.frame_type=OC_INTER_FRAME; + oc_mode_scheme_chooser_reset(&_enc->chooser); + oc_enc_tokenize_start(_enc); + oc_enc_pipeline_init(_enc,&pipe); + if(_allow_keyframe)oc_qii_state_init(&intra_luma_qs); + _enc->mv_bits[0]=_enc->mv_bits[1]=0; + interbits=intrabits=0; + last_mv[0]=last_mv[1]=prior_mv[0]=prior_mv[1]=0; + /*Choose MVs and MB modes and quantize and code luma. + Must be done in Hilbert order.*/ + map_idxs=OC_MB_MAP_IDXS[_enc->state.info.pixel_fmt]; + nmap_idxs=OC_MB_MAP_NIDXS[_enc->state.info.pixel_fmt]; + qi=_enc->state.qis[0]; + coded_mbis=_enc->coded_mbis; + uncoded_mbis=coded_mbis+_enc->state.nmbs; + ncoded_mbis=0; + nuncoded_mbis=0; + _enc->state.ncoded_fragis[0]=0; + _enc->state.ncoded_fragis[1]=0; + _enc->state.ncoded_fragis[2]=0; + sb_flags=_enc->state.sb_flags; + mb_modes=_enc->state.mb_modes; + sb_maps=(const oc_sb_map *)_enc->state.sb_maps; + mb_maps=(const oc_mb_map *)_enc->state.mb_maps; + embs=_enc->mb_info; + frags=_enc->state.frags; + frag_mvs=_enc->state.frag_mvs; + vdec=!(_enc->state.info.pixel_fmt&2); + notstart=0; + notdone=1; + mcu_nvsbs=_enc->mcu_nvsbs; + for(stripe_sby=0;notdone;stripe_sby+=mcu_nvsbs){ + notdone=oc_enc_pipeline_set_stripe(_enc,&pipe,stripe_sby); + sbi_end=pipe.sbi_end[0]; + for(sbi=pipe.sbi0[0];sbisp_levelsp_levellambda*3; + if(modes[OC_MODE_INTER_MV_FOUR].cost>2][mbi&3][bi]; + frags[fragi].qii=modes[mb_mode].qii[bi]; + } + if(oc_enc_mb_transform_quantize_luma(_enc,&pipe,mbi, + modes[mb_mode].overhead>>OC_BIT_SCALE)>0){ + int orig_mb_mode; + orig_mb_mode=mb_mode; + mb_mode=mb_modes[mbi]; + switch(mb_mode){ + case OC_MODE_INTER_MV:{ + memcpy(prior_mv,last_mv,sizeof(prior_mv)); + /*If we're backing out from 4MV, find the MV we're actually + using.*/ + if(orig_mb_mode==OC_MODE_INTER_MV_FOUR){ + for(bi=0;;bi++){ + fragi=mb_maps[mbi][0][bi]; + if(frags[fragi].coded){ + memcpy(last_mv,frag_mvs[fragi],sizeof(last_mv)); + dx=frag_mvs[fragi][0]; + dy=frag_mvs[fragi][1]; + break; + } + } + mb_mv_bits_0=OC_MV_BITS[0][dx+31]+OC_MV_BITS[0][dy+31]; + } + /*Otherwise we used the original analysis MV.*/ + else{ + memcpy(last_mv, + embs[mbi].analysis_mv[0][OC_FRAME_PREV],sizeof(last_mv)); + } + _enc->mv_bits[0]+=mb_mv_bits_0; + _enc->mv_bits[1]+=12; + }break; + case OC_MODE_INTER_MV_LAST2:{ + oc_mv tmp_mv; + memcpy(tmp_mv,prior_mv,sizeof(tmp_mv)); + memcpy(prior_mv,last_mv,sizeof(prior_mv)); + memcpy(last_mv,tmp_mv,sizeof(last_mv)); + }break; + case OC_MODE_GOLDEN_MV:{ + _enc->mv_bits[0]+=mb_gmv_bits_0; + _enc->mv_bits[1]+=12; + }break; + case OC_MODE_INTER_MV_FOUR:{ + oc_mv lbmvs[4]; + oc_mv cbmvs[4]; + memcpy(prior_mv,last_mv,sizeof(prior_mv)); + for(bi=0;bi<4;bi++){ + fragi=mb_maps[mbi][0][bi]; + if(frags[fragi].coded){ + memcpy(last_mv,frag_mvs[fragi],sizeof(last_mv)); + memcpy(lbmvs[bi],frag_mvs[fragi],sizeof(lbmvs[bi])); + _enc->mv_bits[0]+=OC_MV_BITS[0][frag_mvs[fragi][0]+31] + +OC_MV_BITS[0][frag_mvs[fragi][1]+31]; + _enc->mv_bits[1]+=12; + } + /*Replace the block MVs for not-coded blocks with (0,0).*/ + else memset(lbmvs[bi],0,sizeof(lbmvs[bi])); + } + (*set_chroma_mvs)(cbmvs,(const oc_mv *)lbmvs); + for(mapii=4;mapii>2; + bi=mapi&3; + fragi=mb_maps[mbi][pli][bi]; + frags[fragi].mb_mode=mb_mode; + frags[fragi].qii=modes[OC_MODE_INTER_MV_FOUR].qii[mapii]; + memcpy(frag_mvs[fragi],cbmvs[bi],sizeof(frag_mvs[fragi])); + } + }break; + } + coded_mbis[ncoded_mbis++]=mbi; + oc_mode_scheme_chooser_update(&_enc->chooser,mb_mode); + interbits+=modes[mb_mode].rate+modes[mb_mode].overhead; + } + else{ + *(uncoded_mbis-++nuncoded_mbis)=mbi; + mb_mode=OC_MODE_INTER_NOMV; + dx=dy=0; + } + /*Propagate final MB mode and MVs to the chroma blocks. + This has already been done for 4MV mode, since it requires individual + block motion vectors.*/ + if(mb_mode!=OC_MODE_INTER_MV_FOUR){ + for(mapii=4;mapii>2; + bi=mapi&3; + fragi=mb_maps[mbi][pli][bi]; + frags[fragi].mb_mode=mb_mode; + /*If we switched from 4MV mode to INTER_MV mode, then the qii + values won't have been chosen with the right MV, but it's + probaby not worth re-estimating them.*/ + frags[fragi].qii=modes[mb_mode].qii[mapii]; + frag_mvs[fragi][0]=(signed char)dx; + frag_mvs[fragi][1]=(signed char)dy; + } + } + } + oc_fr_state_flush_sb(pipe.fr+0); + sb_flags[sbi].coded_fully=pipe.fr[0].sb_full; + sb_flags[sbi].coded_partially=pipe.fr[0].sb_partial; + } + oc_enc_pipeline_finish_mcu_plane(_enc,&pipe,0,notstart,notdone); + /*Code chroma planes.*/ + for(pli=1;pli<3;pli++){ + oc_enc_sb_transform_quantize_chroma(_enc,&pipe, + pli,pipe.sbi0[pli],pipe.sbi_end[pli]); + oc_enc_pipeline_finish_mcu_plane(_enc,&pipe,pli,notstart,notdone); + } + notstart=1; + } + /*Finish filling in the reference frame borders.*/ + refi=_enc->state.ref_frame_idx[OC_FRAME_SELF]; + for(pli=0;pli<3;pli++)oc_state_borders_fill_caps(&_enc->state,refi,pli); + /*Finish adding flagging overhead costs to inter bit counts to determine if + we should have coded a key frame instead.*/ + if(_allow_keyframe){ + if(interbits>intrabits)return 1; + /*Technically the chroma plane counts are over-estimations, because they + don't account for continuing runs from the luma planes, but the + inaccuracy is small.*/ + for(pli=0;pli<3;pli++)interbits+=pipe.fr[pli].bits<mv_bits[0],_enc->mv_bits[1])<chooser.scheme_bits[_enc->chooser.scheme_list[0]]<intrabits)return 1; + } + _enc->ncoded_mbis=ncoded_mbis; + /*Compact the coded fragment list.*/ + { + ptrdiff_t ncoded_fragis; + ncoded_fragis=_enc->state.ncoded_fragis[0]; + for(pli=1;pli<3;pli++){ + memmove(_enc->state.coded_fragis+ncoded_fragis, + _enc->state.coded_fragis+_enc->state.fplanes[pli].froffset, + _enc->state.ncoded_fragis[pli]*sizeof(*_enc->state.coded_fragis)); + ncoded_fragis+=_enc->state.ncoded_fragis[pli]; + } + _enc->state.ntotal_coded_fragis=ncoded_fragis; + } + return 0; +} + +#if defined(OC_COLLECT_METRICS) +# include +# include + +/*TODO: It may be helpful (for block-level quantizers especially) to separate + out the contributions from AC and DC into separate tables.*/ + +# define OC_ZWEIGHT (0.25) + +static void oc_mode_metrics_add(oc_mode_metrics *_metrics, + double _w,int _satd,int _rate,double _rmse){ + double rate; + /*Accumulate statistics without the scaling; this lets us change the scale + factor yet still use old data.*/ + rate=ldexp(_rate,-OC_BIT_SCALE); + if(_metrics->fragw>0){ + double dsatd; + double drate; + double drmse; + double w; + dsatd=_satd-_metrics->satd/_metrics->fragw; + drate=rate-_metrics->rate/_metrics->fragw; + drmse=_rmse-_metrics->rmse/_metrics->fragw; + w=_metrics->fragw*_w/(_metrics->fragw+_w); + _metrics->satd2+=dsatd*dsatd*w; + _metrics->satdrate+=dsatd*drate*w; + _metrics->rate2+=drate*drate*w; + _metrics->satdrmse+=dsatd*drmse*w; + _metrics->rmse2+=drmse*drmse*w; + } + _metrics->fragw+=_w; + _metrics->satd+=_satd*_w; + _metrics->rate+=rate*_w; + _metrics->rmse+=_rmse*_w; +} + +static void oc_mode_metrics_merge(oc_mode_metrics *_dst, + const oc_mode_metrics *_src,int _n){ + int i; + /*Find a non-empty set of metrics.*/ + for(i=0;i<_n&&_src[i].fragw<=0;i++); + if(i>=_n){ + memset(_dst,0,sizeof(*_dst)); + return; + } + memcpy(_dst,_src+i,sizeof(*_dst)); + /*And iterate over the remaining non-empty sets of metrics.*/ + for(i++;i<_n;i++)if(_src[i].fragw>0){ + double wa; + double wb; + double dsatd; + double drate; + double drmse; + double w; + wa=_dst->fragw; + wb=_src[i].fragw; + dsatd=_src[i].satd/wb-_dst->satd/wa; + drate=_src[i].rate/wb-_dst->rate/wa; + drmse=_src[i].rmse/wb-_dst->rmse/wa; + w=wa*wb/(wa+wb); + _dst->fragw+=_src[i].fragw; + _dst->satd+=_src[i].satd; + _dst->rate+=_src[i].rate; + _dst->rmse+=_src[i].rmse; + _dst->satd2+=_src[i].satd2+dsatd*dsatd*w; + _dst->satdrate+=_src[i].satdrate+dsatd*drate*w; + _dst->rate2+=_src[i].rate2+drate*drate*w; + _dst->satdrmse+=_src[i].satdrmse+dsatd*drmse*w; + _dst->rmse2+=_src[i].rmse2+drmse*drmse*w; + } +} + +/*Compile collected SATD/rate/RMSE metrics into a form that's immediately + useful for mode decision.*/ +static void oc_enc_mode_metrics_update(oc_enc_ctx *_enc,int _qi){ + int pli; + int qti; + oc_restore_fpu(&_enc->state); + /*Convert raw collected data into cleaned up sample points.*/ + for(pli=0;pli<3;pli++){ + for(qti=0;qti<2;qti++){ + double fragw; + int bin0; + int bin1; + int bin; + fragw=0; + bin0=bin1=0; + for(bin=0;bin=OC_ZWEIGHT){ + fragw-=OC_MODE_METRICS[_qi][pli][qti][bin0++].fragw; + } + /*Merge statistics and fit lines.*/ + oc_mode_metrics_merge(&metrics, + OC_MODE_METRICS[_qi][pli][qti]+bin0,bin1-bin0); + if(metrics.fragw>0&&metrics.satd2>0){ + double a; + double b; + double msatd; + double mrate; + double mrmse; + double rate; + double rmse; + msatd=metrics.satd/metrics.fragw; + mrate=metrics.rate/metrics.fragw; + mrmse=metrics.rmse/metrics.fragw; + /*Compute the points on these lines corresponding to the actual bin + value.*/ + b=metrics.satdrate/metrics.satd2; + a=mrate-b*msatd; + rate=ldexp(a+b*(bin<>1); + return -_extra_bits; +} + +/*Handles the pure zero run tokens.*/ +static ptrdiff_t oc_token_skip_zrl(int _token,int _extra_bits){ + return _extra_bits+1; +} + +/*Handles a normal coefficient value token.*/ +static ptrdiff_t oc_token_skip_val(void){ + return 1; +} + +/*Handles a category 1A zero run/coefficient value combo token.*/ +static ptrdiff_t oc_token_skip_run_cat1a(int _token){ + return _token-OC_DCT_RUN_CAT1A+2; +} + +/*Handles category 1b, 1c, 2a, and 2b zero run/coefficient value combo tokens.*/ +static ptrdiff_t oc_token_skip_run(int _token,int _extra_bits){ + int run_cati; + int ncoeffs_mask; + int ncoeffs_adjust; + run_cati=_token-OC_DCT_RUN_CAT1B; + ncoeffs_mask=OC_BYTE_TABLE32(3,7,0,1,run_cati); + ncoeffs_adjust=OC_BYTE_TABLE32(7,11,2,3,run_cati); + return (_extra_bits&ncoeffs_mask)+ncoeffs_adjust; +} + +/*A jump table for computing the number of coefficients or blocks to skip for + a given token value. + This reduces all the conditional branches, etc., needed to parse these token + values down to one indirect jump.*/ +static const oc_token_skip_func OC_TOKEN_SKIP_TABLE[TH_NDCT_TOKENS]={ + oc_token_skip_eob, + oc_token_skip_eob, + oc_token_skip_eob, + oc_token_skip_eob, + oc_token_skip_eob, + oc_token_skip_eob, + oc_token_skip_eob6, + oc_token_skip_zrl, + oc_token_skip_zrl, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_val, + (oc_token_skip_func)oc_token_skip_run_cat1a, + (oc_token_skip_func)oc_token_skip_run_cat1a, + (oc_token_skip_func)oc_token_skip_run_cat1a, + (oc_token_skip_func)oc_token_skip_run_cat1a, + (oc_token_skip_func)oc_token_skip_run_cat1a, + oc_token_skip_run, + oc_token_skip_run, + oc_token_skip_run, + oc_token_skip_run +}; + +/*Determines the number of blocks or coefficients to be skipped for a given + token value. + _token: The token value to skip. + _extra_bits: The extra bits attached to this token. + Return: A positive value indicates that number of coefficients are to be + skipped in the current block. + Otherwise, the negative of the return value indicates that number of + blocks are to be ended. + 0 will never be returned, so that at least one coefficient in one + block will always be decoded for every token.*/ +static ptrdiff_t oc_dct_token_skip(int _token,int _extra_bits){ + return (*OC_TOKEN_SKIP_TABLE[_token])(_token,_extra_bits); +} + + + +void oc_enc_mode_metrics_collect(oc_enc_ctx *_enc){ + static const unsigned char OC_ZZI_HUFF_OFFSET[64]={ + 0,16,16,16,16,16,32,32, + 32,32,32,32,32,32,32,48, + 48,48,48,48,48,48,48,48, + 48,48,48,48,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64 + }; + const oc_fragment *frags; + const unsigned *frag_satd; + const unsigned *frag_ssd; + const ptrdiff_t *coded_fragis; + ptrdiff_t ncoded_fragis; + ptrdiff_t fragii; + double fragw; + int qti; + int qii; + int qi; + int pli; + int zzi; + int token; + int eb; + oc_restore_fpu(&_enc->state); + /*Load any existing mode metrics if we haven't already.*/ + if(!oc_has_mode_metrics){ + FILE *fmetrics; + memset(OC_MODE_METRICS,0,sizeof(OC_MODE_METRICS)); + fmetrics=fopen("modedec.stats","rb"); + if(fmetrics!=NULL){ + fread(OC_MODE_METRICS,sizeof(OC_MODE_METRICS),1,fmetrics); + fclose(fmetrics); + } + for(qi=0;qi<64;qi++)oc_enc_mode_metrics_update(_enc,qi); + oc_has_mode_metrics=1; + } + qti=_enc->state.frame_type; + frags=_enc->state.frags; + frag_satd=_enc->frag_satd; + frag_ssd=_enc->frag_ssd; + coded_fragis=_enc->state.coded_fragis; + ncoded_fragis=fragii=0; + /*Weight the fragments by the inverse frame size; this prevents HD content + from dominating the statistics.*/ + fragw=1.0/_enc->state.nfrags; + for(pli=0;pli<3;pli++){ + ptrdiff_t ti[64]; + int eob_token[64]; + int eob_run[64]; + /*Set up token indices and eob run counts. + We don't bother trying to figure out the real cost of the runs that span + coefficients; instead we use the costs that were available when R-D + token optimization was done.*/ + for(zzi=0;zzi<64;zzi++){ + ti[zzi]=_enc->dct_token_offs[pli][zzi]; + if(ti[zzi]>0){ + token=_enc->dct_tokens[pli][zzi][0]; + eb=_enc->extra_bits[pli][zzi][0]; + eob_token[zzi]=token; + eob_run[zzi]=-oc_dct_token_skip(token,eb); + } + else{ + eob_token[zzi]=OC_NDCT_EOB_TOKEN_MAX; + eob_run[zzi]=0; + } + } + /*Scan the list of coded fragments for this plane.*/ + ncoded_fragis+=_enc->state.ncoded_fragis[pli]; + for(;fragii0){ + /*We've reached the end of the block.*/ + eob_run[zzi]--; + break; + } + huffi=_enc->huff_idxs[qti][zzi>0][pli+1>>1] + +OC_ZZI_HUFF_OFFSET[zzi]; + if(eob_token[zzi]huff_codes[huffi][eob_token[zzi]].nbits + +OC_DCT_TOKEN_EXTRA_BITS[eob_token[zzi]]; + eob_token[zzi]=OC_NDCT_EOB_TOKEN_MAX; + } + token=_enc->dct_tokens[pli][zzi][ti[zzi]]; + eb=_enc->extra_bits[pli][zzi][ti[zzi]]; + ti[zzi]++; + skip=oc_dct_token_skip(token,eb); + if(skip<0){ + eob_token[zzi]=token; + eob_run[zzi]=-skip; + } + else{ + /*A regular DCT value token; accumulate the bits for it.*/ + frag_bits+=_enc->huff_codes[huffi][token].nbits + +OC_DCT_TOKEN_EXTRA_BITS[token]; + zzi+=skip; + } + } + mb_mode=frags[fragi].mb_mode; + qi=_enc->state.qis[frags[fragi].qii]; + satd=frag_satd[fragi]<<(pli+1&2); + bin=OC_MINI(satd>>OC_SAD_SHIFT,OC_SAD_BINS-1); + oc_mode_metrics_add(OC_MODE_METRICS[qi][pli][mb_mode!=OC_MODE_INTRA]+bin, + fragw,satd,frag_bits<state.nqis;qii++){ + oc_enc_mode_metrics_update(_enc,_enc->state.qis[qii]); + } +} + +void oc_enc_mode_metrics_dump(oc_enc_ctx *_enc){ + FILE *fmetrics; + int qi; + /*Generate sample points for complete list of QI values.*/ + for(qi=0;qi<64;qi++)oc_enc_mode_metrics_update(_enc,qi); + fmetrics=fopen("modedec.stats","wb"); + if(fmetrics!=NULL){ + fwrite(OC_MODE_METRICS,sizeof(OC_MODE_METRICS),1,fmetrics); + fclose(fmetrics); + } + fprintf(stdout, + "/*File generated by libtheora with OC_COLLECT_METRICS" + " defined at compile time.*/\n" + "#if !defined(_modedec_H)\n" + "# define _modedec_H (1)\n" + "\n" + "\n" + "\n" + "# if defined(OC_COLLECT_METRICS)\n" + "typedef struct oc_mode_metrics oc_mode_metrics;\n" + "# endif\n" + "typedef struct oc_mode_rd oc_mode_rd;\n" + "\n" + "\n" + "\n" + "/*The number of extra bits of precision at which to store rate" + " metrics.*/\n" + "# define OC_BIT_SCALE (%i)\n" + "/*The number of extra bits of precision at which to store RMSE metrics.\n" + " This must be at least half OC_BIT_SCALE (rounded up).*/\n" + "# define OC_RMSE_SCALE (%i)\n" + "/*The number of bins to partition statistics into.*/\n" + "# define OC_SAD_BINS (%i)\n" + "/*The number of bits of precision to drop" + " from SAD scores to assign them to a\n" + " bin.*/\n" + "# define OC_SAD_SHIFT (%i)\n" + "\n" + "\n" + "\n" + "# if defined(OC_COLLECT_METRICS)\n" + "struct oc_mode_metrics{\n" + " double fragw;\n" + " double satd;\n" + " double rate;\n" + " double rmse;\n" + " double satd2;\n" + " double satdrate;\n" + " double rate2;\n" + " double satdrmse;\n" + " double rmse2;\n" + "};\n" + "\n" + "\n" + "int oc_has_mode_metrics;\n" + "oc_mode_metrics OC_MODE_METRICS[64][3][2][OC_SAD_BINS];\n" + "# endif\n" + "\n" + "\n" + "\n" + "struct oc_mode_rd{\n" + " ogg_int16_t rate;\n" + " ogg_int16_t rmse;\n" + "};\n" + "\n" + "\n" + "# if !defined(OC_COLLECT_METRICS)\n" + "static const\n" + "# endif\n" + "oc_mode_rd OC_MODE_RD[64][3][2][OC_SAD_BINS]={\n", + OC_BIT_SCALE,OC_RMSE_SCALE,OC_SAD_BINS,OC_SAD_SHIFT); + for(qi=0;qi<64;qi++){ + int pli; + fprintf(stdout," {\n"); + for(pli=0;pli<3;pli++){ + int qti; + fprintf(stdout," {\n"); + for(qti=0;qti<2;qti++){ + int bin; + static const char *pl_names[3]={"Y'","Cb","Cr"}; + static const char *qti_names[2]={"INTRA","INTER"}; + fprintf(stdout," /*%s qi=%i %s*/\n", + pl_names[pli],qi,qti_names[qti]); + fprintf(stdout," {\n"); + fprintf(stdout," "); + for(bin=0;bininternal_decode!=NULL){ - (*((oc_state_dispatch_vtbl *)_th->internal_decode)->clear)(_th); + (*((oc_state_dispatch_vtable *)_th->internal_decode)->clear)(_th); } if(_th->internal_encode!=NULL){ - (*((oc_state_dispatch_vtbl *)_th->internal_encode)->clear)(_th); + (*((oc_state_dispatch_vtable *)_th->internal_encode)->clear)(_th); } if(_th->i!=NULL)theora_info_clear(_th->i); memset(_th,0,sizeof(*_th)); @@ -59,11 +59,11 @@ void theora_clear(theora_state *_th){ int theora_control(theora_state *_th,int _req,void *_buf,size_t _buf_sz){ /*Provide compatibility with mixed encoder and decoder shared lib versions.*/ if(_th->internal_decode!=NULL){ - return (*((oc_state_dispatch_vtbl *)_th->internal_decode)->control)(_th, + return (*((oc_state_dispatch_vtable *)_th->internal_decode)->control)(_th, _req,_buf,_buf_sz); } else if(_th->internal_encode!=NULL){ - return (*((oc_state_dispatch_vtbl *)_th->internal_encode)->control)(_th, + return (*((oc_state_dispatch_vtable *)_th->internal_encode)->control)(_th, _req,_buf,_buf_sz); } else return TH_EINVAL; @@ -72,11 +72,11 @@ int theora_control(theora_state *_th,int _req,void *_buf,size_t _buf_sz){ ogg_int64_t theora_granule_frame(theora_state *_th,ogg_int64_t _gp){ /*Provide compatibility with mixed encoder and decoder shared lib versions.*/ if(_th->internal_decode!=NULL){ - return (*((oc_state_dispatch_vtbl *)_th->internal_decode)->granule_frame)( + return (*((oc_state_dispatch_vtable *)_th->internal_decode)->granule_frame)( _th,_gp); } else if(_th->internal_encode!=NULL){ - return (*((oc_state_dispatch_vtbl *)_th->internal_encode)->granule_frame)( + return (*((oc_state_dispatch_vtable *)_th->internal_encode)->granule_frame)( _th,_gp); } else return -1; @@ -85,11 +85,11 @@ ogg_int64_t theora_granule_frame(theora_state *_th,ogg_int64_t _gp){ double theora_granule_time(theora_state *_th, ogg_int64_t _gp){ /*Provide compatibility with mixed encoder and decoder shared lib versions.*/ if(_th->internal_decode!=NULL){ - return (*((oc_state_dispatch_vtbl *)_th->internal_decode)->granule_time)( + return (*((oc_state_dispatch_vtable *)_th->internal_decode)->granule_time)( _th,_gp); } else if(_th->internal_encode!=NULL){ - return (*((oc_state_dispatch_vtbl *)_th->internal_encode)->granule_time)( + return (*((oc_state_dispatch_vtable *)_th->internal_encode)->granule_time)( _th,_gp); } else return -1; diff --git a/Engine/lib/libtheora/lib/dec/apiwrapper.h b/Engine/lib/libtheora/lib/apiwrapper.h similarity index 92% rename from Engine/lib/libtheora/lib/dec/apiwrapper.h rename to Engine/lib/libtheora/lib/apiwrapper.h index 211021fc0..93454d7bd 100644 --- a/Engine/lib/libtheora/lib/dec/apiwrapper.h +++ b/Engine/lib/libtheora/lib/apiwrapper.h @@ -5,7 +5,7 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** @@ -20,9 +20,8 @@ # include # include # include "theora/theoradec.h" -/*# include "theora/theoraenc.h"*/ -typedef struct th_enc_ctx th_enc_ctx; -# include "../internal.h" +# include "theora/theoraenc.h" +# include "internal.h" typedef struct th_api_wrapper th_api_wrapper; typedef struct th_api_info th_api_info; diff --git a/Engine/lib/libtheora/lib/bitpack.c b/Engine/lib/libtheora/lib/bitpack.c new file mode 100644 index 000000000..8195003ba --- /dev/null +++ b/Engine/lib/libtheora/lib/bitpack.c @@ -0,0 +1,111 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 OggTheora SOURCE CODE IS (C) COPYRIGHT 1994-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: packing variable sized words into an octet stream + last mod: $Id: bitpack.c 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ +#include +#include +#include "bitpack.h" + +/*We're 'MSb' endian; if we write a word but read individual bits, + then we'll read the MSb first.*/ + +void oc_pack_readinit(oc_pack_buf *_b,unsigned char *_buf,long _bytes){ + memset(_b,0,sizeof(*_b)); + _b->ptr=_buf; + _b->stop=_buf+_bytes; +} + +static oc_pb_window oc_pack_refill(oc_pack_buf *_b,int _bits){ + const unsigned char *ptr; + const unsigned char *stop; + oc_pb_window window; + int available; + window=_b->window; + available=_b->bits; + ptr=_b->ptr; + stop=_b->stop; + while(available<=OC_PB_WINDOW_SIZE-8&&ptrptr=ptr; + if(_bits>available){ + if(ptr>=stop){ + _b->eof=1; + available=OC_LOTS_OF_BITS; + } + else window|=*ptr>>(available&7); + } + _b->bits=available; + return window; +} + +int oc_pack_look1(oc_pack_buf *_b){ + oc_pb_window window; + int available; + window=_b->window; + available=_b->bits; + if(available<1)_b->window=window=oc_pack_refill(_b,1); + return window>>OC_PB_WINDOW_SIZE-1; +} + +void oc_pack_adv1(oc_pack_buf *_b){ + _b->window<<=1; + _b->bits--; +} + +/*Here we assume that 0<=_bits&&_bits<=32.*/ +long oc_pack_read(oc_pack_buf *_b,int _bits){ + oc_pb_window window; + int available; + long result; + window=_b->window; + available=_b->bits; + if(_bits==0)return 0; + if(available<_bits){ + window=oc_pack_refill(_b,_bits); + available=_b->bits; + } + result=window>>OC_PB_WINDOW_SIZE-_bits; + available-=_bits; + window<<=1; + window<<=_bits-1; + _b->bits=available; + _b->window=window; + return result; +} + +int oc_pack_read1(oc_pack_buf *_b){ + oc_pb_window window; + int available; + int result; + window=_b->window; + available=_b->bits; + if(available<1){ + window=oc_pack_refill(_b,1); + available=_b->bits; + } + result=window>>OC_PB_WINDOW_SIZE-1; + available--; + window<<=1; + _b->bits=available; + _b->window=window; + return result; +} + +long oc_pack_bytes_left(oc_pack_buf *_b){ + if(_b->eof)return -1; + return _b->stop-_b->ptr+(_b->bits>>3); +} diff --git a/Engine/lib/libtheora/lib/dec/bitpack.h b/Engine/lib/libtheora/lib/bitpack.h similarity index 51% rename from Engine/lib/libtheora/lib/dec/bitpack.h rename to Engine/lib/libtheora/lib/bitpack.h index 1bff3fa50..a020a292f 100644 --- a/Engine/lib/libtheora/lib/dec/bitpack.h +++ b/Engine/lib/libtheora/lib/bitpack.h @@ -5,7 +5,7 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE OggTheora SOURCE CODE IS (C) COPYRIGHT 1994-2008 * + * THE OggTheora SOURCE CODE IS (C) COPYRIGHT 1994-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** @@ -16,23 +16,44 @@ ********************************************************************/ #if !defined(_bitpack_H) # define _bitpack_H (1) -# include +# include -void theorapackB_readinit(oggpack_buffer *_b,unsigned char *_buf,int _bytes); -int theorapackB_look1(oggpack_buffer *_b,long *_ret); -void theorapackB_adv1(oggpack_buffer *_b); + + +typedef unsigned long oc_pb_window; +typedef struct oc_pack_buf oc_pack_buf; + + + +# define OC_PB_WINDOW_SIZE ((int)sizeof(oc_pb_window)*CHAR_BIT) +/*This is meant to be a large, positive constant that can still be efficiently + loaded as an immediate (on platforms like ARM, for example). + Even relatively modest values like 100 would work fine.*/ +# define OC_LOTS_OF_BITS (0x40000000) + + + +struct oc_pack_buf{ + oc_pb_window window; + const unsigned char *ptr; + const unsigned char *stop; + int bits; + int eof; +}; + +void oc_pack_readinit(oc_pack_buf *_b,unsigned char *_buf,long _bytes); +int oc_pack_look1(oc_pack_buf *_b); +void oc_pack_adv1(oc_pack_buf *_b); /*Here we assume 0<=_bits&&_bits<=32.*/ -int theorapackB_read(oggpack_buffer *_b,int _bits,long *_ret); -int theorapackB_read1(oggpack_buffer *_b,long *_ret); -long theorapackB_bytes(oggpack_buffer *_b); -long theorapackB_bits(oggpack_buffer *_b); -unsigned char *theorapackB_get_buffer(oggpack_buffer *_b); +long oc_pack_read(oc_pack_buf *_b,int _bits); +int oc_pack_read1(oc_pack_buf *_b); +/* returns -1 for read beyond EOF, or the number of whole bytes available */ +long oc_pack_bytes_left(oc_pack_buf *_b); /*These two functions are implemented locally in huffdec.c*/ /*Read in bits without advancing the bitptr. Here we assume 0<=_bits&&_bits<=32.*/ -/*static int theorapackB_look(oggpack_buffer *_b,int _bits,long *_ret);*/ -/*static void theorapackB_adv(oggpack_buffer *_b,int _bits);*/ - +/*static int oc_pack_look(oc_pack_buf *_b,int _bits);*/ +/*static void oc_pack_adv(oc_pack_buf *_b,int _bits);*/ #endif diff --git a/Engine/lib/libtheora/lib/cpu.c b/Engine/lib/libtheora/lib/cpu.c index 8da50d070..a863aad7f 100644 --- a/Engine/lib/libtheora/lib/cpu.c +++ b/Engine/lib/libtheora/lib/cpu.c @@ -5,7 +5,7 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2008 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** @@ -14,13 +14,13 @@ Originally written by Rudolf Marek. function: - last mod: $Id: cpu.c 15427 2008-10-21 02:36:19Z xiphmont $ + last mod: $Id: cpu.c 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ #include "cpu.h" -#if !defined(USE_ASM) +#if !defined(OC_X86_ASM) static ogg_uint32_t oc_cpu_flags_get(void){ return 0; } @@ -166,7 +166,7 @@ static ogg_uint32_t oc_cpu_flags_get(void){ /* D M A c i t n e h t u A*/ else if(ecx==0x444D4163&&edx==0x69746E65&&ebx==0x68747541|| /* C S N y b e d o e G*/ - ecx==0x43534E20&&edx==0x79622065&&ebx==0x646F6547){ + ecx==0x43534e20&&edx==0x79622065&&ebx==0x646f6547){ /*AMD, Geode:*/ cpuid(0x80000000,eax,ebx,ecx,edx); if(eax<0x80000001)flags=0; @@ -192,7 +192,6 @@ static ogg_uint32_t oc_cpu_flags_get(void){ The C3-2 (Nehemiah) cores appear to, as well.*/ cpuid(1,eax,ebx,ecx,edx); flags=oc_parse_intel_flags(edx,ecx); - cpuid(0x80000000,eax,ebx,ecx,edx); if(eax>=0x80000001){ /*The (non-Nehemiah) C3 processors support AMD-like cpuid info. We need to check this even if the Intel test succeeds to pick up 3DNow! diff --git a/Engine/lib/libtheora/lib/cpu.h b/Engine/lib/libtheora/lib/cpu.h index efe5e9f52..a43c957a3 100644 --- a/Engine/lib/libtheora/lib/cpu.h +++ b/Engine/lib/libtheora/lib/cpu.h @@ -5,12 +5,12 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: cpu.h 15430 2008-10-21 05:03:55Z giles $ + last mod: $Id: cpu.h 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ diff --git a/Engine/lib/libtheora/lib/dec/dct.h b/Engine/lib/libtheora/lib/dct.h similarity index 90% rename from Engine/lib/libtheora/lib/dec/dct.h rename to Engine/lib/libtheora/lib/dct.h index 09043dc51..24ba6f111 100644 --- a/Engine/lib/libtheora/lib/dec/dct.h +++ b/Engine/lib/libtheora/lib/dct.h @@ -5,13 +5,13 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: dct.h 15400 2008-10-15 12:10:58Z tterribe $ + last mod: $Id: dct.h 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ diff --git a/Engine/lib/libtheora/lib/dec/bitpack.c b/Engine/lib/libtheora/lib/dec/bitpack.c deleted file mode 100644 index 3836150c2..000000000 --- a/Engine/lib/libtheora/lib/dec/bitpack.c +++ /dev/null @@ -1,121 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 OggTheora SOURCE CODE IS (C) COPYRIGHT 1994-2008 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: packing variable sized words into an octet stream - last mod: $Id: bitpack.c 15400 2008-10-15 12:10:58Z tterribe $ - - ********************************************************************/ - -/*We're 'MSb' endian; if we write a word but read individual bits, - then we'll read the MSb first.*/ - -#include -#include -#include "bitpack.h" - -void theorapackB_readinit(oggpack_buffer *_b,unsigned char *_buf,int _bytes){ - memset(_b,0,sizeof(*_b)); - _b->buffer=_b->ptr=_buf; - _b->storage=_bytes; -} - -int theorapackB_look1(oggpack_buffer *_b,long *_ret){ - if(_b->endbyte>=_b->storage){ - *_ret=0L; - return -1; - } - *_ret=(_b->ptr[0]>>7-_b->endbit)&1; - return 0; -} - -void theorapackB_adv1(oggpack_buffer *_b){ - if(++(_b->endbit)>7){ - _b->endbit=0; - _b->ptr++; - _b->endbyte++; - } -} - -/*Here we assume that 0<=_bits&&_bits<=32.*/ -int theorapackB_read(oggpack_buffer *_b,int _bits,long *_ret){ - long ret; - long m; - long d; - int fail; - m=32-_bits; - _bits+=_b->endbit; - d=_b->storage-_b->endbyte; - if(d<=4){ - /*Not the main path.*/ - if(d*8<_bits){ - *_ret=0L; - fail=-1; - goto overflow; - } - /*Special case to avoid reading _b->ptr[0], which might be past the end of - the buffer; also skips some useless accounting.*/ - else if(!_bits){ - *_ret=0L; - return 0; - } - } - ret=_b->ptr[0]<<24+_b->endbit; - if(_bits>8){ - ret|=_b->ptr[1]<<16+_b->endbit; - if(_bits>16){ - ret|=_b->ptr[2]<<8+_b->endbit; - if(_bits>24){ - ret|=_b->ptr[3]<<_b->endbit; - if(_bits>32)ret|=_b->ptr[4]>>8-_b->endbit; - } - } - } - *_ret=((ret&0xFFFFFFFFUL)>>(m>>1))>>(m+1>>1); - fail=0; -overflow: - _b->ptr+=_bits>>3; - _b->endbyte+=_bits>>3; - _b->endbit=_bits&7; - return fail; -} - -int theorapackB_read1(oggpack_buffer *_b,long *_ret){ - int fail; - if(_b->endbyte>=_b->storage){ - /*Not the main path.*/ - *_ret=0L; - fail=-1; - } - else{ - *_ret=(_b->ptr[0]>>7-_b->endbit)&1; - fail=0; - } - _b->endbit++; - if(_b->endbit>7){ - _b->endbit=0; - _b->ptr++; - _b->endbyte++; - } - return fail; -} - -long theorapackB_bytes(oggpack_buffer *_b){ - return _b->endbyte+(_b->endbit+7>>3); -} - -long theorapackB_bits(oggpack_buffer *_b){ - return _b->endbyte*8+_b->endbit; -} - -unsigned char *theorapackB_get_buffer(oggpack_buffer *_b){ - return _b->buffer; -} diff --git a/Engine/lib/libtheora/lib/dec/decode.c b/Engine/lib/libtheora/lib/dec/decode.c deleted file mode 100644 index 5804cf709..000000000 --- a/Engine/lib/libtheora/lib/dec/decode.c +++ /dev/null @@ -1,2057 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: decode.c 15403 2008-10-16 12:44:05Z tterribe $ - - ********************************************************************/ - -#include -#include -#include -#include "decint.h" -#if defined(OC_DUMP_IMAGES) -# include -# include "png.h" -#endif - -/*No post-processing.*/ -#define OC_PP_LEVEL_DISABLED (0) -/*Keep track of DC qi for each block only.*/ -#define OC_PP_LEVEL_TRACKDCQI (1) -/*Deblock the luma plane.*/ -#define OC_PP_LEVEL_DEBLOCKY (2) -/*Dering the luma plane.*/ -#define OC_PP_LEVEL_DERINGY (3) -/*Stronger luma plane deringing.*/ -#define OC_PP_LEVEL_SDERINGY (4) -/*Deblock the chroma planes.*/ -#define OC_PP_LEVEL_DEBLOCKC (5) -/*Dering the chroma planes.*/ -#define OC_PP_LEVEL_DERINGC (6) -/*Stronger chroma plane deringing.*/ -#define OC_PP_LEVEL_SDERINGC (7) -/*Maximum valid post-processing level.*/ -#define OC_PP_LEVEL_MAX (7) - - - -/*The mode alphabets for the various mode coding schemes. - Scheme 0 uses a custom alphabet, which is not stored in this table.*/ -static const int OC_MODE_ALPHABETS[7][OC_NMODES]={ - /*Last MV dominates */ - { - OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV_LAST2,OC_MODE_INTER_MV, - OC_MODE_INTER_NOMV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, - OC_MODE_INTER_MV_FOUR - }, - { - OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV_LAST2,OC_MODE_INTER_NOMV, - OC_MODE_INTER_MV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, - OC_MODE_INTER_MV_FOUR - }, - { - OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV,OC_MODE_INTER_MV_LAST2, - OC_MODE_INTER_NOMV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, - OC_MODE_INTER_MV_FOUR - }, - { - OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV,OC_MODE_INTER_NOMV, - OC_MODE_INTER_MV_LAST2,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV, - OC_MODE_GOLDEN_MV,OC_MODE_INTER_MV_FOUR - }, - /*No MV dominates.*/ - { - OC_MODE_INTER_NOMV,OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV_LAST2, - OC_MODE_INTER_MV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, - OC_MODE_INTER_MV_FOUR - }, - { - OC_MODE_INTER_NOMV,OC_MODE_GOLDEN_NOMV,OC_MODE_INTER_MV_LAST, - OC_MODE_INTER_MV_LAST2,OC_MODE_INTER_MV,OC_MODE_INTRA,OC_MODE_GOLDEN_MV, - OC_MODE_INTER_MV_FOUR - }, - /*Default ordering.*/ - { - OC_MODE_INTER_NOMV,OC_MODE_INTRA,OC_MODE_INTER_MV,OC_MODE_INTER_MV_LAST, - OC_MODE_INTER_MV_LAST2,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, - OC_MODE_INTER_MV_FOUR - } -}; - - -static int oc_sb_run_unpack(oggpack_buffer *_opb){ - long bits; - int ret; - /*Coding scheme: - Codeword Run Length - 0 1 - 10x 2-3 - 110x 4-5 - 1110xx 6-9 - 11110xxx 10-17 - 111110xxxx 18-33 - 111111xxxxxxxxxxxx 34-4129*/ - theorapackB_read1(_opb,&bits); - if(bits==0)return 1; - theorapackB_read(_opb,2,&bits); - if((bits&2)==0)return 2+(int)bits; - else if((bits&1)==0){ - theorapackB_read1(_opb,&bits); - return 4+(int)bits; - } - theorapackB_read(_opb,3,&bits); - if((bits&4)==0)return 6+(int)bits; - else if((bits&2)==0){ - ret=10+((bits&1)<<2); - theorapackB_read(_opb,2,&bits); - return ret+(int)bits; - } - else if((bits&1)==0){ - theorapackB_read(_opb,4,&bits); - return 18+(int)bits; - } - theorapackB_read(_opb,12,&bits); - return 34+(int)bits; -} - -static int oc_block_run_unpack(oggpack_buffer *_opb){ - long bits; - long bits2; - /*Coding scheme: - Codeword Run Length - 0x 1-2 - 10x 3-4 - 110x 5-6 - 1110xx 7-10 - 11110xx 11-14 - 11111xxxx 15-30*/ - theorapackB_read(_opb,2,&bits); - if((bits&2)==0)return 1+(int)bits; - else if((bits&1)==0){ - theorapackB_read1(_opb,&bits); - return 3+(int)bits; - } - theorapackB_read(_opb,2,&bits); - if((bits&2)==0)return 5+(int)bits; - else if((bits&1)==0){ - theorapackB_read(_opb,2,&bits); - return 7+(int)bits; - } - theorapackB_read(_opb,3,&bits); - if((bits&4)==0)return 11+bits; - theorapackB_read(_opb,2,&bits2); - return 15+((bits&3)<<2)+bits2; -} - - - -static int oc_dec_init(oc_dec_ctx *_dec,const th_info *_info, - const th_setup_info *_setup){ - int qti; - int pli; - int qi; - int ret; - ret=oc_state_init(&_dec->state,_info); - if(ret<0)return ret; - oc_huff_trees_copy(_dec->huff_tables, - (const oc_huff_node *const *)_setup->huff_tables); - for(qti=0;qti<2;qti++)for(pli=0;pli<3;pli++){ - _dec->state.dequant_tables[qti][pli]= - _dec->state.dequant_table_data[qti][pli]; - } - oc_dequant_tables_init(_dec->state.dequant_tables,_dec->pp_dc_scale, - &_setup->qinfo); - for(qi=0;qi<64;qi++){ - int qsum; - qsum=0; - for(qti=0;qti<2;qti++)for(pli=0;pli<3;pli++){ - qsum+=_dec->state.dequant_tables[qti][pli][qi][18]+ - _dec->state.dequant_tables[qti][pli][qi][19]+ - _dec->state.dequant_tables[qti][pli][qi][26]+ - _dec->state.dequant_tables[qti][pli][qi][27]<<(pli==0); - } - _dec->pp_sharp_mod[qi]=-(qsum>>11); - } - _dec->dct_tokens=(unsigned char **)oc_calloc_2d(64, - _dec->state.nfrags,sizeof(_dec->dct_tokens[0][0])); - _dec->extra_bits=(ogg_uint16_t **)oc_calloc_2d(64, - _dec->state.nfrags,sizeof(_dec->extra_bits[0][0])); - memcpy(_dec->state.loop_filter_limits,_setup->qinfo.loop_filter_limits, - sizeof(_dec->state.loop_filter_limits)); - _dec->pp_level=OC_PP_LEVEL_DISABLED; - _dec->dc_qis=NULL; - _dec->variances=NULL; - _dec->pp_frame_data=NULL; - _dec->stripe_cb.ctx=NULL; - _dec->stripe_cb.stripe_decoded=NULL; - return 0; -} - -static void oc_dec_clear(oc_dec_ctx *_dec){ - _ogg_free(_dec->pp_frame_data); - _ogg_free(_dec->variances); - _ogg_free(_dec->dc_qis); - oc_free_2d(_dec->extra_bits); - oc_free_2d(_dec->dct_tokens); - oc_huff_trees_clear(_dec->huff_tables); - oc_state_clear(&_dec->state); -} - - -static int oc_dec_frame_header_unpack(oc_dec_ctx *_dec){ - long val; - /*Check to make sure this is a data packet.*/ - theorapackB_read1(&_dec->opb,&val); - if(val!=0)return TH_EBADPACKET; - /*Read in the frame type (I or P).*/ - theorapackB_read1(&_dec->opb,&val); - _dec->state.frame_type=(int)val; - /*Read in the current qi.*/ - theorapackB_read(&_dec->opb,6,&val); - _dec->state.qis[0]=(int)val; - theorapackB_read1(&_dec->opb,&val); - if(!val)_dec->state.nqis=1; - else{ - theorapackB_read(&_dec->opb,6,&val); - _dec->state.qis[1]=(int)val; - theorapackB_read1(&_dec->opb,&val); - if(!val)_dec->state.nqis=2; - else{ - theorapackB_read(&_dec->opb,6,&val); - _dec->state.qis[2]=(int)val; - _dec->state.nqis=3; - } - } - if(_dec->state.frame_type==OC_INTRA_FRAME){ - /*Keyframes have 3 unused configuration bits, holdovers from VP3 days. - Most of the other unused bits in the VP3 headers were eliminated. - I don't know why these remain.*/ - /* I wanted to eliminate wasted bits, but not all config wiggle room --Monty */ - theorapackB_read(&_dec->opb,3,&val); - if(val!=0)return TH_EIMPL; - } - return 0; -} - -/*Mark all fragments as coded and in OC_MODE_INTRA. - This also builds up the coded fragment list (in coded order), and clears the - uncoded fragment list. - It does not update the coded macro block list, as that is not used when - decoding INTRA frames.*/ -static void oc_dec_mark_all_intra(oc_dec_ctx *_dec){ - oc_sb *sb; - oc_sb *sb_end; - int pli; - int ncoded_fragis; - int prev_ncoded_fragis; - prev_ncoded_fragis=ncoded_fragis=0; - sb=sb_end=_dec->state.sbs; - for(pli=0;pli<3;pli++){ - const oc_fragment_plane *fplane; - fplane=_dec->state.fplanes+pli; - sb_end+=fplane->nsbs; - for(;sbquad_valid&1<map[quadi][bi]; - if(fragi>=0){ - oc_fragment *frag; - frag=_dec->state.frags+fragi; - frag->coded=1; - frag->mbmode=OC_MODE_INTRA; - _dec->state.coded_fragis[ncoded_fragis++]=fragi; - } - } - } - } - _dec->state.ncoded_fragis[pli]=ncoded_fragis-prev_ncoded_fragis; - prev_ncoded_fragis=ncoded_fragis; - _dec->state.nuncoded_fragis[pli]=0; - } -} - -/*Decodes the bit flags for whether or not each super block is partially coded - or not. - Return: The number of partially coded super blocks.*/ -static int oc_dec_partial_sb_flags_unpack(oc_dec_ctx *_dec){ - oc_sb *sb; - oc_sb *sb_end; - long val; - int flag; - int npartial; - int run_count; - theorapackB_read1(&_dec->opb,&val); - flag=(int)val; - sb=_dec->state.sbs; - sb_end=sb+_dec->state.nsbs; - run_count=npartial=0; - while(sbopb); - full_run=run_count>=4129; - do{ - sb->coded_partially=flag; - sb->coded_fully=0; - npartial+=flag; - sb++; - } - while(--run_count>0&&sbopb,&val); - flag=(int)val; - } - else flag=!flag; - } - /*TODO: run_count should be 0 here. - If it's not, we should issue a warning of some kind.*/ - return npartial; -} - -/*Decodes the bit flags for whether or not each non-partially-coded super - block is fully coded or not. - This function should only be called if there is at least one - non-partially-coded super block. - Return: The number of partially coded super blocks.*/ -static void oc_dec_coded_sb_flags_unpack(oc_dec_ctx *_dec){ - oc_sb *sb; - oc_sb *sb_end; - long val; - int flag; - int run_count; - sb=_dec->state.sbs; - sb_end=sb+_dec->state.nsbs; - /*Skip partially coded super blocks.*/ - for(;sb->coded_partially;sb++); - theorapackB_read1(&_dec->opb,&val); - flag=(int)val; - while(sbopb); - full_run=run_count>=4129; - for(;sbcoded_partially)continue; - if(run_count--<=0)break; - sb->coded_fully=flag; - } - if(full_run&&sbopb,&val); - flag=(int)val; - } - else flag=!flag; - } - /*TODO: run_count should be 0 here. - If it's not, we should issue a warning of some kind.*/ -} - -static void oc_dec_coded_flags_unpack(oc_dec_ctx *_dec){ - oc_sb *sb; - oc_sb *sb_end; - long val; - int npartial; - int pli; - int flag; - int run_count; - int ncoded_fragis; - int prev_ncoded_fragis; - int nuncoded_fragis; - int prev_nuncoded_fragis; - npartial=oc_dec_partial_sb_flags_unpack(_dec); - if(npartial<_dec->state.nsbs)oc_dec_coded_sb_flags_unpack(_dec); - if(npartial>0){ - theorapackB_read1(&_dec->opb,&val); - flag=!(int)val; - } - else flag=0; - run_count=0; - prev_ncoded_fragis=ncoded_fragis=prev_nuncoded_fragis=nuncoded_fragis=0; - sb=sb_end=_dec->state.sbs; - for(pli=0;pli<3;pli++){ - const oc_fragment_plane *fplane; - fplane=_dec->state.fplanes+pli; - sb_end+=fplane->nsbs; - for(;sbquad_valid&1<map[quadi][bi]; - if(fragi>=0){ - oc_fragment *frag; - frag=_dec->state.frags+fragi; - if(sb->coded_fully)frag->coded=1; - else if(!sb->coded_partially)frag->coded=0; - else{ - if(run_count<=0){ - run_count=oc_block_run_unpack(&_dec->opb); - flag=!flag; - } - run_count--; - frag->coded=flag; - } - if(frag->coded)_dec->state.coded_fragis[ncoded_fragis++]=fragi; - else *(_dec->state.uncoded_fragis-++nuncoded_fragis)=fragi; - } - } - } - } - _dec->state.ncoded_fragis[pli]=ncoded_fragis-prev_ncoded_fragis; - prev_ncoded_fragis=ncoded_fragis; - _dec->state.nuncoded_fragis[pli]=nuncoded_fragis-prev_nuncoded_fragis; - prev_nuncoded_fragis=nuncoded_fragis; - } - /*TODO: run_count should be 0 here. - If it's not, we should issue a warning of some kind.*/ -} - - - -typedef int (*oc_mode_unpack_func)(oggpack_buffer *_opb); - -static int oc_vlc_mode_unpack(oggpack_buffer *_opb){ - long val; - int i; - for(i=0;i<7;i++){ - theorapackB_read1(_opb,&val); - if(!val)break; - } - return i; -} - -static int oc_clc_mode_unpack(oggpack_buffer *_opb){ - long val; - theorapackB_read(_opb,3,&val); - return (int)val; -} - -/*Unpacks the list of macro block modes for INTER frames.*/ -static void oc_dec_mb_modes_unpack(oc_dec_ctx *_dec){ - oc_mode_unpack_func mode_unpack; - oc_mb *mb; - oc_mb *mb_end; - const int *alphabet; - long val; - int scheme0_alphabet[8]; - int mode_scheme; - theorapackB_read(&_dec->opb,3,&val); - mode_scheme=(int)val; - if(mode_scheme==0){ - int mi; - /*Just in case, initialize the modes to something. - If the bitstream doesn't contain each index exactly once, it's likely - corrupt and the rest of the packet is garbage anyway, but this way we - won't crash, and we'll decode SOMETHING.*/ - /*LOOP VECTORIZES.*/ - for(mi=0;miopb,3,&val); - scheme0_alphabet[val]=OC_MODE_ALPHABETS[6][mi]; - } - alphabet=scheme0_alphabet; - } - else alphabet=OC_MODE_ALPHABETS[mode_scheme-1]; - if(mode_scheme==7)mode_unpack=oc_clc_mode_unpack; - else mode_unpack=oc_vlc_mode_unpack; - mb=_dec->state.mbs; - mb_end=mb+_dec->state.nmbs; - for(;mbmode!=OC_MODE_INVALID){ - int bi; - for(bi=0;bi<4;bi++){ - int fragi; - fragi=mb->map[0][bi]; - if(fragi>=0&&_dec->state.frags[fragi].coded)break; - } - if(bi<4)mb->mode=alphabet[(*mode_unpack)(&_dec->opb)]; - else mb->mode=OC_MODE_INTER_NOMV; - } - } -} - - - -typedef int (*oc_mv_comp_unpack_func)(oggpack_buffer *_opb); - -static int oc_vlc_mv_comp_unpack(oggpack_buffer *_opb){ - long bits; - int mvsigned[2]; - theorapackB_read(_opb,3,&bits); - switch(bits){ - case 0:return 0; - case 1:return 1; - case 2:return -1; - case 3: - case 4:{ - mvsigned[0]=(int)(bits-1); - theorapackB_read1(_opb,&bits); - }break; - /*case 5: - case 6: - case 7:*/ - default:{ - mvsigned[0]=1<>1); - bits&=1; - }break; - } - mvsigned[1]=-mvsigned[0]; - return mvsigned[bits]; -} - -static int oc_clc_mv_comp_unpack(oggpack_buffer *_opb){ - long bits; - int mvsigned[2]; - theorapackB_read(_opb,6,&bits); - mvsigned[0]=bits>>1; - mvsigned[1]=-mvsigned[0]; - return mvsigned[bits&1]; -} - -/*Unpacks the list of motion vectors for INTER frames, and propagtes the macro - block modes and motion vectors to the individual fragments.*/ -static void oc_dec_mv_unpack_and_frag_modes_fill(oc_dec_ctx *_dec){ - oc_set_chroma_mvs_func set_chroma_mvs; - oc_mv_comp_unpack_func mv_comp_unpack; - oc_mb *mb; - oc_mb *mb_end; - const int *map_idxs; - long val; - int map_nidxs; - oc_mv last_mv[2]; - oc_mv cbmvs[4]; - set_chroma_mvs=OC_SET_CHROMA_MVS_TABLE[_dec->state.info.pixel_fmt]; - theorapackB_read1(&_dec->opb,&val); - mv_comp_unpack=val?oc_clc_mv_comp_unpack:oc_vlc_mv_comp_unpack; - map_idxs=OC_MB_MAP_IDXS[_dec->state.info.pixel_fmt]; - map_nidxs=OC_MB_MAP_NIDXS[_dec->state.info.pixel_fmt]; - memset(last_mv,0,sizeof(last_mv)); - mb=_dec->state.mbs; - mb_end=mb+_dec->state.nmbs; - for(;mbmode!=OC_MODE_INVALID){ - oc_fragment *frag; - oc_mv mbmv; - int coded[13]; - int codedi; - int ncoded; - int mapi; - int mapii; - int fragi; - int mb_mode; - /*Search for at least one coded fragment.*/ - ncoded=mapii=0; - do{ - mapi=map_idxs[mapii]; - fragi=mb->map[mapi>>2][mapi&3]; - if(fragi>=0&&_dec->state.frags[fragi].coded)coded[ncoded++]=mapi; - } - while(++mapiimode; - switch(mb_mode){ - case OC_MODE_INTER_MV_FOUR:{ - oc_mv lbmvs[4]; - int bi; - /*Mark the tail of the list, so we don't accidentally go past it.*/ - coded[ncoded]=-1; - for(bi=codedi=0;bi<4;bi++){ - if(coded[codedi]==bi){ - codedi++; - frag=_dec->state.frags+mb->map[0][bi]; - frag->mbmode=mb_mode; - frag->mv[0]=lbmvs[bi][0]=(signed char)(*mv_comp_unpack)(&_dec->opb); - frag->mv[1]=lbmvs[bi][1]=(signed char)(*mv_comp_unpack)(&_dec->opb); - } - else lbmvs[bi][0]=lbmvs[bi][1]=0; - } - if(codedi>0){ - last_mv[1][0]=last_mv[0][0]; - last_mv[1][1]=last_mv[0][1]; - last_mv[0][0]=lbmvs[coded[codedi-1]][0]; - last_mv[0][1]=lbmvs[coded[codedi-1]][1]; - } - if(codedistate.frags+mb->map[mapi>>2][bi]; - frag->mbmode=mb_mode; - frag->mv[0]=cbmvs[bi][0]; - frag->mv[1]=cbmvs[bi][1]; - } - } - }break; - case OC_MODE_INTER_MV:{ - last_mv[1][0]=last_mv[0][0]; - last_mv[1][1]=last_mv[0][1]; - mbmv[0]=last_mv[0][0]=(signed char)(*mv_comp_unpack)(&_dec->opb); - mbmv[1]=last_mv[0][1]=(signed char)(*mv_comp_unpack)(&_dec->opb); - }break; - case OC_MODE_INTER_MV_LAST:{ - mbmv[0]=last_mv[0][0]; - mbmv[1]=last_mv[0][1]; - }break; - case OC_MODE_INTER_MV_LAST2:{ - mbmv[0]=last_mv[1][0]; - mbmv[1]=last_mv[1][1]; - last_mv[1][0]=last_mv[0][0]; - last_mv[1][1]=last_mv[0][1]; - last_mv[0][0]=mbmv[0]; - last_mv[0][1]=mbmv[1]; - }break; - case OC_MODE_GOLDEN_MV:{ - mbmv[0]=(signed char)(*mv_comp_unpack)(&_dec->opb); - mbmv[1]=(signed char)(*mv_comp_unpack)(&_dec->opb); - }break; - default:mbmv[0]=mbmv[1]=0;break; - } - /*4MV mode fills in the fragments itself. - For all other modes we can use this common code.*/ - if(mb_mode!=OC_MODE_INTER_MV_FOUR){ - for(codedi=0;codedimap[mapi>>2][mapi&3]; - frag=_dec->state.frags+fragi; - frag->mbmode=mb_mode; - frag->mv[0]=mbmv[0]; - frag->mv[1]=mbmv[1]; - } - } - } -} - -static void oc_dec_block_qis_unpack(oc_dec_ctx *_dec){ - oc_fragment *frag; - int *coded_fragi; - int *coded_fragi_end; - int ncoded_fragis; - ncoded_fragis=_dec->state.ncoded_fragis[0]+ - _dec->state.ncoded_fragis[1]+_dec->state.ncoded_fragis[2]; - if(ncoded_fragis<=0)return; - coded_fragi=_dec->state.coded_fragis; - coded_fragi_end=coded_fragi+ncoded_fragis; - if(_dec->state.nqis==1){ - /*If this frame has only a single qi value, then just set it in all coded - fragments.*/ - while(coded_fragistate.frags[*coded_fragi++].qi=_dec->state.qis[0]; - } - } - else{ - long val; - int flag; - int nqi1; - int run_count; - /*Otherwise, we decode a qi index for each fragment, using two passes of - the same binary RLE scheme used for super-block coded bits. - The first pass marks each fragment as having a qii of 0 or greater than - 0, and the second pass (if necessary), distinguishes between a qii of - 1 and 2. - At first we just store the qii in the fragment. - After all the qii's are decoded, we make a final pass to replace them - with the corresponding qi's for this frame.*/ - theorapackB_read1(&_dec->opb,&val); - flag=(int)val; - run_count=nqi1=0; - while(coded_fragiopb); - full_run=run_count>=4129; - do{ - _dec->state.frags[*coded_fragi++].qi=flag; - nqi1+=flag; - } - while(--run_count>0&&coded_fragiopb,&val); - flag=(int)val; - } - else flag=!flag; - } - /*TODO: run_count should be 0 here. - If it's not, we should issue a warning of some kind.*/ - /*If we have 3 different qi's for this frame, and there was at least one - fragment with a non-zero qi, make the second pass.*/ - if(_dec->state.nqis==3&&nqi1>0){ - /*Skip qii==0 fragments.*/ - for(coded_fragi=_dec->state.coded_fragis; - _dec->state.frags[*coded_fragi].qi==0;coded_fragi++); - theorapackB_read1(&_dec->opb,&val); - flag=(int)val; - while(coded_fragiopb); - full_run=run_count>=4129; - for(;coded_fragistate.frags+*coded_fragi; - if(frag->qi==0)continue; - if(run_count--<=0)break; - frag->qi+=flag; - } - if(full_run&&coded_fragiopb,&val); - flag=(int)val; - } - else flag=!flag; - } - /*TODO: run_count should be 0 here. - If it's not, we should issue a warning of some kind.*/ - } - /*Finally, translate qii's to qi's.*/ - for(coded_fragi=_dec->state.coded_fragis;coded_fragistate.frags+*coded_fragi; - frag->qi=_dec->state.qis[frag->qi]; - } - } -} - - - -/*Returns the decoded value of the given token. - It CANNOT be called for any of the EOB tokens. - _token: The token value to skip. - _extra_bits: The extra bits attached to this token. - Return: The decoded coefficient value.*/ -typedef int (*oc_token_dec1val_func)(int _token,int _extra_bits); - -/*Handles zero run tokens.*/ -static int oc_token_dec1val_zrl(void){ - return 0; -} - -/*Handles 1, -1, 2 and -2 tokens.*/ -static int oc_token_dec1val_const(int _token){ - static const int CONST_VALS[4]={1,-1,2,-2}; - return CONST_VALS[_token-OC_NDCT_ZRL_TOKEN_MAX]; -} - -/*Handles DCT value tokens category 2.*/ -static int oc_token_dec1val_cat2(int _token,int _extra_bits){ - int valsigned[2]; - valsigned[0]=_token-OC_DCT_VAL_CAT2+3; - valsigned[1]=-valsigned[0]; - return valsigned[_extra_bits]; -} - -/*Handles DCT value tokens categories 3 through 8.*/ -static int oc_token_dec1val_cati(int _token,int _extra_bits){ - static const int VAL_CAT_OFFS[6]={ - OC_NDCT_VAL_CAT2_SIZE+3, - OC_NDCT_VAL_CAT2_SIZE+5, - OC_NDCT_VAL_CAT2_SIZE+9, - OC_NDCT_VAL_CAT2_SIZE+17, - OC_NDCT_VAL_CAT2_SIZE+33, - OC_NDCT_VAL_CAT2_SIZE+65 - }; - static const int VAL_CAT_MASKS[6]={ - 0x001,0x003,0x007,0x00F,0x01F,0x1FF - }; - static const int VAL_CAT_SHIFTS[6]={1,2,3,4,5,9}; - int valsigned[2]; - int cati; - cati=_token-OC_NDCT_VAL_CAT2_MAX; - valsigned[0]=VAL_CAT_OFFS[cati]+(_extra_bits&VAL_CAT_MASKS[cati]); - valsigned[1]=-valsigned[0]; - return valsigned[_extra_bits>>VAL_CAT_SHIFTS[cati]&1]; -} - -/*A jump table for compute the first coefficient value the given token value - represents.*/ -static const oc_token_dec1val_func OC_TOKEN_DEC1VAL_TABLE[TH_NDCT_TOKENS- - OC_NDCT_EOB_TOKEN_MAX]={ - (oc_token_dec1val_func)oc_token_dec1val_zrl, - (oc_token_dec1val_func)oc_token_dec1val_zrl, - (oc_token_dec1val_func)oc_token_dec1val_const, - (oc_token_dec1val_func)oc_token_dec1val_const, - (oc_token_dec1val_func)oc_token_dec1val_const, - (oc_token_dec1val_func)oc_token_dec1val_const, - oc_token_dec1val_cat2, - oc_token_dec1val_cat2, - oc_token_dec1val_cat2, - oc_token_dec1val_cat2, - oc_token_dec1val_cati, - oc_token_dec1val_cati, - oc_token_dec1val_cati, - oc_token_dec1val_cati, - oc_token_dec1val_cati, - oc_token_dec1val_cati, - (oc_token_dec1val_func)oc_token_dec1val_zrl, - (oc_token_dec1val_func)oc_token_dec1val_zrl, - (oc_token_dec1val_func)oc_token_dec1val_zrl, - (oc_token_dec1val_func)oc_token_dec1val_zrl, - (oc_token_dec1val_func)oc_token_dec1val_zrl, - (oc_token_dec1val_func)oc_token_dec1val_zrl, - (oc_token_dec1val_func)oc_token_dec1val_zrl, - (oc_token_dec1val_func)oc_token_dec1val_zrl, - (oc_token_dec1val_func)oc_token_dec1val_zrl -}; - -/*Returns the decoded value of the given token. - It CANNOT be called for any of the EOB tokens. - _token: The token value to skip. - _extra_bits: The extra bits attached to this token. - Return: The decoded coefficient value.*/ -static int oc_dct_token_dec1val(int _token,int _extra_bits){ - return (*OC_TOKEN_DEC1VAL_TABLE[_token-OC_NDCT_EOB_TOKEN_MAX])(_token, - _extra_bits); -} - -/*Unpacks the DC coefficient tokens. - Unlike when unpacking the AC coefficient tokens, we actually need to decode - the DC coefficient values now so that we can do DC prediction. - _huff_idx: The index of the Huffman table to use for each color plane. - _ntoks_left: The number of tokens left to be decoded in each color plane for - each coefficient. - This is updated as EOB tokens and zero run tokens are decoded. - Return: The length of any outstanding EOB run.*/ -static int oc_dec_dc_coeff_unpack(oc_dec_ctx *_dec,int _huff_idxs[3], - int _ntoks_left[3][64]){ - long val; - int *coded_fragi; - int *coded_fragi_end; - int run_counts[64]; - int cfi; - int eobi; - int eobs; - int ti; - int ebi; - int pli; - int rli; - eobs=0; - ti=ebi=0; - coded_fragi_end=coded_fragi=_dec->state.coded_fragis; - for(pli=0;pli<3;pli++){ - coded_fragi_end+=_dec->state.ncoded_fragis[pli]; - memset(run_counts,0,sizeof(run_counts)); - _dec->eob_runs[pli][0]=eobs; - /*Continue any previous EOB run, if there was one.*/ - for(eobi=eobs;eobi-->0&&coded_fragistate.frags[*coded_fragi++].dc=0; - } - cfi=0; - while(eobs<_ntoks_left[pli][0]-cfi){ - int token; - int neb; - int eb; - int skip; - cfi+=eobs; - run_counts[63]+=eobs; - token=oc_huff_token_decode(&_dec->opb, - _dec->huff_tables[_huff_idxs[pli]]); - _dec->dct_tokens[0][ti++]=(unsigned char)token; - neb=OC_DCT_TOKEN_EXTRA_BITS[token]; - if(neb){ - theorapackB_read(&_dec->opb,neb,&val); - eb=(int)val; - _dec->extra_bits[0][ebi++]=(ogg_uint16_t)eb; - } - else eb=0; - skip=oc_dct_token_skip(token,eb); - if(skip<0){ - eobs=eobi=-skip; - while(eobi-->0&&coded_fragistate.frags[*coded_fragi++].dc=0; - } - } - else{ - run_counts[skip-1]++; - cfi++; - eobs=0; - _dec->state.frags[*coded_fragi++].dc=oc_dct_token_dec1val(token,eb); - } - } - _dec->ti0[pli][0]=ti; - _dec->ebi0[pli][0]=ebi; - /*Set the EOB count to the portion of the last EOB run which extends past - this coefficient.*/ - eobs=eobs+cfi-_ntoks_left[pli][0]; - /*Add the portion of the last EOB which was included in this coefficient to - to the longest run length.*/ - run_counts[63]+=_ntoks_left[pli][0]-cfi; - /*And convert the run_counts array to a moment table.*/ - for(rli=63;rli-->0;)run_counts[rli]+=run_counts[rli+1]; - /*Finally, subtract off the number of coefficients that have been - accounted for by runs started in this coefficient.*/ - for(rli=64;rli-->0;)_ntoks_left[pli][rli]-=run_counts[rli]; - } - return eobs; -} - -/*Unpacks the AC coefficient tokens. - This can completely discard coefficient values while unpacking, and so is - somewhat simpler than unpacking the DC coefficient tokens. - _huff_idx: The index of the Huffman table to use for each color plane. - _ntoks_left: The number of tokens left to be decoded in each color plane for - each coefficient. - This is updated as EOB tokens and zero run tokens are decoded. - _eobs: The length of any outstanding EOB run from previous - coefficients. - Return: The length of any outstanding EOB run.*/ -static int oc_dec_ac_coeff_unpack(oc_dec_ctx *_dec,int _zzi,int _huff_idxs[3], - int _ntoks_left[3][64],int _eobs){ - long val; - int run_counts[64]; - int cfi; - int ti; - int ebi; - int pli; - int rli; - ti=ebi=0; - for(pli=0;pli<3;pli++){ - memset(run_counts,0,sizeof(run_counts)); - _dec->eob_runs[pli][_zzi]=_eobs; - cfi=0; - while(_eobs<_ntoks_left[pli][_zzi]-cfi){ - int token; - int neb; - int eb; - int skip; - cfi+=_eobs; - run_counts[63]+=_eobs; - token=oc_huff_token_decode(&_dec->opb, - _dec->huff_tables[_huff_idxs[pli]]); - _dec->dct_tokens[_zzi][ti++]=(unsigned char)token; - neb=OC_DCT_TOKEN_EXTRA_BITS[token]; - if(neb){ - theorapackB_read(&_dec->opb,neb,&val); - eb=(int)val; - _dec->extra_bits[_zzi][ebi++]=(ogg_uint16_t)eb; - } - else eb=0; - skip=oc_dct_token_skip(token,eb); - if(skip<0)_eobs=-skip; - else{ - run_counts[skip-1]++; - cfi++; - _eobs=0; - } - } - _dec->ti0[pli][_zzi]=ti; - _dec->ebi0[pli][_zzi]=ebi; - /*Set the EOB count to the portion of the last EOB run which extends past - this coefficient.*/ - _eobs=_eobs+cfi-_ntoks_left[pli][_zzi]; - /*Add the portion of the last EOB which was included in this coefficient to - to the longest run length.*/ - run_counts[63]+=_ntoks_left[pli][_zzi]-cfi; - /*And convert the run_counts array to a moment table.*/ - for(rli=63;rli-->0;)run_counts[rli]+=run_counts[rli+1]; - /*Finally, subtract off the number of coefficients that have been - accounted for by runs started in this coefficient.*/ - for(rli=64-_zzi;rli-->0;)_ntoks_left[pli][_zzi+rli]-=run_counts[rli]; - } - return _eobs; -} - -/*Tokens describing the DCT coefficients that belong to each fragment are - stored in the bitstream grouped by coefficient, not by fragment. - - This means that we either decode all the tokens in order, building up a - separate coefficient list for each fragment as we go, and then go back and - do the iDCT on each fragment, or we have to create separate lists of tokens - for each coefficient, so that we can pull the next token required off the - head of the appropriate list when decoding a specific fragment. - - The former was VP3's choice, and it meant 2*w*h extra storage for all the - decoded coefficient values. - - We take the second option, which lets us store just one or three bytes per - token (generally far fewer than the number of coefficients, due to EOB - tokens and zero runs), and which requires us to only maintain a counter for - each of the 64 coefficients, instead of a counter for every fragment to - determine where the next token goes. - - Actually, we use 3 counters per coefficient, one for each color plane, so we - can decode all color planes simultaneously. - - This lets color conversion, etc., be done as soon as a full MCU (one or - two super block rows) is decoded, while the image data is still in cache.*/ - -static void oc_dec_residual_tokens_unpack(oc_dec_ctx *_dec){ - static const int OC_HUFF_LIST_MAX[5]={1,6,15,28,64}; - long val; - int ntoks_left[3][64]; - int huff_idxs[3]; - int pli; - int zzi; - int hgi; - int huffi_y; - int huffi_c; - int eobs; - for(pli=0;pli<3;pli++)for(zzi=0;zzi<64;zzi++){ - ntoks_left[pli][zzi]=_dec->state.ncoded_fragis[pli]; - } - theorapackB_read(&_dec->opb,4,&val); - huffi_y=(int)val; - theorapackB_read(&_dec->opb,4,&val); - huffi_c=(int)val; - huff_idxs[0]=huffi_y; - huff_idxs[1]=huff_idxs[2]=huffi_c; - _dec->eob_runs[0][0]=0; - eobs=oc_dec_dc_coeff_unpack(_dec,huff_idxs,ntoks_left); - theorapackB_read(&_dec->opb,4,&val); - huffi_y=(int)val; - theorapackB_read(&_dec->opb,4,&val); - huffi_c=(int)val; - zzi=1; - for(hgi=1;hgi<5;hgi++){ - huff_idxs[0]=huffi_y+(hgi<<4); - huff_idxs[1]=huff_idxs[2]=huffi_c+(hgi<<4); - for(;zzi0); - *_zzi=zzi; -} - -/*Expands a constant, single-value token.*/ -static void oc_token_expand_const(int _token,int _extra_bits, - ogg_int16_t _dct_coeffs[128],int *_zzi){ - _dct_coeffs[(*_zzi)++]=(ogg_int16_t)oc_token_dec1val_const(_token); -} - -/*Expands category 2 single-valued tokens.*/ -static void oc_token_expand_cat2(int _token,int _extra_bits, - ogg_int16_t _dct_coeffs[128],int *_zzi){ - _dct_coeffs[(*_zzi)++]= - (ogg_int16_t)oc_token_dec1val_cat2(_token,_extra_bits); -} - -/*Expands category 3 through 8 single-valued tokens.*/ -static void oc_token_expand_cati(int _token,int _extra_bits, - ogg_int16_t _dct_coeffs[128],int *_zzi){ - _dct_coeffs[(*_zzi)++]= - (ogg_int16_t)oc_token_dec1val_cati(_token,_extra_bits); -} - -/*Expands a category 1a zero run/value combo token.*/ -static void oc_token_expand_run_cat1a(int _token,int _extra_bits, - ogg_int16_t _dct_coeffs[128],int *_zzi){ - int zzi; - int rl; - zzi=*_zzi; - /*LOOP VECTORIZES.*/ - for(rl=_token-OC_DCT_RUN_CAT1A+1;rl-->0;)_dct_coeffs[zzi++]=0; - _dct_coeffs[zzi++]=(ogg_int16_t)(1-(_extra_bits<<1)); - *_zzi=zzi; -} - -/*Expands all other zero run/value combo tokens.*/ -static void oc_token_expand_run(int _token,int _extra_bits, - ogg_int16_t _dct_coeffs[128],int *_zzi){ - static const int NZEROS_ADJUST[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ - 6,10,1,2 - }; - static const int NZEROS_MASK[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ - 3,7,0,1 - }; - static const int VALUE_SHIFT[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ - 0,0,0,1 - }; - static const int VALUE_MASK[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ - 0,0,1,1 - }; - static const int VALUE_ADJUST[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ - 1,1,2,2 - }; - static const int SIGN_SHIFT[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ - 2,3,1,2 - }; - int valsigned[2]; - int zzi; - int rl; - _token-=OC_DCT_RUN_CAT1B; - rl=(_extra_bits&NZEROS_MASK[_token])+NZEROS_ADJUST[_token]; - zzi=*_zzi; - /*LOOP VECTORIZES.*/ - while(rl-->0)_dct_coeffs[zzi++]=0; - valsigned[0]=VALUE_ADJUST[_token]+ - (_extra_bits>>VALUE_SHIFT[_token]&VALUE_MASK[_token]); - valsigned[1]=-valsigned[0]; - _dct_coeffs[zzi++]=(ogg_int16_t)valsigned[ - _extra_bits>>SIGN_SHIFT[_token]]; - *_zzi=zzi; -} - -/*A jump table for expanding token values into coefficient values. - This reduces all the conditional branches, etc., needed to parse these token - values down to one indirect jump.*/ -static const oc_token_expand_func OC_TOKEN_EXPAND_TABLE[TH_NDCT_TOKENS- - OC_NDCT_EOB_TOKEN_MAX]={ - oc_token_expand_zrl, - oc_token_expand_zrl, - oc_token_expand_const, - oc_token_expand_const, - oc_token_expand_const, - oc_token_expand_const, - oc_token_expand_cat2, - oc_token_expand_cat2, - oc_token_expand_cat2, - oc_token_expand_cat2, - oc_token_expand_cati, - oc_token_expand_cati, - oc_token_expand_cati, - oc_token_expand_cati, - oc_token_expand_cati, - oc_token_expand_cati, - oc_token_expand_run_cat1a, - oc_token_expand_run_cat1a, - oc_token_expand_run_cat1a, - oc_token_expand_run_cat1a, - oc_token_expand_run_cat1a, - oc_token_expand_run, - oc_token_expand_run, - oc_token_expand_run, - oc_token_expand_run -}; - -/*Expands a single token into the given coefficient list. - This fills in the zeros for zero runs as well as coefficient values, and - updates the index of the current coefficient. - It CANNOT be called for any of the EOB tokens. - _token: The token value to expand. - _extra_bits: The extra bits associated with the token. - _dct_coeffs: The current list of coefficients, in zig-zag order. - _zzi: A pointer to the zig-zag index of the next coefficient to write - to. - This is updated before the function returns.*/ -static void oc_dct_token_expand(int _token,int _extra_bits, - ogg_int16_t *_dct_coeffs,int *_zzi){ - (*OC_TOKEN_EXPAND_TABLE[_token-OC_NDCT_EOB_TOKEN_MAX])(_token, - _extra_bits,_dct_coeffs,_zzi); -} - - - -static int oc_dec_postprocess_init(oc_dec_ctx *_dec){ - /*pp_level 0: disabled; free any memory used and return*/ - if(_dec->pp_level<=OC_PP_LEVEL_DISABLED){ - if(_dec->dc_qis!=NULL){ - _ogg_free(_dec->dc_qis); - _dec->dc_qis=NULL; - _ogg_free(_dec->variances); - _dec->variances=NULL; - _ogg_free(_dec->pp_frame_data); - _dec->pp_frame_data=NULL; - } - return 1; - } - if(_dec->dc_qis==NULL){ - /*If we haven't been tracking DC quantization indices, there's no point in - starting now.*/ - if(_dec->state.frame_type!=OC_INTRA_FRAME)return 1; - _dec->dc_qis=(unsigned char *)_ogg_malloc( - _dec->state.nfrags*sizeof(_dec->dc_qis[0])); - memset(_dec->dc_qis,_dec->state.qis[0],_dec->state.nfrags); - } - else{ - int *coded_fragi; - int *coded_fragi_end; - unsigned char qi0; - /*Update the DC quantization index of each coded block.*/ - qi0=(unsigned char)_dec->state.qis[0]; - coded_fragi_end=_dec->state.coded_fragis+_dec->state.ncoded_fragis[0]+ - _dec->state.ncoded_fragis[1]+_dec->state.ncoded_fragis[2]; - for(coded_fragi=_dec->state.coded_fragis;coded_fragidc_qis[*coded_fragi]=qi0; - } - } - /*pp_level 1: Stop after updating DC quantization indices.*/ - if(_dec->pp_level<=OC_PP_LEVEL_TRACKDCQI){ - if(_dec->variances!=NULL){ - _ogg_free(_dec->variances); - _dec->variances=NULL; - _ogg_free(_dec->pp_frame_data); - _dec->pp_frame_data=NULL; - } - return 1; - } - if(_dec->variances==NULL|| - _dec->pp_frame_has_chroma!=(_dec->pp_level>=OC_PP_LEVEL_DEBLOCKC)){ - size_t frame_sz; - frame_sz=_dec->state.info.frame_width*_dec->state.info.frame_height; - if(_dec->pp_levelvariances=(int *)_ogg_realloc(_dec->variances, - _dec->state.fplanes[0].nfrags*sizeof(_dec->variances[0])); - _dec->pp_frame_data=(unsigned char *)_ogg_realloc( - _dec->pp_frame_data,frame_sz*sizeof(_dec->pp_frame_data[0])); - _dec->pp_frame_buf[0].width=_dec->state.info.frame_width; - _dec->pp_frame_buf[0].height=_dec->state.info.frame_height; - _dec->pp_frame_buf[0].stride=-_dec->pp_frame_buf[0].width; - _dec->pp_frame_buf[0].data=_dec->pp_frame_data+ - (1-_dec->pp_frame_buf[0].height)*_dec->pp_frame_buf[0].stride; - } - else{ - size_t y_sz; - size_t c_sz; - int c_w; - int c_h; - _dec->variances=(int *)_ogg_realloc(_dec->variances, - _dec->state.nfrags*sizeof(_dec->variances[0])); - y_sz=frame_sz; - c_w=_dec->state.info.frame_width>>!(_dec->state.info.pixel_fmt&1); - c_h=_dec->state.info.frame_height>>!(_dec->state.info.pixel_fmt&2); - c_sz=c_w*c_h; - frame_sz+=c_sz<<1; - _dec->pp_frame_data=(unsigned char *)_ogg_realloc( - _dec->pp_frame_data,frame_sz*sizeof(_dec->pp_frame_data[0])); - _dec->pp_frame_buf[0].width=_dec->state.info.frame_width; - _dec->pp_frame_buf[0].height=_dec->state.info.frame_height; - _dec->pp_frame_buf[0].stride=_dec->pp_frame_buf[0].width; - _dec->pp_frame_buf[0].data=_dec->pp_frame_data; - _dec->pp_frame_buf[1].width=c_w; - _dec->pp_frame_buf[1].height=c_h; - _dec->pp_frame_buf[1].stride=_dec->pp_frame_buf[1].width; - _dec->pp_frame_buf[1].data=_dec->pp_frame_buf[0].data+y_sz; - _dec->pp_frame_buf[2].width=c_w; - _dec->pp_frame_buf[2].height=c_h; - _dec->pp_frame_buf[2].stride=_dec->pp_frame_buf[2].width; - _dec->pp_frame_buf[2].data=_dec->pp_frame_buf[1].data+c_sz; - oc_ycbcr_buffer_flip(_dec->pp_frame_buf,_dec->pp_frame_buf); - } - _dec->pp_frame_has_chroma=(_dec->pp_level>=OC_PP_LEVEL_DEBLOCKC); - } - /*If we're not processing chroma, copy the reference frame's chroma planes.*/ - if(_dec->pp_levelpp_frame_buf+1, - _dec->state.ref_frame_bufs[_dec->state.ref_frame_idx[OC_FRAME_SELF]]+1, - sizeof(_dec->pp_frame_buf[1])*2); - } - return 0; -} - - - -typedef struct{ - int ti[3][64]; - int ebi[3][64]; - int eob_runs[3][64]; - int bounding_values[256]; - int *coded_fragis[3]; - int *uncoded_fragis[3]; - int fragy0[3]; - int fragy_end[3]; - int ncoded_fragis[3]; - int nuncoded_fragis[3]; - int pred_last[3][3]; - int mcu_nvfrags; - int loop_filter; - int pp_level; -}oc_dec_pipeline_state; - - - -/*Initialize the main decoding pipeline.*/ -static void oc_dec_pipeline_init(oc_dec_ctx *_dec, - oc_dec_pipeline_state *_pipe){ - int *coded_fragi_end; - int *uncoded_fragi_end; - int pli; - /*If chroma is sub-sampled in the vertical direction, we have to decode two - super block rows of Y' for each super block row of Cb and Cr.*/ - _pipe->mcu_nvfrags=4<state.info.pixel_fmt&2); - /*Initialize the token and extra bits indices for each plane and - coefficient.*/ - memset(_pipe->ti[0],0,sizeof(_pipe->ti[0])); - memset(_pipe->ebi[0],0,sizeof(_pipe->ebi[0])); - for(pli=1;pli<3;pli++){ - memcpy(_pipe->ti[pli],_dec->ti0[pli-1],sizeof(_pipe->ti[0])); - memcpy(_pipe->ebi[pli],_dec->ebi0[pli-1],sizeof(_pipe->ebi[0])); - } - /*Also copy over the initial the EOB run counts.*/ - memcpy(_pipe->eob_runs,_dec->eob_runs,sizeof(_pipe->eob_runs)); - /*Set up per-plane pointers to the coded and uncoded fragments lists.*/ - coded_fragi_end=_dec->state.coded_fragis; - uncoded_fragi_end=_dec->state.uncoded_fragis; - for(pli=0;pli<3;pli++){ - _pipe->coded_fragis[pli]=coded_fragi_end; - _pipe->uncoded_fragis[pli]=uncoded_fragi_end; - coded_fragi_end+=_dec->state.ncoded_fragis[pli]; - uncoded_fragi_end-=_dec->state.nuncoded_fragis[pli]; - } - /*Set the previous DC predictor to 0 for all color planes and frame types.*/ - memset(_pipe->pred_last,0,sizeof(_pipe->pred_last)); - /*Initialize the bounding value array for the loop filter.*/ - _pipe->loop_filter=!oc_state_loop_filter_init(&_dec->state, - _pipe->bounding_values); - /*Initialize any buffers needed for post-processing. - We also save the current post-processing level, to guard against the user - changing it from a callback.*/ - if(!oc_dec_postprocess_init(_dec))_pipe->pp_level=_dec->pp_level; - /*If we don't have enough information to post-process, disable it, regardless - of the user-requested level.*/ - else{ - _pipe->pp_level=OC_PP_LEVEL_DISABLED; - memcpy(_dec->pp_frame_buf, - _dec->state.ref_frame_bufs[_dec->state.ref_frame_idx[OC_FRAME_SELF]], - sizeof(_dec->pp_frame_buf[0])*3); - } -} - -/*Undo the DC prediction in a single plane of an MCU (one or two super block - rows). - As a side effect, the number of coded and uncoded fragments in this plane of - the MCU is also computed.*/ -static void oc_dec_dc_unpredict_mcu_plane(oc_dec_ctx *_dec, - oc_dec_pipeline_state *_pipe,int _pli){ - /*Undo the DC prediction.*/ - oc_fragment_plane *fplane; - oc_fragment *frag; - int *pred_last; - int ncoded_fragis; - int fragx; - int fragy; - int fragy0; - int fragy_end; - /*Compute the first and last fragment row of the current MCU for this - plane.*/ - fplane=_dec->state.fplanes+_pli; - fragy0=_pipe->fragy0[_pli]; - fragy_end=_pipe->fragy_end[_pli]; - frag=_dec->state.frags+fplane->froffset+(fragy0*fplane->nhfrags); - ncoded_fragis=0; - pred_last=_pipe->pred_last[_pli]; - for(fragy=fragy0;fragynhfrags;fragx++,frag++){ - if(!frag->coded)continue; - pred_last[OC_FRAME_FOR_MODE[frag->mbmode]]=frag->dc+= - oc_frag_pred_dc(frag,fplane,fragx,fragy,pred_last); - ncoded_fragis++; - } - } - _pipe->ncoded_fragis[_pli]=ncoded_fragis; - /*Also save the number of uncoded fragments so we know how many to copy.*/ - _pipe->nuncoded_fragis[_pli]= - (fragy_end-fragy0)*fplane->nhfrags-ncoded_fragis; -} - -/*Reconstructs all coded fragments in a single MCU (one or two super block - rows). - This requires that each coded fragment have a proper macro block mode and - motion vector (if not in INTRA mode), and have it's DC value decoded, with - the DC prediction process reversed, and the number of coded and uncoded - fragments in this plane of the MCU be counted. - The token lists for each color plane and coefficient should also be filled - in, along with initial token offsets, extra bits offsets, and EOB run - counts.*/ -static void oc_dec_frags_recon_mcu_plane(oc_dec_ctx *_dec, - oc_dec_pipeline_state *_pipe,int _pli){ - /*Decode the AC coefficients.*/ - int *ti; - int *ebi; - int *eob_runs; - int *coded_fragi; - int *coded_fragi_end; - ti=_pipe->ti[_pli]; - ebi=_pipe->ebi[_pli]; - eob_runs=_pipe->eob_runs[_pli]; - coded_fragi_end=coded_fragi=_pipe->coded_fragis[_pli]; - coded_fragi_end+=_pipe->ncoded_fragis[_pli]; - for(;coded_fragistate.frags+fragi; - for(zzi=0;zzi<64;){ - int token; - int eb; - last_zzi=zzi; - if(eob_runs[zzi]){ - eob_runs[zzi]--; - break; - } - else{ - int ebflag; - token=_dec->dct_tokens[zzi][ti[zzi]++]; - ebflag=OC_DCT_TOKEN_EXTRA_BITS[token]!=0; - eb=_dec->extra_bits[zzi][ebi[zzi]]&-ebflag; - ebi[zzi]+=ebflag; - if(tokendc; - iquants=_dec->state.dequant_tables[frag->mbmode!=OC_MODE_INTRA][_pli]; - /*last_zzi is always initialized. - If your compiler thinks otherwise, it is dumb.*/ - oc_state_frag_recon(&_dec->state,frag,_pli,dct_coeffs,last_zzi,zzi, - iquants[_dec->state.qis[0]][0],iquants[frag->qi]); - } - _pipe->coded_fragis[_pli]=coded_fragi; - /*Right now the reconstructed MCU has only the coded blocks in it.*/ - /*TODO: We make the decision here to always copy the uncoded blocks into it - from the reference frame. - We could also copy the coded blocks back over the reference frame, if we - wait for an additional MCU to be decoded, which might be faster if only a - small number of blocks are coded. - However, this introduces more latency, creating a larger cache footprint. - It's unknown which decision is better, but this one results in simpler - code, and the hard case (high bitrate, high resolution) is handled - correctly.*/ - /*Copy the uncoded blocks from the previous reference frame.*/ - _pipe->uncoded_fragis[_pli]-=_pipe->nuncoded_fragis[_pli]; - oc_state_frag_copy(&_dec->state,_pipe->uncoded_fragis[_pli], - _pipe->nuncoded_fragis[_pli],OC_FRAME_SELF,OC_FRAME_PREV,_pli); -} - -/*Filter a horizontal block edge.*/ -static void oc_filter_hedge(unsigned char *_dst,int _dst_ystride, - const unsigned char *_src,int _src_ystride,int _qstep,int _flimit, - int *_variance0,int *_variance1){ - unsigned char *rdst; - const unsigned char *rsrc; - unsigned char *cdst; - const unsigned char *csrc; - int r[10]; - int sum0; - int sum1; - int bx; - int by; - rdst=_dst; - rsrc=_src; - for(bx=0;bx<8;bx++){ - cdst=rdst; - csrc=rsrc; - for(by=0;by<10;by++){ - r[by]=*csrc; - csrc+=_src_ystride; - } - sum0=sum1=0; - for(by=0;by<4;by++){ - sum0+=abs(r[by+1]-r[by]); - sum1+=abs(r[by+5]-r[by+6]); - } - *_variance0+=OC_MINI(255,sum0); - *_variance1+=OC_MINI(255,sum1); - if(sum0<_flimit&&sum1<_flimit&&r[5]-r[4]<_qstep&&r[4]-r[5]<_qstep){ - *cdst=(unsigned char)(r[0]*3+r[1]*2+r[2]+r[3]+r[4]+4>>3); - cdst+=_dst_ystride; - *cdst=(unsigned char)(r[0]*2+r[1]+r[2]*2+r[3]+r[4]+r[5]+4>>3); - cdst+=_dst_ystride; - for(by=0;by<4;by++){ - *cdst=(unsigned char)(r[by]+r[by+1]+r[by+2]+r[by+3]*2+ - r[by+4]+r[by+5]+r[by+6]+4>>3); - cdst+=_dst_ystride; - } - *cdst=(unsigned char)(r[4]+r[5]+r[6]+r[7]*2+r[8]+r[9]*2+4>>3); - cdst+=_dst_ystride; - *cdst=(unsigned char)(r[5]+r[6]+r[7]+r[8]*2+r[9]*3+4>>3); - } - else{ - for(by=1;by<=8;by++){ - *cdst=(unsigned char)r[by]; - cdst+=_dst_ystride; - } - } - rdst++; - rsrc++; - } -} - -/*Filter a vertical block edge.*/ -static void oc_filter_vedge(unsigned char *_dst,int _dst_ystride, - int _qstep,int _flimit,int *_variances){ - unsigned char *rdst; - const unsigned char *rsrc; - unsigned char *cdst; - int r[10]; - int sum0; - int sum1; - int bx; - int by; - cdst=_dst; - for(by=0;by<8;by++){ - rsrc=cdst-1; - rdst=cdst; - for(bx=0;bx<10;bx++)r[bx]=*rsrc++; - sum0=sum1=0; - for(bx=0;bx<4;bx++){ - sum0+=abs(r[bx+1]-r[bx]); - sum1+=abs(r[bx+5]-r[bx+6]); - } - _variances[0]+=OC_MINI(255,sum0); - _variances[1]+=OC_MINI(255,sum1); - if(sum0<_flimit&&sum1<_flimit&&r[5]-r[4]<_qstep&&r[4]-r[5]<_qstep){ - *rdst++=(unsigned char)(r[0]*3+r[1]*2+r[2]+r[3]+r[4]+4>>3); - *rdst++=(unsigned char)(r[0]*2+r[1]+r[2]*2+r[3]+r[4]+r[5]+4>>3); - for(bx=0;bx<4;bx++){ - *rdst++=(unsigned char)(r[bx]+r[bx+1]+r[bx+2]+r[bx+3]*2+ - r[bx+4]+r[bx+5]+r[bx+6]+4>>3); - } - *rdst++=(unsigned char)(r[4]+r[5]+r[6]+r[7]*2+r[8]+r[9]*2+4>>3); - *rdst=(unsigned char)(r[5]+r[6]+r[7]+r[8]*2+r[9]*3+4>>3); - } - else for(bx=1;bx<=8;bx++)*rdst++=(unsigned char)r[bx]; - cdst+=_dst_ystride; - } -} - -static void oc_dec_deblock_frag_rows(oc_dec_ctx *_dec, - th_img_plane *_dst,th_img_plane *_src,int _pli,int _fragy0, - int _fragy_end){ - oc_fragment_plane *fplane; - int *variance; - unsigned char *dc_qi; - unsigned char *dst; - const unsigned char *src; - int notstart; - int notdone; - int froffset; - int flimit; - int qstep; - int y_end; - int y; - int x; - _dst+=_pli; - _src+=_pli; - fplane=_dec->state.fplanes+_pli; - froffset=fplane->froffset+_fragy0*fplane->nhfrags; - variance=_dec->variances+froffset; - dc_qi=_dec->dc_qis+froffset; - notstart=_fragy0>0; - notdone=_fragy_endnvfrags; - /*We want to clear an extra row of variances, except at the end.*/ - memset(variance+(fplane->nhfrags&-notstart),0, - (_fragy_end+notdone-_fragy0-notstart)*fplane->nhfrags*sizeof(variance[0])); - /*Except for the first time, we want to point to the middle of the row.*/ - y=(_fragy0<<3)+(notstart<<2); - dst=_dst->data+y*_dst->stride; - src=_src->data+y*_src->stride; - for(;y<4;y++){ - memcpy(dst,src,_dst->width*sizeof(dst[0])); - dst+=_dst->stride; - src+=_src->stride; - } - /*We also want to skip the last row in the frame for this loop.*/ - y_end=_fragy_end-!notdone<<3; - for(;ypp_dc_scale[*dc_qi]; - flimit=(qstep*3)>>2; - oc_filter_hedge(dst,_dst->stride,src-_src->stride,_src->stride, - qstep,flimit,variance,variance+fplane->nhfrags); - variance++; - dc_qi++; - for(x=8;x<_dst->width;x+=8){ - qstep=_dec->pp_dc_scale[*dc_qi]; - flimit=(qstep*3)>>2; - oc_filter_hedge(dst+x,_dst->stride,src+x-_src->stride,_src->stride, - qstep,flimit,variance,variance+fplane->nhfrags); - oc_filter_vedge(dst+x-(_dst->stride<<2)-4,_dst->stride, - qstep,flimit,variance-1); - variance++; - dc_qi++; - } - dst+=_dst->stride<<3; - src+=_src->stride<<3; - } - /*And finally, handle the last row in the frame, if it's in the range.*/ - if(!notdone){ - for(;y<_dst->height;y++){ - memcpy(dst,src,_dst->width*sizeof(dst[0])); - dst+=_dst->stride; - src+=_src->stride; - } - /*Filter the last row of vertical block edges.*/ - dc_qi++; - for(x=8;x<_dst->width;x+=8){ - qstep=_dec->pp_dc_scale[*dc_qi++]; - flimit=(qstep*3)>>2; - oc_filter_vedge(dst+x-(_dst->stride<<3)-4,_dst->stride, - qstep,flimit,variance++); - } - } -} - -static void oc_dering_block(unsigned char *_idata,int _ystride,int _b, - int _dc_scale,int _sharp_mod,int _strong){ - static const int MOD_MAX[2]={24,32}; - static const int MOD_SHIFT[2]={1,0}; - const unsigned char *psrc; - const unsigned char *src; - const unsigned char *nsrc; - unsigned char *dst; - int vmod[72]; - int hmod[72]; - int mod_hi; - int by; - int bx; - mod_hi=OC_MINI(3*_dc_scale,MOD_MAX[_strong]); - dst=_idata; - src=dst; - psrc=src-(_ystride&-!(_b&4)); - for(by=0;by<9;by++){ - for(bx=0;bx<8;bx++){ - int mod; - mod=32+_dc_scale-(abs(src[bx]-psrc[bx])<>7); - for(bx=1;bx<7;bx++){ - a=128; - b=64; - w=hmod[(bx<<3)+by]; - a-=w; - b+=w*src[bx-1]; - w=vmod[(by<<3)+bx]; - a-=w; - b+=w*psrc[bx]; - w=vmod[(by+1<<3)+bx]; - a-=w; - b+=w*nsrc[bx]; - w=hmod[(bx+1<<3)+by]; - a-=w; - b+=w*src[bx+1]; - dst[bx]=OC_CLAMP255(a*src[bx]+b>>7); - } - a=128; - b=64; - w=hmod[(7<<3)+by]; - a-=w; - b+=w*src[6]; - w=vmod[(by<<3)+7]; - a-=w; - b+=w*psrc[7]; - w=vmod[(by+1<<3)+7]; - a-=w; - b+=w*nsrc[7]; - w=hmod[(8<<3)+by]; - a-=w; - b+=w*src[7+!(_b&2)]; - dst[7]=OC_CLAMP255(a*src[7]+b>>7); - dst+=_ystride; - psrc=src; - src=nsrc; - nsrc+=_ystride&-(!(_b&8)|by<6); - } -} - -#define OC_DERING_THRESH1 (384) -#define OC_DERING_THRESH2 (4*OC_DERING_THRESH1) -#define OC_DERING_THRESH3 (5*OC_DERING_THRESH1) -#define OC_DERING_THRESH4 (10*OC_DERING_THRESH1) - -static void oc_dec_dering_frag_rows(oc_dec_ctx *_dec,th_img_plane *_img, - int _pli,int _fragy0,int _fragy_end){ - th_img_plane *iplane; - oc_fragment_plane *fplane; - oc_fragment *frag; - int *variance; - unsigned char *idata; - int sthresh; - int strong; - int froffset; - int y_end; - int y; - int x; - iplane=_img+_pli; - fplane=_dec->state.fplanes+_pli; - froffset=fplane->froffset+_fragy0*fplane->nhfrags; - variance=_dec->variances+froffset; - frag=_dec->state.frags+froffset; - strong=_dec->pp_level>=(_pli?OC_PP_LEVEL_SDERINGC:OC_PP_LEVEL_SDERINGY); - sthresh=_pli?OC_DERING_THRESH4:OC_DERING_THRESH3; - y=_fragy0<<3; - idata=iplane->data+y*iplane->stride; - y_end=_fragy_end<<3; - for(;ywidth;x+=8){ - int b; - int qi; - int var; - qi=frag->qi; - var=*variance; - b=(x<=0)|(x+8>=iplane->width)<<1|(y<=0)<<2|(y+8>=iplane->height)<<3; - if(strong&&var>sthresh){ - oc_dering_block(idata+x,iplane->stride,b, - _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1); - if(_pli||!(b&1)&&*(variance-1)>OC_DERING_THRESH4|| - !(b&2)&&variance[1]>OC_DERING_THRESH4|| - !(b&4)&&*(variance-fplane->nvfrags)>OC_DERING_THRESH4|| - !(b&8)&&variance[fplane->nvfrags]>OC_DERING_THRESH4){ - oc_dering_block(idata+x,iplane->stride,b, - _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1); - oc_dering_block(idata+x,iplane->stride,b, - _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1); - } - } - else if(var>OC_DERING_THRESH2){ - oc_dering_block(idata+x,iplane->stride,b, - _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1); - } - else if(var>OC_DERING_THRESH1){ - oc_dering_block(idata+x,iplane->stride,b, - _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],0); - } - frag++; - variance++; - } - idata+=iplane->stride<<3; - } -} - - - -th_dec_ctx *th_decode_alloc(const th_info *_info, - const th_setup_info *_setup){ - oc_dec_ctx *dec; - if(_info==NULL||_setup==NULL)return NULL; - dec=_ogg_malloc(sizeof(*dec)); - if(oc_dec_init(dec,_info,_setup)<0){ - _ogg_free(dec); - return NULL; - } - dec->state.curframe_num=0; - return dec; -} - -void th_decode_free(th_dec_ctx *_dec){ - if(_dec!=NULL){ - oc_dec_clear(_dec); - _ogg_free(_dec); - } -} - -int th_decode_ctl(th_dec_ctx *_dec,int _req,void *_buf, - size_t _buf_sz){ - switch(_req){ - case TH_DECCTL_GET_PPLEVEL_MAX:{ - if(_dec==NULL||_buf==NULL)return TH_EFAULT; - if(_buf_sz!=sizeof(int))return TH_EINVAL; - (*(int *)_buf)=OC_PP_LEVEL_MAX; - return 0; - }break; - case TH_DECCTL_SET_PPLEVEL:{ - int pp_level; - if(_dec==NULL||_buf==NULL)return TH_EFAULT; - if(_buf_sz!=sizeof(int))return TH_EINVAL; - pp_level=*(int *)_buf; - if(pp_level<0||pp_level>OC_PP_LEVEL_MAX)return TH_EINVAL; - _dec->pp_level=pp_level; - return 0; - }break; - case TH_DECCTL_SET_GRANPOS:{ - ogg_int64_t granpos; - if(_dec==NULL||_buf==NULL)return TH_EFAULT; - if(_buf_sz!=sizeof(ogg_int64_t))return TH_EINVAL; - granpos=*(ogg_int64_t *)_buf; - if(granpos<0)return TH_EINVAL; - _dec->state.granpos=granpos; - _dec->state.keyframe_num= - granpos>>_dec->state.info.keyframe_granule_shift; - _dec->state.curframe_num=_dec->state.keyframe_num+ - (granpos&(1<<_dec->state.info.keyframe_granule_shift)-1); - return 0; - }break; - case TH_DECCTL_SET_STRIPE_CB:{ - th_stripe_callback *cb; - if(_dec==NULL||_buf==NULL)return TH_EFAULT; - if(_buf_sz!=sizeof(th_stripe_callback))return TH_EINVAL; - cb=(th_stripe_callback *)_buf; - _dec->stripe_cb.ctx=cb->ctx; - _dec->stripe_cb.stripe_decoded=cb->stripe_decoded; - return 0; - }break; - default:return TH_EIMPL; - } -} - -int th_decode_packetin(th_dec_ctx *_dec,const ogg_packet *_op, - ogg_int64_t *_granpos){ - int ret; - if(_dec==NULL||_op==NULL)return TH_EFAULT; - /*A completely empty packet indicates a dropped frame and is treated exactly - like an inter frame with no coded blocks. - Only proceed if we have a non-empty packet.*/ - if(_op->bytes!=0){ - oc_dec_pipeline_state pipe; - th_ycbcr_buffer stripe_buf; - int stripe_fragy; - int refi; - int pli; - int notstart; - int notdone; - theorapackB_readinit(&_dec->opb,_op->packet,_op->bytes); - ret=oc_dec_frame_header_unpack(_dec); - if(ret<0)return ret; - /*Select a free buffer to use for the reconstructed version of this - frame.*/ - if(_dec->state.frame_type!=OC_INTRA_FRAME&& - (_dec->state.ref_frame_idx[OC_FRAME_GOLD]<0|| - _dec->state.ref_frame_idx[OC_FRAME_PREV]<0)){ - th_info *info; - size_t yplane_sz; - size_t cplane_sz; - int yhstride; - int yvstride; - int chstride; - int cvstride; - /*We're decoding an INTER frame, but have no initialized reference - buffers (i.e., decoding did not start on a key frame). - We initialize them to a solid gray here.*/ - _dec->state.ref_frame_idx[OC_FRAME_GOLD]=0; - _dec->state.ref_frame_idx[OC_FRAME_PREV]=0; - _dec->state.ref_frame_idx[OC_FRAME_SELF]=refi=1; - info=&_dec->state.info; - yhstride=info->frame_width+2*OC_UMV_PADDING; - yvstride=info->frame_height+2*OC_UMV_PADDING; - chstride=yhstride>>!(info->pixel_fmt&1); - cvstride=yvstride>>!(info->pixel_fmt&2); - yplane_sz=(size_t)yhstride*yvstride; - cplane_sz=(size_t)chstride*cvstride; - memset(_dec->state.ref_frame_data,0x80,yplane_sz+2*cplane_sz); - } - else{ - for(refi=0;refi==_dec->state.ref_frame_idx[OC_FRAME_GOLD]|| - refi==_dec->state.ref_frame_idx[OC_FRAME_PREV];refi++); - _dec->state.ref_frame_idx[OC_FRAME_SELF]=refi; - } - if(_dec->state.frame_type==OC_INTRA_FRAME){ - oc_dec_mark_all_intra(_dec); - _dec->state.keyframe_num=_dec->state.curframe_num; - }else{ - oc_dec_coded_flags_unpack(_dec); - oc_dec_mb_modes_unpack(_dec); - oc_dec_mv_unpack_and_frag_modes_fill(_dec); - } - oc_dec_block_qis_unpack(_dec); - oc_dec_residual_tokens_unpack(_dec); - /*Update granule position. - This must be done before the striped decode callbacks so that the - application knows what to do with the frame data.*/ - _dec->state.granpos= - (_dec->state.keyframe_num<<_dec->state.info.keyframe_granule_shift)+ - (_dec->state.curframe_num-_dec->state.keyframe_num); - _dec->state.curframe_num++; - if(_granpos!=NULL)*_granpos=_dec->state.granpos; - /*All of the rest of the operations -- DC prediction reversal, - reconstructing coded fragments, copying uncoded fragments, loop - filtering, extending borders, and out-of-loop post-processing -- should - be pipelined. - I.e., DC prediction reversal, reconstruction, and uncoded fragment - copying are done for one or two super block rows, then loop filtering is - run as far as it can, then bordering copying, then post-processing. - For 4:2:0 video a Minimum Codable Unit or MCU contains two luma super - block rows, and one chroma. - Otherwise, an MCU consists of one super block row from each plane. - Inside each MCU, we perform all of the steps on one color plane before - moving on to the next. - After reconstruction, the additional filtering stages introduce a delay - since they need some pixels from the next fragment row. - Thus the actual number of decoded rows available is slightly smaller for - the first MCU, and slightly larger for the last. - - This entire process allows us to operate on the data while it is still in - cache, resulting in big performance improvements. - An application callback allows further application processing (blitting - to video memory, color conversion, etc.) to also use the data while it's - in cache.*/ - oc_dec_pipeline_init(_dec,&pipe); - oc_ycbcr_buffer_flip(stripe_buf,_dec->pp_frame_buf); - notstart=0; - notdone=1; - for(stripe_fragy=notstart=0;notdone;stripe_fragy+=pipe.mcu_nvfrags){ - int avail_fragy0; - int avail_fragy_end; - avail_fragy0=avail_fragy_end=_dec->state.fplanes[0].nvfrags; - notdone=stripe_fragy+pipe.mcu_nvfragsstate.fplanes+pli; - /*Compute the first and last fragment row of the current MCU for this - plane.*/ - frag_shift=pli!=0&&!(_dec->state.info.pixel_fmt&2); - pipe.fragy0[pli]=stripe_fragy>>frag_shift; - pipe.fragy_end[pli]=OC_MINI(fplane->nvfrags, - pipe.fragy0[pli]+(pipe.mcu_nvfrags>>frag_shift)); - oc_dec_dc_unpredict_mcu_plane(_dec,&pipe,pli); - oc_dec_frags_recon_mcu_plane(_dec,&pipe,pli); - sdelay=edelay=0; - if(pipe.loop_filter){ - sdelay+=notstart; - edelay+=notdone; - oc_state_loop_filter_frag_rows(&_dec->state,pipe.bounding_values, - refi,pli,pipe.fragy0[pli]-sdelay,pipe.fragy_end[pli]-edelay); - } - /*To fill the borders, we have an additional two pixel delay, since a - fragment in the next row could filter its top edge, using two pixels - from a fragment in this row. - But there's no reason to delay a full fragment between the two.*/ - oc_state_borders_fill_rows(&_dec->state,refi,pli, - (pipe.fragy0[pli]-sdelay<<3)-(sdelay<<1), - (pipe.fragy_end[pli]-edelay<<3)-(edelay<<1)); - /*Out-of-loop post-processing.*/ - pp_offset=3*(pli!=0); - if(pipe.pp_level>=OC_PP_LEVEL_DEBLOCKY+pp_offset){ - /*Perform de-blocking in one plane.*/ - sdelay+=notstart; - edelay+=notdone; - oc_dec_deblock_frag_rows(_dec,_dec->pp_frame_buf, - _dec->state.ref_frame_bufs[refi],pli, - pipe.fragy0[pli]-sdelay,pipe.fragy_end[pli]-edelay); - if(pipe.pp_level>=OC_PP_LEVEL_DERINGY+pp_offset){ - /*Perform de-ringing in one plane.*/ - sdelay+=notstart; - edelay+=notdone; - oc_dec_dering_frag_rows(_dec,_dec->pp_frame_buf,pli, - pipe.fragy0[pli]-sdelay,pipe.fragy_end[pli]-edelay); - } - } - /*If no post-processing is done, we still need to delay a row for the - loop filter, thanks to the strange filtering order VP3 chose.*/ - else if(pipe.loop_filter){ - sdelay+=notstart; - edelay+=notdone; - } - /*Compute the intersection of the available rows in all planes. - If chroma is sub-sampled, the effect of each of its delays is - doubled, but luma might have more post-processing filters enabled - than chroma, so we don't know up front which one is the limiting - factor.*/ - avail_fragy0=OC_MINI(avail_fragy0,pipe.fragy0[pli]-sdelay<stripe_cb.stripe_decoded!=NULL){ - /*Make the callback, ensuring we flip the sense of the "start" and - "end" of the available region upside down.*/ - (*_dec->stripe_cb.stripe_decoded)(_dec->stripe_cb.ctx,stripe_buf, - _dec->state.fplanes[0].nvfrags-avail_fragy_end, - _dec->state.fplanes[0].nvfrags-avail_fragy0); - } - notstart=1; - } - /*Finish filling in the reference frame borders.*/ - for(pli=0;pli<3;pli++)oc_state_borders_fill_caps(&_dec->state,refi,pli); - /*Update the reference frame indices.*/ - if(_dec->state.frame_type==OC_INTRA_FRAME){ - /*The new frame becomes both the previous and gold reference frames.*/ - _dec->state.ref_frame_idx[OC_FRAME_GOLD]= - _dec->state.ref_frame_idx[OC_FRAME_PREV]= - _dec->state.ref_frame_idx[OC_FRAME_SELF]; - } - else{ - /*Otherwise, just replace the previous reference frame.*/ - _dec->state.ref_frame_idx[OC_FRAME_PREV]= - _dec->state.ref_frame_idx[OC_FRAME_SELF]; - } -#if defined(OC_DUMP_IMAGES) - /*Don't dump images for dropped frames.*/ - oc_state_dump_frame(&_dec->state,OC_FRAME_SELF,"dec"); -#endif - return 0; - } - else{ - /*Just update the granule position and return.*/ - _dec->state.granpos= - (_dec->state.keyframe_num<<_dec->state.info.keyframe_granule_shift)+ - (_dec->state.curframe_num-_dec->state.keyframe_num); - _dec->state.curframe_num++; - if(_granpos!=NULL)*_granpos=_dec->state.granpos; - return TH_DUPFRAME; - } -} - -int th_decode_ycbcr_out(th_dec_ctx *_dec,th_ycbcr_buffer _ycbcr){ - oc_ycbcr_buffer_flip(_ycbcr,_dec->pp_frame_buf); - return 0; -} diff --git a/Engine/lib/libtheora/lib/dec/fragment.c b/Engine/lib/libtheora/lib/dec/fragment.c deleted file mode 100644 index 77f1c7f6b..000000000 --- a/Engine/lib/libtheora/lib/dec/fragment.c +++ /dev/null @@ -1,199 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: fragment.c 15469 2008-10-30 12:49:42Z tterribe $ - - ********************************************************************/ - -#include "../internal.h" - -void oc_frag_recon_intra(const oc_theora_state *_state,unsigned char *_dst, - int _dst_ystride,const ogg_int16_t *_residue){ - _state->opt_vtable.frag_recon_intra(_dst,_dst_ystride,_residue); -} - -void oc_frag_recon_intra_c(unsigned char *_dst,int _dst_ystride, - const ogg_int16_t *_residue){ - int i; - for(i=0;i<8;i++){ - int j; - for(j=0;j<8;j++){ - int res; - res=*_residue++; - _dst[j]=OC_CLAMP255(res+128); - } - _dst+=_dst_ystride; - } -} - -void oc_frag_recon_inter(const oc_theora_state *_state,unsigned char *_dst, - int _dst_ystride,const unsigned char *_src,int _src_ystride, - const ogg_int16_t *_residue){ - _state->opt_vtable.frag_recon_inter(_dst,_dst_ystride,_src,_src_ystride, - _residue); -} - -void oc_frag_recon_inter_c(unsigned char *_dst,int _dst_ystride, - const unsigned char *_src,int _src_ystride,const ogg_int16_t *_residue){ - int i; - for(i=0;i<8;i++){ - int j; - for(j=0;j<8;j++){ - int res; - res=*_residue++; - _dst[j]=OC_CLAMP255(res+_src[j]); - } - _dst+=_dst_ystride; - _src+=_src_ystride; - } -} - -void oc_frag_recon_inter2(const oc_theora_state *_state,unsigned char *_dst, - int _dst_ystride,const unsigned char *_src1,int _src1_ystride, - const unsigned char *_src2,int _src2_ystride,const ogg_int16_t *_residue){ - _state->opt_vtable.frag_recon_inter2(_dst,_dst_ystride,_src1,_src1_ystride, - _src2,_src2_ystride,_residue); -} - -void oc_frag_recon_inter2_c(unsigned char *_dst,int _dst_ystride, - const unsigned char *_src1,int _src1_ystride,const unsigned char *_src2, - int _src2_ystride,const ogg_int16_t *_residue){ - int i; - for(i=0;i<8;i++){ - int j; - for(j=0;j<8;j++){ - int res; - res=*_residue++; - _dst[j]=OC_CLAMP255(res+((int)_src1[j]+_src2[j]>>1)); - } - _dst+=_dst_ystride; - _src1+=_src1_ystride; - _src2+=_src2_ystride; - } -} - -/*Computes the predicted DC value for the given fragment. - This requires that the fully decoded DC values be available for the left, - upper-left, upper, and upper-right fragments (if they exist). - _frag: The fragment to predict the DC value for. - _fplane: The fragment plane the fragment belongs to. - _x: The x-coordinate of the fragment. - _y: The y-coordinate of the fragment. - _pred_last: The last fully-decoded DC value for each predictor frame - (OC_FRAME_GOLD, OC_FRAME_PREV and OC_FRAME_SELF). - This should be initialized to 0's for the first fragment in each - color plane. - Return: The predicted DC value for this fragment.*/ -int oc_frag_pred_dc(const oc_fragment *_frag, - const oc_fragment_plane *_fplane,int _x,int _y,int _pred_last[3]){ - static const int PRED_SCALE[16][4]={ - /*0*/ - {0,0,0,0}, - /*OC_PL*/ - {1,0,0,0}, - /*OC_PUL*/ - {1,0,0,0}, - /*OC_PL|OC_PUL*/ - {1,0,0,0}, - /*OC_PU*/ - {1,0,0,0}, - /*OC_PL|OC_PU*/ - {1,1,0,0}, - /*OC_PUL|OC_PU*/ - {0,1,0,0}, - /*OC_PL|OC_PUL|PC_PU*/ - {29,-26,29,0}, - /*OC_PUR*/ - {1,0,0,0}, - /*OC_PL|OC_PUR*/ - {75,53,0,0}, - /*OC_PUL|OC_PUR*/ - {1,1,0,0}, - /*OC_PL|OC_PUL|OC_PUR*/ - {75,0,53,0}, - /*OC_PU|OC_PUR*/ - {1,0,0,0}, - /*OC_PL|OC_PU|OC_PUR*/ - {75,0,53,0}, - /*OC_PUL|OC_PU|OC_PUR*/ - {3,10,3,0}, - /*OC_PL|OC_PUL|OC_PU|OC_PUR*/ - {29,-26,29,0} - }; - static const int PRED_SHIFT[16]={0,0,0,0,0,1,0,5,0,7,1,7,0,7,4,5}; - static const int PRED_RMASK[16]={0,0,0,0,0,1,0,31,0,127,1,127,0,127,15,31}; - static const int BC_MASK[8]={ - /*No boundary condition.*/ - OC_PL|OC_PUL|OC_PU|OC_PUR, - /*Left column.*/ - OC_PU|OC_PUR, - /*Top row.*/ - OC_PL, - /*Top row, left column.*/ - 0, - /*Right column.*/ - OC_PL|OC_PUL|OC_PU, - /*Right and left column.*/ - OC_PU, - /*Top row, right column.*/ - OC_PL, - /*Top row, right and left column.*/ - 0 - }; - /*Predictor fragments, left, up-left, up, up-right.*/ - const oc_fragment *predfr[4]; - /*The frame used for prediction for this fragment.*/ - int pred_frame; - /*The boundary condition flags.*/ - int bc; - /*DC predictor values: left, up-left, up, up-right, missing values - skipped.*/ - int p[4]; - /*Predictor count.*/ - int np; - /*Which predictor constants to use.*/ - int pflags; - /*The predicted DC value.*/ - int ret; - int i; - pred_frame=OC_FRAME_FOR_MODE[_frag->mbmode]; - bc=(_x==0)+((_y==0)<<1)+((_x+1==_fplane->nhfrags)<<2); - predfr[0]=_frag-1; - predfr[1]=_frag-_fplane->nhfrags-1; - predfr[2]=predfr[1]+1; - predfr[3]=predfr[2]+1; - np=0; - pflags=0; - for(i=0;i<4;i++){ - int pflag; - pflag=1<coded&& - OC_FRAME_FOR_MODE[predfr[i]->mbmode]==pred_frame){ - p[np++]=predfr[i]->dc; - pflags|=pflag; - } - } - if(pflags==0)return _pred_last[pred_frame]; - else{ - ret=PRED_SCALE[pflags][0]*p[0]; - /*LOOP VECTORIZES.*/ - for(i=1;i128)ret=p[2]; - else if(abs(ret-p[0])>128)ret=p[0]; - else if(abs(ret-p[1])>128)ret=p[1]; - } - return ret; -} diff --git a/Engine/lib/libtheora/lib/dec/huffdec.c b/Engine/lib/libtheora/lib/dec/huffdec.c deleted file mode 100644 index 86c52b62f..000000000 --- a/Engine/lib/libtheora/lib/dec/huffdec.c +++ /dev/null @@ -1,325 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: huffdec.c 15431 2008-10-21 05:04:02Z giles $ - - ********************************************************************/ - -#include -#include -#include "huffdec.h" -#include "decint.h" - - -/*The ANSI offsetof macro is broken on some platforms (e.g., older DECs).*/ -#define _ogg_offsetof(_type,_field)\ - ((size_t)((char *)&((_type *)0)->_field-(char *)0)) - -/*These two functions are really part of the bitpack.c module, but - they are only used here. Declaring local static versions so they - can be inlined saves considerable function call overhead.*/ - -/*Read in bits without advancing the bitptr. - Here we assume 0<=_bits&&_bits<=32.*/ -static int theorapackB_look(oggpack_buffer *_b,int _bits,long *_ret){ - long ret; - long m; - long d; - m=32-_bits; - _bits+=_b->endbit; - d=_b->storage-_b->endbyte; - if(d<=4){ - /*Not the main path.*/ - if(d<=0){ - *_ret=0L; - return -(_bits>d*8); - } - /*If we have some bits left, but not enough, return the ones we have.*/ - if(d*8<_bits)_bits=d*8; - } - ret=_b->ptr[0]<<24+_b->endbit; - if(_bits>8){ - ret|=_b->ptr[1]<<16+_b->endbit; - if(_bits>16){ - ret|=_b->ptr[2]<<8+_b->endbit; - if(_bits>24){ - ret|=_b->ptr[3]<<_b->endbit; - if(_bits>32)ret|=_b->ptr[4]>>8-_b->endbit; - } - } - } - *_ret=((ret&0xFFFFFFFF)>>(m>>1))>>(m+1>>1); - return 0; -} - -/*advance the bitptr*/ -static void theorapackB_adv(oggpack_buffer *_b,int _bits){ - _bits+=_b->endbit; - _b->ptr+=_bits>>3; - _b->endbyte+=_bits>>3; - _b->endbit=_bits&7; -} - - -/*The log_2 of the size of a lookup table is allowed to grow to relative to - the number of unique nodes it contains. - E.g., if OC_HUFF_SLUSH is 2, then at most 75% of the space in the tree is - wasted (each node will have an amortized cost of at most 20 bytes when using - 4-byte pointers). - Larger numbers can decode tokens with fewer read operations, while smaller - numbers may save more space (requiring as little as 8 bytes amortized per - node, though there will be more nodes). - With a sample file: - 32233473 read calls are required when no tree collapsing is done (100.0%). - 19269269 read calls are required when OC_HUFF_SLUSH is 0 (59.8%). - 11144969 read calls are required when OC_HUFF_SLUSH is 1 (34.6%). - 10538563 read calls are required when OC_HUFF_SLUSH is 2 (32.7%). - 10192578 read calls are required when OC_HUFF_SLUSH is 3 (31.6%). - Since a value of 1 gets us the vast majority of the speed-up with only a - small amount of wasted memory, this is what we use.*/ -#define OC_HUFF_SLUSH (1) - - -/*Allocates a Huffman tree node that represents a subtree of depth _nbits. - _nbits: The depth of the subtree. - If this is 0, the node is a leaf node. - Otherwise 1<<_nbits pointers are allocated for children. - Return: The newly allocated and fully initialized Huffman tree node.*/ -static oc_huff_node *oc_huff_node_alloc(int _nbits){ - oc_huff_node *ret; - size_t size; - size=_ogg_offsetof(oc_huff_node,nodes); - if(_nbits>0)size+=sizeof(oc_huff_node *)*(1<<_nbits); - ret=_ogg_calloc(1,size); - ret->nbits=(unsigned char)_nbits; - return ret; -} - -/*Frees a Huffman tree node allocated with oc_huf_node_alloc. - _node: The node to free. - This may be NULL.*/ -static void oc_huff_node_free(oc_huff_node *_node){ - _ogg_free(_node); -} - -/*Frees the memory used by a Huffman tree. - _node: The Huffman tree to free. - This may be NULL.*/ -static void oc_huff_tree_free(oc_huff_node *_node){ - if(_node==NULL)return; - if(_node->nbits){ - int nchildren; - int i; - int inext; - nchildren=1<<_node->nbits; - for(i=0;inodes[i]!=NULL?1<<_node->nbits-_node->nodes[i]->depth:1); - oc_huff_tree_free(_node->nodes[i]); - } - } - oc_huff_node_free(_node); -} - -/*Unpacks a sub-tree from the given buffer. - _opb: The buffer to unpack from. - _binode: The location to store a pointer to the sub-tree in. - _depth: The current depth of the tree. - This is used to prevent infinite recursion. - Return: 0 on success, or a negative value on error.*/ -static int oc_huff_tree_unpack(oggpack_buffer *_opb, - oc_huff_node **_binode,int _depth){ - oc_huff_node *binode; - long bits; - /*Prevent infinite recursion.*/ - if(++_depth>32)return TH_EBADHEADER; - if(theorapackB_read1(_opb,&bits)<0)return TH_EBADHEADER; - /*Read an internal node:*/ - if(!bits){ - int ret; - binode=oc_huff_node_alloc(1); - binode->depth=(unsigned char)(_depth>1); - ret=oc_huff_tree_unpack(_opb,binode->nodes,_depth); - if(ret>=0)ret=oc_huff_tree_unpack(_opb,binode->nodes+1,_depth); - if(ret<0){ - oc_huff_tree_free(binode); - *_binode=NULL; - return ret; - } - } - /*Read a leaf node:*/ - else{ - if(theorapackB_read(_opb,OC_NDCT_TOKEN_BITS,&bits)<0)return TH_EBADHEADER; - binode=oc_huff_node_alloc(0); - binode->depth=(unsigned char)(_depth>1); - binode->token=(unsigned char)bits; - } - *_binode=binode; - return 0; -} - -/*Finds the depth of shortest branch of the given sub-tree. - The tree must be binary. - _binode: The root of the given sub-tree. - _binode->nbits must be 0 or 1. - Return: The smallest depth of a leaf node in this sub-tree. - 0 indicates this sub-tree is a leaf node.*/ -static int oc_huff_tree_mindepth(oc_huff_node *_binode){ - int depth0; - int depth1; - if(_binode->nbits==0)return 0; - depth0=oc_huff_tree_mindepth(_binode->nodes[0]); - depth1=oc_huff_tree_mindepth(_binode->nodes[1]); - return OC_MINI(depth0,depth1)+1; -} - -/*Finds the number of internal nodes at a given depth, plus the number of - leaves at that depth or shallower. - The tree must be binary. - _binode: The root of the given sub-tree. - _binode->nbits must be 0 or 1. - Return: The number of entries that would be contained in a jump table of the - given depth.*/ -static int oc_huff_tree_occupancy(oc_huff_node *_binode,int _depth){ - if(_binode->nbits==0||_depth<=0)return 1; - else{ - return oc_huff_tree_occupancy(_binode->nodes[0],_depth-1)+ - oc_huff_tree_occupancy(_binode->nodes[1],_depth-1); - } -} - -static oc_huff_node *oc_huff_tree_collapse(oc_huff_node *_binode); - -/*Fills the given nodes table with all the children in the sub-tree at the - given depth. - The nodes in the sub-tree with a depth less than that stored in the table - are freed. - The sub-tree must be binary and complete up until the given depth. - _nodes: The nodes table to fill. - _binode: The root of the sub-tree to fill it with. - _binode->nbits must be 0 or 1. - _level: The current level in the table. - 0 indicates that the current node should be stored, regardless of - whether it is a leaf node or an internal node. - _depth: The depth of the nodes to fill the table with, relative to their - parent.*/ -static void oc_huff_node_fill(oc_huff_node **_nodes, - oc_huff_node *_binode,int _level,int _depth){ - if(_level<=0||_binode->nbits==0){ - int i; - _binode->depth=(unsigned char)(_depth-_level); - _nodes[0]=oc_huff_tree_collapse(_binode); - for(i=1;i<1<<_level;i++)_nodes[i]=_nodes[0]; - } - else{ - _level--; - oc_huff_node_fill(_nodes,_binode->nodes[0],_level,_depth); - oc_huff_node_fill(_nodes+(1<<_level),_binode->nodes[1],_level,_depth); - oc_huff_node_free(_binode); - } -} - -/*Finds the largest complete sub-tree rooted at the current node and collapses - it into a single node. - This procedure is then applied recursively to all the children of that node. - _binode: The root of the sub-tree to collapse. - _binode->nbits must be 0 or 1. - Return: The new root of the collapsed sub-tree.*/ -static oc_huff_node *oc_huff_tree_collapse(oc_huff_node *_binode){ - oc_huff_node *root; - int mindepth; - int depth; - int loccupancy; - int occupancy; - depth=mindepth=oc_huff_tree_mindepth(_binode); - occupancy=1<loccupancy&&occupancy>=1<depth=_binode->depth; - oc_huff_node_fill(root->nodes,_binode,depth,depth); - return root; -} - -/*Makes a copy of the given Huffman tree. - _node: The Huffman tree to copy. - Return: The copy of the Huffman tree.*/ -static oc_huff_node *oc_huff_tree_copy(const oc_huff_node *_node){ - oc_huff_node *ret; - ret=oc_huff_node_alloc(_node->nbits); - ret->depth=_node->depth; - if(_node->nbits){ - int nchildren; - int i; - int inext; - nchildren=1<<_node->nbits; - for(i=0;inodes[i]=oc_huff_tree_copy(_node->nodes[i]); - inext=i+(1<<_node->nbits-ret->nodes[i]->depth); - while(++inodes[i]=ret->nodes[i-1]; - } - } - else ret->token=_node->token; - return ret; -} - -/*Unpacks a set of Huffman trees, and reduces them to a collapsed - representation. - _opb: The buffer to unpack the trees from. - _nodes: The table to fill with the Huffman trees. - Return: 0 on success, or a negative value on error.*/ -int oc_huff_trees_unpack(oggpack_buffer *_opb, - oc_huff_node *_nodes[TH_NHUFFMAN_TABLES]){ - int i; - for(i=0;inbits!=0){ - theorapackB_look(_opb,_node->nbits,&bits); - _node=_node->nodes[bits]; - theorapackB_adv(_opb,_node->depth); - } - return _node->token; -} diff --git a/Engine/lib/libtheora/lib/dec/idct.h b/Engine/lib/libtheora/lib/dec/idct.h deleted file mode 100644 index 3ee53712e..000000000 --- a/Engine/lib/libtheora/lib/dec/idct.h +++ /dev/null @@ -1,26 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: idct.h 15400 2008-10-15 12:10:58Z tterribe $ - - ********************************************************************/ - -/*Inverse DCT transforms.*/ -#include -#if !defined(_idct_H) -# define _idct_H (1) - -void oc_idct8x8_c(ogg_int16_t _y[64],const ogg_int16_t _x[64]); -void oc_idct8x8_10_c(ogg_int16_t _y[64],const ogg_int16_t _x[64]); - -#endif diff --git a/Engine/lib/libtheora/lib/dec/ocintrin.h b/Engine/lib/libtheora/lib/dec/ocintrin.h deleted file mode 100644 index 317f5aeae..000000000 --- a/Engine/lib/libtheora/lib/dec/ocintrin.h +++ /dev/null @@ -1,88 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: ocintrin.h 15400 2008-10-15 12:10:58Z tterribe $ - - ********************************************************************/ - -/*Some common macros for potential platform-specific optimization.*/ -#include -#if !defined(_ocintrin_H) -# define _ocintrin_H (1) - -/*Some specific platforms may have optimized intrinsic or inline assembly - versions of these functions which can substantially improve performance. - We define macros for them to allow easy incorporation of these non-ANSI - features.*/ - -/*Branchless, but not correct for differences larger than INT_MAX. -static int oc_mini(int _a,int _b){ - int ambsign; - ambsign=_a-_b>>sizeof(int)*8-1; - return (_a&~ambsign)+(_b&ambsign); -}*/ - - -#define OC_MAXI(_a,_b) ((_a)<(_b)?(_b):(_a)) -#define OC_MINI(_a,_b) ((_a)>(_b)?(_b):(_a)) -/*Clamps an integer into the given range. - If _a>_c, then the lower bound _a is respected over the upper bound _c (this - behavior is required to meet our documented API behavior). - _a: The lower bound. - _b: The value to clamp. - _c: The upper boud.*/ -#define OC_CLAMPI(_a,_b,_c) (OC_MAXI(_a,OC_MINI(_b,_c))) -#define OC_CLAMP255(_x) ((unsigned char)((((_x)<0)-1)&((_x)|-((_x)>255)))) -/*Divides an integer by a power of two, truncating towards 0. - _dividend: The integer to divide. - _shift: The non-negative power of two to divide by. - _rmask: (1<<_shift)-1*/ -#define OC_DIV_POW2(_dividend,_shift,_rmask)\ - ((_dividend)+(((_dividend)>>sizeof(_dividend)*8-1)&(_rmask))>>(_shift)) -/*Divides _x by 65536, truncating towards 0.*/ -#define OC_DIV2_16(_x) OC_DIV_POW2(_x,16,0xFFFF) -/*Divides _x by 2, truncating towards 0.*/ -#define OC_DIV2(_x) OC_DIV_POW2(_x,1,0x1) -/*Divides _x by 8, truncating towards 0.*/ -#define OC_DIV8(_x) OC_DIV_POW2(_x,3,0x7) -/*Divides _x by 16, truncating towards 0.*/ -#define OC_DIV16(_x) OC_DIV_POW2(_x,4,0xF) -/*Right shifts _dividend by _shift, adding _rval, and subtracting one for - negative dividends first.. - When _rval is (1<<_shift-1), this is equivalent to division with rounding - ties towards positive infinity.*/ -#define OC_DIV_ROUND_POW2(_dividend,_shift,_rval)\ - ((_dividend)+((_dividend)>>sizeof(_dividend)*8-1)+(_rval)>>(_shift)) -/*Swaps two integers _a and _b if _a>_b.*/ -#define OC_SORT2I(_a,_b)\ - if((_a)>(_b)){\ - int t__;\ - t__=(_a);\ - (_a)=(_b);\ - (_b)=t__;\ - } - - - -/*All of these macros should expect floats as arguments.*/ -#define OC_MAXF(_a,_b) ((_a)<(_b)?(_b):(_a)) -#define OC_MINF(_a,_b) ((_a)>(_b)?(_b):(_a)) -#define OC_CLAMPF(_a,_b,_c) (OC_MINF(_a,OC_MAXF(_b,_c))) -#define OC_FABSF(_f) ((float)fabs(_f)) -#define OC_SQRTF(_f) ((float)sqrt(_f)) -#define OC_POWF(_b,_e) ((float)pow(_b,_e)) -#define OC_LOGF(_f) ((float)log(_f)) -#define OC_IFLOORF(_f) ((int)floor(_f)) -#define OC_ICEILF(_f) ((int)ceil(_f)) - -#endif diff --git a/Engine/lib/libtheora/lib/dec/quant.c b/Engine/lib/libtheora/lib/dec/quant.c deleted file mode 100644 index 5cb7784db..000000000 --- a/Engine/lib/libtheora/lib/dec/quant.c +++ /dev/null @@ -1,122 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: quant.c 15400 2008-10-15 12:10:58Z tterribe $ - - ********************************************************************/ - -#include -#include -#include -#include "quant.h" -#include "decint.h" - -static const unsigned OC_DC_QUANT_MIN[2]={4<<2,8<<2}; -static const unsigned OC_AC_QUANT_MIN[2]={2<<2,4<<2}; - -/*Initializes the dequantization tables from a set of quantizer info. - Currently the dequantizer (and elsewhere enquantizer) tables are expected to - be initialized as pointing to the storage reserved for them in the - oc_theora_state (resp. oc_enc_ctx) structure. - If some tables are duplicates of others, the pointers will be adjusted to - point to a single copy of the tables, but the storage for them will not be - freed. - If you're concerned about the memory footprint, the obvious thing to do is - to move the storage out of its fixed place in the structures and allocate - it on demand. - However, a much, much better option is to only store the quantization - matrices being used for the current frame, and to recalculate these as the - qi values change between frames (this is what VP3 did).*/ -void oc_dequant_tables_init(oc_quant_table *_dequant[2][3], - int _pp_dc_scale[64],const th_quant_info *_qinfo){ - /*coding mode: intra or inter.*/ - int qti; - /*Y', C_b, C_r*/ - int pli; - for(qti=0;qti<2;qti++){ - for(pli=0;pli<3;pli++){ - oc_quant_tables stage; - /*Quality index.*/ - int qi; - /*Range iterator.*/ - int qri; - for(qi=0,qri=0; qri<=_qinfo->qi_ranges[qti][pli].nranges; qri++){ - th_quant_base base; - ogg_uint32_t q; - int qi_start; - int qi_end; - int ci; - memcpy(base,_qinfo->qi_ranges[qti][pli].base_matrices[qri], - sizeof(base)); - qi_start=qi; - if(qri==_qinfo->qi_ranges[qti][pli].nranges)qi_end=qi+1; - else qi_end=qi+_qinfo->qi_ranges[qti][pli].sizes[qri]; - /*Iterate over quality indicies in this range.*/ - for(;;){ - ogg_uint32_t qfac; - /*In the original VP3.2 code, the rounding offset and the size of the - dead zone around 0 were controlled by a "sharpness" parameter. - The size of our dead zone is now controlled by the per-coefficient - quality thresholds returned by our HVS module. - We round down from a more accurate value when the quality of the - reconstruction does not fall below our threshold and it saves bits. - Hence, all of that VP3.2 code is gone from here, and the remaining - floating point code has been implemented as equivalent integer code - with exact precision.*/ - qfac=(ogg_uint32_t)_qinfo->dc_scale[qi]*base[0]; - /*For postprocessing, not dequantization.*/ - if(_pp_dc_scale!=NULL)_pp_dc_scale[qi]=(int)(qfac/160); - /*Scale DC the coefficient from the proper table.*/ - q=(qfac/100)<<2; - q=OC_CLAMPI(OC_DC_QUANT_MIN[qti],q,OC_QUANT_MAX); - stage[qi][0]=(ogg_uint16_t)q; - /*Now scale AC coefficients from the proper table.*/ - for(ci=1;ci<64;ci++){ - q=((ogg_uint32_t)_qinfo->ac_scale[qi]*base[ci]/100)<<2; - q=OC_CLAMPI(OC_AC_QUANT_MIN[qti],q,OC_QUANT_MAX); - stage[qi][ci]=(ogg_uint16_t)q; - } - if(++qi>=qi_end)break; - /*Interpolate the next base matrix.*/ - for(ci=0;ci<64;ci++){ - base[ci]=(unsigned char)( - (2*((qi_end-qi)*_qinfo->qi_ranges[qti][pli].base_matrices[qri][ci]+ - (qi-qi_start)*_qinfo->qi_ranges[qti][pli].base_matrices[qri+1][ci]) - +_qinfo->qi_ranges[qti][pli].sizes[qri])/ - (2*_qinfo->qi_ranges[qti][pli].sizes[qri])); - } - } - } - /*Staging matrices complete; commit to memory only if this isn't a - duplicate of a preceeding plane. - This simple check helps us improve cache coherency later.*/ - { - int dupe; - int qtj; - int plj; - dupe=0; - for(qtj=0;qtj<=qti;qtj++){ - for(plj=0;plj<(qtj - -#if defined(USE_ASM) - -static const __attribute__((aligned(8),used)) int OC_FZIG_ZAGMMX[64]={ - 0, 8, 1, 2, 9,16,24,17, - 10, 3,32,11,18,25, 4,12, - 5,26,19,40,33,34,41,48, - 27, 6,13,20,28,21,14, 7, - 56,49,42,35,43,50,57,36, - 15,22,29,30,23,44,37,58, - 51,59,38,45,52,31,60,53, - 46,39,47,54,61,62,55,63 -}; - - - -void oc_state_frag_recon_mmx(oc_theora_state *_state,oc_fragment *_frag, - int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, - ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]){ - ogg_int16_t __attribute__((aligned(8))) res_buf[64]; - int dst_framei; - int dst_ystride; - int zzi; - /*_last_zzi is subtly different from an actual count of the number of - coefficients we decoded for this block. - It contains the value of zzi BEFORE the final token in the block was - decoded. - In most cases this is an EOB token (the continuation of an EOB run from a - previous block counts), and so this is the same as the coefficient count. - However, in the case that the last token was NOT an EOB token, but filled - the block up with exactly 64 coefficients, _last_zzi will be less than 64. - Provided the last token was not a pure zero run, the minimum value it can - be is 46, and so that doesn't affect any of the cases in this routine. - However, if the last token WAS a pure zero run of length 63, then _last_zzi - will be 1 while the number of coefficients decoded is 64. - Thus, we will trigger the following special case, where the real - coefficient count would not. - Note also that a zero run of length 64 will give _last_zzi a value of 0, - but we still process the DC coefficient, which might have a non-zero value - due to DC prediction. - Although convoluted, this is arguably the correct behavior: it allows us to - dequantize fewer coefficients and use a smaller transform when the block - ends with a long zero run instead of a normal EOB token. - It could be smarter... multiple separate zero runs at the end of a block - will fool it, but an encoder that generates these really deserves what it - gets. - Needless to say we inherited this approach from VP3.*/ - /*Special case only having a DC component.*/ - if(_last_zzi<2){ - ogg_uint16_t p; - /*Why is the iquant product rounded in this case and no others? - Who knows.*/ - p=(ogg_int16_t)((ogg_int32_t)_frag->dc*_dc_iquant+15>>5); - /*Fill res_buf with p.*/ - __asm__ __volatile__( - /*mm0=0000 0000 0000 AAAA*/ - "movd %[p],%%mm0\n\t" - /*mm1=0000 0000 0000 AAAA*/ - "movd %[p],%%mm1\n\t" - /*mm0=0000 0000 AAAA 0000*/ - "pslld $16,%%mm0\n\t" - /*mm0=0000 0000 AAAA AAAA*/ - "por %%mm1,%%mm0\n\t" - /*mm0=AAAA AAAA AAAA AAAA*/ - "punpcklwd %%mm0,%%mm0\n\t" - "movq %%mm0,(%[res_buf])\n\t" - "movq %%mm0,8(%[res_buf])\n\t" - "movq %%mm0,16(%[res_buf])\n\t" - "movq %%mm0,24(%[res_buf])\n\t" - "movq %%mm0,32(%[res_buf])\n\t" - "movq %%mm0,40(%[res_buf])\n\t" - "movq %%mm0,48(%[res_buf])\n\t" - "movq %%mm0,56(%[res_buf])\n\t" - "movq %%mm0,64(%[res_buf])\n\t" - "movq %%mm0,72(%[res_buf])\n\t" - "movq %%mm0,80(%[res_buf])\n\t" - "movq %%mm0,88(%[res_buf])\n\t" - "movq %%mm0,96(%[res_buf])\n\t" - "movq %%mm0,104(%[res_buf])\n\t" - "movq %%mm0,112(%[res_buf])\n\t" - "movq %%mm0,120(%[res_buf])\n\t" - : - :[res_buf]"r"(res_buf),[p]"r"((unsigned)p) - :"memory" - ); - } - else{ - /*Then, fill in the remainder of the coefficients with 0's, and perform - the iDCT.*/ - /*First zero the buffer.*/ - /*On K7, etc., this could be replaced with movntq and sfence.*/ - __asm__ __volatile__( - "pxor %%mm0,%%mm0\n\t" - "movq %%mm0,(%[res_buf])\n\t" - "movq %%mm0,8(%[res_buf])\n\t" - "movq %%mm0,16(%[res_buf])\n\t" - "movq %%mm0,24(%[res_buf])\n\t" - "movq %%mm0,32(%[res_buf])\n\t" - "movq %%mm0,40(%[res_buf])\n\t" - "movq %%mm0,48(%[res_buf])\n\t" - "movq %%mm0,56(%[res_buf])\n\t" - "movq %%mm0,64(%[res_buf])\n\t" - "movq %%mm0,72(%[res_buf])\n\t" - "movq %%mm0,80(%[res_buf])\n\t" - "movq %%mm0,88(%[res_buf])\n\t" - "movq %%mm0,96(%[res_buf])\n\t" - "movq %%mm0,104(%[res_buf])\n\t" - "movq %%mm0,112(%[res_buf])\n\t" - "movq %%mm0,120(%[res_buf])\n\t" - : - :[res_buf]"r"(res_buf) - :"memory" - ); - res_buf[0]=(ogg_int16_t)((ogg_int32_t)_frag->dc*_dc_iquant); - /*This is planned to be rewritten in MMX.*/ - for(zzi=1;zzi<_ncoefs;zzi++){ - int ci; - ci=OC_FZIG_ZAG[zzi]; - res_buf[OC_FZIG_ZAGMMX[zzi]]=(ogg_int16_t)((ogg_int32_t)_dct_coeffs[zzi]* - _ac_iquant[ci]); - } - if(_last_zzi<10)oc_idct8x8_10_mmx(res_buf); - else oc_idct8x8_mmx(res_buf); - } - /*Fill in the target buffer.*/ - dst_framei=_state->ref_frame_idx[OC_FRAME_SELF]; - dst_ystride=_state->ref_frame_bufs[dst_framei][_pli].stride; - /*For now ystride values in all ref frames assumed to be equal.*/ - if(_frag->mbmode==OC_MODE_INTRA){ - oc_frag_recon_intra_mmx(_frag->buffer[dst_framei],dst_ystride,res_buf); - } - else{ - int ref_framei; - int ref_ystride; - int mvoffsets[2]; - ref_framei=_state->ref_frame_idx[OC_FRAME_FOR_MODE[_frag->mbmode]]; - ref_ystride=_state->ref_frame_bufs[ref_framei][_pli].stride; - if(oc_state_get_mv_offsets(_state,mvoffsets,_frag->mv[0],_frag->mv[1], - ref_ystride,_pli)>1){ - oc_frag_recon_inter2_mmx(_frag->buffer[dst_framei],dst_ystride, - _frag->buffer[ref_framei]+mvoffsets[0],ref_ystride, - _frag->buffer[ref_framei]+mvoffsets[1],ref_ystride,res_buf); - } - else{ - oc_frag_recon_inter_mmx(_frag->buffer[dst_framei],dst_ystride, - _frag->buffer[ref_framei]+mvoffsets[0],ref_ystride,res_buf); - } - } - oc_restore_fpu(_state); -} - -/*Copies the fragments specified by the lists of fragment indices from one - frame to another. - _fragis: A pointer to a list of fragment indices. - _nfragis: The number of fragment indices to copy. - _dst_frame: The reference frame to copy to. - _src_frame: The reference frame to copy from. - _pli: The color plane the fragments lie in.*/ -void oc_state_frag_copy_mmx(const oc_theora_state *_state,const int *_fragis, - int _nfragis,int _dst_frame,int _src_frame,int _pli){ - const int *fragi; - const int *fragi_end; - int dst_framei; - ptrdiff_t dst_ystride; - int src_framei; - ptrdiff_t src_ystride; - dst_framei=_state->ref_frame_idx[_dst_frame]; - src_framei=_state->ref_frame_idx[_src_frame]; - dst_ystride=_state->ref_frame_bufs[dst_framei][_pli].stride; - src_ystride=_state->ref_frame_bufs[src_framei][_pli].stride; - fragi_end=_fragis+_nfragis; - for(fragi=_fragis;fragifrags+*fragi; - dst=frag->buffer[dst_framei]; - src=frag->buffer[src_framei]; - __asm__ __volatile__( - /*src+0*src_ystride*/ - "movq (%[src]),%%mm0\n\t" - /*s=src_ystride*3*/ - "lea (%[src_ystride],%[src_ystride],2),%[s]\n\t" - /*src+1*src_ystride*/ - "movq (%[src],%[src_ystride]),%%mm1\n\t" - /*src+2*src_ystride*/ - "movq (%[src],%[src_ystride],2),%%mm2\n\t" - /*src+3*src_ystride*/ - "movq (%[src],%[s]),%%mm3\n\t" - /*dst+0*dst_ystride*/ - "movq %%mm0,(%[dst])\n\t" - /*s=dst_ystride*3*/ - "lea (%[dst_ystride],%[dst_ystride],2),%[s]\n\t" - /*dst+1*dst_ystride*/ - "movq %%mm1,(%[dst],%[dst_ystride])\n\t" - /*Pointer to next 4.*/ - "lea (%[src],%[src_ystride],4),%[src]\n\t" - /*dst+2*dst_ystride*/ - "movq %%mm2,(%[dst],%[dst_ystride],2)\n\t" - /*dst+3*dst_ystride*/ - "movq %%mm3,(%[dst],%[s])\n\t" - /*Pointer to next 4.*/ - "lea (%[dst],%[dst_ystride],4),%[dst]\n\t" - /*src+0*src_ystride*/ - "movq (%[src]),%%mm0\n\t" - /*s=src_ystride*3*/ - "lea (%[src_ystride],%[src_ystride],2),%[s]\n\t" - /*src+1*src_ystride*/ - "movq (%[src],%[src_ystride]),%%mm1\n\t" - /*src+2*src_ystride*/ - "movq (%[src],%[src_ystride],2),%%mm2\n\t" - /*src+3*src_ystride*/ - "movq (%[src],%[s]),%%mm3\n\t" - /*dst+0*dst_ystride*/ - "movq %%mm0,(%[dst])\n\t" - /*s=dst_ystride*3*/ - "lea (%[dst_ystride],%[dst_ystride],2),%[s]\n\t" - /*dst+1*dst_ystride*/ - "movq %%mm1,(%[dst],%[dst_ystride])\n\t" - /*dst+2*dst_ystride*/ - "movq %%mm2,(%[dst],%[dst_ystride],2)\n\t" - /*dst+3*dst_ystride*/ - "movq %%mm3,(%[dst],%[s])\n\t" - :[s]"=&r"(s) - :[dst]"r"(dst),[src]"r"(src),[dst_ystride]"r"(dst_ystride), - [src_ystride]"r"(src_ystride) - :"memory" - ); - } - /*This needs to be removed when decode specific functions are implemented:*/ - __asm__ __volatile__("emms\n\t"); -} - -static void loop_filter_v(unsigned char *_pix,int _ystride, - const ogg_int16_t *_ll){ - ptrdiff_t s; - _pix-=_ystride*2; - __asm__ __volatile__( - /*mm0=0*/ - "pxor %%mm0,%%mm0\n\t" - /*s=_ystride*3*/ - "lea (%[ystride],%[ystride],2),%[s]\n\t" - /*mm7=_pix[0...8]*/ - "movq (%[pix]),%%mm7\n\t" - /*mm4=_pix[0...8+_ystride*3]*/ - "movq (%[pix],%[s]),%%mm4\n\t" - /*mm6=_pix[0...8]*/ - "movq %%mm7,%%mm6\n\t" - /*Expand unsigned _pix[0...3] to 16 bits.*/ - "punpcklbw %%mm0,%%mm6\n\t" - "movq %%mm4,%%mm5\n\t" - /*Expand unsigned _pix[4...8] to 16 bits.*/ - "punpckhbw %%mm0,%%mm7\n\t" - /*Expand other arrays too.*/ - "punpcklbw %%mm0,%%mm4\n\t" - "punpckhbw %%mm0,%%mm5\n\t" - /*mm7:mm6=_p[0...8]-_p[0...8+_ystride*3]:*/ - "psubw %%mm4,%%mm6\n\t" - "psubw %%mm5,%%mm7\n\t" - /*mm5=mm4=_pix[0...8+_ystride]*/ - "movq (%[pix],%[ystride]),%%mm4\n\t" - /*mm1=mm3=mm2=_pix[0..8]+_ystride*2]*/ - "movq (%[pix],%[ystride],2),%%mm2\n\t" - "movq %%mm4,%%mm5\n\t" - "movq %%mm2,%%mm3\n\t" - "movq %%mm2,%%mm1\n\t" - /*Expand these arrays.*/ - "punpckhbw %%mm0,%%mm5\n\t" - "punpcklbw %%mm0,%%mm4\n\t" - "punpckhbw %%mm0,%%mm3\n\t" - "punpcklbw %%mm0,%%mm2\n\t" - /*mm0=3 3 3 3 - mm3:mm2=_pix[0...8+_ystride*2]-_pix[0...8+_ystride]*/ - "pcmpeqw %%mm0,%%mm0\n\t" - "psubw %%mm5,%%mm3\n\t" - "psrlw $14,%%mm0\n\t" - "psubw %%mm4,%%mm2\n\t" - /*Scale by 3.*/ - "pmullw %%mm0,%%mm3\n\t" - "pmullw %%mm0,%%mm2\n\t" - /*mm0=4 4 4 4 - f=mm3:mm2==_pix[0...8]-_pix[0...8+_ystride*3]+ - 3*(_pix[0...8+_ystride*2]-_pix[0...8+_ystride])*/ - "psrlw $1,%%mm0\n\t" - "paddw %%mm7,%%mm3\n\t" - "psllw $2,%%mm0\n\t" - "paddw %%mm6,%%mm2\n\t" - /*Add 4.*/ - "paddw %%mm0,%%mm3\n\t" - "paddw %%mm0,%%mm2\n\t" - /*"Divide" by 8.*/ - "psraw $3,%%mm3\n\t" - "psraw $3,%%mm2\n\t" - /*Now compute lflim of mm3:mm2 cf. Section 7.10 of the sepc.*/ - /*Free up mm5.*/ - "packuswb %%mm5,%%mm4\n\t" - /*mm0=L L L L*/ - "movq (%[ll]),%%mm0\n\t" - /*if(R_i<-2L||R_i>2L)R_i=0:*/ - "movq %%mm2,%%mm5\n\t" - "pxor %%mm6,%%mm6\n\t" - "movq %%mm0,%%mm7\n\t" - "psubw %%mm0,%%mm6\n\t" - "psllw $1,%%mm7\n\t" - "psllw $1,%%mm6\n\t" - /*mm2==R_3 R_2 R_1 R_0*/ - /*mm5==R_3 R_2 R_1 R_0*/ - /*mm6==-2L -2L -2L -2L*/ - /*mm7==2L 2L 2L 2L*/ - "pcmpgtw %%mm2,%%mm7\n\t" - "pcmpgtw %%mm6,%%mm5\n\t" - "pand %%mm7,%%mm2\n\t" - "movq %%mm0,%%mm7\n\t" - "pand %%mm5,%%mm2\n\t" - "psllw $1,%%mm7\n\t" - "movq %%mm3,%%mm5\n\t" - /*mm3==R_7 R_6 R_5 R_4*/ - /*mm5==R_7 R_6 R_5 R_4*/ - /*mm6==-2L -2L -2L -2L*/ - /*mm7==2L 2L 2L 2L*/ - "pcmpgtw %%mm3,%%mm7\n\t" - "pcmpgtw %%mm6,%%mm5\n\t" - "pand %%mm7,%%mm3\n\t" - "movq %%mm0,%%mm7\n\t" - "pand %%mm5,%%mm3\n\t" - /*if(R_i<-L)R_i'=R_i+2L; - if(R_i>L)R_i'=R_i-2L; - if(R_i<-L||R_i>L)R_i=-R_i':*/ - "psraw $1,%%mm6\n\t" - "movq %%mm2,%%mm5\n\t" - "psllw $1,%%mm7\n\t" - /*mm2==R_3 R_2 R_1 R_0*/ - /*mm5==R_3 R_2 R_1 R_0*/ - /*mm6==-L -L -L -L*/ - /*mm0==L L L L*/ - /*mm5=R_i>L?FF:00*/ - "pcmpgtw %%mm0,%%mm5\n\t" - /*mm6=-L>R_i?FF:00*/ - "pcmpgtw %%mm2,%%mm6\n\t" - /*mm7=R_i>L?2L:0*/ - "pand %%mm5,%%mm7\n\t" - /*mm2=R_i>L?R_i-2L:R_i*/ - "psubw %%mm7,%%mm2\n\t" - "movq %%mm0,%%mm7\n\t" - /*mm5=-L>R_i||R_i>L*/ - "por %%mm6,%%mm5\n\t" - "psllw $1,%%mm7\n\t" - /*mm7=-L>R_i?2L:0*/ - "pand %%mm6,%%mm7\n\t" - "pxor %%mm6,%%mm6\n\t" - /*mm2=-L>R_i?R_i+2L:R_i*/ - "paddw %%mm7,%%mm2\n\t" - "psubw %%mm0,%%mm6\n\t" - /*mm5=-L>R_i||R_i>L?-R_i':0*/ - "pand %%mm2,%%mm5\n\t" - "movq %%mm0,%%mm7\n\t" - /*mm2=-L>R_i||R_i>L?0:R_i*/ - "psubw %%mm5,%%mm2\n\t" - "psllw $1,%%mm7\n\t" - /*mm2=-L>R_i||R_i>L?-R_i':R_i*/ - "psubw %%mm5,%%mm2\n\t" - "movq %%mm3,%%mm5\n\t" - /*mm3==R_7 R_6 R_5 R_4*/ - /*mm5==R_7 R_6 R_5 R_4*/ - /*mm6==-L -L -L -L*/ - /*mm0==L L L L*/ - /*mm6=-L>R_i?FF:00*/ - "pcmpgtw %%mm3,%%mm6\n\t" - /*mm5=R_i>L?FF:00*/ - "pcmpgtw %%mm0,%%mm5\n\t" - /*mm7=R_i>L?2L:0*/ - "pand %%mm5,%%mm7\n\t" - /*mm2=R_i>L?R_i-2L:R_i*/ - "psubw %%mm7,%%mm3\n\t" - "psllw $1,%%mm0\n\t" - /*mm5=-L>R_i||R_i>L*/ - "por %%mm6,%%mm5\n\t" - /*mm0=-L>R_i?2L:0*/ - "pand %%mm6,%%mm0\n\t" - /*mm3=-L>R_i?R_i+2L:R_i*/ - "paddw %%mm0,%%mm3\n\t" - /*mm5=-L>R_i||R_i>L?-R_i':0*/ - "pand %%mm3,%%mm5\n\t" - /*mm2=-L>R_i||R_i>L?0:R_i*/ - "psubw %%mm5,%%mm3\n\t" - /*mm2=-L>R_i||R_i>L?-R_i':R_i*/ - "psubw %%mm5,%%mm3\n\t" - /*Unfortunately, there's no unsigned byte+signed byte with unsigned - saturation op code, so we have to promote things back 16 bits.*/ - "pxor %%mm0,%%mm0\n\t" - "movq %%mm4,%%mm5\n\t" - "punpcklbw %%mm0,%%mm4\n\t" - "punpckhbw %%mm0,%%mm5\n\t" - "movq %%mm1,%%mm6\n\t" - "punpcklbw %%mm0,%%mm1\n\t" - "punpckhbw %%mm0,%%mm6\n\t" - /*_pix[0...8+_ystride]+=R_i*/ - "paddw %%mm2,%%mm4\n\t" - "paddw %%mm3,%%mm5\n\t" - /*_pix[0...8+_ystride*2]-=R_i*/ - "psubw %%mm2,%%mm1\n\t" - "psubw %%mm3,%%mm6\n\t" - "packuswb %%mm5,%%mm4\n\t" - "packuswb %%mm6,%%mm1\n\t" - /*Write it back out.*/ - "movq %%mm4,(%[pix],%[ystride])\n\t" - "movq %%mm1,(%[pix],%[ystride],2)\n\t" - :[s]"=&r"(s) - :[pix]"r"(_pix),[ystride]"r"((ptrdiff_t)_ystride),[ll]"r"(_ll) - :"memory" - ); -} - -/*This code implements the bulk of loop_filter_h(). - Data are striped p0 p1 p2 p3 ... p0 p1 p2 p3 ..., so in order to load all - four p0's to one register we must transpose the values in four mmx regs. - When half is done we repeat this for the rest.*/ -static void loop_filter_h4(unsigned char *_pix,ptrdiff_t _ystride, - const ogg_int16_t *_ll){ - ptrdiff_t s; - /*d doesn't technically need to be 64-bit on x86-64, but making it so will - help avoid partial register stalls.*/ - ptrdiff_t d; - __asm__ __volatile__( - /*x x x x 3 2 1 0*/ - "movd (%[pix]),%%mm0\n\t" - /*s=_ystride*3*/ - "lea (%[ystride],%[ystride],2),%[s]\n\t" - /*x x x x 7 6 5 4*/ - "movd (%[pix],%[ystride]),%%mm1\n\t" - /*x x x x B A 9 8*/ - "movd (%[pix],%[ystride],2),%%mm2\n\t" - /*x x x x F E D C*/ - "movd (%[pix],%[s]),%%mm3\n\t" - /*mm0=7 3 6 2 5 1 4 0*/ - "punpcklbw %%mm1,%%mm0\n\t" - /*mm2=F B E A D 9 C 8*/ - "punpcklbw %%mm3,%%mm2\n\t" - /*mm1=7 3 6 2 5 1 4 0*/ - "movq %%mm0,%%mm1\n\t" - /*mm0=F B 7 3 E A 6 2*/ - "punpckhwd %%mm2,%%mm0\n\t" - /*mm1=D 9 5 1 C 8 4 0*/ - "punpcklwd %%mm2,%%mm1\n\t" - "pxor %%mm7,%%mm7\n\t" - /*mm5=D 9 5 1 C 8 4 0*/ - "movq %%mm1,%%mm5\n\t" - /*mm1=x C x 8 x 4 x 0==pix[0]*/ - "punpcklbw %%mm7,%%mm1\n\t" - /*mm5=x D x 9 x 5 x 1==pix[1]*/ - "punpckhbw %%mm7,%%mm5\n\t" - /*mm3=F B 7 3 E A 6 2*/ - "movq %%mm0,%%mm3\n\t" - /*mm0=x E x A x 6 x 2==pix[2]*/ - "punpcklbw %%mm7,%%mm0\n\t" - /*mm3=x F x B x 7 x 3==pix[3]*/ - "punpckhbw %%mm7,%%mm3\n\t" - /*mm1=mm1-mm3==pix[0]-pix[3]*/ - "psubw %%mm3,%%mm1\n\t" - /*Save a copy of pix[2] for later.*/ - "movq %%mm0,%%mm4\n\t" - /*mm2=3 3 3 3 - mm0=mm0-mm5==pix[2]-pix[1]*/ - "pcmpeqw %%mm2,%%mm2\n\t" - "psubw %%mm5,%%mm0\n\t" - "psrlw $14,%%mm2\n\t" - /*Scale by 3.*/ - "pmullw %%mm2,%%mm0\n\t" - /*mm2=4 4 4 4 - f=mm1==_pix[0]-_pix[3]+ 3*(_pix[2]-_pix[1])*/ - "psrlw $1,%%mm2\n\t" - "paddw %%mm1,%%mm0\n\t" - "psllw $2,%%mm2\n\t" - /*Add 4.*/ - "paddw %%mm2,%%mm0\n\t" - /*"Divide" by 8, producing the residuals R_i.*/ - "psraw $3,%%mm0\n\t" - /*Now compute lflim of mm0 cf. Section 7.10 of the sepc.*/ - /*mm6=L L L L*/ - "movq (%[ll]),%%mm6\n\t" - /*if(R_i<-2L||R_i>2L)R_i=0:*/ - "movq %%mm0,%%mm1\n\t" - "pxor %%mm2,%%mm2\n\t" - "movq %%mm6,%%mm3\n\t" - "psubw %%mm6,%%mm2\n\t" - "psllw $1,%%mm3\n\t" - "psllw $1,%%mm2\n\t" - /*mm0==R_3 R_2 R_1 R_0*/ - /*mm1==R_3 R_2 R_1 R_0*/ - /*mm2==-2L -2L -2L -2L*/ - /*mm3==2L 2L 2L 2L*/ - "pcmpgtw %%mm0,%%mm3\n\t" - "pcmpgtw %%mm2,%%mm1\n\t" - "pand %%mm3,%%mm0\n\t" - "pand %%mm1,%%mm0\n\t" - /*if(R_i<-L)R_i'=R_i+2L; - if(R_i>L)R_i'=R_i-2L; - if(R_i<-L||R_i>L)R_i=-R_i':*/ - "psraw $1,%%mm2\n\t" - "movq %%mm0,%%mm1\n\t" - "movq %%mm6,%%mm3\n\t" - /*mm0==R_3 R_2 R_1 R_0*/ - /*mm1==R_3 R_2 R_1 R_0*/ - /*mm2==-L -L -L -L*/ - /*mm6==L L L L*/ - /*mm2=-L>R_i?FF:00*/ - "pcmpgtw %%mm0,%%mm2\n\t" - /*mm1=R_i>L?FF:00*/ - "pcmpgtw %%mm6,%%mm1\n\t" - /*mm3=2L 2L 2L 2L*/ - "psllw $1,%%mm3\n\t" - /*mm6=2L 2L 2L 2L*/ - "psllw $1,%%mm6\n\t" - /*mm3=R_i>L?2L:0*/ - "pand %%mm1,%%mm3\n\t" - /*mm6=-L>R_i?2L:0*/ - "pand %%mm2,%%mm6\n\t" - /*mm0=R_i>L?R_i-2L:R_i*/ - "psubw %%mm3,%%mm0\n\t" - /*mm1=-L>R_i||R_i>L*/ - "por %%mm2,%%mm1\n\t" - /*mm0=-L>R_i?R_i+2L:R_i*/ - "paddw %%mm6,%%mm0\n\t" - /*mm1=-L>R_i||R_i>L?R_i':0*/ - "pand %%mm0,%%mm1\n\t" - /*mm0=-L>R_i||R_i>L?0:R_i*/ - "psubw %%mm1,%%mm0\n\t" - /*mm0=-L>R_i||R_i>L?-R_i':R_i*/ - "psubw %%mm1,%%mm0\n\t" - /*_pix[1]+=R_i;*/ - "paddw %%mm0,%%mm5\n\t" - /*_pix[2]-=R_i;*/ - "psubw %%mm0,%%mm4\n\t" - /*mm5=x x x x D 9 5 1*/ - "packuswb %%mm7,%%mm5\n\t" - /*mm4=x x x x E A 6 2*/ - "packuswb %%mm7,%%mm4\n\t" - /*mm5=E D A 9 6 5 2 1*/ - "punpcklbw %%mm4,%%mm5\n\t" - /*d=6 5 2 1*/ - "movd %%mm5,%[d]\n\t" - "movw %w[d],1(%[pix])\n\t" - /*Why is there such a big stall here?*/ - "psrlq $32,%%mm5\n\t" - "shr $16,%[d]\n\t" - "movw %w[d],1(%[pix],%[ystride])\n\t" - /*d=E D A 9*/ - "movd %%mm5,%[d]\n\t" - "movw %w[d],1(%[pix],%[ystride],2)\n\t" - "shr $16,%[d]\n\t" - "movw %w[d],1(%[pix],%[s])\n\t" - :[s]"=&r"(s),[d]"=&r"(d), - [pix]"+r"(_pix),[ystride]"+r"(_ystride),[ll]"+r"(_ll) - : - :"memory" - ); -} - -static void loop_filter_h(unsigned char *_pix,int _ystride, - const ogg_int16_t *_ll){ - _pix-=2; - loop_filter_h4(_pix,_ystride,_ll); - loop_filter_h4(_pix+(_ystride<<2),_ystride,_ll); -} - -/*We copy the whole function because the MMX routines will be inlined 4 times, - and we can do just a single emms call at the end this way. - We also do not use the _bv lookup table, instead computing the values that - would lie in it on the fly.*/ - -/*Apply the loop filter to a given set of fragment rows in the given plane. - The filter may be run on the bottom edge, affecting pixels in the next row of - fragments, so this row also needs to be available. - _bv: The bounding values array. - _refi: The index of the frame buffer to filter. - _pli: The color plane to filter. - _fragy0: The Y coordinate of the first fragment row to filter. - _fragy_end: The Y coordinate of the fragment row to stop filtering at.*/ -void oc_state_loop_filter_frag_rows_mmx(oc_theora_state *_state,int *_bv, - int _refi,int _pli,int _fragy0,int _fragy_end){ - ogg_int16_t __attribute__((aligned(8))) ll[4]; - th_img_plane *iplane; - oc_fragment_plane *fplane; - oc_fragment *frag_top; - oc_fragment *frag0; - oc_fragment *frag; - oc_fragment *frag_end; - oc_fragment *frag0_end; - oc_fragment *frag_bot; - ll[0]=ll[1]=ll[2]=ll[3]= - (ogg_int16_t)_state->loop_filter_limits[_state->qis[0]]; - iplane=_state->ref_frame_bufs[_refi]+_pli; - fplane=_state->fplanes+_pli; - /*The following loops are constructed somewhat non-intuitively on purpose. - The main idea is: if a block boundary has at least one coded fragment on - it, the filter is applied to it. - However, the order that the filters are applied in matters, and VP3 chose - the somewhat strange ordering used below.*/ - frag_top=_state->frags+fplane->froffset; - frag0=frag_top+_fragy0*fplane->nhfrags; - frag0_end=frag0+(_fragy_end-_fragy0)*fplane->nhfrags; - frag_bot=_state->frags+fplane->froffset+fplane->nfrags; - while(frag0nhfrags; - while(fragcoded){ - if(frag>frag0){ - loop_filter_h(frag->buffer[_refi],iplane->stride,ll); - } - if(frag0>frag_top){ - loop_filter_v(frag->buffer[_refi],iplane->stride,ll); - } - if(frag+1coded){ - loop_filter_h(frag->buffer[_refi]+8,iplane->stride,ll); - } - if(frag+fplane->nhfragsnhfrags)->coded){ - loop_filter_v((frag+fplane->nhfrags)->buffer[_refi], - iplane->stride,ll); - } - } - frag++; - } - frag0+=fplane->nhfrags; - } - /*This needs to be removed when decode specific functions are implemented:*/ - __asm__ __volatile__("emms\n\t"); -} - -#endif diff --git a/Engine/lib/libtheora/lib/dec/x86/x86int.h b/Engine/lib/libtheora/lib/dec/x86/x86int.h deleted file mode 100644 index 05f9c57c1..000000000 --- a/Engine/lib/libtheora/lib/dec/x86/x86int.h +++ /dev/null @@ -1,42 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: x86int.h 15400 2008-10-15 12:10:58Z tterribe $ - - ********************************************************************/ - -#if !defined(_x86_x86int_H) -# define _x86_x86int_H (1) -# include "../../internal.h" - -void oc_state_vtable_init_x86(oc_theora_state *_state); - -void oc_frag_recon_intra_mmx(unsigned char *_dst,int _dst_ystride, - const ogg_int16_t *_residue); -void oc_frag_recon_inter_mmx(unsigned char *_dst,int _dst_ystride, - const unsigned char *_src,int _src_ystride,const ogg_int16_t *_residue); -void oc_frag_recon_inter2_mmx(unsigned char *_dst,int _dst_ystride, - const unsigned char *_src1,int _src1_ystride,const unsigned char *_src2, - int _src2_ystride,const ogg_int16_t *_residue); -void oc_state_frag_copy_mmx(const oc_theora_state *_state,const int *_fragis, - int _nfragis,int _dst_frame,int _src_frame,int _pli); -void oc_state_frag_recon_mmx(oc_theora_state *_state,oc_fragment *_frag, - int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, - ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]); -void oc_restore_fpu_mmx(void); -void oc_idct8x8_mmx(ogg_int16_t _y[64]); -void oc_idct8x8_10_mmx(ogg_int16_t _y[64]); -void oc_fill_idct_constants_mmx(void); -void oc_state_loop_filter_frag_rows_mmx(oc_theora_state *_state,int *_bv, - int _refi,int _pli,int _fragy0,int _fragy_end); -#endif diff --git a/Engine/lib/libtheora/lib/dec/x86_vc/mmxfrag.c b/Engine/lib/libtheora/lib/dec/x86_vc/mmxfrag.c deleted file mode 100644 index e87e0640d..000000000 --- a/Engine/lib/libtheora/lib/dec/x86_vc/mmxfrag.c +++ /dev/null @@ -1,214 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: - - ********************************************************************/ -#include "../../internal.h" - -/* ------------------------------------------------------------------------ - MMX reconstruction fragment routines for Visual Studio. - Tested with VS2005. Should compile for VS2003 and VC6 as well. - - Initial implementation 2007 by Nils Pipenbrinck. - ---------------------------------------------------------------------*/ - -#if defined(USE_ASM) - -void oc_frag_recon_intra_mmx(unsigned char *_dst,int _dst_ystride, - const ogg_int16_t *_residue){ - /* --------------------------------------------------------------------- - This function does the inter reconstruction step with 8 iterations - unrolled. The iteration for each instruction is noted by the #id in the - comments (in case you want to reconstruct it) - --------------------------------------------------------------------- */ - _asm{ - mov edi, [_residue] /* load residue ptr */ - mov eax, 0x00800080 /* generate constant */ - mov ebx, [_dst_ystride] /* load dst-stride */ - mov edx, [_dst] /* load dest pointer */ - - /* unrolled loop begins here */ - - movd mm0, eax /* load constant */ - movq mm1, [edi+ 8*0] /* #1 load low residue */ - movq mm2, [edi+ 8*1] /* #1 load high residue */ - punpckldq mm0, mm0 /* build constant */ - movq mm3, [edi+ 8*2] /* #2 load low residue */ - movq mm4, [edi+ 8*3] /* #2 load high residue */ - movq mm5, [edi+ 8*4] /* #3 load low residue */ - movq mm6, [edi+ 8*5] /* #3 load high residue */ - paddsw mm1, mm0 /* #1 bias low residue */ - paddsw mm2, mm0 /* #1 bias high residue */ - packuswb mm1, mm2 /* #1 pack to byte */ - paddsw mm3, mm0 /* #2 bias low residue */ - paddsw mm4, mm0 /* #2 bias high residue */ - packuswb mm3, mm4 /* #2 pack to byte */ - paddsw mm5, mm0 /* #3 bias low residue */ - paddsw mm6, mm0 /* #3 bias high residue */ - packuswb mm5, mm6 /* #3 pack to byte */ - movq [edx], mm1 /* #1 write row */ - movq [edx + ebx], mm3 /* #2 write row */ - movq [edx + ebx*2], mm5 /* #3 write row */ - movq mm1, [edi+ 8*6] /* #4 load low residue */ - lea ecx, [ebx + ebx*2] /* make dst_ystride * 3 */ - movq mm2, [edi+ 8*7] /* #4 load high residue */ - movq mm3, [edi+ 8*8] /* #5 load low residue */ - lea esi, [ebx*4 + ebx] /* make dst_ystride * 5 */ - movq mm4, [edi+ 8*9] /* #5 load high residue */ - movq mm5, [edi+ 8*10] /* #6 load low residue */ - lea eax, [ecx*2 + ebx] /* make dst_ystride * 7 */ - movq mm6, [edi+ 8*11] /* #6 load high residue */ - paddsw mm1, mm0 /* #4 bias low residue */ - paddsw mm2, mm0 /* #4 bias high residue */ - packuswb mm1, mm2 /* #4 pack to byte */ - paddsw mm3, mm0 /* #5 bias low residue */ - paddsw mm4, mm0 /* #5 bias high residue */ - packuswb mm3, mm4 /* #5 pack to byte */ - paddsw mm5, mm0 /* #6 bias low residue */ - paddsw mm6, mm0 /* #6 bias high residue */ - packuswb mm5, mm6 /* #6 pack to byte */ - movq [edx + ecx], mm1 /* #4 write row */ - movq [edx + ebx*4], mm3 /* #5 write row */ - movq [edx + esi], mm5 /* #6 write row */ - movq mm1, [edi+ 8*12] /* #7 load low residue */ - movq mm2, [edi+ 8*13] /* #7 load high residue */ - movq mm3, [edi+ 8*14] /* #8 load low residue */ - movq mm4, [edi+ 8*15] /* #8 load high residue */ - paddsw mm1, mm0 /* #7 bias low residue */ - paddsw mm2, mm0 /* #7 bias high residue */ - packuswb mm1, mm2 /* #7 pack to byte */ - paddsw mm3, mm0 /* #8 bias low residue */ - paddsw mm4, mm0 /* #8 bias high residue */ - packuswb mm3, mm4 /* #8 pack to byte */ - movq [edx + ecx*2], mm1 /* #7 write row */ - movq [edx + eax], mm3 /* #8 write row */ - } -} - - - -void oc_frag_recon_inter_mmx (unsigned char *_dst, int _dst_ystride, - const unsigned char *_src, int _src_ystride, const ogg_int16_t *_residue){ - /* --------------------------------------------------------------------- - This function does the inter reconstruction step with two iterations - running in parallel to hide some load-latencies and break the dependency - chains. The iteration for each instruction is noted by the #id in the - comments (in case you want to reconstruct it) - --------------------------------------------------------------------- */ - _asm{ - pxor mm0, mm0 /* generate constant 0 */ - mov esi, [_src] - mov edi, [_residue] - mov eax, [_src_ystride] - mov edx, [_dst] - mov ebx, [_dst_ystride] - mov ecx, 4 - - align 16 - -nextchunk: - movq mm3, [esi] /* #1 load source */ - movq mm1, [edi+0] /* #1 load residium low */ - movq mm2, [edi+8] /* #1 load residium high */ - movq mm7, [esi+eax] /* #2 load source */ - movq mm4, mm3 /* #1 get copy of src */ - movq mm5, [edi+16] /* #2 load residium low */ - punpckhbw mm4, mm0 /* #1 expand high source */ - movq mm6, [edi+24] /* #2 load residium high */ - punpcklbw mm3, mm0 /* #1 expand low source */ - paddsw mm4, mm2 /* #1 add residium high */ - movq mm2, mm7 /* #2 get copy of src */ - paddsw mm3, mm1 /* #1 add residium low */ - punpckhbw mm2, mm0 /* #2 expand high source */ - packuswb mm3, mm4 /* #1 final row pixels */ - punpcklbw mm7, mm0 /* #2 expand low source */ - movq [edx], mm3 /* #1 write row */ - paddsw mm2, mm6 /* #2 add residium high */ - add edi, 32 /* residue += 4 */ - paddsw mm7, mm5 /* #2 add residium low */ - sub ecx, 1 /* update loop counter */ - packuswb mm7, mm2 /* #2 final row */ - lea esi, [esi+eax*2] /* src += stride * 2 */ - movq [edx + ebx], mm7 /* #2 write row */ - lea edx, [edx+ebx*2] /* dst += stride * 2 */ - jne nextchunk - } -} - - -void oc_frag_recon_inter2_mmx(unsigned char *_dst, int _dst_ystride, - const unsigned char *_src1, int _src1_ystride, const unsigned char *_src2, - int _src2_ystride,const ogg_int16_t *_residue){ - /* --------------------------------------------------------------------- - This function does the inter2 reconstruction step.The building of the - average is done with a bit-twiddeling trick to avoid excessive register - copy work during byte to word conversion. - - average = (a & b) + (((a ^ b) & 0xfe) >> 1); - - (shown for a single byte; it's done with 8 of them at a time) - - Slightly faster than the obvious method using add and shift, but not - earthshaking improvement either. - - If anyone comes up with a way that produces bit-identical outputs - using the pavgb instruction let me know and I'll do the 3dnow codepath. - --------------------------------------------------------------------- */ - _asm{ - mov eax, 0xfefefefe - mov esi, [_src1] - mov edi, [_src2] - movd mm1, eax - mov ebx, [_residue] - mov edx, [_dst] - mov eax, [_dst_ystride] - punpckldq mm1, mm1 /* replicate lsb32 */ - mov ecx, 8 /* init loop counter */ - pxor mm0, mm0 /* constant zero */ - sub edx, eax /* dst -= dst_stride */ - - align 16 - -nextrow: - movq mm2, [esi] /* load source1 */ - movq mm3, [edi] /* load source2 */ - movq mm5, [ebx + 0] /* load lower residue */ - movq mm6, [ebx + 8] /* load higer residue */ - add esi, _src1_ystride /* src1 += src1_stride */ - add edi, _src2_ystride /* src2 += src1_stride */ - movq mm4, mm2 /* get copy of source1 */ - pand mm2, mm3 /* s1 & s2 (avg part) */ - pxor mm3, mm4 /* s1 ^ s2 (avg part) */ - add ebx, 16 /* residue++ */ - pand mm3, mm1 /* mask out low bits */ - psrlq mm3, 1 /* shift xor avg-part */ - paddd mm3, mm2 /* build final average */ - add edx, eax /* dst += dst_stride */ - movq mm2, mm3 /* get copy of average */ - punpckhbw mm3, mm0 /* average high */ - punpcklbw mm2, mm0 /* average low */ - paddsw mm3, mm6 /* high + residue */ - paddsw mm2, mm5 /* low + residue */ - sub ecx, 1 /* update loop counter */ - packuswb mm2, mm3 /* pack and saturate */ - movq [edx], mm2 /* write row */ - jne nextrow - } -} - -void oc_restore_fpu_mmx(void){ - _asm { emms } -} - -#endif diff --git a/Engine/lib/libtheora/lib/dec/x86_vc/mmxidct.c b/Engine/lib/libtheora/lib/dec/x86_vc/mmxidct.c deleted file mode 100644 index 2c171594f..000000000 --- a/Engine/lib/libtheora/lib/dec/x86_vc/mmxidct.c +++ /dev/null @@ -1,1006 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: - - ********************************************************************/ - -/* ------------------------------------------------------------------- - MMX based IDCT for the theora codec. - - Originally written by Rudolf Marek, based on code from On2's VP3. - Converted to Visual Studio inline assembly by Nils Pipenbrinck. - - ---------------------------------------------------------------------*/ -#if defined(USE_ASM) - -#include -#include "../dct.h" -#include "../idct.h" -#include "x86int.h" - -/*A table of constants used by the MMX routines.*/ -static const __declspec(align(16)) ogg_uint16_t - OC_IDCT_CONSTS[(7+1)*4]={ - (ogg_uint16_t)OC_C1S7,(ogg_uint16_t)OC_C1S7, - (ogg_uint16_t)OC_C1S7,(ogg_uint16_t)OC_C1S7, - (ogg_uint16_t)OC_C2S6,(ogg_uint16_t)OC_C2S6, - (ogg_uint16_t)OC_C2S6,(ogg_uint16_t)OC_C2S6, - (ogg_uint16_t)OC_C3S5,(ogg_uint16_t)OC_C3S5, - (ogg_uint16_t)OC_C3S5,(ogg_uint16_t)OC_C3S5, - (ogg_uint16_t)OC_C4S4,(ogg_uint16_t)OC_C4S4, - (ogg_uint16_t)OC_C4S4,(ogg_uint16_t)OC_C4S4, - (ogg_uint16_t)OC_C5S3,(ogg_uint16_t)OC_C5S3, - (ogg_uint16_t)OC_C5S3,(ogg_uint16_t)OC_C5S3, - (ogg_uint16_t)OC_C6S2,(ogg_uint16_t)OC_C6S2, - (ogg_uint16_t)OC_C6S2,(ogg_uint16_t)OC_C6S2, - (ogg_uint16_t)OC_C7S1,(ogg_uint16_t)OC_C7S1, - (ogg_uint16_t)OC_C7S1,(ogg_uint16_t)OC_C7S1, - 8, 8, 8, 8 -}; - - -void oc_idct8x8_10_mmx(ogg_int16_t _y[64]){ - _asm { - mov edx, [_y] - mov eax, offset OC_IDCT_CONSTS - movq mm2, [edx + 30H] - movq mm6, [eax + 10H] - movq mm4, mm2 - movq mm7, [edx + 18H] - pmulhw mm4, mm6 - movq mm1, [eax + 20H] - pmulhw mm6, mm7 - movq mm5, mm1 - pmulhw mm1, mm2 - movq mm3, [edx + 10H] - pmulhw mm5, mm7 - movq mm0, [eax] - paddw mm4, mm2 - paddw mm6, mm7 - paddw mm2, mm1 - movq mm1, [edx + 38H] - paddw mm7, mm5 - movq mm5, mm0 - pmulhw mm0, mm3 - paddw mm4, mm7 - pmulhw mm5, mm1 - movq mm7, [eax + 30H] - psubw mm6, mm2 - paddw mm0, mm3 - pmulhw mm3, mm7 - movq mm2, [edx + 20H] - pmulhw mm7, mm1 - paddw mm5, mm1 - movq mm1, mm2 - pmulhw mm2, [eax + 08H] - psubw mm3, mm5 - movq mm5, [edx + 28H] - paddw mm0, mm7 - movq mm7, mm5 - psubw mm0, mm4 - pmulhw mm5, [eax + 08H] - paddw mm2, mm1 - pmulhw mm1, [eax + 28H] - paddw mm4, mm4 - paddw mm4, mm0 - psubw mm3, mm6 - paddw mm5, mm7 - paddw mm6, mm6 - pmulhw mm7, [eax + 28H] - paddw mm6, mm3 - movq [edx + 10H], mm4 - psubw mm1, mm5 - movq mm4, [eax + 18H] - movq mm5, mm3 - pmulhw mm3, mm4 - paddw mm7, mm2 - movq [edx + 20H], mm6 - movq mm2, mm0 - movq mm6, [edx] - pmulhw mm0, mm4 - paddw mm5, mm3 - movq mm3, [edx + 08H] - psubw mm5, mm1 - paddw mm2, mm0 - psubw mm6, mm3 - movq mm0, mm6 - pmulhw mm6, mm4 - paddw mm3, mm3 - paddw mm1, mm1 - paddw mm3, mm0 - paddw mm1, mm5 - pmulhw mm4, mm3 - paddw mm6, mm0 - psubw mm6, mm2 - paddw mm2, mm2 - movq mm0, [edx + 10H] - paddw mm2, mm6 - paddw mm4, mm3 - psubw mm2, mm1 - movq mm3, [edx + 20H] - psubw mm4, mm7 - paddw mm1, mm1 - paddw mm7, mm7 - paddw mm1, mm2 - paddw mm7, mm4 - psubw mm4, mm3 - paddw mm3, mm3 - psubw mm6, mm5 - paddw mm5, mm5 - paddw mm3, mm4 - paddw mm5, mm6 - psubw mm7, mm0 - paddw mm0, mm0 - movq [edx + 10H], mm1 - paddw mm0, mm7 - movq mm1, mm4 - punpcklwd mm4, mm5 - movq [edx], mm0 - punpckhwd mm1, mm5 - movq mm0, mm6 - punpcklwd mm6, mm7 - movq mm5, mm4 - punpckldq mm4, mm6 - punpckhdq mm5, mm6 - movq mm6, mm1 - movq [edx + 08H], mm4 - punpckhwd mm0, mm7 - movq [edx + 18H], mm5 - punpckhdq mm6, mm0 - movq mm4, [edx] - punpckldq mm1, mm0 - movq mm5, [edx + 10H] - movq mm0, mm4 - movq [edx + 38H], mm6 - punpcklwd mm0, mm5 - movq [edx + 28H], mm1 - punpckhwd mm4, mm5 - movq mm5, mm2 - punpcklwd mm2, mm3 - movq mm1, mm0 - punpckldq mm0, mm2 - punpckhdq mm1, mm2 - movq mm2, mm4 - movq [edx], mm0 - punpckhwd mm5, mm3 - movq [edx + 10H], mm1 - punpckhdq mm4, mm5 - punpckldq mm2, mm5 - movq [edx + 30H], mm4 - movq [edx + 20H], mm2 - movq mm2, [edx + 70H] - movq mm6, [eax + 10H] - movq mm4, mm2 - movq mm7, [edx + 58H] - pmulhw mm4, mm6 - movq mm1, [eax + 20H] - pmulhw mm6, mm7 - movq mm5, mm1 - pmulhw mm1, mm2 - movq mm3, [edx + 50H] - pmulhw mm5, mm7 - movq mm0, [eax] - paddw mm4, mm2 - paddw mm6, mm7 - paddw mm2, mm1 - movq mm1, [edx + 78H] - paddw mm7, mm5 - movq mm5, mm0 - pmulhw mm0, mm3 - paddw mm4, mm7 - pmulhw mm5, mm1 - movq mm7, [eax + 30H] - psubw mm6, mm2 - paddw mm0, mm3 - pmulhw mm3, mm7 - movq mm2, [edx + 60H] - pmulhw mm7, mm1 - paddw mm5, mm1 - movq mm1, mm2 - pmulhw mm2, [eax + 08H] - psubw mm3, mm5 - movq mm5, [edx + 68H] - paddw mm0, mm7 - movq mm7, mm5 - psubw mm0, mm4 - pmulhw mm5, [eax + 08H] - paddw mm2, mm1 - pmulhw mm1, [eax + 28H] - paddw mm4, mm4 - paddw mm4, mm0 - psubw mm3, mm6 - paddw mm5, mm7 - paddw mm6, mm6 - pmulhw mm7, [eax + 28H] - paddw mm6, mm3 - movq [edx + 50H], mm4 - psubw mm1, mm5 - movq mm4, [eax + 18H] - movq mm5, mm3 - pmulhw mm3, mm4 - paddw mm7, mm2 - movq [edx + 60H], mm6 - movq mm2, mm0 - movq mm6, [edx + 40H] - pmulhw mm0, mm4 - paddw mm5, mm3 - movq mm3, [edx + 48H] - psubw mm5, mm1 - paddw mm2, mm0 - psubw mm6, mm3 - movq mm0, mm6 - pmulhw mm6, mm4 - paddw mm3, mm3 - paddw mm1, mm1 - paddw mm3, mm0 - paddw mm1, mm5 - pmulhw mm4, mm3 - paddw mm6, mm0 - psubw mm6, mm2 - paddw mm2, mm2 - movq mm0, [edx + 50H] - paddw mm2, mm6 - paddw mm4, mm3 - psubw mm2, mm1 - movq mm3, [edx + 60H] - psubw mm4, mm7 - paddw mm1, mm1 - paddw mm7, mm7 - paddw mm1, mm2 - paddw mm7, mm4 - psubw mm4, mm3 - paddw mm3, mm3 - psubw mm6, mm5 - paddw mm5, mm5 - paddw mm3, mm4 - paddw mm5, mm6 - psubw mm7, mm0 - paddw mm0, mm0 - movq [edx + 50H], mm1 - paddw mm0, mm7 - movq mm1, mm4 - punpcklwd mm4, mm5 - movq [edx + 40H], mm0 - punpckhwd mm1, mm5 - movq mm0, mm6 - punpcklwd mm6, mm7 - movq mm5, mm4 - punpckldq mm4, mm6 - punpckhdq mm5, mm6 - movq mm6, mm1 - movq [edx + 48H], mm4 - punpckhwd mm0, mm7 - movq [edx + 58H], mm5 - punpckhdq mm6, mm0 - movq mm4, [edx + 40H] - punpckldq mm1, mm0 - movq mm5, [edx + 50H] - movq mm0, mm4 - movq [edx + 78H], mm6 - punpcklwd mm0, mm5 - movq [edx + 68H], mm1 - punpckhwd mm4, mm5 - movq mm5, mm2 - punpcklwd mm2, mm3 - movq mm1, mm0 - punpckldq mm0, mm2 - punpckhdq mm1, mm2 - movq mm2, mm4 - movq [edx + 40H], mm0 - punpckhwd mm5, mm3 - movq [edx + 50H], mm1 - punpckhdq mm4, mm5 - punpckldq mm2, mm5 - movq [edx + 70H], mm4 - movq [edx + 60H], mm2 - movq mm2, [edx + 30H] - movq mm6, [eax + 10H] - movq mm4, mm2 - movq mm7, [edx + 50H] - pmulhw mm4, mm6 - movq mm1, [eax + 20H] - pmulhw mm6, mm7 - movq mm5, mm1 - pmulhw mm1, mm2 - movq mm3, [edx + 10H] - pmulhw mm5, mm7 - movq mm0, [eax] - paddw mm4, mm2 - paddw mm6, mm7 - paddw mm2, mm1 - movq mm1, [edx + 70H] - paddw mm7, mm5 - movq mm5, mm0 - pmulhw mm0, mm3 - paddw mm4, mm7 - pmulhw mm5, mm1 - movq mm7, [eax + 30H] - psubw mm6, mm2 - paddw mm0, mm3 - pmulhw mm3, mm7 - movq mm2, [edx + 20H] - pmulhw mm7, mm1 - paddw mm5, mm1 - movq mm1, mm2 - pmulhw mm2, [eax + 08H] - psubw mm3, mm5 - movq mm5, [edx + 60H] - paddw mm0, mm7 - movq mm7, mm5 - psubw mm0, mm4 - pmulhw mm5, [eax + 08H] - paddw mm2, mm1 - pmulhw mm1, [eax + 28H] - paddw mm4, mm4 - paddw mm4, mm0 - psubw mm3, mm6 - paddw mm5, mm7 - paddw mm6, mm6 - pmulhw mm7, [eax + 28H] - paddw mm6, mm3 - movq [edx + 10H], mm4 - psubw mm1, mm5 - movq mm4, [eax + 18H] - movq mm5, mm3 - pmulhw mm3, mm4 - paddw mm7, mm2 - movq [edx + 20H], mm6 - movq mm2, mm0 - movq mm6, [edx] - pmulhw mm0, mm4 - paddw mm5, mm3 - movq mm3, [edx + 40H] - psubw mm5, mm1 - paddw mm2, mm0 - psubw mm6, mm3 - movq mm0, mm6 - pmulhw mm6, mm4 - paddw mm3, mm3 - paddw mm1, mm1 - paddw mm3, mm0 - paddw mm1, mm5 - pmulhw mm4, mm3 - paddw mm6, mm0 - psubw mm6, mm2 - paddw mm2, mm2 - movq mm0, [edx + 10H] - paddw mm2, mm6 - paddw mm4, mm3 - psubw mm2, mm1 - paddw mm2, [eax + 38H] - paddw mm1, mm1 - paddw mm1, mm2 - psraw mm2, 4 - psubw mm4, mm7 - psraw mm1, 4 - movq mm3, [edx + 20H] - paddw mm7, mm7 - movq [edx + 20H], mm2 - paddw mm7, mm4 - movq [edx + 10H], mm1 - psubw mm4, mm3 - paddw mm4, [eax + 38H] - paddw mm3, mm3 - paddw mm3, mm4 - psraw mm4, 4 - psubw mm6, mm5 - psraw mm3, 4 - paddw mm6, [eax + 38H] - paddw mm5, mm5 - paddw mm5, mm6 - psraw mm6, 4 - movq [edx + 40H], mm4 - psraw mm5, 4 - movq [edx + 30H], mm3 - psubw mm7, mm0 - paddw mm7, [eax + 38H] - paddw mm0, mm0 - paddw mm0, mm7 - psraw mm7, 4 - movq [edx + 60H], mm6 - psraw mm0, 4 - movq [edx + 50H], mm5 - movq [edx + 70H], mm7 - movq [edx], mm0 - movq mm2, [edx + 38H] - movq mm6, [eax + 10H] - movq mm4, mm2 - movq mm7, [edx + 58H] - pmulhw mm4, mm6 - movq mm1, [eax + 20H] - pmulhw mm6, mm7 - movq mm5, mm1 - pmulhw mm1, mm2 - movq mm3, [edx + 18H] - pmulhw mm5, mm7 - movq mm0, [eax] - paddw mm4, mm2 - paddw mm6, mm7 - paddw mm2, mm1 - movq mm1, [edx + 78H] - paddw mm7, mm5 - movq mm5, mm0 - pmulhw mm0, mm3 - paddw mm4, mm7 - pmulhw mm5, mm1 - movq mm7, [eax + 30H] - psubw mm6, mm2 - paddw mm0, mm3 - pmulhw mm3, mm7 - movq mm2, [edx + 28H] - pmulhw mm7, mm1 - paddw mm5, mm1 - movq mm1, mm2 - pmulhw mm2, [eax + 08H] - psubw mm3, mm5 - movq mm5, [edx + 68H] - paddw mm0, mm7 - movq mm7, mm5 - psubw mm0, mm4 - pmulhw mm5, [eax + 08H] - paddw mm2, mm1 - pmulhw mm1, [eax + 28H] - paddw mm4, mm4 - paddw mm4, mm0 - psubw mm3, mm6 - paddw mm5, mm7 - paddw mm6, mm6 - pmulhw mm7, [eax + 28H] - paddw mm6, mm3 - movq [edx + 18H], mm4 - psubw mm1, mm5 - movq mm4, [eax + 18H] - movq mm5, mm3 - pmulhw mm3, mm4 - paddw mm7, mm2 - movq [edx + 28H], mm6 - movq mm2, mm0 - movq mm6, [edx + 08H] - pmulhw mm0, mm4 - paddw mm5, mm3 - movq mm3, [edx + 48H] - psubw mm5, mm1 - paddw mm2, mm0 - psubw mm6, mm3 - movq mm0, mm6 - pmulhw mm6, mm4 - paddw mm3, mm3 - paddw mm1, mm1 - paddw mm3, mm0 - paddw mm1, mm5 - pmulhw mm4, mm3 - paddw mm6, mm0 - psubw mm6, mm2 - paddw mm2, mm2 - movq mm0, [edx + 18H] - paddw mm2, mm6 - paddw mm4, mm3 - psubw mm2, mm1 - paddw mm2, [eax + 38H] - paddw mm1, mm1 - paddw mm1, mm2 - psraw mm2, 4 - psubw mm4, mm7 - psraw mm1, 4 - movq mm3, [edx + 28H] - paddw mm7, mm7 - movq [edx + 28H], mm2 - paddw mm7, mm4 - movq [edx + 18H], mm1 - psubw mm4, mm3 - paddw mm4, [eax + 38H] - paddw mm3, mm3 - paddw mm3, mm4 - psraw mm4, 4 - psubw mm6, mm5 - psraw mm3, 4 - paddw mm6, [eax + 38H] - paddw mm5, mm5 - paddw mm5, mm6 - psraw mm6, 4 - movq [edx + 48H], mm4 - psraw mm5, 4 - movq [edx + 38H], mm3 - psubw mm7, mm0 - paddw mm7, [eax + 38H] - paddw mm0, mm0 - paddw mm0, mm7 - psraw mm7, 4 - movq [edx + 68H], mm6 - psraw mm0, 4 - movq [edx + 58H], mm5 - movq [edx + 78H], mm7 - movq [edx + 08H], mm0 - /* emms */ - } -} - - -void oc_idct8x8_mmx(ogg_int16_t _y[64]){ - _asm { - mov edx, [_y] - mov eax, offset OC_IDCT_CONSTS - movq mm2, [edx + 30H] - movq mm6, [eax + 10H] - movq mm4, mm2 - movq mm7, [edx + 18H] - pmulhw mm4, mm6 - movq mm1, [eax + 20H] - pmulhw mm6, mm7 - movq mm5, mm1 - pmulhw mm1, mm2 - movq mm3, [edx + 10H] - pmulhw mm5, mm7 - movq mm0, [eax] - paddw mm4, mm2 - paddw mm6, mm7 - paddw mm2, mm1 - movq mm1, [edx + 38H] - paddw mm7, mm5 - movq mm5, mm0 - pmulhw mm0, mm3 - paddw mm4, mm7 - pmulhw mm5, mm1 - movq mm7, [eax + 30H] - psubw mm6, mm2 - paddw mm0, mm3 - pmulhw mm3, mm7 - movq mm2, [edx + 20H] - pmulhw mm7, mm1 - paddw mm5, mm1 - movq mm1, mm2 - pmulhw mm2, [eax + 08H] - psubw mm3, mm5 - movq mm5, [edx + 28H] - paddw mm0, mm7 - movq mm7, mm5 - psubw mm0, mm4 - pmulhw mm5, [eax + 08H] - paddw mm2, mm1 - pmulhw mm1, [eax + 28H] - paddw mm4, mm4 - paddw mm4, mm0 - psubw mm3, mm6 - paddw mm5, mm7 - paddw mm6, mm6 - pmulhw mm7, [eax + 28H] - paddw mm6, mm3 - movq [edx + 10H], mm4 - psubw mm1, mm5 - movq mm4, [eax + 18H] - movq mm5, mm3 - pmulhw mm3, mm4 - paddw mm7, mm2 - movq [edx + 20H], mm6 - movq mm2, mm0 - movq mm6, [edx] - pmulhw mm0, mm4 - paddw mm5, mm3 - movq mm3, [edx + 08H] - psubw mm5, mm1 - paddw mm2, mm0 - psubw mm6, mm3 - movq mm0, mm6 - pmulhw mm6, mm4 - paddw mm3, mm3 - paddw mm1, mm1 - paddw mm3, mm0 - paddw mm1, mm5 - pmulhw mm4, mm3 - paddw mm6, mm0 - psubw mm6, mm2 - paddw mm2, mm2 - movq mm0, [edx + 10H] - paddw mm2, mm6 - paddw mm4, mm3 - psubw mm2, mm1 - movq mm3, [edx + 20H] - psubw mm4, mm7 - paddw mm1, mm1 - paddw mm7, mm7 - paddw mm1, mm2 - paddw mm7, mm4 - psubw mm4, mm3 - paddw mm3, mm3 - psubw mm6, mm5 - paddw mm5, mm5 - paddw mm3, mm4 - paddw mm5, mm6 - psubw mm7, mm0 - paddw mm0, mm0 - movq [edx + 10H], mm1 - paddw mm0, mm7 - movq mm1, mm4 - punpcklwd mm4, mm5 - movq [edx], mm0 - punpckhwd mm1, mm5 - movq mm0, mm6 - punpcklwd mm6, mm7 - movq mm5, mm4 - punpckldq mm4, mm6 - punpckhdq mm5, mm6 - movq mm6, mm1 - movq [edx + 08H], mm4 - punpckhwd mm0, mm7 - movq [edx + 18H], mm5 - punpckhdq mm6, mm0 - movq mm4, [edx] - punpckldq mm1, mm0 - movq mm5, [edx + 10H] - movq mm0, mm4 - movq [edx + 38H], mm6 - punpcklwd mm0, mm5 - movq [edx + 28H], mm1 - punpckhwd mm4, mm5 - movq mm5, mm2 - punpcklwd mm2, mm3 - movq mm1, mm0 - punpckldq mm0, mm2 - punpckhdq mm1, mm2 - movq mm2, mm4 - movq [edx], mm0 - punpckhwd mm5, mm3 - movq [edx + 10H], mm1 - punpckhdq mm4, mm5 - punpckldq mm2, mm5 - movq [edx + 30H], mm4 - movq [edx + 20H], mm2 - movq mm2, [edx + 70H] - movq mm6, [eax + 10H] - movq mm4, mm2 - movq mm7, [edx + 58H] - pmulhw mm4, mm6 - movq mm1, [eax + 20H] - pmulhw mm6, mm7 - movq mm5, mm1 - pmulhw mm1, mm2 - movq mm3, [edx + 50H] - pmulhw mm5, mm7 - movq mm0, [eax] - paddw mm4, mm2 - paddw mm6, mm7 - paddw mm2, mm1 - movq mm1, [edx + 78H] - paddw mm7, mm5 - movq mm5, mm0 - pmulhw mm0, mm3 - paddw mm4, mm7 - pmulhw mm5, mm1 - movq mm7, [eax + 30H] - psubw mm6, mm2 - paddw mm0, mm3 - pmulhw mm3, mm7 - movq mm2, [edx + 60H] - pmulhw mm7, mm1 - paddw mm5, mm1 - movq mm1, mm2 - pmulhw mm2, [eax + 08H] - psubw mm3, mm5 - movq mm5, [edx + 68H] - paddw mm0, mm7 - movq mm7, mm5 - psubw mm0, mm4 - pmulhw mm5, [eax + 08H] - paddw mm2, mm1 - pmulhw mm1, [eax + 28H] - paddw mm4, mm4 - paddw mm4, mm0 - psubw mm3, mm6 - paddw mm5, mm7 - paddw mm6, mm6 - pmulhw mm7, [eax + 28H] - paddw mm6, mm3 - movq [edx + 50H], mm4 - psubw mm1, mm5 - movq mm4, [eax + 18H] - movq mm5, mm3 - pmulhw mm3, mm4 - paddw mm7, mm2 - movq [edx + 60H], mm6 - movq mm2, mm0 - movq mm6, [edx + 40H] - pmulhw mm0, mm4 - paddw mm5, mm3 - movq mm3, [edx + 48H] - psubw mm5, mm1 - paddw mm2, mm0 - psubw mm6, mm3 - movq mm0, mm6 - pmulhw mm6, mm4 - paddw mm3, mm3 - paddw mm1, mm1 - paddw mm3, mm0 - paddw mm1, mm5 - pmulhw mm4, mm3 - paddw mm6, mm0 - psubw mm6, mm2 - paddw mm2, mm2 - movq mm0, [edx + 50H] - paddw mm2, mm6 - paddw mm4, mm3 - psubw mm2, mm1 - movq mm3, [edx + 60H] - psubw mm4, mm7 - paddw mm1, mm1 - paddw mm7, mm7 - paddw mm1, mm2 - paddw mm7, mm4 - psubw mm4, mm3 - paddw mm3, mm3 - psubw mm6, mm5 - paddw mm5, mm5 - paddw mm3, mm4 - paddw mm5, mm6 - psubw mm7, mm0 - paddw mm0, mm0 - movq [edx + 50H], mm1 - paddw mm0, mm7 - movq mm1, mm4 - punpcklwd mm4, mm5 - movq [edx + 40H], mm0 - punpckhwd mm1, mm5 - movq mm0, mm6 - punpcklwd mm6, mm7 - movq mm5, mm4 - punpckldq mm4, mm6 - punpckhdq mm5, mm6 - movq mm6, mm1 - movq [edx + 48H], mm4 - punpckhwd mm0, mm7 - movq [edx + 58H], mm5 - punpckhdq mm6, mm0 - movq mm4, [edx + 40H] - punpckldq mm1, mm0 - movq mm5, [edx + 50H] - movq mm0, mm4 - movq [edx + 78H], mm6 - punpcklwd mm0, mm5 - movq [edx + 68H], mm1 - punpckhwd mm4, mm5 - movq mm5, mm2 - punpcklwd mm2, mm3 - movq mm1, mm0 - punpckldq mm0, mm2 - punpckhdq mm1, mm2 - movq mm2, mm4 - movq [edx + 40H], mm0 - punpckhwd mm5, mm3 - movq [edx + 50H], mm1 - punpckhdq mm4, mm5 - punpckldq mm2, mm5 - movq [edx + 70H], mm4 - movq [edx + 60H], mm2 - movq mm2, [edx + 30H] - movq mm6, [eax + 10H] - movq mm4, mm2 - movq mm7, [edx + 50H] - pmulhw mm4, mm6 - movq mm1, [eax + 20H] - pmulhw mm6, mm7 - movq mm5, mm1 - pmulhw mm1, mm2 - movq mm3, [edx + 10H] - pmulhw mm5, mm7 - movq mm0, [eax] - paddw mm4, mm2 - paddw mm6, mm7 - paddw mm2, mm1 - movq mm1, [edx + 70H] - paddw mm7, mm5 - movq mm5, mm0 - pmulhw mm0, mm3 - paddw mm4, mm7 - pmulhw mm5, mm1 - movq mm7, [eax + 30H] - psubw mm6, mm2 - paddw mm0, mm3 - pmulhw mm3, mm7 - movq mm2, [edx + 20H] - pmulhw mm7, mm1 - paddw mm5, mm1 - movq mm1, mm2 - pmulhw mm2, [eax + 08H] - psubw mm3, mm5 - movq mm5, [edx + 60H] - paddw mm0, mm7 - movq mm7, mm5 - psubw mm0, mm4 - pmulhw mm5, [eax + 08H] - paddw mm2, mm1 - pmulhw mm1, [eax + 28H] - paddw mm4, mm4 - paddw mm4, mm0 - psubw mm3, mm6 - paddw mm5, mm7 - paddw mm6, mm6 - pmulhw mm7, [eax + 28H] - paddw mm6, mm3 - movq [edx + 10H], mm4 - psubw mm1, mm5 - movq mm4, [eax + 18H] - movq mm5, mm3 - pmulhw mm3, mm4 - paddw mm7, mm2 - movq [edx + 20H], mm6 - movq mm2, mm0 - movq mm6, [edx] - pmulhw mm0, mm4 - paddw mm5, mm3 - movq mm3, [edx + 40H] - psubw mm5, mm1 - paddw mm2, mm0 - psubw mm6, mm3 - movq mm0, mm6 - pmulhw mm6, mm4 - paddw mm3, mm3 - paddw mm1, mm1 - paddw mm3, mm0 - paddw mm1, mm5 - pmulhw mm4, mm3 - paddw mm6, mm0 - psubw mm6, mm2 - paddw mm2, mm2 - movq mm0, [edx + 10H] - paddw mm2, mm6 - paddw mm4, mm3 - psubw mm2, mm1 - paddw mm2, [eax + 38H] - paddw mm1, mm1 - paddw mm1, mm2 - psraw mm2, 4 - psubw mm4, mm7 - psraw mm1, 4 - movq mm3, [edx + 20H] - paddw mm7, mm7 - movq [edx + 20H], mm2 - paddw mm7, mm4 - movq [edx + 10H], mm1 - psubw mm4, mm3 - paddw mm4, [eax + 38H] - paddw mm3, mm3 - paddw mm3, mm4 - psraw mm4, 4 - psubw mm6, mm5 - psraw mm3, 4 - paddw mm6, [eax + 38H] - paddw mm5, mm5 - paddw mm5, mm6 - psraw mm6, 4 - movq [edx + 40H], mm4 - psraw mm5, 4 - movq [edx + 30H], mm3 - psubw mm7, mm0 - paddw mm7, [eax + 38H] - paddw mm0, mm0 - paddw mm0, mm7 - psraw mm7, 4 - movq [edx + 60H], mm6 - psraw mm0, 4 - movq [edx + 50H], mm5 - movq [edx + 70H], mm7 - movq [edx], mm0 - movq mm2, [edx + 38H] - movq mm6, [eax + 10H] - movq mm4, mm2 - movq mm7, [edx + 58H] - pmulhw mm4, mm6 - movq mm1, [eax + 20H] - pmulhw mm6, mm7 - movq mm5, mm1 - pmulhw mm1, mm2 - movq mm3, [edx + 18H] - pmulhw mm5, mm7 - movq mm0, [eax] - paddw mm4, mm2 - paddw mm6, mm7 - paddw mm2, mm1 - movq mm1, [edx + 78H] - paddw mm7, mm5 - movq mm5, mm0 - pmulhw mm0, mm3 - paddw mm4, mm7 - pmulhw mm5, mm1 - movq mm7, [eax + 30H] - psubw mm6, mm2 - paddw mm0, mm3 - pmulhw mm3, mm7 - movq mm2, [edx + 28H] - pmulhw mm7, mm1 - paddw mm5, mm1 - movq mm1, mm2 - pmulhw mm2, [eax + 08H] - psubw mm3, mm5 - movq mm5, [edx + 68H] - paddw mm0, mm7 - movq mm7, mm5 - psubw mm0, mm4 - pmulhw mm5, [eax + 08H] - paddw mm2, mm1 - pmulhw mm1, [eax + 28H] - paddw mm4, mm4 - paddw mm4, mm0 - psubw mm3, mm6 - paddw mm5, mm7 - paddw mm6, mm6 - pmulhw mm7, [eax + 28H] - paddw mm6, mm3 - movq [edx + 18H], mm4 - psubw mm1, mm5 - movq mm4, [eax + 18H] - movq mm5, mm3 - pmulhw mm3, mm4 - paddw mm7, mm2 - movq [edx + 28H], mm6 - movq mm2, mm0 - movq mm6, [edx + 08H] - pmulhw mm0, mm4 - paddw mm5, mm3 - movq mm3, [edx + 48H] - psubw mm5, mm1 - paddw mm2, mm0 - psubw mm6, mm3 - movq mm0, mm6 - pmulhw mm6, mm4 - paddw mm3, mm3 - paddw mm1, mm1 - paddw mm3, mm0 - paddw mm1, mm5 - pmulhw mm4, mm3 - paddw mm6, mm0 - psubw mm6, mm2 - paddw mm2, mm2 - movq mm0, [edx + 18H] - paddw mm2, mm6 - paddw mm4, mm3 - psubw mm2, mm1 - paddw mm2, [eax + 38H] - paddw mm1, mm1 - paddw mm1, mm2 - psraw mm2, 4 - psubw mm4, mm7 - psraw mm1, 4 - movq mm3, [edx + 28H] - paddw mm7, mm7 - movq [edx + 28H], mm2 - paddw mm7, mm4 - movq [edx + 18H], mm1 - psubw mm4, mm3 - paddw mm4, [eax + 38H] - paddw mm3, mm3 - paddw mm3, mm4 - psraw mm4, 4 - psubw mm6, mm5 - psraw mm3, 4 - paddw mm6, [eax + 38H] - paddw mm5, mm5 - paddw mm5, mm6 - psraw mm6, 4 - movq [edx + 48H], mm4 - psraw mm5, 4 - movq [edx + 38H], mm3 - psubw mm7, mm0 - paddw mm7, [eax + 38H] - paddw mm0, mm0 - paddw mm0, mm7 - psraw mm7, 4 - movq [edx + 68H], mm6 - psraw mm0, 4 - movq [edx + 58H], mm5 - movq [edx + 78H], mm7 - movq [edx + 08H], mm0 - /* emms */ - } -} - -#endif diff --git a/Engine/lib/libtheora/lib/dec/x86_vc/mmxloopfilter.c b/Engine/lib/libtheora/lib/dec/x86_vc/mmxloopfilter.c deleted file mode 100644 index 62d06dc89..000000000 --- a/Engine/lib/libtheora/lib/dec/x86_vc/mmxloopfilter.c +++ /dev/null @@ -1,377 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: - - ********************************************************************/ - -/* ------------------------------------------------------------------- - MMX based loop filter for the theora codec. - - Originally written by Rudolf Marek, based on code from On2's VP3. - Converted to Visual Studio inline assembly by Nils Pipenbrinck. - - Note: I can't test these since my example files never get into the - loop filters, but the code has been converted semi-automatic from - the GCC sources, so it ought to work. - ---------------------------------------------------------------------*/ -#include "../../internal.h" -#include "x86int.h" -#include - -#if defined(USE_ASM) - - - -static void loop_filter_v(unsigned char *_pix,int _ystride, - const ogg_int16_t *_ll){ - _asm { - mov eax, [_pix] - mov edx, [_ystride] - mov ebx, [_ll] - - /* _pix -= ystride */ - sub eax, edx - /* mm0=0 */ - pxor mm0, mm0 - /* _pix -= ystride */ - sub eax, edx - /* esi=_ystride*3 */ - lea esi, [edx + edx*2] - - /* mm7=_pix[0...8]*/ - movq mm7, [eax] - /* mm4=_pix[0...8+_ystride*3]*/ - movq mm4, [eax + esi] - /* mm6=_pix[0...8]*/ - movq mm6, mm7 - /* Expand unsigned _pix[0...3] to 16 bits.*/ - punpcklbw mm6, mm0 - movq mm5, mm4 - /* Expand unsigned _pix[4...7] to 16 bits.*/ - punpckhbw mm7, mm0 - punpcklbw mm4, mm0 - /* Expand other arrays too.*/ - punpckhbw mm5, mm0 - /*mm7:mm6=_p[0...7]-_p[0...7+_ystride*3]:*/ - psubw mm6, mm4 - psubw mm7, mm5 - /*mm5=mm4=_pix[0...7+_ystride]*/ - movq mm4, [eax + edx] - /*mm1=mm3=mm2=_pix[0..7]+_ystride*2]*/ - movq mm2, [eax + edx*2] - movq mm5, mm4 - movq mm3, mm2 - movq mm1, mm2 - /*Expand these arrays.*/ - punpckhbw mm5, mm0 - punpcklbw mm4, mm0 - punpckhbw mm3, mm0 - punpcklbw mm2, mm0 - pcmpeqw mm0, mm0 - /*mm0=3 3 3 3 - mm3:mm2=_pix[0...8+_ystride*2]-_pix[0...8+_ystride]*/ - psubw mm3, mm5 - psrlw mm0, 14 - psubw mm2, mm4 - /*Scale by 3.*/ - pmullw mm3, mm0 - pmullw mm2, mm0 - /*mm0=4 4 4 4 - f=mm3:mm2==_pix[0...8]-_pix[0...8+_ystride*3]+ - 3*(_pix[0...8+_ystride*2]-_pix[0...8+_ystride])*/ - psrlw mm0, 1 - paddw mm3, mm7 - psllw mm0, 2 - paddw mm2, mm6 - /*Add 4.*/ - paddw mm3, mm0 - paddw mm2, mm0 - /*"Divide" by 8.*/ - psraw mm3, 3 - psraw mm2, 3 - /*Now compute lflim of mm3:mm2 cf. Section 7.10 of the sepc.*/ - /*Free up mm5.*/ - packuswb mm4, mm5 - /*mm0=L L L L*/ - movq mm0, [ebx] - /*if(R_i<-2L||R_i>2L)R_i=0:*/ - movq mm5, mm2 - pxor mm6, mm6 - movq mm7, mm0 - psubw mm6, mm0 - psllw mm7, 1 - psllw mm6, 1 - /*mm2==R_3 R_2 R_1 R_0*/ - /*mm5==R_3 R_2 R_1 R_0*/ - /*mm6==-2L -2L -2L -2L*/ - /*mm7==2L 2L 2L 2L*/ - pcmpgtw mm7, mm2 - pcmpgtw mm5, mm6 - pand mm2, mm7 - movq mm7, mm0 - pand mm2, mm5 - psllw mm7, 1 - movq mm5, mm3 - /*mm3==R_7 R_6 R_5 R_4*/ - /*mm5==R_7 R_6 R_5 R_4*/ - /*mm6==-2L -2L -2L -2L*/ - /*mm7==2L 2L 2L 2L*/ - pcmpgtw mm7, mm3 - pcmpgtw mm5, mm6 - pand mm3, mm7 - movq mm7, mm0 - pand mm3, mm5 - /*if(R_i<-L)R_i'=R_i+2L; - if(R_i>L)R_i'=R_i-2L; - if(R_i<-L||R_i>L)R_i=-R_i':*/ - psraw mm6, 1 - movq mm5, mm2 - psllw mm7, 1 - /*mm2==R_3 R_2 R_1 R_0*/ - /*mm5==R_3 R_2 R_1 R_0*/ - /*mm6==-L -L -L -L*/ - /*mm0==L L L L*/ - /*mm5=R_i>L?FF:00*/ - pcmpgtw mm5, mm0 - /*mm6=-L>R_i?FF:00*/ - pcmpgtw mm6, mm2 - /*mm7=R_i>L?2L:0*/ - pand mm7, mm5 - /*mm2=R_i>L?R_i-2L:R_i*/ - psubw mm2, mm7 - movq mm7, mm0 - /*mm5=-L>R_i||R_i>L*/ - por mm5, mm6 - psllw mm7, 1 - /*mm7=-L>R_i?2L:0*/ - pand mm7, mm6 - pxor mm6, mm6 - /*mm2=-L>R_i?R_i+2L:R_i*/ - paddw mm2, mm7 - psubw mm6, mm0 - /*mm5=-L>R_i||R_i>L?-R_i':0*/ - pand mm5, mm2 - movq mm7, mm0 - /*mm2=-L>R_i||R_i>L?0:R_i*/ - psubw mm2, mm5 - psllw mm7, 1 - /*mm2=-L>R_i||R_i>L?-R_i':R_i*/ - psubw mm2, mm5 - movq mm5, mm3 - /*mm3==R_7 R_6 R_5 R_4*/ - /*mm5==R_7 R_6 R_5 R_4*/ - /*mm6==-L -L -L -L*/ - /*mm0==L L L L*/ - /*mm6=-L>R_i?FF:00*/ - pcmpgtw mm6, mm3 - /*mm5=R_i>L?FF:00*/ - pcmpgtw mm5, mm0 - /*mm7=R_i>L?2L:0*/ - pand mm7, mm5 - /*mm2=R_i>L?R_i-2L:R_i*/ - psubw mm3, mm7 - psllw mm0, 1 - /*mm5=-L>R_i||R_i>L*/ - por mm5, mm6 - /*mm0=-L>R_i?2L:0*/ - pand mm0, mm6 - /*mm3=-L>R_i?R_i+2L:R_i*/ - paddw mm3, mm0 - /*mm5=-L>R_i||R_i>L?-R_i':0*/ - pand mm5, mm3 - /*mm2=-L>R_i||R_i>L?0:R_i*/ - psubw mm3, mm5 - /*mm3=-L>R_i||R_i>L?-R_i':R_i*/ - psubw mm3, mm5 - /*Unfortunately, there's no unsigned byte+signed byte with unsigned - saturation op code, so we have to promote things back 16 bits.*/ - pxor mm0, mm0 - movq mm5, mm4 - punpcklbw mm4, mm0 - punpckhbw mm5, mm0 - movq mm6, mm1 - punpcklbw mm1, mm0 - punpckhbw mm6, mm0 - /*_pix[0...8+_ystride]+=R_i*/ - paddw mm4, mm2 - paddw mm5, mm3 - /*_pix[0...8+_ystride*2]-=R_i*/ - psubw mm1, mm2 - psubw mm6, mm3 - packuswb mm4, mm5 - packuswb mm1, mm6 - /*Write it back out.*/ - movq [eax + edx], mm4 - movq [eax + edx*2], mm1 - } -} - -/*This code implements the bulk of loop_filter_h(). - Data are striped p0 p1 p2 p3 ... p0 p1 p2 p3 ..., so in order to load all - four p0's to one register we must transpose the values in four mmx regs. - When half is done we repeat this for the rest.*/ -static void loop_filter_h4(unsigned char *_pix,long _ystride, - const ogg_int16_t *_ll){ - /* todo: merge the comments from the GCC sources */ - _asm { - mov ecx, [_pix] - mov edx, [_ystride] - mov eax, [_ll] - /*esi=_ystride*3*/ - lea esi, [edx + edx*2] - - movd mm0, dword ptr [ecx] - movd mm1, dword ptr [ecx + edx] - movd mm2, dword ptr [ecx + edx*2] - movd mm3, dword ptr [ecx + esi] - punpcklbw mm0, mm1 - punpcklbw mm2, mm3 - movq mm1, mm0 - punpckhwd mm0, mm2 - punpcklwd mm1, mm2 - pxor mm7, mm7 - movq mm5, mm1 - punpcklbw mm1, mm7 - punpckhbw mm5, mm7 - movq mm3, mm0 - punpcklbw mm0, mm7 - punpckhbw mm3, mm7 - psubw mm1, mm3 - movq mm4, mm0 - pcmpeqw mm2, mm2 - psubw mm0, mm5 - psrlw mm2, 14 - pmullw mm0, mm2 - psrlw mm2, 1 - paddw mm0, mm1 - psllw mm2, 2 - paddw mm0, mm2 - psraw mm0, 3 - movq mm6, qword ptr [eax] - movq mm1, mm0 - pxor mm2, mm2 - movq mm3, mm6 - psubw mm2, mm6 - psllw mm3, 1 - psllw mm2, 1 - pcmpgtw mm3, mm0 - pcmpgtw mm1, mm2 - pand mm0, mm3 - pand mm0, mm1 - psraw mm2, 1 - movq mm1, mm0 - movq mm3, mm6 - pcmpgtw mm2, mm0 - pcmpgtw mm1, mm6 - psllw mm3, 1 - psllw mm6, 1 - pand mm3, mm1 - pand mm6, mm2 - psubw mm0, mm3 - por mm1, mm2 - paddw mm0, mm6 - pand mm1, mm0 - psubw mm0, mm1 - psubw mm0, mm1 - paddw mm5, mm0 - psubw mm4, mm0 - packuswb mm5, mm7 - packuswb mm4, mm7 - punpcklbw mm5, mm4 - movd edi, mm5 - mov word ptr [ecx + 01H], di - psrlq mm5, 32 - shr edi, 16 - mov word ptr [ecx + edx + 01H], di - movd edi, mm5 - mov word ptr [ecx + edx*2 + 01H], di - shr edi, 16 - mov word ptr [ecx + esi + 01H], di - } -} - -static void loop_filter_h(unsigned char *_pix,int _ystride, - const ogg_int16_t *_ll){ - _pix-=2; - loop_filter_h4(_pix,_ystride,_ll); - loop_filter_h4(_pix+(_ystride<<2),_ystride,_ll); -} - - -/*We copy the whole function because the MMX routines will be inlined 4 times, - and we can do just a single emms call at the end this way. - We also do not use the _bv lookup table, instead computing the values that - would lie in it on the fly.*/ - -/*Apply the loop filter to a given set of fragment rows in the given plane. - The filter may be run on the bottom edge, affecting pixels in the next row of - fragments, so this row also needs to be available. - _bv: The bounding values array. - _refi: The index of the frame buffer to filter. - _pli: The color plane to filter. - _fragy0: The Y coordinate of the first fragment row to filter. - _fragy_end: The Y coordinate of the fragment row to stop filtering at.*/ -void oc_state_loop_filter_frag_rows_mmx(oc_theora_state *_state,int *_bv, - int _refi,int _pli,int _fragy0,int _fragy_end){ - ogg_int16_t __declspec(align(8)) ll[4]; - th_img_plane *iplane; - oc_fragment_plane *fplane; - oc_fragment *frag_top; - oc_fragment *frag0; - oc_fragment *frag; - oc_fragment *frag_end; - oc_fragment *frag0_end; - oc_fragment *frag_bot; - ll[0]=ll[1]=ll[2]=ll[3]= - (ogg_int16_t)_state->loop_filter_limits[_state->qis[0]]; - iplane=_state->ref_frame_bufs[_refi]+_pli; - fplane=_state->fplanes+_pli; - /*The following loops are constructed somewhat non-intuitively on purpose. - The main idea is: if a block boundary has at least one coded fragment on - it, the filter is applied to it. - However, the order that the filters are applied in matters, and VP3 chose - the somewhat strange ordering used below.*/ - frag_top=_state->frags+fplane->froffset; - frag0=frag_top+_fragy0*fplane->nhfrags; - frag0_end=frag0+(_fragy_end-_fragy0)*fplane->nhfrags; - frag_bot=_state->frags+fplane->froffset+fplane->nfrags; - while(frag0nhfrags; - while(fragcoded){ - if(frag>frag0){ - loop_filter_h(frag->buffer[_refi],iplane->stride,ll); - } - if(frag0>frag_top){ - loop_filter_v(frag->buffer[_refi],iplane->stride,ll); - } - if(frag+1coded){ - loop_filter_h(frag->buffer[_refi]+8,iplane->stride,ll); - } - if(frag+fplane->nhfragsnhfrags)->coded){ - loop_filter_v((frag+fplane->nhfrags)->buffer[_refi], - iplane->stride,ll); - } - } - frag++; - } - frag0+=fplane->nhfrags; - } - - /*This needs to be removed when decode specific functions are implemented:*/ - _mm_empty(); -} - -#endif diff --git a/Engine/lib/libtheora/lib/dec/x86_vc/mmxstate.c b/Engine/lib/libtheora/lib/dec/x86_vc/mmxstate.c deleted file mode 100644 index 526ef53f3..000000000 --- a/Engine/lib/libtheora/lib/dec/x86_vc/mmxstate.c +++ /dev/null @@ -1,189 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2008 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: mmxstate.c 15400 2008-10-15 12:10:58Z tterribe $ - - ********************************************************************/ - -/* ------------------------------------------------------------------------ - MMX acceleration of complete fragment reconstruction algorithm. - Originally written by Rudolf Marek. - - Conversion to MSC intrinsics by Nils Pipenbrinck. - ---------------------------------------------------------------------*/ -#if defined(USE_ASM) - -#include "../../internal.h" -#include "../idct.h" -#include "x86int.h" -#include - -static const unsigned char OC_FZIG_ZAGMMX[64]= -{ - 0, 8, 1, 2, 9,16,24,17, - 10, 3,32,11,18,25, 4,12, - 5,26,19,40,33,34,41,48, - 27, 6,13,20,28,21,14, 7, - 56,49,42,35,43,50,57,36, - 15,22,29,30,23,44,37,58, - 51,59,38,45,52,31,60,53, - 46,39,47,54,61,62,55,63 -}; - -/* Fill a block with value */ -static __inline void loc_fill_mmx_value (__m64 * _dst, __m64 _value){ - __m64 t = _value; - _dst[0] = t; _dst[1] = t; _dst[2] = t; _dst[3] = t; - _dst[4] = t; _dst[5] = t; _dst[6] = t; _dst[7] = t; - _dst[8] = t; _dst[9] = t; _dst[10] = t; _dst[11] = t; - _dst[12] = t; _dst[13] = t; _dst[14] = t; _dst[15] = t; -} - -/* copy a block of 8 byte elements using different strides */ -static __inline void loc_blockcopy_mmx (unsigned char * _dst, int _dst_ystride, - unsigned char * _src, int _src_ystride){ - __m64 a,b,c,d,e,f,g,h; - a = *(__m64*)(_src + 0 * _src_ystride); - b = *(__m64*)(_src + 1 * _src_ystride); - c = *(__m64*)(_src + 2 * _src_ystride); - d = *(__m64*)(_src + 3 * _src_ystride); - e = *(__m64*)(_src + 4 * _src_ystride); - f = *(__m64*)(_src + 5 * _src_ystride); - g = *(__m64*)(_src + 6 * _src_ystride); - h = *(__m64*)(_src + 7 * _src_ystride); - *(__m64*)(_dst + 0 * _dst_ystride) = a; - *(__m64*)(_dst + 1 * _dst_ystride) = b; - *(__m64*)(_dst + 2 * _dst_ystride) = c; - *(__m64*)(_dst + 3 * _dst_ystride) = d; - *(__m64*)(_dst + 4 * _dst_ystride) = e; - *(__m64*)(_dst + 5 * _dst_ystride) = f; - *(__m64*)(_dst + 6 * _dst_ystride) = g; - *(__m64*)(_dst + 7 * _dst_ystride) = h; -} - -void oc_state_frag_recon_mmx(oc_theora_state *_state,const oc_fragment *_frag, - int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, - ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]){ - ogg_int16_t __declspec(align(16)) res_buf[64]; - int dst_framei; - int dst_ystride; - int zzi; - /*_last_zzi is subtly different from an actual count of the number of - coefficients we decoded for this block. - It contains the value of zzi BEFORE the final token in the block was - decoded. - In most cases this is an EOB token (the continuation of an EOB run from a - previous block counts), and so this is the same as the coefficient count. - However, in the case that the last token was NOT an EOB token, but filled - the block up with exactly 64 coefficients, _last_zzi will be less than 64. - Provided the last token was not a pure zero run, the minimum value it can - be is 46, and so that doesn't affect any of the cases in this routine. - However, if the last token WAS a pure zero run of length 63, then _last_zzi - will be 1 while the number of coefficients decoded is 64. - Thus, we will trigger the following special case, where the real - coefficient count would not. - Note also that a zero run of length 64 will give _last_zzi a value of 0, - but we still process the DC coefficient, which might have a non-zero value - due to DC prediction. - Although convoluted, this is arguably the correct behavior: it allows us to - dequantize fewer coefficients and use a smaller transform when the block - ends with a long zero run instead of a normal EOB token. - It could be smarter... multiple separate zero runs at the end of a block - will fool it, but an encoder that generates these really deserves what it - gets. - Needless to say we inherited this approach from VP3.*/ - /*Special case only having a DC component.*/ - if(_last_zzi<2){ - __m64 p; - /*Why is the iquant product rounded in this case and no others? Who knows.*/ - p = _m_from_int((ogg_int32_t)_frag->dc*_dc_iquant+15>>5); - /* broadcast 16 bits into all 4 mmx subregisters */ - p = _m_punpcklwd (p,p); - p = _m_punpckldq (p,p); - loc_fill_mmx_value ((__m64 *)res_buf, p); - } - else{ - /*Then, fill in the remainder of the coefficients with 0's, and perform - the iDCT.*/ - /*First zero the buffer.*/ - /*On K7, etc., this could be replaced with movntq and sfence.*/ - loc_fill_mmx_value ((__m64 *)res_buf, _mm_setzero_si64()); - - res_buf[0]=(ogg_int16_t)((ogg_int32_t)_frag->dc*_dc_iquant); - /*This is planned to be rewritten in MMX.*/ - for(zzi=1;zzi<_ncoefs;zzi++) - { - int ci; - ci=OC_FZIG_ZAG[zzi]; - res_buf[OC_FZIG_ZAGMMX[zzi]]=(ogg_int16_t)((ogg_int32_t)_dct_coeffs[zzi]* - _ac_iquant[ci]); - } - - if(_last_zzi<10){ - oc_idct8x8_10_mmx(res_buf); - } - else { - oc_idct8x8_mmx(res_buf); - } - } - /*Fill in the target buffer.*/ - dst_framei=_state->ref_frame_idx[OC_FRAME_SELF]; - dst_ystride=_state->ref_frame_bufs[dst_framei][_pli].stride; - /*For now ystride values in all ref frames assumed to be equal.*/ - if(_frag->mbmode==OC_MODE_INTRA){ - oc_frag_recon_intra_mmx(_frag->buffer[dst_framei],dst_ystride,res_buf); - } - else{ - int ref_framei; - int ref_ystride; - int mvoffsets[2]; - ref_framei=_state->ref_frame_idx[OC_FRAME_FOR_MODE[_frag->mbmode]]; - ref_ystride=_state->ref_frame_bufs[ref_framei][_pli].stride; - if(oc_state_get_mv_offsets(_state,mvoffsets,_frag->mv[0], - _frag->mv[1],ref_ystride,_pli)>1){ - oc_frag_recon_inter2_mmx(_frag->buffer[dst_framei],dst_ystride, - _frag->buffer[ref_framei]+mvoffsets[0],ref_ystride, - _frag->buffer[ref_framei]+mvoffsets[1],ref_ystride,res_buf); - } - else{ - oc_frag_recon_inter_mmx(_frag->buffer[dst_framei],dst_ystride, - _frag->buffer[ref_framei]+mvoffsets[0],ref_ystride,res_buf); - } - } - - _mm_empty(); -} - - -void oc_state_frag_copy_mmx(const oc_theora_state *_state,const int *_fragis, - int _nfragis,int _dst_frame,int _src_frame,int _pli){ - const int *fragi; - const int *fragi_end; - int dst_framei; - int dst_ystride; - int src_framei; - int src_ystride; - dst_framei=_state->ref_frame_idx[_dst_frame]; - src_framei=_state->ref_frame_idx[_src_frame]; - dst_ystride=_state->ref_frame_bufs[dst_framei][_pli].stride; - src_ystride=_state->ref_frame_bufs[src_framei][_pli].stride; - fragi_end=_fragis+_nfragis; - for(fragi=_fragis;fragifrags+*fragi; - loc_blockcopy_mmx (frag->buffer[dst_framei], dst_ystride, - frag->buffer[src_framei], src_ystride); - } - _m_empty(); -} - -#endif diff --git a/Engine/lib/libtheora/lib/dec/x86_vc/x86int.h b/Engine/lib/libtheora/lib/dec/x86_vc/x86int.h deleted file mode 100644 index be5016100..000000000 --- a/Engine/lib/libtheora/lib/dec/x86_vc/x86int.h +++ /dev/null @@ -1,49 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: x86int.h 15400 2008-10-15 12:10:58Z tterribe $ - - ********************************************************************/ - -#if !defined(_x86_x86int_vc_H) -# define _x86_x86int_vc_H (1) -# include "../../internal.h" - -void oc_state_vtable_init_x86(oc_theora_state *_state); - -void oc_frag_recon_intra_mmx(unsigned char *_dst,int _dst_ystride, - const ogg_int16_t *_residue); - -void oc_frag_recon_inter_mmx(unsigned char *_dst,int _dst_ystride, - const unsigned char *_src,int _src_ystride,const ogg_int16_t *_residue); - -void oc_frag_recon_inter2_mmx(unsigned char *_dst,int _dst_ystride, - const unsigned char *_src1,int _src1_ystride,const unsigned char *_src2, - int _src2_ystride,const ogg_int16_t *_residue); - -void oc_state_frag_copy_mmx(const oc_theora_state *_state,const int *_fragis, - int _nfragis,int _dst_frame,int _src_frame,int _pli); - -void oc_restore_fpu_mmx(void); - -void oc_state_frag_recon_mmx(oc_theora_state *_state,const oc_fragment *_frag, - int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, - ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]); - -void oc_idct8x8_mmx(ogg_int16_t _y[64]); -void oc_idct8x8_10_mmx(ogg_int16_t _y[64]); - -void oc_state_loop_filter_frag_rows_mmx(oc_theora_state *_state,int *_bv, - int _refi,int _pli,int _fragy0,int _fragy_end); - -#endif diff --git a/Engine/lib/libtheora/lib/dec/decapiwrapper.c b/Engine/lib/libtheora/lib/decapiwrapper.c similarity index 95% rename from Engine/lib/libtheora/lib/dec/decapiwrapper.c rename to Engine/lib/libtheora/lib/decapiwrapper.c index bceec6c26..12ea475d1 100644 --- a/Engine/lib/libtheora/lib/dec/decapiwrapper.c +++ b/Engine/lib/libtheora/lib/decapiwrapper.c @@ -5,7 +5,7 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** @@ -19,6 +19,7 @@ #include #include #include "apiwrapper.h" +#include "decint.h" #include "theora/theoradec.h" static void th_dec_api_clear(th_api_wrapper *_api){ @@ -47,7 +48,7 @@ static double theora_decode_granule_time(theora_state *_td,ogg_int64_t _gp){ return th_granule_time(((th_api_wrapper *)_td->i->codec_setup)->decode,_gp); } -static const oc_state_dispatch_vtbl OC_DEC_DISPATCH_VTBL={ +static const oc_state_dispatch_vtable OC_DEC_DISPATCH_VTBL={ (oc_state_clear_func)theora_decode_clear, (oc_state_control_func)theora_decode_control, (oc_state_granule_frame_func)theora_decode_granule_frame, @@ -95,6 +96,7 @@ int theora_decode_init(theora_state *_td,theora_info *_ci){ This avoids having to figure out whether or not we need to free the info struct in either theora_info_clear() or theora_clear().*/ apiinfo=(th_api_info *)_ogg_calloc(1,sizeof(*apiinfo)); + if(apiinfo==NULL)return OC_FAULT; /*Make our own copy of the info struct, since its lifetime should be independent of the one we were passed in.*/ *&apiinfo->info=*_ci; @@ -130,6 +132,7 @@ int theora_decode_header(theora_info *_ci,theora_comment *_cc,ogg_packet *_op){ theora_info struct like the ones that are used in a theora_state struct.*/ if(api==NULL){ _ci->codec_setup=_ogg_calloc(1,sizeof(*api)); + if(_ci->codec_setup==NULL)return OC_FAULT; api=(th_api_wrapper *)_ci->codec_setup; api->clear=(oc_setup_clear_func)th_dec_api_clear; } @@ -167,12 +170,14 @@ int theora_decode_packetin(theora_state *_td,ogg_packet *_op){ int theora_decode_YUVout(theora_state *_td,yuv_buffer *_yuv){ th_api_wrapper *api; + th_dec_ctx *decode; th_ycbcr_buffer buf; int ret; if(!_td||!_td->i||!_td->i->codec_setup)return OC_FAULT; api=(th_api_wrapper *)_td->i->codec_setup; - if(!api->decode)return OC_FAULT; - ret=th_decode_ycbcr_out(api->decode,buf); + decode=(th_dec_ctx *)api->decode; + if(!decode)return OC_FAULT; + ret=th_decode_ycbcr_out(decode,buf); if(ret>=0){ _yuv->y_width=buf[0].width; _yuv->y_height=buf[0].height; diff --git a/Engine/lib/libtheora/lib/dec/decinfo.c b/Engine/lib/libtheora/lib/decinfo.c similarity index 77% rename from Engine/lib/libtheora/lib/dec/decinfo.c rename to Engine/lib/libtheora/lib/decinfo.c index 3c4ba868a..845eb1361 100644 --- a/Engine/lib/libtheora/lib/dec/decinfo.c +++ b/Engine/lib/libtheora/lib/decinfo.c @@ -5,13 +5,13 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: decinfo.c 15400 2008-10-15 12:10:58Z tterribe $ + last mod: $Id: decinfo.c 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ @@ -27,30 +27,30 @@ _opb: The pack buffer to read the octets from. _buf: The byte array to store the unpacked bytes in. _len: The number of octets to unpack.*/ -static void oc_unpack_octets(oggpack_buffer *_opb,char *_buf,size_t _len){ +static void oc_unpack_octets(oc_pack_buf *_opb,char *_buf,size_t _len){ while(_len-->0){ long val; - theorapackB_read(_opb,8,&val); + val=oc_pack_read(_opb,8); *_buf++=(char)val; } } /*Unpacks a 32-bit integer encoded by octets in little-endian form.*/ -static long oc_unpack_length(oggpack_buffer *_opb){ +static long oc_unpack_length(oc_pack_buf *_opb){ long ret[4]; int i; - for(i=0;i<4;i++)theorapackB_read(_opb,8,ret+i); + for(i=0;i<4;i++)ret[i]=oc_pack_read(_opb,8); return ret[0]|ret[1]<<8|ret[2]<<16|ret[3]<<24; } -static int oc_info_unpack(oggpack_buffer *_opb,th_info *_info){ +static int oc_info_unpack(oc_pack_buf *_opb,th_info *_info){ long val; /*Check the codec bitstream version.*/ - theorapackB_read(_opb,8,&val); + val=oc_pack_read(_opb,8); _info->version_major=(unsigned char)val; - theorapackB_read(_opb,8,&val); + val=oc_pack_read(_opb,8); _info->version_minor=(unsigned char)val; - theorapackB_read(_opb,8,&val); + val=oc_pack_read(_opb,8); _info->version_subminor=(unsigned char)val; /*verify we can parse this bitstream version. We accept earlier minors and all subminors, by spec*/ @@ -60,25 +60,21 @@ static int oc_info_unpack(oggpack_buffer *_opb,th_info *_info){ return TH_EVERSION; } /*Read the encoded frame description.*/ - theorapackB_read(_opb,16,&val); + val=oc_pack_read(_opb,16); _info->frame_width=(ogg_uint32_t)val<<4; - theorapackB_read(_opb,16,&val); + val=oc_pack_read(_opb,16); _info->frame_height=(ogg_uint32_t)val<<4; - theorapackB_read(_opb,24,&val); + val=oc_pack_read(_opb,24); _info->pic_width=(ogg_uint32_t)val; - theorapackB_read(_opb,24,&val); + val=oc_pack_read(_opb,24); _info->pic_height=(ogg_uint32_t)val; - theorapackB_read(_opb,8,&val); + val=oc_pack_read(_opb,8); _info->pic_x=(ogg_uint32_t)val; - /*Note: The sense of pic_y is inverted in what we pass back to the - application compared to how it is stored in the bitstream. - This is because the bitstream uses a right-handed coordinate system, while - applications expect a left-handed one.*/ - theorapackB_read(_opb,8,&val); - _info->pic_y=_info->frame_height-_info->pic_height-(ogg_uint32_t)val; - theorapackB_read(_opb,32,&val); + val=oc_pack_read(_opb,8); + _info->pic_y=(ogg_uint32_t)val; + val=oc_pack_read(_opb,32); _info->fps_numerator=(ogg_uint32_t)val; - theorapackB_read(_opb,32,&val); + val=oc_pack_read(_opb,32); _info->fps_denominator=(ogg_uint32_t)val; if(_info->frame_width==0||_info->frame_height==0|| _info->pic_width+_info->pic_x>_info->frame_width|| @@ -86,38 +82,46 @@ static int oc_info_unpack(oggpack_buffer *_opb,th_info *_info){ _info->fps_numerator==0||_info->fps_denominator==0){ return TH_EBADHEADER; } - theorapackB_read(_opb,24,&val); + /*Note: The sense of pic_y is inverted in what we pass back to the + application compared to how it is stored in the bitstream. + This is because the bitstream uses a right-handed coordinate system, while + applications expect a left-handed one.*/ + _info->pic_y=_info->frame_height-_info->pic_height-_info->pic_y; + val=oc_pack_read(_opb,24); _info->aspect_numerator=(ogg_uint32_t)val; - theorapackB_read(_opb,24,&val); + val=oc_pack_read(_opb,24); _info->aspect_denominator=(ogg_uint32_t)val; - theorapackB_read(_opb,8,&val); + val=oc_pack_read(_opb,8); _info->colorspace=(th_colorspace)val; - theorapackB_read(_opb,24,&val); + val=oc_pack_read(_opb,24); _info->target_bitrate=(int)val; - theorapackB_read(_opb,6,&val); + val=oc_pack_read(_opb,6); _info->quality=(int)val; - theorapackB_read(_opb,5,&val); + val=oc_pack_read(_opb,5); _info->keyframe_granule_shift=(int)val; - theorapackB_read(_opb,2,&val); + val=oc_pack_read(_opb,2); _info->pixel_fmt=(th_pixel_fmt)val; if(_info->pixel_fmt==TH_PF_RSVD)return TH_EBADHEADER; - if(theorapackB_read(_opb,3,&val)<0||val!=0)return TH_EBADHEADER; + val=oc_pack_read(_opb,3); + if(val!=0||oc_pack_bytes_left(_opb)<0)return TH_EBADHEADER; return 0; } -static int oc_comment_unpack(oggpack_buffer *_opb,th_comment *_tc){ +static int oc_comment_unpack(oc_pack_buf *_opb,th_comment *_tc){ long len; int i; /*Read the vendor string.*/ len=oc_unpack_length(_opb); - if(len<0||theorapackB_bytes(_opb)+len>_opb->storage)return TH_EBADHEADER; + if(len<0||len>oc_pack_bytes_left(_opb))return TH_EBADHEADER; _tc->vendor=_ogg_malloc((size_t)len+1); + if(_tc->vendor==NULL)return TH_EFAULT; oc_unpack_octets(_opb,_tc->vendor,len); _tc->vendor[len]='\0'; /*Read the user comments.*/ _tc->comments=(int)oc_unpack_length(_opb); - if(_tc->comments<0||_tc->comments>(LONG_MAX>>2)|| - theorapackB_bytes(_opb)+((long)_tc->comments<<2)>_opb->storage){ + len=_tc->comments; + if(len<0||len>(LONG_MAX>>2)||len<<2>oc_pack_bytes_left(_opb)){ + _tc->comments=0; return TH_EBADHEADER; } _tc->comment_lengths=(int *)_ogg_malloc( @@ -126,19 +130,23 @@ static int oc_comment_unpack(oggpack_buffer *_opb,th_comment *_tc){ _tc->comments*sizeof(_tc->user_comments[0])); for(i=0;i<_tc->comments;i++){ len=oc_unpack_length(_opb); - if(len<0||theorapackB_bytes(_opb)+len>_opb->storage){ + if(len<0||len>oc_pack_bytes_left(_opb)){ _tc->comments=i; return TH_EBADHEADER; } _tc->comment_lengths[i]=len; _tc->user_comments[i]=_ogg_malloc((size_t)len+1); + if(_tc->user_comments[i]==NULL){ + _tc->comments=i; + return TH_EFAULT; + } oc_unpack_octets(_opb,_tc->user_comments[i],len); _tc->user_comments[i][len]='\0'; } - return theorapackB_read(_opb,0,&len)<0?TH_EBADHEADER:0; + return oc_pack_bytes_left(_opb)<0?TH_EBADHEADER:0; } -static int oc_setup_unpack(oggpack_buffer *_opb,th_setup_info *_setup){ +static int oc_setup_unpack(oc_pack_buf *_opb,th_setup_info *_setup){ int ret; /*Read the quantizer tables.*/ ret=oc_quant_params_unpack(_opb,&_setup->qinfo); @@ -152,13 +160,13 @@ static void oc_setup_clear(th_setup_info *_setup){ oc_huff_trees_clear(_setup->huff_tables); } -static int oc_dec_headerin(oggpack_buffer *_opb,th_info *_info, +static int oc_dec_headerin(oc_pack_buf *_opb,th_info *_info, th_comment *_tc,th_setup_info **_setup,ogg_packet *_op){ char buffer[6]; long val; int packtype; int ret; - theorapackB_read(_opb,8,&val); + val=oc_pack_read(_opb,8); packtype=(int)val; /*If we're at a data packet and we have received all three headers, we're done.*/ @@ -198,6 +206,7 @@ static int oc_dec_headerin(oggpack_buffer *_opb,th_info *_info, return TH_EBADHEADER; } setup=(oc_setup_info *)_ogg_calloc(1,sizeof(*setup)); + if(setup==NULL)return TH_EFAULT; ret=oc_setup_unpack(_opb,setup); if(ret<0){ oc_setup_clear(setup); @@ -222,13 +231,11 @@ static int oc_dec_headerin(oggpack_buffer *_opb,th_info *_info, stream until it returns 0.*/ int th_decode_headerin(th_info *_info,th_comment *_tc, th_setup_info **_setup,ogg_packet *_op){ - oggpack_buffer opb; - int ret; + oc_pack_buf opb; if(_op==NULL)return TH_EBADHEADER; if(_info==NULL)return TH_EFAULT; - theorapackB_readinit(&opb,_op->packet,_op->bytes); - ret=oc_dec_headerin(&opb,_info,_tc,_setup,_op); - return ret; + oc_pack_readinit(&opb,_op->packet,_op->bytes); + return oc_dec_headerin(&opb,_info,_tc,_setup,_op); } void th_setup_free(th_setup_info *_setup){ diff --git a/Engine/lib/libtheora/lib/dec/decint.h b/Engine/lib/libtheora/lib/decint.h similarity index 68% rename from Engine/lib/libtheora/lib/dec/decint.h rename to Engine/lib/libtheora/lib/decint.h index 7924c0e0c..261b67631 100644 --- a/Engine/lib/libtheora/lib/dec/decint.h +++ b/Engine/lib/libtheora/lib/decint.h @@ -5,13 +5,13 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: decint.h 15400 2008-10-15 12:10:58Z tterribe $ + last mod: $Id: decint.h 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ @@ -19,13 +19,12 @@ #if !defined(_decint_H) # define _decint_H (1) # include "theora/theoradec.h" -# include "../internal.h" +# include "internal.h" # include "bitpack.h" typedef struct th_setup_info oc_setup_info; typedef struct th_dec_ctx oc_dec_ctx; -# include "idct.h" # include "huffdec.h" # include "dequant.h" @@ -54,24 +53,20 @@ struct th_dec_ctx{ when a frame has been processed and a data packet is ready.*/ int packet_state; /*Buffer in which to assemble packets.*/ - oggpack_buffer opb; + oc_pack_buf opb; /*Huffman decode trees.*/ oc_huff_node *huff_tables[TH_NHUFFMAN_TABLES]; - /*The index of one past the last token in each plane for each coefficient. - The final entries are the total number of tokens for each coefficient.*/ - int ti0[3][64]; - /*The index of one past the last extra bits entry in each plane for each - coefficient. - The final entries are the total number of extra bits entries for each - coefficient.*/ - int ebi0[3][64]; + /*The index of the first token in each plane for each coefficient.*/ + ptrdiff_t ti0[3][64]; /*The number of outstanding EOB runs at the start of each coefficient in each plane.*/ - int eob_runs[3][64]; + ptrdiff_t eob_runs[3][64]; /*The DCT token lists.*/ - unsigned char **dct_tokens; + unsigned char *dct_tokens; /*The extra bits associated with DCT tokens.*/ - ogg_uint16_t **extra_bits; + unsigned char *extra_bits; + /*The number of dct tokens unpacked so far.*/ + int dct_tokens_count; /*The out-of-loop post-processing level.*/ int pp_level; /*The DC scale used for out-of-loop deblocking.*/ @@ -85,11 +80,28 @@ struct th_dec_ctx{ /*The storage for the post-processed frame buffer.*/ unsigned char *pp_frame_data; /*Whether or not the post-processsed frame buffer has space for chroma.*/ - int pp_frame_has_chroma; - /*The buffer used for the post-processed frame.*/ + int pp_frame_state; + /*The buffer used for the post-processed frame. + Note that this is _not_ guaranteed to have the same strides and offsets as + the reference frame buffers.*/ th_ycbcr_buffer pp_frame_buf; /*The striped decode callback function.*/ th_stripe_callback stripe_cb; +# if defined(HAVE_CAIRO) + /*Output metrics for debugging.*/ + int telemetry; + int telemetry_mbmode; + int telemetry_mv; + int telemetry_qi; + int telemetry_bits; + int telemetry_frame_bytes; + int telemetry_coding_bytes; + int telemetry_mode_bytes; + int telemetry_mv_bytes; + int telemetry_qi_bytes; + int telemetry_dc_bytes; + unsigned char *telemetry_frame_data; +# endif }; #endif diff --git a/Engine/lib/libtheora/lib/decode.c b/Engine/lib/libtheora/lib/decode.c new file mode 100644 index 000000000..7be66463d --- /dev/null +++ b/Engine/lib/libtheora/lib/decode.c @@ -0,0 +1,2943 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: decode.c 16581 2009-09-25 22:56:16Z gmaxwell $ + + ********************************************************************/ + +#include +#include +#include +#include "decint.h" +#if defined(OC_DUMP_IMAGES) +# include +# include "png.h" +#endif +#if defined(HAVE_CAIRO) +# include +#endif + + +/*No post-processing.*/ +#define OC_PP_LEVEL_DISABLED (0) +/*Keep track of DC qi for each block only.*/ +#define OC_PP_LEVEL_TRACKDCQI (1) +/*Deblock the luma plane.*/ +#define OC_PP_LEVEL_DEBLOCKY (2) +/*Dering the luma plane.*/ +#define OC_PP_LEVEL_DERINGY (3) +/*Stronger luma plane deringing.*/ +#define OC_PP_LEVEL_SDERINGY (4) +/*Deblock the chroma planes.*/ +#define OC_PP_LEVEL_DEBLOCKC (5) +/*Dering the chroma planes.*/ +#define OC_PP_LEVEL_DERINGC (6) +/*Stronger chroma plane deringing.*/ +#define OC_PP_LEVEL_SDERINGC (7) +/*Maximum valid post-processing level.*/ +#define OC_PP_LEVEL_MAX (7) + + + +/*The mode alphabets for the various mode coding schemes. + Scheme 0 uses a custom alphabet, which is not stored in this table.*/ +static const unsigned char OC_MODE_ALPHABETS[7][OC_NMODES]={ + /*Last MV dominates */ + { + OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV_LAST2,OC_MODE_INTER_MV, + OC_MODE_INTER_NOMV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, + OC_MODE_INTER_MV_FOUR + }, + { + OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV_LAST2,OC_MODE_INTER_NOMV, + OC_MODE_INTER_MV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, + OC_MODE_INTER_MV_FOUR + }, + { + OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV,OC_MODE_INTER_MV_LAST2, + OC_MODE_INTER_NOMV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, + OC_MODE_INTER_MV_FOUR + }, + { + OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV,OC_MODE_INTER_NOMV, + OC_MODE_INTER_MV_LAST2,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV, + OC_MODE_GOLDEN_MV,OC_MODE_INTER_MV_FOUR + }, + /*No MV dominates.*/ + { + OC_MODE_INTER_NOMV,OC_MODE_INTER_MV_LAST,OC_MODE_INTER_MV_LAST2, + OC_MODE_INTER_MV,OC_MODE_INTRA,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, + OC_MODE_INTER_MV_FOUR + }, + { + OC_MODE_INTER_NOMV,OC_MODE_GOLDEN_NOMV,OC_MODE_INTER_MV_LAST, + OC_MODE_INTER_MV_LAST2,OC_MODE_INTER_MV,OC_MODE_INTRA,OC_MODE_GOLDEN_MV, + OC_MODE_INTER_MV_FOUR + }, + /*Default ordering.*/ + { + OC_MODE_INTER_NOMV,OC_MODE_INTRA,OC_MODE_INTER_MV,OC_MODE_INTER_MV_LAST, + OC_MODE_INTER_MV_LAST2,OC_MODE_GOLDEN_NOMV,OC_MODE_GOLDEN_MV, + OC_MODE_INTER_MV_FOUR + } +}; + + +/*The original DCT tokens are extended and reordered during the construction of + the Huffman tables. + The extension means more bits can be read with fewer calls to the bitpacker + during the Huffman decoding process (at the cost of larger Huffman tables), + and fewer tokens require additional extra bits (reducing the average storage + per decoded token). + The revised ordering reveals essential information in the token value + itself; specifically, whether or not there are additional extra bits to read + and the parameter to which those extra bits are applied. + The token is used to fetch a code word from the OC_DCT_CODE_WORD table below. + The extra bits are added into code word at the bit position inferred from the + token value, giving the final code word from which all required parameters + are derived. + The number of EOBs and the leading zero run length can be extracted directly. + The coefficient magnitude is optionally negated before extraction, according + to a 'flip' bit.*/ + +/*The number of additional extra bits that are decoded with each of the + internal DCT tokens.*/ +static const unsigned char OC_INTERNAL_DCT_TOKEN_EXTRA_BITS[15]={ + 12,4,3,3,4,4,5,5,8,8,8,8,3,3,6 +}; + +/*Whether or not an internal token needs any additional extra bits.*/ +#define OC_DCT_TOKEN_NEEDS_MORE(token) \ + (token<(sizeof(OC_INTERNAL_DCT_TOKEN_EXTRA_BITS)/ \ + sizeof(*OC_INTERNAL_DCT_TOKEN_EXTRA_BITS))) + +/*This token (OC_DCT_REPEAT_RUN3_TOKEN) requires more than 8 extra bits.*/ +#define OC_DCT_TOKEN_FAT_EOB (0) + +/*The number of EOBs to use for an end-of-frame token. + Note: We want to set eobs to PTRDIFF_MAX here, but that requires C99, which + is not yet available everywhere; this should be equivalent.*/ +#define OC_DCT_EOB_FINISH (~(size_t)0>>1) + +/*The location of the (6) run legth bits in the code word. + These are placed at index 0 and given 8 bits (even though 6 would suffice) + because it may be faster to extract the lower byte on some platforms.*/ +#define OC_DCT_CW_RLEN_SHIFT (0) +/*The location of the (12) EOB bits in the code word.*/ +#define OC_DCT_CW_EOB_SHIFT (8) +/*The location of the (1) flip bit in the code word. + This must be right under the magnitude bits.*/ +#define OC_DCT_CW_FLIP_BIT (20) +/*The location of the (11) token magnitude bits in the code word. + These must be last, and rely on a sign-extending right shift.*/ +#define OC_DCT_CW_MAG_SHIFT (21) + +/*Pack the given fields into a code word.*/ +#define OC_DCT_CW_PACK(_eobs,_rlen,_mag,_flip) \ + ((_eobs)<state,_info,3); + if(ret<0)return ret; + ret=oc_huff_trees_copy(_dec->huff_tables, + (const oc_huff_node *const *)_setup->huff_tables); + if(ret<0){ + oc_state_clear(&_dec->state); + return ret; + } + /*For each fragment, allocate one byte for every DCT coefficient token, plus + one byte for extra-bits for each token, plus one more byte for the long + EOB run, just in case it's the very last token and has a run length of + one.*/ + _dec->dct_tokens=(unsigned char *)_ogg_malloc((64+64+1)* + _dec->state.nfrags*sizeof(_dec->dct_tokens[0])); + if(_dec->dct_tokens==NULL){ + oc_huff_trees_clear(_dec->huff_tables); + oc_state_clear(&_dec->state); + return TH_EFAULT; + } + for(qi=0;qi<64;qi++)for(pli=0;pli<3;pli++)for(qti=0;qti<2;qti++){ + _dec->state.dequant_tables[qi][pli][qti]= + _dec->state.dequant_table_data[qi][pli][qti]; + } + oc_dequant_tables_init(_dec->state.dequant_tables,_dec->pp_dc_scale, + &_setup->qinfo); + for(qi=0;qi<64;qi++){ + int qsum; + qsum=0; + for(qti=0;qti<2;qti++)for(pli=0;pli<3;pli++){ + qsum+=_dec->state.dequant_tables[qti][pli][qi][12]+ + _dec->state.dequant_tables[qti][pli][qi][17]+ + _dec->state.dequant_tables[qti][pli][qi][18]+ + _dec->state.dequant_tables[qti][pli][qi][24]<<(pli==0); + } + _dec->pp_sharp_mod[qi]=-(qsum>>11); + } + memcpy(_dec->state.loop_filter_limits,_setup->qinfo.loop_filter_limits, + sizeof(_dec->state.loop_filter_limits)); + _dec->pp_level=OC_PP_LEVEL_DISABLED; + _dec->dc_qis=NULL; + _dec->variances=NULL; + _dec->pp_frame_data=NULL; + _dec->stripe_cb.ctx=NULL; + _dec->stripe_cb.stripe_decoded=NULL; +#if defined(HAVE_CAIRO) + _dec->telemetry=0; + _dec->telemetry_bits=0; + _dec->telemetry_qi=0; + _dec->telemetry_mbmode=0; + _dec->telemetry_mv=0; + _dec->telemetry_frame_data=NULL; +#endif + return 0; +} + +static void oc_dec_clear(oc_dec_ctx *_dec){ +#if defined(HAVE_CAIRO) + _ogg_free(_dec->telemetry_frame_data); +#endif + _ogg_free(_dec->pp_frame_data); + _ogg_free(_dec->variances); + _ogg_free(_dec->dc_qis); + _ogg_free(_dec->dct_tokens); + oc_huff_trees_clear(_dec->huff_tables); + oc_state_clear(&_dec->state); +} + + +static int oc_dec_frame_header_unpack(oc_dec_ctx *_dec){ + long val; + /*Check to make sure this is a data packet.*/ + val=oc_pack_read1(&_dec->opb); + if(val!=0)return TH_EBADPACKET; + /*Read in the frame type (I or P).*/ + val=oc_pack_read1(&_dec->opb); + _dec->state.frame_type=(int)val; + /*Read in the qi list.*/ + val=oc_pack_read(&_dec->opb,6); + _dec->state.qis[0]=(unsigned char)val; + val=oc_pack_read1(&_dec->opb); + if(!val)_dec->state.nqis=1; + else{ + val=oc_pack_read(&_dec->opb,6); + _dec->state.qis[1]=(unsigned char)val; + val=oc_pack_read1(&_dec->opb); + if(!val)_dec->state.nqis=2; + else{ + val=oc_pack_read(&_dec->opb,6); + _dec->state.qis[2]=(unsigned char)val; + _dec->state.nqis=3; + } + } + if(_dec->state.frame_type==OC_INTRA_FRAME){ + /*Keyframes have 3 unused configuration bits, holdovers from VP3 days. + Most of the other unused bits in the VP3 headers were eliminated. + I don't know why these remain.*/ + /*I wanted to eliminate wasted bits, but not all config wiggle room + --Monty.*/ + val=oc_pack_read(&_dec->opb,3); + if(val!=0)return TH_EIMPL; + } + return 0; +} + +/*Mark all fragments as coded and in OC_MODE_INTRA. + This also builds up the coded fragment list (in coded order), and clears the + uncoded fragment list. + It does not update the coded macro block list nor the super block flags, as + those are not used when decoding INTRA frames.*/ +static void oc_dec_mark_all_intra(oc_dec_ctx *_dec){ + const oc_sb_map *sb_maps; + const oc_sb_flags *sb_flags; + oc_fragment *frags; + ptrdiff_t *coded_fragis; + ptrdiff_t ncoded_fragis; + ptrdiff_t prev_ncoded_fragis; + unsigned nsbs; + unsigned sbi; + int pli; + coded_fragis=_dec->state.coded_fragis; + prev_ncoded_fragis=ncoded_fragis=0; + sb_maps=(const oc_sb_map *)_dec->state.sb_maps; + sb_flags=_dec->state.sb_flags; + frags=_dec->state.frags; + sbi=nsbs=0; + for(pli=0;pli<3;pli++){ + nsbs+=_dec->state.fplanes[pli].nsbs; + for(;sbi=0){ + frags[fragi].coded=1; + frags[fragi].mb_mode=OC_MODE_INTRA; + coded_fragis[ncoded_fragis++]=fragi; + } + } + } + } + _dec->state.ncoded_fragis[pli]=ncoded_fragis-prev_ncoded_fragis; + prev_ncoded_fragis=ncoded_fragis; + } + _dec->state.ntotal_coded_fragis=ncoded_fragis; +} + +/*Decodes the bit flags indicating whether each super block is partially coded + or not. + Return: The number of partially coded super blocks.*/ +static unsigned oc_dec_partial_sb_flags_unpack(oc_dec_ctx *_dec){ + oc_sb_flags *sb_flags; + unsigned nsbs; + unsigned sbi; + unsigned npartial; + unsigned run_count; + long val; + int flag; + val=oc_pack_read1(&_dec->opb); + flag=(int)val; + sb_flags=_dec->state.sb_flags; + nsbs=_dec->state.nsbs; + sbi=npartial=0; + while(sbiopb); + full_run=run_count>=4129; + do{ + sb_flags[sbi].coded_partially=flag; + sb_flags[sbi].coded_fully=0; + npartial+=flag; + sbi++; + } + while(--run_count>0&&sbiopb); + flag=(int)val; + } + else flag=!flag; + } + /*TODO: run_count should be 0 here. + If it's not, we should issue a warning of some kind.*/ + return npartial; +} + +/*Decodes the bit flags for whether or not each non-partially-coded super + block is fully coded or not. + This function should only be called if there is at least one + non-partially-coded super block. + Return: The number of partially coded super blocks.*/ +static void oc_dec_coded_sb_flags_unpack(oc_dec_ctx *_dec){ + oc_sb_flags *sb_flags; + unsigned nsbs; + unsigned sbi; + unsigned run_count; + long val; + int flag; + sb_flags=_dec->state.sb_flags; + nsbs=_dec->state.nsbs; + /*Skip partially coded super blocks.*/ + for(sbi=0;sb_flags[sbi].coded_partially;sbi++); + val=oc_pack_read1(&_dec->opb); + flag=(int)val; + do{ + int full_run; + run_count=oc_sb_run_unpack(&_dec->opb); + full_run=run_count>=4129; + for(;sbiopb); + flag=(int)val; + } + else flag=!flag; + } + while(sbistate.nsbs)oc_dec_coded_sb_flags_unpack(_dec); + if(npartial>0){ + val=oc_pack_read1(&_dec->opb); + flag=!(int)val; + } + else flag=0; + sb_maps=(const oc_sb_map *)_dec->state.sb_maps; + sb_flags=_dec->state.sb_flags; + frags=_dec->state.frags; + sbi=nsbs=run_count=0; + coded_fragis=_dec->state.coded_fragis; + uncoded_fragis=coded_fragis+_dec->state.nfrags; + prev_ncoded_fragis=ncoded_fragis=nuncoded_fragis=0; + for(pli=0;pli<3;pli++){ + nsbs+=_dec->state.fplanes[pli].nsbs; + for(;sbi=0){ + int coded; + if(sb_flags[sbi].coded_fully)coded=1; + else if(!sb_flags[sbi].coded_partially)coded=0; + else{ + if(run_count<=0){ + run_count=oc_block_run_unpack(&_dec->opb); + flag=!flag; + } + run_count--; + coded=flag; + } + if(coded)coded_fragis[ncoded_fragis++]=fragi; + else *(uncoded_fragis-++nuncoded_fragis)=fragi; + frags[fragi].coded=coded; + } + } + } + } + _dec->state.ncoded_fragis[pli]=ncoded_fragis-prev_ncoded_fragis; + prev_ncoded_fragis=ncoded_fragis; + } + _dec->state.ntotal_coded_fragis=ncoded_fragis; + /*TODO: run_count should be 0 here. + If it's not, we should issue a warning of some kind.*/ +} + + + +typedef int (*oc_mode_unpack_func)(oc_pack_buf *_opb); + +static int oc_vlc_mode_unpack(oc_pack_buf *_opb){ + long val; + int i; + for(i=0;i<7;i++){ + val=oc_pack_read1(_opb); + if(!val)break; + } + return i; +} + +static int oc_clc_mode_unpack(oc_pack_buf *_opb){ + long val; + val=oc_pack_read(_opb,3); + return (int)val; +} + +/*Unpacks the list of macro block modes for INTER frames.*/ +static void oc_dec_mb_modes_unpack(oc_dec_ctx *_dec){ + const oc_mb_map *mb_maps; + signed char *mb_modes; + const oc_fragment *frags; + const unsigned char *alphabet; + unsigned char scheme0_alphabet[8]; + oc_mode_unpack_func mode_unpack; + size_t nmbs; + size_t mbi; + long val; + int mode_scheme; + val=oc_pack_read(&_dec->opb,3); + mode_scheme=(int)val; + if(mode_scheme==0){ + int mi; + /*Just in case, initialize the modes to something. + If the bitstream doesn't contain each index exactly once, it's likely + corrupt and the rest of the packet is garbage anyway, but this way we + won't crash, and we'll decode SOMETHING.*/ + /*LOOP VECTORIZES*/ + for(mi=0;miopb,3); + scheme0_alphabet[val]=OC_MODE_ALPHABETS[6][mi]; + } + alphabet=scheme0_alphabet; + } + else alphabet=OC_MODE_ALPHABETS[mode_scheme-1]; + if(mode_scheme==7)mode_unpack=oc_clc_mode_unpack; + else mode_unpack=oc_vlc_mode_unpack; + mb_modes=_dec->state.mb_modes; + mb_maps=(const oc_mb_map *)_dec->state.mb_maps; + nmbs=_dec->state.nmbs; + frags=_dec->state.frags; + for(mbi=0;mbiopb)]; + /*There were none: INTER_NOMV is forced.*/ + else mb_modes[mbi]=OC_MODE_INTER_NOMV; + } + } +} + + + +typedef int (*oc_mv_comp_unpack_func)(oc_pack_buf *_opb); + +static int oc_vlc_mv_comp_unpack(oc_pack_buf *_opb){ + long bits; + int mask; + int mv; + bits=oc_pack_read(_opb,3); + switch(bits){ + case 0:return 0; + case 1:return 1; + case 2:return -1; + case 3: + case 4:{ + mv=(int)(bits-1); + bits=oc_pack_read1(_opb); + }break; + /*case 5: + case 6: + case 7:*/ + default:{ + mv=1<>1); + bits&=1; + }break; + } + mask=-(int)bits; + return mv+mask^mask; +} + +static int oc_clc_mv_comp_unpack(oc_pack_buf *_opb){ + long bits; + int mask; + int mv; + bits=oc_pack_read(_opb,6); + mv=(int)bits>>1; + mask=-((int)bits&1); + return mv+mask^mask; +} + +/*Unpacks the list of motion vectors for INTER frames, and propagtes the macro + block modes and motion vectors to the individual fragments.*/ +static void oc_dec_mv_unpack_and_frag_modes_fill(oc_dec_ctx *_dec){ + const oc_mb_map *mb_maps; + const signed char *mb_modes; + oc_set_chroma_mvs_func set_chroma_mvs; + oc_mv_comp_unpack_func mv_comp_unpack; + oc_fragment *frags; + oc_mv *frag_mvs; + const unsigned char *map_idxs; + int map_nidxs; + oc_mv last_mv[2]; + oc_mv cbmvs[4]; + size_t nmbs; + size_t mbi; + long val; + set_chroma_mvs=OC_SET_CHROMA_MVS_TABLE[_dec->state.info.pixel_fmt]; + val=oc_pack_read1(&_dec->opb); + mv_comp_unpack=val?oc_clc_mv_comp_unpack:oc_vlc_mv_comp_unpack; + map_idxs=OC_MB_MAP_IDXS[_dec->state.info.pixel_fmt]; + map_nidxs=OC_MB_MAP_NIDXS[_dec->state.info.pixel_fmt]; + memset(last_mv,0,sizeof(last_mv)); + frags=_dec->state.frags; + frag_mvs=_dec->state.frag_mvs; + mb_maps=(const oc_mb_map *)_dec->state.mb_maps; + mb_modes=_dec->state.mb_modes; + nmbs=_dec->state.nmbs; + for(mbi=0;mbi>2][mapi&3]; + if(frags[fragi].coded)coded[ncoded++]=mapi; + } + while(++mapiiopb); + lbmvs[bi][1]=(signed char)(*mv_comp_unpack)(&_dec->opb); + memcpy(frag_mvs[fragi],lbmvs[bi],sizeof(lbmvs[bi])); + } + else lbmvs[bi][0]=lbmvs[bi][1]=0; + } + if(codedi>0){ + memcpy(last_mv[1],last_mv[0],sizeof(last_mv[1])); + memcpy(last_mv[0],lbmvs[coded[codedi-1]],sizeof(last_mv[0])); + } + if(codedi>2][bi]; + frags[fragi].mb_mode=mb_mode; + memcpy(frag_mvs[fragi],cbmvs[bi],sizeof(cbmvs[bi])); + } + } + }break; + case OC_MODE_INTER_MV:{ + memcpy(last_mv[1],last_mv[0],sizeof(last_mv[1])); + mbmv[0]=last_mv[0][0]=(signed char)(*mv_comp_unpack)(&_dec->opb); + mbmv[1]=last_mv[0][1]=(signed char)(*mv_comp_unpack)(&_dec->opb); + }break; + case OC_MODE_INTER_MV_LAST:memcpy(mbmv,last_mv[0],sizeof(mbmv));break; + case OC_MODE_INTER_MV_LAST2:{ + memcpy(mbmv,last_mv[1],sizeof(mbmv)); + memcpy(last_mv[1],last_mv[0],sizeof(last_mv[1])); + memcpy(last_mv[0],mbmv,sizeof(last_mv[0])); + }break; + case OC_MODE_GOLDEN_MV:{ + mbmv[0]=(signed char)(*mv_comp_unpack)(&_dec->opb); + mbmv[1]=(signed char)(*mv_comp_unpack)(&_dec->opb); + }break; + default:memset(mbmv,0,sizeof(mbmv));break; + } + /*4MV mode fills in the fragments itself. + For all other modes we can use this common code.*/ + if(mb_mode!=OC_MODE_INTER_MV_FOUR){ + for(codedi=0;codedi>2][mapi&3]; + frags[fragi].mb_mode=mb_mode; + memcpy(frag_mvs[fragi],mbmv,sizeof(mbmv)); + } + } + } + } +} + +static void oc_dec_block_qis_unpack(oc_dec_ctx *_dec){ + oc_fragment *frags; + const ptrdiff_t *coded_fragis; + ptrdiff_t ncoded_fragis; + ptrdiff_t fragii; + ptrdiff_t fragi; + ncoded_fragis=_dec->state.ntotal_coded_fragis; + if(ncoded_fragis<=0)return; + frags=_dec->state.frags; + coded_fragis=_dec->state.coded_fragis; + if(_dec->state.nqis==1){ + /*If this frame has only a single qi value, then just use it for all coded + fragments.*/ + for(fragii=0;fragiiopb); + flag=(int)val; + nqi1=0; + fragii=0; + while(fragiiopb); + full_run=run_count>=4129; + do{ + frags[coded_fragis[fragii++]].qii=flag; + nqi1+=flag; + } + while(--run_count>0&&fragiiopb); + flag=(int)val; + } + else flag=!flag; + } + /*TODO: run_count should be 0 here. + If it's not, we should issue a warning of some kind.*/ + /*If we have 3 different qi's for this frame, and there was at least one + fragment with a non-zero qi, make the second pass.*/ + if(_dec->state.nqis==3&&nqi1>0){ + /*Skip qii==0 fragments.*/ + for(fragii=0;frags[coded_fragis[fragii]].qii==0;fragii++); + val=oc_pack_read1(&_dec->opb); + flag=(int)val; + do{ + int full_run; + run_count=oc_sb_run_unpack(&_dec->opb); + full_run=run_count>=4129; + for(;fragiiopb); + flag=(int)val; + } + else flag=!flag; + } + while(fragiidct_tokens; + frags=_dec->state.frags; + coded_fragis=_dec->state.coded_fragis; + ncoded_fragis=fragii=eobs=ti=0; + for(pli=0;pli<3;pli++){ + ptrdiff_t run_counts[64]; + ptrdiff_t eob_count; + ptrdiff_t eobi; + int rli; + ncoded_fragis+=_dec->state.ncoded_fragis[pli]; + memset(run_counts,0,sizeof(run_counts)); + _dec->eob_runs[pli][0]=eobs; + _dec->ti0[pli][0]=ti; + /*Continue any previous EOB run, if there was one.*/ + eobi=eobs; + if(ncoded_fragis-fragii0)frags[coded_fragis[fragii++]].dc=0; + while(fragiiopb, + _dec->huff_tables[_huff_idxs[pli+1>>1]]); + dct_tokens[ti++]=(unsigned char)token; + if(OC_DCT_TOKEN_NEEDS_MORE(token)){ + eb=(int)oc_pack_read(&_dec->opb, + OC_INTERNAL_DCT_TOKEN_EXTRA_BITS[token]); + dct_tokens[ti++]=(unsigned char)eb; + if(token==OC_DCT_TOKEN_FAT_EOB)dct_tokens[ti++]=(unsigned char)(eb>>8); + eb<<=OC_DCT_TOKEN_EB_POS(token); + } + else eb=0; + cw=OC_DCT_CODE_WORD[token]+eb; + eobs=cw>>OC_DCT_CW_EOB_SHIFT&0xFFF; + if(cw==OC_DCT_CW_FINISH)eobs=OC_DCT_EOB_FINISH; + if(eobs){ + eobi=OC_MINI(eobs,ncoded_fragis-fragii); + eob_count+=eobi; + eobs-=eobi; + while(eobi-->0)frags[coded_fragis[fragii++]].dc=0; + } + else{ + int coeff; + skip=(unsigned char)(cw>>OC_DCT_CW_RLEN_SHIFT); + cw^=-(cw&1<>OC_DCT_CW_MAG_SHIFT; + if(skip)coeff=0; + run_counts[skip]++; + frags[coded_fragis[fragii++]].dc=coeff; + } + } + /*Add the total EOB count to the longest run length.*/ + run_counts[63]+=eob_count; + /*And convert the run_counts array to a moment table.*/ + for(rli=63;rli-->0;)run_counts[rli]+=run_counts[rli+1]; + /*Finally, subtract off the number of coefficients that have been + accounted for by runs started in this coefficient.*/ + for(rli=64;rli-->0;)_ntoks_left[pli][rli]-=run_counts[rli]; + } + _dec->dct_tokens_count=ti; + return eobs; +} + +/*Unpacks the AC coefficient tokens. + This can completely discard coefficient values while unpacking, and so is + somewhat simpler than unpacking the DC coefficient tokens. + _huff_idx: The index of the Huffman table to use for each color plane. + _ntoks_left: The number of tokens left to be decoded in each color plane for + each coefficient. + This is updated as EOB tokens and zero run tokens are decoded. + _eobs: The length of any outstanding EOB run from previous + coefficients. + Return: The length of any outstanding EOB run.*/ +static int oc_dec_ac_coeff_unpack(oc_dec_ctx *_dec,int _zzi,int _huff_idxs[2], + ptrdiff_t _ntoks_left[3][64],ptrdiff_t _eobs){ + unsigned char *dct_tokens; + ptrdiff_t ti; + int pli; + dct_tokens=_dec->dct_tokens; + ti=_dec->dct_tokens_count; + for(pli=0;pli<3;pli++){ + ptrdiff_t run_counts[64]; + ptrdiff_t eob_count; + size_t ntoks_left; + size_t ntoks; + int rli; + _dec->eob_runs[pli][_zzi]=_eobs; + _dec->ti0[pli][_zzi]=ti; + ntoks_left=_ntoks_left[pli][_zzi]; + memset(run_counts,0,sizeof(run_counts)); + eob_count=0; + ntoks=0; + while(ntoks+_eobsopb, + _dec->huff_tables[_huff_idxs[pli+1>>1]]); + dct_tokens[ti++]=(unsigned char)token; + if(OC_DCT_TOKEN_NEEDS_MORE(token)){ + eb=(int)oc_pack_read(&_dec->opb, + OC_INTERNAL_DCT_TOKEN_EXTRA_BITS[token]); + dct_tokens[ti++]=(unsigned char)eb; + if(token==OC_DCT_TOKEN_FAT_EOB)dct_tokens[ti++]=(unsigned char)(eb>>8); + eb<<=OC_DCT_TOKEN_EB_POS(token); + } + else eb=0; + cw=OC_DCT_CODE_WORD[token]+eb; + skip=(unsigned char)(cw>>OC_DCT_CW_RLEN_SHIFT); + _eobs=cw>>OC_DCT_CW_EOB_SHIFT&0xFFF; + if(cw==OC_DCT_CW_FINISH)_eobs=OC_DCT_EOB_FINISH; + if(_eobs==0){ + run_counts[skip]++; + ntoks++; + } + } + /*Add the portion of the last EOB run actually used by this coefficient.*/ + eob_count+=ntoks_left-ntoks; + /*And remove it from the remaining EOB count.*/ + _eobs-=ntoks_left-ntoks; + /*Add the total EOB count to the longest run length.*/ + run_counts[63]+=eob_count; + /*And convert the run_counts array to a moment table.*/ + for(rli=63;rli-->0;)run_counts[rli]+=run_counts[rli+1]; + /*Finally, subtract off the number of coefficients that have been + accounted for by runs started in this coefficient.*/ + for(rli=64-_zzi;rli-->0;)_ntoks_left[pli][_zzi+rli]-=run_counts[rli]; + } + _dec->dct_tokens_count=ti; + return _eobs; +} + +/*Tokens describing the DCT coefficients that belong to each fragment are + stored in the bitstream grouped by coefficient, not by fragment. + + This means that we either decode all the tokens in order, building up a + separate coefficient list for each fragment as we go, and then go back and + do the iDCT on each fragment, or we have to create separate lists of tokens + for each coefficient, so that we can pull the next token required off the + head of the appropriate list when decoding a specific fragment. + + The former was VP3's choice, and it meant 2*w*h extra storage for all the + decoded coefficient values. + + We take the second option, which lets us store just one to three bytes per + token (generally far fewer than the number of coefficients, due to EOB + tokens and zero runs), and which requires us to only maintain a counter for + each of the 64 coefficients, instead of a counter for every fragment to + determine where the next token goes. + + We actually use 3 counters per coefficient, one for each color plane, so we + can decode all color planes simultaneously. + This lets color conversion, etc., be done as soon as a full MCU (one or + two super block rows) is decoded, while the image data is still in cache.*/ + +static void oc_dec_residual_tokens_unpack(oc_dec_ctx *_dec){ + static const unsigned char OC_HUFF_LIST_MAX[5]={1,6,15,28,64}; + ptrdiff_t ntoks_left[3][64]; + int huff_idxs[2]; + ptrdiff_t eobs; + long val; + int pli; + int zzi; + int hgi; + for(pli=0;pli<3;pli++)for(zzi=0;zzi<64;zzi++){ + ntoks_left[pli][zzi]=_dec->state.ncoded_fragis[pli]; + } + val=oc_pack_read(&_dec->opb,4); + huff_idxs[0]=(int)val; + val=oc_pack_read(&_dec->opb,4); + huff_idxs[1]=(int)val; + _dec->eob_runs[0][0]=0; + eobs=oc_dec_dc_coeff_unpack(_dec,huff_idxs,ntoks_left); +#if defined(HAVE_CAIRO) + _dec->telemetry_dc_bytes=oc_pack_bytes_left(&_dec->opb); +#endif + val=oc_pack_read(&_dec->opb,4); + huff_idxs[0]=(int)val; + val=oc_pack_read(&_dec->opb,4); + huff_idxs[1]=(int)val; + zzi=1; + for(hgi=1;hgi<5;hgi++){ + huff_idxs[0]+=16; + huff_idxs[1]+=16; + for(;zzipp_level<=OC_PP_LEVEL_DISABLED){ + if(_dec->dc_qis!=NULL){ + _ogg_free(_dec->dc_qis); + _dec->dc_qis=NULL; + _ogg_free(_dec->variances); + _dec->variances=NULL; + _ogg_free(_dec->pp_frame_data); + _dec->pp_frame_data=NULL; + } + return 1; + } + if(_dec->dc_qis==NULL){ + /*If we haven't been tracking DC quantization indices, there's no point in + starting now.*/ + if(_dec->state.frame_type!=OC_INTRA_FRAME)return 1; + _dec->dc_qis=(unsigned char *)_ogg_malloc( + _dec->state.nfrags*sizeof(_dec->dc_qis[0])); + if(_dec->dc_qis==NULL)return 1; + memset(_dec->dc_qis,_dec->state.qis[0],_dec->state.nfrags); + } + else{ + unsigned char *dc_qis; + const ptrdiff_t *coded_fragis; + ptrdiff_t ncoded_fragis; + ptrdiff_t fragii; + unsigned char qi0; + /*Update the DC quantization index of each coded block.*/ + dc_qis=_dec->dc_qis; + coded_fragis=_dec->state.coded_fragis; + ncoded_fragis=_dec->state.ncoded_fragis[0]+ + _dec->state.ncoded_fragis[1]+_dec->state.ncoded_fragis[2]; + qi0=(unsigned char)_dec->state.qis[0]; + for(fragii=0;fragiipp_level<=OC_PP_LEVEL_TRACKDCQI){ + if(_dec->variances!=NULL){ + _ogg_free(_dec->variances); + _dec->variances=NULL; + _ogg_free(_dec->pp_frame_data); + _dec->pp_frame_data=NULL; + } + return 1; + } + if(_dec->variances==NULL){ + size_t frame_sz; + size_t c_sz; + int c_w; + int c_h; + frame_sz=_dec->state.info.frame_width*(size_t)_dec->state.info.frame_height; + c_w=_dec->state.info.frame_width>>!(_dec->state.info.pixel_fmt&1); + c_h=_dec->state.info.frame_height>>!(_dec->state.info.pixel_fmt&2); + c_sz=c_w*(size_t)c_h; + /*Allocate space for the chroma planes, even if we're not going to use + them; this simplifies allocation state management, though it may waste + memory on the few systems that don't overcommit pages.*/ + frame_sz+=c_sz<<1; + _dec->pp_frame_data=(unsigned char *)_ogg_malloc( + frame_sz*sizeof(_dec->pp_frame_data[0])); + _dec->variances=(int *)_ogg_malloc( + _dec->state.nfrags*sizeof(_dec->variances[0])); + if(_dec->variances==NULL||_dec->pp_frame_data==NULL){ + _ogg_free(_dec->pp_frame_data); + _dec->pp_frame_data=NULL; + _ogg_free(_dec->variances); + _dec->variances=NULL; + return 1; + } + /*Force an update of the PP buffer pointers.*/ + _dec->pp_frame_state=0; + } + /*Update the PP buffer pointers if necessary.*/ + if(_dec->pp_frame_state!=1+(_dec->pp_level>=OC_PP_LEVEL_DEBLOCKC)){ + if(_dec->pp_levelpp_frame_buf[0].width=_dec->state.info.frame_width; + _dec->pp_frame_buf[0].height=_dec->state.info.frame_height; + _dec->pp_frame_buf[0].stride=-_dec->pp_frame_buf[0].width; + _dec->pp_frame_buf[0].data=_dec->pp_frame_data+ + (1-_dec->pp_frame_buf[0].height)*(ptrdiff_t)_dec->pp_frame_buf[0].stride; + } + else{ + size_t y_sz; + size_t c_sz; + int c_w; + int c_h; + /*Otherwise, set up pointers to all three PP planes.*/ + y_sz=_dec->state.info.frame_width*(size_t)_dec->state.info.frame_height; + c_w=_dec->state.info.frame_width>>!(_dec->state.info.pixel_fmt&1); + c_h=_dec->state.info.frame_height>>!(_dec->state.info.pixel_fmt&2); + c_sz=c_w*(size_t)c_h; + _dec->pp_frame_buf[0].width=_dec->state.info.frame_width; + _dec->pp_frame_buf[0].height=_dec->state.info.frame_height; + _dec->pp_frame_buf[0].stride=_dec->pp_frame_buf[0].width; + _dec->pp_frame_buf[0].data=_dec->pp_frame_data; + _dec->pp_frame_buf[1].width=c_w; + _dec->pp_frame_buf[1].height=c_h; + _dec->pp_frame_buf[1].stride=_dec->pp_frame_buf[1].width; + _dec->pp_frame_buf[1].data=_dec->pp_frame_buf[0].data+y_sz; + _dec->pp_frame_buf[2].width=c_w; + _dec->pp_frame_buf[2].height=c_h; + _dec->pp_frame_buf[2].stride=_dec->pp_frame_buf[2].width; + _dec->pp_frame_buf[2].data=_dec->pp_frame_buf[1].data+c_sz; + oc_ycbcr_buffer_flip(_dec->pp_frame_buf,_dec->pp_frame_buf); + } + _dec->pp_frame_state=1+(_dec->pp_level>=OC_PP_LEVEL_DEBLOCKC); + } + /*If we're not processing chroma, copy the reference frame's chroma planes.*/ + if(_dec->pp_levelpp_frame_buf+1, + _dec->state.ref_frame_bufs[_dec->state.ref_frame_idx[OC_FRAME_SELF]]+1, + sizeof(_dec->pp_frame_buf[1])*2); + } + return 0; +} + + + +typedef struct{ + int bounding_values[256]; + ptrdiff_t ti[3][64]; + ptrdiff_t eob_runs[3][64]; + const ptrdiff_t *coded_fragis[3]; + const ptrdiff_t *uncoded_fragis[3]; + ptrdiff_t ncoded_fragis[3]; + ptrdiff_t nuncoded_fragis[3]; + const ogg_uint16_t *dequant[3][3][2]; + int fragy0[3]; + int fragy_end[3]; + int pred_last[3][3]; + int mcu_nvfrags; + int loop_filter; + int pp_level; +}oc_dec_pipeline_state; + + + +/*Initialize the main decoding pipeline.*/ +static void oc_dec_pipeline_init(oc_dec_ctx *_dec, + oc_dec_pipeline_state *_pipe){ + const ptrdiff_t *coded_fragis; + const ptrdiff_t *uncoded_fragis; + int pli; + int qii; + int qti; + /*If chroma is sub-sampled in the vertical direction, we have to decode two + super block rows of Y' for each super block row of Cb and Cr.*/ + _pipe->mcu_nvfrags=4<state.info.pixel_fmt&2); + /*Initialize the token and extra bits indices for each plane and + coefficient.*/ + memcpy(_pipe->ti,_dec->ti0,sizeof(_pipe->ti)); + /*Also copy over the initial the EOB run counts.*/ + memcpy(_pipe->eob_runs,_dec->eob_runs,sizeof(_pipe->eob_runs)); + /*Set up per-plane pointers to the coded and uncoded fragments lists.*/ + coded_fragis=_dec->state.coded_fragis; + uncoded_fragis=coded_fragis+_dec->state.nfrags; + for(pli=0;pli<3;pli++){ + ptrdiff_t ncoded_fragis; + _pipe->coded_fragis[pli]=coded_fragis; + _pipe->uncoded_fragis[pli]=uncoded_fragis; + ncoded_fragis=_dec->state.ncoded_fragis[pli]; + coded_fragis+=ncoded_fragis; + uncoded_fragis+=ncoded_fragis-_dec->state.fplanes[pli].nfrags; + } + /*Set up condensed quantizer tables.*/ + for(pli=0;pli<3;pli++){ + for(qii=0;qii<_dec->state.nqis;qii++){ + for(qti=0;qti<2;qti++){ + _pipe->dequant[pli][qii][qti]= + _dec->state.dequant_tables[_dec->state.qis[qii]][pli][qti]; + } + } + } + /*Set the previous DC predictor to 0 for all color planes and frame types.*/ + memset(_pipe->pred_last,0,sizeof(_pipe->pred_last)); + /*Initialize the bounding value array for the loop filter.*/ + _pipe->loop_filter=!oc_state_loop_filter_init(&_dec->state, + _pipe->bounding_values); + /*Initialize any buffers needed for post-processing. + We also save the current post-processing level, to guard against the user + changing it from a callback.*/ + if(!oc_dec_postprocess_init(_dec))_pipe->pp_level=_dec->pp_level; + /*If we don't have enough information to post-process, disable it, regardless + of the user-requested level.*/ + else{ + _pipe->pp_level=OC_PP_LEVEL_DISABLED; + memcpy(_dec->pp_frame_buf, + _dec->state.ref_frame_bufs[_dec->state.ref_frame_idx[OC_FRAME_SELF]], + sizeof(_dec->pp_frame_buf[0])*3); + } +} + +/*Undo the DC prediction in a single plane of an MCU (one or two super block + rows). + As a side effect, the number of coded and uncoded fragments in this plane of + the MCU is also computed.*/ +static void oc_dec_dc_unpredict_mcu_plane(oc_dec_ctx *_dec, + oc_dec_pipeline_state *_pipe,int _pli){ + const oc_fragment_plane *fplane; + oc_fragment *frags; + int *pred_last; + ptrdiff_t ncoded_fragis; + ptrdiff_t fragi; + int fragx; + int fragy; + int fragy0; + int fragy_end; + int nhfrags; + /*Compute the first and last fragment row of the current MCU for this + plane.*/ + fplane=_dec->state.fplanes+_pli; + fragy0=_pipe->fragy0[_pli]; + fragy_end=_pipe->fragy_end[_pli]; + nhfrags=fplane->nhfrags; + pred_last=_pipe->pred_last[_pli]; + frags=_dec->state.frags; + ncoded_fragis=0; + fragi=fplane->froffset+fragy0*(ptrdiff_t)nhfrags; + for(fragy=fragy0;fragy=nhfrags)ur_ref=-1; + else{ + ur_ref=u_frags[fragi+1].coded? + OC_FRAME_FOR_MODE(u_frags[fragi+1].mb_mode):-1; + } + if(frags[fragi].coded){ + int pred; + int ref; + ref=OC_FRAME_FOR_MODE(frags[fragi].mb_mode); + /*We break out a separate case based on which of our neighbors use + the same reference frames. + This is somewhat faster than trying to make a generic case which + handles all of them, since it reduces lots of poorly predicted + jumps to one switch statement, and also lets a number of the + multiplications be optimized out by strength reduction.*/ + switch((l_ref==ref)|(ul_ref==ref)<<1| + (u_ref==ref)<<2|(ur_ref==ref)<<3){ + default:pred=pred_last[ref];break; + case 1: + case 3:pred=frags[fragi-1].dc;break; + case 2:pred=u_frags[fragi-1].dc;break; + case 4: + case 6: + case 12:pred=u_frags[fragi].dc;break; + case 5:pred=(frags[fragi-1].dc+u_frags[fragi].dc)/2;break; + case 8:pred=u_frags[fragi+1].dc;break; + case 9: + case 11: + case 13:{ + pred=(75*frags[fragi-1].dc+53*u_frags[fragi+1].dc)/128; + }break; + case 10:pred=(u_frags[fragi-1].dc+u_frags[fragi+1].dc)/2;break; + case 14:{ + pred=(3*(u_frags[fragi-1].dc+u_frags[fragi+1].dc) + +10*u_frags[fragi].dc)/16; + }break; + case 7: + case 15:{ + int p0; + int p1; + int p2; + p0=frags[fragi-1].dc; + p1=u_frags[fragi-1].dc; + p2=u_frags[fragi].dc; + pred=(29*(p0+p2)-26*p1)/32; + if(abs(pred-p2)>128)pred=p2; + else if(abs(pred-p0)>128)pred=p0; + else if(abs(pred-p1)>128)pred=p1; + }break; + } + pred_last[ref]=frags[fragi].dc+=pred; + ncoded_fragis++; + l_ref=ref; + } + else l_ref=-1; + ul_ref=u_ref; + u_ref=ur_ref; + } + } + } + _pipe->ncoded_fragis[_pli]=ncoded_fragis; + /*Also save the number of uncoded fragments so we know how many to copy.*/ + _pipe->nuncoded_fragis[_pli]= + (fragy_end-fragy0)*(ptrdiff_t)nhfrags-ncoded_fragis; +} + +/*Reconstructs all coded fragments in a single MCU (one or two super block + rows). + This requires that each coded fragment have a proper macro block mode and + motion vector (if not in INTRA mode), and have it's DC value decoded, with + the DC prediction process reversed, and the number of coded and uncoded + fragments in this plane of the MCU be counted. + The token lists for each color plane and coefficient should also be filled + in, along with initial token offsets, extra bits offsets, and EOB run + counts.*/ +static void oc_dec_frags_recon_mcu_plane(oc_dec_ctx *_dec, + oc_dec_pipeline_state *_pipe,int _pli){ + unsigned char *dct_tokens; + const unsigned char *dct_fzig_zag; + ogg_uint16_t dc_quant[2]; + const oc_fragment *frags; + const ptrdiff_t *coded_fragis; + ptrdiff_t ncoded_fragis; + ptrdiff_t fragii; + ptrdiff_t *ti; + ptrdiff_t *eob_runs; + int qti; + dct_tokens=_dec->dct_tokens; + dct_fzig_zag=_dec->state.opt_data.dct_fzig_zag; + frags=_dec->state.frags; + coded_fragis=_pipe->coded_fragis[_pli]; + ncoded_fragis=_pipe->ncoded_fragis[_pli]; + ti=_pipe->ti[_pli]; + eob_runs=_pipe->eob_runs[_pli]; + for(qti=0;qti<2;qti++)dc_quant[qti]=_pipe->dequant[_pli][0][qti][0]; + for(fragii=0;fragiidequant[_pli][frags[fragi].qii][qti]; + /*Decode the AC coefficients.*/ + for(zzi=0;zzi<64;){ + int token; + last_zzi=zzi; + if(eob_runs[zzi]){ + eob_runs[zzi]--; + break; + } + else{ + ptrdiff_t eob; + int cw; + int rlen; + int coeff; + int lti; + lti=ti[zzi]; + token=dct_tokens[lti++]; + cw=OC_DCT_CODE_WORD[token]; + /*These parts could be done branchless, but the branches are fairly + predictable and the C code translates into more than a few + instructions, so it's worth it to avoid them.*/ + if(OC_DCT_TOKEN_NEEDS_MORE(token)){ + cw+=dct_tokens[lti++]<>OC_DCT_CW_EOB_SHIFT&0xFFF; + if(token==OC_DCT_TOKEN_FAT_EOB){ + eob+=dct_tokens[lti++]<<8; + if(eob==0)eob=OC_DCT_EOB_FINISH; + } + rlen=(unsigned char)(cw>>OC_DCT_CW_RLEN_SHIFT); + cw^=-(cw&1<>OC_DCT_CW_MAG_SHIFT; + eob_runs[zzi]=eob; + ti[zzi]=lti; + zzi+=rlen; + dct_coeffs[dct_fzig_zag[zzi]]=(ogg_int16_t)(coeff*(int)ac_quant[zzi]); + zzi+=!eob; + } + } + /*TODO: zzi should be exactly 64 here. + If it's not, we should report some kind of warning.*/ + zzi=OC_MINI(zzi,64); + dct_coeffs[0]=(ogg_int16_t)frags[fragi].dc; + /*last_zzi is always initialized. + If your compiler thinks otherwise, it is dumb.*/ + oc_state_frag_recon(&_dec->state,fragi,_pli, + dct_coeffs,last_zzi,dc_quant[qti]); + } + _pipe->coded_fragis[_pli]+=ncoded_fragis; + /*Right now the reconstructed MCU has only the coded blocks in it.*/ + /*TODO: We make the decision here to always copy the uncoded blocks into it + from the reference frame. + We could also copy the coded blocks back over the reference frame, if we + wait for an additional MCU to be decoded, which might be faster if only a + small number of blocks are coded. + However, this introduces more latency, creating a larger cache footprint. + It's unknown which decision is better, but this one results in simpler + code, and the hard case (high bitrate, high resolution) is handled + correctly.*/ + /*Copy the uncoded blocks from the previous reference frame.*/ + _pipe->uncoded_fragis[_pli]-=_pipe->nuncoded_fragis[_pli]; + oc_state_frag_copy_list(&_dec->state,_pipe->uncoded_fragis[_pli], + _pipe->nuncoded_fragis[_pli],OC_FRAME_SELF,OC_FRAME_PREV,_pli); +} + +/*Filter a horizontal block edge.*/ +static void oc_filter_hedge(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src,int _src_ystride,int _qstep,int _flimit, + int *_variance0,int *_variance1){ + unsigned char *rdst; + const unsigned char *rsrc; + unsigned char *cdst; + const unsigned char *csrc; + int r[10]; + int sum0; + int sum1; + int bx; + int by; + rdst=_dst; + rsrc=_src; + for(bx=0;bx<8;bx++){ + cdst=rdst; + csrc=rsrc; + for(by=0;by<10;by++){ + r[by]=*csrc; + csrc+=_src_ystride; + } + sum0=sum1=0; + for(by=0;by<4;by++){ + sum0+=abs(r[by+1]-r[by]); + sum1+=abs(r[by+5]-r[by+6]); + } + *_variance0+=OC_MINI(255,sum0); + *_variance1+=OC_MINI(255,sum1); + if(sum0<_flimit&&sum1<_flimit&&r[5]-r[4]<_qstep&&r[4]-r[5]<_qstep){ + *cdst=(unsigned char)(r[0]*3+r[1]*2+r[2]+r[3]+r[4]+4>>3); + cdst+=_dst_ystride; + *cdst=(unsigned char)(r[0]*2+r[1]+r[2]*2+r[3]+r[4]+r[5]+4>>3); + cdst+=_dst_ystride; + for(by=0;by<4;by++){ + *cdst=(unsigned char)(r[by]+r[by+1]+r[by+2]+r[by+3]*2+ + r[by+4]+r[by+5]+r[by+6]+4>>3); + cdst+=_dst_ystride; + } + *cdst=(unsigned char)(r[4]+r[5]+r[6]+r[7]*2+r[8]+r[9]*2+4>>3); + cdst+=_dst_ystride; + *cdst=(unsigned char)(r[5]+r[6]+r[7]+r[8]*2+r[9]*3+4>>3); + } + else{ + for(by=1;by<=8;by++){ + *cdst=(unsigned char)r[by]; + cdst+=_dst_ystride; + } + } + rdst++; + rsrc++; + } +} + +/*Filter a vertical block edge.*/ +static void oc_filter_vedge(unsigned char *_dst,int _dst_ystride, + int _qstep,int _flimit,int *_variances){ + unsigned char *rdst; + const unsigned char *rsrc; + unsigned char *cdst; + int r[10]; + int sum0; + int sum1; + int bx; + int by; + cdst=_dst; + for(by=0;by<8;by++){ + rsrc=cdst-1; + rdst=cdst; + for(bx=0;bx<10;bx++)r[bx]=*rsrc++; + sum0=sum1=0; + for(bx=0;bx<4;bx++){ + sum0+=abs(r[bx+1]-r[bx]); + sum1+=abs(r[bx+5]-r[bx+6]); + } + _variances[0]+=OC_MINI(255,sum0); + _variances[1]+=OC_MINI(255,sum1); + if(sum0<_flimit&&sum1<_flimit&&r[5]-r[4]<_qstep&&r[4]-r[5]<_qstep){ + *rdst++=(unsigned char)(r[0]*3+r[1]*2+r[2]+r[3]+r[4]+4>>3); + *rdst++=(unsigned char)(r[0]*2+r[1]+r[2]*2+r[3]+r[4]+r[5]+4>>3); + for(bx=0;bx<4;bx++){ + *rdst++=(unsigned char)(r[bx]+r[bx+1]+r[bx+2]+r[bx+3]*2+ + r[bx+4]+r[bx+5]+r[bx+6]+4>>3); + } + *rdst++=(unsigned char)(r[4]+r[5]+r[6]+r[7]*2+r[8]+r[9]*2+4>>3); + *rdst=(unsigned char)(r[5]+r[6]+r[7]+r[8]*2+r[9]*3+4>>3); + } + cdst+=_dst_ystride; + } +} + +static void oc_dec_deblock_frag_rows(oc_dec_ctx *_dec, + th_img_plane *_dst,th_img_plane *_src,int _pli,int _fragy0, + int _fragy_end){ + oc_fragment_plane *fplane; + int *variance; + unsigned char *dc_qi; + unsigned char *dst; + const unsigned char *src; + ptrdiff_t froffset; + int dst_ystride; + int src_ystride; + int nhfrags; + int width; + int notstart; + int notdone; + int flimit; + int qstep; + int y_end; + int y; + int x; + _dst+=_pli; + _src+=_pli; + fplane=_dec->state.fplanes+_pli; + nhfrags=fplane->nhfrags; + froffset=fplane->froffset+_fragy0*(ptrdiff_t)nhfrags; + variance=_dec->variances+froffset; + dc_qi=_dec->dc_qis+froffset; + notstart=_fragy0>0; + notdone=_fragy_endnvfrags; + /*We want to clear an extra row of variances, except at the end.*/ + memset(variance+(nhfrags&-notstart),0, + (_fragy_end+notdone-_fragy0-notstart)*(nhfrags*sizeof(variance[0]))); + /*Except for the first time, we want to point to the middle of the row.*/ + y=(_fragy0<<3)+(notstart<<2); + dst_ystride=_dst->stride; + src_ystride=_src->stride; + dst=_dst->data+y*(ptrdiff_t)dst_ystride; + src=_src->data+y*(ptrdiff_t)src_ystride; + width=_dst->width; + for(;y<4;y++){ + memcpy(dst,src,width*sizeof(dst[0])); + dst+=dst_ystride; + src+=src_ystride; + } + /*We also want to skip the last row in the frame for this loop.*/ + y_end=_fragy_end-!notdone<<3; + for(;ypp_dc_scale[*dc_qi]; + flimit=(qstep*3)>>2; + oc_filter_hedge(dst,dst_ystride,src-src_ystride,src_ystride, + qstep,flimit,variance,variance+nhfrags); + variance++; + dc_qi++; + for(x=8;xpp_dc_scale[*dc_qi]; + flimit=(qstep*3)>>2; + oc_filter_hedge(dst+x,dst_ystride,src+x-src_ystride,src_ystride, + qstep,flimit,variance,variance+nhfrags); + oc_filter_vedge(dst+x-(dst_ystride<<2)-4,dst_ystride, + qstep,flimit,variance-1); + variance++; + dc_qi++; + } + dst+=dst_ystride<<3; + src+=src_ystride<<3; + } + /*And finally, handle the last row in the frame, if it's in the range.*/ + if(!notdone){ + int height; + height=_dst->height; + for(;ypp_dc_scale[*dc_qi++]; + flimit=(qstep*3)>>2; + oc_filter_vedge(dst+x-(dst_ystride<<3)-4,dst_ystride, + qstep,flimit,variance++); + } + } +} + +static void oc_dering_block(unsigned char *_idata,int _ystride,int _b, + int _dc_scale,int _sharp_mod,int _strong){ + static const unsigned char OC_MOD_MAX[2]={24,32}; + static const unsigned char OC_MOD_SHIFT[2]={1,0}; + const unsigned char *psrc; + const unsigned char *src; + const unsigned char *nsrc; + unsigned char *dst; + int vmod[72]; + int hmod[72]; + int mod_hi; + int by; + int bx; + mod_hi=OC_MINI(3*_dc_scale,OC_MOD_MAX[_strong]); + dst=_idata; + src=dst; + psrc=src-(_ystride&-!(_b&4)); + for(by=0;by<9;by++){ + for(bx=0;bx<8;bx++){ + int mod; + mod=32+_dc_scale-(abs(src[bx]-psrc[bx])<>7); + for(bx=1;bx<7;bx++){ + a=128; + b=64; + w=hmod[(bx<<3)+by]; + a-=w; + b+=w*src[bx-1]; + w=vmod[(by<<3)+bx]; + a-=w; + b+=w*psrc[bx]; + w=vmod[(by+1<<3)+bx]; + a-=w; + b+=w*nsrc[bx]; + w=hmod[(bx+1<<3)+by]; + a-=w; + b+=w*src[bx+1]; + dst[bx]=OC_CLAMP255(a*src[bx]+b>>7); + } + a=128; + b=64; + w=hmod[(7<<3)+by]; + a-=w; + b+=w*src[6]; + w=vmod[(by<<3)+7]; + a-=w; + b+=w*psrc[7]; + w=vmod[(by+1<<3)+7]; + a-=w; + b+=w*nsrc[7]; + w=hmod[(8<<3)+by]; + a-=w; + b+=w*src[7+!(_b&2)]; + dst[7]=OC_CLAMP255(a*src[7]+b>>7); + dst+=_ystride; + psrc=src; + src=nsrc; + nsrc+=_ystride&-(!(_b&8)|by<6); + } +} + +#define OC_DERING_THRESH1 (384) +#define OC_DERING_THRESH2 (4*OC_DERING_THRESH1) +#define OC_DERING_THRESH3 (5*OC_DERING_THRESH1) +#define OC_DERING_THRESH4 (10*OC_DERING_THRESH1) + +static void oc_dec_dering_frag_rows(oc_dec_ctx *_dec,th_img_plane *_img, + int _pli,int _fragy0,int _fragy_end){ + th_img_plane *iplane; + oc_fragment_plane *fplane; + oc_fragment *frag; + int *variance; + unsigned char *idata; + ptrdiff_t froffset; + int ystride; + int nhfrags; + int sthresh; + int strong; + int y_end; + int width; + int height; + int y; + int x; + iplane=_img+_pli; + fplane=_dec->state.fplanes+_pli; + nhfrags=fplane->nhfrags; + froffset=fplane->froffset+_fragy0*(ptrdiff_t)nhfrags; + variance=_dec->variances+froffset; + frag=_dec->state.frags+froffset; + strong=_dec->pp_level>=(_pli?OC_PP_LEVEL_SDERINGC:OC_PP_LEVEL_SDERINGY); + sthresh=_pli?OC_DERING_THRESH4:OC_DERING_THRESH3; + y=_fragy0<<3; + ystride=iplane->stride; + idata=iplane->data+y*(ptrdiff_t)ystride; + y_end=_fragy_end<<3; + width=iplane->width; + height=iplane->height; + for(;ystate.qis[frag->qii]; + var=*variance; + b=(x<=0)|(x+8>=width)<<1|(y<=0)<<2|(y+8>=height)<<3; + if(strong&&var>sthresh){ + oc_dering_block(idata+x,ystride,b, + _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1); + if(_pli||!(b&1)&&*(variance-1)>OC_DERING_THRESH4|| + !(b&2)&&variance[1]>OC_DERING_THRESH4|| + !(b&4)&&*(variance-nhfrags)>OC_DERING_THRESH4|| + !(b&8)&&variance[nhfrags]>OC_DERING_THRESH4){ + oc_dering_block(idata+x,ystride,b, + _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1); + oc_dering_block(idata+x,ystride,b, + _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1); + } + } + else if(var>OC_DERING_THRESH2){ + oc_dering_block(idata+x,ystride,b, + _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],1); + } + else if(var>OC_DERING_THRESH1){ + oc_dering_block(idata+x,ystride,b, + _dec->pp_dc_scale[qi],_dec->pp_sharp_mod[qi],0); + } + frag++; + variance++; + } + idata+=ystride<<3; + } +} + + + +th_dec_ctx *th_decode_alloc(const th_info *_info,const th_setup_info *_setup){ + oc_dec_ctx *dec; + if(_info==NULL||_setup==NULL)return NULL; + dec=_ogg_malloc(sizeof(*dec)); + if(dec==NULL||oc_dec_init(dec,_info,_setup)<0){ + _ogg_free(dec); + return NULL; + } + dec->state.curframe_num=0; + return dec; +} + +void th_decode_free(th_dec_ctx *_dec){ + if(_dec!=NULL){ + oc_dec_clear(_dec); + _ogg_free(_dec); + } +} + +int th_decode_ctl(th_dec_ctx *_dec,int _req,void *_buf, + size_t _buf_sz){ + switch(_req){ + case TH_DECCTL_GET_PPLEVEL_MAX:{ + if(_dec==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(int))return TH_EINVAL; + (*(int *)_buf)=OC_PP_LEVEL_MAX; + return 0; + }break; + case TH_DECCTL_SET_PPLEVEL:{ + int pp_level; + if(_dec==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(int))return TH_EINVAL; + pp_level=*(int *)_buf; + if(pp_level<0||pp_level>OC_PP_LEVEL_MAX)return TH_EINVAL; + _dec->pp_level=pp_level; + return 0; + }break; + case TH_DECCTL_SET_GRANPOS:{ + ogg_int64_t granpos; + if(_dec==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(ogg_int64_t))return TH_EINVAL; + granpos=*(ogg_int64_t *)_buf; + if(granpos<0)return TH_EINVAL; + _dec->state.granpos=granpos; + _dec->state.keyframe_num=(granpos>>_dec->state.info.keyframe_granule_shift) + -_dec->state.granpos_bias; + _dec->state.curframe_num=_dec->state.keyframe_num + +(granpos&(1<<_dec->state.info.keyframe_granule_shift)-1); + return 0; + }break; + case TH_DECCTL_SET_STRIPE_CB:{ + th_stripe_callback *cb; + if(_dec==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(th_stripe_callback))return TH_EINVAL; + cb=(th_stripe_callback *)_buf; + _dec->stripe_cb.ctx=cb->ctx; + _dec->stripe_cb.stripe_decoded=cb->stripe_decoded; + return 0; + }break; +#ifdef HAVE_CAIRO + case TH_DECCTL_SET_TELEMETRY_MBMODE:{ + if(_dec==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(int))return TH_EINVAL; + _dec->telemetry=1; + _dec->telemetry_mbmode=*(int *)_buf; + return 0; + }break; + case TH_DECCTL_SET_TELEMETRY_MV:{ + if(_dec==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(int))return TH_EINVAL; + _dec->telemetry=1; + _dec->telemetry_mv=*(int *)_buf; + return 0; + }break; + case TH_DECCTL_SET_TELEMETRY_QI:{ + if(_dec==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(int))return TH_EINVAL; + _dec->telemetry=1; + _dec->telemetry_qi=*(int *)_buf; + return 0; + }break; + case TH_DECCTL_SET_TELEMETRY_BITS:{ + if(_dec==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(int))return TH_EINVAL; + _dec->telemetry=1; + _dec->telemetry_bits=*(int *)_buf; + return 0; + }break; +#endif + default:return TH_EIMPL; + } +} + +/*We're decoding an INTER frame, but have no initialized reference + buffers (i.e., decoding did not start on a key frame). + We initialize them to a solid gray here.*/ +static void oc_dec_init_dummy_frame(th_dec_ctx *_dec){ + th_info *info; + size_t yplane_sz; + size_t cplane_sz; + int yhstride; + int yheight; + int chstride; + int cheight; + _dec->state.ref_frame_idx[OC_FRAME_GOLD]=0; + _dec->state.ref_frame_idx[OC_FRAME_PREV]=0; + _dec->state.ref_frame_idx[OC_FRAME_SELF]=1; + info=&_dec->state.info; + yhstride=info->frame_width+2*OC_UMV_PADDING; + yheight=info->frame_height+2*OC_UMV_PADDING; + chstride=yhstride>>!(info->pixel_fmt&1); + cheight=yheight>>!(info->pixel_fmt&2); + yplane_sz=yhstride*(size_t)yheight; + cplane_sz=chstride*(size_t)cheight; + memset(_dec->state.ref_frame_data[0],0x80,yplane_sz+2*cplane_sz); +} + +int th_decode_packetin(th_dec_ctx *_dec,const ogg_packet *_op, + ogg_int64_t *_granpos){ + int ret; + if(_dec==NULL||_op==NULL)return TH_EFAULT; + /*A completely empty packet indicates a dropped frame and is treated exactly + like an inter frame with no coded blocks. + Only proceed if we have a non-empty packet.*/ + if(_op->bytes!=0){ + oc_dec_pipeline_state pipe; + th_ycbcr_buffer stripe_buf; + int stripe_fragy; + int refi; + int pli; + int notstart; + int notdone; + oc_pack_readinit(&_dec->opb,_op->packet,_op->bytes); +#if defined(HAVE_CAIRO) + _dec->telemetry_frame_bytes=_op->bytes; +#endif + ret=oc_dec_frame_header_unpack(_dec); + if(ret<0)return ret; + /*Select a free buffer to use for the reconstructed version of this + frame.*/ + if(_dec->state.frame_type!=OC_INTRA_FRAME&& + (_dec->state.ref_frame_idx[OC_FRAME_GOLD]<0|| + _dec->state.ref_frame_idx[OC_FRAME_PREV]<0)){ + /*No reference frames yet!*/ + oc_dec_init_dummy_frame(_dec); + refi=_dec->state.ref_frame_idx[OC_FRAME_SELF]; + } + else{ + for(refi=0;refi==_dec->state.ref_frame_idx[OC_FRAME_GOLD]|| + refi==_dec->state.ref_frame_idx[OC_FRAME_PREV];refi++); + _dec->state.ref_frame_idx[OC_FRAME_SELF]=refi; + } + if(_dec->state.frame_type==OC_INTRA_FRAME){ + oc_dec_mark_all_intra(_dec); + _dec->state.keyframe_num=_dec->state.curframe_num; +#if defined(HAVE_CAIRO) + _dec->telemetry_coding_bytes= + _dec->telemetry_mode_bytes= + _dec->telemetry_mv_bytes=oc_pack_bytes_left(&_dec->opb); +#endif + } + else{ + oc_dec_coded_flags_unpack(_dec); +#if defined(HAVE_CAIRO) + _dec->telemetry_coding_bytes=oc_pack_bytes_left(&_dec->opb); +#endif + oc_dec_mb_modes_unpack(_dec); +#if defined(HAVE_CAIRO) + _dec->telemetry_mode_bytes=oc_pack_bytes_left(&_dec->opb); +#endif + oc_dec_mv_unpack_and_frag_modes_fill(_dec); +#if defined(HAVE_CAIRO) + _dec->telemetry_mv_bytes=oc_pack_bytes_left(&_dec->opb); +#endif + } + oc_dec_block_qis_unpack(_dec); +#if defined(HAVE_CAIRO) + _dec->telemetry_qi_bytes=oc_pack_bytes_left(&_dec->opb); +#endif + oc_dec_residual_tokens_unpack(_dec); + /*Update granule position. + This must be done before the striped decode callbacks so that the + application knows what to do with the frame data.*/ + _dec->state.granpos=(_dec->state.keyframe_num+_dec->state.granpos_bias<< + _dec->state.info.keyframe_granule_shift) + +(_dec->state.curframe_num-_dec->state.keyframe_num); + _dec->state.curframe_num++; + if(_granpos!=NULL)*_granpos=_dec->state.granpos; + /*All of the rest of the operations -- DC prediction reversal, + reconstructing coded fragments, copying uncoded fragments, loop + filtering, extending borders, and out-of-loop post-processing -- should + be pipelined. + I.e., DC prediction reversal, reconstruction, and uncoded fragment + copying are done for one or two super block rows, then loop filtering is + run as far as it can, then bordering copying, then post-processing. + For 4:2:0 video a Minimum Codable Unit or MCU contains two luma super + block rows, and one chroma. + Otherwise, an MCU consists of one super block row from each plane. + Inside each MCU, we perform all of the steps on one color plane before + moving on to the next. + After reconstruction, the additional filtering stages introduce a delay + since they need some pixels from the next fragment row. + Thus the actual number of decoded rows available is slightly smaller for + the first MCU, and slightly larger for the last. + + This entire process allows us to operate on the data while it is still in + cache, resulting in big performance improvements. + An application callback allows further application processing (blitting + to video memory, color conversion, etc.) to also use the data while it's + in cache.*/ + oc_dec_pipeline_init(_dec,&pipe); + oc_ycbcr_buffer_flip(stripe_buf,_dec->pp_frame_buf); + notstart=0; + notdone=1; + for(stripe_fragy=0;notdone;stripe_fragy+=pipe.mcu_nvfrags){ + int avail_fragy0; + int avail_fragy_end; + avail_fragy0=avail_fragy_end=_dec->state.fplanes[0].nvfrags; + notdone=stripe_fragy+pipe.mcu_nvfragsstate.fplanes+pli; + /*Compute the first and last fragment row of the current MCU for this + plane.*/ + frag_shift=pli!=0&&!(_dec->state.info.pixel_fmt&2); + pipe.fragy0[pli]=stripe_fragy>>frag_shift; + pipe.fragy_end[pli]=OC_MINI(fplane->nvfrags, + pipe.fragy0[pli]+(pipe.mcu_nvfrags>>frag_shift)); + oc_dec_dc_unpredict_mcu_plane(_dec,&pipe,pli); + oc_dec_frags_recon_mcu_plane(_dec,&pipe,pli); + sdelay=edelay=0; + if(pipe.loop_filter){ + sdelay+=notstart; + edelay+=notdone; + oc_state_loop_filter_frag_rows(&_dec->state,pipe.bounding_values, + refi,pli,pipe.fragy0[pli]-sdelay,pipe.fragy_end[pli]-edelay); + } + /*To fill the borders, we have an additional two pixel delay, since a + fragment in the next row could filter its top edge, using two pixels + from a fragment in this row. + But there's no reason to delay a full fragment between the two.*/ + oc_state_borders_fill_rows(&_dec->state,refi,pli, + (pipe.fragy0[pli]-sdelay<<3)-(sdelay<<1), + (pipe.fragy_end[pli]-edelay<<3)-(edelay<<1)); + /*Out-of-loop post-processing.*/ + pp_offset=3*(pli!=0); + if(pipe.pp_level>=OC_PP_LEVEL_DEBLOCKY+pp_offset){ + /*Perform de-blocking in one plane.*/ + sdelay+=notstart; + edelay+=notdone; + oc_dec_deblock_frag_rows(_dec,_dec->pp_frame_buf, + _dec->state.ref_frame_bufs[refi],pli, + pipe.fragy0[pli]-sdelay,pipe.fragy_end[pli]-edelay); + if(pipe.pp_level>=OC_PP_LEVEL_DERINGY+pp_offset){ + /*Perform de-ringing in one plane.*/ + sdelay+=notstart; + edelay+=notdone; + oc_dec_dering_frag_rows(_dec,_dec->pp_frame_buf,pli, + pipe.fragy0[pli]-sdelay,pipe.fragy_end[pli]-edelay); + } + } + /*If no post-processing is done, we still need to delay a row for the + loop filter, thanks to the strange filtering order VP3 chose.*/ + else if(pipe.loop_filter){ + sdelay+=notstart; + edelay+=notdone; + } + /*Compute the intersection of the available rows in all planes. + If chroma is sub-sampled, the effect of each of its delays is + doubled, but luma might have more post-processing filters enabled + than chroma, so we don't know up front which one is the limiting + factor.*/ + avail_fragy0=OC_MINI(avail_fragy0,pipe.fragy0[pli]-sdelay<stripe_cb.stripe_decoded!=NULL){ + /*The callback might want to use the FPU, so let's make sure they can. + We violate all kinds of ABI restrictions by not doing this until + now, but none of them actually matter since we don't use floating + point ourselves.*/ + oc_restore_fpu(&_dec->state); + /*Make the callback, ensuring we flip the sense of the "start" and + "end" of the available region upside down.*/ + (*_dec->stripe_cb.stripe_decoded)(_dec->stripe_cb.ctx,stripe_buf, + _dec->state.fplanes[0].nvfrags-avail_fragy_end, + _dec->state.fplanes[0].nvfrags-avail_fragy0); + } + notstart=1; + } + /*Finish filling in the reference frame borders.*/ + for(pli=0;pli<3;pli++)oc_state_borders_fill_caps(&_dec->state,refi,pli); + /*Update the reference frame indices.*/ + if(_dec->state.frame_type==OC_INTRA_FRAME){ + /*The new frame becomes both the previous and gold reference frames.*/ + _dec->state.ref_frame_idx[OC_FRAME_GOLD]= + _dec->state.ref_frame_idx[OC_FRAME_PREV]= + _dec->state.ref_frame_idx[OC_FRAME_SELF]; + } + else{ + /*Otherwise, just replace the previous reference frame.*/ + _dec->state.ref_frame_idx[OC_FRAME_PREV]= + _dec->state.ref_frame_idx[OC_FRAME_SELF]; + } + /*Restore the FPU before dump_frame, since that _does_ use the FPU (for PNG + gamma values, if nothing else).*/ + oc_restore_fpu(&_dec->state); +#if defined(OC_DUMP_IMAGES) + /*Don't dump images for dropped frames.*/ + oc_state_dump_frame(&_dec->state,OC_FRAME_SELF,"dec"); +#endif + return 0; + } + else{ + if(_dec->state.ref_frame_idx[OC_FRAME_GOLD]<0|| + _dec->state.ref_frame_idx[OC_FRAME_PREV]<0){ + int refi; + /*No reference frames yet!*/ + oc_dec_init_dummy_frame(_dec); + refi=_dec->state.ref_frame_idx[OC_FRAME_PREV]; + _dec->state.ref_frame_idx[OC_FRAME_SELF]=refi; + memcpy(_dec->pp_frame_buf,_dec->state.ref_frame_bufs[refi], + sizeof(_dec->pp_frame_buf[0])*3); + } + /*Just update the granule position and return.*/ + _dec->state.granpos=(_dec->state.keyframe_num+_dec->state.granpos_bias<< + _dec->state.info.keyframe_granule_shift) + +(_dec->state.curframe_num-_dec->state.keyframe_num); + _dec->state.curframe_num++; + if(_granpos!=NULL)*_granpos=_dec->state.granpos; + return TH_DUPFRAME; + } +} + +int th_decode_ycbcr_out(th_dec_ctx *_dec,th_ycbcr_buffer _ycbcr){ + if(_dec==NULL||_ycbcr==NULL)return TH_EFAULT; + oc_ycbcr_buffer_flip(_ycbcr,_dec->pp_frame_buf); +#if defined(HAVE_CAIRO) + /*If telemetry ioctls are active, we need to draw to the output buffer. + Stuff the plane into cairo.*/ + if(_dec->telemetry){ + cairo_surface_t *cs; + unsigned char *data; + unsigned char *y_row; + unsigned char *u_row; + unsigned char *v_row; + unsigned char *rgb_row; + int cstride; + int w; + int h; + int x; + int y; + int hdec; + int vdec; + w=_ycbcr[0].width; + h=_ycbcr[0].height; + hdec=!(_dec->state.info.pixel_fmt&1); + vdec=!(_dec->state.info.pixel_fmt&2); + /*Lazy data buffer init. + We could try to re-use the post-processing buffer, which would save + memory, but complicate the allocation logic there. + I don't think anyone cares about memory usage when using telemetry; it is + not meant for embedded devices.*/ + if(_dec->telemetry_frame_data==NULL){ + _dec->telemetry_frame_data=_ogg_malloc( + (w*h+2*(w>>hdec)*(h>>vdec))*sizeof(*_dec->telemetry_frame_data)); + if(_dec->telemetry_frame_data==NULL)return 0; + } + cs=cairo_image_surface_create(CAIRO_FORMAT_RGB24,w,h); + /*Sadly, no YUV support in Cairo (yet); convert into the RGB buffer.*/ + data=cairo_image_surface_get_data(cs); + if(data==NULL){ + cairo_surface_destroy(cs); + return 0; + } + cstride=cairo_image_surface_get_stride(cs); + y_row=_ycbcr[0].data; + u_row=_ycbcr[1].data; + v_row=_ycbcr[2].data; + rgb_row=data; + for(y=0;y>hdec]-363703744)/1635200; + g=(3827562*y_row[x]-1287801*u_row[x>>hdec] + -2672387*v_row[x>>hdec]+447306710)/3287200; + b=(952000*y_row[x]+1649289*u_row[x>>hdec]-225932192)/817600; + rgb_row[4*x+0]=OC_CLAMP255(b); + rgb_row[4*x+1]=OC_CLAMP255(g); + rgb_row[4*x+2]=OC_CLAMP255(r); + } + y_row+=_ycbcr[0].stride; + u_row+=_ycbcr[1].stride&-((y&1)|!vdec); + v_row+=_ycbcr[2].stride&-((y&1)|!vdec); + rgb_row+=cstride; + } + /*Draw coded identifier for each macroblock (stored in Hilbert order).*/ + { + cairo_t *c; + const oc_fragment *frags; + oc_mv *frag_mvs; + const signed char *mb_modes; + oc_mb_map *mb_maps; + size_t nmbs; + size_t mbi; + int row2; + int col2; + int qim[3]={0,0,0}; + if(_dec->state.nqis==2){ + int bqi; + bqi=_dec->state.qis[0]; + if(_dec->state.qis[1]>bqi)qim[1]=1; + if(_dec->state.qis[1]state.nqis==3){ + int bqi; + int cqi; + int dqi; + bqi=_dec->state.qis[0]; + cqi=_dec->state.qis[1]; + dqi=_dec->state.qis[2]; + if(cqi>bqi&&dqi>bqi){ + if(dqi>cqi){ + qim[1]=1; + qim[2]=2; + } + else{ + qim[1]=2; + qim[2]=1; + } + } + else if(cqistate.frags; + frag_mvs=_dec->state.frag_mvs; + mb_modes=_dec->state.mb_modes; + mb_maps=_dec->state.mb_maps; + nmbs=_dec->state.nmbs; + row2=0; + col2=0; + for(mbi=0;mbi>1)&1))*16-16; + x=(col2>>1)*16; + cairo_set_line_width(c,1.); + /*Keyframe (all intra) red box.*/ + if(_dec->state.frame_type==OC_INTRA_FRAME){ + if(_dec->telemetry_mbmode&0x02){ + cairo_set_source_rgba(c,1.,0,0,.5); + cairo_rectangle(c,x+2.5,y+2.5,11,11); + cairo_stroke_preserve(c); + cairo_set_source_rgba(c,1.,0,0,.25); + cairo_fill(c); + } + } + else{ + const signed char *frag_mv; + ptrdiff_t fragi; + for(bi=0;bi<4;bi++){ + fragi=mb_maps[mbi][0][bi]; + if(fragi>=0&&frags[fragi].coded){ + frag_mv=frag_mvs[fragi]; + break; + } + } + if(bi<4){ + switch(mb_modes[mbi]){ + case OC_MODE_INTRA:{ + if(_dec->telemetry_mbmode&0x02){ + cairo_set_source_rgba(c,1.,0,0,.5); + cairo_rectangle(c,x+2.5,y+2.5,11,11); + cairo_stroke_preserve(c); + cairo_set_source_rgba(c,1.,0,0,.25); + cairo_fill(c); + } + }break; + case OC_MODE_INTER_NOMV:{ + if(_dec->telemetry_mbmode&0x01){ + cairo_set_source_rgba(c,0,0,1.,.5); + cairo_rectangle(c,x+2.5,y+2.5,11,11); + cairo_stroke_preserve(c); + cairo_set_source_rgba(c,0,0,1.,.25); + cairo_fill(c); + } + }break; + case OC_MODE_INTER_MV:{ + if(_dec->telemetry_mbmode&0x04){ + cairo_rectangle(c,x+2.5,y+2.5,11,11); + cairo_set_source_rgba(c,0,1.,0,.5); + cairo_stroke(c); + } + if(_dec->telemetry_mv&0x04){ + cairo_move_to(c,x+8+frag_mv[0],y+8-frag_mv[1]); + cairo_set_source_rgba(c,1.,1.,1.,.9); + cairo_set_line_width(c,3.); + cairo_line_to(c,x+8+frag_mv[0]*.66,y+8-frag_mv[1]*.66); + cairo_stroke_preserve(c); + cairo_set_line_width(c,2.); + cairo_line_to(c,x+8+frag_mv[0]*.33,y+8-frag_mv[1]*.33); + cairo_stroke_preserve(c); + cairo_set_line_width(c,1.); + cairo_line_to(c,x+8,y+8); + cairo_stroke(c); + } + }break; + case OC_MODE_INTER_MV_LAST:{ + if(_dec->telemetry_mbmode&0x08){ + cairo_rectangle(c,x+2.5,y+2.5,11,11); + cairo_set_source_rgba(c,0,1.,0,.5); + cairo_move_to(c,x+13.5,y+2.5); + cairo_line_to(c,x+2.5,y+8); + cairo_line_to(c,x+13.5,y+13.5); + cairo_stroke(c); + } + if(_dec->telemetry_mv&0x08){ + cairo_move_to(c,x+8+frag_mv[0],y+8-frag_mv[1]); + cairo_set_source_rgba(c,1.,1.,1.,.9); + cairo_set_line_width(c,3.); + cairo_line_to(c,x+8+frag_mv[0]*.66,y+8-frag_mv[1]*.66); + cairo_stroke_preserve(c); + cairo_set_line_width(c,2.); + cairo_line_to(c,x+8+frag_mv[0]*.33,y+8-frag_mv[1]*.33); + cairo_stroke_preserve(c); + cairo_set_line_width(c,1.); + cairo_line_to(c,x+8,y+8); + cairo_stroke(c); + } + }break; + case OC_MODE_INTER_MV_LAST2:{ + if(_dec->telemetry_mbmode&0x10){ + cairo_rectangle(c,x+2.5,y+2.5,11,11); + cairo_set_source_rgba(c,0,1.,0,.5); + cairo_move_to(c,x+8,y+2.5); + cairo_line_to(c,x+2.5,y+8); + cairo_line_to(c,x+8,y+13.5); + cairo_move_to(c,x+13.5,y+2.5); + cairo_line_to(c,x+8,y+8); + cairo_line_to(c,x+13.5,y+13.5); + cairo_stroke(c); + } + if(_dec->telemetry_mv&0x10){ + cairo_move_to(c,x+8+frag_mv[0],y+8-frag_mv[1]); + cairo_set_source_rgba(c,1.,1.,1.,.9); + cairo_set_line_width(c,3.); + cairo_line_to(c,x+8+frag_mv[0]*.66,y+8-frag_mv[1]*.66); + cairo_stroke_preserve(c); + cairo_set_line_width(c,2.); + cairo_line_to(c,x+8+frag_mv[0]*.33,y+8-frag_mv[1]*.33); + cairo_stroke_preserve(c); + cairo_set_line_width(c,1.); + cairo_line_to(c,x+8,y+8); + cairo_stroke(c); + } + }break; + case OC_MODE_GOLDEN_NOMV:{ + if(_dec->telemetry_mbmode&0x20){ + cairo_set_source_rgba(c,1.,1.,0,.5); + cairo_rectangle(c,x+2.5,y+2.5,11,11); + cairo_stroke_preserve(c); + cairo_set_source_rgba(c,1.,1.,0,.25); + cairo_fill(c); + } + }break; + case OC_MODE_GOLDEN_MV:{ + if(_dec->telemetry_mbmode&0x40){ + cairo_rectangle(c,x+2.5,y+2.5,11,11); + cairo_set_source_rgba(c,1.,1.,0,.5); + cairo_stroke(c); + } + if(_dec->telemetry_mv&0x40){ + cairo_move_to(c,x+8+frag_mv[0],y+8-frag_mv[1]); + cairo_set_source_rgba(c,1.,1.,1.,.9); + cairo_set_line_width(c,3.); + cairo_line_to(c,x+8+frag_mv[0]*.66,y+8-frag_mv[1]*.66); + cairo_stroke_preserve(c); + cairo_set_line_width(c,2.); + cairo_line_to(c,x+8+frag_mv[0]*.33,y+8-frag_mv[1]*.33); + cairo_stroke_preserve(c); + cairo_set_line_width(c,1.); + cairo_line_to(c,x+8,y+8); + cairo_stroke(c); + } + }break; + case OC_MODE_INTER_MV_FOUR:{ + if(_dec->telemetry_mbmode&0x80){ + cairo_rectangle(c,x+2.5,y+2.5,4,4); + cairo_rectangle(c,x+9.5,y+2.5,4,4); + cairo_rectangle(c,x+2.5,y+9.5,4,4); + cairo_rectangle(c,x+9.5,y+9.5,4,4); + cairo_set_source_rgba(c,0,1.,0,.5); + cairo_stroke(c); + } + /*4mv is odd, coded in raster order.*/ + fragi=mb_maps[mbi][0][0]; + if(frags[fragi].coded&&_dec->telemetry_mv&0x80){ + frag_mv=frag_mvs[fragi]; + cairo_move_to(c,x+4+frag_mv[0],y+12-frag_mv[1]); + cairo_set_source_rgba(c,1.,1.,1.,.9); + cairo_set_line_width(c,3.); + cairo_line_to(c,x+4+frag_mv[0]*.66,y+12-frag_mv[1]*.66); + cairo_stroke_preserve(c); + cairo_set_line_width(c,2.); + cairo_line_to(c,x+4+frag_mv[0]*.33,y+12-frag_mv[1]*.33); + cairo_stroke_preserve(c); + cairo_set_line_width(c,1.); + cairo_line_to(c,x+4,y+12); + cairo_stroke(c); + } + fragi=mb_maps[mbi][0][1]; + if(frags[fragi].coded&&_dec->telemetry_mv&0x80){ + frag_mv=frag_mvs[fragi]; + cairo_move_to(c,x+12+frag_mv[0],y+12-frag_mv[1]); + cairo_set_source_rgba(c,1.,1.,1.,.9); + cairo_set_line_width(c,3.); + cairo_line_to(c,x+12+frag_mv[0]*.66,y+12-frag_mv[1]*.66); + cairo_stroke_preserve(c); + cairo_set_line_width(c,2.); + cairo_line_to(c,x+12+frag_mv[0]*.33,y+12-frag_mv[1]*.33); + cairo_stroke_preserve(c); + cairo_set_line_width(c,1.); + cairo_line_to(c,x+12,y+12); + cairo_stroke(c); + } + fragi=mb_maps[mbi][0][2]; + if(frags[fragi].coded&&_dec->telemetry_mv&0x80){ + frag_mv=frag_mvs[fragi]; + cairo_move_to(c,x+4+frag_mv[0],y+4-frag_mv[1]); + cairo_set_source_rgba(c,1.,1.,1.,.9); + cairo_set_line_width(c,3.); + cairo_line_to(c,x+4+frag_mv[0]*.66,y+4-frag_mv[1]*.66); + cairo_stroke_preserve(c); + cairo_set_line_width(c,2.); + cairo_line_to(c,x+4+frag_mv[0]*.33,y+4-frag_mv[1]*.33); + cairo_stroke_preserve(c); + cairo_set_line_width(c,1.); + cairo_line_to(c,x+4,y+4); + cairo_stroke(c); + } + fragi=mb_maps[mbi][0][3]; + if(frags[fragi].coded&&_dec->telemetry_mv&0x80){ + frag_mv=frag_mvs[fragi]; + cairo_move_to(c,x+12+frag_mv[0],y+4-frag_mv[1]); + cairo_set_source_rgba(c,1.,1.,1.,.9); + cairo_set_line_width(c,3.); + cairo_line_to(c,x+12+frag_mv[0]*.66,y+4-frag_mv[1]*.66); + cairo_stroke_preserve(c); + cairo_set_line_width(c,2.); + cairo_line_to(c,x+12+frag_mv[0]*.33,y+4-frag_mv[1]*.33); + cairo_stroke_preserve(c); + cairo_set_line_width(c,1.); + cairo_line_to(c,x+12,y+4); + cairo_stroke(c); + } + }break; + } + } + } + /*qii illustration.*/ + if(_dec->telemetry_qi&0x2){ + cairo_set_line_cap(c,CAIRO_LINE_CAP_SQUARE); + for(bi=0;bi<4;bi++){ + ptrdiff_t fragi; + int qiv; + int xp; + int yp; + xp=x+(bi&1)*8; + yp=y+8-(bi&2)*4; + fragi=mb_maps[mbi][0][bi]; + if(fragi>=0&&frags[fragi].coded){ + qiv=qim[frags[fragi].qii]; + cairo_set_line_width(c,3.); + cairo_set_source_rgba(c,0.,0.,0.,.5); + switch(qiv){ + /*Double plus:*/ + case 2:{ + if((bi&1)^((bi&2)>>1)){ + cairo_move_to(c,xp+2.5,yp+1.5); + cairo_line_to(c,xp+2.5,yp+3.5); + cairo_move_to(c,xp+1.5,yp+2.5); + cairo_line_to(c,xp+3.5,yp+2.5); + cairo_move_to(c,xp+5.5,yp+4.5); + cairo_line_to(c,xp+5.5,yp+6.5); + cairo_move_to(c,xp+4.5,yp+5.5); + cairo_line_to(c,xp+6.5,yp+5.5); + cairo_stroke_preserve(c); + cairo_set_source_rgba(c,0.,1.,1.,1.); + } + else{ + cairo_move_to(c,xp+5.5,yp+1.5); + cairo_line_to(c,xp+5.5,yp+3.5); + cairo_move_to(c,xp+4.5,yp+2.5); + cairo_line_to(c,xp+6.5,yp+2.5); + cairo_move_to(c,xp+2.5,yp+4.5); + cairo_line_to(c,xp+2.5,yp+6.5); + cairo_move_to(c,xp+1.5,yp+5.5); + cairo_line_to(c,xp+3.5,yp+5.5); + cairo_stroke_preserve(c); + cairo_set_source_rgba(c,0.,1.,1.,1.); + } + }break; + /*Double minus:*/ + case -2:{ + cairo_move_to(c,xp+2.5,yp+2.5); + cairo_line_to(c,xp+5.5,yp+2.5); + cairo_move_to(c,xp+2.5,yp+5.5); + cairo_line_to(c,xp+5.5,yp+5.5); + cairo_stroke_preserve(c); + cairo_set_source_rgba(c,1.,1.,1.,1.); + }break; + /*Plus:*/ + case 1:{ + if(bi&2==0)yp-=2; + if(bi&1==0)xp-=2; + cairo_move_to(c,xp+4.5,yp+2.5); + cairo_line_to(c,xp+4.5,yp+6.5); + cairo_move_to(c,xp+2.5,yp+4.5); + cairo_line_to(c,xp+6.5,yp+4.5); + cairo_stroke_preserve(c); + cairo_set_source_rgba(c,.1,1.,.3,1.); + break; + } + /*Fall through.*/ + /*Minus:*/ + case -1:{ + cairo_move_to(c,xp+2.5,yp+4.5); + cairo_line_to(c,xp+6.5,yp+4.5); + cairo_stroke_preserve(c); + cairo_set_source_rgba(c,1.,.3,.1,1.); + }break; + default:continue; + } + cairo_set_line_width(c,1.); + cairo_stroke(c); + } + } + } + col2++; + if((col2>>1)>=_dec->state.nhmbs){ + col2=0; + row2+=2; + } + } + /*Bit usage indicator[s]:*/ + if(_dec->telemetry_bits){ + int widths[6]; + int fpsn; + int fpsd; + int mult; + int fullw; + int padw; + int i; + fpsn=_dec->state.info.fps_numerator; + fpsd=_dec->state.info.fps_denominator; + mult=(_dec->telemetry_bits>=0xFF?1:_dec->telemetry_bits); + fullw=250.f*h*fpsd*mult/fpsn; + padw=w-24; + /*Header and coded block bits.*/ + if(_dec->telemetry_frame_bytes<0|| + _dec->telemetry_frame_bytes==OC_LOTS_OF_BITS){ + _dec->telemetry_frame_bytes=0; + } + if(_dec->telemetry_coding_bytes<0|| + _dec->telemetry_coding_bytes>_dec->telemetry_frame_bytes){ + _dec->telemetry_coding_bytes=0; + } + if(_dec->telemetry_mode_bytes<0|| + _dec->telemetry_mode_bytes>_dec->telemetry_frame_bytes){ + _dec->telemetry_mode_bytes=0; + } + if(_dec->telemetry_mv_bytes<0|| + _dec->telemetry_mv_bytes>_dec->telemetry_frame_bytes){ + _dec->telemetry_mv_bytes=0; + } + if(_dec->telemetry_qi_bytes<0|| + _dec->telemetry_qi_bytes>_dec->telemetry_frame_bytes){ + _dec->telemetry_qi_bytes=0; + } + if(_dec->telemetry_dc_bytes<0|| + _dec->telemetry_dc_bytes>_dec->telemetry_frame_bytes){ + _dec->telemetry_dc_bytes=0; + } + widths[0]=padw*(_dec->telemetry_frame_bytes-_dec->telemetry_coding_bytes)/fullw; + widths[1]=padw*(_dec->telemetry_coding_bytes-_dec->telemetry_mode_bytes)/fullw; + widths[2]=padw*(_dec->telemetry_mode_bytes-_dec->telemetry_mv_bytes)/fullw; + widths[3]=padw*(_dec->telemetry_mv_bytes-_dec->telemetry_qi_bytes)/fullw; + widths[4]=padw*(_dec->telemetry_qi_bytes-_dec->telemetry_dc_bytes)/fullw; + widths[5]=padw*(_dec->telemetry_dc_bytes)/fullw; + for(i=0;i<6;i++)if(widths[i]>w)widths[i]=w; + cairo_set_source_rgba(c,.0,.0,.0,.6); + cairo_rectangle(c,10,h-33,widths[0]+1,5); + cairo_rectangle(c,10,h-29,widths[1]+1,5); + cairo_rectangle(c,10,h-25,widths[2]+1,5); + cairo_rectangle(c,10,h-21,widths[3]+1,5); + cairo_rectangle(c,10,h-17,widths[4]+1,5); + cairo_rectangle(c,10,h-13,widths[5]+1,5); + cairo_fill(c); + cairo_set_source_rgb(c,1,0,0); + cairo_rectangle(c,10.5,h-32.5,widths[0],4); + cairo_fill(c); + cairo_set_source_rgb(c,0,1,0); + cairo_rectangle(c,10.5,h-28.5,widths[1],4); + cairo_fill(c); + cairo_set_source_rgb(c,0,0,1); + cairo_rectangle(c,10.5,h-24.5,widths[2],4); + cairo_fill(c); + cairo_set_source_rgb(c,.6,.4,.0); + cairo_rectangle(c,10.5,h-20.5,widths[3],4); + cairo_fill(c); + cairo_set_source_rgb(c,.3,.3,.3); + cairo_rectangle(c,10.5,h-16.5,widths[4],4); + cairo_fill(c); + cairo_set_source_rgb(c,.5,.5,.8); + cairo_rectangle(c,10.5,h-12.5,widths[5],4); + cairo_fill(c); + } + /*Master qi indicator[s]:*/ + if(_dec->telemetry_qi&0x1){ + cairo_text_extents_t extents; + char buffer[10]; + int p; + int y; + p=0; + y=h-7.5; + if(_dec->state.qis[0]>=10)buffer[p++]=48+_dec->state.qis[0]/10; + buffer[p++]=48+_dec->state.qis[0]%10; + if(_dec->state.nqis>=2){ + buffer[p++]=' '; + if(_dec->state.qis[1]>=10)buffer[p++]=48+_dec->state.qis[1]/10; + buffer[p++]=48+_dec->state.qis[1]%10; + } + if(_dec->state.nqis==3){ + buffer[p++]=' '; + if(_dec->state.qis[2]>=10)buffer[p++]=48+_dec->state.qis[2]/10; + buffer[p++]=48+_dec->state.qis[2]%10; + } + buffer[p++]='\0'; + cairo_select_font_face(c,"sans", + CAIRO_FONT_SLANT_NORMAL,CAIRO_FONT_WEIGHT_BOLD); + cairo_set_font_size(c,18); + cairo_text_extents(c,buffer,&extents); + cairo_set_source_rgb(c,1,1,1); + cairo_move_to(c,w-extents.x_advance-10,y); + cairo_show_text(c,buffer); + cairo_set_source_rgb(c,0,0,0); + cairo_move_to(c,w-extents.x_advance-10,y); + cairo_text_path(c,buffer); + cairo_set_line_width(c,.8); + cairo_set_line_join(c,CAIRO_LINE_JOIN_ROUND); + cairo_stroke(c); + } + cairo_destroy(c); + } + /*Out of the Cairo plane into the telemetry YUV buffer.*/ + _ycbcr[0].data=_dec->telemetry_frame_data; + _ycbcr[0].stride=_ycbcr[0].width; + _ycbcr[1].data=_ycbcr[0].data+h*_ycbcr[0].stride; + _ycbcr[1].stride=_ycbcr[1].width; + _ycbcr[2].data=_ycbcr[1].data+(h>>vdec)*_ycbcr[1].stride; + _ycbcr[2].stride=_ycbcr[2].width; + y_row=_ycbcr[0].data; + u_row=_ycbcr[1].data; + v_row=_ycbcr[2].data; + rgb_row=data; + /*This is one of the few places it's worth handling chroma on a + case-by-case basis.*/ + switch(_dec->state.info.pixel_fmt){ + case TH_PF_420:{ + for(y=0;y>1]=OC_CLAMP255(u); + v_row[x>>1]=OC_CLAMP255(v); + } + y_row+=_ycbcr[0].stride<<1; + u_row+=_ycbcr[1].stride; + v_row+=_ycbcr[2].stride; + rgb_row+=cstride<<1; + } + }break; + case TH_PF_422:{ + for(y=0;y>1]=OC_CLAMP255(u); + v_row[x>>1]=OC_CLAMP255(v); + } + y_row+=_ycbcr[0].stride; + u_row+=_ycbcr[1].stride; + v_row+=_ycbcr[2].stride; + rgb_row+=cstride; + } + }break; + /*case TH_PF_444:*/ + default:{ + for(y=0;yloop_filter_limits[qi]=(unsigned char)val; } - theorapackB_read(_opb,4,&val); + val=oc_pack_read(_opb,4); nbits=(int)val+1; for(qi=0;qi<64;qi++){ - theorapackB_read(_opb,nbits,&val); + val=oc_pack_read(_opb,nbits); _qinfo->ac_scale[qi]=(ogg_uint16_t)val; } - theorapackB_read(_opb,4,&val); + val=oc_pack_read(_opb,4); nbits=(int)val+1; for(qi=0;qi<64;qi++){ - theorapackB_read(_opb,nbits,&val); + val=oc_pack_read(_opb,nbits); _qinfo->dc_scale[qi]=(ogg_uint16_t)val; } - theorapackB_read(_opb,9,&val); + val=oc_pack_read(_opb,9); nbase_mats=(int)val+1; base_mats=_ogg_malloc(nbase_mats*sizeof(base_mats[0])); + if(base_mats==NULL)return TH_EFAULT; for(bmi=0;bmiqi_ranges[qti]+pli; if(i>0){ - theorapackB_read1(_opb,&val); + val=oc_pack_read1(_opb); if(!val){ int qtj; int plj; if(qti>0){ - theorapackB_read1(_opb,&val); + val=oc_pack_read1(_opb); if(val){ qtj=qti-1; plj=pli; @@ -95,13 +95,13 @@ int oc_quant_params_unpack(oggpack_buffer *_opb, continue; } } - theorapackB_read(_opb,nbits,&val); + val=oc_pack_read(_opb,nbits); indices[0]=(int)val; for(qi=qri=0;qi<63;){ - theorapackB_read(_opb,oc_ilog(62-qi),&val); + val=oc_pack_read(_opb,oc_ilog(62-qi)); sizes[qri]=(int)val+1; qi+=(int)val+1; - theorapackB_read(_opb,nbits,&val); + val=oc_pack_read(_opb,nbits); indices[++qri]=(int)val; } /*Note: The caller is responsible for cleaning up any partially @@ -112,8 +112,20 @@ int oc_quant_params_unpack(oggpack_buffer *_opb, } qranges->nranges=qri; qranges->sizes=qrsizes=(int *)_ogg_malloc(qri*sizeof(qrsizes[0])); + if(qranges->sizes==NULL){ + /*Note: The caller is responsible for cleaning up any partially + constructed qinfo.*/ + _ogg_free(base_mats); + return TH_EFAULT; + } memcpy(qrsizes,sizes,qri*sizeof(qrsizes[0])); qrbms=(th_quant_base *)_ogg_malloc((qri+1)*sizeof(qrbms[0])); + if(qrbms==NULL){ + /*Note: The caller is responsible for cleaning up any partially + constructed qinfo.*/ + _ogg_free(base_mats); + return TH_EFAULT; + } qranges->base_matrices=(const th_quant_base *)qrbms; do{ bmi=indices[qri]; diff --git a/Engine/lib/libtheora/lib/dec/dequant.h b/Engine/lib/libtheora/lib/dequant.h similarity index 82% rename from Engine/lib/libtheora/lib/dec/dequant.h rename to Engine/lib/libtheora/lib/dequant.h index 928b509e5..ef25838e3 100644 --- a/Engine/lib/libtheora/lib/dec/dequant.h +++ b/Engine/lib/libtheora/lib/dequant.h @@ -5,21 +5,22 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: dequant.h 15400 2008-10-15 12:10:58Z tterribe $ + last mod: $Id: dequant.h 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ #if !defined(_dequant_H) # define _dequant_H (1) # include "quant.h" +# include "bitpack.h" -int oc_quant_params_unpack(oggpack_buffer *_opb, +int oc_quant_params_unpack(oc_pack_buf *_opb, th_quant_info *_qinfo); void oc_quant_params_clear(th_quant_info *_qinfo); diff --git a/Engine/lib/libtheora/lib/enc/block_inline.h b/Engine/lib/libtheora/lib/enc/block_inline.h deleted file mode 100644 index 008977095..000000000 --- a/Engine/lib/libtheora/lib/enc/block_inline.h +++ /dev/null @@ -1,37 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: block_inline.h 14059 2007-10-28 23:43:27Z xiphmont $ - - ********************************************************************/ - -#include "codec_internal.h" - -static const ogg_int32_t MBOrderMap[4] = { 0, 2, 3, 1 }; -static const ogg_int32_t BlockOrderMap1[4][4] = { - { 0, 1, 3, 2 }, - { 0, 2, 3, 1 }, - { 0, 2, 3, 1 }, - { 3, 2, 0, 1 } -}; - -static ogg_int32_t QuadMapToIndex1( ogg_int32_t (*BlockMap)[4][4], - ogg_uint32_t SB, ogg_uint32_t MB, - ogg_uint32_t B ){ - return BlockMap[SB][MBOrderMap[MB]][BlockOrderMap1[MB][B]]; -} - -static ogg_int32_t QuadMapToMBTopLeft( ogg_int32_t (*BlockMap)[4][4], - ogg_uint32_t SB, ogg_uint32_t MB ){ - return BlockMap[SB][MBOrderMap[MB]][0]; -} diff --git a/Engine/lib/libtheora/lib/enc/blockmap.c b/Engine/lib/libtheora/lib/enc/blockmap.c deleted file mode 100644 index 5f3478fc2..000000000 --- a/Engine/lib/libtheora/lib/enc/blockmap.c +++ /dev/null @@ -1,99 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: blockmap.c 14059 2007-10-28 23:43:27Z xiphmont $ - - ********************************************************************/ - -#include "codec_internal.h" - -static void CreateMapping ( ogg_int32_t (*BlockMap)[4][4], - ogg_uint32_t FirstSB, - ogg_uint32_t FirstFrag, ogg_uint32_t HFrags, - ogg_uint32_t VFrags ){ - ogg_uint32_t i, j = 0; - ogg_uint32_t xpos; - ogg_uint32_t ypos; - ogg_uint32_t SBrow, SBcol; - ogg_uint32_t SBRows, SBCols; - ogg_uint32_t MB, B; - - ogg_uint32_t SB=FirstSB; - ogg_uint32_t FragIndex=FirstFrag; - - /* Set Super-Block dimensions */ - SBRows = VFrags/4 + ( VFrags%4 ? 1 : 0 ); - SBCols = HFrags/4 + ( HFrags%4 ? 1 : 0 ); - - /* Map each Super-Block */ - for ( SBrow=0; SBrow - FragIndex */ - - /* Coded flag arrays and counters for them */ - unsigned char *SBCodedFlags; - unsigned char *SBFullyFlags; - unsigned char *MBCodedFlags; - unsigned char *MBFullyFlags; - - /**********************************************************************/ - ogg_uint32_t EOB_Run; - - COORDINATE *FragCoordinates; - MOTION_VECTOR MVector; - ogg_int32_t ReconPtr2Offset; /* Offset for second reconstruction - in half pixel MC */ - Q_LIST_ENTRY *quantized_list; - ogg_int16_t *ReconDataBuffer; - Q_LIST_ENTRY InvLastIntraDC; - Q_LIST_ENTRY InvLastInterDC; - Q_LIST_ENTRY LastIntraDC; - Q_LIST_ENTRY LastInterDC; - - ogg_uint32_t BlocksToDecode; /* Blocks to be decoded this frame */ - ogg_uint32_t DcHuffChoice; /* Huffman table selection variables */ - unsigned char ACHuffChoice; - ogg_uint32_t QuadMBListIndex; - - ogg_int32_t ByteCount; - - ogg_uint32_t bit_pattern; - unsigned char bits_so_far; - unsigned char NextBit; - ogg_int32_t BitsLeft; - - ogg_int16_t *DequantBuffer; - - ogg_int32_t fp_quant_InterUV_coeffs[64]; - ogg_int32_t fp_quant_InterUV_round[64]; - ogg_int32_t fp_ZeroBinSize_InterUV[64]; - - ogg_int16_t *TmpReconBuffer; - ogg_int16_t *TmpDataBuffer; - - /* Loop filter bounding values */ - ogg_int16_t FiltBoundingValue[256]; - - /* Naming convention for all quant matrices and related data structures: - * Fields containing "Inter" in their name are for Inter frames, the - * rest is Intra. */ - - /* Dequantiser and rounding tables */ - ogg_uint16_t *QThreshTable; - Q_LIST_ENTRY dequant_Y_coeffs[64]; - Q_LIST_ENTRY dequant_U_coeffs[64]; - Q_LIST_ENTRY dequant_V_coeffs[64]; - Q_LIST_ENTRY dequant_InterY_coeffs[64]; - Q_LIST_ENTRY dequant_InterU_coeffs[64]; - Q_LIST_ENTRY dequant_InterV_coeffs[64]; - - Q_LIST_ENTRY *dequant_coeffs; /* currently active quantizer */ - unsigned int zigzag_index[64]; - - HUFF_ENTRY *HuffRoot_VP3x[NUM_HUFF_TABLES]; - ogg_uint32_t *HuffCodeArray_VP3x[NUM_HUFF_TABLES]; - unsigned char *HuffCodeLengthArray_VP3x[NUM_HUFF_TABLES]; - const unsigned char *ExtraBitLengths_VP3x; - - th_quant_info quant_info; - oc_quant_tables quant_tables[2][3]; - - /* Quantiser and rounding tables */ - /* this is scheduled to be replaced a new mechanism - that will simply reuse the dequantizer information. */ - ogg_int32_t fp_quant_Y_coeffs[64]; /* used in reiniting quantizers */ - ogg_int32_t fp_quant_U_coeffs[64]; - ogg_int32_t fp_quant_V_coeffs[64]; - ogg_int32_t fp_quant_Inter_Y_coeffs[64]; - ogg_int32_t fp_quant_Inter_U_coeffs[64]; - ogg_int32_t fp_quant_Inter_V_coeffs[64]; - - ogg_int32_t fp_quant_Y_round[64]; - ogg_int32_t fp_quant_U_round[64]; - ogg_int32_t fp_quant_V_round[64]; - ogg_int32_t fp_quant_Inter_Y_round[64]; - ogg_int32_t fp_quant_Inter_U_round[64]; - ogg_int32_t fp_quant_Inter_V_round[64]; - - ogg_int32_t fp_ZeroBinSize_Y[64]; - ogg_int32_t fp_ZeroBinSize_U[64]; - ogg_int32_t fp_ZeroBinSize_V[64]; - ogg_int32_t fp_ZeroBinSize_Inter_Y[64]; - ogg_int32_t fp_ZeroBinSize_Inter_U[64]; - ogg_int32_t fp_ZeroBinSize_Inter_V[64]; - - ogg_int32_t *fquant_coeffs; - ogg_int32_t *fquant_round; - ogg_int32_t *fquant_ZbSize; - - /* Predictor used in choosing entropy table for decoding block patterns. */ - unsigned char BlockPatternPredictor; - - short Modifier[4][512]; - short *ModifierPointer[4]; - - unsigned char *DataOutputInPtr; - - DspFunctions dsp; /* Selected functions for this platform */ - -}; - -/* Encoder (Compressor) instance -- installed in a theora_state */ -typedef struct CP_INSTANCE { - /*This structure must be first. - It contains entry points accessed by the decoder library's API wrapper, and - is the only assumption that library makes about our internal format.*/ - oc_state_dispatch_vtbl dispatch_vtbl; - - /* Compressor Configuration */ - SCAN_CONFIG_DATA ScanConfig; - CONFIG_TYPE2 Configuration; - int GoldenFrameEnabled; - int InterPrediction; - int MotionCompensation; - - ogg_uint32_t LastKeyFrame ; - ogg_int32_t DropCount ; - ogg_int32_t MaxConsDroppedFrames ; - ogg_int32_t DropFrameTriggerBytes; - int DropFrameCandidate; - - /* Compressor Statistics */ - double TotErrScore; - ogg_int64_t KeyFrameCount; /* Count of key frames. */ - ogg_int64_t TotKeyFrameBytes; - ogg_uint32_t LastKeyFrameSize; - ogg_uint32_t PriorKeyFrameSize[KEY_FRAME_CONTEXT]; - ogg_uint32_t PriorKeyFrameDistance[KEY_FRAME_CONTEXT]; - ogg_int32_t FrameQuality[6]; - int DecoderErrorCode; /* Decoder error flag. */ - ogg_int32_t ThreshMapThreshold; - ogg_int32_t TotalMotionScore; - ogg_int64_t TotalByteCount; - ogg_int32_t FixedQ; - - /* Frame Statistics */ - signed char InterCodeCount; - ogg_int64_t CurrentFrame; - ogg_int64_t CarryOver ; - ogg_uint32_t LastFrameSize; - ogg_uint32_t FrameBitCount; - int ThisIsFirstFrame; - int ThisIsKeyFrame; - - ogg_int32_t MotionScore; - ogg_uint32_t RegulationBlocks; - ogg_int32_t RecoveryMotionScore; - int RecoveryBlocksAdded ; - double ProportionRecBlocks; - double MaxRecFactor ; - - /* Rate Targeting variables. */ - ogg_uint32_t ThisFrameTargetBytes; - double BpbCorrectionFactor; - - /* Up regulation variables */ - ogg_uint32_t FinalPassLastPos; /* Used to regulate a final - unrestricted high quality - pass. */ - ogg_uint32_t LastEndSB; /* Where we were in the loop - last time. */ - ogg_uint32_t ResidueLastEndSB; /* Where we were in the residue - update loop last time. */ - - /* Controlling Block Selection */ - ogg_uint32_t MVChangeFactor; - ogg_uint32_t FourMvChangeFactor; - ogg_uint32_t MinImprovementForNewMV; - ogg_uint32_t ExhaustiveSearchThresh; - ogg_uint32_t MinImprovementForFourMV; - ogg_uint32_t FourMVThreshold; - - /* Module shared data structures. */ - ogg_int32_t frame_target_rate; - ogg_int32_t BaseLineFrameTargetRate; - ogg_int32_t min_blocks_per_frame; - ogg_uint32_t tot_bytes_old; - - /*********************************************************************/ - /* Frames Used in the selecetive convolution filtering of the Y plane. */ - unsigned char *ConvDestBuffer; - YUV_BUFFER_ENTRY *yuv0ptr; - YUV_BUFFER_ENTRY *yuv1ptr; - /*********************************************************************/ - - /*********************************************************************/ - /* Token Buffers */ - ogg_uint32_t *OptimisedTokenListEb; /* Optimised token list extra bits */ - unsigned char *OptimisedTokenList; /* Optimised token list. */ - unsigned char *OptimisedTokenListHi; /* Optimised token list huffman - table index */ - - unsigned char *OptimisedTokenListPl; /* Plane to which the token - belongs Y = 0 or UV = 1 */ - ogg_int32_t OptimisedTokenCount; /* Count of Optimized tokens */ - ogg_uint32_t RunHuffIndex; /* Huffman table in force at - the start of a run */ - ogg_uint32_t RunPlaneIndex; /* The plane (Y=0 UV=1) to - which the first token in - an EOB run belonged. */ - - - ogg_uint32_t TotTokenCount; - ogg_int32_t TokensToBeCoded; - ogg_int32_t TokensCoded; - /********************************************************************/ - - /* SuperBlock, MacroBLock and Fragment Information */ - /* Coded flag arrays and counters for them */ - unsigned char *PartiallyCodedFlags; - unsigned char *PartiallyCodedMbPatterns; - unsigned char *UncodedMbFlags; - - unsigned char *extra_fragments; /* extra updates not - recommended by pre-processor */ - ogg_int16_t *OriginalDC; - - ogg_uint32_t *FragmentLastQ; /* Array used to keep track of - quality at which each - fragment was last - updated. */ - unsigned char *FragTokens; - ogg_uint32_t *FragTokenCounts; /* Number of tokens per fragment */ - - ogg_uint32_t *RunHuffIndices; - ogg_uint32_t *LastCodedErrorScore; - ogg_uint32_t *ModeList; - MOTION_VECTOR *MVList; - - unsigned char *BlockCodedFlags; - - ogg_uint32_t MvListCount; - ogg_uint32_t ModeListCount; - - - unsigned char *DataOutputBuffer; - /*********************************************************************/ - - ogg_uint32_t RunLength; - ogg_uint32_t MaxBitTarget; /* Cut off target for rate capping */ - double BitRateCapFactor; /* Factor relating delta frame target - to cut off target. */ - - unsigned char MBCodingMode; /* Coding mode flags */ - - ogg_int32_t MVPixelOffsetY[MAX_SEARCH_SITES]; - ogg_uint32_t InterTripOutThresh; - unsigned char MVEnabled; - ogg_uint32_t MotionVectorSearchCount; - ogg_uint32_t FrameMVSearcOunt; - ogg_int32_t MVSearchSteps; - ogg_int32_t MVOffsetX[MAX_SEARCH_SITES]; - ogg_int32_t MVOffsetY[MAX_SEARCH_SITES]; - ogg_int32_t HalfPixelRef2Offset[9]; /* Offsets for half pixel - compensation */ - signed char HalfPixelXOffset[9]; /* Half pixel MV offsets for X */ - signed char HalfPixelYOffset[9]; /* Half pixel MV offsets for Y */ - - ogg_uint32_t bit_pattern ; - unsigned char bits_so_far ; - ogg_uint32_t lastval ; - ogg_uint32_t lastrun ; - - Q_LIST_ENTRY *quantized_list; - - MOTION_VECTOR MVector; - ogg_uint32_t TempBitCount; - ogg_int16_t *DCT_codes; /* Buffer that stores the result of - Forward DCT */ - ogg_int16_t *DCTDataBuffer; /* Input data buffer for Forward DCT */ - - /* Motion compensation related variables */ - ogg_uint32_t MvMaxExtent; - - double QTargetModifier[Q_TABLE_SIZE]; - - /* instances (used for reconstructing buffers and to hold tokens etc.) */ - PP_INSTANCE pp; /* preprocessor */ - PB_INSTANCE pb; /* playback */ - - /* ogg bitpacker for use in packet coding, other API state */ - oggpack_buffer *oggbuffer; - int readyflag; - int packetflag; - int doneflag; - - DspFunctions dsp; /* Selected functions for this platform */ - -} CP_INSTANCE; - -#define clamp255(x) ((unsigned char)((((x)<0)-1) & ((x) | -((x)>255)))) - -extern void ConfigurePP( PP_INSTANCE *ppi, int Level ) ; -extern ogg_uint32_t YUVAnalyseFrame( PP_INSTANCE *ppi, - ogg_uint32_t * KFIndicator ); - -extern void ClearPPInstance(PP_INSTANCE *ppi); -extern void InitPPInstance(PP_INSTANCE *ppi, DspFunctions *funcs); -extern void InitPBInstance(PB_INSTANCE *pbi); -extern void ClearPBInstance(PB_INSTANCE *pbi); - -extern void IDct1( Q_LIST_ENTRY * InputData, - ogg_int16_t *QuantMatrix, - ogg_int16_t * OutputData ); - -extern void ReconIntra( PB_INSTANCE *pbi, unsigned char * ReconPtr, - ogg_int16_t * ChangePtr, ogg_uint32_t LineStep ); - -extern void ReconInter( PB_INSTANCE *pbi, unsigned char * ReconPtr, - unsigned char * RefPtr, ogg_int16_t * ChangePtr, - ogg_uint32_t LineStep ) ; - -extern void ReconInterHalfPixel2( PB_INSTANCE *pbi, unsigned char * ReconPtr, - unsigned char * RefPtr1, - unsigned char * RefPtr2, - ogg_int16_t * ChangePtr, - ogg_uint32_t LineStep ) ; - -extern void SetupLoopFilter(PB_INSTANCE *pbi); -extern void CopyBlock(unsigned char *src, - unsigned char *dest, - unsigned int srcstride); -extern void LoopFilter(PB_INSTANCE *pbi); -extern void ReconRefFrames (PB_INSTANCE *pbi); -extern void ExpandToken( Q_LIST_ENTRY * ExpandedBlock, - unsigned char * CoeffIndex, ogg_uint32_t Token, - ogg_int32_t ExtraBits ); -extern void ClearDownQFragData(PB_INSTANCE *pbi); - -extern void select_quantiser (PB_INSTANCE *pbi, int type); - -extern void quantize( PB_INSTANCE *pbi, - ogg_int16_t * DCT_block, - Q_LIST_ENTRY * quantized_list); -extern void UpdateQ( PB_INSTANCE *pbi, int NewQIndex ); -extern void UpdateQC( CP_INSTANCE *cpi, ogg_uint32_t NewQ ); -extern void fdct_short ( ogg_int16_t * InputData, ogg_int16_t * OutputData ); -extern ogg_uint32_t DPCMTokenizeBlock (CP_INSTANCE *cpi, - ogg_int32_t FragIndex); -extern void TransformQuantizeBlock (CP_INSTANCE *cpi, ogg_int32_t FragIndex, - ogg_uint32_t PixelsPerLine ) ; -extern void ClearFragmentInfo(PB_INSTANCE * pbi); -extern void InitFragmentInfo(PB_INSTANCE * pbi); -extern void ClearFrameInfo(PB_INSTANCE * pbi); -extern void InitFrameInfo(PB_INSTANCE * pbi, unsigned int FrameSize); -extern void InitializeFragCoordinates(PB_INSTANCE *pbi); -extern void InitFrameDetails(PB_INSTANCE *pbi); -extern void WriteQTables(PB_INSTANCE *pbi,oggpack_buffer *opb); -extern void InitQTables( PB_INSTANCE *pbi ); -extern void quant_tables_init( PB_INSTANCE *pbi, const th_quant_info *qinfo); -extern void InitHuffmanSet( PB_INSTANCE *pbi ); -extern void ClearHuffmanSet( PB_INSTANCE *pbi ); -extern int ReadHuffmanTrees(codec_setup_info *ci, oggpack_buffer *opb); -extern void WriteHuffmanTrees(HUFF_ENTRY *HuffRoot[NUM_HUFF_TABLES], - oggpack_buffer *opb); -extern void InitHuffmanTrees(PB_INSTANCE *pbi, const codec_setup_info *ci); -extern void ClearHuffmanTrees(HUFF_ENTRY *HuffRoot[NUM_HUFF_TABLES]); -extern int ReadFilterTables(codec_setup_info *ci, oggpack_buffer *opb); -extern void QuadDecodeDisplayFragments ( PB_INSTANCE *pbi ); -extern void PackAndWriteDFArray( CP_INSTANCE *cpi ); -extern void UpdateFragQIndex(PB_INSTANCE *pbi); -extern void PostProcess(PB_INSTANCE *pbi); -extern void InitMotionCompensation ( CP_INSTANCE *cpi ); -extern ogg_uint32_t GetMBIntraError (CP_INSTANCE *cpi, ogg_uint32_t FragIndex, - ogg_uint32_t PixelsPerLine ) ; -extern ogg_uint32_t GetMBInterError (CP_INSTANCE *cpi, - unsigned char * SrcPtr, - unsigned char * RefPtr, - ogg_uint32_t FragIndex, - ogg_int32_t LastXMV, - ogg_int32_t LastYMV, - ogg_uint32_t PixelsPerLine ) ; -extern void WriteFrameHeader( CP_INSTANCE *cpi) ; -extern ogg_uint32_t GetMBMVInterError (CP_INSTANCE *cpi, - unsigned char * RefFramePtr, - ogg_uint32_t FragIndex, - ogg_uint32_t PixelsPerLine, - ogg_int32_t *MVPixelOffset, - MOTION_VECTOR *MV ); -extern ogg_uint32_t GetMBMVExhaustiveSearch (CP_INSTANCE *cpi, - unsigned char * RefFramePtr, - ogg_uint32_t FragIndex, - ogg_uint32_t PixelsPerLine, - MOTION_VECTOR *MV ); -extern ogg_uint32_t GetFOURMVExhaustiveSearch (CP_INSTANCE *cpi, - unsigned char * RefFramePtr, - ogg_uint32_t FragIndex, - ogg_uint32_t PixelsPerLine, - MOTION_VECTOR *MV ) ; -extern ogg_uint32_t EncodeData(CP_INSTANCE *cpi); -extern ogg_uint32_t PickIntra( CP_INSTANCE *cpi, - ogg_uint32_t SBRows, - ogg_uint32_t SBCols); -extern ogg_uint32_t PickModes(CP_INSTANCE *cpi, - ogg_uint32_t SBRows, - ogg_uint32_t SBCols, - ogg_uint32_t PixelsPerLine, - ogg_uint32_t *InterError, - ogg_uint32_t *IntraError); - -extern CODING_MODE FrArrayUnpackMode(PB_INSTANCE *pbi); -extern void CreateBlockMapping ( ogg_int32_t (*BlockMap)[4][4], - ogg_uint32_t YSuperBlocks, - ogg_uint32_t UVSuperBlocks, - ogg_uint32_t HFrags, ogg_uint32_t VFrags ); -extern void UpRegulateDataStream (CP_INSTANCE *cpi, ogg_uint32_t RegulationQ, - ogg_int32_t RecoveryBlocks ) ; -extern void RegulateQ( CP_INSTANCE *cpi, ogg_int32_t UpdateScore ); -extern void CopyBackExtraFrags(CP_INSTANCE *cpi); - -extern void UpdateUMVBorder( PB_INSTANCE *pbi, - unsigned char * DestReconPtr ); -extern void PInitFrameInfo(PP_INSTANCE * ppi); - -extern double GetEstimatedBpb( CP_INSTANCE *cpi, ogg_uint32_t TargetQ ); -extern void ClearTmpBuffers(PB_INSTANCE * pbi); -extern void InitTmpBuffers(PB_INSTANCE * pbi); -extern void ScanYUVInit( PP_INSTANCE * ppi, - SCAN_CONFIG_DATA * ScanConfigPtr); - -#endif /* ENCODER_INTERNAL_H */ diff --git a/Engine/lib/libtheora/lib/enc/dct.c b/Engine/lib/libtheora/lib/enc/dct.c deleted file mode 100644 index 29bf8f269..000000000 --- a/Engine/lib/libtheora/lib/enc/dct.c +++ /dev/null @@ -1,268 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: dct.c 13884 2007-09-22 08:38:10Z giles $ - - ********************************************************************/ - -#include "codec_internal.h" -#include "dsp.h" -#include "../cpu.h" - -static ogg_int32_t xC1S7 = 64277; -static ogg_int32_t xC2S6 = 60547; -static ogg_int32_t xC3S5 = 54491; -static ogg_int32_t xC4S4 = 46341; -static ogg_int32_t xC5S3 = 36410; -static ogg_int32_t xC6S2 = 25080; -static ogg_int32_t xC7S1 = 12785; - -#define SIGNBITDUPPED(X) ((signed )(((X) & 0x80000000)) >> 31) -#define DOROUND(X) ( (SIGNBITDUPPED(X) & (0xffff)) + (X) ) - -static void fdct_short__c ( ogg_int16_t * InputData, ogg_int16_t * OutputData ){ - int loop; - - ogg_int32_t is07, is12, is34, is56; - ogg_int32_t is0734, is1256; - ogg_int32_t id07, id12, id34, id56; - - ogg_int32_t irot_input_x, irot_input_y; - ogg_int32_t icommon_product1; /* Re-used product (c4s4 * (s12 - s56)). */ - ogg_int32_t icommon_product2; /* Re-used product (c4s4 * (d12 + d56)). */ - - ogg_int32_t temp1, temp2; /* intermediate variable for computation */ - - ogg_int32_t InterData[64]; - ogg_int32_t *ip = InterData; - ogg_int16_t * op = OutputData; - for (loop = 0; loop < 8; loop++){ - /* Pre calculate some common sums and differences. */ - is07 = InputData[0] + InputData[7]; - is12 = InputData[1] + InputData[2]; - is34 = InputData[3] + InputData[4]; - is56 = InputData[5] + InputData[6]; - - id07 = InputData[0] - InputData[7]; - id12 = InputData[1] - InputData[2]; - id34 = InputData[3] - InputData[4]; - id56 = InputData[5] - InputData[6]; - - is0734 = is07 + is34; - is1256 = is12 + is56; - - /* Pre-Calculate some common product terms. */ - icommon_product1 = xC4S4*(is12 - is56); - icommon_product1 = DOROUND(icommon_product1); - icommon_product1>>=16; - - icommon_product2 = xC4S4*(id12 + id56); - icommon_product2 = DOROUND(icommon_product2); - icommon_product2>>=16; - - - ip[0] = (xC4S4*(is0734 + is1256)); - ip[0] = DOROUND(ip[0]); - ip[0] >>= 16; - - ip[4] = (xC4S4*(is0734 - is1256)); - ip[4] = DOROUND(ip[4]); - ip[4] >>= 16; - - /* Define inputs to rotation for outputs 2 and 6 */ - irot_input_x = id12 - id56; - irot_input_y = is07 - is34; - - /* Apply rotation for outputs 2 and 6. */ - temp1=xC6S2*irot_input_x; - temp1=DOROUND(temp1); - temp1>>=16; - temp2=xC2S6*irot_input_y; - temp2=DOROUND(temp2); - temp2>>=16; - ip[2] = temp1 + temp2; - - temp1=xC6S2*irot_input_y; - temp1=DOROUND(temp1); - temp1>>=16; - temp2=xC2S6*irot_input_x ; - temp2=DOROUND(temp2); - temp2>>=16; - ip[6] = temp1 -temp2 ; - - /* Define inputs to rotation for outputs 1 and 7 */ - irot_input_x = icommon_product1 + id07; - irot_input_y = -( id34 + icommon_product2 ); - - /* Apply rotation for outputs 1 and 7. */ - - temp1=xC1S7*irot_input_x; - temp1=DOROUND(temp1); - temp1>>=16; - temp2=xC7S1*irot_input_y; - temp2=DOROUND(temp2); - temp2>>=16; - ip[1] = temp1 - temp2; - - temp1=xC7S1*irot_input_x; - temp1=DOROUND(temp1); - temp1>>=16; - temp2=xC1S7*irot_input_y ; - temp2=DOROUND(temp2); - temp2>>=16; - ip[7] = temp1 + temp2 ; - - /* Define inputs to rotation for outputs 3 and 5 */ - irot_input_x = id07 - icommon_product1; - irot_input_y = id34 - icommon_product2; - - /* Apply rotation for outputs 3 and 5. */ - temp1=xC3S5*irot_input_x; - temp1=DOROUND(temp1); - temp1>>=16; - temp2=xC5S3*irot_input_y ; - temp2=DOROUND(temp2); - temp2>>=16; - ip[3] = temp1 - temp2 ; - - temp1=xC5S3*irot_input_x; - temp1=DOROUND(temp1); - temp1>>=16; - temp2=xC3S5*irot_input_y; - temp2=DOROUND(temp2); - temp2>>=16; - ip[5] = temp1 + temp2; - - /* Increment data pointer for next row. */ - InputData += 8 ; - ip += 8; /* advance pointer to next row */ - - } - - - /* Performed DCT on rows, now transform the columns */ - ip = InterData; - for (loop = 0; loop < 8; loop++){ - /* Pre calculate some common sums and differences. */ - is07 = ip[0 * 8] + ip[7 * 8]; - is12 = ip[1 * 8] + ip[2 * 8]; - is34 = ip[3 * 8] + ip[4 * 8]; - is56 = ip[5 * 8] + ip[6 * 8]; - - id07 = ip[0 * 8] - ip[7 * 8]; - id12 = ip[1 * 8] - ip[2 * 8]; - id34 = ip[3 * 8] - ip[4 * 8]; - id56 = ip[5 * 8] - ip[6 * 8]; - - is0734 = is07 + is34; - is1256 = is12 + is56; - - /* Pre-Calculate some common product terms. */ - icommon_product1 = xC4S4*(is12 - is56) ; - icommon_product2 = xC4S4*(id12 + id56) ; - icommon_product1 = DOROUND(icommon_product1); - icommon_product2 = DOROUND(icommon_product2); - icommon_product1>>=16; - icommon_product2>>=16; - - - temp1 = xC4S4*(is0734 + is1256) ; - temp2 = xC4S4*(is0734 - is1256) ; - temp1 = DOROUND(temp1); - temp2 = DOROUND(temp2); - temp1>>=16; - temp2>>=16; - op[0*8] = (ogg_int16_t) temp1; - op[4*8] = (ogg_int16_t) temp2; - - /* Define inputs to rotation for outputs 2 and 6 */ - irot_input_x = id12 - id56; - irot_input_y = is07 - is34; - - /* Apply rotation for outputs 2 and 6. */ - temp1=xC6S2*irot_input_x; - temp1=DOROUND(temp1); - temp1>>=16; - temp2=xC2S6*irot_input_y; - temp2=DOROUND(temp2); - temp2>>=16; - op[2*8] = (ogg_int16_t) (temp1 + temp2); - - temp1=xC6S2*irot_input_y; - temp1=DOROUND(temp1); - temp1>>=16; - temp2=xC2S6*irot_input_x ; - temp2=DOROUND(temp2); - temp2>>=16; - op[6*8] = (ogg_int16_t) (temp1 -temp2) ; - - /* Define inputs to rotation for outputs 1 and 7 */ - irot_input_x = icommon_product1 + id07; - irot_input_y = -( id34 + icommon_product2 ); - - /* Apply rotation for outputs 1 and 7. */ - temp1=xC1S7*irot_input_x; - temp1=DOROUND(temp1); - temp1>>=16; - temp2=xC7S1*irot_input_y; - temp2=DOROUND(temp2); - temp2>>=16; - op[1*8] = (ogg_int16_t) (temp1 - temp2); - - temp1=xC7S1*irot_input_x; - temp1=DOROUND(temp1); - temp1>>=16; - temp2=xC1S7*irot_input_y ; - temp2=DOROUND(temp2); - temp2>>=16; - op[7*8] = (ogg_int16_t) (temp1 + temp2); - - /* Define inputs to rotation for outputs 3 and 5 */ - irot_input_x = id07 - icommon_product1; - irot_input_y = id34 - icommon_product2; - - /* Apply rotation for outputs 3 and 5. */ - temp1=xC3S5*irot_input_x; - temp1=DOROUND(temp1); - temp1>>=16; - temp2=xC5S3*irot_input_y ; - temp2=DOROUND(temp2); - temp2>>=16; - op[3*8] = (ogg_int16_t) (temp1 - temp2) ; - - temp1=xC5S3*irot_input_x; - temp1=DOROUND(temp1); - temp1>>=16; - temp2=xC3S5*irot_input_y; - temp2=DOROUND(temp2); - temp2>>=16; - op[5*8] = (ogg_int16_t) (temp1 + temp2); - - /* Increment data pointer for next column. */ - ip ++; - op ++; - } -} - -void dsp_dct_init (DspFunctions *funcs, ogg_uint32_t cpu_flags) -{ - funcs->fdct_short = fdct_short__c; - dsp_dct_decode_init(funcs, cpu_flags); - dsp_idct_init(funcs, cpu_flags); -#if defined(USE_ASM) - if (cpu_flags & OC_CPU_X86_MMX) { - dsp_mmx_fdct_init(funcs); - } -#endif -} - diff --git a/Engine/lib/libtheora/lib/enc/dct_decode.c b/Engine/lib/libtheora/lib/enc/dct_decode.c deleted file mode 100644 index e27611610..000000000 --- a/Engine/lib/libtheora/lib/enc/dct_decode.c +++ /dev/null @@ -1,941 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: dct_decode.c 15400 2008-10-15 12:10:58Z tterribe $ - - ********************************************************************/ - -#include -#include -#include "codec_internal.h" -#include "quant_lookup.h" - - -#define GOLDEN_FRAME_THRESH_Q 50 -#define PUR 8 -#define PU 4 -#define PUL 2 -#define PL 1 -#define HIGHBITDUPPED(X) (((signed short) X) >> 15) - - -static const int ModeUsesMC[MAX_MODES] = { 0, 0, 1, 1, 1, 0, 1, 1 }; - -static void SetupBoundingValueArray_Generic(ogg_int16_t *BoundingValuePtr, - ogg_int32_t FLimit){ - - ogg_int32_t i; - - /* Set up the bounding value array. */ - memset ( BoundingValuePtr, 0, (256*sizeof(*BoundingValuePtr)) ); - for ( i = 0; i < FLimit; i++ ){ - BoundingValuePtr[127-i-FLimit] = (-FLimit+i); - BoundingValuePtr[127-i] = -i; - BoundingValuePtr[127+i] = i; - BoundingValuePtr[127+i+FLimit] = FLimit-i; - } -} - -static void ExpandKFBlock ( PB_INSTANCE *pbi, ogg_int32_t FragmentNumber ){ - ogg_uint32_t ReconPixelsPerLine; - ogg_int32_t ReconPixelIndex; - - /* Select the appropriate inverse Q matrix and line stride */ - if ( FragmentNumber<(ogg_int32_t)pbi->YPlaneFragments ){ - ReconPixelsPerLine = pbi->YStride; - pbi->dequant_coeffs = pbi->dequant_Y_coeffs; - }else if ( FragmentNumber<(ogg_int32_t)(pbi->YPlaneFragments + pbi->UVPlaneFragments) ){ - ReconPixelsPerLine = pbi->UVStride; - pbi->dequant_coeffs = pbi->dequant_U_coeffs; - }else{ - ReconPixelsPerLine = pbi->UVStride; - pbi->dequant_coeffs = pbi->dequant_V_coeffs; - } - - /* Set up pointer into the quantisation buffer. */ - pbi->quantized_list = &pbi->QFragData[FragmentNumber][0]; - - /* Invert quantisation and DCT to get pixel data. */ - switch(pbi->FragCoefEOB[FragmentNumber]){ - case 0:case 1: - IDct1( pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); - break; - case 2: case 3: - dsp_IDct3(pbi->dsp, pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); - break; - case 4:case 5:case 6:case 7:case 8: case 9:case 10: - dsp_IDct10(pbi->dsp, pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); - break; - default: - dsp_IDctSlow(pbi->dsp, pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); - } - - /* Convert fragment number to a pixel offset in a reconstruction buffer. */ - ReconPixelIndex = pbi->recon_pixel_index_table[FragmentNumber]; - - /* Get the pixel index for the first pixel in the fragment. */ - dsp_recon_intra8x8 (pbi->dsp, (unsigned char *)(&pbi->ThisFrameRecon[ReconPixelIndex]), - (ogg_int16_t *)pbi->ReconDataBuffer, ReconPixelsPerLine); -} - -static void ExpandBlock ( PB_INSTANCE *pbi, ogg_int32_t FragmentNumber){ - unsigned char *LastFrameRecPtr; /* Pointer into previous frame - reconstruction. */ - unsigned char *LastFrameRecPtr2; /* Pointer into previous frame - reconstruction for 1/2 pixel MC. */ - - ogg_uint32_t ReconPixelsPerLine; /* Pixels per line */ - ogg_int32_t ReconPixelIndex; /* Offset for block into a - reconstruction buffer */ - ogg_int32_t ReconPtr2Offset; /* Offset for second - reconstruction in half pixel - MC */ - ogg_int32_t MVOffset; /* Baseline motion vector offset */ - ogg_int32_t MvShift ; /* Shift to correct to 1/2 or 1/4 pixel */ - ogg_int32_t MvModMask; /* Mask to determine whether 1/2 - pixel is used */ - - /* Get coding mode for this block */ - if ( pbi->FrameType == KEY_FRAME ){ - pbi->CodingMode = CODE_INTRA; - }else{ - /* Get Motion vector and mode for this block. */ - pbi->CodingMode = pbi->FragCodingMethod[FragmentNumber]; - } - - /* Select the appropriate inverse Q matrix and line stride */ - if ( FragmentNumber<(ogg_int32_t)pbi->YPlaneFragments ) { - ReconPixelsPerLine = pbi->YStride; - MvShift = 1; - MvModMask = 0x00000001; - - /* Select appropriate dequantiser matrix. */ - if ( pbi->CodingMode == CODE_INTRA ) - pbi->dequant_coeffs = pbi->dequant_Y_coeffs; - else - pbi->dequant_coeffs = pbi->dequant_InterY_coeffs; - }else{ - ReconPixelsPerLine = pbi->UVStride; - MvShift = 2; - MvModMask = 0x00000003; - - /* Select appropriate dequantiser matrix. */ - if ( pbi->CodingMode == CODE_INTRA ) - if ( FragmentNumber < - (ogg_int32_t)(pbi->YPlaneFragments + pbi->UVPlaneFragments) ) - pbi->dequant_coeffs = pbi->dequant_U_coeffs; - else - pbi->dequant_coeffs = pbi->dequant_V_coeffs; - else - if ( FragmentNumber < - (ogg_int32_t)(pbi->YPlaneFragments + pbi->UVPlaneFragments) ) - pbi->dequant_coeffs = pbi->dequant_InterU_coeffs; - else - pbi->dequant_coeffs = pbi->dequant_InterV_coeffs; - } - - /* Set up pointer into the quantisation buffer. */ - pbi->quantized_list = &pbi->QFragData[FragmentNumber][0]; - - /* Invert quantisation and DCT to get pixel data. */ - switch(pbi->FragCoefEOB[FragmentNumber]){ - case 0:case 1: - IDct1( pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); - break; - case 2: case 3: - dsp_IDct3(pbi->dsp, pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); - break; - case 4:case 5:case 6:case 7:case 8: case 9:case 10: - dsp_IDct10(pbi->dsp, pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); - break; - default: - dsp_IDctSlow(pbi->dsp, pbi->quantized_list, pbi->dequant_coeffs, pbi->ReconDataBuffer ); - } - - /* Convert fragment number to a pixel offset in a reconstruction buffer. */ - ReconPixelIndex = pbi->recon_pixel_index_table[FragmentNumber]; - - /* Action depends on decode mode. */ - if ( pbi->CodingMode == CODE_INTER_NO_MV ){ - /* Inter with no motion vector */ - /* Reconstruct the pixel data using the last frame reconstruction - and change data when the motion vector is (0,0), the recon is - based on the lastframe without loop filtering---- for testing */ - dsp_recon_inter8x8 (pbi->dsp, &pbi->ThisFrameRecon[ReconPixelIndex], - &pbi->LastFrameRecon[ReconPixelIndex], - pbi->ReconDataBuffer, ReconPixelsPerLine); - }else if ( ModeUsesMC[pbi->CodingMode] ) { - /* The mode uses a motion vector. */ - /* Get vector from list */ - pbi->MVector.x = pbi->FragMVect[FragmentNumber].x; - pbi->MVector.y = pbi->FragMVect[FragmentNumber].y; - - /* Work out the base motion vector offset and the 1/2 pixel offset - if any. For the U and V planes the MV specifies 1/4 pixel - accuracy. This is adjusted to 1/2 pixel as follows ( 0->0, - 1/4->1/2, 1/2->1/2, 3/4->1/2 ). */ - MVOffset = 0; - ReconPtr2Offset = 0; - if ( pbi->MVector.x > 0 ){ - MVOffset = pbi->MVector.x >> MvShift; - if ( pbi->MVector.x & MvModMask ) - ReconPtr2Offset += 1; - } else if ( pbi->MVector.x < 0 ) { - MVOffset -= (-pbi->MVector.x) >> MvShift; - if ( (-pbi->MVector.x) & MvModMask ) - ReconPtr2Offset -= 1; - } - - if ( pbi->MVector.y > 0 ){ - MVOffset += (pbi->MVector.y >> MvShift) * ReconPixelsPerLine; - if ( pbi->MVector.y & MvModMask ) - ReconPtr2Offset += ReconPixelsPerLine; - } else if ( pbi->MVector.y < 0 ){ - MVOffset -= ((-pbi->MVector.y) >> MvShift) * ReconPixelsPerLine; - if ( (-pbi->MVector.y) & MvModMask ) - ReconPtr2Offset -= ReconPixelsPerLine; - } - - /* Set up the first of the two reconstruction buffer pointers. */ - if ( pbi->CodingMode==CODE_GOLDEN_MV ) { - LastFrameRecPtr = &pbi->GoldenFrame[ReconPixelIndex] + MVOffset; - }else{ - LastFrameRecPtr = &pbi->LastFrameRecon[ReconPixelIndex] + MVOffset; - } - - /* Set up the second of the two reconstruction pointers. */ - LastFrameRecPtr2 = LastFrameRecPtr + ReconPtr2Offset; - - /* Select the appropriate reconstruction function */ - if ( (int)(LastFrameRecPtr - LastFrameRecPtr2) == 0 ) { - /* Reconstruct the pixel dats from the reference frame and change data - (no half pixel in this case as the two references were the same. */ - dsp_recon_inter8x8 (pbi->dsp, - &pbi->ThisFrameRecon[ReconPixelIndex], - LastFrameRecPtr, pbi->ReconDataBuffer, - ReconPixelsPerLine); - }else{ - /* Fractional pixel reconstruction. */ - /* Note that we only use two pixels per reconstruction even for - the diagonal. */ - dsp_recon_inter8x8_half(pbi->dsp, &pbi->ThisFrameRecon[ReconPixelIndex], - LastFrameRecPtr, LastFrameRecPtr2, - pbi->ReconDataBuffer, ReconPixelsPerLine); - } - } else if ( pbi->CodingMode == CODE_USING_GOLDEN ){ - /* Golden frame with motion vector */ - /* Reconstruct the pixel data using the golden frame - reconstruction and change data */ - dsp_recon_inter8x8 (pbi->dsp, &pbi->ThisFrameRecon[ReconPixelIndex], - &pbi->GoldenFrame[ ReconPixelIndex ], - pbi->ReconDataBuffer, ReconPixelsPerLine); - } else { - /* Simple Intra coding */ - /* Get the pixel index for the first pixel in the fragment. */ - dsp_recon_intra8x8 (pbi->dsp, &pbi->ThisFrameRecon[ReconPixelIndex], - pbi->ReconDataBuffer, ReconPixelsPerLine); - } -} - -static void UpdateUMV_HBorders( PB_INSTANCE *pbi, - unsigned char * DestReconPtr, - ogg_uint32_t PlaneFragOffset ) { - ogg_uint32_t i; - ogg_uint32_t PixelIndex; - - ogg_uint32_t PlaneStride; - ogg_uint32_t BlockVStep; - ogg_uint32_t PlaneFragments; - ogg_uint32_t LineFragments; - ogg_uint32_t PlaneBorderWidth; - - unsigned char *SrcPtr1; - unsigned char *SrcPtr2; - unsigned char *DestPtr1; - unsigned char *DestPtr2; - - /* Work out various plane specific values */ - if ( PlaneFragOffset == 0 ) { - /* Y Plane */ - BlockVStep = (pbi->YStride * - (VFRAGPIXELS - 1)); - PlaneStride = pbi->YStride; - PlaneBorderWidth = UMV_BORDER; - PlaneFragments = pbi->YPlaneFragments; - LineFragments = pbi->HFragments; - }else{ - /* U or V plane. */ - BlockVStep = (pbi->UVStride * - (VFRAGPIXELS - 1)); - PlaneStride = pbi->UVStride; - PlaneBorderWidth = UMV_BORDER / 2; - PlaneFragments = pbi->UVPlaneFragments; - LineFragments = pbi->HFragments / 2; - } - - /* Setup the source and destination pointers for the top and bottom - borders */ - PixelIndex = pbi->recon_pixel_index_table[PlaneFragOffset]; - SrcPtr1 = &DestReconPtr[ PixelIndex - PlaneBorderWidth ]; - DestPtr1 = SrcPtr1 - (PlaneBorderWidth * PlaneStride); - - PixelIndex = pbi->recon_pixel_index_table[PlaneFragOffset + - PlaneFragments - LineFragments] + - BlockVStep; - SrcPtr2 = &DestReconPtr[ PixelIndex - PlaneBorderWidth]; - DestPtr2 = SrcPtr2 + PlaneStride; - - /* Now copy the top and bottom source lines into each line of the - respective borders */ - for ( i = 0; i < PlaneBorderWidth; i++ ) { - memcpy( DestPtr1, SrcPtr1, PlaneStride ); - memcpy( DestPtr2, SrcPtr2, PlaneStride ); - DestPtr1 += PlaneStride; - DestPtr2 += PlaneStride; - } -} - -static void UpdateUMV_VBorders( PB_INSTANCE *pbi, - unsigned char * DestReconPtr, - ogg_uint32_t PlaneFragOffset ){ - ogg_uint32_t i; - ogg_uint32_t PixelIndex; - - ogg_uint32_t PlaneStride; - ogg_uint32_t LineFragments; - ogg_uint32_t PlaneBorderWidth; - ogg_uint32_t PlaneHeight; - - unsigned char *SrcPtr1; - unsigned char *SrcPtr2; - unsigned char *DestPtr1; - unsigned char *DestPtr2; - - /* Work out various plane specific values */ - if ( PlaneFragOffset == 0 ) { - /* Y Plane */ - PlaneStride = pbi->YStride; - PlaneBorderWidth = UMV_BORDER; - LineFragments = pbi->HFragments; - PlaneHeight = pbi->info.height; - }else{ - /* U or V plane. */ - PlaneStride = pbi->UVStride; - PlaneBorderWidth = UMV_BORDER / 2; - LineFragments = pbi->HFragments / 2; - PlaneHeight = pbi->info.height / 2; - } - - /* Setup the source data values and destination pointers for the - left and right edge borders */ - PixelIndex = pbi->recon_pixel_index_table[PlaneFragOffset]; - SrcPtr1 = &DestReconPtr[ PixelIndex ]; - DestPtr1 = &DestReconPtr[ PixelIndex - PlaneBorderWidth ]; - - PixelIndex = pbi->recon_pixel_index_table[PlaneFragOffset + - LineFragments - 1] + - (HFRAGPIXELS - 1); - SrcPtr2 = &DestReconPtr[ PixelIndex ]; - DestPtr2 = &DestReconPtr[ PixelIndex + 1 ]; - - /* Now copy the top and bottom source lines into each line of the - respective borders */ - for ( i = 0; i < PlaneHeight; i++ ) { - memset( DestPtr1, SrcPtr1[0], PlaneBorderWidth ); - memset( DestPtr2, SrcPtr2[0], PlaneBorderWidth ); - SrcPtr1 += PlaneStride; - SrcPtr2 += PlaneStride; - DestPtr1 += PlaneStride; - DestPtr2 += PlaneStride; - } -} - -void UpdateUMVBorder( PB_INSTANCE *pbi, - unsigned char * DestReconPtr ) { - ogg_uint32_t PlaneFragOffset; - - /* Y plane */ - PlaneFragOffset = 0; - UpdateUMV_VBorders( pbi, DestReconPtr, PlaneFragOffset ); - UpdateUMV_HBorders( pbi, DestReconPtr, PlaneFragOffset ); - - /* Then the U and V Planes */ - PlaneFragOffset = pbi->YPlaneFragments; - UpdateUMV_VBorders( pbi, DestReconPtr, PlaneFragOffset ); - UpdateUMV_HBorders( pbi, DestReconPtr, PlaneFragOffset ); - - PlaneFragOffset = pbi->YPlaneFragments + pbi->UVPlaneFragments; - UpdateUMV_VBorders( pbi, DestReconPtr, PlaneFragOffset ); - UpdateUMV_HBorders( pbi, DestReconPtr, PlaneFragOffset ); -} - -static void CopyRecon( PB_INSTANCE *pbi, unsigned char * DestReconPtr, - unsigned char * SrcReconPtr ) { - ogg_uint32_t i; - ogg_uint32_t PlaneLineStep; /* Pixels per line */ - ogg_uint32_t PixelIndex; - - unsigned char *SrcPtr; /* Pointer to line of source image data */ - unsigned char *DestPtr; /* Pointer to line of destination image data */ - - /* Copy over only updated blocks.*/ - - /* First Y plane */ - PlaneLineStep = pbi->YStride; - for ( i = 0; i < pbi->YPlaneFragments; i++ ) { - if ( pbi->display_fragments[i] ) { - PixelIndex = pbi->recon_pixel_index_table[i]; - SrcPtr = &SrcReconPtr[ PixelIndex ]; - DestPtr = &DestReconPtr[ PixelIndex ]; - - dsp_copy8x8 (pbi->dsp, SrcPtr, DestPtr, PlaneLineStep); - } - } - - /* Then U and V */ - PlaneLineStep = pbi->UVStride; - for ( i = pbi->YPlaneFragments; i < pbi->UnitFragments; i++ ) { - if ( pbi->display_fragments[i] ) { - PixelIndex = pbi->recon_pixel_index_table[i]; - SrcPtr = &SrcReconPtr[ PixelIndex ]; - DestPtr = &DestReconPtr[ PixelIndex ]; - - dsp_copy8x8 (pbi->dsp, SrcPtr, DestPtr, PlaneLineStep); - - } - } -} - -static void CopyNotRecon( PB_INSTANCE *pbi, unsigned char * DestReconPtr, - unsigned char * SrcReconPtr ) { - ogg_uint32_t i; - ogg_uint32_t PlaneLineStep; /* Pixels per line */ - ogg_uint32_t PixelIndex; - - unsigned char *SrcPtr; /* Pointer to line of source image data */ - unsigned char *DestPtr; /* Pointer to line of destination image data*/ - - /* Copy over only updated blocks. */ - - /* First Y plane */ - PlaneLineStep = pbi->YStride; - for ( i = 0; i < pbi->YPlaneFragments; i++ ) { - if ( !pbi->display_fragments[i] ) { - PixelIndex = pbi->recon_pixel_index_table[i]; - SrcPtr = &SrcReconPtr[ PixelIndex ]; - DestPtr = &DestReconPtr[ PixelIndex ]; - - dsp_copy8x8 (pbi->dsp, SrcPtr, DestPtr, PlaneLineStep); - } - } - - /* Then U and V */ - PlaneLineStep = pbi->UVStride; - for ( i = pbi->YPlaneFragments; i < pbi->UnitFragments; i++ ) { - if ( !pbi->display_fragments[i] ) { - PixelIndex = pbi->recon_pixel_index_table[i]; - SrcPtr = &SrcReconPtr[ PixelIndex ]; - DestPtr = &DestReconPtr[ PixelIndex ]; - - dsp_copy8x8 (pbi->dsp, SrcPtr, DestPtr, PlaneLineStep); - - } - } -} - -void ExpandToken( Q_LIST_ENTRY * ExpandedBlock, - unsigned char * CoeffIndex, ogg_uint32_t Token, - ogg_int32_t ExtraBits ){ - /* Is the token is a combination run and value token. */ - if ( Token >= DCT_RUN_CATEGORY1 ){ - /* Expand the token and additional bits to a zero run length and - data value. */ - if ( Token < DCT_RUN_CATEGORY2 ) { - /* Decoding method depends on token */ - if ( Token < DCT_RUN_CATEGORY1B ) { - /* Step on by the zero run length */ - *CoeffIndex += (unsigned char)((Token - DCT_RUN_CATEGORY1) + 1); - - /* The extra bit determines the sign. */ - if ( ExtraBits & 0x01 ) - ExpandedBlock[*CoeffIndex] = -1; - else - ExpandedBlock[*CoeffIndex] = 1; - } else if ( Token == DCT_RUN_CATEGORY1B ) { - /* Bits 0-1 determines the zero run length */ - *CoeffIndex += (6 + (ExtraBits & 0x03)); - - /* Bit 2 determines the sign */ - if ( ExtraBits & 0x04 ) - ExpandedBlock[*CoeffIndex] = -1; - else - ExpandedBlock[*CoeffIndex] = 1; - }else{ - /* Bits 0-2 determines the zero run length */ - *CoeffIndex += (10 + (ExtraBits & 0x07)); - - /* Bit 3 determines the sign */ - if ( ExtraBits & 0x08 ) - ExpandedBlock[*CoeffIndex] = -1; - else - ExpandedBlock[*CoeffIndex] = 1; - } - }else{ - /* If token == DCT_RUN_CATEGORY2 we have a single 0 followed by - a value */ - if ( Token == DCT_RUN_CATEGORY2 ){ - /* Step on by the zero run length */ - *CoeffIndex += 1; - - /* Bit 1 determines sign, bit 0 the value */ - if ( ExtraBits & 0x02 ) - ExpandedBlock[*CoeffIndex] = -(2 + (ExtraBits & 0x01)); - else - ExpandedBlock[*CoeffIndex] = 2 + (ExtraBits & 0x01); - }else{ - /* else we have 2->3 zeros followed by a value */ - /* Bit 0 determines the zero run length */ - *CoeffIndex += 2 + (ExtraBits & 0x01); - - /* Bit 2 determines the sign, bit 1 the value */ - if ( ExtraBits & 0x04 ) - ExpandedBlock[*CoeffIndex] = -(2 + ((ExtraBits & 0x02) >> 1)); - else - ExpandedBlock[*CoeffIndex] = 2 + ((ExtraBits & 0x02) >> 1); - } - } - - /* Step on over value */ - *CoeffIndex += 1; - - } else if ( Token == DCT_SHORT_ZRL_TOKEN ) { - /* Token is a ZRL token so step on by the appropriate number of zeros */ - *CoeffIndex += ExtraBits + 1; - } else if ( Token == DCT_ZRL_TOKEN ) { - /* Token is a ZRL token so step on by the appropriate number of zeros */ - *CoeffIndex += ExtraBits + 1; - } else if ( Token < LOW_VAL_TOKENS ) { - /* Token is a small single value token. */ - switch ( Token ) { - case ONE_TOKEN: - ExpandedBlock[*CoeffIndex] = 1; - break; - case MINUS_ONE_TOKEN: - ExpandedBlock[*CoeffIndex] = -1; - break; - case TWO_TOKEN: - ExpandedBlock[*CoeffIndex] = 2; - break; - case MINUS_TWO_TOKEN: - ExpandedBlock[*CoeffIndex] = -2; - break; - } - - /* Step on the coefficient index. */ - *CoeffIndex += 1; - }else{ - /* Token is a larger single value token */ - /* Expand the token and additional bits to a data value. */ - if ( Token < DCT_VAL_CATEGORY3 ) { - /* Offset from LOW_VAL_TOKENS determines value */ - Token = Token - LOW_VAL_TOKENS; - - /* Extra bit determines sign */ - if ( ExtraBits ) - ExpandedBlock[*CoeffIndex] = - -((Q_LIST_ENTRY)(Token + DCT_VAL_CAT2_MIN)); - else - ExpandedBlock[*CoeffIndex] = - (Q_LIST_ENTRY)(Token + DCT_VAL_CAT2_MIN); - } else if ( Token == DCT_VAL_CATEGORY3 ) { - /* Bit 1 determines sign, Bit 0 the value */ - if ( ExtraBits & 0x02 ) - ExpandedBlock[*CoeffIndex] = -(DCT_VAL_CAT3_MIN + (ExtraBits & 0x01)); - else - ExpandedBlock[*CoeffIndex] = DCT_VAL_CAT3_MIN + (ExtraBits & 0x01); - } else if ( Token == DCT_VAL_CATEGORY4 ) { - /* Bit 2 determines sign, Bit 0-1 the value */ - if ( ExtraBits & 0x04 ) - ExpandedBlock[*CoeffIndex] = -(DCT_VAL_CAT4_MIN + (ExtraBits & 0x03)); - else - ExpandedBlock[*CoeffIndex] = DCT_VAL_CAT4_MIN + (ExtraBits & 0x03); - } else if ( Token == DCT_VAL_CATEGORY5 ) { - /* Bit 3 determines sign, Bit 0-2 the value */ - if ( ExtraBits & 0x08 ) - ExpandedBlock[*CoeffIndex] = -(DCT_VAL_CAT5_MIN + (ExtraBits & 0x07)); - else - ExpandedBlock[*CoeffIndex] = DCT_VAL_CAT5_MIN + (ExtraBits & 0x07); - } else if ( Token == DCT_VAL_CATEGORY6 ) { - /* Bit 4 determines sign, Bit 0-3 the value */ - if ( ExtraBits & 0x10 ) - ExpandedBlock[*CoeffIndex] = -(DCT_VAL_CAT6_MIN + (ExtraBits & 0x0F)); - else - ExpandedBlock[*CoeffIndex] = DCT_VAL_CAT6_MIN + (ExtraBits & 0x0F); - } else if ( Token == DCT_VAL_CATEGORY7 ) { - /* Bit 5 determines sign, Bit 0-4 the value */ - if ( ExtraBits & 0x20 ) - ExpandedBlock[*CoeffIndex] = -(DCT_VAL_CAT7_MIN + (ExtraBits & 0x1F)); - else - ExpandedBlock[*CoeffIndex] = DCT_VAL_CAT7_MIN + (ExtraBits & 0x1F); - } else if ( Token == DCT_VAL_CATEGORY8 ) { - /* Bit 9 determines sign, Bit 0-8 the value */ - if ( ExtraBits & 0x200 ) - ExpandedBlock[*CoeffIndex] = -(DCT_VAL_CAT8_MIN + (ExtraBits & 0x1FF)); - else - ExpandedBlock[*CoeffIndex] = DCT_VAL_CAT8_MIN + (ExtraBits & 0x1FF); - } - - /* Step on the coefficient index. */ - *CoeffIndex += 1; - } -} - -void ClearDownQFragData(PB_INSTANCE *pbi){ - ogg_int32_t i; - Q_LIST_ENTRY * QFragPtr; - - for ( i = 0; i < pbi->CodedBlockIndex; i++ ) { - /* Get the linear index for the current fragment. */ - QFragPtr = pbi->QFragData[pbi->CodedBlockList[i]]; - memset(QFragPtr, 0, 64*sizeof(Q_LIST_ENTRY)); - } -} - -static void loop_filter_h(unsigned char * PixelPtr, - ogg_int32_t LineLength, - ogg_int16_t *BoundingValuePtr){ - ogg_int32_t j; - ogg_int32_t FiltVal; - PixelPtr-=2; - - for ( j = 0; j < 8; j++ ){ - FiltVal = - ( PixelPtr[0] ) - - ( PixelPtr[1] * 3 ) + - ( PixelPtr[2] * 3 ) - - ( PixelPtr[3] ); - - FiltVal = *(BoundingValuePtr+((FiltVal + 4) >> 3)); - - PixelPtr[1] = clamp255(PixelPtr[1] + FiltVal); - PixelPtr[2] = clamp255(PixelPtr[2] - FiltVal); - - PixelPtr += LineLength; - } -} - -static void loop_filter_v(unsigned char * PixelPtr, - ogg_int32_t LineLength, - ogg_int16_t *BoundingValuePtr){ - ogg_int32_t j; - ogg_int32_t FiltVal; - PixelPtr -= 2*LineLength; - - for ( j = 0; j < 8; j++ ) { - FiltVal = ( (ogg_int32_t)PixelPtr[0] ) - - ( (ogg_int32_t)PixelPtr[LineLength] * 3 ) + - ( (ogg_int32_t)PixelPtr[2 * LineLength] * 3 ) - - ( (ogg_int32_t)PixelPtr[3 * LineLength] ); - - FiltVal = *(BoundingValuePtr+((FiltVal + 4) >> 3)); - - PixelPtr[LineLength] = clamp255(PixelPtr[LineLength] + FiltVal); - PixelPtr[2 * LineLength] = clamp255(PixelPtr[2*LineLength] - FiltVal); - - PixelPtr ++; - } -} - -static void LoopFilter__c(PB_INSTANCE *pbi, int FLimit){ - - int j; - ogg_int16_t BoundingValues[256]; - ogg_int16_t *bvp = BoundingValues+127; - unsigned char *cp = pbi->display_fragments; - ogg_uint32_t *bp = pbi->recon_pixel_index_table; - - if ( FLimit == 0 ) return; - SetupBoundingValueArray_Generic(BoundingValues, FLimit); - - for ( j = 0; j < 3 ; j++){ - ogg_uint32_t *bp_begin = bp; - ogg_uint32_t *bp_end; - int stride; - int h; - - switch(j) { - case 0: /* y */ - bp_end = bp + pbi->YPlaneFragments; - h = pbi->HFragments; - stride = pbi->YStride; - break; - default: /* u,v, 4:20 specific */ - bp_end = bp + pbi->UVPlaneFragments; - h = pbi->HFragments >> 1; - stride = pbi->UVStride; - break; - } - - while(bpbp_left) - loop_filter_h(&pbi->LastFrameRecon[bp[0]],stride,bvp); - if(bp_left>bp_begin) - loop_filter_v(&pbi->LastFrameRecon[bp[0]],stride,bvp); - if(bp+1LastFrameRecon[bp[0]]+8,stride,bvp); - if(bp+hLastFrameRecon[bp[h]],stride,bvp); - } - bp++; - cp++; - } - } - } -} - -void ReconRefFrames (PB_INSTANCE *pbi){ - ogg_int32_t i; - unsigned char *SwapReconBuffersTemp; - - /* predictor multiplier up-left, up, up-right,left, shift - Entries are packed in the order L, UL, U, UR, with missing entries - moved to the end (before the shift parameters). */ - static const ogg_int16_t pc[16][6]={ - {0,0,0,0,0,0}, - {1,0,0,0,0,0}, /* PL */ - {1,0,0,0,0,0}, /* PUL */ - {1,0,0,0,0,0}, /* PUL|PL */ - {1,0,0,0,0,0}, /* PU */ - {1,1,0,0,1,1}, /* PU|PL */ - {0,1,0,0,0,0}, /* PU|PUL */ - {29,-26,29,0,5,31}, /* PU|PUL|PL */ - {1,0,0,0,0,0}, /* PUR */ - {75,53,0,0,7,127}, /* PUR|PL */ - {1,1,0,0,1,1}, /* PUR|PUL */ - {75,0,53,0,7,127}, /* PUR|PUL|PL */ - {1,0,0,0,0,0}, /* PUR|PU */ - {75,0,53,0,7,127}, /* PUR|PU|PL */ - {3,10,3,0,4,15}, /* PUR|PU|PUL */ - {29,-26,29,0,5,31} /* PUR|PU|PUL|PL */ - }; - - /* boundary case bit masks. */ - static const int bc_mask[8]={ - /* normal case no boundary condition */ - PUR|PU|PUL|PL, - /* left column */ - PUR|PU, - /* top row */ - PL, - /* top row, left column */ - 0, - /* right column */ - PU|PUL|PL, - /* right and left column */ - PU, - /* top row, right column */ - PL, - /* top row, right and left column */ - 0 - }; - - /* value left value up-left, value up, value up-right, missing - values skipped. */ - int v[4]; - - /* fragment number left, up-left, up, up-right */ - int fn[4]; - - /* predictor count. */ - int pcount; - - short wpc; - static const short Mode2Frame[] = { - 1, /* CODE_INTER_NO_MV 0 => Encoded diff from same MB last frame */ - 0, /* CODE_INTRA 1 => DCT Encoded Block */ - 1, /* CODE_INTER_PLUS_MV 2 => Encoded diff from included MV MB last frame */ - 1, /* CODE_INTER_LAST_MV 3 => Encoded diff from MRU MV MB last frame */ - 1, /* CODE_INTER_PRIOR_MV 4 => Encoded diff from included 4 separate MV blocks */ - 2, /* CODE_USING_GOLDEN 5 => Encoded diff from same MB golden frame */ - 2, /* CODE_GOLDEN_MV 6 => Encoded diff from included MV MB golden frame */ - 1 /* CODE_INTER_FOUR_MV 7 => Encoded diff from included 4 separate MV blocks */ - }; - short Last[3]; - short PredictedDC; - int FragsAcross=pbi->HFragments; - int FromFragment,ToFragment; - int FragsDown = pbi->VFragments; - - int WhichFrame; - int WhichCase; - int j,k,m,n; - - void (*ExpandBlockA) ( PB_INSTANCE *pbi, ogg_int32_t FragmentNumber ); - - if ( pbi->FrameType == KEY_FRAME ) - ExpandBlockA=ExpandKFBlock; - else - ExpandBlockA=ExpandBlock; - - /* for y,u,v */ - for ( j = 0; j < 3 ; j++) { - /* pick which fragments based on Y, U, V */ - switch(j){ - case 0: /* y */ - FromFragment = 0; - ToFragment = pbi->YPlaneFragments; - FragsAcross = pbi->HFragments; - FragsDown = pbi->VFragments; - break; - case 1: /* u */ - FromFragment = pbi->YPlaneFragments; - ToFragment = pbi->YPlaneFragments + pbi->UVPlaneFragments ; - FragsAcross = pbi->HFragments >> 1; - FragsDown = pbi->VFragments >> 1; - break; - /*case 2: v */ - default: - FromFragment = pbi->YPlaneFragments + pbi->UVPlaneFragments; - ToFragment = pbi->YPlaneFragments + (2 * pbi->UVPlaneFragments) ; - FragsAcross = pbi->HFragments >> 1; - FragsDown = pbi->VFragments >> 1; - break; - } - - /* initialize our array of last used DC Components */ - for(k=0;k<3;k++) - Last[k]=0; - - i=FromFragment; - - /* do prediction on all of Y, U or V */ - for ( m = 0 ; m < FragsDown ; m++) { - for ( n = 0 ; n < FragsAcross ; n++, i++){ - - /* only do 2 prediction if fragment coded and on non intra or - if all fragments are intra */ - if( pbi->display_fragments[i] || (pbi->FrameType == KEY_FRAME) ){ - /* Type of Fragment */ - WhichFrame = Mode2Frame[pbi->FragCodingMethod[i]]; - - /* Check Borderline Cases */ - WhichCase = (n==0) + ((m==0) << 1) + ((n+1 == FragsAcross) << 2); - - fn[0]=i-1; - fn[1]=i-FragsAcross-1; - fn[2]=i-FragsAcross; - fn[3]=i-FragsAcross+1; - - /* fragment valid for prediction use if coded and it comes - from same frame as the one we are predicting */ - for(k=pcount=wpc=0; k<4; k++) { - int pflag; - pflag=1<display_fragments[fn[k]] && - (Mode2Frame[pbi->FragCodingMethod[fn[k]]] == WhichFrame)){ - v[pcount]=pbi->QFragData[fn[k]][0]; - wpc|=pflag; - pcount++; - } - } - - if(wpc==0){ - /* fall back to the last coded fragment */ - pbi->QFragData[i][0] += Last[WhichFrame]; - - }else{ - - /* don't do divide if divisor is 1 or 0 */ - PredictedDC = pc[wpc][0]*v[0]; - for(k=1; k>= pc[wpc][4]; - } - - /* check for outranging on the two predictors that can outrange */ - if((wpc&(PU|PUL|PL)) == (PU|PUL|PL)){ - if( abs(PredictedDC - v[2]) > 128) { - PredictedDC = v[2]; - } else if( abs(PredictedDC - v[0]) > 128) { - PredictedDC = v[0]; - } else if( abs(PredictedDC - v[1]) > 128) { - PredictedDC = v[1]; - } - } - - pbi->QFragData[i][0] += PredictedDC; - - } - - /* Save the last fragment coded for whatever frame we are - predicting from */ - Last[WhichFrame] = pbi->QFragData[i][0]; - - /* Inverse DCT and reconstitute buffer in thisframe */ - ExpandBlockA( pbi, i ); - } - } - } - } - - /* Copy the current reconstruction back to the last frame recon buffer. */ - if(pbi->CodedBlockIndex > (ogg_int32_t) (pbi->UnitFragments >> 1)){ - SwapReconBuffersTemp = pbi->ThisFrameRecon; - pbi->ThisFrameRecon = pbi->LastFrameRecon; - pbi->LastFrameRecon = SwapReconBuffersTemp; - CopyNotRecon( pbi, pbi->LastFrameRecon, pbi->ThisFrameRecon ); - }else{ - CopyRecon( pbi, pbi->LastFrameRecon, pbi->ThisFrameRecon ); - } - - /* Apply a loop filter to edge pixels of updated blocks */ - dsp_LoopFilter(pbi->dsp, pbi, pbi->quant_info.loop_filter_limits[pbi->FrameQIndex]); - - /* We may need to update the UMV border */ - UpdateUMVBorder(pbi, pbi->LastFrameRecon); - - /* Reconstruct the golden frame if necessary. - For VFW codec only on key frames */ - if ( pbi->FrameType == KEY_FRAME ){ - CopyRecon( pbi, pbi->GoldenFrame, pbi->LastFrameRecon ); - /* We may need to update the UMV border */ - UpdateUMVBorder(pbi, pbi->GoldenFrame); - } -} - -void dsp_dct_decode_init (DspFunctions *funcs, ogg_uint32_t cpu_flags) -{ - funcs->LoopFilter = LoopFilter__c; -#if defined(USE_ASM) - // Todo: Port the dct for MSC one day. -#if !defined (_MSC_VER) - if (cpu_flags & OC_CPU_X86_MMX) { - dsp_mmx_dct_decode_init(funcs); - } -#endif -#endif -} diff --git a/Engine/lib/libtheora/lib/enc/dct_encode.c b/Engine/lib/libtheora/lib/enc/dct_encode.c deleted file mode 100644 index 3a3c47778..000000000 --- a/Engine/lib/libtheora/lib/enc/dct_encode.c +++ /dev/null @@ -1,469 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: dct_encode.c 15153 2008-08-04 18:37:55Z tterribe $ - - ********************************************************************/ - -#include -#include "codec_internal.h" -#include "dsp.h" -#include "quant_lookup.h" - - -static int ModeUsesMC[MAX_MODES] = { 0, 0, 1, 1, 1, 0, 1, 1 }; - -static unsigned char TokenizeDctValue (ogg_int16_t DataValue, - ogg_uint32_t * TokenListPtr ){ - unsigned char tokens_added = 0; - ogg_uint32_t AbsDataVal = abs( (ogg_int32_t)DataValue ); - - /* Values are tokenised as category value and a number of additional - bits that define the position within the category. */ - - if ( DataValue == 0 ) return 0; - - if ( AbsDataVal == 1 ){ - if ( DataValue == 1 ) - TokenListPtr[0] = ONE_TOKEN; - else - TokenListPtr[0] = MINUS_ONE_TOKEN; - tokens_added = 1; - } else if ( AbsDataVal == 2 ) { - if ( DataValue == 2 ) - TokenListPtr[0] = TWO_TOKEN; - else - TokenListPtr[0] = MINUS_TWO_TOKEN; - tokens_added = 1; - } else if ( AbsDataVal <= MAX_SINGLE_TOKEN_VALUE ) { - TokenListPtr[0] = LOW_VAL_TOKENS + (AbsDataVal - DCT_VAL_CAT2_MIN); - if ( DataValue > 0 ) - TokenListPtr[1] = 0; - else - TokenListPtr[1] = 1; - tokens_added = 2; - } else if ( AbsDataVal <= 8 ) { - /* Bit 1 determines sign, Bit 0 the value */ - TokenListPtr[0] = DCT_VAL_CATEGORY3; - if ( DataValue > 0 ) - TokenListPtr[1] = (AbsDataVal - DCT_VAL_CAT3_MIN); - else - TokenListPtr[1] = (0x02) + (AbsDataVal - DCT_VAL_CAT3_MIN); - tokens_added = 2; - } else if ( AbsDataVal <= 12 ) { - /* Bit 2 determines sign, Bit 0-2 the value */ - TokenListPtr[0] = DCT_VAL_CATEGORY4; - if ( DataValue > 0 ) - TokenListPtr[1] = (AbsDataVal - DCT_VAL_CAT4_MIN); - else - TokenListPtr[1] = (0x04) + (AbsDataVal - DCT_VAL_CAT4_MIN); - tokens_added = 2; - } else if ( AbsDataVal <= 20 ) { - /* Bit 3 determines sign, Bit 0-2 the value */ - TokenListPtr[0] = DCT_VAL_CATEGORY5; - if ( DataValue > 0 ) - TokenListPtr[1] = (AbsDataVal - DCT_VAL_CAT5_MIN); - else - TokenListPtr[1] = (0x08) + (AbsDataVal - DCT_VAL_CAT5_MIN); - tokens_added = 2; - } else if ( AbsDataVal <= 36 ) { - /* Bit 4 determines sign, Bit 0-3 the value */ - TokenListPtr[0] = DCT_VAL_CATEGORY6; - if ( DataValue > 0 ) - TokenListPtr[1] = (AbsDataVal - DCT_VAL_CAT6_MIN); - else - TokenListPtr[1] = (0x010) + (AbsDataVal - DCT_VAL_CAT6_MIN); - tokens_added = 2; - } else if ( AbsDataVal <= 68 ) { - /* Bit 5 determines sign, Bit 0-4 the value */ - TokenListPtr[0] = DCT_VAL_CATEGORY7; - if ( DataValue > 0 ) - TokenListPtr[1] = (AbsDataVal - DCT_VAL_CAT7_MIN); - else - TokenListPtr[1] = (0x20) + (AbsDataVal - DCT_VAL_CAT7_MIN); - tokens_added = 2; - } else if ( AbsDataVal <= 511 ) { - /* Bit 9 determines sign, Bit 0-8 the value */ - TokenListPtr[0] = DCT_VAL_CATEGORY8; - if ( DataValue > 0 ) - TokenListPtr[1] = (AbsDataVal - DCT_VAL_CAT8_MIN); - else - TokenListPtr[1] = (0x200) + (AbsDataVal - DCT_VAL_CAT8_MIN); - tokens_added = 2; - } else { - TokenListPtr[0] = DCT_VAL_CATEGORY8; - if ( DataValue > 0 ) - TokenListPtr[1] = (511 - DCT_VAL_CAT8_MIN); - else - TokenListPtr[1] = (0x200) + (511 - DCT_VAL_CAT8_MIN); - tokens_added = 2; - } - - /* Return the total number of tokens added */ - return tokens_added; -} - -static unsigned char TokenizeDctRunValue (unsigned char RunLength, - ogg_int16_t DataValue, - ogg_uint32_t * TokenListPtr ){ - unsigned char tokens_added = 0; - ogg_uint32_t AbsDataVal = abs( (ogg_int32_t)DataValue ); - - /* Values are tokenised as category value and a number of additional - bits that define the category. */ - if ( DataValue == 0 ) return 0; - if ( AbsDataVal == 1 ) { - /* Zero runs of 1-5 */ - if ( RunLength <= 5 ) { - TokenListPtr[0] = DCT_RUN_CATEGORY1 + (RunLength - 1); - if ( DataValue > 0 ) - TokenListPtr[1] = 0; - else - TokenListPtr[1] = 1; - } else if ( RunLength <= 9 ) { - /* Zero runs of 6-9 */ - TokenListPtr[0] = DCT_RUN_CATEGORY1B; - if ( DataValue > 0 ) - TokenListPtr[1] = (RunLength - 6); - else - TokenListPtr[1] = 0x04 + (RunLength - 6); - } else { - /* Zero runs of 10-17 */ - TokenListPtr[0] = DCT_RUN_CATEGORY1C; - if ( DataValue > 0 ) - TokenListPtr[1] = (RunLength - 10); - else - TokenListPtr[1] = 0x08 + (RunLength - 10); - } - tokens_added = 2; - } else if ( AbsDataVal <= 3 ) { - if ( RunLength == 1 ) { - TokenListPtr[0] = DCT_RUN_CATEGORY2; - - /* Extra bits token bit 1 indicates sign, bit 0 indicates value */ - if ( DataValue > 0 ) - TokenListPtr[1] = (AbsDataVal - 2); - else - TokenListPtr[1] = (0x02) + (AbsDataVal - 2); - tokens_added = 2; - }else{ - TokenListPtr[0] = DCT_RUN_CATEGORY2 + 1; - - /* Extra bits token. */ - /* bit 2 indicates sign, bit 1 indicates value, bit 0 indicates - run length */ - if ( DataValue > 0 ) - TokenListPtr[1] = ((AbsDataVal - 2) << 1) + (RunLength - 2); - else - TokenListPtr[1] = (0x04) + ((AbsDataVal - 2) << 1) + (RunLength - 2); - tokens_added = 2; - } - } else { - tokens_added = 2; /* ERROR */ - /*IssueWarning( "Bad Input to TokenizeDctRunValue" );*/ - } - - /* Return the total number of tokens added */ - return tokens_added; -} - -static unsigned char TokenizeDctBlock (ogg_int16_t * RawData, - ogg_uint32_t * TokenListPtr ) { - ogg_uint32_t i; - unsigned char run_count; - unsigned char token_count = 0; /* Number of tokens crated. */ - ogg_uint32_t AbsData; - - - /* Tokenize the block */ - for( i = 0; i < BLOCK_SIZE; i++ ){ - run_count = 0; - - /* Look for a zero run. */ - /* NOTE the use of & instead of && which is faster (and - equivalent) in this instance. */ - /* NO, NO IT ISN'T --Monty */ - while( (i < BLOCK_SIZE) && (!RawData[i]) ){ - run_count++; - i++; - } - - /* If we have reached the end of the block then code EOB */ - if ( i == BLOCK_SIZE ){ - TokenListPtr[token_count] = DCT_EOB_TOKEN; - token_count++; - }else{ - /* If we have a short zero run followed by a low data value code - the two as a composite token. */ - if ( run_count ){ - AbsData = abs(RawData[i]); - - if ( ((AbsData == 1) && (run_count <= 17)) || - ((AbsData <= 3) && (run_count <= 3)) ) { - /* Tokenise the run and subsequent value combination value */ - token_count += TokenizeDctRunValue( run_count, - RawData[i], - &TokenListPtr[token_count] ); - }else{ - - /* Else if we have a long non-EOB run or a run followed by a - value token > MAX_RUN_VAL then code the run and token - seperately */ - if ( run_count <= 8 ) - TokenListPtr[token_count] = DCT_SHORT_ZRL_TOKEN; - else - TokenListPtr[token_count] = DCT_ZRL_TOKEN; - - token_count++; - TokenListPtr[token_count] = run_count - 1; - token_count++; - - /* Now tokenize the value */ - token_count += TokenizeDctValue( RawData[i], - &TokenListPtr[token_count] ); - } - }else{ - /* Else there was NO zero run. */ - /* Tokenise the value */ - token_count += TokenizeDctValue( RawData[i], - &TokenListPtr[token_count] ); - } - } - } - - /* Return the total number of tokens (including additional bits - tokens) used. */ - return token_count; -} - -ogg_uint32_t DPCMTokenizeBlock (CP_INSTANCE *cpi, - ogg_int32_t FragIndex){ - ogg_uint32_t token_count; - - if ( cpi->pb.FrameType == KEY_FRAME ){ - /* Key frame so code block in INTRA mode. */ - cpi->pb.CodingMode = CODE_INTRA; - }else{ - /* Get Motion vector and mode for this block. */ - cpi->pb.CodingMode = cpi->pb.FragCodingMethod[FragIndex]; - } - - /* Tokenise the dct data. */ - token_count = TokenizeDctBlock( cpi->pb.QFragData[FragIndex], - cpi->pb.TokenList[FragIndex] ); - - cpi->FragTokenCounts[FragIndex] = token_count; - cpi->TotTokenCount += token_count; - - /* Return number of pixels coded (i.e. 8x8). */ - return BLOCK_SIZE; -} - -static int AllZeroDctData( Q_LIST_ENTRY * QuantList ){ - ogg_uint32_t i; - - for ( i = 0; i < 64; i ++ ) - if ( QuantList[i] != 0 ) - return 0; - - return 1; -} - -static void MotionBlockDifference (CP_INSTANCE * cpi, unsigned char * FiltPtr, - ogg_int16_t *DctInputPtr, ogg_int32_t MvDevisor, - unsigned char* old_ptr1, unsigned char* new_ptr1, - ogg_uint32_t FragIndex,ogg_uint32_t PixelsPerLine, - ogg_uint32_t ReconPixelsPerLine) { - - ogg_int32_t MvShift; - ogg_int32_t MvModMask; - ogg_int32_t AbsRefOffset; - ogg_int32_t AbsXOffset; - ogg_int32_t AbsYOffset; - ogg_int32_t MVOffset; /* Baseline motion vector offset */ - ogg_int32_t ReconPtr2Offset; /* Offset for second reconstruction in - half pixel MC */ - unsigned char *ReconPtr1; /* DCT reconstructed image pointers */ - unsigned char *ReconPtr2; /* Pointer used in half pixel MC */ - - switch(MvDevisor) { - case 2: - MvShift = 1; - MvModMask = 1; - break; - case 4: - MvShift = 2; - MvModMask = 3; - break; - default: - break; - } - - cpi->MVector.x = cpi->pb.FragMVect[FragIndex].x; - cpi->MVector.y = cpi->pb.FragMVect[FragIndex].y; - - /* Set up the baseline offset for the motion vector. */ - MVOffset = ((cpi->MVector.y / MvDevisor) * ReconPixelsPerLine) + - (cpi->MVector.x / MvDevisor); - - /* Work out the offset of the second reference position for 1/2 - pixel interpolation. For the U and V planes the MV specifies 1/4 - pixel accuracy. This is adjusted to 1/2 pixel as follows ( 0->0, - 1/4->1/2, 1/2->1/2, 3/4->1/2 ). */ - ReconPtr2Offset = 0; - AbsXOffset = cpi->MVector.x % MvDevisor; - AbsYOffset = cpi->MVector.y % MvDevisor; - - if ( AbsXOffset ) { - if ( cpi->MVector.x > 0 ) - ReconPtr2Offset += 1; - else - ReconPtr2Offset -= 1; - } - - if ( AbsYOffset ) { - if ( cpi->MVector.y > 0 ) - ReconPtr2Offset += ReconPixelsPerLine; - else - ReconPtr2Offset -= ReconPixelsPerLine; - } - - if ( cpi->pb.CodingMode==CODE_GOLDEN_MV ) { - ReconPtr1 = &cpi-> - pb.GoldenFrame[cpi->pb.recon_pixel_index_table[FragIndex]]; - } else { - ReconPtr1 = &cpi-> - pb.LastFrameRecon[cpi->pb.recon_pixel_index_table[FragIndex]]; - } - - ReconPtr1 += MVOffset; - ReconPtr2 = ReconPtr1 + ReconPtr2Offset; - - AbsRefOffset = abs((int)(ReconPtr1 - ReconPtr2)); - - /* Is the MV offset exactly pixel alligned */ - if ( AbsRefOffset == 0 ){ - dsp_sub8x8(cpi->dsp, FiltPtr, ReconPtr1, DctInputPtr, - PixelsPerLine, ReconPixelsPerLine); - dsp_copy8x8 (cpi->dsp, new_ptr1, old_ptr1, PixelsPerLine); - } else { - /* Fractional pixel MVs. */ - /* Note that we only use two pixel values even for the diagonal */ - dsp_sub8x8avg2(cpi->dsp, FiltPtr, ReconPtr1,ReconPtr2,DctInputPtr, - PixelsPerLine, ReconPixelsPerLine); - dsp_copy8x8 (cpi->dsp, new_ptr1, old_ptr1, PixelsPerLine); - } -} - -void TransformQuantizeBlock (CP_INSTANCE *cpi, ogg_int32_t FragIndex, - ogg_uint32_t PixelsPerLine) { - unsigned char *new_ptr1; /* Pointers into current frame */ - unsigned char *old_ptr1; /* Pointers into old frame */ - unsigned char *FiltPtr; /* Pointers to srf filtered pixels */ - ogg_int16_t *DctInputPtr; /* Pointer into buffer containing input to DCT */ - int LeftEdge; /* Flag if block at left edge of component */ - ogg_uint32_t ReconPixelsPerLine; /* Line length for recon buffers. */ - - unsigned char *ReconPtr1; /* DCT reconstructed image pointers */ - ogg_int32_t MvDevisor; /* Defines MV resolution (2 = 1/2 - pixel for Y or 4 = 1/4 for UV) */ - - new_ptr1 = &cpi->yuv1ptr[cpi->pb.pixel_index_table[FragIndex]]; - old_ptr1 = &cpi->yuv0ptr[cpi->pb.pixel_index_table[FragIndex]]; - DctInputPtr = cpi->DCTDataBuffer; - - /* Set plane specific values */ - if (FragIndex < (ogg_int32_t)cpi->pb.YPlaneFragments){ - ReconPixelsPerLine = cpi->pb.YStride; - MvDevisor = 2; /* 1/2 pixel accuracy in Y */ - }else{ - ReconPixelsPerLine = cpi->pb.UVStride; - MvDevisor = 4; /* UV planes at 1/2 resolution of Y */ - } - - /* adjusted / filtered pointers */ - FiltPtr = &cpi->ConvDestBuffer[cpi->pb.pixel_index_table[FragIndex]]; - - if ( cpi->pb.FrameType == KEY_FRAME ) { - /* Key frame so code block in INTRA mode. */ - cpi->pb.CodingMode = CODE_INTRA; - }else{ - /* Get Motion vector and mode for this block. */ - cpi->pb.CodingMode = cpi->pb.FragCodingMethod[FragIndex]; - } - - /* Selection of Quantiser matrix and set other plane related values. */ - if ( FragIndex < (ogg_int32_t)cpi->pb.YPlaneFragments ){ - LeftEdge = !(FragIndex%cpi->pb.HFragments); - - /* Select the appropriate Y quantiser matrix */ - if ( cpi->pb.CodingMode == CODE_INTRA ) - select_quantiser(&cpi->pb, BLOCK_Y); - else - select_quantiser(&cpi->pb, BLOCK_INTER_Y); - } else { - LeftEdge = !((FragIndex-cpi->pb.YPlaneFragments)%(cpi->pb.HFragments>>1)); - - if(FragIndex < (ogg_int32_t)cpi->pb.YPlaneFragments + (ogg_int32_t)cpi->pb.UVPlaneFragments) { - /* U plane */ - if ( cpi->pb.CodingMode == CODE_INTRA ) - select_quantiser(&cpi->pb, BLOCK_U); - else - select_quantiser(&cpi->pb, BLOCK_INTER_U); - } else { - /* V plane */ - if ( cpi->pb.CodingMode == CODE_INTRA ) - select_quantiser(&cpi->pb, BLOCK_V); - else - select_quantiser(&cpi->pb, BLOCK_INTER_V); - } - } - - if ( ModeUsesMC[cpi->pb.CodingMode] ){ - - MotionBlockDifference(cpi, FiltPtr, DctInputPtr, MvDevisor, - old_ptr1, new_ptr1, FragIndex, PixelsPerLine, - ReconPixelsPerLine); - - } else if ( (cpi->pb.CodingMode==CODE_INTER_NO_MV ) || - ( cpi->pb.CodingMode==CODE_USING_GOLDEN ) ) { - if ( cpi->pb.CodingMode==CODE_INTER_NO_MV ) { - ReconPtr1 = &cpi-> - pb.LastFrameRecon[cpi->pb.recon_pixel_index_table[FragIndex]]; - } else { - ReconPtr1 = &cpi-> - pb.GoldenFrame[cpi->pb.recon_pixel_index_table[FragIndex]]; - } - - dsp_sub8x8(cpi->dsp, FiltPtr, ReconPtr1, DctInputPtr, - PixelsPerLine, ReconPixelsPerLine); - dsp_copy8x8 (cpi->dsp, new_ptr1, old_ptr1, PixelsPerLine); - } else if ( cpi->pb.CodingMode==CODE_INTRA ) { - dsp_sub8x8_128(cpi->dsp, FiltPtr, DctInputPtr, PixelsPerLine); - dsp_copy8x8 (cpi->dsp, new_ptr1, old_ptr1, PixelsPerLine); - } - - /* Proceed to encode the data into the encode buffer if the encoder - is enabled. */ - /* Perform a 2D DCT transform on the data. */ - dsp_fdct_short(cpi->dsp, cpi->DCTDataBuffer, cpi->DCT_codes ); - - /* Quantize that transform data. */ - quantize ( &cpi->pb, cpi->DCT_codes, cpi->pb.QFragData[FragIndex] ); - - if ( (cpi->pb.CodingMode == CODE_INTER_NO_MV) && - ( AllZeroDctData(cpi->pb.QFragData[FragIndex]) ) ) { - cpi->pb.display_fragments[FragIndex] = 0; - } - -} diff --git a/Engine/lib/libtheora/lib/enc/dsp.c b/Engine/lib/libtheora/lib/enc/dsp.c deleted file mode 100644 index 9fe402d4e..000000000 --- a/Engine/lib/libtheora/lib/enc/dsp.c +++ /dev/null @@ -1,422 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: dsp.c 15427 2008-10-21 02:36:19Z xiphmont $ - - ********************************************************************/ - -#include -#include "codec_internal.h" -#include "../cpu.c" - -#define DSP_OP_AVG(a,b) ((((int)(a)) + ((int)(b)))/2) -#define DSP_OP_DIFF(a,b) (((int)(a)) - ((int)(b))) -#define DSP_OP_ABS_DIFF(a,b) abs((((int)(a)) - ((int)(b)))) - -static void sub8x8__c (unsigned char *FiltPtr, unsigned char *ReconPtr, - ogg_int16_t *DctInputPtr, ogg_uint32_t PixelsPerLine, - ogg_uint32_t ReconPixelsPerLine) { - int i; - - /* For each block row */ - for (i=8; i; i--) { - DctInputPtr[0] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[0], ReconPtr[0]); - DctInputPtr[1] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[1], ReconPtr[1]); - DctInputPtr[2] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[2], ReconPtr[2]); - DctInputPtr[3] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[3], ReconPtr[3]); - DctInputPtr[4] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[4], ReconPtr[4]); - DctInputPtr[5] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[5], ReconPtr[5]); - DctInputPtr[6] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[6], ReconPtr[6]); - DctInputPtr[7] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[7], ReconPtr[7]); - - /* Start next row */ - FiltPtr += PixelsPerLine; - ReconPtr += ReconPixelsPerLine; - DctInputPtr += 8; - } -} - -static void sub8x8_128__c (unsigned char *FiltPtr, ogg_int16_t *DctInputPtr, - ogg_uint32_t PixelsPerLine) { - int i; - /* For each block row */ - for (i=8; i; i--) { - /* INTRA mode so code raw image data */ - /* We convert the data to 8 bit signed (by subtracting 128) as - this reduces the internal precision requirments in the DCT - transform. */ - DctInputPtr[0] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[0], 128); - DctInputPtr[1] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[1], 128); - DctInputPtr[2] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[2], 128); - DctInputPtr[3] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[3], 128); - DctInputPtr[4] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[4], 128); - DctInputPtr[5] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[5], 128); - DctInputPtr[6] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[6], 128); - DctInputPtr[7] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[7], 128); - - /* Start next row */ - FiltPtr += PixelsPerLine; - DctInputPtr += 8; - } -} - -static void sub8x8avg2__c (unsigned char *FiltPtr, unsigned char *ReconPtr1, - unsigned char *ReconPtr2, ogg_int16_t *DctInputPtr, - ogg_uint32_t PixelsPerLine, - ogg_uint32_t ReconPixelsPerLine) -{ - int i; - - /* For each block row */ - for (i=8; i; i--) { - DctInputPtr[0] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[0], DSP_OP_AVG (ReconPtr1[0], ReconPtr2[0])); - DctInputPtr[1] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[1], DSP_OP_AVG (ReconPtr1[1], ReconPtr2[1])); - DctInputPtr[2] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[2], DSP_OP_AVG (ReconPtr1[2], ReconPtr2[2])); - DctInputPtr[3] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[3], DSP_OP_AVG (ReconPtr1[3], ReconPtr2[3])); - DctInputPtr[4] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[4], DSP_OP_AVG (ReconPtr1[4], ReconPtr2[4])); - DctInputPtr[5] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[5], DSP_OP_AVG (ReconPtr1[5], ReconPtr2[5])); - DctInputPtr[6] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[6], DSP_OP_AVG (ReconPtr1[6], ReconPtr2[6])); - DctInputPtr[7] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[7], DSP_OP_AVG (ReconPtr1[7], ReconPtr2[7])); - - /* Start next row */ - FiltPtr += PixelsPerLine; - ReconPtr1 += ReconPixelsPerLine; - ReconPtr2 += ReconPixelsPerLine; - DctInputPtr += 8; - } -} - -static ogg_uint32_t row_sad8__c (unsigned char *Src1, unsigned char *Src2) -{ - ogg_uint32_t SadValue; - ogg_uint32_t SadValue1; - - SadValue = DSP_OP_ABS_DIFF (Src1[0], Src2[0]) + - DSP_OP_ABS_DIFF (Src1[1], Src2[1]) + - DSP_OP_ABS_DIFF (Src1[2], Src2[2]) + - DSP_OP_ABS_DIFF (Src1[3], Src2[3]); - - SadValue1 = DSP_OP_ABS_DIFF (Src1[4], Src2[4]) + - DSP_OP_ABS_DIFF (Src1[5], Src2[5]) + - DSP_OP_ABS_DIFF (Src1[6], Src2[6]) + - DSP_OP_ABS_DIFF (Src1[7], Src2[7]); - - SadValue = ( SadValue > SadValue1 ) ? SadValue : SadValue1; - - return SadValue; -} - -static ogg_uint32_t col_sad8x8__c (unsigned char *Src1, unsigned char *Src2, - ogg_uint32_t stride) -{ - ogg_uint32_t SadValue[8] = {0,0,0,0,0,0,0,0}; - ogg_uint32_t SadValue2[8] = {0,0,0,0,0,0,0,0}; - ogg_uint32_t MaxSad = 0; - ogg_uint32_t i; - - for ( i = 0; i < 4; i++ ){ - SadValue[0] += abs(Src1[0] - Src2[0]); - SadValue[1] += abs(Src1[1] - Src2[1]); - SadValue[2] += abs(Src1[2] - Src2[2]); - SadValue[3] += abs(Src1[3] - Src2[3]); - SadValue[4] += abs(Src1[4] - Src2[4]); - SadValue[5] += abs(Src1[5] - Src2[5]); - SadValue[6] += abs(Src1[6] - Src2[6]); - SadValue[7] += abs(Src1[7] - Src2[7]); - - Src1 += stride; - Src2 += stride; - } - - for ( i = 0; i < 4; i++ ){ - SadValue2[0] += abs(Src1[0] - Src2[0]); - SadValue2[1] += abs(Src1[1] - Src2[1]); - SadValue2[2] += abs(Src1[2] - Src2[2]); - SadValue2[3] += abs(Src1[3] - Src2[3]); - SadValue2[4] += abs(Src1[4] - Src2[4]); - SadValue2[5] += abs(Src1[5] - Src2[5]); - SadValue2[6] += abs(Src1[6] - Src2[6]); - SadValue2[7] += abs(Src1[7] - Src2[7]); - - Src1 += stride; - Src2 += stride; - } - - for ( i = 0; i < 8; i++ ){ - if ( SadValue[i] > MaxSad ) - MaxSad = SadValue[i]; - if ( SadValue2[i] > MaxSad ) - MaxSad = SadValue2[i]; - } - - return MaxSad; -} - -static ogg_uint32_t sad8x8__c (unsigned char *ptr1, ogg_uint32_t stride1, - unsigned char *ptr2, ogg_uint32_t stride2) -{ - ogg_uint32_t i; - ogg_uint32_t sad = 0; - - for (i=8; i; i--) { - sad += DSP_OP_ABS_DIFF(ptr1[0], ptr2[0]); - sad += DSP_OP_ABS_DIFF(ptr1[1], ptr2[1]); - sad += DSP_OP_ABS_DIFF(ptr1[2], ptr2[2]); - sad += DSP_OP_ABS_DIFF(ptr1[3], ptr2[3]); - sad += DSP_OP_ABS_DIFF(ptr1[4], ptr2[4]); - sad += DSP_OP_ABS_DIFF(ptr1[5], ptr2[5]); - sad += DSP_OP_ABS_DIFF(ptr1[6], ptr2[6]); - sad += DSP_OP_ABS_DIFF(ptr1[7], ptr2[7]); - - /* Step to next row of block. */ - ptr1 += stride1; - ptr2 += stride2; - } - - return sad; -} - -static ogg_uint32_t sad8x8_thres__c (unsigned char *ptr1, ogg_uint32_t stride1, - unsigned char *ptr2, ogg_uint32_t stride2, - ogg_uint32_t thres) -{ - ogg_uint32_t i; - ogg_uint32_t sad = 0; - - for (i=8; i; i--) { - sad += DSP_OP_ABS_DIFF(ptr1[0], ptr2[0]); - sad += DSP_OP_ABS_DIFF(ptr1[1], ptr2[1]); - sad += DSP_OP_ABS_DIFF(ptr1[2], ptr2[2]); - sad += DSP_OP_ABS_DIFF(ptr1[3], ptr2[3]); - sad += DSP_OP_ABS_DIFF(ptr1[4], ptr2[4]); - sad += DSP_OP_ABS_DIFF(ptr1[5], ptr2[5]); - sad += DSP_OP_ABS_DIFF(ptr1[6], ptr2[6]); - sad += DSP_OP_ABS_DIFF(ptr1[7], ptr2[7]); - - if (sad > thres ) - break; - - /* Step to next row of block. */ - ptr1 += stride1; - ptr2 += stride2; - } - - return sad; -} - -static ogg_uint32_t sad8x8_xy2_thres__c (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr1, - unsigned char *RefDataPtr2, ogg_uint32_t RefStride, - ogg_uint32_t thres) -{ - ogg_uint32_t i; - ogg_uint32_t sad = 0; - - for (i=8; i; i--) { - sad += DSP_OP_ABS_DIFF(SrcData[0], DSP_OP_AVG (RefDataPtr1[0], RefDataPtr2[0])); - sad += DSP_OP_ABS_DIFF(SrcData[1], DSP_OP_AVG (RefDataPtr1[1], RefDataPtr2[1])); - sad += DSP_OP_ABS_DIFF(SrcData[2], DSP_OP_AVG (RefDataPtr1[2], RefDataPtr2[2])); - sad += DSP_OP_ABS_DIFF(SrcData[3], DSP_OP_AVG (RefDataPtr1[3], RefDataPtr2[3])); - sad += DSP_OP_ABS_DIFF(SrcData[4], DSP_OP_AVG (RefDataPtr1[4], RefDataPtr2[4])); - sad += DSP_OP_ABS_DIFF(SrcData[5], DSP_OP_AVG (RefDataPtr1[5], RefDataPtr2[5])); - sad += DSP_OP_ABS_DIFF(SrcData[6], DSP_OP_AVG (RefDataPtr1[6], RefDataPtr2[6])); - sad += DSP_OP_ABS_DIFF(SrcData[7], DSP_OP_AVG (RefDataPtr1[7], RefDataPtr2[7])); - - if ( sad > thres ) - break; - - /* Step to next row of block. */ - SrcData += SrcStride; - RefDataPtr1 += RefStride; - RefDataPtr2 += RefStride; - } - - return sad; -} - -static ogg_uint32_t intra8x8_err__c (unsigned char *DataPtr, ogg_uint32_t Stride) -{ - ogg_uint32_t i; - ogg_uint32_t XSum=0; - ogg_uint32_t XXSum=0; - - for (i=8; i; i--) { - /* Examine alternate pixel locations. */ - XSum += DataPtr[0]; - XXSum += DataPtr[0]*DataPtr[0]; - XSum += DataPtr[1]; - XXSum += DataPtr[1]*DataPtr[1]; - XSum += DataPtr[2]; - XXSum += DataPtr[2]*DataPtr[2]; - XSum += DataPtr[3]; - XXSum += DataPtr[3]*DataPtr[3]; - XSum += DataPtr[4]; - XXSum += DataPtr[4]*DataPtr[4]; - XSum += DataPtr[5]; - XXSum += DataPtr[5]*DataPtr[5]; - XSum += DataPtr[6]; - XXSum += DataPtr[6]*DataPtr[6]; - XSum += DataPtr[7]; - XXSum += DataPtr[7]*DataPtr[7]; - - /* Step to next row of block. */ - DataPtr += Stride; - } - - /* Compute population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum ) ); -} - -static ogg_uint32_t inter8x8_err__c (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr, ogg_uint32_t RefStride) -{ - ogg_uint32_t i; - ogg_uint32_t XSum=0; - ogg_uint32_t XXSum=0; - ogg_int32_t DiffVal; - - for (i=8; i; i--) { - DiffVal = DSP_OP_DIFF (SrcData[0], RefDataPtr[0]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF (SrcData[1], RefDataPtr[1]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF (SrcData[2], RefDataPtr[2]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF (SrcData[3], RefDataPtr[3]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF (SrcData[4], RefDataPtr[4]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF (SrcData[5], RefDataPtr[5]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF (SrcData[6], RefDataPtr[6]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF (SrcData[7], RefDataPtr[7]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - /* Step to next row of block. */ - SrcData += SrcStride; - RefDataPtr += RefStride; - } - - /* Compute and return population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum )); -} - -static ogg_uint32_t inter8x8_err_xy2__c (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr1, - unsigned char *RefDataPtr2, ogg_uint32_t RefStride) -{ - ogg_uint32_t i; - ogg_uint32_t XSum=0; - ogg_uint32_t XXSum=0; - ogg_int32_t DiffVal; - - for (i=8; i; i--) { - DiffVal = DSP_OP_DIFF(SrcData[0], DSP_OP_AVG (RefDataPtr1[0], RefDataPtr2[0])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF(SrcData[1], DSP_OP_AVG (RefDataPtr1[1], RefDataPtr2[1])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF(SrcData[2], DSP_OP_AVG (RefDataPtr1[2], RefDataPtr2[2])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF(SrcData[3], DSP_OP_AVG (RefDataPtr1[3], RefDataPtr2[3])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF(SrcData[4], DSP_OP_AVG (RefDataPtr1[4], RefDataPtr2[4])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF(SrcData[5], DSP_OP_AVG (RefDataPtr1[5], RefDataPtr2[5])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF(SrcData[6], DSP_OP_AVG (RefDataPtr1[6], RefDataPtr2[6])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF(SrcData[7], DSP_OP_AVG (RefDataPtr1[7], RefDataPtr2[7])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - /* Step to next row of block. */ - SrcData += SrcStride; - RefDataPtr1 += RefStride; - RefDataPtr2 += RefStride; - } - - /* Compute and return population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum )); -} - -static void nop (void) { /* NOP */ } - -void dsp_init(DspFunctions *funcs) -{ - funcs->save_fpu = nop; - funcs->restore_fpu = nop; - funcs->sub8x8 = sub8x8__c; - funcs->sub8x8_128 = sub8x8_128__c; - funcs->sub8x8avg2 = sub8x8avg2__c; - funcs->row_sad8 = row_sad8__c; - funcs->col_sad8x8 = col_sad8x8__c; - funcs->sad8x8 = sad8x8__c; - funcs->sad8x8_thres = sad8x8_thres__c; - funcs->sad8x8_xy2_thres = sad8x8_xy2_thres__c; - funcs->intra8x8_err = intra8x8_err__c; - funcs->inter8x8_err = inter8x8_err__c; - funcs->inter8x8_err_xy2 = inter8x8_err_xy2__c; -} - -void dsp_static_init(DspFunctions *funcs) -{ - ogg_uint32_t cpuflags; - - cpuflags = oc_cpu_flags_get (); - dsp_init (funcs); - - dsp_recon_init (funcs, cpuflags); - dsp_dct_init (funcs, cpuflags); -#if defined(USE_ASM) - if (cpuflags & OC_CPU_X86_MMX) { - dsp_mmx_init(funcs); - } -# ifndef WIN32 - /* This is implemented for win32 yet */ - if (cpuflags & OC_CPU_X86_MMXEXT) { - dsp_mmxext_init(funcs); - } -# endif -#endif -} - diff --git a/Engine/lib/libtheora/lib/enc/dsp.h b/Engine/lib/libtheora/lib/enc/dsp.h deleted file mode 100644 index 7f96f7f84..000000000 --- a/Engine/lib/libtheora/lib/enc/dsp.h +++ /dev/null @@ -1,166 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: dsp.h 15153 2008-08-04 18:37:55Z tterribe $ - - ********************************************************************/ - -#ifndef DSP_H -#define DSP_H - -#include "theora/theora.h" -#include "../cpu.h" - -typedef struct -{ - void (*save_fpu) (void); - void (*restore_fpu) (void); - - void (*sub8x8) (unsigned char *FiltPtr, unsigned char *ReconPtr, - ogg_int16_t *DctInputPtr, ogg_uint32_t PixelsPerLine, - ogg_uint32_t ReconPixelsPerLine); - - void (*sub8x8_128) (unsigned char *FiltPtr, ogg_int16_t *DctInputPtr, - ogg_uint32_t PixelsPerLine); - - void (*sub8x8avg2) (unsigned char *FiltPtr, unsigned char *ReconPtr1, - unsigned char *ReconPtr2, ogg_int16_t *DctInputPtr, - ogg_uint32_t PixelsPerLine, - ogg_uint32_t ReconPixelsPerLine); - - void (*copy8x8) (unsigned char *src, unsigned char *dest, - ogg_uint32_t stride); - - void (*recon_intra8x8) (unsigned char *ReconPtr, ogg_int16_t *ChangePtr, - ogg_uint32_t LineStep); - - void (*recon_inter8x8) (unsigned char *ReconPtr, unsigned char *RefPtr, - ogg_int16_t *ChangePtr, ogg_uint32_t LineStep); - - void (*recon_inter8x8_half) (unsigned char *ReconPtr, unsigned char *RefPtr1, - unsigned char *RefPtr2, ogg_int16_t *ChangePtr, - ogg_uint32_t LineStep); - - void (*fdct_short) (ogg_int16_t *InputData, ogg_int16_t *OutputData); - - ogg_uint32_t (*row_sad8) (unsigned char *Src1, unsigned char *Src2); - - ogg_uint32_t (*col_sad8x8) (unsigned char *Src1, unsigned char *Src2, - ogg_uint32_t stride); - - ogg_uint32_t (*sad8x8) (unsigned char *ptr1, ogg_uint32_t stride1, - unsigned char *ptr2, ogg_uint32_t stride2); - - ogg_uint32_t (*sad8x8_thres) (unsigned char *ptr1, ogg_uint32_t stride1, - unsigned char *ptr2, ogg_uint32_t stride2, - ogg_uint32_t thres); - - ogg_uint32_t (*sad8x8_xy2_thres)(unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr1, - unsigned char *RefDataPtr2, ogg_uint32_t RefStride, - ogg_uint32_t thres); - - ogg_uint32_t (*intra8x8_err) (unsigned char *DataPtr, ogg_uint32_t Stride); - - ogg_uint32_t (*inter8x8_err) (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr, ogg_uint32_t RefStride); - - ogg_uint32_t (*inter8x8_err_xy2)(unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr1, - unsigned char *RefDataPtr2, ogg_uint32_t RefStride); - - void (*LoopFilter) (PB_INSTANCE *pbi, int FLimit); - - void (*FilterVert) (unsigned char * PixelPtr, - ogg_int32_t LineLength, ogg_int16_t *BoundingValuePtr); - - void (*IDctSlow) (ogg_int16_t *InputData, - ogg_int16_t *QuantMatrix, ogg_int16_t *OutputData); - - void (*IDct3) (ogg_int16_t *InputData, - ogg_int16_t *QuantMatrix, ogg_int16_t *OutputData); - - void (*IDct10) (ogg_int16_t *InputData, - ogg_int16_t *QuantMatrix, ogg_int16_t *OutputData); -} DspFunctions; - -extern void dsp_dct_init(DspFunctions *funcs, ogg_uint32_t cpu_flags); -extern void dsp_recon_init (DspFunctions *funcs, ogg_uint32_t cpu_flags); -extern void dsp_dct_decode_init(DspFunctions *funcs, ogg_uint32_t cpu_flags); -extern void dsp_idct_init(DspFunctions *funcs, ogg_uint32_t cpu_flags); - -void dsp_init(DspFunctions *funcs); -void dsp_static_init(DspFunctions *funcs); -#if defined(USE_ASM) && (defined(__i386__) || defined(__x86_64__) || defined(WIN32)) -extern void dsp_mmx_init(DspFunctions *funcs); -extern void dsp_mmxext_init(DspFunctions *funcs); -extern void dsp_mmx_fdct_init(DspFunctions *funcs); -extern void dsp_mmx_recon_init(DspFunctions *funcs); -extern void dsp_mmx_dct_decode_init(DspFunctions *funcs); -extern void dsp_mmx_idct_init(DspFunctions *funcs); -#endif - -#define dsp_save_fpu(funcs) (funcs.save_fpu ()) - -#define dsp_restore_fpu(funcs) (funcs.restore_fpu ()) - -#define dsp_sub8x8(funcs,a1,a2,a3,a4,a5) (funcs.sub8x8 (a1,a2,a3,a4,a5)) - -#define dsp_sub8x8_128(funcs,a1,a2,a3) (funcs.sub8x8_128 (a1,a2,a3)) - -#define dsp_sub8x8avg2(funcs,a1,a2,a3,a4,a5,a6) (funcs.sub8x8avg2 (a1,a2,a3,a4,a5,a6)) - -#define dsp_copy8x8(funcs,ptr1,ptr2,str1) (funcs.copy8x8 (ptr1,ptr2,str1)) - -#define dsp_recon_intra8x8(funcs,ptr1,ptr2,str1) (funcs.recon_intra8x8 (ptr1,ptr2,str1)) - -#define dsp_recon_inter8x8(funcs,ptr1,ptr2,ptr3,str1) \ - (funcs.recon_inter8x8 (ptr1,ptr2,ptr3,str1)) - -#define dsp_recon_inter8x8_half(funcs,ptr1,ptr2,ptr3,ptr4,str1) \ - (funcs.recon_inter8x8_half (ptr1,ptr2,ptr3,ptr4,str1)) - -#define dsp_fdct_short(funcs,in,out) (funcs.fdct_short (in,out)) - -#define dsp_row_sad8(funcs,ptr1,ptr2) (funcs.row_sad8 (ptr1,ptr2)) - -#define dsp_col_sad8x8(funcs,ptr1,ptr2,str1) (funcs.col_sad8x8 (ptr1,ptr2,str1)) - -#define dsp_sad8x8(funcs,ptr1,str1,ptr2,str2) (funcs.sad8x8 (ptr1,str1,ptr2,str2)) - -#define dsp_sad8x8_thres(funcs,ptr1,str1,ptr2,str2,t) (funcs.sad8x8_thres (ptr1,str1,ptr2,str2,t)) - -#define dsp_sad8x8_xy2_thres(funcs,ptr1,str1,ptr2,ptr3,str2,t) \ - (funcs.sad8x8_xy2_thres (ptr1,str1,ptr2,ptr3,str2,t)) - -#define dsp_intra8x8_err(funcs,ptr1,str1) (funcs.intra8x8_err (ptr1,str1)) - -#define dsp_inter8x8_err(funcs,ptr1,str1,ptr2,str2) \ - (funcs.inter8x8_err (ptr1,str1,ptr2,str2)) - -#define dsp_inter8x8_err_xy2(funcs,ptr1,str1,ptr2,ptr3,str2) \ - (funcs.inter8x8_err_xy2 (ptr1,str1,ptr2,ptr3,str2)) - -#define dsp_LoopFilter(funcs, ptr1, i) \ - (funcs.LoopFilter(ptr1, i)) - -#define dsp_IDctSlow(funcs, ptr1, ptr2, ptr3) \ - (funcs.IDctSlow(ptr1, ptr2, ptr3)) - -#define dsp_IDct3(funcs, ptr1, ptr2, ptr3) \ - (funcs.IDctSlow(ptr1, ptr2, ptr3)) - -#define dsp_IDct10(funcs, ptr1, ptr2, ptr3) \ - (funcs.IDctSlow(ptr1, ptr2, ptr3)) - -#endif /* DSP_H */ diff --git a/Engine/lib/libtheora/lib/enc/encode.c b/Engine/lib/libtheora/lib/enc/encode.c deleted file mode 100644 index 5dc89f2af..000000000 --- a/Engine/lib/libtheora/lib/enc/encode.c +++ /dev/null @@ -1,1479 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: encode.c 15383 2008-10-10 14:33:46Z xiphmont $ - - ********************************************************************/ - -#include -#include -#include "codec_internal.h" -#include "encoder_lookup.h" -#include "block_inline.h" - -#define PUR 8 -#define PU 4 -#define PUL 2 -#define PL 1 -#define HIGHBITDUPPED(X) (((ogg_int16_t) X) >> 15) - -static ogg_uint32_t QuadCodeComponent ( CP_INSTANCE *cpi, - ogg_uint32_t FirstSB, - ogg_uint32_t SBRows, - ogg_uint32_t SBCols, - ogg_uint32_t PixelsPerLine){ - - ogg_int32_t FragIndex; /* Fragment number */ - ogg_uint32_t MB, B; /* Macro-Block, Block indices */ - ogg_uint32_t SBrow; /* Super-Block row number */ - ogg_uint32_t SBcol; /* Super-Block row number */ - ogg_uint32_t SB=FirstSB; /* Super-Block index, initialised to first - of this component */ - ogg_uint32_t coded_pixels=0; /* Number of pixels coded */ - int MBCodedFlag; - - /* actually transform and quantize the image now that we've decided - on the modes Parse in quad-tree ordering */ - - for ( SBrow=0; SBrowpb.BlockMap,SB,MB) >= 0 ) { - - MBCodedFlag = 0; - - /* Now actually code the blocks */ - for ( B=0; B<4; B++ ) { - FragIndex = QuadMapToIndex1( cpi->pb.BlockMap, SB, MB, B ); - - /* Does Block lie in frame: */ - if ( FragIndex >= 0 ) { - - /* In Frame: Is it coded: */ - if ( cpi->pb.display_fragments[FragIndex] ) { - - /* transform and quantize block */ - TransformQuantizeBlock( cpi, FragIndex, PixelsPerLine ); - - /* Has the block got struck off (no MV and no data - generated after DCT) If not then mark it and the - assosciated MB as coded. */ - if ( cpi->pb.display_fragments[FragIndex] ) { - /* Create linear list of coded block indices */ - cpi->pb.CodedBlockList[cpi->pb.CodedBlockIndex] = FragIndex; - cpi->pb.CodedBlockIndex++; - - /* MB is still coded */ - MBCodedFlag = 1; - cpi->MBCodingMode = cpi->pb.FragCodingMethod[FragIndex]; - - } - } - } - } - /* If the MB is marked as coded and we are in the Y plane then */ - /* the mode list needs to be updated. */ - if ( MBCodedFlag && (FirstSB == 0) ){ - /* Make a note of the selected mode in the mode list */ - cpi->ModeList[cpi->ModeListCount] = cpi->MBCodingMode; - cpi->ModeListCount++; - } - } - } - - SB++; - - } - } - - /* Return number of pixels coded */ - return coded_pixels; -} - -static void EncodeDcTokenList (CP_INSTANCE *cpi) { - ogg_int32_t i,j; - ogg_uint32_t Token; - ogg_uint32_t ExtraBitsToken; - ogg_uint32_t HuffIndex; - - ogg_uint32_t BestDcBits; - ogg_uint32_t DcHuffChoice[2]; - ogg_uint32_t EntropyTableBits[2][DC_HUFF_CHOICES]; - - oggpack_buffer *opb=cpi->oggbuffer; - - /* Clear table data structure */ - memset ( EntropyTableBits, 0, sizeof(ogg_uint32_t)*DC_HUFF_CHOICES*2 ); - - /* Analyse token list to see which is the best entropy table to use */ - for ( i = 0; i < cpi->OptimisedTokenCount; i++ ) { - /* Count number of bits for each table option */ - Token = (ogg_uint32_t)cpi->OptimisedTokenList[i]; - for ( j = 0; j < DC_HUFF_CHOICES; j++ ){ - EntropyTableBits[cpi->OptimisedTokenListPl[i]][j] += - cpi->pb.HuffCodeLengthArray_VP3x[DC_HUFF_OFFSET + j][Token]; - } - } - - /* Work out which table option is best for Y */ - BestDcBits = EntropyTableBits[0][0]; - DcHuffChoice[0] = 0; - for ( j = 1; j < DC_HUFF_CHOICES; j++ ) { - if ( EntropyTableBits[0][j] < BestDcBits ) { - BestDcBits = EntropyTableBits[0][j]; - DcHuffChoice[0] = j; - } - } - - /* Add the DC huffman table choice to the bitstream */ - oggpackB_write( opb, DcHuffChoice[0], DC_HUFF_CHOICE_BITS ); - - /* Work out which table option is best for UV */ - BestDcBits = EntropyTableBits[1][0]; - DcHuffChoice[1] = 0; - for ( j = 1; j < DC_HUFF_CHOICES; j++ ) { - if ( EntropyTableBits[1][j] < BestDcBits ) { - BestDcBits = EntropyTableBits[1][j]; - DcHuffChoice[1] = j; - } - } - - /* Add the DC huffman table choice to the bitstream */ - oggpackB_write( opb, DcHuffChoice[1], DC_HUFF_CHOICE_BITS ); - - /* Encode the token list */ - for ( i = 0; i < cpi->OptimisedTokenCount; i++ ) { - - /* Get the token and extra bits */ - Token = (ogg_uint32_t)cpi->OptimisedTokenList[i]; - ExtraBitsToken = (ogg_uint32_t)cpi->OptimisedTokenListEb[i]; - - /* Select the huffman table */ - if ( cpi->OptimisedTokenListPl[i] == 0) - HuffIndex = (ogg_uint32_t)DC_HUFF_OFFSET + (ogg_uint32_t)DcHuffChoice[0]; - else - HuffIndex = (ogg_uint32_t)DC_HUFF_OFFSET + (ogg_uint32_t)DcHuffChoice[1]; - - /* Add the bits to the encode holding buffer. */ - cpi->FrameBitCount += cpi->pb.HuffCodeLengthArray_VP3x[HuffIndex][Token]; - oggpackB_write( opb, cpi->pb.HuffCodeArray_VP3x[HuffIndex][Token], - (ogg_uint32_t)cpi-> - pb.HuffCodeLengthArray_VP3x[HuffIndex][Token] ); - - /* If the token is followed by an extra bits token then code it */ - if ( cpi->pb.ExtraBitLengths_VP3x[Token] > 0 ) { - /* Add the bits to the encode holding buffer. */ - cpi->FrameBitCount += cpi->pb.ExtraBitLengths_VP3x[Token]; - oggpackB_write( opb, ExtraBitsToken, - (ogg_uint32_t)cpi->pb.ExtraBitLengths_VP3x[Token] ); - } - - } - - /* Reset the count of second order optimised tokens */ - cpi->OptimisedTokenCount = 0; -} - -static void EncodeAcTokenList (CP_INSTANCE *cpi) { - ogg_int32_t i,j; - ogg_uint32_t Token; - ogg_uint32_t ExtraBitsToken; - ogg_uint32_t HuffIndex; - - ogg_uint32_t BestAcBits; - ogg_uint32_t AcHuffChoice[2]; - ogg_uint32_t EntropyTableBits[2][AC_HUFF_CHOICES]; - - oggpack_buffer *opb=cpi->oggbuffer; - - memset ( EntropyTableBits, 0, sizeof(ogg_uint32_t)*AC_HUFF_CHOICES*2 ); - - /* Analyse token list to see which is the best entropy table to use */ - for ( i = 0; i < cpi->OptimisedTokenCount; i++ ) { - /* Count number of bits for each table option */ - Token = (ogg_uint32_t)cpi->OptimisedTokenList[i]; - HuffIndex = cpi->OptimisedTokenListHi[i]; - for ( j = 0; j < AC_HUFF_CHOICES; j++ ) { - EntropyTableBits[cpi->OptimisedTokenListPl[i]][j] += - cpi->pb.HuffCodeLengthArray_VP3x[HuffIndex + j][Token]; - } - } - - /* Select the best set of AC tables for Y */ - BestAcBits = EntropyTableBits[0][0]; - AcHuffChoice[0] = 0; - for ( j = 1; j < AC_HUFF_CHOICES; j++ ) { - if ( EntropyTableBits[0][j] < BestAcBits ) { - BestAcBits = EntropyTableBits[0][j]; - AcHuffChoice[0] = j; - } - } - - /* Add the AC-Y huffman table choice to the bitstream */ - oggpackB_write( opb, AcHuffChoice[0], AC_HUFF_CHOICE_BITS ); - - /* Select the best set of AC tables for UV */ - BestAcBits = EntropyTableBits[1][0]; - AcHuffChoice[1] = 0; - for ( j = 1; j < AC_HUFF_CHOICES; j++ ) { - if ( EntropyTableBits[1][j] < BestAcBits ) { - BestAcBits = EntropyTableBits[1][j]; - AcHuffChoice[1] = j; - } - } - - /* Add the AC-UV huffman table choice to the bitstream */ - oggpackB_write( opb, AcHuffChoice[1], AC_HUFF_CHOICE_BITS ); - - /* Encode the token list */ - for ( i = 0; i < cpi->OptimisedTokenCount; i++ ) { - /* Get the token and extra bits */ - Token = (ogg_uint32_t)cpi->OptimisedTokenList[i]; - ExtraBitsToken = (ogg_uint32_t)cpi->OptimisedTokenListEb[i]; - - /* Select the huffman table */ - HuffIndex = (ogg_uint32_t)cpi->OptimisedTokenListHi[i] + - AcHuffChoice[cpi->OptimisedTokenListPl[i]]; - - /* Add the bits to the encode holding buffer. */ - cpi->FrameBitCount += cpi->pb.HuffCodeLengthArray_VP3x[HuffIndex][Token]; - oggpackB_write( opb, cpi->pb.HuffCodeArray_VP3x[HuffIndex][Token], - (ogg_uint32_t)cpi-> - pb.HuffCodeLengthArray_VP3x[HuffIndex][Token] ); - - /* If the token is followed by an extra bits token then code it */ - if ( cpi->pb.ExtraBitLengths_VP3x[Token] > 0 ) { - /* Add the bits to the encode holding buffer. */ - cpi->FrameBitCount += cpi->pb.ExtraBitLengths_VP3x[Token]; - oggpackB_write( opb, ExtraBitsToken, - (ogg_uint32_t)cpi->pb.ExtraBitLengths_VP3x[Token] ); - } - } - - /* Reset the count of second order optimised tokens */ - cpi->OptimisedTokenCount = 0; -} - -static void PackModes (CP_INSTANCE *cpi) { - ogg_uint32_t i,j; - unsigned char ModeIndex; - const unsigned char *SchemeList; - - unsigned char BestModeSchemes[MAX_MODES]; - ogg_int32_t ModeCount[MAX_MODES]; - ogg_int32_t TmpFreq = -1; - ogg_int32_t TmpIndex = -1; - - ogg_uint32_t BestScheme; - ogg_uint32_t BestSchemeScore; - ogg_uint32_t SchemeScore; - - oggpack_buffer *opb=cpi->oggbuffer; - - /* Build a frequency map for the modes in this frame */ - memset( ModeCount, 0, MAX_MODES*sizeof(ogg_int32_t) ); - for ( i = 0; i < cpi->ModeListCount; i++ ) - ModeCount[cpi->ModeList[i]] ++; - - /* Order the modes from most to least frequent. Store result as - scheme 0 */ - for ( j = 0; j < MAX_MODES; j++ ) { - TmpFreq = -1; /* need to re-initialize for each loop */ - /* Find the most frequent */ - for ( i = 0; i < MAX_MODES; i++ ) { - /* Is this the best scheme so far ??? */ - if ( ModeCount[i] > TmpFreq ) { - TmpFreq = ModeCount[i]; - TmpIndex = i; - } - } - /* I don't know if the above loop ever fails to match, but it's - better safe than sorry. Plus this takes care of gcc warning */ - if ( TmpIndex != -1 ) { - ModeCount[TmpIndex] = -1; - BestModeSchemes[TmpIndex] = (unsigned char)j; - } - } - - /* Default/ fallback scheme uses MODE_BITS bits per mode entry */ - BestScheme = (MODE_METHODS - 1); - BestSchemeScore = cpi->ModeListCount * 3; - /* Get a bit score for the available schemes. */ - for ( j = 0; j < (MODE_METHODS - 1); j++ ) { - - /* Reset the scheme score */ - if ( j == 0 ){ - /* Scheme 0 additional cost of sending frequency order */ - SchemeScore = 24; - SchemeList = BestModeSchemes; - } else { - SchemeScore = 0; - SchemeList = ModeSchemes[j-1]; - } - - /* Find the total bits to code using each avaialable scheme */ - for ( i = 0; i < cpi->ModeListCount; i++ ) - SchemeScore += ModeBitLengths[SchemeList[cpi->ModeList[i]]]; - - /* Is this the best scheme so far ??? */ - if ( SchemeScore < BestSchemeScore ) { - BestSchemeScore = SchemeScore; - BestScheme = j; - } - } - - /* Encode the best scheme. */ - oggpackB_write( opb, BestScheme, (ogg_uint32_t)MODE_METHOD_BITS ); - - /* If the chosen schems is scheme 0 send details of the mode - frequency order */ - if ( BestScheme == 0 ) { - for ( j = 0; j < MAX_MODES; j++ ){ - /* Note that the last two entries are implicit */ - oggpackB_write( opb, BestModeSchemes[j], (ogg_uint32_t)MODE_BITS ); - } - SchemeList = BestModeSchemes; - } - else { - SchemeList = ModeSchemes[BestScheme-1]; - } - - /* Are we using one of the alphabet based schemes or the fallback scheme */ - if ( BestScheme < (MODE_METHODS - 1)) { - /* Pack and encode the Mode list */ - for ( i = 0; i < cpi->ModeListCount; i++) { - /* Add the appropriate mode entropy token. */ - ModeIndex = SchemeList[cpi->ModeList[i]]; - oggpackB_write( opb, ModeBitPatterns[ModeIndex], - (ogg_uint32_t)ModeBitLengths[ModeIndex] ); - } - }else{ - /* Fall back to MODE_BITS per entry */ - for ( i = 0; i < cpi->ModeListCount; i++) - /* Add the appropriate mode entropy token. */ - oggpackB_write( opb, cpi->ModeList[i], MODE_BITS ); - } - -} - -static void PackMotionVectors (CP_INSTANCE *cpi) { - ogg_int32_t i; - ogg_uint32_t MethodBits[2] = {0,0}; - const ogg_uint32_t * MvBitsPtr; - const ogg_uint32_t * MvPatternPtr; - - oggpack_buffer *opb=cpi->oggbuffer; - - /* Choose the coding method */ - MvBitsPtr = &MvBits[MAX_MV_EXTENT]; - for ( i = 0; i < (ogg_int32_t)cpi->MvListCount; i++ ) { - MethodBits[0] += MvBitsPtr[cpi->MVList[i].x]; - MethodBits[0] += MvBitsPtr[cpi->MVList[i].y]; - MethodBits[1] += 12; /* Simple six bits per mv component fallback - mechanism */ - } - - /* Select entropy table */ - if ( MethodBits[0] < MethodBits[1] ) { - oggpackB_write( opb, 0, 1 ); - MvBitsPtr = &MvBits[MAX_MV_EXTENT]; - MvPatternPtr = &MvPattern[MAX_MV_EXTENT]; - }else{ - oggpackB_write( opb, 1, 1 ); - MvBitsPtr = &MvBits2[MAX_MV_EXTENT]; - MvPatternPtr = &MvPattern2[MAX_MV_EXTENT]; - } - - /* Pack and encode the motion vectors */ - for ( i = 0; i < (ogg_int32_t)cpi->MvListCount; i++ ) { - oggpackB_write( opb, MvPatternPtr[cpi->MVList[i].x], - (ogg_uint32_t)MvBitsPtr[cpi->MVList[i].x] ); - oggpackB_write( opb, MvPatternPtr[cpi->MVList[i].y], - (ogg_uint32_t)MvBitsPtr[cpi->MVList[i].y] ); - } - -} - -static void PackEOBRun( CP_INSTANCE *cpi) { - if(cpi->RunLength == 0) - return; - - /* Note the appropriate EOB or EOB run token and any extra bits in - the optimised token list. Use the huffman index assosciated with - the first token in the run */ - - /* Mark out which plane the block belonged to */ - cpi->OptimisedTokenListPl[cpi->OptimisedTokenCount] = - (unsigned char)cpi->RunPlaneIndex; - - /* Note the huffman index to be used */ - cpi->OptimisedTokenListHi[cpi->OptimisedTokenCount] = - (unsigned char)cpi->RunHuffIndex; - - if ( cpi->RunLength <= 3 ) { - if ( cpi->RunLength == 1 ) { - cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = DCT_EOB_TOKEN; - } else if ( cpi->RunLength == 2 ) { - cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = DCT_EOB_PAIR_TOKEN; - } else { - cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = DCT_EOB_TRIPLE_TOKEN; - } - - cpi->RunLength = 0; - - } else { - - /* Choose a token appropriate to the run length. */ - if ( cpi->RunLength < 8 ) { - cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = - DCT_REPEAT_RUN_TOKEN; - cpi->OptimisedTokenListEb[cpi->OptimisedTokenCount] = - cpi->RunLength - 4; - cpi->RunLength = 0; - } else if ( cpi->RunLength < 16 ) { - cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = - DCT_REPEAT_RUN2_TOKEN; - cpi->OptimisedTokenListEb[cpi->OptimisedTokenCount] = - cpi->RunLength - 8; - cpi->RunLength = 0; - } else if ( cpi->RunLength < 32 ) { - cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = - DCT_REPEAT_RUN3_TOKEN; - cpi->OptimisedTokenListEb[cpi->OptimisedTokenCount] = - cpi->RunLength - 16; - cpi->RunLength = 0; - } else if ( cpi->RunLength < 4096) { - cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = - DCT_REPEAT_RUN4_TOKEN; - cpi->OptimisedTokenListEb[cpi->OptimisedTokenCount] = - cpi->RunLength; - cpi->RunLength = 0; - } - - } - - cpi->OptimisedTokenCount++; - /* Reset run EOB length */ - cpi->RunLength = 0; -} - -static void PackToken ( CP_INSTANCE *cpi, ogg_int32_t FragmentNumber, - ogg_uint32_t HuffIndex ) { - ogg_uint32_t Token = - cpi->pb.TokenList[FragmentNumber][cpi->FragTokens[FragmentNumber]]; - ogg_uint32_t ExtraBitsToken = - cpi->pb.TokenList[FragmentNumber][cpi->FragTokens[FragmentNumber] + 1]; - ogg_uint32_t OneOrTwo; - ogg_uint32_t OneOrZero; - - /* Update the record of what coefficient we have got up to for this - block and unpack the encoded token back into the quantised data - array. */ - if ( Token == DCT_EOB_TOKEN ) - cpi->pb.FragCoeffs[FragmentNumber] = BLOCK_SIZE; - else - ExpandToken( cpi->pb.QFragData[FragmentNumber], - &cpi->pb.FragCoeffs[FragmentNumber], - Token, ExtraBitsToken ); - - /* Update record of tokens coded and where we are in this fragment. */ - /* Is there an extra bits token */ - OneOrTwo= 1 + ( cpi->pb.ExtraBitLengths_VP3x[Token] > 0 ); - /* Advance to the next real token. */ - cpi->FragTokens[FragmentNumber] += (unsigned char)OneOrTwo; - - /* Update the counts of tokens coded */ - cpi->TokensCoded += OneOrTwo; - cpi->TokensToBeCoded -= OneOrTwo; - - OneOrZero = ( FragmentNumber < (ogg_int32_t)cpi->pb.YPlaneFragments ); - - if ( Token == DCT_EOB_TOKEN ) { - if ( cpi->RunLength == 0 ) { - cpi->RunHuffIndex = HuffIndex; - cpi->RunPlaneIndex = 1 - OneOrZero; - } - cpi->RunLength++; - - /* we have exceeded our longest run length xmit an eob run token; */ - if ( cpi->RunLength == 4095 ) PackEOBRun(cpi); - - }else{ - - /* If we have an EOB run then code it up first */ - if ( cpi->RunLength > 0 ) PackEOBRun( cpi); - - /* Mark out which plane the block belonged to */ - cpi->OptimisedTokenListPl[cpi->OptimisedTokenCount] = - (unsigned char)(1 - OneOrZero); - - /* Note the token, extra bits and hufman table in the optimised - token list */ - cpi->OptimisedTokenList[cpi->OptimisedTokenCount] = - (unsigned char)Token; - cpi->OptimisedTokenListEb[cpi->OptimisedTokenCount] = - ExtraBitsToken; - cpi->OptimisedTokenListHi[cpi->OptimisedTokenCount] = - (unsigned char)HuffIndex; - - cpi->OptimisedTokenCount++; - } -} - -static ogg_uint32_t GetBlockReconErrorSlow( CP_INSTANCE *cpi, - ogg_int32_t BlockIndex ) { - ogg_uint32_t ErrorVal; - - unsigned char * SrcDataPtr = - &cpi->ConvDestBuffer[cpi->pb.pixel_index_table[BlockIndex]]; - unsigned char * RecDataPtr = - &cpi->pb.LastFrameRecon[cpi->pb.recon_pixel_index_table[BlockIndex]]; - ogg_int32_t SrcStride; - ogg_int32_t RecStride; - - /* Is the block a Y block or a UV block. */ - if ( BlockIndex < (ogg_int32_t)cpi->pb.YPlaneFragments ) { - SrcStride = cpi->pb.info.width; - RecStride = cpi->pb.YStride; - }else{ - SrcStride = cpi->pb.info.width >> 1; - RecStride = cpi->pb.UVStride; - } - - ErrorVal = dsp_sad8x8 (cpi->dsp, SrcDataPtr, SrcStride, RecDataPtr, RecStride); - - return ErrorVal; -} - -static void PackCodedVideo (CP_INSTANCE *cpi) { - ogg_int32_t i; - ogg_int32_t EncodedCoeffs = 1; - ogg_int32_t FragIndex; - ogg_uint32_t HuffIndex; /* Index to group of tables used to code a token */ - - /* Reset the count of second order optimised tokens */ - cpi->OptimisedTokenCount = 0; - - cpi->TokensToBeCoded = cpi->TotTokenCount; - cpi->TokensCoded = 0; - - /* Calculate the bit rate at which this frame should be capped. */ - cpi->MaxBitTarget = (ogg_uint32_t)((double)(cpi->ThisFrameTargetBytes * 8) * - cpi->BitRateCapFactor); - - /* Blank the various fragment data structures before we start. */ - memset(cpi->pb.FragCoeffs, 0, cpi->pb.UnitFragments); - memset(cpi->FragTokens, 0, cpi->pb.UnitFragments); - - /* Clear down the QFragData structure for all coded blocks. */ - ClearDownQFragData(&cpi->pb); - - /* The tree is not needed (implicit) for key frames */ - if ( cpi->pb.FrameType != KEY_FRAME ){ - /* Pack the quad tree fragment mapping. */ - PackAndWriteDFArray( cpi ); - } - - /* Note the number of bits used to code the tree itself. */ - cpi->FrameBitCount = oggpackB_bytes(cpi->oggbuffer) << 3; - - /* Mode and MV data not needed for key frames. */ - if ( cpi->pb.FrameType != KEY_FRAME ){ - /* Pack and code the mode list. */ - PackModes(cpi); - /* Pack the motion vectors */ - PackMotionVectors (cpi); - } - - cpi->FrameBitCount = oggpackB_bytes(cpi->oggbuffer) << 3; - - /* Optimise the DC tokens */ - for ( i = 0; i < cpi->pb.CodedBlockIndex; i++ ) { - /* Get the linear index for the current fragment. */ - FragIndex = cpi->pb.CodedBlockList[i]; - - cpi->pb.FragCoefEOB[FragIndex]=(unsigned char)EncodedCoeffs; - PackToken(cpi, FragIndex, DC_HUFF_OFFSET ); - - } - - /* Pack any outstanding EOB tokens */ - PackEOBRun(cpi); - - /* Now output the optimised DC token list using the appropriate - entropy tables. */ - EncodeDcTokenList(cpi); - - /* Work out the number of DC bits coded */ - - /* Optimise the AC tokens */ - while ( EncodedCoeffs < 64 ) { - /* Huffman table adjustment based upon coefficient number. */ - if ( EncodedCoeffs <= AC_TABLE_2_THRESH ) - HuffIndex = AC_HUFF_OFFSET; - else if ( EncodedCoeffs <= AC_TABLE_3_THRESH ) - HuffIndex = AC_HUFF_OFFSET + AC_HUFF_CHOICES; - else if ( EncodedCoeffs <= AC_TABLE_4_THRESH ) - HuffIndex = AC_HUFF_OFFSET + (AC_HUFF_CHOICES * 2); - else - HuffIndex = AC_HUFF_OFFSET + (AC_HUFF_CHOICES * 3); - - /* Repeatedly scan through the list of blocks. */ - for ( i = 0; i < cpi->pb.CodedBlockIndex; i++ ) { - /* Get the linear index for the current fragment. */ - FragIndex = cpi->pb.CodedBlockList[i]; - - /* Should we code a token for this block on this pass. */ - if ( cpi->FragTokens[FragIndex] < cpi->FragTokenCounts[FragIndex] - && cpi->pb.FragCoeffs[FragIndex] <= EncodedCoeffs ) { - /* Bit pack and a token for this block */ - cpi->pb.FragCoefEOB[FragIndex]=(unsigned char)EncodedCoeffs; - PackToken( cpi, FragIndex, HuffIndex ); - } - } - - EncodedCoeffs ++; - } - - /* Pack any outstanding EOB tokens */ - PackEOBRun(cpi); - - /* Now output the optimised AC token list using the appropriate - entropy tables. */ - EncodeAcTokenList(cpi); - -} - -static ogg_uint32_t QuadCodeDisplayFragments (CP_INSTANCE *cpi) { - ogg_int32_t i,j; - ogg_uint32_t coded_pixels=0; - int QIndex; - int k,m,n; - - /* predictor multiplier up-left, up, up-right,left, shift - Entries are packed in the order L, UL, U, UR, with missing entries - moved to the end (before the shift parameters). */ - static const ogg_int16_t pc[16][6]={ - {0,0,0,0,0,0}, - {1,0,0,0,0,0}, /* PL */ - {1,0,0,0,0,0}, /* PUL */ - {1,0,0,0,0,0}, /* PUL|PL */ - {1,0,0,0,0,0}, /* PU */ - {1,1,0,0,1,1}, /* PU|PL */ - {0,1,0,0,0,0}, /* PU|PUL */ - {29,-26,29,0,5,31}, /* PU|PUL|PL */ - {1,0,0,0,0,0}, /* PUR */ - {75,53,0,0,7,127}, /* PUR|PL */ - {1,1,0,0,1,1}, /* PUR|PUL */ - {75,0,53,0,7,127}, /* PUR|PUL|PL */ - {1,0,0,0,0,0}, /* PUR|PU */ - {75,0,53,0,7,127}, /* PUR|PU|PL */ - {3,10,3,0,4,15}, /* PUR|PU|PUL */ - {29,-26,29,0,5,31} /* PUR|PU|PUL|PL */ - }; - - /* boundary case bit masks. */ - static const int bc_mask[8]={ - /* normal case no boundary condition */ - PUR|PU|PUL|PL, - /* left column */ - PUR|PU, - /* top row */ - PL, - /* top row, left column */ - 0, - /* right column */ - PU|PUL|PL, - /* right and left column */ - PU, - /* top row, right column */ - PL, - /* top row, right and left column */ - 0 - }; - - /* value left value up-left, value up, value up-right, missing - values skipped. */ - int v[4]; - - /* fragment number left, up-left, up, up-right */ - int fn[4]; - - /* predictor count. */ - int pcount; - - /*which predictor constants to use */ - ogg_int16_t wpc; - - /* last used inter predictor (Raster Order) */ - ogg_int16_t Last[3]; /* last value used for given frame */ - - int FragsAcross=cpi->pb.HFragments; - int FragsDown = cpi->pb.VFragments; - int FromFragment,ToFragment; - ogg_int32_t FragIndex; - int WhichFrame; - int WhichCase; - - static const ogg_int16_t Mode2Frame[] = { - 1, /* CODE_INTER_NO_MV 0 => Encoded diff from same MB last frame */ - 0, /* CODE_INTRA 1 => DCT Encoded Block */ - 1, /* CODE_INTER_PLUS_MV 2 => Encoded diff from included MV MB last frame */ - 1, /* CODE_INTER_LAST_MV 3 => Encoded diff from MRU MV MB last frame */ - 1, /* CODE_INTER_PRIOR_MV 4 => Encoded diff from included 4 separate MV blocks */ - 2, /* CODE_USING_GOLDEN 5 => Encoded diff from same MB golden frame */ - 2, /* CODE_GOLDEN_MV 6 => Encoded diff from included MV MB golden frame */ - 1 /* CODE_INTER_FOUR_MV 7 => Encoded diff from included 4 separate MV blocks */ - }; - - ogg_int16_t PredictedDC; - - /* Initialise the coded block indices variables. These allow - subsequent linear access to the quad tree ordered list of coded - blocks */ - cpi->pb.CodedBlockIndex = 0; - - /* Set the inter/intra descision control variables. */ - QIndex = Q_TABLE_SIZE - 1; - while ( QIndex >= 0 ) { - if ( (QIndex == 0) || - ( cpi->pb.QThreshTable[QIndex] >= cpi->pb.ThisFrameQualityValue) ) - break; - QIndex --; - } - - - /* Encode and tokenise the Y, U and V components */ - coded_pixels = QuadCodeComponent(cpi, 0, cpi->pb.YSBRows, cpi->pb.YSBCols, - cpi->pb.info.width ); - coded_pixels += QuadCodeComponent(cpi, cpi->pb.YSuperBlocks, - cpi->pb.UVSBRows, - cpi->pb.UVSBCols, - cpi->pb.info.width>>1 ); - coded_pixels += QuadCodeComponent(cpi, - cpi->pb.YSuperBlocks+cpi->pb.UVSuperBlocks, - cpi->pb.UVSBRows, cpi->pb.UVSBCols, - cpi->pb.info.width>>1 ); - - /* for y,u,v */ - for ( j = 0; j < 3 ; j++) { - /* pick which fragments based on Y, U, V */ - switch(j){ - case 0: /* y */ - FromFragment = 0; - ToFragment = cpi->pb.YPlaneFragments; - FragsAcross = cpi->pb.HFragments; - FragsDown = cpi->pb.VFragments; - break; - case 1: /* u */ - FromFragment = cpi->pb.YPlaneFragments; - ToFragment = cpi->pb.YPlaneFragments + cpi->pb.UVPlaneFragments ; - FragsAcross = cpi->pb.HFragments >> 1; - FragsDown = cpi->pb.VFragments >> 1; - break; - /*case 2: v */ - default: - FromFragment = cpi->pb.YPlaneFragments + cpi->pb.UVPlaneFragments; - ToFragment = cpi->pb.YPlaneFragments + (2 * cpi->pb.UVPlaneFragments) ; - FragsAcross = cpi->pb.HFragments >> 1; - FragsDown = cpi->pb.VFragments >> 1; - break; - } - - /* initialize our array of last used DC Components */ - for(k=0;k<3;k++)Last[k]=0; - i=FromFragment; - - /* do prediction on all of Y, U or V */ - for ( m = 0 ; m < FragsDown ; m++) { - for ( n = 0 ; n < FragsAcross ; n++, i++) { - cpi->OriginalDC[i] = cpi->pb.QFragData[i][0]; - - /* only do 2 prediction if fragment coded and on non intra or - if all fragments are intra */ - if( cpi->pb.display_fragments[i] || - (cpi->pb.FrameType == KEY_FRAME) ) { - /* Type of Fragment */ - - WhichFrame = Mode2Frame[cpi->pb.FragCodingMethod[i]]; - - /* Check Borderline Cases */ - WhichCase = (n==0) + ((m==0) << 1) + ((n+1 == FragsAcross) << 2); - - fn[0]=i-1; - fn[1]=i-FragsAcross-1; - fn[2]=i-FragsAcross; - fn[3]=i-FragsAcross+1; - - /* fragment valid for prediction use if coded and it comes - from same frame as the one we are predicting */ - for(k=pcount=wpc=0; k<4; k++) { - int pflag; - pflag=1<pb.display_fragments[fn[k]] && - (Mode2Frame[cpi->pb.FragCodingMethod[fn[k]]] == WhichFrame)){ - v[pcount]=cpi->OriginalDC[fn[k]]; - wpc|=pflag; - pcount++; - } - } - - if(wpc==0) { - - /* fall back to the last coded fragment */ - cpi->pb.QFragData[i][0] -= Last[WhichFrame]; - - } else { - - /* don't do divide if divisor is 1 or 0 */ - PredictedDC = pc[wpc][0]*v[0]; - for(k=1; k>= pc[wpc][4]; - - } - - /* check for outranging on the two predictors that can outrange */ - if((wpc&(PU|PUL|PL)) == (PU|PUL|PL)){ - if( abs(PredictedDC - v[2]) > 128) { - PredictedDC = v[2]; - } else if( abs(PredictedDC - v[0]) > 128) { - PredictedDC = v[0]; - } else if( abs(PredictedDC - v[1]) > 128) { - PredictedDC = v[1]; - } - } - - cpi->pb.QFragData[i][0] -= PredictedDC; - } - - /* Save the last fragment coded for whatever frame we are - predicting from */ - - Last[WhichFrame] = cpi->OriginalDC[i]; - - } - } - } - } - - /* Pack DC tokens and adjust the ones we couldn't predict 2d */ - for ( i = 0; i < cpi->pb.CodedBlockIndex; i++ ) { - /* Get the linear index for the current coded fragment. */ - FragIndex = cpi->pb.CodedBlockList[i]; - coded_pixels += DPCMTokenizeBlock ( cpi, FragIndex); - - } - - /* Bit pack the video data data */ - PackCodedVideo(cpi); - - /* End the bit packing run. */ - /* EndAddBitsToBuffer(cpi); */ - - /* Reconstruct the reference frames */ - ReconRefFrames(&cpi->pb); - - UpdateFragQIndex(&cpi->pb); - - /* Measure the inter reconstruction error for all the blocks that - were coded */ - /* for use as part of the recovery monitoring process in subsequent frames. */ - for ( i = 0; i < cpi->pb.CodedBlockIndex; i++ ) { - cpi->LastCodedErrorScore[ cpi->pb.CodedBlockList[i] ] = - GetBlockReconErrorSlow( cpi, cpi->pb.CodedBlockList[i] ); - - } - - /* Return total number of coded pixels */ - return coded_pixels; -} - -ogg_uint32_t EncodeData(CP_INSTANCE *cpi){ - ogg_uint32_t coded_pixels = 0; - - /* Zero the count of tokens so far this frame. */ - cpi->TotTokenCount = 0; - - /* Zero the mode and MV list indices. */ - cpi->ModeListCount = 0; - - /* Zero Decoder EOB run count */ - cpi->pb.EOB_Run = 0; - - dsp_save_fpu (cpi->dsp); - - /* Encode any fragments coded using DCT. */ - coded_pixels += QuadCodeDisplayFragments (cpi); - - dsp_restore_fpu (cpi->dsp); - - return coded_pixels; - -} - -ogg_uint32_t PickIntra( CP_INSTANCE *cpi, - ogg_uint32_t SBRows, - ogg_uint32_t SBCols){ - - ogg_int32_t FragIndex; /* Fragment number */ - ogg_uint32_t MB, B; /* Macro-Block, Block indices */ - ogg_uint32_t SBrow; /* Super-Block row number */ - ogg_uint32_t SBcol; /* Super-Block row number */ - ogg_uint32_t SB=0; /* Super-Block index, initialised to first of - this component */ - ogg_uint32_t UVRow; - ogg_uint32_t UVColumn; - ogg_uint32_t UVFragOffset; - - /* decide what block type and motion vectors to use on all of the frames */ - for ( SBrow=0; SBrowpb.BlockMap,SB,MB) >= 0 ) { - - cpi->MBCodingMode = CODE_INTRA; - - /* Now actually code the blocks. */ - for ( B=0; B<4; B++ ) { - FragIndex = QuadMapToIndex1( cpi->pb.BlockMap, SB, MB, B ); - cpi->pb.FragCodingMethod[FragIndex] = cpi->MBCodingMode; - } - - /* Matching fragments in the U and V planes */ - UVRow = (FragIndex / (cpi->pb.HFragments * 2)); - UVColumn = (FragIndex % cpi->pb.HFragments) / 2; - UVFragOffset = (UVRow * (cpi->pb.HFragments / 2)) + UVColumn; - - cpi->pb.FragCodingMethod[cpi->pb.YPlaneFragments + UVFragOffset] = - cpi->MBCodingMode; - cpi->pb.FragCodingMethod[cpi->pb.YPlaneFragments + - cpi->pb.UVPlaneFragments + UVFragOffset] = - cpi->MBCodingMode; - } - } - - /* Next Super-Block */ - SB++; - } - } - return 0; -} - -static void AddMotionVector(CP_INSTANCE *cpi, - MOTION_VECTOR *ThisMotionVector) { - cpi->MVList[cpi->MvListCount].x = ThisMotionVector->x; - cpi->MVList[cpi->MvListCount].y = ThisMotionVector->y; - cpi->MvListCount++; -} - -static void SetFragMotionVectorAndMode(CP_INSTANCE *cpi, - ogg_int32_t FragIndex, - MOTION_VECTOR *ThisMotionVector){ - /* Note the coding mode and vector for each block */ - cpi->pb.FragMVect[FragIndex].x = ThisMotionVector->x; - cpi->pb.FragMVect[FragIndex].y = ThisMotionVector->y; - cpi->pb.FragCodingMethod[FragIndex] = cpi->MBCodingMode; -} - -static void SetMBMotionVectorsAndMode(CP_INSTANCE *cpi, - ogg_int32_t YFragIndex, - ogg_int32_t UFragIndex, - ogg_int32_t VFragIndex, - MOTION_VECTOR *ThisMotionVector){ - SetFragMotionVectorAndMode(cpi, YFragIndex, ThisMotionVector); - SetFragMotionVectorAndMode(cpi, YFragIndex + 1, ThisMotionVector); - SetFragMotionVectorAndMode(cpi, YFragIndex + cpi->pb.HFragments, - ThisMotionVector); - SetFragMotionVectorAndMode(cpi, YFragIndex + cpi->pb.HFragments + 1, - ThisMotionVector); - SetFragMotionVectorAndMode(cpi, UFragIndex, ThisMotionVector); - SetFragMotionVectorAndMode(cpi, VFragIndex, ThisMotionVector); -} - -ogg_uint32_t PickModes(CP_INSTANCE *cpi, - ogg_uint32_t SBRows, ogg_uint32_t SBCols, - ogg_uint32_t PixelsPerLine, - ogg_uint32_t *InterError, ogg_uint32_t *IntraError) { - ogg_int32_t YFragIndex; - ogg_int32_t UFragIndex; - ogg_int32_t VFragIndex; - ogg_uint32_t MB, B; /* Macro-Block, Block indices */ - ogg_uint32_t SBrow; /* Super-Block row number */ - ogg_uint32_t SBcol; /* Super-Block row number */ - ogg_uint32_t SB=0; /* Super-Block index, initialised to first - of this component */ - - ogg_uint32_t MBIntraError; /* Intra error for macro block */ - ogg_uint32_t MBGFError; /* Golden frame macro block error */ - ogg_uint32_t MBGF_MVError; /* Golden frame plus MV error */ - ogg_uint32_t LastMBGF_MVError; /* Golden frame error with - last used GF motion - vector. */ - ogg_uint32_t MBInterError; /* Inter no MV macro block error */ - ogg_uint32_t MBLastInterError; /* Inter with last used MV */ - ogg_uint32_t MBPriorLastInterError; /* Inter with prior last MV */ - ogg_uint32_t MBInterMVError; /* Inter MV macro block error */ - ogg_uint32_t MBInterMVExError; /* Inter MV (exhaustive - search) macro block error */ - ogg_uint32_t MBInterFOURMVError; /* Inter MV error when using 4 - motion vectors per macro - block */ - ogg_uint32_t BestError; /* Best error so far. */ - - MOTION_VECTOR FourMVect[6]; /* storage for last used vectors (one - entry for each block in MB) */ - MOTION_VECTOR LastInterMVect; /* storage for last used Inter frame - MB motion vector */ - MOTION_VECTOR PriorLastInterMVect; /* storage for prior last used - Inter frame MB motion vector */ - MOTION_VECTOR TmpMVect; /* Temporary MV storage */ - MOTION_VECTOR LastGFMVect; /* storage for last used Golden - Frame MB motion vector */ - MOTION_VECTOR InterMVect; /* storage for motion vector */ - MOTION_VECTOR InterMVectEx; /* storage for motion vector result - from exhaustive search */ - MOTION_VECTOR GFMVect; /* storage for motion vector */ - MOTION_VECTOR ZeroVect; - - ogg_uint32_t UVRow; - ogg_uint32_t UVColumn; - ogg_uint32_t UVFragOffset; - - int MBCodedFlag; - unsigned char QIndex; - - /* initialize error scores */ - *InterError = 0; - *IntraError = 0; - - /* clear down the default motion vector. */ - cpi->MvListCount = 0; - FourMVect[0].x = 0; - FourMVect[0].y = 0; - FourMVect[1].x = 0; - FourMVect[1].y = 0; - FourMVect[2].x = 0; - FourMVect[2].y = 0; - FourMVect[3].x = 0; - FourMVect[3].y = 0; - FourMVect[4].x = 0; - FourMVect[4].y = 0; - FourMVect[5].x = 0; - FourMVect[5].y = 0; - LastInterMVect.x = 0; - LastInterMVect.y = 0; - PriorLastInterMVect.x = 0; - PriorLastInterMVect.y = 0; - LastGFMVect.x = 0; - LastGFMVect.y = 0; - InterMVect.x = 0; - InterMVect.y = 0; - GFMVect.x = 0; - GFMVect.y = 0; - - ZeroVect.x = 0; - ZeroVect.y = 0; - - QIndex = (unsigned char)cpi->pb.FrameQIndex; - - - /* change the quatization matrix to the one at best Q to compute the - new error score */ - cpi->MinImprovementForNewMV = (MvThreshTable[QIndex] << 12); - cpi->InterTripOutThresh = (5000<<12); - cpi->MVChangeFactor = MVChangeFactorTable[QIndex]; /* 0.9 */ - - if ( cpi->pb.info.quick_p ) { - cpi->ExhaustiveSearchThresh = (1000<<12); - cpi->FourMVThreshold = (2500<<12); - } else { - cpi->ExhaustiveSearchThresh = (250<<12); - cpi->FourMVThreshold = (500<<12); - } - cpi->MinImprovementForFourMV = cpi->MinImprovementForNewMV * 4; - - if(cpi->MinImprovementForFourMV < (40<<12)) - cpi->MinImprovementForFourMV = (40<<12); - - cpi->FourMvChangeFactor = 8; /* cpi->MVChangeFactor - 0.05; */ - - /* decide what block type and motion vectors to use on all of the frames */ - for ( SBrow=0; SBrowpb.BlockMap,SB,MB) < 0 ) continue; - - /* Is the current macro block coded (in part or in whole) */ - MBCodedFlag = 0; - for ( B=0; B<4; B++ ) { - YFragIndex = QuadMapToIndex1( cpi->pb.BlockMap, SB, MB, B ); - - /* Does Block lie in frame: */ - if ( YFragIndex >= 0 ) { - /* In Frame: Is it coded: */ - if ( cpi->pb.display_fragments[YFragIndex] ) { - MBCodedFlag = 1; - break; - } - } else - MBCodedFlag = 0; - } - - /* This one isn't coded go to the next one */ - if(!MBCodedFlag) continue; - - /* Calculate U and V FragIndex from YFragIndex */ - YFragIndex = QuadMapToMBTopLeft(cpi->pb.BlockMap, SB,MB); - UVRow = (YFragIndex / (cpi->pb.HFragments * 2)); - UVColumn = (YFragIndex % cpi->pb.HFragments) / 2; - UVFragOffset = (UVRow * (cpi->pb.HFragments / 2)) + UVColumn; - UFragIndex = cpi->pb.YPlaneFragments + UVFragOffset; - VFragIndex = cpi->pb.YPlaneFragments + cpi->pb.UVPlaneFragments + - UVFragOffset; - - - /************************************************************** - Find the block choice with the lowest error - - NOTE THAT if U or V is coded but no Y from a macro block then - the mode will be CODE_INTER_NO_MV as this is the default - state to which the mode data structure is initialised in - encoder and decoder at the start of each frame. */ - - BestError = HUGE_ERROR; - - - /* Look at the intra coding error. */ - MBIntraError = GetMBIntraError( cpi, YFragIndex, PixelsPerLine ); - BestError = (BestError > MBIntraError) ? MBIntraError : BestError; - - /* Get the golden frame error */ - MBGFError = GetMBInterError( cpi, cpi->ConvDestBuffer, - cpi->pb.GoldenFrame, YFragIndex, - 0, 0, PixelsPerLine ); - BestError = (BestError > MBGFError) ? MBGFError : BestError; - - /* Calculate the 0,0 case. */ - MBInterError = GetMBInterError( cpi, cpi->ConvDestBuffer, - cpi->pb.LastFrameRecon, - YFragIndex, 0, 0, PixelsPerLine ); - BestError = (BestError > MBInterError) ? MBInterError : BestError; - - /* Measure error for last MV */ - MBLastInterError = GetMBInterError( cpi, cpi->ConvDestBuffer, - cpi->pb.LastFrameRecon, - YFragIndex, LastInterMVect.x, - LastInterMVect.y, PixelsPerLine ); - BestError = (BestError > MBLastInterError) ? - MBLastInterError : BestError; - - /* Measure error for prior last MV */ - MBPriorLastInterError = GetMBInterError( cpi, cpi->ConvDestBuffer, - cpi->pb.LastFrameRecon, - YFragIndex, - PriorLastInterMVect.x, - PriorLastInterMVect.y, - PixelsPerLine ); - BestError = (BestError > MBPriorLastInterError) ? - MBPriorLastInterError : BestError; - - /* Temporarily force usage of no motionvector blocks */ - MBInterMVError = HUGE_ERROR; - InterMVect.x = 0; /* Set 0,0 motion vector */ - InterMVect.y = 0; - - /* If the best error is above the required threshold search - for a new inter MV */ - if ( BestError > cpi->MinImprovementForNewMV && cpi->MotionCompensation) { - /* Use a mix of heirachical and exhaustive searches for - quick mode. */ - if ( cpi->pb.info.quick_p ) { - MBInterMVError = GetMBMVInterError( cpi, cpi->pb.LastFrameRecon, - YFragIndex, PixelsPerLine, - cpi->MVPixelOffsetY, - &InterMVect ); - - /* If we still do not have a good match try an exhaustive - MBMV search */ - if ( (MBInterMVError > cpi->ExhaustiveSearchThresh) && - (BestError > cpi->ExhaustiveSearchThresh) ) { - - MBInterMVExError = - GetMBMVExhaustiveSearch( cpi, cpi->pb.LastFrameRecon, - YFragIndex, PixelsPerLine, - &InterMVectEx ); - - /* Is the Variance measure for the EX search - better... If so then use it. */ - if ( MBInterMVExError < MBInterMVError ) { - MBInterMVError = MBInterMVExError; - InterMVect.x = InterMVectEx.x; - InterMVect.y = InterMVectEx.y; - } - } - }else{ - /* Use an exhaustive search */ - MBInterMVError = - GetMBMVExhaustiveSearch( cpi, cpi->pb.LastFrameRecon, - YFragIndex, PixelsPerLine, - &InterMVect ); - } - - - /* Is the improvement, if any, good enough to justify a new MV */ - if ( (16 * MBInterMVError < (BestError * cpi->MVChangeFactor)) && - ((MBInterMVError + cpi->MinImprovementForNewMV) < BestError) ){ - BestError = MBInterMVError; - } - - } - - /* If the best error is still above the required threshold - search for a golden frame MV */ - MBGF_MVError = HUGE_ERROR; - GFMVect.x = 0; /* Set 0,0 motion vector */ - GFMVect.y = 0; - if ( BestError > cpi->MinImprovementForNewMV && cpi->MotionCompensation) { - /* Do an MV search in the golden reference frame */ - MBGF_MVError = GetMBMVInterError( cpi, cpi->pb.GoldenFrame, - YFragIndex, PixelsPerLine, - cpi->MVPixelOffsetY, &GFMVect ); - - /* Measure error for last GFMV */ - LastMBGF_MVError = GetMBInterError( cpi, cpi->ConvDestBuffer, - cpi->pb.GoldenFrame, - YFragIndex, LastGFMVect.x, - LastGFMVect.y, PixelsPerLine ); - - /* Check against last GF motion vector and reset if the - search has thrown a worse result. */ - if ( LastMBGF_MVError < MBGF_MVError ) { - GFMVect.x = LastGFMVect.x; - GFMVect.y = LastGFMVect.y; - MBGF_MVError = LastMBGF_MVError; - }else{ - LastGFMVect.x = GFMVect.x; - LastGFMVect.y = GFMVect.y; - } - - /* Is the improvement, if any, good enough to justify a new MV */ - if ( (16 * MBGF_MVError < (BestError * cpi->MVChangeFactor)) && - ((MBGF_MVError + cpi->MinImprovementForNewMV) < BestError) ) { - BestError = MBGF_MVError; - } - } - - /* Finally... If the best error is still to high then consider - the 4MV mode */ - MBInterFOURMVError = HUGE_ERROR; - if ( BestError > cpi->FourMVThreshold && cpi->MotionCompensation) { - /* Get the 4MV error. */ - MBInterFOURMVError = - GetFOURMVExhaustiveSearch( cpi, cpi->pb.LastFrameRecon, - YFragIndex, PixelsPerLine, FourMVect ); - - /* If the improvement is great enough then use the four MV mode */ - if ( ((MBInterFOURMVError + cpi->MinImprovementForFourMV) < - BestError) && (16 * MBInterFOURMVError < - (BestError * cpi->FourMvChangeFactor))) { - BestError = MBInterFOURMVError; - } - } - - /******************************************************** - end finding the best error - ******************************************************* - - Figure out what to do with the block we chose - - Over-ride and force intra if error high and Intra error similar - Now choose a mode based on lowest error (with bias towards no MV) */ - - if ( (BestError > cpi->InterTripOutThresh) && - (10 * BestError > MBIntraError * 7 ) ) { - cpi->MBCodingMode = CODE_INTRA; - SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, - VFragIndex,&ZeroVect); - } else if ( BestError == MBInterError ) { - cpi->MBCodingMode = CODE_INTER_NO_MV; - SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, - VFragIndex,&ZeroVect); - } else if ( BestError == MBGFError ) { - cpi->MBCodingMode = CODE_USING_GOLDEN; - SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, - VFragIndex,&ZeroVect); - } else if ( BestError == MBLastInterError ) { - cpi->MBCodingMode = CODE_INTER_LAST_MV; - SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, - VFragIndex,&LastInterMVect); - } else if ( BestError == MBPriorLastInterError ) { - cpi->MBCodingMode = CODE_INTER_PRIOR_LAST; - SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, - VFragIndex,&PriorLastInterMVect); - - /* Swap the prior and last MV cases over */ - TmpMVect.x = PriorLastInterMVect.x; - TmpMVect.y = PriorLastInterMVect.y; - PriorLastInterMVect.x = LastInterMVect.x; - PriorLastInterMVect.y = LastInterMVect.y; - LastInterMVect.x = TmpMVect.x; - LastInterMVect.y = TmpMVect.y; - - } else if ( BestError == MBInterMVError ) { - - cpi->MBCodingMode = CODE_INTER_PLUS_MV; - SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, - VFragIndex,&InterMVect); - - /* Update Prior last mv with last mv */ - PriorLastInterMVect.x = LastInterMVect.x; - PriorLastInterMVect.y = LastInterMVect.y; - - /* Note last inter MV for future use */ - LastInterMVect.x = InterMVect.x; - LastInterMVect.y = InterMVect.y; - - AddMotionVector( cpi, &InterMVect); - - } else if ( BestError == MBGF_MVError ) { - - cpi->MBCodingMode = CODE_GOLDEN_MV; - SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, - VFragIndex,&GFMVect); - - /* Note last inter GF MV for future use */ - LastGFMVect.x = GFMVect.x; - LastGFMVect.y = GFMVect.y; - - AddMotionVector( cpi, &GFMVect); - } else if ( BestError == MBInterFOURMVError ) { - cpi->MBCodingMode = CODE_INTER_FOURMV; - - /* Calculate the UV vectors as the average of the Y plane ones. */ - /* First .x component */ - FourMVect[4].x = FourMVect[0].x + FourMVect[1].x + - FourMVect[2].x + FourMVect[3].x; - if ( FourMVect[4].x >= 0 ) - FourMVect[4].x = (FourMVect[4].x + 2) / 4; - else - FourMVect[4].x = (FourMVect[4].x - 2) / 4; - FourMVect[5].x = FourMVect[4].x; - - /* Then .y component */ - FourMVect[4].y = FourMVect[0].y + FourMVect[1].y + - FourMVect[2].y + FourMVect[3].y; - if ( FourMVect[4].y >= 0 ) - FourMVect[4].y = (FourMVect[4].y + 2) / 4; - else - FourMVect[4].y = (FourMVect[4].y - 2) / 4; - FourMVect[5].y = FourMVect[4].y; - - SetFragMotionVectorAndMode(cpi, YFragIndex, &FourMVect[0]); - SetFragMotionVectorAndMode(cpi, YFragIndex + 1, &FourMVect[1]); - SetFragMotionVectorAndMode(cpi, YFragIndex + cpi->pb.HFragments, - &FourMVect[2]); - SetFragMotionVectorAndMode(cpi, YFragIndex + cpi->pb.HFragments + 1, - &FourMVect[3]); - SetFragMotionVectorAndMode(cpi, UFragIndex, &FourMVect[4]); - SetFragMotionVectorAndMode(cpi, VFragIndex, &FourMVect[5]); - - /* Note the four MVs values for current macro-block. */ - AddMotionVector( cpi, &FourMVect[0]); - AddMotionVector( cpi, &FourMVect[1]); - AddMotionVector( cpi, &FourMVect[2]); - AddMotionVector( cpi, &FourMVect[3]); - - /* Update Prior last mv with last mv */ - PriorLastInterMVect.x = LastInterMVect.x; - PriorLastInterMVect.y = LastInterMVect.y; - - /* Note last inter MV for future use */ - LastInterMVect.x = FourMVect[3].x; - LastInterMVect.y = FourMVect[3].y; - - } else { - - cpi->MBCodingMode = CODE_INTRA; - SetMBMotionVectorsAndMode(cpi,YFragIndex,UFragIndex, - VFragIndex,&ZeroVect); - } - - - /* setting up mode specific block types - *******************************************************/ - - *InterError += (BestError>>8); - *IntraError += (MBIntraError>>8); - - - } - SB++; - - } - } - - /* Return number of pixels coded */ - return 0; -} - -void WriteFrameHeader( CP_INSTANCE *cpi) { - ogg_uint32_t i; - oggpack_buffer *opb=cpi->oggbuffer; - /* Output the frame type (base/key frame or inter frame) */ - oggpackB_write( opb, cpi->pb.FrameType, 1 ); - /* Write out details of the current value of Q... variable resolution. */ - for ( i = 0; i < Q_TABLE_SIZE; i++ ) { - if ( cpi->pb.ThisFrameQualityValue == cpi->pb.QThreshTable[i] ) { - oggpackB_write( opb, i, 6 ); - break; - } - } - - if ( i == Q_TABLE_SIZE ) { - /* An invalid DCT value was specified. */ - /*IssueWarning( "Invalid Q Multiplier" );*/ - oggpackB_write( opb, 31, 6 ); - } - - /* we only support one Q index per frame */ - oggpackB_write( opb, 0, 1 ); - - /* If the frame was a base frame then write out the frame dimensions. */ - if ( cpi->pb.FrameType == KEY_FRAME ) { - /* all bits reserved! */ - oggpackB_write( opb, 0, 3 ); - } -} - diff --git a/Engine/lib/libtheora/lib/enc/encoder_huffman.c b/Engine/lib/libtheora/lib/enc/encoder_huffman.c deleted file mode 100644 index 191ada75c..000000000 --- a/Engine/lib/libtheora/lib/enc/encoder_huffman.c +++ /dev/null @@ -1,310 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: encoder_huffman.c 13884 2007-09-22 08:38:10Z giles $ - - ********************************************************************/ - -#include -#include -#include "codec_internal.h" -#include "hufftables.h" - -static void CreateHuffmanList(HUFF_ENTRY ** HuffRoot, - ogg_uint32_t HIndex, - const ogg_uint32_t *FreqList ) { - int i; - HUFF_ENTRY *entry_ptr; - HUFF_ENTRY *search_ptr; - - /* Create a HUFF entry for token zero. */ - HuffRoot[HIndex] = (HUFF_ENTRY *)_ogg_calloc(1,sizeof(*HuffRoot[HIndex])); - - HuffRoot[HIndex]->Previous = NULL; - HuffRoot[HIndex]->Next = NULL; - HuffRoot[HIndex]->ZeroChild = NULL; - HuffRoot[HIndex]->OneChild = NULL; - HuffRoot[HIndex]->Value = 0; - HuffRoot[HIndex]->Frequency = FreqList[0]; - - if ( HuffRoot[HIndex]->Frequency == 0 ) - HuffRoot[HIndex]->Frequency = 1; - - /* Now add entries for all the other possible tokens. */ - for ( i = 1; i < MAX_ENTROPY_TOKENS; i++ ) { - entry_ptr = (HUFF_ENTRY *)_ogg_calloc(1,sizeof(*entry_ptr)); - - entry_ptr->Value = i; - entry_ptr->Frequency = FreqList[i]; - entry_ptr->ZeroChild = NULL; - entry_ptr->OneChild = NULL; - - /* Force min value of 1. This prevents the tree getting too deep. */ - if ( entry_ptr->Frequency == 0 ) - entry_ptr->Frequency = 1; - - if ( entry_ptr->Frequency <= HuffRoot[HIndex]->Frequency ){ - entry_ptr->Next = HuffRoot[HIndex]; - HuffRoot[HIndex]->Previous = entry_ptr; - entry_ptr->Previous = NULL; - HuffRoot[HIndex] = entry_ptr; - }else{ - search_ptr = HuffRoot[HIndex]; - while ( (search_ptr->Next != NULL) && - (search_ptr->Frequency < entry_ptr->Frequency) ){ - search_ptr = (HUFF_ENTRY *)search_ptr->Next; - } - - if ( search_ptr->Frequency < entry_ptr->Frequency ){ - entry_ptr->Next = NULL; - entry_ptr->Previous = search_ptr; - search_ptr->Next = entry_ptr; - }else{ - entry_ptr->Next = search_ptr; - entry_ptr->Previous = search_ptr->Previous; - search_ptr->Previous->Next = entry_ptr; - search_ptr->Previous = entry_ptr; - } - } - } -} - -static void CreateCodeArray( HUFF_ENTRY * HuffRoot, - ogg_uint32_t *HuffCodeArray, - unsigned char *HuffCodeLengthArray, - ogg_uint32_t CodeValue, - unsigned char CodeLength ) { - - /* If we are at a leaf then fill in a code array entry. */ - if ( ( HuffRoot->ZeroChild == NULL ) && ( HuffRoot->OneChild == NULL ) ){ - HuffCodeArray[HuffRoot->Value] = CodeValue; - HuffCodeLengthArray[HuffRoot->Value] = CodeLength; - }else{ - /* Recursive calls to scan down the tree. */ - CodeLength++; - CreateCodeArray(HuffRoot->ZeroChild, HuffCodeArray, HuffCodeLengthArray, - ((CodeValue << 1) + 0), CodeLength); - CreateCodeArray(HuffRoot->OneChild, HuffCodeArray, HuffCodeLengthArray, - ((CodeValue << 1) + 1), CodeLength); - } -} - -static void BuildHuffmanTree( HUFF_ENTRY **HuffRoot, - ogg_uint32_t *HuffCodeArray, - unsigned char *HuffCodeLengthArray, - ogg_uint32_t HIndex, - const ogg_uint32_t *FreqList ){ - - HUFF_ENTRY *entry_ptr; - HUFF_ENTRY *search_ptr; - - /* First create a sorted linked list representing the frequencies of - each token. */ - CreateHuffmanList( HuffRoot, HIndex, FreqList ); - - /* Now build the tree from the list. */ - - /* While there are at least two items left in the list. */ - while ( HuffRoot[HIndex]->Next != NULL ){ - /* Create the new node as the parent of the first two in the list. */ - entry_ptr = (HUFF_ENTRY *)_ogg_calloc(1,sizeof(*entry_ptr)); - entry_ptr->Value = -1; - entry_ptr->Frequency = HuffRoot[HIndex]->Frequency + - HuffRoot[HIndex]->Next->Frequency ; - entry_ptr->ZeroChild = HuffRoot[HIndex]; - entry_ptr->OneChild = HuffRoot[HIndex]->Next; - - /* If there are still more items in the list then insert the new - node into the list. */ - if (entry_ptr->OneChild->Next != NULL ){ - /* Set up the provisional 'new root' */ - HuffRoot[HIndex] = entry_ptr->OneChild->Next; - HuffRoot[HIndex]->Previous = NULL; - - /* Now scan through the remaining list to insert the new entry - at the appropriate point. */ - if ( entry_ptr->Frequency <= HuffRoot[HIndex]->Frequency ){ - entry_ptr->Next = HuffRoot[HIndex]; - HuffRoot[HIndex]->Previous = entry_ptr; - entry_ptr->Previous = NULL; - HuffRoot[HIndex] = entry_ptr; - }else{ - search_ptr = HuffRoot[HIndex]; - while ( (search_ptr->Next != NULL) && - (search_ptr->Frequency < entry_ptr->Frequency) ){ - search_ptr = search_ptr->Next; - } - - if ( search_ptr->Frequency < entry_ptr->Frequency ){ - entry_ptr->Next = NULL; - entry_ptr->Previous = search_ptr; - search_ptr->Next = entry_ptr; - }else{ - entry_ptr->Next = search_ptr; - entry_ptr->Previous = search_ptr->Previous; - search_ptr->Previous->Next = entry_ptr; - search_ptr->Previous = entry_ptr; - } - } - }else{ - /* Build has finished. */ - entry_ptr->Next = NULL; - entry_ptr->Previous = NULL; - HuffRoot[HIndex] = entry_ptr; - } - - /* Delete the Next/Previous properties of the children (PROB NOT NEC). */ - entry_ptr->ZeroChild->Next = NULL; - entry_ptr->ZeroChild->Previous = NULL; - entry_ptr->OneChild->Next = NULL; - entry_ptr->OneChild->Previous = NULL; - - } - - /* Now build a code array from the tree. */ - CreateCodeArray( HuffRoot[HIndex], HuffCodeArray, - HuffCodeLengthArray, 0, 0); -} - -static void DestroyHuffTree(HUFF_ENTRY *root_ptr){ - if (root_ptr){ - if ( root_ptr->ZeroChild ) - DestroyHuffTree(root_ptr->ZeroChild); - - if ( root_ptr->OneChild ) - DestroyHuffTree(root_ptr->OneChild); - - _ogg_free(root_ptr); - } -} - -void ClearHuffmanSet( PB_INSTANCE *pbi ){ - int i; - - ClearHuffmanTrees(pbi->HuffRoot_VP3x); - - for ( i = 0; i < NUM_HUFF_TABLES; i++ ) - if (pbi->HuffCodeArray_VP3x[i]) - _ogg_free (pbi->HuffCodeArray_VP3x[i]); - - for ( i = 0; i < NUM_HUFF_TABLES; i++ ) - if (pbi->HuffCodeLengthArray_VP3x[i]) - _ogg_free (pbi->HuffCodeLengthArray_VP3x[i]); -} - -void InitHuffmanSet( PB_INSTANCE *pbi ){ - int i; - - ClearHuffmanSet(pbi); - - pbi->ExtraBitLengths_VP3x = ExtraBitLengths_VP31; - - for ( i = 0; i < NUM_HUFF_TABLES; i++ ){ - pbi->HuffCodeArray_VP3x[i] = - _ogg_calloc(MAX_ENTROPY_TOKENS, - sizeof(*pbi->HuffCodeArray_VP3x[i])); - pbi->HuffCodeLengthArray_VP3x[i] = - _ogg_calloc(MAX_ENTROPY_TOKENS, - sizeof(*pbi->HuffCodeLengthArray_VP3x[i])); - BuildHuffmanTree( pbi->HuffRoot_VP3x, - pbi->HuffCodeArray_VP3x[i], - pbi->HuffCodeLengthArray_VP3x[i], - i, FrequencyCounts_VP3[i]); - } -} - -static int ReadHuffTree(HUFF_ENTRY * HuffRoot, int depth, - oggpack_buffer *opb) { - long bit; - long ret; - theora_read(opb,1,&bit); - if(bit < 0) return OC_BADHEADER; - else if(!bit) { - int ret; - if (++depth > 32) return OC_BADHEADER; - HuffRoot->ZeroChild = (HUFF_ENTRY *)_ogg_calloc(1, sizeof(HUFF_ENTRY)); - ret = ReadHuffTree(HuffRoot->ZeroChild, depth, opb); - if (ret < 0) return ret; - HuffRoot->OneChild = (HUFF_ENTRY *)_ogg_calloc(1, sizeof(HUFF_ENTRY)); - ret = ReadHuffTree(HuffRoot->OneChild, depth, opb); - if (ret < 0) return ret; - HuffRoot->Value = -1; - } else { - HuffRoot->ZeroChild = NULL; - HuffRoot->OneChild = NULL; - theora_read(opb,5,&ret); - HuffRoot->Value=ret;; - if (HuffRoot->Value < 0) return OC_BADHEADER; - } - return 0; -} - -int ReadHuffmanTrees(codec_setup_info *ci, oggpack_buffer *opb) { - int i; - for (i=0; iHuffRoot[i] = (HUFF_ENTRY *)_ogg_calloc(1, sizeof(HUFF_ENTRY)); - ret = ReadHuffTree(ci->HuffRoot[i], 0, opb); - if (ret) return ret; - } - return 0; -} - -static void WriteHuffTree(HUFF_ENTRY *HuffRoot, oggpack_buffer *opb) { - if (HuffRoot->Value >= 0) { - oggpackB_write(opb, 1, 1); - oggpackB_write(opb, HuffRoot->Value, 5); - } else { - oggpackB_write(opb, 0, 1); - WriteHuffTree(HuffRoot->ZeroChild, opb); - WriteHuffTree(HuffRoot->OneChild, opb); - } -} - -void WriteHuffmanTrees(HUFF_ENTRY *HuffRoot[NUM_HUFF_TABLES], - oggpack_buffer *opb) { - int i; - for(i=0; iValue = HuffSrc->Value; - if (HuffSrc->Value < 0) { - HuffDst->ZeroChild = CopyHuffTree(HuffSrc->ZeroChild); - HuffDst->OneChild = CopyHuffTree(HuffSrc->OneChild); - } - return HuffDst; - } - return NULL; -} - -void InitHuffmanTrees(PB_INSTANCE *pbi, const codec_setup_info *ci) { - int i; - pbi->ExtraBitLengths_VP3x = ExtraBitLengths_VP31; - for(i=0; iHuffRoot_VP3x[i] = CopyHuffTree(ci->HuffRoot[i]); - } -} - -void ClearHuffmanTrees(HUFF_ENTRY *HuffRoot[NUM_HUFF_TABLES]){ - int i; - for(i=0; i -#include "codec_internal.h" - -#include "quant_lookup.h" - -#define IdctAdjustBeforeShift 8 -/* cos(n*pi/16) or sin(8-n)*pi/16) */ -#define xC1S7 64277 -#define xC2S6 60547 -#define xC3S5 54491 -#define xC4S4 46341 -#define xC5S3 36410 -#define xC6S2 25080 -#define xC7S1 12785 - -/* compute the 16 bit signed 1D inverse DCT - spec version */ -/* -static void idct_short__c ( ogg_int16_t * InputData, ogg_int16_t * OutputData ) { - ogg_int32_t t[8], r; - ogg_int16_t *y = InputData; - ogg_int16_t *x = OutputData; - - t[0] = y[0] + y[4]; - t[0] &= 0xffff; - t[0] = (xC4S4 * t[0]) >> 16; - - t[1] = y[0] - y[4]; - t[1] &= 0xffff; - t[1] = (xC4S4 * t[1]) >> 16; - - t[2] = ((xC6S2 * y[2]) >> 16) - ((xC2S6 * y[6]) >> 16); - t[3] = ((xC2S6 * y[2]) >> 16) + ((xC6S2 * y[6]) >> 16); - t[4] = ((xC7S1 * y[1]) >> 16) - ((xC1S7 * y[7]) >> 16); - t[5] = ((xC3S5 * y[5]) >> 16) - ((xC5S3 * y[3]) >> 16); - t[6] = ((xC5S3 * y[5]) >> 16) + ((xC3S5 * y[3]) >> 16); - t[7] = ((xC1S7 * y[1]) >> 16) + ((xC7S1 * y[7]) >> 16); - - r = t[4] + t[5]; - t[5] = t[4] - t[5]; - t[5] &= 0xffff; - t[5] = (xC4S4 * (-t[5])) >> 16; - t[4] = r; - - r = t[7] + t[6]; - t[6] = t[7] - t[6]; - t[6] &= 0xffff; - t[6] = (xC4S4 * t[6]) >> 16; - t[7] = r; - - r = t[0] + t[3]; - t[3] = t[0] - t[3]; - t[0] = r; - - r = t[1] + t[2]; - t[2] = t[1] - t[2]; - t[1] = r; - - r = t[6] + t[5]; - t[5] = t[6] - t[5]; - t[6] = r; - - r = t[0] + t[7]; - r &= 0xffff; - x[0] = r; - - r = t[1] + t[6]; - r &= 0xffff; - x[1] = r; - - r = t[2] + t[5]; - r &= 0xffff; - x[2] = r; - - r = t[3] + t[4]; - r &= 0xffff; - x[3] = r; - - r = t[3] - t[4]; - r &= 0xffff; - x[4] = r; - - r = t[2] - t[5]; - r &= 0xffff; - x[5] = r; - - r = t[1] - t[6]; - r &= 0xffff; - x[6] = r; - - r = t[0] - t[7]; - r &= 0xffff; - x[7] = r; - -} -*/ - -static void dequant_slow( ogg_int16_t * dequant_coeffs, - ogg_int16_t * quantized_list, - ogg_int32_t * DCT_block) { - int i; - for(i=0;i<64;i++) - DCT_block[dezigzag_index[i]] = quantized_list[i] * dequant_coeffs[i]; -} - - - -void IDctSlow__c( Q_LIST_ENTRY * InputData, - ogg_int16_t *QuantMatrix, - ogg_int16_t * OutputData ) { - ogg_int32_t IntermediateData[64]; - ogg_int32_t * ip = IntermediateData; - ogg_int16_t * op = OutputData; - - ogg_int32_t _A, _B, _C, _D, _Ad, _Bd, _Cd, _Dd, _E, _F, _G, _H; - ogg_int32_t _Ed, _Gd, _Add, _Bdd, _Fd, _Hd; - ogg_int32_t t1, t2; - - int loop; - - dequant_slow( QuantMatrix, InputData, IntermediateData); - - /* Inverse DCT on the rows now */ - for ( loop = 0; loop < 8; loop++){ - /* Check for non-zero values */ - if ( ip[0] | ip[1] | ip[2] | ip[3] | ip[4] | ip[5] | ip[6] | ip[7] ) { - t1 = (xC1S7 * ip[1]); - t2 = (xC7S1 * ip[7]); - t1 >>= 16; - t2 >>= 16; - _A = t1 + t2; - - t1 = (xC7S1 * ip[1]); - t2 = (xC1S7 * ip[7]); - t1 >>= 16; - t2 >>= 16; - _B = t1 - t2; - - t1 = (xC3S5 * ip[3]); - t2 = (xC5S3 * ip[5]); - t1 >>= 16; - t2 >>= 16; - _C = t1 + t2; - - t1 = (xC3S5 * ip[5]); - t2 = (xC5S3 * ip[3]); - t1 >>= 16; - t2 >>= 16; - _D = t1 - t2; - - t1 = (xC4S4 * (ogg_int16_t)(_A - _C)); - t1 >>= 16; - _Ad = t1; - - t1 = (xC4S4 * (ogg_int16_t)(_B - _D)); - t1 >>= 16; - _Bd = t1; - - - _Cd = _A + _C; - _Dd = _B + _D; - - t1 = (xC4S4 * (ogg_int16_t)(ip[0] + ip[4])); - t1 >>= 16; - _E = t1; - - t1 = (xC4S4 * (ogg_int16_t)(ip[0] - ip[4])); - t1 >>= 16; - _F = t1; - - t1 = (xC2S6 * ip[2]); - t2 = (xC6S2 * ip[6]); - t1 >>= 16; - t2 >>= 16; - _G = t1 + t2; - - t1 = (xC6S2 * ip[2]); - t2 = (xC2S6 * ip[6]); - t1 >>= 16; - t2 >>= 16; - _H = t1 - t2; - - - _Ed = _E - _G; - _Gd = _E + _G; - - _Add = _F + _Ad; - _Bdd = _Bd - _H; - - _Fd = _F - _Ad; - _Hd = _Bd + _H; - - /* Final sequence of operations over-write original inputs. */ - ip[0] = (ogg_int16_t)((_Gd + _Cd ) >> 0); - ip[7] = (ogg_int16_t)((_Gd - _Cd ) >> 0); - - ip[1] = (ogg_int16_t)((_Add + _Hd ) >> 0); - ip[2] = (ogg_int16_t)((_Add - _Hd ) >> 0); - - ip[3] = (ogg_int16_t)((_Ed + _Dd ) >> 0); - ip[4] = (ogg_int16_t)((_Ed - _Dd ) >> 0); - - ip[5] = (ogg_int16_t)((_Fd + _Bdd ) >> 0); - ip[6] = (ogg_int16_t)((_Fd - _Bdd ) >> 0); - - } - - ip += 8; /* next row */ - } - - ip = IntermediateData; - - for ( loop = 0; loop < 8; loop++){ - /* Check for non-zero values (bitwise or faster than ||) */ - if ( ip[0 * 8] | ip[1 * 8] | ip[2 * 8] | ip[3 * 8] | - ip[4 * 8] | ip[5 * 8] | ip[6 * 8] | ip[7 * 8] ) { - - t1 = (xC1S7 * ip[1*8]); - t2 = (xC7S1 * ip[7*8]); - t1 >>= 16; - t2 >>= 16; - _A = t1 + t2; - - t1 = (xC7S1 * ip[1*8]); - t2 = (xC1S7 * ip[7*8]); - t1 >>= 16; - t2 >>= 16; - _B = t1 - t2; - - t1 = (xC3S5 * ip[3*8]); - t2 = (xC5S3 * ip[5*8]); - t1 >>= 16; - t2 >>= 16; - _C = t1 + t2; - - t1 = (xC3S5 * ip[5*8]); - t2 = (xC5S3 * ip[3*8]); - t1 >>= 16; - t2 >>= 16; - _D = t1 - t2; - - t1 = (xC4S4 * (ogg_int16_t)(_A - _C)); - t1 >>= 16; - _Ad = t1; - - t1 = (xC4S4 * (ogg_int16_t)(_B - _D)); - t1 >>= 16; - _Bd = t1; - - - _Cd = _A + _C; - _Dd = _B + _D; - - t1 = (xC4S4 * (ogg_int16_t)(ip[0*8] + ip[4*8])); - t1 >>= 16; - _E = t1; - - t1 = (xC4S4 * (ogg_int16_t)(ip[0*8] - ip[4*8])); - t1 >>= 16; - _F = t1; - - t1 = (xC2S6 * ip[2*8]); - t2 = (xC6S2 * ip[6*8]); - t1 >>= 16; - t2 >>= 16; - _G = t1 + t2; - - t1 = (xC6S2 * ip[2*8]); - t2 = (xC2S6 * ip[6*8]); - t1 >>= 16; - t2 >>= 16; - _H = t1 - t2; - - _Ed = _E - _G; - _Gd = _E + _G; - - _Add = _F + _Ad; - _Bdd = _Bd - _H; - - _Fd = _F - _Ad; - _Hd = _Bd + _H; - - _Gd += IdctAdjustBeforeShift; - _Add += IdctAdjustBeforeShift; - _Ed += IdctAdjustBeforeShift; - _Fd += IdctAdjustBeforeShift; - - /* Final sequence of operations over-write original inputs. */ - op[0*8] = (ogg_int16_t)((_Gd + _Cd ) >> 4); - op[7*8] = (ogg_int16_t)((_Gd - _Cd ) >> 4); - - op[1*8] = (ogg_int16_t)((_Add + _Hd ) >> 4); - op[2*8] = (ogg_int16_t)((_Add - _Hd ) >> 4); - - op[3*8] = (ogg_int16_t)((_Ed + _Dd ) >> 4); - op[4*8] = (ogg_int16_t)((_Ed - _Dd ) >> 4); - - op[5*8] = (ogg_int16_t)((_Fd + _Bdd ) >> 4); - op[6*8] = (ogg_int16_t)((_Fd - _Bdd ) >> 4); - }else{ - op[0*8] = 0; - op[7*8] = 0; - op[1*8] = 0; - op[2*8] = 0; - op[3*8] = 0; - op[4*8] = 0; - op[5*8] = 0; - op[6*8] = 0; - } - - ip++; /* next column */ - op++; - } -} - -/************************ - x x x x 0 0 0 0 - x x x 0 0 0 0 0 - x x 0 0 0 0 0 0 - x 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 -*************************/ - -static void dequant_slow10( ogg_int16_t * dequant_coeffs, - ogg_int16_t * quantized_list, - ogg_int32_t * DCT_block){ - int i; - memset(DCT_block,0, 128); - for(i=0;i<10;i++) - DCT_block[dezigzag_index[i]] = quantized_list[i] * dequant_coeffs[i]; - -} - -void IDct10__c( Q_LIST_ENTRY * InputData, - ogg_int16_t *QuantMatrix, - ogg_int16_t * OutputData ){ - ogg_int32_t IntermediateData[64]; - ogg_int32_t * ip = IntermediateData; - ogg_int16_t * op = OutputData; - - ogg_int32_t _A, _B, _C, _D, _Ad, _Bd, _Cd, _Dd, _E, _F, _G, _H; - ogg_int32_t _Ed, _Gd, _Add, _Bdd, _Fd, _Hd; - ogg_int32_t t1, t2; - - int loop; - - dequant_slow10( QuantMatrix, InputData, IntermediateData); - - /* Inverse DCT on the rows now */ - for ( loop = 0; loop < 4; loop++){ - /* Check for non-zero values */ - if ( ip[0] | ip[1] | ip[2] | ip[3] ){ - t1 = (xC1S7 * ip[1]); - t1 >>= 16; - _A = t1; - - t1 = (xC7S1 * ip[1]); - t1 >>= 16; - _B = t1 ; - - t1 = (xC3S5 * ip[3]); - t1 >>= 16; - _C = t1; - - t2 = (xC5S3 * ip[3]); - t2 >>= 16; - _D = -t2; - - - t1 = (xC4S4 * (ogg_int16_t)(_A - _C)); - t1 >>= 16; - _Ad = t1; - - t1 = (xC4S4 * (ogg_int16_t)(_B - _D)); - t1 >>= 16; - _Bd = t1; - - - _Cd = _A + _C; - _Dd = _B + _D; - - t1 = (xC4S4 * ip[0] ); - t1 >>= 16; - _E = t1; - - _F = t1; - - t1 = (xC2S6 * ip[2]); - t1 >>= 16; - _G = t1; - - t1 = (xC6S2 * ip[2]); - t1 >>= 16; - _H = t1 ; - - - _Ed = _E - _G; - _Gd = _E + _G; - - _Add = _F + _Ad; - _Bdd = _Bd - _H; - - _Fd = _F - _Ad; - _Hd = _Bd + _H; - - /* Final sequence of operations over-write original inputs. */ - ip[0] = (ogg_int16_t)((_Gd + _Cd ) >> 0); - ip[7] = (ogg_int16_t)((_Gd - _Cd ) >> 0); - - ip[1] = (ogg_int16_t)((_Add + _Hd ) >> 0); - ip[2] = (ogg_int16_t)((_Add - _Hd ) >> 0); - - ip[3] = (ogg_int16_t)((_Ed + _Dd ) >> 0); - ip[4] = (ogg_int16_t)((_Ed - _Dd ) >> 0); - - ip[5] = (ogg_int16_t)((_Fd + _Bdd ) >> 0); - ip[6] = (ogg_int16_t)((_Fd - _Bdd ) >> 0); - - } - - ip += 8; /* next row */ - } - - ip = IntermediateData; - - for ( loop = 0; loop < 8; loop++) { - /* Check for non-zero values (bitwise or faster than ||) */ - if ( ip[0 * 8] | ip[1 * 8] | ip[2 * 8] | ip[3 * 8] ) { - - t1 = (xC1S7 * ip[1*8]); - t1 >>= 16; - _A = t1 ; - - t1 = (xC7S1 * ip[1*8]); - t1 >>= 16; - _B = t1 ; - - t1 = (xC3S5 * ip[3*8]); - t1 >>= 16; - _C = t1 ; - - t2 = (xC5S3 * ip[3*8]); - t2 >>= 16; - _D = - t2; - - - t1 = (xC4S4 * (ogg_int16_t)(_A - _C)); - t1 >>= 16; - _Ad = t1; - - t1 = (xC4S4 * (ogg_int16_t)(_B - _D)); - t1 >>= 16; - _Bd = t1; - - - _Cd = _A + _C; - _Dd = _B + _D; - - t1 = (xC4S4 * ip[0*8]); - t1 >>= 16; - _E = t1; - _F = t1; - - t1 = (xC2S6 * ip[2*8]); - t1 >>= 16; - _G = t1; - - t1 = (xC6S2 * ip[2*8]); - t1 >>= 16; - _H = t1; - - - _Ed = _E - _G; - _Gd = _E + _G; - - _Add = _F + _Ad; - _Bdd = _Bd - _H; - - _Fd = _F - _Ad; - _Hd = _Bd + _H; - - _Gd += IdctAdjustBeforeShift; - _Add += IdctAdjustBeforeShift; - _Ed += IdctAdjustBeforeShift; - _Fd += IdctAdjustBeforeShift; - - /* Final sequence of operations over-write original inputs. */ - op[0*8] = (ogg_int16_t)((_Gd + _Cd ) >> 4); - op[7*8] = (ogg_int16_t)((_Gd - _Cd ) >> 4); - - op[1*8] = (ogg_int16_t)((_Add + _Hd ) >> 4); - op[2*8] = (ogg_int16_t)((_Add - _Hd ) >> 4); - - op[3*8] = (ogg_int16_t)((_Ed + _Dd ) >> 4); - op[4*8] = (ogg_int16_t)((_Ed - _Dd ) >> 4); - - op[5*8] = (ogg_int16_t)((_Fd + _Bdd ) >> 4); - op[6*8] = (ogg_int16_t)((_Fd - _Bdd ) >> 4); - }else{ - op[0*8] = 0; - op[7*8] = 0; - op[1*8] = 0; - op[2*8] = 0; - op[3*8] = 0; - op[4*8] = 0; - op[5*8] = 0; - op[6*8] = 0; - } - - ip++; /* next column */ - op++; - } -} - -/*************************** - x 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 -**************************/ - -void IDct1( Q_LIST_ENTRY * InputData, - ogg_int16_t *QuantMatrix, - ogg_int16_t * OutputData ){ - int loop; - - ogg_int16_t OutD; - - OutD=(ogg_int16_t) ((ogg_int32_t)(InputData[0]*QuantMatrix[0]+15)>>5); - - for(loop=0;loop<64;loop++) - OutputData[loop]=OutD; - -} - -void dsp_idct_init (DspFunctions *funcs, ogg_uint32_t cpu_flags) -{ - funcs->IDctSlow = IDctSlow__c; - funcs->IDct10 = IDct10__c; - funcs->IDct3 = IDct10__c; -#if defined(USE_ASM) - // todo: make mmx encoder idct for MSC one day... -#if !defined (_MSC_VER) - if (cpu_flags & OC_CPU_X86_MMX) { - dsp_mmx_idct_init(funcs); - } -#endif -#endif -} diff --git a/Engine/lib/libtheora/lib/enc/encoder_lookup.h b/Engine/lib/libtheora/lib/enc/encoder_lookup.h deleted file mode 100644 index c5759869a..000000000 --- a/Engine/lib/libtheora/lib/enc/encoder_lookup.h +++ /dev/null @@ -1,120 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: simple static lookups for VP3 frame encoder - last mod: $Id: encoder_lookup.h 15323 2008-09-19 19:43:59Z giles $ - - ********************************************************************/ - -#include "codec_internal.h" - -static const ogg_uint32_t MvPattern[(MAX_MV_EXTENT * 2) + 1] = { - 0x000000ff, 0x000000fd, 0x000000fb, 0x000000f9, - 0x000000f7, 0x000000f5, 0x000000f3, 0x000000f1, - 0x000000ef, 0x000000ed, 0x000000eb, 0x000000e9, - 0x000000e7, 0x000000e5, 0x000000e3, 0x000000e1, - 0x0000006f, 0x0000006d, 0x0000006b, 0x00000069, - 0x00000067, 0x00000065, 0x00000063, 0x00000061, - 0x0000002f, 0x0000002d, 0x0000002b, 0x00000029, - 0x00000009, 0x00000007, 0x00000002, 0x00000000, - 0x00000001, 0x00000006, 0x00000008, 0x00000028, - 0x0000002a, 0x0000002c, 0x0000002e, 0x00000060, - 0x00000062, 0x00000064, 0x00000066, 0x00000068, - 0x0000006a, 0x0000006c, 0x0000006e, 0x000000e0, - 0x000000e2, 0x000000e4, 0x000000e6, 0x000000e8, - 0x000000ea, 0x000000ec, 0x000000ee, 0x000000f0, - 0x000000f2, 0x000000f4, 0x000000f6, 0x000000f8, - 0x000000fa, 0x000000fc, 0x000000fe, -}; - -static const ogg_uint32_t MvBits[(MAX_MV_EXTENT * 2) + 1] = { - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 7, 7, 7, 7, 7, 7, 7, 7, - 6, 6, 6, 6, 4, 4, 3, 3, - 3, 4, 4, 6, 6, 6, 6, 7, - 7, 7, 7, 7, 7, 7, 7, 8, - 8, 8, 8, 8, 8, 8, 8, 8, - 8, 8, 8, 8, 8, 8, 8, -}; - -static const ogg_uint32_t MvPattern2[(MAX_MV_EXTENT * 2) + 1] = { - 0x0000003f, 0x0000003d, 0x0000003b, 0x00000039, - 0x00000037, 0x00000035, 0x00000033, 0x00000031, - 0x0000002f, 0x0000002d, 0x0000002b, 0x00000029, - 0x00000027, 0x00000025, 0x00000023, 0x00000021, - 0x0000001f, 0x0000001d, 0x0000001b, 0x00000019, - 0x00000017, 0x00000015, 0x00000013, 0x00000011, - 0x0000000f, 0x0000000d, 0x0000000b, 0x00000009, - 0x00000007, 0x00000005, 0x00000003, 0x00000000, - 0x00000002, 0x00000004, 0x00000006, 0x00000008, - 0x0000000a, 0x0000000c, 0x0000000e, 0x00000010, - 0x00000012, 0x00000014, 0x00000016, 0x00000018, - 0x0000001a, 0x0000001c, 0x0000001e, 0x00000020, - 0x00000022, 0x00000024, 0x00000026, 0x00000028, - 0x0000002a, 0x0000002c, 0x0000002e, 0x00000030, - 0x00000032, 0x00000034, 0x00000036, 0x00000038, - 0x0000003a, 0x0000003c, 0x0000003e, -}; - -static const ogg_uint32_t MvBits2[(MAX_MV_EXTENT * 2) + 1] = { - 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, 6, - 6, 6, 6, 6, 6, 6, 6, -}; - -static const ogg_uint32_t ModeBitPatterns[MAX_MODES] = { - 0x00, 0x02, 0x06, 0x0E, 0x1E, 0x3E, 0x7E, 0x7F }; - -static const ogg_int32_t ModeBitLengths[MAX_MODES] = { - 1, 2, 3, 4, 5, 6, 7, 7 }; - -static const unsigned char ModeSchemes[MODE_METHODS-2][MAX_MODES] = { - /* Last Mv dominates */ - { 3, 4, 2, 0, 1, 5, 6, 7 }, /* L P M N I G GM 4 */ - { 2, 4, 3, 0, 1, 5, 6, 7 }, /* L P N M I G GM 4 */ - { 3, 4, 1, 0, 2, 5, 6, 7 }, /* L M P N I G GM 4 */ - { 2, 4, 1, 0, 3, 5, 6, 7 }, /* L M N P I G GM 4 */ - - /* No MV dominates */ - { 0, 4, 3, 1, 2, 5, 6, 7 }, /* N L P M I G GM 4 */ - { 0, 5, 4, 2, 3, 1, 6, 7 }, /* N G L P M I GM 4 */ - -}; - - -static const ogg_uint32_t MvThreshTable[Q_TABLE_SIZE] = { - 65, 65, 65, 65, 50, 50, 50, 50, - 40, 40, 40, 40, 40, 40, 40, 40, - 30, 30, 30, 30, 30, 30, 30, 30, - 20, 20, 20, 20, 20, 20, 20, 20, - 15, 15, 15, 15, 15, 15, 15, 15, - 10, 10, 10, 10, 10, 10, 10, 10, - 5, 5, 5, 5, 5, 5, 5, 5, - 0, 0, 0, 0, 0, 0, 0, 0 -}; - -static const ogg_uint32_t MVChangeFactorTable[Q_TABLE_SIZE] = { - 11, 11, 11, 11, 12, 12, 12, 12, - 13, 13, 13, 13, 13, 13, 13, 13, - 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, - 14, 14, 14, 14, 14, 14, 14, 14, - 15, 15, 15, 15, 15, 15, 15, 15, - 15, 15, 15, 15, 15, 15, 15, 15 -}; diff --git a/Engine/lib/libtheora/lib/enc/encoder_quant.c b/Engine/lib/libtheora/lib/enc/encoder_quant.c deleted file mode 100644 index a5639a233..000000000 --- a/Engine/lib/libtheora/lib/enc/encoder_quant.c +++ /dev/null @@ -1,558 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2005 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: encoder_quant.c 15153 2008-08-04 18:37:55Z tterribe $ - - ********************************************************************/ - -#include -#include -#include "codec_internal.h" -#include "quant_lookup.h" - -#define OC_QUANT_MAX (1024<<2) -static const unsigned DC_QUANT_MIN[2]={4<<2,8<<2}; -static const unsigned AC_QUANT_MIN[2]={2<<2,4<<2}; -#define OC_MAXI(_a,_b) ((_a)<(_b)?(_b):(_a)) -#define OC_MINI(_a,_b) ((_a)>(_b)?(_b):(_a)) -#define OC_CLAMPI(_a,_b,_c) (OC_MAXI(_a,OC_MINI(_b,_c))) - -static int ilog(unsigned _v){ - int ret; - for(ret=0;_v;ret++)_v>>=1; - return ret; -} - - -void WriteQTables(PB_INSTANCE *pbi,oggpack_buffer* _opb) { - - th_quant_info *_qinfo = &pbi->quant_info; - - const th_quant_ranges *qranges; - const th_quant_base *base_mats[2*3*64]; - int indices[2][3][64]; - int nbase_mats; - int nbits; - int ci; - int qi; - int qri; - int qti; - int pli; - int qtj; - int plj; - int bmi; - int i; - - /*Unlike the scale tables, we can't assume the maximum value will be in - index 0, so search for it here.*/ - i=_qinfo->loop_filter_limits[0]; - for(qi=1;qi<64;qi++)i=OC_MAXI(i,_qinfo->loop_filter_limits[qi]); - nbits=ilog(i); - oggpackB_write(_opb,nbits,3); - for(qi=0;qi<64;qi++){ - oggpackB_write(_opb,_qinfo->loop_filter_limits[qi],nbits); - } - /* 580 bits for VP3.*/ - nbits=OC_MAXI(ilog(_qinfo->ac_scale[0]),1); - oggpackB_write(_opb,nbits-1,4); - for(qi=0;qi<64;qi++)oggpackB_write(_opb,_qinfo->ac_scale[qi],nbits); - /* 516 bits for VP3.*/ - nbits=OC_MAXI(ilog(_qinfo->dc_scale[0]),1); - oggpackB_write(_opb,nbits-1,4); - for(qi=0;qi<64;qi++)oggpackB_write(_opb,_qinfo->dc_scale[qi],nbits); - /*Consolidate any duplicate base matrices.*/ - nbase_mats=0; - for(qti=0;qti<2;qti++)for(pli=0;pli<3;pli++){ - qranges=_qinfo->qi_ranges[qti]+pli; - for(qri=0;qri<=qranges->nranges;qri++){ - for(bmi=0;;bmi++){ - if(bmi>=nbase_mats){ - base_mats[bmi]=qranges->base_matrices+qri; - indices[qti][pli][qri]=nbase_mats++; - break; - } - else if(memcmp(base_mats[bmi][0],qranges->base_matrices[qri], - sizeof(base_mats[bmi][0]))==0){ - indices[qti][pli][qri]=bmi; - break; - } - } - } - } - /*Write out the list of unique base matrices. - 1545 bits for VP3 matrices.*/ - oggpackB_write(_opb,nbase_mats-1,9); - for(bmi=0;bmiqi_ranges[qti]+pli; - if(i>0){ - if(qti>0){ - if(qranges->nranges==_qinfo->qi_ranges[qti-1][pli].nranges&& - memcmp(qranges->sizes,_qinfo->qi_ranges[qti-1][pli].sizes, - qranges->nranges*sizeof(qranges->sizes[0]))==0&& - memcmp(indices[qti][pli],indices[qti-1][pli], - (qranges->nranges+1)*sizeof(indices[qti][pli][0]))==0){ - oggpackB_write(_opb,1,2); - continue; - } - } - qtj=(i-1)/3; - plj=(i-1)%3; - if(qranges->nranges==_qinfo->qi_ranges[qtj][plj].nranges&& - memcmp(qranges->sizes,_qinfo->qi_ranges[qtj][plj].sizes, - qranges->nranges*sizeof(qranges->sizes[0]))==0&& - memcmp(indices[qti][pli],indices[qtj][plj], - (qranges->nranges+1)*sizeof(indices[qti][pli][0]))==0){ - oggpackB_write(_opb,0,1+(qti>0)); - continue; - } - oggpackB_write(_opb,1,1); - } - oggpackB_write(_opb,indices[qti][pli][0],nbits); - for(qi=qri=0;qi<63;qri++){ - oggpackB_write(_opb,qranges->sizes[qri]-1,ilog(62-qi)); - qi+=qranges->sizes[qri]; - oggpackB_write(_opb,indices[qti][pli][qri+1],nbits); - } - } -} - -/* a copied/reconciled version of derf's theora-exp code; redundancy - should be eliminated at some point */ -void InitQTables( PB_INSTANCE *pbi ){ - int qti; /* coding mode: intra or inter */ - int pli; /* Y U V */ - th_quant_info *qinfo = &pbi->quant_info; - - pbi->QThreshTable = pbi->quant_info.ac_scale; - - for(qti=0;qti<2;qti++){ - for(pli=0;pli<3;pli++){ - int qi; /* quality index */ - int qri; /* range iterator */ - - for(qi=0,qri=0; qri<=qinfo->qi_ranges[qti][pli].nranges; qri++){ - th_quant_base base; - - ogg_uint32_t q; - int qi_start; - int qi_end; - int ci; - memcpy(base,qinfo->qi_ranges[qti][pli].base_matrices[qri], - sizeof(base)); - - qi_start=qi; - if(qri==qinfo->qi_ranges[qti][pli].nranges) - qi_end=qi+1; - else - qi_end=qi+qinfo->qi_ranges[qti][pli].sizes[qri]; - - /* Iterate over quality indicies in this range */ - for(;;){ - - /*Scale DC the coefficient from the proper table.*/ - q=((ogg_uint32_t)qinfo->dc_scale[qi]*base[0]/100)<<2; - q=OC_CLAMPI(DC_QUANT_MIN[qti],q,OC_QUANT_MAX); - pbi->quant_tables[qti][pli][qi][0]=(ogg_uint16_t)q; - - /*Now scale AC coefficients from the proper table.*/ - for(ci=1;ci<64;ci++){ - q=((ogg_uint32_t)qinfo->ac_scale[qi]*base[ci]/100)<<2; - q=OC_CLAMPI(AC_QUANT_MIN[qti],q,OC_QUANT_MAX); - pbi->quant_tables[qti][pli][qi][ci]=(ogg_uint16_t)q; - } - - if(++qi>=qi_end)break; - - /*Interpolate the next base matrix.*/ - for(ci=0;ci<64;ci++){ - base[ci]=(unsigned char) - ((2*((qi_end-qi)*qinfo->qi_ranges[qti][pli].base_matrices[qri][ci]+ - (qi-qi_start)*qinfo->qi_ranges[qti][pli].base_matrices[qri+1][ci]) - +qinfo->qi_ranges[qti][pli].sizes[qri])/ - (2*qinfo->qi_ranges[qti][pli].sizes[qri])); - } - } - } - } - } -} - -static void BuildZigZagIndex(PB_INSTANCE *pbi){ - ogg_int32_t i,j; - - /* invert the row to zigzag coeffient order lookup table */ - for ( i = 0; i < BLOCK_SIZE; i++ ){ - j = dezigzag_index[i]; - pbi->zigzag_index[j] = i; - } -} - -static void init_quantizer ( CP_INSTANCE *cpi, - unsigned char QIndex ){ - int i; - double ZBinFactor; - double RoundingFactor; - - double temp_fp_quant_coeffs; - double temp_fp_quant_round; - double temp_fp_ZeroBinSize; - PB_INSTANCE *pbi = &cpi->pb; - - - const ogg_uint16_t * temp_Y_coeffs; - const ogg_uint16_t * temp_U_coeffs; - const ogg_uint16_t * temp_V_coeffs; - const ogg_uint16_t * temp_Inter_Y_coeffs; - const ogg_uint16_t * temp_Inter_U_coeffs; - const ogg_uint16_t * temp_Inter_V_coeffs; - ogg_uint16_t scale_factor = cpi->pb.quant_info.ac_scale[QIndex]; - - /* Notes on setup of quantisers. The initial multiplication by - the scale factor is done in the ogg_int32_t domain to insure that the - precision in the quantiser is the same as in the inverse - quantiser where all calculations are integer. The "<< 2" is a - normalisation factor for the forward DCT transform. */ - - temp_Y_coeffs = pbi->quant_tables[0][0][QIndex]; - temp_U_coeffs = pbi->quant_tables[0][1][QIndex]; - temp_V_coeffs = pbi->quant_tables[0][2][QIndex]; - temp_Inter_Y_coeffs = pbi->quant_tables[1][0][QIndex]; - temp_Inter_U_coeffs = pbi->quant_tables[1][1][QIndex]; - temp_Inter_V_coeffs = pbi->quant_tables[1][2][QIndex]; - - ZBinFactor = 0.9; - - switch(cpi->pb.info.sharpness){ - case 0: - ZBinFactor = 0.65; - if ( scale_factor <= 50 ) - RoundingFactor = 0.499; - else - RoundingFactor = 0.46; - break; - case 1: - ZBinFactor = 0.75; - if ( scale_factor <= 50 ) - RoundingFactor = 0.476; - else - RoundingFactor = 0.400; - break; - - default: - ZBinFactor = 0.9; - if ( scale_factor <= 50 ) - RoundingFactor = 0.476; - else - RoundingFactor = 0.333; - break; - } - - /* Use fixed multiplier for intra Y DC */ - temp_fp_quant_coeffs = temp_Y_coeffs[0]; - temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; - pbi->fp_quant_Y_round[0] = (ogg_int32_t) (0.5 + temp_fp_quant_round); - temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; - pbi->fp_ZeroBinSize_Y[0] = (ogg_int32_t) (0.5 + temp_fp_ZeroBinSize); - temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; - pbi->fp_quant_Y_coeffs[0] = (0.5 + SHIFT16 * temp_fp_quant_coeffs); - - /* Intra U */ - temp_fp_quant_coeffs = temp_U_coeffs[0]; - temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; - pbi->fp_quant_U_round[0] = (0.5 + temp_fp_quant_round); - temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; - pbi->fp_ZeroBinSize_U[0] = (0.5 + temp_fp_ZeroBinSize); - temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; - pbi->fp_quant_U_coeffs[0]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); - - /* Intra V */ - temp_fp_quant_coeffs = temp_V_coeffs[0]; - temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; - pbi->fp_quant_V_round[0] = (0.5 + temp_fp_quant_round); - temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; - pbi->fp_ZeroBinSize_V[0] = (0.5 + temp_fp_ZeroBinSize); - temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; - pbi->fp_quant_V_coeffs[0]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); - - - /* Inter Y */ - temp_fp_quant_coeffs = temp_Inter_Y_coeffs[0]; - temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; - pbi->fp_quant_Inter_Y_round[0]= (0.5 + temp_fp_quant_round); - temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; - pbi->fp_ZeroBinSize_Inter_Y[0]= (0.5 + temp_fp_ZeroBinSize); - temp_fp_quant_coeffs= 1.0 / temp_fp_quant_coeffs; - pbi->fp_quant_Inter_Y_coeffs[0]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); - - /* Inter U */ - temp_fp_quant_coeffs = temp_Inter_U_coeffs[0]; - temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; - pbi->fp_quant_Inter_U_round[0]= (0.5 + temp_fp_quant_round); - temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; - pbi->fp_ZeroBinSize_Inter_U[0]= (0.5 + temp_fp_ZeroBinSize); - temp_fp_quant_coeffs= 1.0 / temp_fp_quant_coeffs; - pbi->fp_quant_Inter_U_coeffs[0]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); - - /* Inter V */ - temp_fp_quant_coeffs = temp_Inter_V_coeffs[0]; - temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; - pbi->fp_quant_Inter_V_round[0]= (0.5 + temp_fp_quant_round); - temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; - pbi->fp_ZeroBinSize_Inter_V[0]= (0.5 + temp_fp_ZeroBinSize); - temp_fp_quant_coeffs= 1.0 / temp_fp_quant_coeffs; - pbi->fp_quant_Inter_V_coeffs[0]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); - - - for ( i = 1; i < 64; i++ ){ - /* Intra Y */ - temp_fp_quant_coeffs = temp_Y_coeffs[i]; - temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; - pbi->fp_quant_Y_round[i] = (0.5 + temp_fp_quant_round); - temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; - pbi->fp_ZeroBinSize_Y[i] = (0.5 + temp_fp_ZeroBinSize); - temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; - pbi->fp_quant_Y_coeffs[i] = (0.5 + SHIFT16 * temp_fp_quant_coeffs); - - /* Intra U */ - temp_fp_quant_coeffs = temp_U_coeffs[i]; - temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; - pbi->fp_quant_U_round[i] = (0.5 + temp_fp_quant_round); - temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; - pbi->fp_ZeroBinSize_U[i] = (0.5 + temp_fp_ZeroBinSize); - temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; - pbi->fp_quant_U_coeffs[i]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); - - /* Intra V */ - temp_fp_quant_coeffs = temp_V_coeffs[i]; - temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; - pbi->fp_quant_V_round[i] = (0.5 + temp_fp_quant_round); - temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; - pbi->fp_ZeroBinSize_V[i] = (0.5 + temp_fp_ZeroBinSize); - temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; - pbi->fp_quant_V_coeffs[i]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); - - /* Inter Y */ - temp_fp_quant_coeffs = temp_Inter_Y_coeffs[i]; - temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; - pbi->fp_quant_Inter_Y_round[i]= (0.5 + temp_fp_quant_round); - temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; - pbi->fp_ZeroBinSize_Inter_Y[i]= (0.5 + temp_fp_ZeroBinSize); - temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; - pbi->fp_quant_Inter_Y_coeffs[i]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); - - /* Inter U */ - temp_fp_quant_coeffs = temp_Inter_U_coeffs[i]; - temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; - pbi->fp_quant_Inter_U_round[i]= (0.5 + temp_fp_quant_round); - temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; - pbi->fp_ZeroBinSize_Inter_U[i]= (0.5 + temp_fp_ZeroBinSize); - temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; - pbi->fp_quant_Inter_U_coeffs[i]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); - - /* Inter V */ - temp_fp_quant_coeffs = temp_Inter_V_coeffs[i]; - temp_fp_quant_round = temp_fp_quant_coeffs * RoundingFactor; - pbi->fp_quant_Inter_V_round[i]= (0.5 + temp_fp_quant_round); - temp_fp_ZeroBinSize = temp_fp_quant_coeffs * ZBinFactor; - pbi->fp_ZeroBinSize_Inter_V[i]= (0.5 + temp_fp_ZeroBinSize); - temp_fp_quant_coeffs = 1.0 / temp_fp_quant_coeffs; - pbi->fp_quant_Inter_V_coeffs[i]= (0.5 + SHIFT16 * temp_fp_quant_coeffs); - - - } - - pbi->fquant_coeffs = pbi->fp_quant_Y_coeffs; - -} - -void select_quantiser(PB_INSTANCE *pbi, int type) { - /* select a quantiser according to what plane has to be coded in what - * mode. Could be extended to a more sophisticated scheme. */ - - switch(type) { - case BLOCK_Y: - pbi->fquant_coeffs = pbi->fp_quant_Y_coeffs; - pbi->fquant_round = pbi->fp_quant_Y_round; - pbi->fquant_ZbSize = pbi->fp_ZeroBinSize_Y; - break; - case BLOCK_U: - pbi->fquant_coeffs = pbi->fp_quant_U_coeffs; - pbi->fquant_round = pbi->fp_quant_U_round; - pbi->fquant_ZbSize = pbi->fp_ZeroBinSize_U; - break; - case BLOCK_V: - pbi->fquant_coeffs = pbi->fp_quant_V_coeffs; - pbi->fquant_round = pbi->fp_quant_V_round; - pbi->fquant_ZbSize = pbi->fp_ZeroBinSize_V; - break; - case BLOCK_INTER_Y: - pbi->fquant_coeffs = pbi->fp_quant_Inter_Y_coeffs; - pbi->fquant_round = pbi->fp_quant_Inter_Y_round; - pbi->fquant_ZbSize = pbi->fp_ZeroBinSize_Inter_Y; - break; - case BLOCK_INTER_U: - pbi->fquant_coeffs = pbi->fp_quant_Inter_U_coeffs; - pbi->fquant_round = pbi->fp_quant_Inter_U_round; - pbi->fquant_ZbSize = pbi->fp_ZeroBinSize_Inter_U; - break; - case BLOCK_INTER_V: - pbi->fquant_coeffs = pbi->fp_quant_Inter_V_coeffs; - pbi->fquant_round = pbi->fp_quant_Inter_V_round; - pbi->fquant_ZbSize = pbi->fp_ZeroBinSize_Inter_V; - break; - } -} - - -void quantize( PB_INSTANCE *pbi, - ogg_int16_t * DCT_block, - Q_LIST_ENTRY * quantized_list){ - ogg_uint32_t i; /* Row index */ - Q_LIST_ENTRY val; /* Quantised value. */ - - ogg_int32_t * FquantRoundPtr = pbi->fquant_round; - ogg_int32_t * FquantCoeffsPtr = pbi->fquant_coeffs; - ogg_int32_t * FquantZBinSizePtr = pbi->fquant_ZbSize; - ogg_int16_t * DCT_blockPtr = DCT_block; - ogg_uint32_t * ZigZagPtr = (ogg_uint32_t *)pbi->zigzag_index; - ogg_int32_t temp; - - /* Set the quantized_list to default to 0 */ - memset( quantized_list, 0, 64 * sizeof(Q_LIST_ENTRY) ); - - /* Note that we add half divisor to effect rounding on positive number */ - for( i = 0; i < VFRAGPIXELS; i++) { - - int col; - /* Iterate through columns */ - for( col = 0; col < 8; col++) { - if ( DCT_blockPtr[col] >= FquantZBinSizePtr[col] ) { - temp = FquantCoeffsPtr[col] * ( DCT_blockPtr[col] + FquantRoundPtr[col] ) ; - val = (Q_LIST_ENTRY) (temp>>16); - quantized_list[ZigZagPtr[col]] = ( val > 511 ) ? 511 : val; - } else if ( DCT_blockPtr[col] <= -FquantZBinSizePtr[col] ) { - temp = FquantCoeffsPtr[col] * - ( DCT_blockPtr[col] - FquantRoundPtr[col] ) + MIN16; - val = (Q_LIST_ENTRY) (temp>>16); - quantized_list[ZigZagPtr[col]] = ( val < -511 ) ? -511 : val; - } - } - - FquantRoundPtr += 8; - FquantCoeffsPtr += 8; - FquantZBinSizePtr += 8; - DCT_blockPtr += 8; - ZigZagPtr += 8; - } -} - -static void init_dequantizer ( PB_INSTANCE *pbi, - unsigned char QIndex ){ - int i, j; - - ogg_uint16_t * InterY_coeffs; - ogg_uint16_t * InterU_coeffs; - ogg_uint16_t * InterV_coeffs; - ogg_uint16_t * Y_coeffs; - ogg_uint16_t * U_coeffs; - ogg_uint16_t * V_coeffs; - - Y_coeffs = pbi->quant_tables[0][0][QIndex]; - U_coeffs = pbi->quant_tables[0][1][QIndex]; - V_coeffs = pbi->quant_tables[0][2][QIndex]; - InterY_coeffs = pbi->quant_tables[1][0][QIndex]; - InterU_coeffs = pbi->quant_tables[1][1][QIndex]; - InterV_coeffs = pbi->quant_tables[1][2][QIndex]; - - /* invert the dequant index into the quant index - the dxer has a different order than the cxer. */ - BuildZigZagIndex(pbi); - - /* Reorder dequantisation coefficients into dct zigzag order. */ - for ( i = 0; i < BLOCK_SIZE; i++ ) { - j = pbi->zigzag_index[i]; - pbi->dequant_Y_coeffs[j] = Y_coeffs[i]; - } - for ( i = 0; i < BLOCK_SIZE; i++ ) { - j = pbi->zigzag_index[i]; - pbi->dequant_U_coeffs[j] = U_coeffs[i]; - } - for ( i = 0; i < BLOCK_SIZE; i++ ) { - j = pbi->zigzag_index[i]; - pbi->dequant_V_coeffs[j] = V_coeffs[i]; - } - for ( i = 0; i < BLOCK_SIZE; i++ ){ - j = pbi->zigzag_index[i]; - pbi->dequant_InterY_coeffs[j] = InterY_coeffs[i]; - } - for ( i = 0; i < BLOCK_SIZE; i++ ){ - j = pbi->zigzag_index[i]; - pbi->dequant_InterU_coeffs[j] = InterU_coeffs[i]; - } - for ( i = 0; i < BLOCK_SIZE; i++ ){ - j = pbi->zigzag_index[i]; - pbi->dequant_InterV_coeffs[j] = InterV_coeffs[i]; - } - - pbi->dequant_coeffs = pbi->dequant_Y_coeffs; -} - -void UpdateQ( PB_INSTANCE *pbi, int NewQIndex ){ - ogg_uint32_t qscale; - - /* clamp to legal bounds */ - if (NewQIndex >= Q_TABLE_SIZE) NewQIndex = Q_TABLE_SIZE - 1; - else if (NewQIndex < 0) NewQIndex = 0; - - pbi->FrameQIndex = NewQIndex; - - qscale = pbi->quant_info.ac_scale[NewQIndex]; - pbi->ThisFrameQualityValue = qscale; - - /* Re-initialise the Q tables for forward and reverse transforms. */ - init_dequantizer ( pbi, (unsigned char) pbi->FrameQIndex ); -} - -void UpdateQC( CP_INSTANCE *cpi, ogg_uint32_t NewQ ){ - ogg_uint32_t qscale; - PB_INSTANCE *pbi = &cpi->pb; - - /* Do bounds checking and convert to a float. */ - qscale = NewQ; - if ( qscale < pbi->quant_info.ac_scale[Q_TABLE_SIZE-1] ) - qscale = pbi->quant_info.ac_scale[Q_TABLE_SIZE-1]; - else if ( qscale > pbi->quant_info.ac_scale[0] ) - qscale = pbi->quant_info.ac_scale[0]; - - /* Set the inter/intra descision control variables. */ - pbi->FrameQIndex = Q_TABLE_SIZE - 1; - while ((ogg_int32_t) pbi->FrameQIndex >= 0 ) { - if ( (pbi->FrameQIndex == 0) || - ( pbi->quant_info.ac_scale[pbi->FrameQIndex] >= NewQ) ) - break; - pbi->FrameQIndex --; - } - - /* Re-initialise the Q tables for forward and reverse transforms. */ - init_quantizer ( cpi, pbi->FrameQIndex ); - init_dequantizer ( pbi, pbi->FrameQIndex ); -} diff --git a/Engine/lib/libtheora/lib/enc/encoder_toplevel.c b/Engine/lib/libtheora/lib/enc/encoder_toplevel.c deleted file mode 100644 index 9356bba23..000000000 --- a/Engine/lib/libtheora/lib/enc/encoder_toplevel.c +++ /dev/null @@ -1,1447 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: encoder_toplevel.c 15383 2008-10-10 14:33:46Z xiphmont $ - - ********************************************************************/ - -#ifdef HAVE_CONFIG_H -# include "config.h" -#endif - -#include -#include -#include "toplevel_lookup.h" -#include "../internal.h" -#include "dsp.h" -#include "codec_internal.h" - -#define A_TABLE_SIZE 29 -#define DF_CANDIDATE_WINDOW 5 - -/* - * th_quant_info for VP3 - */ - -/*The default quantization parameters used by VP3.1.*/ -static const int OC_VP31_RANGE_SIZES[1]={63}; -static const th_quant_base OC_VP31_BASES_INTRA_Y[2]={ - { - 16, 11, 10, 16, 24, 40, 51, 61, - 12, 12, 14, 19, 26, 58, 60, 55, - 14, 13, 16, 24, 40, 57, 69, 56, - 14, 17, 22, 29, 51, 87, 80, 62, - 18, 22, 37, 58, 68, 109,103, 77, - 24, 35, 55, 64, 81, 104,113, 92, - 49, 64, 78, 87,103, 121,120,101, - 72, 92, 95, 98,112, 100,103, 99 - }, - { - 16, 11, 10, 16, 24, 40, 51, 61, - 12, 12, 14, 19, 26, 58, 60, 55, - 14, 13, 16, 24, 40, 57, 69, 56, - 14, 17, 22, 29, 51, 87, 80, 62, - 18, 22, 37, 58, 68, 109,103, 77, - 24, 35, 55, 64, 81, 104,113, 92, - 49, 64, 78, 87,103, 121,120,101, - 72, 92, 95, 98,112, 100,103, 99 - } -}; -static const th_quant_base OC_VP31_BASES_INTRA_C[2]={ - { - 17, 18, 24, 47, 99, 99, 99, 99, - 18, 21, 26, 66, 99, 99, 99, 99, - 24, 26, 56, 99, 99, 99, 99, 99, - 47, 66, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99 - }, - { - 17, 18, 24, 47, 99, 99, 99, 99, - 18, 21, 26, 66, 99, 99, 99, 99, - 24, 26, 56, 99, 99, 99, 99, 99, - 47, 66, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99, - 99, 99, 99, 99, 99, 99, 99, 99 - } -}; -static const th_quant_base OC_VP31_BASES_INTER[2]={ - { - 16, 16, 16, 20, 24, 28, 32, 40, - 16, 16, 20, 24, 28, 32, 40, 48, - 16, 20, 24, 28, 32, 40, 48, 64, - 20, 24, 28, 32, 40, 48, 64, 64, - 24, 28, 32, 40, 48, 64, 64, 64, - 28, 32, 40, 48, 64, 64, 64, 96, - 32, 40, 48, 64, 64, 64, 96,128, - 40, 48, 64, 64, 64, 96,128,128 - }, - { - 16, 16, 16, 20, 24, 28, 32, 40, - 16, 16, 20, 24, 28, 32, 40, 48, - 16, 20, 24, 28, 32, 40, 48, 64, - 20, 24, 28, 32, 40, 48, 64, 64, - 24, 28, 32, 40, 48, 64, 64, 64, - 28, 32, 40, 48, 64, 64, 64, 96, - 32, 40, 48, 64, 64, 64, 96,128, - 40, 48, 64, 64, 64, 96,128,128 - } -}; - -const th_quant_info TH_VP31_QUANT_INFO={ - { - 220,200,190,180,170,170,160,160, - 150,150,140,140,130,130,120,120, - 110,110,100,100, 90, 90, 90, 80, - 80, 80, 70, 70, 70, 60, 60, 60, - 60, 50, 50, 50, 50, 40, 40, 40, - 40, 40, 30, 30, 30, 30, 30, 30, - 30, 20, 20, 20, 20, 20, 20, 20, - 20, 10, 10, 10, 10, 10, 10, 10 - }, - { - 500,450,400,370,340,310,285,265, - 245,225,210,195,185,180,170,160, - 150,145,135,130,125,115,110,107, - 100, 96, 93, 89, 85, 82, 75, 74, - 70, 68, 64, 60, 57, 56, 52, 50, - 49, 45, 44, 43, 40, 38, 37, 35, - 33, 32, 30, 29, 28, 25, 24, 22, - 21, 19, 18, 17, 15, 13, 12, 10 - }, - { - 30,25,20,20,15,15,14,14, - 13,13,12,12,11,11,10,10, - 9, 9, 8, 8, 7, 7, 7, 7, - 6, 6, 6, 6, 5, 5, 5, 5, - 4, 4, 4, 4, 3, 3, 3, 3, - 2, 2, 2, 2, 2, 2, 2, 2, - 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0 - }, - { - { - {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTRA_Y}, - {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTRA_C}, - {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTRA_C} - }, - { - {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTER}, - {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTER}, - {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTER} - } - } -}; - - -static void EClearFragmentInfo(CP_INSTANCE * cpi){ - if(cpi->extra_fragments) - _ogg_free(cpi->extra_fragments); - if(cpi->FragmentLastQ) - _ogg_free(cpi->FragmentLastQ); - if(cpi->FragTokens) - _ogg_free(cpi->FragTokens); - if(cpi->FragTokenCounts) - _ogg_free(cpi->FragTokenCounts); - if(cpi->RunHuffIndices) - _ogg_free(cpi->RunHuffIndices); - if(cpi->LastCodedErrorScore) - _ogg_free(cpi->LastCodedErrorScore); - if(cpi->ModeList) - _ogg_free(cpi->ModeList); - if(cpi->MVList) - _ogg_free(cpi->MVList); - if(cpi->DCT_codes ) - _ogg_free( cpi->DCT_codes ); - if(cpi->DCTDataBuffer ) - _ogg_free( cpi->DCTDataBuffer); - if(cpi->quantized_list) - _ogg_free( cpi->quantized_list); - if(cpi->OriginalDC) - _ogg_free( cpi->OriginalDC); - if(cpi->PartiallyCodedFlags) - _ogg_free(cpi->PartiallyCodedFlags); - if(cpi->PartiallyCodedMbPatterns) - _ogg_free(cpi->PartiallyCodedMbPatterns); - if(cpi->UncodedMbFlags) - _ogg_free(cpi->UncodedMbFlags); - - if(cpi->BlockCodedFlags) - _ogg_free(cpi->BlockCodedFlags); - - cpi->extra_fragments = 0; - cpi->FragmentLastQ = 0; - cpi->FragTokens = 0; - cpi->FragTokenCounts = 0; - cpi->RunHuffIndices = 0; - cpi->LastCodedErrorScore = 0; - cpi->ModeList = 0; - cpi->MVList = 0; - cpi->DCT_codes = 0; - cpi->DCTDataBuffer = 0; - cpi->quantized_list = 0; - cpi->OriginalDC = 0; - cpi->BlockCodedFlags = 0; -} - -static void EInitFragmentInfo(CP_INSTANCE * cpi){ - - /* clear any existing info */ - EClearFragmentInfo(cpi); - - /* Perform Fragment Allocations */ - cpi->extra_fragments = - _ogg_malloc(cpi->pb.UnitFragments*sizeof(unsigned char)); - - /* A note to people reading and wondering why malloc returns aren't - checked: - - lines like the following that implement a general strategy of - 'check the return of malloc; a zero pointer means we're out of - memory!'...: - - if(!cpi->extra_fragments) { EDeleteFragmentInfo(cpi); return FALSE; } - - ...are not useful. It's true that many platforms follow this - malloc behavior, but many do not. The more modern malloc - strategy is only to allocate virtual pages, which are not mapped - until the memory on that page is touched. At *that* point, if - the machine is out of heap, the page fails to be mapped and a - SEGV is generated. - - That means that if we want to deal with out of memory conditions, - we *must* be prepared to process a SEGV. If we implement the - SEGV handler, there's no reason to to check malloc return; it is - a waste of code. */ - - cpi->FragmentLastQ = - _ogg_malloc(cpi->pb.UnitFragments* - sizeof(*cpi->FragmentLastQ)); - cpi->FragTokens = - _ogg_malloc(cpi->pb.UnitFragments* - sizeof(*cpi->FragTokens)); - cpi->OriginalDC = - _ogg_malloc(cpi->pb.UnitFragments* - sizeof(*cpi->OriginalDC)); - cpi->FragTokenCounts = - _ogg_malloc(cpi->pb.UnitFragments* - sizeof(*cpi->FragTokenCounts)); - cpi->RunHuffIndices = - _ogg_malloc(cpi->pb.UnitFragments* - sizeof(*cpi->RunHuffIndices)); - cpi->LastCodedErrorScore = - _ogg_malloc(cpi->pb.UnitFragments* - sizeof(*cpi->LastCodedErrorScore)); - cpi->BlockCodedFlags = - _ogg_malloc(cpi->pb.UnitFragments* - sizeof(*cpi->BlockCodedFlags)); - cpi->ModeList = - _ogg_malloc(cpi->pb.UnitFragments* - sizeof(*cpi->ModeList)); - cpi->MVList = - _ogg_malloc(cpi->pb.UnitFragments* - sizeof(*cpi->MVList)); - cpi->DCT_codes = - _ogg_malloc(64* - sizeof(*cpi->DCT_codes)); - cpi->DCTDataBuffer = - _ogg_malloc(64* - sizeof(*cpi->DCTDataBuffer)); - cpi->quantized_list = - _ogg_malloc(64* - sizeof(*cpi->quantized_list)); - cpi->PartiallyCodedFlags = - _ogg_malloc(cpi->pb.MacroBlocks* - sizeof(*cpi->PartiallyCodedFlags)); - cpi->PartiallyCodedMbPatterns = - _ogg_malloc(cpi->pb.MacroBlocks* - sizeof(*cpi->PartiallyCodedMbPatterns)); - cpi->UncodedMbFlags = - _ogg_malloc(cpi->pb.MacroBlocks* - sizeof(*cpi->UncodedMbFlags)); - -} - -static void EClearFrameInfo(CP_INSTANCE * cpi) { - if(cpi->ConvDestBuffer ) - _ogg_free(cpi->ConvDestBuffer ); - cpi->ConvDestBuffer = 0; - - if(cpi->yuv0ptr) - _ogg_free(cpi->yuv0ptr); - cpi->yuv0ptr = 0; - - if(cpi->yuv1ptr) - _ogg_free(cpi->yuv1ptr); - cpi->yuv1ptr = 0; - - if(cpi->OptimisedTokenListEb ) - _ogg_free(cpi->OptimisedTokenListEb); - cpi->OptimisedTokenListEb = 0; - - if(cpi->OptimisedTokenList ) - _ogg_free(cpi->OptimisedTokenList); - cpi->OptimisedTokenList = 0; - - if(cpi->OptimisedTokenListHi ) - _ogg_free(cpi->OptimisedTokenListHi); - cpi->OptimisedTokenListHi = 0; - - if(cpi->OptimisedTokenListPl ) - _ogg_free(cpi->OptimisedTokenListPl); - cpi->OptimisedTokenListPl = 0; - -} - -static void EInitFrameInfo(CP_INSTANCE * cpi){ - int FrameSize = cpi->pb.ReconYPlaneSize + 2 * cpi->pb.ReconUVPlaneSize; - - /* clear any existing info */ - EClearFrameInfo(cpi); - - /* allocate frames */ - cpi->ConvDestBuffer = - _ogg_malloc(FrameSize* - sizeof(*cpi->ConvDestBuffer)); - cpi->yuv0ptr = - _ogg_malloc(FrameSize* - sizeof(*cpi->yuv0ptr)); - cpi->yuv1ptr = - _ogg_malloc(FrameSize* - sizeof(*cpi->yuv1ptr)); - cpi->OptimisedTokenListEb = - _ogg_malloc(FrameSize* - sizeof(*cpi->OptimisedTokenListEb)); - cpi->OptimisedTokenList = - _ogg_malloc(FrameSize* - sizeof(*cpi->OptimisedTokenList)); - cpi->OptimisedTokenListHi = - _ogg_malloc(FrameSize* - sizeof(*cpi->OptimisedTokenListHi)); - cpi->OptimisedTokenListPl = - _ogg_malloc(FrameSize* - sizeof(*cpi->OptimisedTokenListPl)); -} - -static void SetupKeyFrame(CP_INSTANCE *cpi) { - /* Make sure the "last frame" buffer contains the first frame data - as well. */ - memcpy ( cpi->yuv0ptr, cpi->yuv1ptr, - cpi->pb.ReconYPlaneSize + 2 * cpi->pb.ReconUVPlaneSize ); - - /* Initialise the cpi->pb.display_fragments and other fragment - structures for the first frame. */ - memset( cpi->pb.display_fragments, 1, cpi->pb.UnitFragments ); - memset( cpi->extra_fragments, 1, cpi->pb.UnitFragments ); - - /* Set up for a KEY FRAME */ - cpi->pb.FrameType = KEY_FRAME; -} - -static void AdjustKeyFrameContext(CP_INSTANCE *cpi) { - ogg_uint32_t i; - ogg_uint32_t AvKeyFrameFrequency = - (ogg_uint32_t) (cpi->CurrentFrame / cpi->KeyFrameCount); - ogg_uint32_t AvKeyFrameBytes = - (ogg_uint32_t) (cpi->TotKeyFrameBytes / cpi->KeyFrameCount); - ogg_uint32_t TotalWeight=0; - ogg_int32_t AvKeyFramesPerSecond; - ogg_int32_t MinFrameTargetRate; - - /* Update the frame carry over. */ - cpi->TotKeyFrameBytes += oggpackB_bytes(cpi->oggbuffer); - - /* reset keyframe context and calculate weighted average of last - KEY_FRAME_CONTEXT keyframes */ - for( i = 0 ; i < KEY_FRAME_CONTEXT ; i ++ ) { - if ( i < KEY_FRAME_CONTEXT -1) { - cpi->PriorKeyFrameSize[i] = cpi->PriorKeyFrameSize[i+1]; - cpi->PriorKeyFrameDistance[i] = cpi->PriorKeyFrameDistance[i+1]; - } else { - cpi->PriorKeyFrameSize[KEY_FRAME_CONTEXT - 1] = - oggpackB_bytes(cpi->oggbuffer); - cpi->PriorKeyFrameDistance[KEY_FRAME_CONTEXT - 1] = - cpi->LastKeyFrame; - } - - AvKeyFrameBytes += PriorKeyFrameWeight[i] * - cpi->PriorKeyFrameSize[i]; - AvKeyFrameFrequency += PriorKeyFrameWeight[i] * - cpi->PriorKeyFrameDistance[i]; - TotalWeight += PriorKeyFrameWeight[i]; - } - AvKeyFrameBytes /= TotalWeight; - AvKeyFrameFrequency /= TotalWeight; - AvKeyFramesPerSecond = 100 * cpi->Configuration.OutputFrameRate / - AvKeyFrameFrequency ; - - /* Calculate a new target rate per frame allowing for average key - frame frequency over newest frames . */ - if ( 100 * cpi->Configuration.TargetBandwidth > - AvKeyFrameBytes * AvKeyFramesPerSecond && - (100 * cpi->Configuration.OutputFrameRate - AvKeyFramesPerSecond )){ - cpi->frame_target_rate = - (ogg_int32_t)(100* cpi->Configuration.TargetBandwidth - - AvKeyFrameBytes * AvKeyFramesPerSecond ) / - ( (100 * cpi->Configuration.OutputFrameRate - AvKeyFramesPerSecond ) ); - } else { - /* don't let this number get too small!!! */ - cpi->frame_target_rate = 1; - } - - /* minimum allowable frame_target_rate */ - MinFrameTargetRate = (cpi->Configuration.TargetBandwidth / - cpi->Configuration.OutputFrameRate) / 3; - - if(cpi->frame_target_rate < MinFrameTargetRate ) { - cpi->frame_target_rate = MinFrameTargetRate; - } - - cpi->LastKeyFrame = 1; - cpi->LastKeyFrameSize=oggpackB_bytes(cpi->oggbuffer); - -} - -static void UpdateFrame(CP_INSTANCE *cpi){ - - double CorrectionFactor; - - /* Reset the DC predictors. */ - cpi->pb.LastIntraDC = 0; - cpi->pb.InvLastIntraDC = 0; - cpi->pb.LastInterDC = 0; - cpi->pb.InvLastInterDC = 0; - - /* Initialise bit packing mechanism. */ - oggpackB_reset(cpi->oggbuffer); - - /* mark as video frame */ - oggpackB_write(cpi->oggbuffer,0,1); - - /* Write out the frame header information including size. */ - WriteFrameHeader(cpi); - - /* Copy back any extra frags that are to be updated by the codec - as part of the background cleanup task */ - CopyBackExtraFrags(cpi); - - /* Encode the data. */ - EncodeData(cpi); - - /* Adjust drop frame trigger. */ - if ( cpi->pb.FrameType != KEY_FRAME ) { - /* Apply decay factor then add in the last frame size. */ - cpi->DropFrameTriggerBytes = - ((cpi->DropFrameTriggerBytes * (DF_CANDIDATE_WINDOW-1)) / - DF_CANDIDATE_WINDOW) + oggpackB_bytes(cpi->oggbuffer); - }else{ - /* Increase cpi->DropFrameTriggerBytes a little. Just after a key - frame may actually be a good time to drop a frame. */ - cpi->DropFrameTriggerBytes = - (cpi->DropFrameTriggerBytes * DF_CANDIDATE_WINDOW) / - (DF_CANDIDATE_WINDOW-1); - } - - /* Test for overshoot which may require a dropped frame next time - around. If we are already in a drop frame condition but the - previous frame was not dropped then the threshold for continuing - to allow dropped frames is reduced. */ - if ( cpi->DropFrameCandidate ) { - if ( cpi->DropFrameTriggerBytes > - (cpi->frame_target_rate * (DF_CANDIDATE_WINDOW+1)) ) - cpi->DropFrameCandidate = 1; - else - cpi->DropFrameCandidate = 0; - } else { - if ( cpi->DropFrameTriggerBytes > - (cpi->frame_target_rate * ((DF_CANDIDATE_WINDOW*2)-2)) ) - cpi->DropFrameCandidate = 1; - else - cpi->DropFrameCandidate = 0; - } - - /* Update the BpbCorrectionFactor variable according to whether or - not we were close enough with our selection of DCT quantiser. */ - if ( cpi->pb.FrameType != KEY_FRAME ) { - /* Work out a size correction factor. */ - CorrectionFactor = (double)oggpackB_bytes(cpi->oggbuffer) / - (double)cpi->ThisFrameTargetBytes; - - if ( (CorrectionFactor > 1.05) && - (cpi->pb.ThisFrameQualityValue < - cpi->pb.QThreshTable[cpi->Configuration.ActiveMaxQ]) ) { - CorrectionFactor = 1.0 + ((CorrectionFactor - 1.0)/2); - if ( CorrectionFactor > 1.5 ) - cpi->BpbCorrectionFactor *= 1.5; - else - cpi->BpbCorrectionFactor *= CorrectionFactor; - - /* Keep BpbCorrectionFactor within limits */ - if ( cpi->BpbCorrectionFactor > MAX_BPB_FACTOR ) - cpi->BpbCorrectionFactor = MAX_BPB_FACTOR; - } else if ( (CorrectionFactor < 0.95) && - (cpi->pb.ThisFrameQualityValue > VERY_BEST_Q) ){ - CorrectionFactor = 1.0 - ((1.0 - CorrectionFactor)/2); - if ( CorrectionFactor < 0.75 ) - cpi->BpbCorrectionFactor *= 0.75; - else - cpi->BpbCorrectionFactor *= CorrectionFactor; - - /* Keep BpbCorrectionFactor within limits */ - if ( cpi->BpbCorrectionFactor < MIN_BPB_FACTOR ) - cpi->BpbCorrectionFactor = MIN_BPB_FACTOR; - } - } - - /* Adjust carry over and or key frame context. */ - if ( cpi->pb.FrameType == KEY_FRAME ) { - /* Adjust the key frame context unless the key frame was very small */ - AdjustKeyFrameContext(cpi); - } else { - /* Update the frame carry over */ - cpi->CarryOver += ((ogg_int32_t)cpi->frame_target_rate - - (ogg_int32_t)oggpackB_bytes(cpi->oggbuffer)); - } - cpi->TotalByteCount += oggpackB_bytes(cpi->oggbuffer); -} - -static void CompressFirstFrame(CP_INSTANCE *cpi) { - ogg_uint32_t i; - - /* set up context of key frame sizes and distances for more local - datarate control */ - for( i = 0 ; i < KEY_FRAME_CONTEXT ; i ++ ) { - cpi->PriorKeyFrameSize[i] = cpi->Configuration.KeyFrameDataTarget; - cpi->PriorKeyFrameDistance[i] = cpi->pb.info.keyframe_frequency_force; - } - - /* Keep track of the total number of Key Frames Coded. */ - cpi->KeyFrameCount = 1; - cpi->LastKeyFrame = 1; - cpi->TotKeyFrameBytes = 0; - - /* A key frame is not a dropped frame there for reset the count of - consequative dropped frames. */ - cpi->DropCount = 0; - - SetupKeyFrame(cpi); - - /* Calculate a new target rate per frame allowing for average key - frame frequency and size thus far. */ - if ( cpi->Configuration.TargetBandwidth > - ((cpi->Configuration.KeyFrameDataTarget * - cpi->Configuration.OutputFrameRate)/ - cpi->pb.info.keyframe_frequency) ) { - - cpi->frame_target_rate = - (ogg_int32_t)((cpi->Configuration.TargetBandwidth - - ((cpi->Configuration.KeyFrameDataTarget * - cpi->Configuration.OutputFrameRate)/ - cpi->pb.info.keyframe_frequency)) / - cpi->Configuration.OutputFrameRate); - }else - cpi->frame_target_rate = 1; - - /* Set baseline frame target rate. */ - cpi->BaseLineFrameTargetRate = cpi->frame_target_rate; - - /* A key frame is not a dropped frame there for reset the count of - consequative dropped frames. */ - cpi->DropCount = 0; - - /* Initialise drop frame trigger to 5 frames worth of data. */ - cpi->DropFrameTriggerBytes = cpi->frame_target_rate * DF_CANDIDATE_WINDOW; - - /* Set a target size for this key frame based upon the baseline - target and frequency */ - cpi->ThisFrameTargetBytes = cpi->Configuration.KeyFrameDataTarget; - - /* Get a DCT quantizer level for the key frame. */ - cpi->MotionScore = cpi->pb.UnitFragments; - - RegulateQ(cpi, cpi->pb.UnitFragments); - - cpi->pb.LastFrameQualityValue = cpi->pb.ThisFrameQualityValue; - - /* Initialise quantizer. */ - UpdateQC(cpi, cpi->pb.ThisFrameQualityValue ); - - /* Initialise the cpi->pb.display_fragments and other fragment - structures for the first frame. */ - for ( i = 0; i < cpi->pb.UnitFragments; i ++ ) - cpi->FragmentLastQ[i] = cpi->pb.ThisFrameQualityValue; - - /* Compress and output the frist frame. */ - PickIntra( cpi, - cpi->pb.YSBRows, cpi->pb.YSBCols); - UpdateFrame(cpi); - - /* Initialise the carry over rate targeting variables. */ - cpi->CarryOver = 0; - -} - -static void CompressKeyFrame(CP_INSTANCE *cpi){ - ogg_uint32_t i; - - /* Before we compress reset the carry over to the actual frame carry over */ - cpi->CarryOver = cpi->Configuration.TargetBandwidth * cpi->CurrentFrame / - cpi->Configuration.OutputFrameRate - cpi->TotalByteCount; - - /* Keep track of the total number of Key Frames Coded */ - cpi->KeyFrameCount += 1; - - /* A key frame is not a dropped frame there for reset the count of - consequative dropped frames. */ - cpi->DropCount = 0; - - SetupKeyFrame(cpi); - - /* set a target size for this frame */ - cpi->ThisFrameTargetBytes = (ogg_int32_t) cpi->frame_target_rate + - ( (cpi->Configuration.KeyFrameDataTarget - cpi->frame_target_rate) * - cpi->LastKeyFrame / cpi->pb.info.keyframe_frequency_force ); - - if ( cpi->ThisFrameTargetBytes > cpi->Configuration.KeyFrameDataTarget ) - cpi->ThisFrameTargetBytes = cpi->Configuration.KeyFrameDataTarget; - - /* Get a DCT quantizer level for the key frame. */ - cpi->MotionScore = cpi->pb.UnitFragments; - - RegulateQ(cpi, cpi->pb.UnitFragments); - - cpi->pb.LastFrameQualityValue = cpi->pb.ThisFrameQualityValue; - - /* Initialise DCT tables. */ - UpdateQC(cpi, cpi->pb.ThisFrameQualityValue ); - - /* Initialise the cpi->pb.display_fragments and other fragment - structures for the first frame. */ - for ( i = 0; i < cpi->pb.UnitFragments; i ++ ) - cpi->FragmentLastQ[i] = cpi->pb.ThisFrameQualityValue; - - - /* Compress and output the frist frame. */ - PickIntra( cpi, - cpi->pb.YSBRows, cpi->pb.YSBCols); - UpdateFrame(cpi); - -} - -static void CompressFrame( CP_INSTANCE *cpi) { - ogg_int32_t min_blocks_per_frame; - ogg_uint32_t i; - int DropFrame = 0; - ogg_uint32_t ResidueBlocksAdded=0; - ogg_uint32_t KFIndicator = 0; - - double QModStep; - double QModifier = 1.0; - - /* Clear down the macro block level mode and MV arrays. */ - for ( i = 0; i < cpi->pb.UnitFragments; i++ ) { - cpi->pb.FragCodingMethod[i] = CODE_INTER_NO_MV; /* Default coding mode */ - cpi->pb.FragMVect[i].x = 0; - cpi->pb.FragMVect[i].y = 0; - } - - /* Default to delta frames. */ - cpi->pb.FrameType = DELTA_FRAME; - - /* Clear down the difference arrays for the current frame. */ - memset( cpi->pb.display_fragments, 0, cpi->pb.UnitFragments ); - memset( cpi->extra_fragments, 0, cpi->pb.UnitFragments ); - - /* Calculate the target bytes for this frame. */ - cpi->ThisFrameTargetBytes = cpi->frame_target_rate; - - /* Correct target to try and compensate for any overall rate error - that is developing */ - - /* Set the max allowed Q for this frame based upon carry over - history. First set baseline worst Q for this frame */ - cpi->Configuration.ActiveMaxQ = cpi->Configuration.MaxQ + 10; - if ( cpi->Configuration.ActiveMaxQ >= Q_TABLE_SIZE ) - cpi->Configuration.ActiveMaxQ = Q_TABLE_SIZE - 1; - - /* Make a further adjustment based upon the carry over and recent - history.. cpi->Configuration.ActiveMaxQ reduced by 1 for each 1/2 - seconds worth of -ve carry over up to a limit of 6. Also - cpi->Configuration.ActiveMaxQ reduced if frame is a - "DropFrameCandidate". Remember that if we are behind the bit - target carry over is -ve. */ - if ( cpi->CarryOver < 0 ) { - if ( cpi->DropFrameCandidate ) { - cpi->Configuration.ActiveMaxQ -= 4; - } - - if ( cpi->CarryOver < - -((ogg_int32_t)cpi->Configuration.TargetBandwidth*3) ) - cpi->Configuration.ActiveMaxQ -= 6; - else - cpi->Configuration.ActiveMaxQ += - (ogg_int32_t) ((cpi->CarryOver*2) / - (ogg_int32_t)cpi->Configuration.TargetBandwidth); - - /* Check that we have not dropped quality too far */ - if ( cpi->Configuration.ActiveMaxQ < cpi->Configuration.MaxQ ) - cpi->Configuration.ActiveMaxQ = cpi->Configuration.MaxQ; - } - - /* Calculate the Q Modifier step size required to cause a step down - from full target bandwidth to 40% of target between max Q and - best Q */ - QModStep = 0.5 / (double)((Q_TABLE_SIZE - 1) - - cpi->Configuration.ActiveMaxQ); - - /* Set up the cpi->QTargetModifier[] table. */ - for ( i = 0; i < cpi->Configuration.ActiveMaxQ; i++ ) { - cpi->QTargetModifier[i] = QModifier; - } - for ( i = cpi->Configuration.ActiveMaxQ; i < Q_TABLE_SIZE; i++ ) { - cpi->QTargetModifier[i] = QModifier; - QModifier -= QModStep; - } - - /* if we are allowed to drop frames and are falling behind (eg more - than x frames worth of bandwidth) */ - if ( cpi->pb.info.dropframes_p && - ( cpi->DropCount < cpi->MaxConsDroppedFrames) && - ( cpi->CarryOver < - -((ogg_int32_t)cpi->Configuration.TargetBandwidth)) && - ( cpi->DropFrameCandidate) ) { - /* (we didn't do this frame so we should have some left over for - the next frame) */ - cpi->CarryOver += cpi->frame_target_rate; - DropFrame = 1; - cpi->DropCount ++; - - /* Adjust DropFrameTriggerBytes to account for the saving achieved. */ - cpi->DropFrameTriggerBytes = - (cpi->DropFrameTriggerBytes * - (DF_CANDIDATE_WINDOW-1))/DF_CANDIDATE_WINDOW; - - /* Even if we drop a frame we should account for it when - considering key frame seperation. */ - cpi->LastKeyFrame++; - } else if ( cpi->CarryOver < - -((ogg_int32_t)cpi->Configuration.TargetBandwidth * 2) ) { - /* Reduce frame bit target by 1.75% for each 1/10th of a seconds - worth of -ve carry over down to a minimum of 65% of its - un-modified value. */ - - cpi->ThisFrameTargetBytes = - (ogg_uint32_t)(cpi->ThisFrameTargetBytes * 0.65); - } else if ( cpi->CarryOver < 0 ) { - /* Note that cpi->CarryOver is a -ve here hence 1.0 "+" ... */ - cpi->ThisFrameTargetBytes = - (ogg_uint32_t)(cpi->ThisFrameTargetBytes * - (1.0 + ( ((cpi->CarryOver * 10)/ - ((ogg_int32_t)cpi-> - Configuration.TargetBandwidth)) * 0.0175) )); - } - - if ( !DropFrame ) { - /* pick all the macroblock modes and motion vectors */ - ogg_uint32_t InterError; - ogg_uint32_t IntraError; - - - /* Set Baseline filter level. */ - ConfigurePP( &cpi->pp, cpi->pb.info.noise_sensitivity); - - /* Score / analyses the fragments. */ - cpi->MotionScore = YUVAnalyseFrame(&cpi->pp, &KFIndicator ); - - /* Get the baseline Q value */ - RegulateQ( cpi, cpi->MotionScore ); - - /* Recode blocks if the error score in last frame was high. */ - ResidueBlocksAdded = 0; - for ( i = 0; i < cpi->pb.UnitFragments; i++ ){ - if ( !cpi->pb.display_fragments[i] ){ - if ( cpi->LastCodedErrorScore[i] >= - ResidueErrorThresh[cpi->pb.FrameQIndex] ) { - cpi->pb.display_fragments[i] = 1; /* Force block update */ - cpi->extra_fragments[i] = 1; /* Insures up to date - pixel data is used. */ - ResidueBlocksAdded ++; - } - } - } - - /* Adjust the motion score to allow for residue blocks - added. These are assumed to have below average impact on - bitrate (Hence ResidueBlockFactor). */ - cpi->MotionScore = cpi->MotionScore + - (ResidueBlocksAdded / ResidueBlockFactor[cpi->pb.FrameQIndex]); - - /* Estimate the min number of blocks at best Q */ - min_blocks_per_frame = - (ogg_int32_t)(cpi->ThisFrameTargetBytes / - GetEstimatedBpb( cpi, VERY_BEST_Q )); - if ( min_blocks_per_frame == 0 ) - min_blocks_per_frame = 1; - - /* If we have less than this number then consider adding in some - extra blocks */ - if ( cpi->MotionScore < min_blocks_per_frame ) { - min_blocks_per_frame = - cpi->MotionScore + - (ogg_int32_t)(((min_blocks_per_frame - cpi->MotionScore) * 4) / 3 ); - UpRegulateDataStream( cpi, VERY_BEST_Q, min_blocks_per_frame ); - }else{ - /* Reset control variable for best quality final pass. */ - cpi->FinalPassLastPos = 0; - } - - /* Get the modified Q prediction taking into account extra blocks added. */ - RegulateQ( cpi, cpi->MotionScore ); - - /* Unless we are already well ahead (4 seconds of data) of the - projected bitrate */ - if ( cpi->CarryOver < - (ogg_int32_t)(cpi->Configuration.TargetBandwidth * 4) ){ - /* Look at the predicted Q (pbi->FrameQIndex). Adjust the - target bits for this frame based upon projected Q and - re-calculate. The idea is that if the Q is better than a - given (good enough) level then we will try and save some bits - for use in more difficult segments. */ - cpi->ThisFrameTargetBytes = - (ogg_int32_t) (cpi->ThisFrameTargetBytes * - cpi->QTargetModifier[cpi->pb.FrameQIndex]); - - /* Recalculate Q again */ - RegulateQ( cpi, cpi->MotionScore ); - } - - - /* Select modes and motion vectors for each of the blocks : return - an error score for inter and intra */ - PickModes( cpi, cpi->pb.YSBRows, cpi->pb.YSBCols, - cpi->pb.info.width, - &InterError, &IntraError ); - - /* decide whether we really should have made this frame a key frame */ - /* forcing out a keyframe if the max interval is up is done at a higher level */ - if( cpi->pb.info.keyframe_auto_p){ - if( ( 2* IntraError < 5 * InterError ) - && ( KFIndicator >= (ogg_uint32_t) - cpi->pb.info.keyframe_auto_threshold) - && ( cpi->LastKeyFrame > cpi->pb.info.keyframe_mindistance) - ){ - CompressKeyFrame(cpi); /* Code a key frame */ - return; - } - - } - - /* Increment the frames since last key frame count */ - cpi->LastKeyFrame++; - - /* Proceed with the frame update. */ - UpdateFrame(cpi); - cpi->DropCount = 0; - - if ( cpi->MotionScore > 0 ){ - /* Note the Quantizer used for each block coded. */ - for ( i = 0; i < cpi->pb.UnitFragments; i++ ){ - if ( cpi->pb.display_fragments[i] ){ - cpi->FragmentLastQ[i] = cpi->pb.ThisFrameQualityValue; - } - } - - } - }else{ - /* even if we 'drop' a frame, a placeholder must be written as we - currently assume fixed frame rate timebase as Ogg mapping - invariant */ - UpdateFrame(cpi); - } -} - -/********************** The toplevel: encode ***********************/ - -static int _ilog(unsigned int v){ - int ret=0; - while(v){ - ret++; - v>>=1; - } - return(ret); -} - -static void theora_encode_dispatch_init(CP_INSTANCE *cpi); - -int theora_encode_init(theora_state *th, theora_info *c){ - int i; - - CP_INSTANCE *cpi; - - memset(th, 0, sizeof(*th)); - /*Currently only the 4:2:0 format is supported.*/ - if(c->pixelformat!=OC_PF_420)return OC_IMPL; - th->internal_encode=cpi=_ogg_calloc(1,sizeof(*cpi)); - theora_encode_dispatch_init(cpi); - - dsp_static_init (&cpi->dsp); - memcpy (&cpi->pb.dsp, &cpi->dsp, sizeof(DspFunctions)); - - c->version_major=TH_VERSION_MAJOR; - c->version_minor=TH_VERSION_MINOR; - c->version_subminor=TH_VERSION_SUB; - - InitTmpBuffers(&cpi->pb); - InitPPInstance(&cpi->pp, &cpi->dsp); - - /* Initialise Configuration structure to legal values */ - if(c->quality>63)c->quality=63; - if(c->quality<0)c->quality=32; - if(c->target_bitrate<0)c->target_bitrate=0; - /* we clamp target_bitrate to 24 bits after setting up the encoder */ - - cpi->Configuration.BaseQ = c->quality; - cpi->Configuration.FirstFrameQ = c->quality; - cpi->Configuration.MaxQ = c->quality; - cpi->Configuration.ActiveMaxQ = c->quality; - - cpi->MVChangeFactor = 14; - cpi->FourMvChangeFactor = 8; - cpi->MinImprovementForNewMV = 25; - cpi->ExhaustiveSearchThresh = 2500; - cpi->MinImprovementForFourMV = 100; - cpi->FourMVThreshold = 10000; - cpi->BitRateCapFactor = 1.5; - cpi->InterTripOutThresh = 5000; - cpi->MVEnabled = 1; - cpi->InterCodeCount = 127; - cpi->BpbCorrectionFactor = 1.0; - cpi->GoldenFrameEnabled = 1; - cpi->InterPrediction = 1; - cpi->MotionCompensation = 1; - cpi->ThreshMapThreshold = 5; - cpi->MaxConsDroppedFrames = 1; - - /* Set encoder flags. */ - /* if not AutoKeyframing cpi->ForceKeyFrameEvery = is frequency */ - if(!c->keyframe_auto_p) - c->keyframe_frequency_force = c->keyframe_frequency; - - /* Set the frame rate variables. */ - if ( c->fps_numerator < 1 ) - c->fps_numerator = 1; - if ( c->fps_denominator < 1 ) - c->fps_denominator = 1; - - /* don't go too nuts on keyframe spacing; impose a high limit to - make certain the granulepos encoding strategy works */ - if(c->keyframe_frequency_force>32768)c->keyframe_frequency_force=32768; - if(c->keyframe_mindistance>32768)c->keyframe_mindistance=32768; - if(c->keyframe_mindistance>c->keyframe_frequency_force) - c->keyframe_mindistance=c->keyframe_frequency_force; - cpi->pb.keyframe_granule_shift=_ilog(c->keyframe_frequency_force-1); - - /* clamp the target_bitrate to a maximum of 24 bits so we get a - more meaningful value when we write this out in the header. */ - if(c->target_bitrate>(1<<24)-1)c->target_bitrate=(1<<24)-1; - - /* copy in config */ - memcpy(&cpi->pb.info,c,sizeof(*c)); - th->i=&cpi->pb.info; - th->granulepos=-1; - - /* Set up default values for QTargetModifier[Q_TABLE_SIZE] table */ - for ( i = 0; i < Q_TABLE_SIZE; i++ ) - cpi->QTargetModifier[i] = 1.0; - - /* Set up an encode buffer */ - cpi->oggbuffer = _ogg_malloc(sizeof(oggpack_buffer)); - oggpackB_writeinit(cpi->oggbuffer); - - /* Set data rate related variables. */ - cpi->Configuration.TargetBandwidth = (c->target_bitrate) / 8; - - cpi->Configuration.OutputFrameRate = - (double)( c->fps_numerator / - c->fps_denominator ); - - cpi->frame_target_rate = cpi->Configuration.TargetBandwidth / - cpi->Configuration.OutputFrameRate; - - /* Set key frame data rate target; this is nominal keyframe size */ - cpi->Configuration.KeyFrameDataTarget = (c->keyframe_data_target_bitrate * - c->fps_denominator / - c->fps_numerator ) / 8; - - /* Note the height and width in the pre-processor control structure. */ - cpi->ScanConfig.VideoFrameHeight = cpi->pb.info.height; - cpi->ScanConfig.VideoFrameWidth = cpi->pb.info.width; - - InitFrameDetails(&cpi->pb); - EInitFragmentInfo(cpi); - EInitFrameInfo(cpi); - - /* Set up pre-processor config pointers. */ - cpi->ScanConfig.Yuv0ptr = cpi->yuv0ptr; - cpi->ScanConfig.Yuv1ptr = cpi->yuv1ptr; - cpi->ScanConfig.SrfWorkSpcPtr = cpi->ConvDestBuffer; - cpi->ScanConfig.disp_fragments = cpi->pb.display_fragments; - cpi->ScanConfig.RegionIndex = cpi->pb.pixel_index_table; - - /* Initialise the pre-processor module. */ - ScanYUVInit(&cpi->pp, &(cpi->ScanConfig)); - - /* Initialise Motion compensation */ - InitMotionCompensation(cpi); - - /* Initialise the compression process. */ - /* We always start at frame 1 */ - cpi->CurrentFrame = 1; - - /* Reset the rate targeting correction factor. */ - cpi->BpbCorrectionFactor = 1.0; - - cpi->TotalByteCount = 0; - cpi->TotalMotionScore = 0; - - /* Up regulation variables. */ - cpi->FinalPassLastPos = 0; /* Used to regulate a final unrestricted pass. */ - cpi->LastEndSB = 0; /* Where we were in the loop last time. */ - cpi->ResidueLastEndSB = 0; /* Where we were in the residue update - loop last time. */ - - InitHuffmanSet(&cpi->pb); - - /* This makes sure encoder version specific tables are initialised */ - memcpy(&cpi->pb.quant_info, &TH_VP31_QUANT_INFO, sizeof(th_quant_info)); - InitQTables(&cpi->pb); - - /* Indicate that the next frame to be compressed is the first in the - current clip. */ - cpi->ThisIsFirstFrame = 1; - cpi->readyflag = 1; - - cpi->pb.HeadersWritten = 0; - /*We overload this flag to track header output.*/ - cpi->doneflag=-3; - - return 0; -} - -int theora_encode_YUVin(theora_state *t, - yuv_buffer *yuv){ - ogg_int32_t i; - unsigned char *LocalDataPtr; - unsigned char *InputDataPtr; - CP_INSTANCE *cpi=(CP_INSTANCE *)(t->internal_encode); - - if(!cpi->readyflag)return OC_EINVAL; - if(cpi->doneflag>0)return OC_EINVAL; - - /* If frame size has changed, abort out for now */ - if (yuv->y_height != (int)cpi->pb.info.height || - yuv->y_width != (int)cpi->pb.info.width ) - return(-1); - - - /* Copy over input YUV to internal YUV buffers. */ - /* we invert the image for backward compatibility with VP3 */ - /* First copy over the Y data */ - LocalDataPtr = cpi->yuv1ptr + yuv->y_width*(yuv->y_height - 1); - InputDataPtr = yuv->y; - for ( i = 0; i < yuv->y_height; i++ ){ - memcpy( LocalDataPtr, InputDataPtr, yuv->y_width ); - LocalDataPtr -= yuv->y_width; - InputDataPtr += yuv->y_stride; - } - - /* Now copy over the U data */ - LocalDataPtr = &cpi->yuv1ptr[(yuv->y_height * yuv->y_width)]; - LocalDataPtr += yuv->uv_width*(yuv->uv_height - 1); - InputDataPtr = yuv->u; - for ( i = 0; i < yuv->uv_height; i++ ){ - memcpy( LocalDataPtr, InputDataPtr, yuv->uv_width ); - LocalDataPtr -= yuv->uv_width; - InputDataPtr += yuv->uv_stride; - } - - /* Now copy over the V data */ - LocalDataPtr = - &cpi->yuv1ptr[((yuv->y_height*yuv->y_width)*5)/4]; - LocalDataPtr += yuv->uv_width*(yuv->uv_height - 1); - InputDataPtr = yuv->v; - for ( i = 0; i < yuv->uv_height; i++ ){ - memcpy( LocalDataPtr, InputDataPtr, yuv->uv_width ); - LocalDataPtr -= yuv->uv_width; - InputDataPtr += yuv->uv_stride; - } - - /* Special case for first frame */ - if ( cpi->ThisIsFirstFrame ){ - CompressFirstFrame(cpi); - cpi->ThisIsFirstFrame = 0; - cpi->ThisIsKeyFrame = 0; - } else { - - /* don't allow generating invalid files that overflow the p-frame - shift, even if keyframe_auto_p is turned off */ - if(cpi->LastKeyFrame >= (ogg_uint32_t) - cpi->pb.info.keyframe_frequency_force) - cpi->ThisIsKeyFrame = 1; - - if ( cpi->ThisIsKeyFrame ) { - CompressKeyFrame(cpi); - cpi->ThisIsKeyFrame = 0; - } else { - /* Compress the frame. */ - CompressFrame( cpi ); - } - - } - - /* Update stats variables. */ - cpi->LastFrameSize = oggpackB_bytes(cpi->oggbuffer); - cpi->CurrentFrame++; - cpi->packetflag=1; - - t->granulepos= - ((cpi->CurrentFrame - cpi->LastKeyFrame)<pb.keyframe_granule_shift)+ - cpi->LastKeyFrame - 1; - - return 0; -} - -int theora_encode_packetout( theora_state *t, int last_p, ogg_packet *op){ - CP_INSTANCE *cpi=(CP_INSTANCE *)(t->internal_encode); - long bytes=oggpackB_bytes(cpi->oggbuffer); - - if(!bytes)return(0); - if(!cpi->packetflag)return(0); - if(cpi->doneflag>0)return(-1); - - op->packet=oggpackB_get_buffer(cpi->oggbuffer); - op->bytes=bytes; - op->b_o_s=0; - op->e_o_s=last_p; - - op->packetno=cpi->CurrentFrame; - op->granulepos=t->granulepos; - - cpi->packetflag=0; - if(last_p)cpi->doneflag=1; - - return 1; -} - -static void _tp_writebuffer(oggpack_buffer *opb, const char *buf, const long len) -{ - long i; - - for (i = 0; i < len; i++) - oggpackB_write(opb, *buf++, 8); -} - -static void _tp_writelsbint(oggpack_buffer *opb, long value) -{ - oggpackB_write(opb, value&0xFF, 8); - oggpackB_write(opb, value>>8&0xFF, 8); - oggpackB_write(opb, value>>16&0xFF, 8); - oggpackB_write(opb, value>>24&0xFF, 8); -} - -/* build the initial short header for stream recognition and format */ -int theora_encode_header(theora_state *t, ogg_packet *op){ - CP_INSTANCE *cpi=(CP_INSTANCE *)(t->internal_encode); - int offset_y; - - oggpackB_reset(cpi->oggbuffer); - oggpackB_write(cpi->oggbuffer,0x80,8); - _tp_writebuffer(cpi->oggbuffer, "theora", 6); - - oggpackB_write(cpi->oggbuffer,TH_VERSION_MAJOR,8); - oggpackB_write(cpi->oggbuffer,TH_VERSION_MINOR,8); - oggpackB_write(cpi->oggbuffer,TH_VERSION_SUB,8); - - oggpackB_write(cpi->oggbuffer,cpi->pb.info.width>>4,16); - oggpackB_write(cpi->oggbuffer,cpi->pb.info.height>>4,16); - oggpackB_write(cpi->oggbuffer,cpi->pb.info.frame_width,24); - oggpackB_write(cpi->oggbuffer,cpi->pb.info.frame_height,24); - oggpackB_write(cpi->oggbuffer,cpi->pb.info.offset_x,8); - /* Applications use offset_y to mean offset from the top of the image; the - * meaning in the bitstream is the opposite (from the bottom). Transform. - */ - offset_y = cpi->pb.info.height - cpi->pb.info.frame_height - - cpi->pb.info.offset_y; - oggpackB_write(cpi->oggbuffer,offset_y,8); - - oggpackB_write(cpi->oggbuffer,cpi->pb.info.fps_numerator,32); - oggpackB_write(cpi->oggbuffer,cpi->pb.info.fps_denominator,32); - oggpackB_write(cpi->oggbuffer,cpi->pb.info.aspect_numerator,24); - oggpackB_write(cpi->oggbuffer,cpi->pb.info.aspect_denominator,24); - - oggpackB_write(cpi->oggbuffer,cpi->pb.info.colorspace,8); - oggpackB_write(cpi->oggbuffer,cpi->pb.info.target_bitrate,24); - oggpackB_write(cpi->oggbuffer,cpi->pb.info.quality,6); - - oggpackB_write(cpi->oggbuffer,cpi->pb.keyframe_granule_shift,5); - - oggpackB_write(cpi->oggbuffer,cpi->pb.info.pixelformat,2); - - oggpackB_write(cpi->oggbuffer,0,3); /* spare config bits */ - - op->packet=oggpackB_get_buffer(cpi->oggbuffer); - op->bytes=oggpackB_bytes(cpi->oggbuffer); - - op->b_o_s=1; - op->e_o_s=0; - - op->packetno=0; - - op->granulepos=0; - cpi->packetflag=0; - - return(0); -} - -/* build the comment header packet from the passed metadata */ -int theora_encode_comment(theora_comment *tc, ogg_packet *op) -{ - const char *vendor = theora_version_string(); - const int vendor_length = strlen(vendor); - oggpack_buffer *opb; - - opb = _ogg_malloc(sizeof(oggpack_buffer)); - oggpackB_writeinit(opb); - oggpackB_write(opb, 0x81, 8); - _tp_writebuffer(opb, "theora", 6); - - _tp_writelsbint(opb, vendor_length); - _tp_writebuffer(opb, vendor, vendor_length); - - _tp_writelsbint(opb, tc->comments); - if(tc->comments){ - int i; - for(i=0;icomments;i++){ - if(tc->user_comments[i]){ - _tp_writelsbint(opb,tc->comment_lengths[i]); - _tp_writebuffer(opb,tc->user_comments[i],tc->comment_lengths[i]); - }else{ - oggpackB_write(opb,0,32); - } - } - } - op->bytes=oggpack_bytes(opb); - - /* So we're expecting the application will free this? */ - op->packet=_ogg_malloc(oggpack_bytes(opb)); - memcpy(op->packet, oggpack_get_buffer(opb), oggpack_bytes(opb)); - oggpack_writeclear(opb); - - _ogg_free(opb); - - op->b_o_s=0; - op->e_o_s=0; - - op->packetno=0; - op->granulepos=0; - - return (0); -} - -/* build the final header packet with the tables required - for decode */ -int theora_encode_tables(theora_state *t, ogg_packet *op){ - CP_INSTANCE *cpi=(CP_INSTANCE *)(t->internal_encode); - - oggpackB_reset(cpi->oggbuffer); - oggpackB_write(cpi->oggbuffer,0x82,8); - _tp_writebuffer(cpi->oggbuffer,"theora",6); - - WriteQTables(&cpi->pb,cpi->oggbuffer); - WriteHuffmanTrees(cpi->pb.HuffRoot_VP3x,cpi->oggbuffer); - - op->packet=oggpackB_get_buffer(cpi->oggbuffer); - op->bytes=oggpackB_bytes(cpi->oggbuffer); - - op->b_o_s=0; - op->e_o_s=0; - - op->packetno=0; - - op->granulepos=0; - cpi->packetflag=0; - - cpi->pb.HeadersWritten = 1; - - return(0); -} - -static void theora_encode_clear (theora_state *th){ - CP_INSTANCE *cpi; - cpi=(CP_INSTANCE *)th->internal_encode; - if(cpi){ - - ClearHuffmanSet(&cpi->pb); - ClearFragmentInfo(&cpi->pb); - ClearFrameInfo(&cpi->pb); - EClearFragmentInfo(cpi); - EClearFrameInfo(cpi); - ClearTmpBuffers(&cpi->pb); - ClearPPInstance(&cpi->pp); - - oggpackB_writeclear(cpi->oggbuffer); - _ogg_free(cpi->oggbuffer); - _ogg_free(cpi); - } - - memset(th,0,sizeof(*th)); -} - - -/* returns, in seconds, absolute time of current packet in given - logical stream */ -static double theora_encode_granule_time(theora_state *th, - ogg_int64_t granulepos){ -#ifndef THEORA_DISABLE_FLOAT - CP_INSTANCE *cpi=(CP_INSTANCE *)(th->internal_encode); - PB_INSTANCE *pbi=(PB_INSTANCE *)(th->internal_decode); - - if(cpi)pbi=&cpi->pb; - - if(granulepos>=0){ - ogg_int64_t iframe=granulepos>>pbi->keyframe_granule_shift; - ogg_int64_t pframe=granulepos-(iframe<keyframe_granule_shift); - - return (iframe+pframe)* - ((double)pbi->info.fps_denominator/pbi->info.fps_numerator); - - } -#endif - - return(-1); /* negative granulepos or float calculations disabled */ -} - -/* returns frame number of current packet in given logical stream */ -static ogg_int64_t theora_encode_granule_frame(theora_state *th, - ogg_int64_t granulepos){ - CP_INSTANCE *cpi=(CP_INSTANCE *)(th->internal_encode); - PB_INSTANCE *pbi=(PB_INSTANCE *)(th->internal_decode); - - if(cpi)pbi=&cpi->pb; - - if(granulepos>=0){ - ogg_int64_t iframe=granulepos>>pbi->keyframe_granule_shift; - ogg_int64_t pframe=granulepos-(iframe<keyframe_granule_shift); - - return (iframe+pframe-1); - } - - return(-1); -} - - -static int theora_encode_control(theora_state *th,int req, - void *buf,size_t buf_sz) { - CP_INSTANCE *cpi; - PB_INSTANCE *pbi; - int value; - - if(th == NULL) - return TH_EFAULT; - - cpi = th->internal_encode; - pbi = &cpi->pb; - - switch(req) { - case TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE: - { - ogg_uint32_t keyframe_frequency_force; - if( (buf==NULL) || (buf_sz!=sizeof(ogg_uint32_t))) return TH_EINVAL; - keyframe_frequency_force=*(ogg_uint32_t *)buf; - - keyframe_frequency_force= - OC_MINI(keyframe_frequency_force, - 1U<pb.keyframe_granule_shift); - cpi->pb.info.keyframe_frequency_force= - OC_MAXI(1,keyframe_frequency_force); - *(ogg_uint32_t *)buf=cpi->pb.info.keyframe_frequency_force; - return 0; - } - case TH_ENCCTL_SET_QUANT_PARAMS: - if( ( buf==NULL&&buf_sz!=0 ) - || ( buf!=NULL&&buf_sz!=sizeof(th_quant_info) ) - || cpi->pb.HeadersWritten ){ - return TH_EINVAL; - } - - if(buf==NULL) - memcpy(&pbi->quant_info, &TH_VP31_QUANT_INFO, sizeof(th_quant_info)); - else - memcpy(&pbi->quant_info, buf, sizeof(th_quant_info)); - InitQTables(pbi); - - return 0; - case TH_ENCCTL_SET_VP3_COMPATIBLE: - if(cpi->pb.HeadersWritten) - return TH_EINVAL; - - memcpy(&pbi->quant_info, &TH_VP31_QUANT_INFO, sizeof(th_quant_info)); - InitQTables(pbi); - - return 0; - case TH_ENCCTL_SET_SPLEVEL: - if(buf == NULL || buf_sz != sizeof(int)) - return TH_EINVAL; - - memcpy(&value, buf, sizeof(int)); - - switch(value) { - case 0: - cpi->MotionCompensation = 1; - pbi->info.quick_p = 0; - break; - - case 1: - cpi->MotionCompensation = 1; - pbi->info.quick_p = 1; - break; - - case 2: - cpi->MotionCompensation = 0; - pbi->info.quick_p = 1; - break; - - default: - return TH_EINVAL; - } - - return 0; - case TH_ENCCTL_GET_SPLEVEL_MAX: - value = 2; - memcpy(buf, &value, sizeof(int)); - return 0; - default: - return TH_EIMPL; - } -} - -static void theora_encode_dispatch_init(CP_INSTANCE *cpi){ - cpi->dispatch_vtbl.clear=theora_encode_clear; - cpi->dispatch_vtbl.control=theora_encode_control; - cpi->dispatch_vtbl.granule_frame=theora_encode_granule_frame; - cpi->dispatch_vtbl.granule_time=theora_encode_granule_time; -} diff --git a/Engine/lib/libtheora/lib/enc/frarray.c b/Engine/lib/libtheora/lib/enc/frarray.c deleted file mode 100644 index 51b327206..000000000 --- a/Engine/lib/libtheora/lib/enc/frarray.c +++ /dev/null @@ -1,243 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: frarray.c 15153 2008-08-04 18:37:55Z tterribe $ - - ********************************************************************/ - -#include -#include "codec_internal.h" -#include "block_inline.h" - -/* Long run bit string coding */ -static ogg_uint32_t FrArrayCodeSBRun( CP_INSTANCE *cpi, ogg_uint32_t value){ - ogg_uint32_t CodedVal = 0; - ogg_uint32_t CodedBits = 0; - - /* Coding scheme: - Codeword RunLength - 0 1 - 10x 2-3 - 110x 4-5 - 1110xx 6-9 - 11110xxx 10-17 - 111110xxxx 18-33 - 111111xxxxxxxxxxxx 34-4129 */ - - if ( value == 1 ){ - CodedVal = 0; - CodedBits = 1; - } else if ( value <= 3 ) { - CodedVal = 0x0004 + (value - 2); - CodedBits = 3; - } else if ( value <= 5 ) { - CodedVal = 0x000C + (value - 4); - CodedBits = 4; - } else if ( value <= 9 ) { - CodedVal = 0x0038 + (value - 6); - CodedBits = 6; - } else if ( value <= 17 ) { - CodedVal = 0x00F0 + (value - 10); - CodedBits = 8; - } else if ( value <= 33 ) { - CodedVal = 0x03E0 + (value - 18); - CodedBits = 10; - } else { - CodedVal = 0x3F000 + (value - 34); - CodedBits = 18; - } - - /* Add the bits to the encode holding buffer. */ - oggpackB_write( cpi->oggbuffer, CodedVal, CodedBits ); - - return CodedBits; -} - -/* Short run bit string coding */ -static ogg_uint32_t FrArrayCodeBlockRun( CP_INSTANCE *cpi, - ogg_uint32_t value ) { - ogg_uint32_t CodedVal = 0; - ogg_uint32_t CodedBits = 0; - - /* Coding scheme: - Codeword RunLength - 0x 1-2 - 10x 3-4 - 110x 5-6 - 1110xx 7-10 - 11110xx 11-14 - 11111xxxx 15-30 */ - - if ( value <= 2 ) { - CodedVal = value - 1; - CodedBits = 2; - } else if ( value <= 4 ) { - CodedVal = 0x0004 + (value - 3); - CodedBits = 3; - - } else if ( value <= 6 ) { - CodedVal = 0x000C + (value - 5); - CodedBits = 4; - - } else if ( value <= 10 ) { - CodedVal = 0x0038 + (value - 7); - CodedBits = 6; - - } else if ( value <= 14 ) { - CodedVal = 0x0078 + (value - 11); - CodedBits = 7; - } else { - CodedVal = 0x01F0 + (value - 15); - CodedBits = 9; - } - - /* Add the bits to the encode holding buffer. */ - oggpackB_write( cpi->oggbuffer, CodedVal, CodedBits ); - - return CodedBits; -} - -void PackAndWriteDFArray( CP_INSTANCE *cpi ){ - ogg_uint32_t i; - unsigned char val; - ogg_uint32_t run_count; - - ogg_uint32_t SB, MB, B; /* Block, MB and SB loop variables */ - ogg_uint32_t BListIndex = 0; - ogg_uint32_t LastSbBIndex = 0; - ogg_int32_t DfBlockIndex; /* Block index in display_fragments */ - - /* Initialise workspaces */ - memset( cpi->pb.SBFullyFlags, 1, cpi->pb.SuperBlocks); - memset( cpi->pb.SBCodedFlags, 0, cpi->pb.SuperBlocks ); - memset( cpi->PartiallyCodedFlags, 0, cpi->pb.SuperBlocks ); - memset( cpi->BlockCodedFlags, 0, cpi->pb.UnitFragments); - - for( SB = 0; SB < cpi->pb.SuperBlocks; SB++ ) { - /* Check for coded blocks and macro-blocks */ - for ( MB=0; MB<4; MB++ ) { - /* If MB in frame */ - if ( QuadMapToMBTopLeft(cpi->pb.BlockMap,SB,MB) >= 0 ) { - for ( B=0; B<4; B++ ) { - DfBlockIndex = QuadMapToIndex1( cpi->pb.BlockMap,SB, MB, B ); - - /* Does Block lie in frame: */ - if ( DfBlockIndex >= 0 ) { - /* In Frame: If it is not coded then this SB is only - partly coded.: */ - if ( cpi->pb.display_fragments[DfBlockIndex] ) { - cpi->pb.SBCodedFlags[SB] = 1; /* SB at least partly coded */ - cpi->BlockCodedFlags[BListIndex] = 1; /* Block is coded */ - - }else{ - cpi->pb.SBFullyFlags[SB] = 0; /* SB not fully coded */ - cpi->BlockCodedFlags[BListIndex] = 0; /* Block is not coded */ - } - - BListIndex++; - } - } - } - } - - /* Is the SB fully coded or uncoded. - If so then backup BListIndex and MBListIndex */ - if ( cpi->pb.SBFullyFlags[SB] || !cpi->pb.SBCodedFlags[SB] ) { - BListIndex = LastSbBIndex; /* Reset to values from previous SB */ - }else{ - cpi->PartiallyCodedFlags[SB] = 1; /* Set up list of partially - coded SBs */ - LastSbBIndex = BListIndex; - } - } - - /* Code list of partially coded Super-Block. */ - val = cpi->PartiallyCodedFlags[0]; - oggpackB_write( cpi->oggbuffer, (ogg_uint32_t)val, 1); - - i = 0; - while ( i < cpi->pb.SuperBlocks ) { - run_count = 0; - while ( (ipb.SuperBlocks) && - (cpi->PartiallyCodedFlags[i]==val) && - run_count<4129 ) { - i++; - run_count++; - } - - /* Code the run */ - FrArrayCodeSBRun( cpi, run_count); - - if(run_count >= 4129 && i < cpi->pb.SuperBlocks ){ - val = cpi->PartiallyCodedFlags[i]; - oggpackB_write( cpi->oggbuffer, (ogg_uint32_t)val, 1); - - }else - val = ( val == 0 ) ? 1 : 0; - } - - /* RLC Super-Block fully/not coded. */ - i = 0; - - /* Skip partially coded blocks */ - while( (i < cpi->pb.SuperBlocks) && cpi->PartiallyCodedFlags[i] ) - i++; - - if ( i < cpi->pb.SuperBlocks ) { - val = cpi->pb.SBFullyFlags[i]; - oggpackB_write( cpi->oggbuffer, (ogg_uint32_t)val, 1); - - while ( i < cpi->pb.SuperBlocks ) { - run_count = 0; - while ( (i < cpi->pb.SuperBlocks) && - (cpi->pb.SBFullyFlags[i] == val) && - run_count < 4129) { - i++; - /* Skip partially coded blocks */ - while( (i < cpi->pb.SuperBlocks) && cpi->PartiallyCodedFlags[i] ) - i++; - run_count++; - } - - /* Code the run */ - FrArrayCodeSBRun( cpi, run_count ); - - if(run_count >= 4129 && i < cpi->pb.SuperBlocks ){ - val = cpi->PartiallyCodedFlags[i]; - oggpackB_write( cpi->oggbuffer, (ogg_uint32_t)val, 1); - }else - val = ( val == 0 ) ? 1 : 0; - } - } - - - /* Now code the block flags */ - if ( BListIndex > 0 ) { - /* Code the block flags start value */ - val = cpi->BlockCodedFlags[0]; - oggpackB_write( cpi->oggbuffer, (ogg_uint32_t)val, 1); - - /* Now code the block flags. */ - for ( i = 0; i < BListIndex; ) { - run_count = 0; - while ( (i < BListIndex) && (cpi->BlockCodedFlags[i] == val) ) { - i++; - run_count++; - } - - FrArrayCodeBlockRun( cpi, run_count ); - - val = ( val == 0 ) ? 1 : 0; - } - } -} diff --git a/Engine/lib/libtheora/lib/enc/frinit.c b/Engine/lib/libtheora/lib/enc/frinit.c deleted file mode 100644 index ae6bbd64f..000000000 --- a/Engine/lib/libtheora/lib/enc/frinit.c +++ /dev/null @@ -1,392 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: frinit.c 15153 2008-08-04 18:37:55Z tterribe $ - - ********************************************************************/ - -#include -#include "codec_internal.h" - - -void InitializeFragCoordinates(PB_INSTANCE *pbi){ - - ogg_uint32_t i, j; - - ogg_uint32_t HorizFrags = pbi->HFragments; - ogg_uint32_t VertFrags = pbi->VFragments; - ogg_uint32_t StartFrag = 0; - - /* Y */ - - for(i = 0; i< VertFrags; i++){ - for(j = 0; j< HorizFrags; j++){ - - ogg_uint32_t ThisFrag = i * HorizFrags + j; - pbi->FragCoordinates[ ThisFrag ].x=j * BLOCK_HEIGHT_WIDTH; - pbi->FragCoordinates[ ThisFrag ].y=i * BLOCK_HEIGHT_WIDTH; - - } - } - - /* U */ - HorizFrags >>= 1; - VertFrags >>= 1; - StartFrag = pbi->YPlaneFragments; - - for(i = 0; i< VertFrags; i++) { - for(j = 0; j< HorizFrags; j++) { - ogg_uint32_t ThisFrag = StartFrag + i * HorizFrags + j; - pbi->FragCoordinates[ ThisFrag ].x=j * BLOCK_HEIGHT_WIDTH; - pbi->FragCoordinates[ ThisFrag ].y=i * BLOCK_HEIGHT_WIDTH; - - } - } - - /* V */ - StartFrag = pbi->YPlaneFragments + pbi->UVPlaneFragments; - for(i = 0; i< VertFrags; i++) { - for(j = 0; j< HorizFrags; j++) { - ogg_uint32_t ThisFrag = StartFrag + i * HorizFrags + j; - pbi->FragCoordinates[ ThisFrag ].x=j * BLOCK_HEIGHT_WIDTH; - pbi->FragCoordinates[ ThisFrag ].y=i * BLOCK_HEIGHT_WIDTH; - - } - } -} - -static void CalcPixelIndexTable( PB_INSTANCE *pbi){ - ogg_uint32_t i; - ogg_uint32_t * PixelIndexTablePtr; - - /* Calculate the pixel index table for normal image buffers */ - PixelIndexTablePtr = pbi->pixel_index_table; - for ( i = 0; i < pbi->YPlaneFragments; i++ ) { - PixelIndexTablePtr[ i ] = - ((i / pbi->HFragments) * VFRAGPIXELS * - pbi->info.width); - PixelIndexTablePtr[ i ] += - ((i % pbi->HFragments) * HFRAGPIXELS); - } - - PixelIndexTablePtr = &pbi->pixel_index_table[pbi->YPlaneFragments]; - for ( i = 0; i < ((pbi->HFragments >> 1) * pbi->VFragments); i++ ) { - PixelIndexTablePtr[ i ] = - ((i / (pbi->HFragments / 2) ) * - (VFRAGPIXELS * - (pbi->info.width / 2)) ); - PixelIndexTablePtr[ i ] += - ((i % (pbi->HFragments / 2) ) * - HFRAGPIXELS) + pbi->YPlaneSize; - } - - /************************************************************************/ - /* Now calculate the pixel index table for image reconstruction buffers */ - PixelIndexTablePtr = pbi->recon_pixel_index_table; - for ( i = 0; i < pbi->YPlaneFragments; i++ ){ - PixelIndexTablePtr[ i ] = - ((i / pbi->HFragments) * VFRAGPIXELS * - pbi->YStride); - PixelIndexTablePtr[ i ] += - ((i % pbi->HFragments) * HFRAGPIXELS) + - pbi->ReconYDataOffset; - } - - /* U blocks */ - PixelIndexTablePtr = &pbi->recon_pixel_index_table[pbi->YPlaneFragments]; - for ( i = 0; i < pbi->UVPlaneFragments; i++ ) { - PixelIndexTablePtr[ i ] = - ((i / (pbi->HFragments / 2) ) * - (VFRAGPIXELS * (pbi->UVStride)) ); - PixelIndexTablePtr[ i ] += - ((i % (pbi->HFragments / 2) ) * - HFRAGPIXELS) + pbi->ReconUDataOffset; - } - - /* V blocks */ - PixelIndexTablePtr = - &pbi->recon_pixel_index_table[pbi->YPlaneFragments + - pbi->UVPlaneFragments]; - - for ( i = 0; i < pbi->UVPlaneFragments; i++ ) { - PixelIndexTablePtr[ i ] = - ((i / (pbi->HFragments / 2) ) * - (VFRAGPIXELS * (pbi->UVStride)) ); - PixelIndexTablePtr[ i ] += - ((i % (pbi->HFragments / 2) ) * HFRAGPIXELS) + - pbi->ReconVDataOffset; - } -} - -void ClearFragmentInfo(PB_INSTANCE * pbi){ - - /* free prior allocs if present */ - if(pbi->display_fragments) _ogg_free(pbi->display_fragments); - if(pbi->pixel_index_table) _ogg_free(pbi->pixel_index_table); - if(pbi->recon_pixel_index_table) _ogg_free(pbi->recon_pixel_index_table); - if(pbi->FragTokenCounts) _ogg_free(pbi->FragTokenCounts); - if(pbi->CodedBlockList) _ogg_free(pbi->CodedBlockList); - if(pbi->FragMVect) _ogg_free(pbi->FragMVect); - if(pbi->FragCoeffs) _ogg_free(pbi->FragCoeffs); - if(pbi->FragCoefEOB) _ogg_free(pbi->FragCoefEOB); - if(pbi->skipped_display_fragments) _ogg_free(pbi->skipped_display_fragments); - if(pbi->QFragData) _ogg_free(pbi->QFragData); - if(pbi->TokenList) _ogg_free(pbi->TokenList); - if(pbi->FragCodingMethod) _ogg_free(pbi->FragCodingMethod); - if(pbi->FragCoordinates) _ogg_free(pbi->FragCoordinates); - - if(pbi->FragQIndex) _ogg_free(pbi->FragQIndex); - if(pbi->PPCoefBuffer) _ogg_free(pbi->PPCoefBuffer); - if(pbi->FragmentVariances) _ogg_free(pbi->FragmentVariances); - - if(pbi->BlockMap) _ogg_free(pbi->BlockMap); - - if(pbi->SBCodedFlags) _ogg_free(pbi->SBCodedFlags); - if(pbi->SBFullyFlags) _ogg_free(pbi->SBFullyFlags); - if(pbi->MBFullyFlags) _ogg_free(pbi->MBFullyFlags); - if(pbi->MBCodedFlags) _ogg_free(pbi->MBCodedFlags); - - if(pbi->_Nodes) _ogg_free(pbi->_Nodes); - pbi->_Nodes = 0; - - pbi->QFragData = 0; - pbi->TokenList = 0; - pbi->skipped_display_fragments = 0; - pbi->FragCoeffs = 0; - pbi->FragCoefEOB = 0; - pbi->display_fragments = 0; - pbi->pixel_index_table = 0; - pbi->recon_pixel_index_table = 0; - pbi->FragTokenCounts = 0; - pbi->CodedBlockList = 0; - pbi->FragCodingMethod = 0; - pbi->FragMVect = 0; - pbi->MBCodedFlags = 0; - pbi->MBFullyFlags = 0; - pbi->BlockMap = 0; - - pbi->SBCodedFlags = 0; - pbi->SBFullyFlags = 0; - pbi->QFragData = 0; - pbi->TokenList = 0; - pbi->skipped_display_fragments = 0; - pbi->FragCoeffs = 0; - pbi->FragCoefEOB = 0; - pbi->display_fragments = 0; - pbi->pixel_index_table = 0; - pbi->recon_pixel_index_table = 0; - pbi->FragTokenCounts = 0; - pbi->CodedBlockList = 0; - pbi->FragCodingMethod = 0; - pbi->FragCoordinates = 0; - pbi->FragMVect = 0; - - pbi->PPCoefBuffer=0; - pbi->PPCoefBuffer=0; - pbi->FragQIndex = 0; - pbi->FragQIndex = 0; - pbi->FragmentVariances= 0; - pbi->FragmentVariances = 0 ; -} - -void InitFragmentInfo(PB_INSTANCE * pbi){ - - /* clear any existing info */ - ClearFragmentInfo(pbi); - - /* Perform Fragment Allocations */ - pbi->display_fragments = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->display_fragments)); - - pbi->pixel_index_table = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->pixel_index_table)); - - pbi->recon_pixel_index_table = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->recon_pixel_index_table)); - - pbi->FragTokenCounts = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragTokenCounts)); - - pbi->CodedBlockList = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->CodedBlockList)); - - pbi->FragMVect = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragMVect)); - - pbi->FragCoeffs = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragCoeffs)); - - pbi->FragCoefEOB = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragCoefEOB)); - - pbi->skipped_display_fragments = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->skipped_display_fragments)); - - pbi->QFragData = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->QFragData)); - - pbi->TokenList = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->TokenList)); - - pbi->FragCodingMethod = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragCodingMethod)); - - pbi->FragCoordinates = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragCoordinates)); - - pbi->FragQIndex = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragQIndex)); - - pbi->PPCoefBuffer = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->PPCoefBuffer)); - - pbi->FragmentVariances = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->FragmentVariances)); - - pbi->_Nodes = - _ogg_malloc(pbi->UnitFragments * sizeof(*pbi->_Nodes)); - - /* Super Block Initialization */ - pbi->SBCodedFlags = - _ogg_malloc(pbi->SuperBlocks * sizeof(*pbi->SBCodedFlags)); - - pbi->SBFullyFlags = - _ogg_malloc(pbi->SuperBlocks * sizeof(*pbi->SBFullyFlags)); - - /* Macro Block Initialization */ - pbi->MBCodedFlags = - _ogg_malloc(pbi->MacroBlocks * sizeof(*pbi->MBCodedFlags)); - - pbi->MBFullyFlags = - _ogg_malloc(pbi->MacroBlocks * sizeof(*pbi->MBFullyFlags)); - - pbi->BlockMap = - _ogg_malloc(pbi->SuperBlocks * sizeof(*pbi->BlockMap)); - -} - -void ClearFrameInfo(PB_INSTANCE * pbi){ - if(pbi->ThisFrameRecon ) - _ogg_free(pbi->ThisFrameRecon ); - if(pbi->GoldenFrame) - _ogg_free(pbi->GoldenFrame); - if(pbi->LastFrameRecon) - _ogg_free(pbi->LastFrameRecon); - if(pbi->PostProcessBuffer) - _ogg_free(pbi->PostProcessBuffer); - - - pbi->ThisFrameRecon = 0; - pbi->GoldenFrame = 0; - pbi->LastFrameRecon = 0; - pbi->PostProcessBuffer = 0; - - - pbi->ThisFrameRecon = 0; - pbi->GoldenFrame = 0; - pbi->LastFrameRecon = 0; - pbi->PostProcessBuffer = 0; - -} - -void InitFrameInfo(PB_INSTANCE * pbi, unsigned int FrameSize){ - - /* clear any existing info */ - ClearFrameInfo(pbi); - - /* allocate frames */ - pbi->ThisFrameRecon = - _ogg_malloc(FrameSize*sizeof(*pbi->ThisFrameRecon)); - - pbi->GoldenFrame = - _ogg_malloc(FrameSize*sizeof(*pbi->GoldenFrame)); - - pbi->LastFrameRecon = - _ogg_malloc(FrameSize*sizeof(*pbi->LastFrameRecon)); - - pbi->PostProcessBuffer = - _ogg_malloc(FrameSize*sizeof(*pbi->PostProcessBuffer)); - -} - -void InitFrameDetails(PB_INSTANCE *pbi){ - int FrameSize; - - /*pbi->PostProcessingLevel = 0; - pbi->PostProcessingLevel = 4; - pbi->PostProcessingLevel = 5; - pbi->PostProcessingLevel = 6;*/ - - pbi->PostProcessingLevel = 0; - - - /* Set the frame size etc. */ - - pbi->YPlaneSize = pbi->info.width * - pbi->info.height; - pbi->UVPlaneSize = pbi->YPlaneSize / 4; - pbi->HFragments = pbi->info.width / HFRAGPIXELS; - pbi->VFragments = pbi->info.height / VFRAGPIXELS; - pbi->UnitFragments = ((pbi->VFragments * pbi->HFragments)*3)/2; - pbi->YPlaneFragments = pbi->HFragments * pbi->VFragments; - pbi->UVPlaneFragments = pbi->YPlaneFragments / 4; - - pbi->YStride = (pbi->info.width + STRIDE_EXTRA); - pbi->UVStride = pbi->YStride / 2; - pbi->ReconYPlaneSize = pbi->YStride * - (pbi->info.height + STRIDE_EXTRA); - pbi->ReconUVPlaneSize = pbi->ReconYPlaneSize / 4; - FrameSize = pbi->ReconYPlaneSize + 2 * pbi->ReconUVPlaneSize; - - pbi->YDataOffset = 0; - pbi->UDataOffset = pbi->YPlaneSize; - pbi->VDataOffset = pbi->YPlaneSize + pbi->UVPlaneSize; - pbi->ReconYDataOffset = - (pbi->YStride * UMV_BORDER) + UMV_BORDER; - pbi->ReconUDataOffset = pbi->ReconYPlaneSize + - (pbi->UVStride * (UMV_BORDER/2)) + (UMV_BORDER/2); - pbi->ReconVDataOffset = pbi->ReconYPlaneSize + pbi->ReconUVPlaneSize + - (pbi->UVStride * (UMV_BORDER/2)) + (UMV_BORDER/2); - - /* Image dimensions in Super-Blocks */ - pbi->YSBRows = (pbi->info.height/32) + - ( pbi->info.height%32 ? 1 : 0 ); - pbi->YSBCols = (pbi->info.width/32) + - ( pbi->info.width%32 ? 1 : 0 ); - pbi->UVSBRows = ((pbi->info.height/2)/32) + - ( (pbi->info.height/2)%32 ? 1 : 0 ); - pbi->UVSBCols = ((pbi->info.width/2)/32) + - ( (pbi->info.width/2)%32 ? 1 : 0 ); - - /* Super-Blocks per component */ - pbi->YSuperBlocks = pbi->YSBRows * pbi->YSBCols; - pbi->UVSuperBlocks = pbi->UVSBRows * pbi->UVSBCols; - pbi->SuperBlocks = pbi->YSuperBlocks+2*pbi->UVSuperBlocks; - - /* Useful externals */ - pbi->MacroBlocks = ((pbi->VFragments+1)/2)*((pbi->HFragments+1)/2); - - InitFragmentInfo(pbi); - InitFrameInfo(pbi, FrameSize); - InitializeFragCoordinates(pbi); - - /* Configure mapping between quad-tree and fragments */ - CreateBlockMapping ( pbi->BlockMap, pbi->YSuperBlocks, - pbi->UVSuperBlocks, pbi->HFragments, pbi->VFragments); - - /* Re-initialise the pixel index table. */ - - CalcPixelIndexTable( pbi ); - -} - diff --git a/Engine/lib/libtheora/lib/enc/hufftables.h b/Engine/lib/libtheora/lib/enc/hufftables.h deleted file mode 100644 index eb4be22c2..000000000 --- a/Engine/lib/libtheora/lib/enc/hufftables.h +++ /dev/null @@ -1,1034 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: hufftables.h 13884 2007-09-22 08:38:10Z giles $ - - ********************************************************************/ - -#include "../dec/huffman.h" -#include "codec_internal.h" - -const unsigned char ExtraBitLengths_VP31[MAX_ENTROPY_TOKENS] = { - 0, 0, 0, 2, 3, 4, 12,3, 6, /* EOB and Zero-run tokens. */ - 0, 0, 0, 0, /* Very low value tokens. */ - 1, 1, 1, 1, 2, 3, 4, 5, 6, 10, /* Other value tokens */ - 1, 1, 1, 1, 1, 3, 4, /* Category 1 runs. */ - 2, 3, /* Category 2 runs. */ -}; - -#define NEW_FREQS 0 /* dbm - test new frequency tables */ - -#if NEW_FREQS -/* New baseline frequency tables for encoder version >= 2 */ -const ogg_uint32_t FrequencyCounts_VP3[NUM_HUFF_TABLES][MAX_ENTROPY_TOKENS] = { - /* DC Intra bias */ - { 272, 84, 31, 36, 10, 2, 1, 92, 1, - 701, 872, 410, 478, - 630, 502, 417, 356, 582, 824, 985, 965, 697, 606, - 125, 119, 40, 3, 9, 15, 10, - 73, 37, - }, - { 311, 107, 41, 51, 18, 4, 2, 120, 1, - 824, 1037, 468, 541, - 714, 555, 451, 374, 595, 819, 929, 817, 474, 220, - 172, 142, 27, 4, 9, 10, 2, - 98, 48, - }, - { 353, 125, 49, 66, 24, 6, 2, 124, 1, - 926, 1172, 512, 594, - 766, 581, 458, 379, 590, 789, 849, 665, 306, 80, - 204, 147, 25, 5, 12, 9, 2, - 108, 54, - }, - { 392, 141, 57, 75, 31, 7, 4, 138, 1, - 1050, 1321, 559, 649, - 806, 594, 460, 372, 568, 727, 710, 475, 155, 19, - 251, 174, 27, 7, 16, 8, 2, - 126, 62, - }, - { 455, 168, 66, 87, 39, 10, 6, 124, 2, - 1143, 1455, 592, 692, - 824, 596, 453, 361, 542, 657, 592, 329, 78, 5, - 269, 184, 27, 9, 19, 7, 2, - 127, 66, - }, - { 544, 201, 80, 102, 45, 11, 6, 99, 1, - 1236, 1587, 610, 720, - 833, 590, 444, 348, 506, 588, 487, 226, 39, 2, - 253, 178, 27, 10, 20, 7, 2, - 118, 65, - }, - { 649, 241, 98, 121, 54, 14, 8, 84, 1, - 1349, 1719, 634, 763, - 847, 583, 428, 323, 456, 492, 349, 120, 13, 1, - 231, 170, 24, 8, 19, 7, 1, - 109, 67, - }, - { 824, 304, 129, 158, 66, 19, 10, 44, 2, - 1476, 1925, 644, 794, - 838, 559, 396, 289, 392, 384, 223, 53, 3, 1, - 159, 121, 17, 6, 16, 6, 2, - 69, 53, - }, - - /* DC Inter Bias */ - { 534, 174, 71, 68, 10, 1, 1, 68, 119, - 1674, 1526, 560, 536, - 539, 331, 229, 168, 233, 262, 231, 149, 71, 51, - 629, 530, 284, 126, 182, 208, 184, - 148, 87, - }, - { 594, 195, 77, 71, 9, 1, 1, 47, 89, - 1723, 1592, 595, 570, - 574, 351, 241, 176, 243, 271, 234, 144, 65, 37, - 534, 449, 240, 117, 167, 277, 153, - 96, 54, - }, - { 642, 213, 88, 83, 12, 1, 1, 40, 80, - 1751, 1630, 621, 600, - 598, 367, 250, 183, 251, 276, 235, 143, 62, 28, - 485, 397, 212, 110, 161, 193, 141, - 84, 48, - }, - { 693, 258, 114, 131, 27, 3, 1, 44, 79, - 1794, 1644, 550, 533, - 518, 314, 213, 154, 209, 223, 174, 97, 40, 14, - 584, 463, 236, 138, 196, 249, 143, - 94, 54, - }, - { 758, 303, 144, 189, 53, 8, 1, 37, 69, - 1842, 1732, 513, 504, - 478, 287, 191, 137, 182, 186, 137, 72, 31, 6, - 589, 469, 199, 128, 177, 264, 161, - 89, 49, - }, - { 817, 344, 170, 243, 84, 18, 2, 30, 65, - 1836, 1733, 518, 511, - 477, 281, 185, 130, 169, 166, 117, 59, 25, 3, - 572, 450, 185, 121, 173, 232, 146, - 80, 43, - }, - { 865, 389, 204, 322, 139, 42, 9, 26, 51, - 1848, 1766, 531, 522, - 477, 275, 177, 122, 153, 144, 97, 50, 16, 1, - 485, 378, 167, 115, 164, 203, 128, - 74, 42, - }, - { 961, 447, 243, 407, 196, 74, 26, 12, 34, - 2003, 1942, 571, 565, - 494, 278, 173, 116, 141, 129, 85, 44, 8, 1, - 285, 223, 101, 66, 104, 120, 74, - 35, 22, - }, - - /* AC INTRA Tables */ - /* AC Intra bias group 1 tables */ - { 245, 68, 25, 28, 5, 1, 1, 359, 4, - 910, 904, 570, 571, - 766, 620, 478, 375, 554, 684, 652, 441, 182, 30, - 535, 206, 118, 77, 69, 90, 16, - 299, 100, - }, - { 302, 86, 32, 36, 8, 1, 1, 362, 3, - 974, 968, 599, 599, - 774, 635, 469, 365, 528, 628, 557, 337, 118, 14, - 577, 219, 136, 82, 69, 65, 13, - 317, 112, - }, - { 348, 102, 39, 44, 9, 2, 1, 363, 3, - 1062, 1055, 607, 609, - 787, 626, 457, 348, 494, 550, 452, 233, 60, 2, - 636, 244, 159, 92, 74, 68, 12, - 327, 119, - }, - { 400, 121, 47, 51, 11, 2, 1, 366, 3, - 1109, 1102, 620, 622, - 786, 624, 450, 331, 459, 490, 366, 163, 29, 1, - 673, 257, 175, 98, 77, 63, 14, - 344, 131, - }, - { 470, 151, 59, 67, 15, 3, 1, 354, 4, - 1198, 1189, 640, 643, - 769, 603, 410, 294, 386, 381, 240, 78, 5, 1, - 746, 282, 205, 113, 87, 64, 15, - 368, 145, - }, - { 553, 189, 77, 94, 24, 6, 1, 347, 4, - 1244, 1232, 650, 653, - 739, 551, 360, 249, 303, 261, 129, 24, 1, 1, - 828, 313, 245, 135, 108, 77, 17, - 403, 169, - }, - { 701, 253, 109, 140, 42, 12, 2, 350, 6, - 1210, 1197, 652, 647, - 673, 495, 299, 189, 211, 151, 50, 2, 1, 1, - 892, 336, 284, 162, 134, 101, 25, - 455, 205, - }, - { 924, 390, 180, 248, 85, 31, 13, 286, 14, - 1242, 1206, 601, 577, - 519, 342, 175, 100, 85, 36, 1, 1, 1, 1, - 1031, 348, 346, 204, 166, 131, 34, - 473, 197, - }, - /* AC Inter bias group 1 tables */ - { 459, 128, 50, 48, 8, 1, 1, 224, 69, - 1285, 1227, 587, 565, - 573, 406, 261, 180, 228, 213, 130, 47, 11, 3, - 1069, 540, 309, 231, 147, 279, 157, - 383, 165, - }, - { 524, 155, 62, 64, 14, 2, 1, 209, 63, - 1345, 1288, 523, 507, - 515, 358, 225, 153, 183, 160, 87, 29, 7, 2, - 1151, 591, 365, 282, 179, 308, 133, - 344, 157, - }, - { 588, 181, 75, 81, 19, 3, 1, 204, 68, - 1344, 1288, 517, 503, - 505, 346, 216, 141, 169, 139, 71, 21, 5, 1, - 1146, 584, 366, 286, 170, 298, 153, - 342, 157, - }, - { 634, 196, 82, 89, 22, 4, 1, 194, 60, - 1356, 1312, 515, 502, - 489, 331, 199, 127, 145, 111, 51, 14, 3, 1, - 1156, 589, 393, 300, 182, 285, 144, - 340, 159, - }, - { 715, 231, 98, 113, 31, 7, 1, 181, 57, - 1345, 1303, 498, 490, - 448, 291, 166, 101, 106, 75, 30, 9, 1, 1, - 1175, 584, 416, 321, 209, 333, 164, - 330, 159, - }, - { 825, 283, 125, 149, 44, 11, 2, 160, 59, - 1343, 1308, 476, 469, - 405, 247, 131, 75, 76, 47, 18, 5, 1, 1, - 1192, 579, 432, 332, 217, 327, 176, - 320, 154, - }, - { 961, 361, 170, 215, 70, 20, 5, 161, 55, - 1250, 1218, 463, 460, - 354, 204, 101, 52, 48, 28, 11, 1, 1, 1, - 1172, 570, 449, 350, 222, 332, 169, - 338, 174, - }, - { 1139, 506, 266, 387, 156, 57, 26, 114, 48, - 1192, 1170, 366, 366, - 226, 113, 47, 22, 22, 12, 1, 1, 1, 1, - 1222, 551, 462, 391, 220, 322, 156, - 290, 136, - }, - - /* AC Intra bias group 2 tables */ - { 245, 49, 15, 11, 1, 1, 1, 332, 38, - 1163, 1162, 685, 683, - 813, 623, 437, 318, 421, 424, 288, 109, 14, 1, - 729, 303, 179, 112, 87, 199, 46, - 364, 135, - }, - { 305, 67, 22, 17, 2, 1, 1, 329, 39, - 1250, 1245, 706, 705, - 801, 584, 385, 267, 330, 296, 165, 40, 3, 1, - 798, 340, 206, 131, 108, 258, 52, - 382, 154, - }, - { 356, 82, 28, 23, 3, 1, 1, 312, 42, - 1340, 1334, 701, 703, - 770, 545, 346, 227, 269, 223, 100, 17, 1, 1, - 846, 359, 222, 142, 120, 284, 55, - 379, 157, - }, - { 402, 95, 33, 30, 4, 1, 1, 300, 43, - 1379, 1371, 710, 714, - 724, 486, 289, 182, 202, 144, 47, 5, 1, 1, - 908, 394, 250, 161, 141, 350, 60, - 391, 171, - }, - { 499, 122, 44, 42, 7, 1, 1, 267, 45, - 1439, 1436, 690, 694, - 628, 385, 213, 122, 117, 62, 14, 1, 1, 1, - 992, 441, 288, 187, 167, 446, 82, - 378, 176, - }, - { 641, 168, 62, 60, 12, 1, 1, 247, 49, - 1435, 1436, 662, 669, - 527, 298, 142, 71, 55, 22, 3, 1, 1, 1, - 1036, 470, 319, 208, 193, 548, 106, - 362, 184, - }, - { 860, 274, 111, 113, 23, 4, 1, 229, 59, - 1331, 1323, 629, 645, - 419, 192, 72, 30, 19, 6, 1, 1, 1, 1, - 1022, 478, 339, 225, 213, 690, 142, - 342, 198, - }, - { 1059, 437, 218, 285, 84, 17, 2, 152, 44, - 1284, 1313, 530, 561, - 212, 66, 17, 6, 3, 1, 1, 1, 1, 1, - 1034, 485, 346, 226, 207, 819, 185, - 248, 145, - }, - /* AC Inter bias group 2 tables */ - { 407, 93, 31, 24, 2, 1, 1, 232, 108, - 1365, 1349, 581, 578, - 498, 305, 170, 100, 103, 67, 24, 5, 1, 1, - 1175, 604, 393, 268, 209, 506, 217, - 379, 193, - }, - { 521, 129, 46, 39, 4, 1, 1, 199, 116, - 1419, 1403, 543, 540, - 446, 263, 138, 78, 75, 44, 13, 2, 1, 1, - 1201, 605, 392, 267, 214, 533, 252, - 334, 167, - }, - { 575, 144, 52, 46, 6, 1, 1, 193, 124, - 1394, 1384, 528, 528, - 406, 227, 112, 59, 54, 28, 7, 1, 1, 1, - 1210, 621, 412, 284, 235, 604, 265, - 320, 167, - }, - { 673, 174, 64, 59, 9, 1, 1, 177, 128, - 1392, 1385, 499, 499, - 352, 183, 85, 42, 35, 16, 3, 1, 1, 1, - 1210, 626, 418, 289, 246, 675, 297, - 292, 158, - }, - { 804, 225, 85, 77, 12, 1, 1, 150, 129, - 1387, 1384, 455, 455, - 277, 129, 53, 23, 17, 7, 1, 1, 1, 1, - 1212, 635, 433, 306, 268, 760, 313, - 249, 137, - }, - { 975, 305, 123, 117, 20, 2, 1, 135, 140, - 1312, 1310, 401, 399, - 201, 80, 28, 11, 8, 2, 1, 1, 1, 1, - 1162, 623, 439, 314, 283, 906, 368, - 203, 121, - }, - { 1205, 452, 208, 231, 50, 6, 1, 123, 149, - 1161, 1164, 370, 370, - 137, 45, 14, 4, 2, 1, 1, 1, 1, 1, - 1047, 562, 413, 300, 277, 1020, 404, - 168, 105, - }, - { 1297, 662, 389, 574, 200, 39, 4, 55, 120, - 1069, 1076, 273, 265, - 66, 14, 2, 1, 1, 1, 1, 1, 1, 1, - 930, 475, 345, 249, 236, 1124, 376, - 91, 56, - }, - - /* AC Intra bias group 3 tables */ - { 278, 55, 17, 12, 1, 1, 1, 288, 71, - 1315, 1304, 725, 724, - 733, 506, 307, 195, 225, 175, 77, 12, 1, 1, - 904, 414, 246, 170, 126, 290, 205, - 423, 185, - }, - { 382, 80, 26, 21, 2, 1, 1, 239, 64, - 1442, 1429, 706, 701, - 664, 420, 239, 146, 152, 105, 34, 2, 1, 1, - 975, 440, 263, 185, 140, 332, 229, - 397, 169, - }, - { 451, 97, 32, 27, 4, 1, 1, 223, 75, - 1462, 1454, 682, 680, - 574, 343, 179, 101, 98, 54, 9, 1, 1, 1, - 1031, 482, 293, 210, 163, 400, 297, - 384, 181, - }, - { 551, 128, 43, 37, 5, 1, 1, 201, 78, - 1497, 1487, 642, 651, - 493, 269, 133, 70, 60, 24, 2, 1, 1, 1, - 1065, 504, 312, 228, 178, 451, 352, - 351, 174, - }, - { 693, 179, 63, 54, 8, 1, 1, 169, 78, - 1502, 1497, 580, 591, - 375, 186, 77, 35, 21, 4, 1, 1, 1, 1, - 1099, 533, 341, 253, 206, 542, 432, - 306, 164, - }, - { 867, 263, 105, 96, 16, 2, 1, 152, 81, - 1435, 1439, 521, 525, - 270, 107, 32, 8, 3, 1, 1, 1, 1, 1, - 1085, 537, 361, 277, 223, 616, 549, - 258, 156, - }, - { 1022, 385, 182, 207, 46, 7, 1, 158, 88, - 1290, 1318, 501, 502, - 184, 38, 6, 1, 1, 1, 1, 1, 1, 1, - 1023, 480, 345, 301, 232, 665, 661, - 210, 133, - }, - { 1184, 555, 307, 457, 185, 44, 6, 115, 41, - 1236, 1253, 329, 340, - 32, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1017, 385, 316, 370, 246, 672, 788, - 85, 23, - }, - /* AC Inter bias group 3 tables */ - { 502, 106, 33, 22, 1, 1, 1, 151, 132, - 1446, 1451, 502, 499, - 343, 181, 84, 42, 36, 16, 3, 1, 1, 1, - 1211, 661, 429, 312, 242, 637, 498, - 288, 156, - }, - { 651, 147, 48, 35, 3, 1, 1, 145, 140, - 1419, 1420, 469, 466, - 281, 132, 56, 25, 18, 6, 1, 1, 1, 1, - 1175, 656, 435, 328, 260, 715, 556, - 252, 147, - }, - { 749, 179, 59, 43, 4, 1, 1, 123, 135, - 1423, 1431, 413, 409, - 221, 95, 36, 15, 9, 2, 1, 1, 1, 1, - 1159, 658, 444, 340, 272, 782, 656, - 205, 124, - }, - { 902, 243, 86, 67, 7, 1, 1, 114, 141, - 1385, 1385, 387, 383, - 178, 67, 22, 7, 4, 1, 1, 1, 1, 1, - 1096, 632, 434, 339, 277, 813, 735, - 171, 109, - }, - { 1081, 337, 133, 112, 15, 1, 1, 92, 137, - 1350, 1349, 311, 309, - 115, 34, 8, 2, 1, 1, 1, 1, 1, 1, - 1016, 595, 418, 342, 283, 870, 883, - 114, 78, - }, - { 1253, 467, 210, 205, 34, 3, 1, 80, 130, - 1318, 1313, 258, 260, - 68, 12, 2, 1, 1, 1, 1, 1, 1, 1, - 874, 516, 378, 330, 273, 877, 1000, - 72, 53, - }, - { 1362, 626, 333, 423, 100, 10, 1, 73, 106, - 1311, 1313, 241, 231, - 31, 3, 1, 1, 1, 1, 1, 1, 1, 1, - 620, 368, 286, 302, 245, 814, 1127, - 34, 28, - }, - { 1203, 743, 460, 774, 284, 36, 1, 13, 25, - 1956, 1961, 103, 106, - 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 248, 131, 149, 272, 165, 535, 813, - 3, 3, - }, - - /* AC Intra bias group 4 tables */ - { 599, 150, 55, 50, 9, 1, 1, 181, 19, - 1487, 1487, 625, 625, - 473, 271, 138, 74, 71, 42, 11, 1, 1, 1, - 1187, 591, 356, 239, 170, 351, 137, - 395, 194, - }, - { 758, 209, 79, 74, 15, 2, 1, 147, 25, - 1514, 1514, 521, 520, - 334, 165, 74, 36, 30, 11, 1, 1, 1, 1, - 1252, 644, 409, 279, 211, 472, 203, - 318, 171, - }, - { 852, 252, 100, 98, 20, 3, 1, 130, 26, - 1493, 1498, 481, 473, - 268, 123, 51, 23, 15, 3, 1, 1, 1, 1, - 1256, 652, 426, 294, 231, 543, 242, - 278, 156, - }, - { 971, 309, 130, 136, 30, 5, 1, 113, 28, - 1458, 1467, 443, 435, - 215, 90, 31, 12, 5, 1, 1, 1, 1, 1, - 1232, 643, 426, 303, 243, 590, 300, - 235, 136, - }, - { 1100, 399, 180, 206, 53, 9, 1, 101, 29, - 1419, 1425, 375, 374, - 158, 47, 10, 1, 1, 1, 1, 1, 1, 1, - 1193, 609, 426, 319, 256, 643, 383, - 166, 103, - }, - { 1195, 505, 249, 326, 98, 20, 3, 102, 25, - 1370, 1356, 355, 347, - 104, 11, 1, 1, 1, 1, 1, 1, 1, 1, - 1100, 568, 381, 330, 261, 642, 466, - 105, 69, - }, - { 1176, 608, 345, 559, 244, 57, 6, 110, 9, - 1370, 1332, 372, 367, - 29, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 859, 427, 269, 359, 375, 608, 451, - 35, 20, - }, - { 1140, 613, 391, 797, 458, 180, 37, 2, 1, - 2037, 1697, 95, 31, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 360, 49, 23, 198, 1001, 719, 160, - 1, 1, - }, - /* AC Inter bias group 4 tables */ - { 931, 272, 105, 96, 16, 1, 1, 91, 52, - 1481, 1489, 347, 349, - 174, 74, 28, 12, 8, 3, 1, 1, 1, 1, - 1247, 719, 490, 356, 279, 706, 363, - 187, 110, - }, - { 1095, 358, 148, 143, 25, 3, 1, 74, 61, - 1439, 1457, 304, 302, - 127, 46, 15, 5, 3, 1, 1, 1, 1, 1, - 1138, 664, 469, 347, 282, 768, 487, - 139, 87, - }, - { 1192, 423, 188, 189, 36, 4, 1, 64, 61, - 1457, 1475, 284, 282, - 106, 35, 10, 3, 1, 1, 1, 1, 1, 1, - 1078, 624, 440, 329, 264, 744, 507, - 117, 73, - }, - { 1275, 496, 231, 258, 52, 6, 1, 53, 55, - 1458, 1470, 248, 245, - 77, 20, 5, 1, 1, 1, 1, 1, 1, 1, - 984, 576, 414, 323, 260, 771, 569, - 84, 54, - }, - { 1377, 603, 302, 367, 87, 11, 1, 37, 52, - 1522, 1532, 207, 204, - 47, 8, 1, 1, 1, 1, 1, 1, 1, 1, - 840, 493, 366, 291, 231, 690, 636, - 52, 32, - }, - { 1409, 708, 385, 529, 148, 24, 1, 23, 37, - 1672, 1670, 163, 162, - 22, 2, 1, 1, 1, 1, 1, 1, 1, 1, - 647, 364, 291, 262, 210, 574, 643, - 26, 14, - }, - { 1348, 778, 481, 755, 245, 53, 4, 13, 19, - 2114, 2089, 141, 139, - 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 302, 183, 162, 181, 182, 344, 437, - 8, 3, - }, - { 1560, 769, 410, 664, 243, 58, 1, 1, 1, - 3017, 2788, 17, 24, - 3, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 34, 16, 8, 55, 134, 105, 86, - 1, 1, - }, -}; - -#else /* Frequency tables for encoder version < 2 */ - -const ogg_uint32_t FrequencyCounts_VP3[NUM_HUFF_TABLES][MAX_ENTROPY_TOKENS] = { - /* DC Intra bias */ - { 198, 62, 22, 31, 14, 6, 6, 205, 3, - 843, 843, 415, 516, - 660, 509, 412, 347, 560, 779, 941, 930, 661, 377, - 170, 155, 39, 2, 9, 15, 11, - 128, 86, - }, - { 299, 92, 34, 39, 15, 6, 6, 132, 1, - 851, 851, 484, 485, - 666, 514, 416, 351, 567, 788, 953, 943, 670, 383, - 117, 119, 26, 4, 17, 7, 1, - 93, 56, - }, - { 367, 115, 42, 47, 16, 6, 6, 105, 1, - 896, 896, 492, 493, - 667, 510, 408, 342, 547, 760, 932, 927, 656, 379, - 114, 103, 10, 3, 6, 2, 1, - 88, 49, - }, - { 462, 158, 63, 76, 28, 9, 8, 145, 1, - 1140, 1140, 573, 574, - 754, 562, 435, 357, 555, 742, 793, 588, 274, 81, - 154, 117, 13, 6, 12, 2, 1, - 104, 62, - }, - { 558, 196, 81, 99, 36, 11, 9, 135, 1, - 1300, 1301, 606, 607, - 779, 560, 429, 349, 536, 680, 644, 405, 153, 30, - 171, 120, 12, 5, 14, 3, 1, - 104, 53, - }, - { 635, 233, 100, 122, 46, 14, 12, 113, 1, - 1414, 1415, 631, 631, - 785, 555, 432, 335, 513, 611, 521, 284, 89, 13, - 170, 113, 10, 5, 14, 3, 1, - 102, 62, - }, - { 720, 276, 119, 154, 62, 20, 16, 101, 1, - 1583, 1583, 661, 661, - 794, 556, 407, 318, 447, 472, 343, 153, 35, 1, - 172, 115, 11, 7, 14, 3, 1, - 112, 70, - }, - { 853, 326, 144, 184, 80, 27, 19, 52, 1, - 1739, 1740, 684, 685, - 800, 540, 381, 277, 364, 352, 218, 78, 13, 1, - 139, 109, 9, 6, 20, 2, 1, - 94, 50, - }, - - /* DC Inter Bias */ - { 490, 154, 57, 53, 10, 2, 1, 238, 160, - 1391, 1390, 579, 578, - 491, 273, 172, 118, 152, 156, 127, 79, 41, 39, - 712, 547, 316, 125, 183, 306, 237, - 451, 358, - }, - { 566, 184, 70, 65, 11, 2, 1, 235, 51, - 1414, 1414, 599, 598, - 510, 285, 180, 124, 157, 161, 131, 82, 42, 40, - 738, 551, 322, 138, 195, 188, 93, - 473, 365, - }, - { 711, 261, 111, 126, 27, 4, 1, 137, 52, - 1506, 1505, 645, 645, - 567, 316, 199, 136, 172, 175, 142, 88, 45, 48, - 548, 449, 255, 145, 184, 174, 121, - 260, 227, - }, - { 823, 319, 144, 175, 43, 7, 1, 53, 42, - 1648, 1648, 653, 652, - 583, 329, 205, 139, 175, 176, 139, 84, 44, 34, - 467, 389, 211, 137, 181, 186, 107, - 106, 85, - }, - { 948, 411, 201, 276, 85, 16, 2, 39, 33, - 1778, 1777, 584, 583, - 489, 265, 162, 111, 140, 140, 108, 64, 38, 23, - 428, 356, 201, 139, 186, 165, 94, - 78, 63, - }, - { 1002, 470, 248, 386, 153, 39, 6, 23, 23, - 1866, 1866, 573, 573, - 467, 249, 155, 103, 130, 128, 94, 60, 38, 14, - 323, 263, 159, 111, 156, 153, 74, - 46, 34, - }, - { 1020, 518, 291, 504, 242, 78, 18, 14, 14, - 1980, 1979, 527, 526, - 408, 219, 132, 87, 110, 104, 79, 55, 31, 7, - 265, 213, 129, 91, 131, 111, 50, - 31, 20, - }, - { 1018, 544, 320, 591, 338, 139, 47, 5, 2, - 2123, 2123, 548, 547, - 414, 212, 126, 83, 101, 96, 79, 60, 23, 1, - 120, 97, 55, 39, 60, 38, 15, - 11, 8, - }, - - /* AC INTRA Tables */ - /* AC Intra bias group 1 tables */ - { 242, 62, 22, 20, 4, 1, 1, 438, 1, - 593, 593, 489, 490, - 657, 580, 471, 374, 599, 783, 869, 770, 491, 279, - 358, 144, 82, 54, 49, 70, 5, - 289, 107, - }, - { 317, 95, 38, 41, 8, 1, 1, 479, 1, - 653, 654, 500, 501, - 682, 611, 473, 376, 582, 762, 806, 656, 358, 155, - 419, 162, 86, 58, 36, 34, 1, - 315, 126, - }, - { 382, 121, 49, 59, 15, 3, 1, 496, 1, - 674, 674, 553, 554, - 755, 636, 487, 391, 576, 718, 701, 488, 221, 72, - 448, 161, 107, 56, 37, 29, 1, - 362, 156, - }, - { 415, 138, 57, 73, 21, 5, 1, 528, 1, - 742, 741, 562, 563, - 753, 669, 492, 388, 563, 664, 589, 340, 129, 26, - 496, 184, 139, 71, 48, 33, 2, - 387, 166, - }, - { 496, 170, 73, 94, 31, 8, 2, 513, 1, - 855, 855, 604, 604, - 769, 662, 477, 356, 486, 526, 381, 183, 51, 5, - 590, 214, 160, 85, 60, 39, 3, - 427, 203, - }, - { 589, 207, 89, 116, 40, 13, 3, 491, 1, - 919, 919, 631, 631, - 769, 633, 432, 308, 408, 378, 247, 94, 17, 1, - 659, 247, 201, 105, 73, 51, 3, - 466, 242, - }, - { 727, 266, 115, 151, 49, 17, 6, 439, 1, - 977, 977, 642, 642, - 718, 572, 379, 243, 285, 251, 133, 40, 1, 1, - 756, 287, 253, 126, 94, 66, 4, - 492, 280, - }, - { 940, 392, 180, 247, 82, 30, 14, 343, 1, - 1064, 1064, 615, 616, - 596, 414, 235, 146, 149, 108, 41, 1, 1, 1, - 882, 314, 346, 172, 125, 83, 6, - 489, 291, - }, - /* AC Inter bias group 1 tables */ - { 440, 102, 33, 23, 2, 1, 1, 465, 85, - 852, 852, 744, 743, - 701, 496, 297, 193, 225, 200, 129, 58, 18, 2, - 798, 450, 269, 202, 145, 308, 154, - 646, 389, - }, - { 592, 151, 53, 43, 6, 1, 1, 409, 34, - 875, 875, 748, 747, - 723, 510, 305, 196, 229, 201, 130, 59, 18, 2, - 800, 436, 253, 185, 115, 194, 88, - 642, 368, - }, - { 759, 222, 86, 85, 17, 2, 1, 376, 46, - 888, 888, 689, 688, - 578, 408, 228, 143, 165, 141, 84, 35, 7, 1, - 878, 488, 321, 244, 147, 266, 124, - 612, 367, - }, - { 912, 298, 122, 133, 34, 7, 1, 261, 44, - 1092, 1091, 496, 496, - 409, 269, 150, 95, 106, 87, 49, 16, 1, 1, - 1102, 602, 428, 335, 193, 323, 157, - 423, 253, - }, - { 1072, 400, 180, 210, 60, 16, 3, 210, 40, - 1063, 1063, 451, 451, - 345, 221, 121, 73, 79, 64, 31, 6, 1, 1, - 1105, 608, 462, 358, 202, 330, 155, - 377, 228, - }, - { 1164, 503, 254, 330, 109, 34, 9, 167, 35, - 1038, 1037, 390, 390, - 278, 170, 89, 54, 56, 40, 13, 1, 1, 1, - 1110, 607, 492, 401, 218, 343, 141, - 323, 192, - }, - { 1173, 583, 321, 486, 196, 68, 23, 124, 23, - 1037, 1037, 347, 346, - 232, 139, 69, 40, 37, 20, 2, 1, 1, 1, - 1128, 584, 506, 410, 199, 301, 113, - 283, 159, - }, - { 1023, 591, 366, 699, 441, 228, 113, 79, 5, - 1056, 1056, 291, 291, - 173, 96, 38, 19, 8, 1, 1, 1, 1, 1, - 1187, 527, 498, 409, 147, 210, 56, - 263, 117, - }, - - /* AC Intra bias group 2 tables */ - { 311, 74, 27, 27, 5, 1, 1, 470, 24, - 665, 667, 637, 638, - 806, 687, 524, 402, 585, 679, 609, 364, 127, 20, - 448, 210, 131, 76, 52, 111, 19, - 393, 195, - }, - { 416, 104, 39, 38, 8, 1, 1, 545, 33, - 730, 731, 692, 692, - 866, 705, 501, 365, 495, 512, 387, 168, 39, 2, - 517, 240, 154, 86, 64, 127, 19, - 461, 247, - }, - { 474, 117, 43, 42, 9, 1, 1, 560, 40, - 783, 783, 759, 760, - 883, 698, 466, 318, 404, 377, 215, 66, 7, 1, - 559, 259, 176, 110, 87, 170, 22, - 520, 278, - }, - { 582, 149, 53, 53, 12, 2, 1, 473, 39, - 992, 993, 712, 713, - 792, 593, 373, 257, 299, 237, 114, 25, 1, 1, - 710, 329, 221, 143, 116, 226, 26, - 490, 259, - }, - { 744, 210, 78, 77, 16, 2, 1, 417, 37, - 1034, 1035, 728, 728, - 718, 509, 296, 175, 184, 122, 42, 3, 1, 1, - 791, 363, 255, 168, 145, 311, 35, - 492, 272, - }, - { 913, 291, 121, 128, 28, 4, 1, 334, 40, - 1083, 1084, 711, 712, - 624, 378, 191, 107, 95, 50, 7, 1, 1, 1, - 876, 414, 288, 180, 164, 382, 39, - 469, 275, - }, - { 1065, 405, 184, 216, 53, 8, 1, 236, 36, - 1134, 1134, 685, 686, - 465, 253, 113, 48, 41, 9, 1, 1, 1, 1, - 965, 451, 309, 179, 166, 429, 53, - 414, 249, - }, - { 1148, 548, 301, 438, 160, 42, 6, 84, 17, - 1222, 1223, 574, 575, - 272, 111, 23, 6, 2, 1, 1, 1, 1, 1, - 1060, 502, 328, 159, 144, 501, 54, - 302, 183, - }, - /* AC Inter bias group 2 tables */ - { 403, 80, 24, 17, 1, 1, 1, 480, 90, - 899, 899, 820, 819, - 667, 413, 228, 133, 139, 98, 42, 10, 1, 1, - 865, 470, 316, 222, 171, 419, 213, - 645, 400, - }, - { 698, 169, 59, 49, 6, 1, 1, 414, 101, - 894, 893, 761, 761, - 561, 338, 171, 96, 97, 64, 26, 6, 1, 1, - 896, 494, 343, 239, 192, 493, 215, - 583, 366, - }, - { 914, 255, 94, 80, 10, 1, 1, 345, 128, - 935, 935, 670, 671, - 415, 222, 105, 55, 51, 30, 10, 1, 1, 1, - 954, 530, 377, 274, 232, 641, 295, - 456, 298, - }, - { 1103, 359, 146, 135, 20, 1, 1, 235, 119, - 1042, 1042, 508, 507, - 293, 146, 65, 33, 30, 16, 4, 1, 1, 1, - 1031, 561, 407, 296, 265, 813, 317, - 301, 192, - }, - { 1255, 504, 238, 265, 51, 5, 1, 185, 113, - 1013, 1013, 437, 438, - 212, 92, 41, 18, 15, 6, 1, 1, 1, 1, - 976, 530, 386, 276, 260, 927, 357, - 224, 148, - }, - { 1292, 610, 332, 460, 127, 16, 1, 136, 99, - 1014, 1015, 384, 384, - 153, 65, 25, 11, 6, 1, 1, 1, 1, 1, - 942, 487, 343, 241, 238, 970, 358, - 174, 103, - }, - { 1219, 655, 407, 700, 280, 55, 2, 100, 60, - 1029, 1029, 337, 336, - 119, 43, 11, 3, 2, 1, 1, 1, 1, 1, - 894, 448, 305, 199, 213, 1005, 320, - 136, 77, - }, - { 1099, 675, 435, 971, 581, 168, 12, 37, 16, - 1181, 1081, 319, 318, - 66, 11, 6, 1, 1, 1, 1, 1, 1, 1, - 914, 370, 235, 138, 145, 949, 128, - 94, 41, - }, - - /* AC Intra bias group 3 tables */ - { 486, 112, 39, 34, 6, 1, 1, 541, 67, - 819, 818, 762, 763, - 813, 643, 403, 280, 332, 295, 164, 53, 6, 1, - 632, 294, 180, 131, 105, 208, 109, - 594, 295, - }, - { 723, 191, 69, 65, 12, 1, 1, 445, 79, - 865, 865, 816, 816, - 750, 515, 290, 172, 184, 122, 46, 5, 1, 1, - 740, 340, 213, 165, 129, 270, 168, - 603, 326, - }, - { 884, 264, 102, 103, 21, 3, 1, 382, 68, - 897, 897, 836, 836, - 684, 427, 227, 119, 119, 70, 16, 1, 1, 1, - 771, 367, 234, 184, 143, 272, 178, - 555, 326, - }, - { 1028, 347, 153, 161, 36, 8, 1, 251, 44, - 1083, 1084, 735, 735, - 541, 289, 144, 77, 57, 23, 3, 1, 1, 1, - 926, 422, 270, 215, 176, 301, 183, - 443, 248, - }, - { 1155, 465, 224, 264, 71, 14, 3, 174, 27, - 1110, 1111, 730, 731, - 429, 206, 79, 30, 19, 4, 1, 1, 1, 1, - 929, 443, 279, 225, 194, 298, 196, - 354, 223, - }, - { 1191, 576, 296, 415, 144, 36, 8, 114, 16, - 1162, 1162, 749, 749, - 338, 108, 29, 8, 5, 1, 1, 1, 1, 1, - 947, 458, 273, 207, 194, 248, 145, - 258, 152, - }, - { 1169, 619, 366, 603, 247, 92, 23, 46, 1, - 1236, 1236, 774, 775, - 191, 35, 14, 1, 1, 1, 1, 1, 1, 1, - 913, 449, 260, 214, 194, 180, 82, - 174, 98, - }, - { 1006, 537, 381, 897, 504, 266, 101, 39, 1, - 1307, 1307, 668, 667, - 116, 3, 1, 1, 1, 1, 1, 1, 1, 1, - 1175, 261, 295, 70, 164, 107, 31, - 10, 76, - }, - /* AC Inter bias group 3 tables */ - { 652, 156, 53, 43, 5, 1, 1, 368, 128, - 983, 984, 825, 825, - 583, 331, 163, 88, 84, 48, 15, 1, 1, 1, - 870, 480, 316, 228, 179, 421, 244, - 562, 349, - }, - { 988, 280, 104, 87, 12, 1, 1, 282, 194, - 980, 981, 738, 739, - 395, 189, 80, 37, 31, 12, 2, 1, 1, 1, - 862, 489, 333, 262, 214, 600, 446, - 390, 260, - }, - { 1176, 399, 165, 154, 24, 2, 1, 218, 224, - 1017, 1018, 651, 651, - 280, 111, 42, 16, 9, 3, 1, 1, 1, 1, - 787, 469, 324, 269, 229, 686, 603, - 267, 194, - }, - { 1319, 530, 255, 268, 47, 4, 1, 113, 183, - 1149, 1150, 461, 461, - 173, 58, 17, 5, 3, 1, 1, 1, 1, 1, - 768, 450, 305, 261, 221, 716, 835, - 136, 97, - }, - { 1362, 669, 355, 465, 104, 9, 1, 76, 153, - 1253, 1253, 398, 397, - 102, 21, 5, 1, 1, 1, 1, 1, 1, 1, - 596, 371, 238, 228, 196, 660, 954, - 68, 53, - }, - { 1354, 741, 446, 702, 174, 15, 1, 38, 87, - 1498, 1498, 294, 294, - 43, 7, 1, 1, 1, 1, 1, 1, 1, 1, - 381, 283, 165, 181, 155, 544, 1039, - 25, 21, - }, - { 1262, 885, 546, 947, 263, 18, 1, 18, 27, - 1908, 1908, 163, 162, - 14, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 195, 152, 83, 125, 109, 361, 827, - 7, 5, - }, - { 2539, 951, 369, 554, 212, 18, 1, 1, 1, - 2290, 2289, 64, 64, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 18, 18, 9, 55, 36, 184, 323, - 1, 1, - }, - - - /* AC Intra bias group 4 tables */ - { 921, 264, 101, 100, 19, 2, 1, 331, 98, - 1015, 1016, 799, 799, - 512, 269, 119, 60, 50, 17, 1, 1, 1, 1, - 841, 442, 307, 222, 182, 493, 256, - 438, 310, - }, - { 1147, 412, 184, 206, 50, 6, 1, 242, 141, - 977, 976, 808, 807, - 377, 135, 40, 10, 7, 1, 1, 1, 1, 1, - 788, 402, 308, 223, 205, 584, 406, - 316, 227, - }, - { 1243, 504, 238, 310, 79, 11, 1, 184, 150, - 983, 984, 814, 813, - 285, 56, 10, 1, 1, 1, 1, 1, 1, 1, - 713, 377, 287, 217, 180, 615, 558, - 208, 164, - }, - { 1266, 606, 329, 484, 161, 27, 1, 79, 92, - 1187, 1188, 589, 588, - 103, 10, 1, 1, 1, 1, 1, 1, 1, 1, - 680, 371, 278, 221, 244, 614, 728, - 80, 62, - }, - { 1126, 828, 435, 705, 443, 90, 8, 10, 55, - 1220, 1219, 350, 350, - 28, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 602, 330, 222, 168, 158, 612, 919, - 104, 5, - }, - { 1210, 506, 1014, 926, 474, 240, 4, 1, 44, - 1801, 1801, 171, 171, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 900, 132, 36, 11, 47, 191, 316, - 2, 1, - }, - { 1210, 506, 1014, 926, 474, 240, 4, 1, 44, - 1801, 1801, 171, 171, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 900, 132, 36, 11, 47, 191, 316, - 2, 1, - }, - { 1210, 506, 1014, 926, 474, 240, 4, 1, 44, - 1801, 1801, 171, 171, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 900, 132, 36, 11, 47, 191, 316, - 2, 1, - }, - /* AC Inter bias group 4 tables */ - { 1064, 325, 129, 117, 20, 2, 1, 266, 121, - 1000, 1000, 706, 706, - 348, 162, 67, 32, 25, 11, 1, 1, 1, 1, - 876, 513, 363, 274, 225, 627, 384, - 370, 251, - }, - { 1311, 517, 238, 254, 45, 3, 1, 188, 160, - 1070, 1070, 635, 635, - 239, 85, 30, 11, 6, 1, 1, 1, 1, 1, - 744, 420, 313, 239, 206, 649, 541, - 221, 155, - }, - { 1394, 632, 322, 385, 78, 7, 1, 134, 152, - 1163, 1164, 607, 607, - 185, 51, 12, 3, 1, 1, 1, 1, 1, 1, - 631, 331, 275, 203, 182, 604, 620, - 146, 98, - }, - { 1410, 727, 407, 546, 146, 19, 1, 67, 88, - 1485, 1486, 419, 418, - 103, 18, 3, 1, 1, 1, 1, 1, 1, 1, - 555, 261, 234, 164, 148, 522, 654, - 67, 39, - }, - { 1423, 822, 492, 719, 216, 22, 1, 28, 59, - 1793, 1793, 323, 324, - 37, 2, 1, 1, 1, 1, 1, 1, 1, 1, - 376, 138, 158, 102, 119, 400, 604, - 28, 9, - }, - { 1585, 923, 563, 918, 207, 25, 1, 5, 20, - 2229, 2230, 172, 172, - 7, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 191, 40, 56, 22, 65, 243, 312, - 2, 1, - }, - { 2225, 1100, 408, 608, 133, 8, 1, 1, 1, - 2658, 2658, 25, 24, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 8, 1, 1, 1, 1, 125, 16, - 1, 1, - }, - { 2539, 951, 369, 554, 212, 18, 1, 1, 1, - 2290, 2289, 64, 64, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 18, 18, 9, 55, 36, 184, 323, - 1, 1, - }, -}; - -#endif /* NEW_FREQS */ diff --git a/Engine/lib/libtheora/lib/enc/mcomp.c b/Engine/lib/libtheora/lib/enc/mcomp.c deleted file mode 100644 index 3b6b4ac28..000000000 --- a/Engine/lib/libtheora/lib/enc/mcomp.c +++ /dev/null @@ -1,767 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: mcomp.c 15153 2008-08-04 18:37:55Z tterribe $ - - ********************************************************************/ - -#include -#include -#include "codec_internal.h" - -/* Initialises motion compentsation. */ -void InitMotionCompensation ( CP_INSTANCE *cpi ){ - int i; - int SearchSite=0; - int Len; - int LineStepY = (ogg_int32_t)cpi->pb.YStride; - - Len=((MAX_MV_EXTENT/2)+1)/2; - - - /* How many search stages are there. */ - cpi->MVSearchSteps = 0; - - /* Set up offsets arrays used in half pixel correction. */ - cpi->HalfPixelRef2Offset[0] = -LineStepY - 1; - cpi->HalfPixelRef2Offset[1] = -LineStepY; - cpi->HalfPixelRef2Offset[2] = -LineStepY + 1; - cpi->HalfPixelRef2Offset[3] = - 1; - cpi->HalfPixelRef2Offset[4] = 0; - cpi->HalfPixelRef2Offset[5] = 1; - cpi->HalfPixelRef2Offset[6] = LineStepY - 1; - cpi->HalfPixelRef2Offset[7] = LineStepY; - cpi->HalfPixelRef2Offset[8] = LineStepY + 1; - - cpi->HalfPixelXOffset[0] = -1; - cpi->HalfPixelXOffset[1] = 0; - cpi->HalfPixelXOffset[2] = 1; - cpi->HalfPixelXOffset[3] = -1; - cpi->HalfPixelXOffset[4] = 0; - cpi->HalfPixelXOffset[5] = 1; - cpi->HalfPixelXOffset[6] = -1; - cpi->HalfPixelXOffset[7] = 0; - cpi->HalfPixelXOffset[8] = 1; - - cpi->HalfPixelYOffset[0] = -1; - cpi->HalfPixelYOffset[1] = -1; - cpi->HalfPixelYOffset[2] = -1; - cpi->HalfPixelYOffset[3] = 0; - cpi->HalfPixelYOffset[4] = 0; - cpi->HalfPixelYOffset[5] = 0; - cpi->HalfPixelYOffset[6] = 1; - cpi->HalfPixelYOffset[7] = 1; - cpi->HalfPixelYOffset[8] = 1; - - - /* Generate offsets for 8 search sites per step. */ - while ( Len>0 ) { - /* Another step. */ - cpi->MVSearchSteps += 1; - - /* Compute offsets for search sites. */ - cpi->MVOffsetX[SearchSite] = -Len; - cpi->MVOffsetY[SearchSite++] = -Len; - cpi->MVOffsetX[SearchSite] = 0; - cpi->MVOffsetY[SearchSite++] = -Len; - cpi->MVOffsetX[SearchSite] = Len; - cpi->MVOffsetY[SearchSite++] = -Len; - cpi->MVOffsetX[SearchSite] = -Len; - cpi->MVOffsetY[SearchSite++] = 0; - cpi->MVOffsetX[SearchSite] = Len; - cpi->MVOffsetY[SearchSite++] = 0; - cpi->MVOffsetX[SearchSite] = -Len; - cpi->MVOffsetY[SearchSite++] = Len; - cpi->MVOffsetX[SearchSite] = 0; - cpi->MVOffsetY[SearchSite++] = Len; - cpi->MVOffsetX[SearchSite] = Len; - cpi->MVOffsetY[SearchSite++] = Len; - - /* Contract. */ - Len /= 2; - } - - /* Compute pixel index offsets. */ - for ( i=SearchSite-1; i>=0; i-- ) - cpi->MVPixelOffsetY[i] = (cpi->MVOffsetY[i]*LineStepY) + cpi->MVOffsetX[i]; -} - -static ogg_uint32_t GetInterErr (CP_INSTANCE *cpi, unsigned char * NewDataPtr, - unsigned char * RefDataPtr1, - unsigned char * RefDataPtr2, - ogg_uint32_t PixelsPerLine ) { - ogg_int32_t DiffVal; - ogg_int32_t RefOffset = (int)(RefDataPtr1 - RefDataPtr2); - ogg_uint32_t RefPixelsPerLine = PixelsPerLine + STRIDE_EXTRA; - - /* Mode of interpolation chosen based upon on the offset of the - second reference pointer */ - if ( RefOffset == 0 ) { - DiffVal = dsp_inter8x8_err (cpi->dsp, NewDataPtr, PixelsPerLine, - RefDataPtr1, RefPixelsPerLine); - }else{ - DiffVal = dsp_inter8x8_err_xy2 (cpi->dsp, NewDataPtr, PixelsPerLine, - RefDataPtr1, - RefDataPtr2, RefPixelsPerLine); - } - - /* Compute and return population variance as mis-match metric. */ - return DiffVal; -} - -static ogg_uint32_t GetHalfPixelSumAbsDiffs (CP_INSTANCE *cpi, - unsigned char * SrcData, - unsigned char * RefDataPtr1, - unsigned char * RefDataPtr2, - ogg_uint32_t PixelsPerLine, - ogg_uint32_t ErrorSoFar, - ogg_uint32_t BestSoFar ) { - - ogg_uint32_t DiffVal = ErrorSoFar; - ogg_int32_t RefOffset = (int)(RefDataPtr1 - RefDataPtr2); - ogg_uint32_t RefPixelsPerLine = PixelsPerLine + STRIDE_EXTRA; - - if ( RefOffset == 0 ) { - /* Simple case as for non 0.5 pixel */ - DiffVal += dsp_sad8x8 (cpi->dsp, SrcData, PixelsPerLine, - RefDataPtr1, RefPixelsPerLine); - } else { - DiffVal += dsp_sad8x8_xy2_thres (cpi->dsp, SrcData, PixelsPerLine, - RefDataPtr1, - RefDataPtr2, RefPixelsPerLine, BestSoFar); - } - - return DiffVal; -} - -ogg_uint32_t GetMBIntraError (CP_INSTANCE *cpi, ogg_uint32_t FragIndex, - ogg_uint32_t PixelsPerLine ) { - ogg_uint32_t LocalFragIndex = FragIndex; - ogg_uint32_t IntraError = 0; - - dsp_save_fpu (cpi->dsp); - - /* Add together the intra errors for those blocks in the macro block - that are coded (Y only) */ - if ( cpi->pb.display_fragments[LocalFragIndex] ) - IntraError += - dsp_intra8x8_err (cpi->dsp, &cpi-> - ConvDestBuffer[cpi->pb.pixel_index_table[LocalFragIndex]], - PixelsPerLine); - - LocalFragIndex++; - if ( cpi->pb.display_fragments[LocalFragIndex] ) - IntraError += - dsp_intra8x8_err (cpi->dsp, &cpi-> - ConvDestBuffer[cpi->pb.pixel_index_table[LocalFragIndex]], - PixelsPerLine); - - LocalFragIndex = FragIndex + cpi->pb.HFragments; - if ( cpi->pb.display_fragments[LocalFragIndex] ) - IntraError += - dsp_intra8x8_err (cpi->dsp, &cpi-> - ConvDestBuffer[cpi->pb.pixel_index_table[LocalFragIndex]], - PixelsPerLine); - - LocalFragIndex++; - if ( cpi->pb.display_fragments[LocalFragIndex] ) - IntraError += - dsp_intra8x8_err (cpi->dsp, &cpi-> - ConvDestBuffer[cpi->pb.pixel_index_table[LocalFragIndex]], - PixelsPerLine); - - dsp_restore_fpu (cpi->dsp); - - return IntraError; -} - -ogg_uint32_t GetMBInterError (CP_INSTANCE *cpi, - unsigned char * SrcPtr, - unsigned char * RefPtr, - ogg_uint32_t FragIndex, - ogg_int32_t LastXMV, - ogg_int32_t LastYMV, - ogg_uint32_t PixelsPerLine ) { - ogg_uint32_t RefPixelsPerLine = cpi->pb.YStride; - ogg_uint32_t LocalFragIndex = FragIndex; - ogg_int32_t PixelIndex; - ogg_int32_t RefPixelIndex; - ogg_int32_t RefPixelOffset; - ogg_int32_t RefPtr2Offset; - - ogg_uint32_t InterError = 0; - - unsigned char * SrcPtr1; - unsigned char * RefPtr1; - - dsp_save_fpu (cpi->dsp); - - /* Work out pixel offset into source buffer. */ - PixelIndex = cpi->pb.pixel_index_table[LocalFragIndex]; - - /* Work out the pixel offset in reference buffer for the default - motion vector */ - RefPixelIndex = cpi->pb.recon_pixel_index_table[LocalFragIndex]; - RefPixelOffset = ((LastYMV/2) * RefPixelsPerLine) + (LastXMV/2); - - /* Work out the second reference pointer offset. */ - RefPtr2Offset = 0; - if ( LastXMV % 2 ) { - if ( LastXMV > 0 ) - RefPtr2Offset += 1; - else - RefPtr2Offset -= 1; - } - if ( LastYMV % 2 ) { - if ( LastYMV > 0 ) - RefPtr2Offset += RefPixelsPerLine; - else - RefPtr2Offset -= RefPixelsPerLine; - } - - /* Add together the errors for those blocks in the macro block that - are coded (Y only) */ - if ( cpi->pb.display_fragments[LocalFragIndex] ) { - SrcPtr1 = &SrcPtr[PixelIndex]; - RefPtr1 = &RefPtr[RefPixelIndex + RefPixelOffset]; - InterError += GetInterErr(cpi, SrcPtr1, RefPtr1, - &RefPtr1[RefPtr2Offset], PixelsPerLine ); - } - - LocalFragIndex++; - if ( cpi->pb.display_fragments[LocalFragIndex] ) { - PixelIndex = cpi->pb.pixel_index_table[LocalFragIndex]; - RefPixelIndex = cpi->pb.recon_pixel_index_table[LocalFragIndex]; - SrcPtr1 = &SrcPtr[PixelIndex]; - RefPtr1 = &RefPtr[RefPixelIndex + RefPixelOffset]; - InterError += GetInterErr(cpi, SrcPtr1, RefPtr1, - &RefPtr1[RefPtr2Offset], PixelsPerLine ); - - } - - LocalFragIndex = FragIndex + cpi->pb.HFragments; - if ( cpi->pb.display_fragments[LocalFragIndex] ) { - PixelIndex = cpi->pb.pixel_index_table[LocalFragIndex]; - RefPixelIndex = cpi->pb.recon_pixel_index_table[LocalFragIndex]; - SrcPtr1 = &SrcPtr[PixelIndex]; - RefPtr1 = &RefPtr[RefPixelIndex + RefPixelOffset]; - InterError += GetInterErr(cpi, SrcPtr1, RefPtr1, - &RefPtr1[RefPtr2Offset], PixelsPerLine ); - } - - LocalFragIndex++; - if ( cpi->pb.display_fragments[LocalFragIndex] ) { - PixelIndex = cpi->pb.pixel_index_table[LocalFragIndex]; - RefPixelIndex = cpi->pb.recon_pixel_index_table[LocalFragIndex]; - SrcPtr1 = &SrcPtr[PixelIndex]; - RefPtr1 = &RefPtr[RefPixelIndex + RefPixelOffset]; - InterError += GetInterErr(cpi, SrcPtr1, RefPtr1, - &RefPtr1[RefPtr2Offset], PixelsPerLine ); - } - - dsp_restore_fpu (cpi->dsp); - - return InterError; -} - -ogg_uint32_t GetMBMVInterError (CP_INSTANCE *cpi, - unsigned char * RefFramePtr, - ogg_uint32_t FragIndex, - ogg_uint32_t PixelsPerLine, - ogg_int32_t *MVPixelOffset, - MOTION_VECTOR *MV ) { - ogg_uint32_t Error = 0; - ogg_uint32_t MinError; - ogg_uint32_t InterMVError = 0; - - ogg_int32_t i; - ogg_int32_t x=0, y=0; - ogg_int32_t step; - ogg_int32_t SearchSite=0; - - unsigned char *SrcPtr[4] = {NULL,NULL,NULL,NULL}; - unsigned char *RefPtr=NULL; - unsigned char *CandidateBlockPtr=NULL; - unsigned char *BestBlockPtr=NULL; - - ogg_uint32_t RefRow2Offset = cpi->pb.YStride * 8; - - int MBlockDispFrags[4]; - - /* Half pixel variables */ - ogg_int32_t HalfPixelError; - ogg_int32_t BestHalfPixelError; - unsigned char BestHalfOffset; - unsigned char * RefDataPtr1; - unsigned char * RefDataPtr2; - - dsp_save_fpu (cpi->dsp); - - /* Note which of the four blocks in the macro block are to be - included in the search. */ - MBlockDispFrags[0] = - cpi->pb.display_fragments[FragIndex]; - MBlockDispFrags[1] = - cpi->pb.display_fragments[FragIndex + 1]; - MBlockDispFrags[2] = - cpi->pb.display_fragments[FragIndex + cpi->pb.HFragments]; - MBlockDispFrags[3] = - cpi->pb.display_fragments[FragIndex + cpi->pb.HFragments + 1]; - - /* Set up the source pointers for the four source blocks. */ - SrcPtr[0] = &cpi->ConvDestBuffer[cpi->pb.pixel_index_table[FragIndex]]; - SrcPtr[1] = SrcPtr[0] + 8; - SrcPtr[2] = SrcPtr[0] + (PixelsPerLine * 8); - SrcPtr[3] = SrcPtr[2] + 8; - - /* Set starting reference point for search. */ - RefPtr = &RefFramePtr[cpi->pb.recon_pixel_index_table[FragIndex]]; - - /* Check the 0,0 candidate. */ - if ( MBlockDispFrags[0] ) { - Error += dsp_sad8x8 (cpi->dsp, SrcPtr[0], PixelsPerLine, RefPtr, - PixelsPerLine + STRIDE_EXTRA); - } - if ( MBlockDispFrags[1] ) { - Error += dsp_sad8x8 (cpi->dsp, SrcPtr[1], PixelsPerLine, RefPtr + 8, - PixelsPerLine + STRIDE_EXTRA); - } - if ( MBlockDispFrags[2] ) { - Error += dsp_sad8x8 (cpi->dsp, SrcPtr[2], PixelsPerLine, RefPtr + RefRow2Offset, - PixelsPerLine + STRIDE_EXTRA); - } - if ( MBlockDispFrags[3] ) { - Error += dsp_sad8x8 (cpi->dsp, SrcPtr[3], PixelsPerLine, RefPtr + RefRow2Offset + 8, - PixelsPerLine + STRIDE_EXTRA); - } - - /* Set starting values to results of 0, 0 vector. */ - MinError = Error; - BestBlockPtr = RefPtr; - x = 0; - y = 0; - MV->x = 0; - MV->y = 0; - - /* Proceed through N-steps. */ - for ( step=0; stepMVSearchSteps; step++ ) { - /* Search the 8-neighbours at distance pertinent to current step.*/ - for ( i=0; i<8; i++ ) { - /* Set pointer to next candidate matching block. */ - CandidateBlockPtr = RefPtr + MVPixelOffset[SearchSite]; - - /* Reset error */ - Error = 0; - - /* Get the score for the current offset */ - if ( MBlockDispFrags[0] ) { - Error += dsp_sad8x8 (cpi->dsp, SrcPtr[0], PixelsPerLine, CandidateBlockPtr, - PixelsPerLine + STRIDE_EXTRA); - } - - if ( MBlockDispFrags[1] && (Error < MinError) ) { - Error += dsp_sad8x8_thres (cpi->dsp, SrcPtr[1], PixelsPerLine, CandidateBlockPtr + 8, - PixelsPerLine + STRIDE_EXTRA, MinError); - } - - if ( MBlockDispFrags[2] && (Error < MinError) ) { - Error += dsp_sad8x8_thres (cpi->dsp, SrcPtr[2], PixelsPerLine, CandidateBlockPtr + RefRow2Offset, - PixelsPerLine + STRIDE_EXTRA, MinError); - } - - if ( MBlockDispFrags[3] && (Error < MinError) ) { - Error += dsp_sad8x8_thres (cpi->dsp, SrcPtr[3], PixelsPerLine, CandidateBlockPtr + RefRow2Offset + 8, - PixelsPerLine + STRIDE_EXTRA, MinError); - } - - if ( Error < MinError ) { - /* Remember best match. */ - MinError = Error; - BestBlockPtr = CandidateBlockPtr; - - /* Where is it. */ - x = MV->x + cpi->MVOffsetX[SearchSite]; - y = MV->y + cpi->MVOffsetY[SearchSite]; - } - - /* Move to next search location. */ - SearchSite += 1; - } - - /* Move to best location this step. */ - RefPtr = BestBlockPtr; - MV->x = x; - MV->y = y; - } - - /* Factor vectors to 1/2 pixel resoultion. */ - MV->x = (MV->x * 2); - MV->y = (MV->y * 2); - - /* Now do the half pixel pass */ - BestHalfOffset = 4; /* Default to the no offset case. */ - BestHalfPixelError = MinError; - - /* Get the half pixel error for each half pixel offset */ - for ( i=0; i < 9; i++ ) { - HalfPixelError = 0; - - if ( MBlockDispFrags[0] ) { - RefDataPtr1 = BestBlockPtr; - RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; - HalfPixelError = - GetHalfPixelSumAbsDiffs(cpi, SrcPtr[0], RefDataPtr1, RefDataPtr2, - PixelsPerLine, HalfPixelError, BestHalfPixelError ); - } - - if ( MBlockDispFrags[1] && (HalfPixelError < BestHalfPixelError) ) { - RefDataPtr1 = BestBlockPtr + 8; - RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; - HalfPixelError = - GetHalfPixelSumAbsDiffs(cpi, SrcPtr[1], RefDataPtr1, RefDataPtr2, - PixelsPerLine, HalfPixelError, BestHalfPixelError ); - } - - if ( MBlockDispFrags[2] && (HalfPixelError < BestHalfPixelError) ) { - RefDataPtr1 = BestBlockPtr + RefRow2Offset; - RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; - HalfPixelError = - GetHalfPixelSumAbsDiffs(cpi, SrcPtr[2], RefDataPtr1, RefDataPtr2, - PixelsPerLine, HalfPixelError, BestHalfPixelError ); - } - - if ( MBlockDispFrags[3] && (HalfPixelError < BestHalfPixelError) ) { - RefDataPtr1 = BestBlockPtr + RefRow2Offset + 8; - RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; - HalfPixelError = - GetHalfPixelSumAbsDiffs(cpi, SrcPtr[3], RefDataPtr1, RefDataPtr2, - PixelsPerLine, HalfPixelError, BestHalfPixelError ); - } - - if ( HalfPixelError < BestHalfPixelError ) { - BestHalfOffset = (unsigned char)i; - BestHalfPixelError = HalfPixelError; - } - } - - /* Half pixel adjust the MV */ - MV->x += cpi->HalfPixelXOffset[BestHalfOffset]; - MV->y += cpi->HalfPixelYOffset[BestHalfOffset]; - - /* Get the error score for the chosen 1/2 pixel offset as a variance. */ - InterMVError = GetMBInterError( cpi, cpi->ConvDestBuffer, RefFramePtr, - FragIndex, MV->x, MV->y, PixelsPerLine ); - - dsp_restore_fpu (cpi->dsp); - - /* Return score of best matching block. */ - return InterMVError; -} - -ogg_uint32_t GetMBMVExhaustiveSearch (CP_INSTANCE *cpi, - unsigned char * RefFramePtr, - ogg_uint32_t FragIndex, - ogg_uint32_t PixelsPerLine, - MOTION_VECTOR *MV ) { - ogg_uint32_t Error = 0; - ogg_uint32_t MinError = HUGE_ERROR; - ogg_uint32_t InterMVError = 0; - - ogg_int32_t i, j; - ogg_int32_t x=0, y=0; - - unsigned char *SrcPtr[4] = {NULL,NULL,NULL,NULL}; - unsigned char *RefPtr; - unsigned char *CandidateBlockPtr=NULL; - unsigned char *BestBlockPtr=NULL; - - ogg_uint32_t RefRow2Offset = cpi->pb.YStride * 8; - - int MBlockDispFrags[4]; - - /* Half pixel variables */ - ogg_int32_t HalfPixelError; - ogg_int32_t BestHalfPixelError; - unsigned char BestHalfOffset; - unsigned char * RefDataPtr1; - unsigned char * RefDataPtr2; - - dsp_save_fpu (cpi->dsp); - - /* Note which of the four blocks in the macro block are to be - included in the search. */ - MBlockDispFrags[0] = cpi-> - pb.display_fragments[FragIndex]; - MBlockDispFrags[1] = cpi-> - pb.display_fragments[FragIndex + 1]; - MBlockDispFrags[2] = cpi-> - pb.display_fragments[FragIndex + cpi->pb.HFragments]; - MBlockDispFrags[3] = cpi-> - pb.display_fragments[FragIndex + cpi->pb.HFragments + 1]; - - /* Set up the source pointers for the four source blocks. */ - SrcPtr[0] = &cpi-> - ConvDestBuffer[cpi->pb.pixel_index_table[FragIndex]]; - SrcPtr[1] = SrcPtr[0] + 8; - SrcPtr[2] = SrcPtr[0] + (PixelsPerLine * 8); - SrcPtr[3] = SrcPtr[2] + 8; - - RefPtr = &RefFramePtr[cpi->pb.recon_pixel_index_table[FragIndex]]; - RefPtr = RefPtr - ((MAX_MV_EXTENT/2) * cpi-> - pb.YStride) - (MAX_MV_EXTENT/2); - - /* Search each pixel alligned site */ - for ( i = 0; i < (ogg_int32_t)MAX_MV_EXTENT; i ++ ) { - /* Starting position in row */ - CandidateBlockPtr = RefPtr; - - for ( j = 0; j < (ogg_int32_t)MAX_MV_EXTENT; j++ ) { - /* Reset error */ - Error = 0; - - /* Summ errors for each block. */ - if ( MBlockDispFrags[0] ) { - Error += dsp_sad8x8 (cpi->dsp, SrcPtr[0], PixelsPerLine, CandidateBlockPtr, - PixelsPerLine + STRIDE_EXTRA); - } - if ( MBlockDispFrags[1] ){ - Error += dsp_sad8x8 (cpi->dsp, SrcPtr[1], PixelsPerLine, CandidateBlockPtr + 8, - PixelsPerLine + STRIDE_EXTRA); - } - if ( MBlockDispFrags[2] ){ - Error += dsp_sad8x8 (cpi->dsp, SrcPtr[2], PixelsPerLine, CandidateBlockPtr + RefRow2Offset, - PixelsPerLine + STRIDE_EXTRA); - } - if ( MBlockDispFrags[3] ){ - Error += dsp_sad8x8 (cpi->dsp, SrcPtr[3], PixelsPerLine, CandidateBlockPtr + RefRow2Offset + 8, - PixelsPerLine + STRIDE_EXTRA); - } - - /* Was this the best so far */ - if ( Error < MinError ) { - MinError = Error; - BestBlockPtr = CandidateBlockPtr; - x = 16 + j - MAX_MV_EXTENT; - y = 16 + i - MAX_MV_EXTENT; - } - - /* Move the the next site */ - CandidateBlockPtr ++; - } - - /* Move on to the next row. */ - RefPtr += cpi->pb.YStride; - - } - - /* Factor vectors to 1/2 pixel resoultion. */ - MV->x = (x * 2); - MV->y = (y * 2); - - /* Now do the half pixel pass */ - BestHalfOffset = 4; /* Default to the no offset case. */ - BestHalfPixelError = MinError; - - /* Get the half pixel error for each half pixel offset */ - for ( i=0; i < 9; i++ ) { - HalfPixelError = 0; - - if ( MBlockDispFrags[0] ) { - RefDataPtr1 = BestBlockPtr; - RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; - HalfPixelError = - GetHalfPixelSumAbsDiffs(cpi, SrcPtr[0], RefDataPtr1, RefDataPtr2, - PixelsPerLine, HalfPixelError, BestHalfPixelError ); - } - - if ( MBlockDispFrags[1] && (HalfPixelError < BestHalfPixelError) ) { - RefDataPtr1 = BestBlockPtr + 8; - RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; - HalfPixelError = - GetHalfPixelSumAbsDiffs(cpi, SrcPtr[1], RefDataPtr1, RefDataPtr2, - PixelsPerLine, HalfPixelError, BestHalfPixelError ); - } - - if ( MBlockDispFrags[2] && (HalfPixelError < BestHalfPixelError) ) { - RefDataPtr1 = BestBlockPtr + RefRow2Offset; - RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; - HalfPixelError = - GetHalfPixelSumAbsDiffs(cpi, SrcPtr[2], RefDataPtr1, RefDataPtr2, - PixelsPerLine, HalfPixelError, BestHalfPixelError ); - } - - if ( MBlockDispFrags[3] && (HalfPixelError < BestHalfPixelError) ) { - RefDataPtr1 = BestBlockPtr + RefRow2Offset + 8; - RefDataPtr2 = RefDataPtr1 + cpi->HalfPixelRef2Offset[i]; - HalfPixelError = - GetHalfPixelSumAbsDiffs(cpi, SrcPtr[3], RefDataPtr1, RefDataPtr2, - PixelsPerLine, HalfPixelError, BestHalfPixelError ); - } - - if ( HalfPixelError < BestHalfPixelError ){ - BestHalfOffset = (unsigned char)i; - BestHalfPixelError = HalfPixelError; - } - } - - /* Half pixel adjust the MV */ - MV->x += cpi->HalfPixelXOffset[BestHalfOffset]; - MV->y += cpi->HalfPixelYOffset[BestHalfOffset]; - - /* Get the error score for the chosen 1/2 pixel offset as a variance. */ - InterMVError = GetMBInterError( cpi, cpi->ConvDestBuffer, RefFramePtr, - FragIndex, MV->x, MV->y, PixelsPerLine ); - - dsp_restore_fpu (cpi->dsp); - - /* Return score of best matching block. */ - return InterMVError; -} - -static ogg_uint32_t GetBMVExhaustiveSearch (CP_INSTANCE *cpi, - unsigned char * RefFramePtr, - ogg_uint32_t FragIndex, - ogg_uint32_t PixelsPerLine, - MOTION_VECTOR *MV ) { - ogg_uint32_t Error = 0; - ogg_uint32_t MinError = HUGE_ERROR; - ogg_uint32_t InterMVError = 0; - - ogg_int32_t i, j; - ogg_int32_t x=0, y=0; - - unsigned char *SrcPtr = NULL; - unsigned char *RefPtr; - unsigned char *CandidateBlockPtr=NULL; - unsigned char *BestBlockPtr=NULL; - - /* Half pixel variables */ - ogg_int32_t HalfPixelError; - ogg_int32_t BestHalfPixelError; - unsigned char BestHalfOffset; - unsigned char * RefDataPtr2; - - /* Set up the source pointer for the block. */ - SrcPtr = &cpi-> - ConvDestBuffer[cpi->pb.pixel_index_table[FragIndex]]; - - RefPtr = &RefFramePtr[cpi->pb.recon_pixel_index_table[FragIndex]]; - RefPtr = RefPtr - ((MAX_MV_EXTENT/2) * - cpi->pb.YStride) - (MAX_MV_EXTENT/2); - - /* Search each pixel alligned site */ - for ( i = 0; i < (ogg_int32_t)MAX_MV_EXTENT; i ++ ) { - /* Starting position in row */ - CandidateBlockPtr = RefPtr; - - for ( j = 0; j < (ogg_int32_t)MAX_MV_EXTENT; j++ ){ - /* Get the block error score. */ - Error = dsp_sad8x8 (cpi->dsp, SrcPtr, PixelsPerLine, CandidateBlockPtr, - PixelsPerLine + STRIDE_EXTRA); - - /* Was this the best so far */ - if ( Error < MinError ) { - MinError = Error; - BestBlockPtr = CandidateBlockPtr; - x = 16 + j - MAX_MV_EXTENT; - y = 16 + i - MAX_MV_EXTENT; - } - - /* Move the the next site */ - CandidateBlockPtr ++; - } - - /* Move on to the next row. */ - RefPtr += cpi->pb.YStride; - } - - /* Factor vectors to 1/2 pixel resoultion. */ - MV->x = (x * 2); - MV->y = (y * 2); - - /* Now do the half pixel pass */ - BestHalfOffset = 4; /* Default to the no offset case. */ - BestHalfPixelError = MinError; - - /* Get the half pixel error for each half pixel offset */ - for ( i=0; i < 9; i++ ) { - RefDataPtr2 = BestBlockPtr + cpi->HalfPixelRef2Offset[i]; - HalfPixelError = - GetHalfPixelSumAbsDiffs(cpi, SrcPtr, BestBlockPtr, RefDataPtr2, - PixelsPerLine, 0, BestHalfPixelError ); - - if ( HalfPixelError < BestHalfPixelError ){ - BestHalfOffset = (unsigned char)i; - BestHalfPixelError = HalfPixelError; - } - } - - /* Half pixel adjust the MV */ - MV->x += cpi->HalfPixelXOffset[BestHalfOffset]; - MV->y += cpi->HalfPixelYOffset[BestHalfOffset]; - - /* Get the variance score at the chosen offset */ - RefDataPtr2 = BestBlockPtr + cpi->HalfPixelRef2Offset[BestHalfOffset]; - - InterMVError = - GetInterErr(cpi, SrcPtr, BestBlockPtr, RefDataPtr2, PixelsPerLine ); - - /* Return score of best matching block. */ - return InterMVError; -} - -ogg_uint32_t GetFOURMVExhaustiveSearch (CP_INSTANCE *cpi, - unsigned char * RefFramePtr, - ogg_uint32_t FragIndex, - ogg_uint32_t PixelsPerLine, - MOTION_VECTOR *MV ) { - ogg_uint32_t InterMVError; - - dsp_save_fpu (cpi->dsp); - - /* For the moment the 4MV mode is only deemed to be valid - if all four Y blocks are to be updated */ - /* This may be adapted later. */ - if ( cpi->pb.display_fragments[FragIndex] && - cpi->pb.display_fragments[FragIndex + 1] && - cpi->pb.display_fragments[FragIndex + cpi->pb.HFragments] && - cpi->pb.display_fragments[FragIndex + cpi->pb.HFragments + 1] ) { - - /* Reset the error score. */ - InterMVError = 0; - - /* Get the error component from each coded block */ - InterMVError += - GetBMVExhaustiveSearch(cpi, RefFramePtr, FragIndex, - PixelsPerLine, &(MV[0]) ); - InterMVError += - GetBMVExhaustiveSearch(cpi, RefFramePtr, (FragIndex + 1), - PixelsPerLine, &(MV[1]) ); - InterMVError += - GetBMVExhaustiveSearch(cpi, RefFramePtr, - (FragIndex + cpi->pb.HFragments), - PixelsPerLine, &(MV[2]) ); - InterMVError += - GetBMVExhaustiveSearch(cpi, RefFramePtr, - (FragIndex + cpi->pb.HFragments + 1), - PixelsPerLine, &(MV[3]) ); - }else{ - InterMVError = HUGE_ERROR; - } - - dsp_restore_fpu (cpi->dsp); - - /* Return score of best matching block. */ - return InterMVError; -} - diff --git a/Engine/lib/libtheora/lib/enc/misc_common.c b/Engine/lib/libtheora/lib/enc/misc_common.c deleted file mode 100644 index 1536a494a..000000000 --- a/Engine/lib/libtheora/lib/enc/misc_common.c +++ /dev/null @@ -1,339 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: misc_common.c 15323 2008-09-19 19:43:59Z giles $ - - ********************************************************************/ - -#include -#include "codec_internal.h" -#include "block_inline.h" - -#define FIXED_Q 150 -#define MAX_UP_REG_LOOPS 2 - -/* Gives the initial bytes per block estimate for each Q value */ -static const double BpbTable[Q_TABLE_SIZE] = { - 0.42, 0.45, 0.46, 0.49, 0.51, 0.53, 0.56, 0.58, - 0.61, 0.64, 0.68, 0.71, 0.74, 0.77, 0.80, 0.84, - 0.89, 0.92, 0.98, 1.01, 1.04, 1.13, 1.17, 1.23, - 1.28, 1.34, 1.41, 1.45, 1.51, 1.59, 1.69, 1.80, - 1.84, 1.94, 2.02, 2.15, 2.23, 2.34, 2.44, 2.50, - 2.69, 2.80, 2.87, 3.04, 3.16, 3.29, 3.59, 3.66, - 3.86, 3.94, 4.22, 4.50, 4.64, 4.70, 5.24, 5.34, - 5.61, 5.87, 6.11, 6.41, 6.71, 6.99, 7.36, 7.69 -}; - -static const double KfBpbTable[Q_TABLE_SIZE] = { - 0.74, 0.81, 0.88, 0.94, 1.00, 1.06, 1.14, 1.19, - 1.27, 1.34, 1.42, 1.49, 1.54, 1.59, 1.66, 1.73, - 1.80, 1.87, 1.97, 2.01, 2.08, 2.21, 2.25, 2.36, - 2.39, 2.50, 2.55, 2.65, 2.71, 2.82, 2.95, 3.01, - 3.11, 3.19, 3.31, 3.42, 3.58, 3.66, 3.78, 3.89, - 4.11, 4.26, 4.36, 4.39, 4.63, 4.76, 4.85, 5.04, - 5.26, 5.29, 5.47, 5.64, 5.76, 6.05, 6.35, 6.67, - 6.91, 7.17, 7.40, 7.56, 8.02, 8.45, 8.86, 9.38 -}; - -double GetEstimatedBpb( CP_INSTANCE *cpi, ogg_uint32_t TargetQ ){ - ogg_uint32_t i; - ogg_int32_t ThreshTableIndex = Q_TABLE_SIZE - 1; - double BytesPerBlock; - - /* Search for the Q table index that matches the given Q. */ - for ( i = 0; i < Q_TABLE_SIZE; i++ ) { - if ( TargetQ >= cpi->pb.QThreshTable[i] ) { - ThreshTableIndex = i; - break; - } - } - - /* Adjust according to Q shift and type of frame */ - if ( cpi->pb.FrameType == KEY_FRAME ) { - /* Get primary prediction */ - BytesPerBlock = KfBpbTable[ThreshTableIndex]; - } else { - /* Get primary prediction */ - BytesPerBlock = BpbTable[ThreshTableIndex]; - BytesPerBlock = BytesPerBlock * cpi->BpbCorrectionFactor; - } - - return BytesPerBlock; -} - -static void UpRegulateMB( CP_INSTANCE *cpi, ogg_uint32_t RegulationQ, - ogg_uint32_t SB, ogg_uint32_t MB, int NoCheck ) { - ogg_int32_t FragIndex; - ogg_uint32_t B; - - /* Variables used in calculating corresponding row,col and index in - UV planes */ - ogg_uint32_t UVRow; - ogg_uint32_t UVColumn; - ogg_uint32_t UVFragOffset; - - /* There may be MB's lying out of frame which must be ignored. For - these MB's Top left block will have a negative Fragment Index. */ - if ( QuadMapToMBTopLeft(cpi->pb.BlockMap, SB, MB ) >= 0 ) { - /* Up regulate the component blocks Y then UV. */ - for ( B=0; B<4; B++ ){ - FragIndex = QuadMapToIndex1( cpi->pb.BlockMap, SB, MB, B ); - - if ( ( !cpi->pb.display_fragments[FragIndex] ) && - ( (NoCheck) || (cpi->FragmentLastQ[FragIndex] > RegulationQ) ) ){ - cpi->pb.display_fragments[FragIndex] = 1; - cpi->extra_fragments[FragIndex] = 1; - cpi->FragmentLastQ[FragIndex] = RegulationQ; - cpi->MotionScore++; - } - } - - /* Check the two UV blocks */ - FragIndex = QuadMapToMBTopLeft(cpi->pb.BlockMap, SB, MB ); - - UVRow = (FragIndex / (cpi->pb.HFragments * 2)); - UVColumn = (FragIndex % cpi->pb.HFragments) / 2; - UVFragOffset = (UVRow * (cpi->pb.HFragments / 2)) + UVColumn; - - FragIndex = cpi->pb.YPlaneFragments + UVFragOffset; - if ( ( !cpi->pb.display_fragments[FragIndex] ) && - ( (NoCheck) || (cpi->FragmentLastQ[FragIndex] > RegulationQ) ) ) { - cpi->pb.display_fragments[FragIndex] = 1; - cpi->extra_fragments[FragIndex] = 1; - cpi->FragmentLastQ[FragIndex] = RegulationQ; - cpi->MotionScore++; - } - - FragIndex += cpi->pb.UVPlaneFragments; - if ( ( !cpi->pb.display_fragments[FragIndex] ) && - ( (NoCheck) || (cpi->FragmentLastQ[FragIndex] > RegulationQ) ) ) { - cpi->pb.display_fragments[FragIndex] = 1; - cpi->extra_fragments[FragIndex] = 1; - cpi->FragmentLastQ[FragIndex] = RegulationQ; - cpi->MotionScore++; - } - } -} - -static void UpRegulateBlocks (CP_INSTANCE *cpi, ogg_uint32_t RegulationQ, - ogg_int32_t RecoveryBlocks, - ogg_uint32_t * LastSB, ogg_uint32_t * LastMB ) { - - ogg_uint32_t LoopTimesRound = 0; - ogg_uint32_t MaxSB = cpi->pb.YSBRows * - cpi->pb.YSBCols; /* Tot super blocks in image */ - ogg_uint32_t SB, MB; /* Super-Block and macro block indices. */ - - /* First scan for blocks for which a residue update is outstanding. */ - while ( (cpi->MotionScore < RecoveryBlocks) && - (LoopTimesRound < MAX_UP_REG_LOOPS) ) { - LoopTimesRound++; - - for ( SB = (*LastSB); SB < MaxSB; SB++ ) { - /* Check its four Macro-Blocks */ - for ( MB=(*LastMB); MB<4; MB++ ) { - /* Mark relevant blocks for update */ - UpRegulateMB( cpi, RegulationQ, SB, MB, 0 ); - - /* Keep track of the last refresh MB. */ - (*LastMB) += 1; - if ( (*LastMB) == 4 ) - (*LastMB) = 0; - - /* Termination clause */ - if (cpi->MotionScore >= RecoveryBlocks) { - /* Make sure we don't stall at SB level */ - if ( *LastMB == 0 ) - SB++; - break; - } - } - - /* Termination clause */ - if (cpi->MotionScore >= RecoveryBlocks) - break; - } - - /* Update super block start index */ - if ( SB >= MaxSB){ - (*LastSB) = 0; - }else{ - (*LastSB) = SB; - } - } -} - -void UpRegulateDataStream (CP_INSTANCE *cpi, ogg_uint32_t RegulationQ, - ogg_int32_t RecoveryBlocks ) { - ogg_uint32_t LastPassMBPos = 0; - ogg_uint32_t StdLastMBPos = 0; - - ogg_uint32_t MaxSB = cpi->pb.YSBRows * - cpi->pb.YSBCols; /* Tot super blocks in image */ - - ogg_uint32_t SB=0; /* Super-Block index */ - ogg_uint32_t MB; /* Macro-Block index */ - - /* Decduct the number of blocks in an MB / 2 from the recover block count. - This will compensate for the fact that once we start checking an MB - we test every block in that macro block */ - if ( RecoveryBlocks > 3 ) - RecoveryBlocks -= 3; - - /* Up regulate blocks last coded at higher Q */ - UpRegulateBlocks( cpi, RegulationQ, RecoveryBlocks, - &cpi->LastEndSB, &StdLastMBPos ); - - /* If we have still not used up the minimum number of blocks and are - at the minimum Q then run through a final pass of the data to - insure that each block gets a final refresh. */ - if ( (RegulationQ == VERY_BEST_Q) && - (cpi->MotionScore < RecoveryBlocks) ) { - if ( cpi->FinalPassLastPos < MaxSB ) { - for ( SB = cpi->FinalPassLastPos; SB < MaxSB; SB++ ) { - /* Check its four Macro-Blocks */ - for ( MB=LastPassMBPos; MB<4; MB++ ) { - /* Mark relevant blocks for update */ - UpRegulateMB( cpi, RegulationQ, SB, MB, 1 ); - - /* Keep track of the last refresh MB. */ - LastPassMBPos += 1; - if ( LastPassMBPos == 4 ) { - LastPassMBPos = 0; - - /* Increment SB index */ - cpi->FinalPassLastPos += 1; - } - - /* Termination clause */ - if (cpi->MotionScore >= RecoveryBlocks) - break; - } - - /* Termination clause */ - if (cpi->MotionScore >= RecoveryBlocks) - break; - - } - } - } -} - -void RegulateQ( CP_INSTANCE *cpi, ogg_int32_t UpdateScore ) { - double PredUnitScoreBytes; - ogg_uint32_t QIndex = Q_TABLE_SIZE - 1; - ogg_uint32_t i; - - if ( UpdateScore > 0 ) { - double TargetUnitScoreBytes = (double)cpi->ThisFrameTargetBytes / - (double)UpdateScore; - double LastBitError = 10000.0; /* Silly high number */ - /* Search for the best Q for the target bitrate. */ - for ( i = 0; i < Q_TABLE_SIZE; i++ ) { - PredUnitScoreBytes = GetEstimatedBpb( cpi, cpi->pb.QThreshTable[i] ); - if ( PredUnitScoreBytes > TargetUnitScoreBytes ) { - if ( (PredUnitScoreBytes - TargetUnitScoreBytes) <= LastBitError ) { - QIndex = i; - } else { - QIndex = i - 1; - } - break; - } else { - LastBitError = TargetUnitScoreBytes - PredUnitScoreBytes; - } - } - } - - /* QIndex should now indicate the optimal Q. */ - cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[QIndex]; - - /* Apply range restrictions for key frames. */ - if ( cpi->pb.FrameType == KEY_FRAME ) { - if ( cpi->pb.ThisFrameQualityValue > cpi->pb.QThreshTable[20] ) - cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[20]; - else if ( cpi->pb.ThisFrameQualityValue < cpi->pb.QThreshTable[50] ) - cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[50]; - } - - /* Limit the Q value to the maximum available value */ - if (cpi->pb.ThisFrameQualityValue > - cpi->pb.QThreshTable[cpi->Configuration.ActiveMaxQ]) { - cpi->pb.ThisFrameQualityValue = - (ogg_uint32_t)cpi->pb.QThreshTable[cpi->Configuration.ActiveMaxQ]; - } - - if(cpi->FixedQ) { - if ( cpi->pb.FrameType == KEY_FRAME ) { - cpi->pb.ThisFrameQualityValue = cpi->pb.QThreshTable[43]; - cpi->pb.ThisFrameQualityValue = cpi->FixedQ; - } else { - cpi->pb.ThisFrameQualityValue = cpi->FixedQ; - } - } - - /* If the quantizer value has changed then re-initialise it */ - if ( cpi->pb.ThisFrameQualityValue != cpi->pb.LastFrameQualityValue ) { - /* Initialise quality tables. */ - UpdateQC( cpi, cpi->pb.ThisFrameQualityValue ); - cpi->pb.LastFrameQualityValue = cpi->pb.ThisFrameQualityValue; - } -} - -void CopyBackExtraFrags(CP_INSTANCE *cpi){ - ogg_uint32_t i,j; - unsigned char * SrcPtr; - unsigned char * DestPtr; - ogg_uint32_t PlaneLineStep; - ogg_uint32_t PixelIndex; - - /* Copy back for Y plane. */ - PlaneLineStep = cpi->pb.info.width; - for ( i = 0; i < cpi->pb.YPlaneFragments; i++ ) { - /* We are only interested in updated fragments. */ - if ( cpi->extra_fragments[i] ) { - /* Get the start index for the fragment. */ - PixelIndex = cpi->pb.pixel_index_table[i]; - SrcPtr = &cpi->yuv1ptr[PixelIndex]; - DestPtr = &cpi->ConvDestBuffer[PixelIndex]; - - for ( j = 0; j < VFRAGPIXELS; j++ ) { - memcpy( DestPtr, SrcPtr, HFRAGPIXELS); - - SrcPtr += PlaneLineStep; - DestPtr += PlaneLineStep; - } - } - } - - /* Now the U and V planes */ - PlaneLineStep = cpi->pb.info.width / 2; - for ( i = cpi->pb.YPlaneFragments; - i < (cpi->pb.YPlaneFragments + (2 * cpi->pb.UVPlaneFragments)) ; - i++ ) { - - /* We are only interested in updated fragments. */ - if ( cpi->extra_fragments[i] ) { - /* Get the start index for the fragment. */ - PixelIndex = cpi->pb.pixel_index_table[i]; - SrcPtr = &cpi->yuv1ptr[PixelIndex]; - DestPtr = &cpi->ConvDestBuffer[PixelIndex]; - - for ( j = 0; j < VFRAGPIXELS; j++ ) { - memcpy( DestPtr, SrcPtr, HFRAGPIXELS); - SrcPtr += PlaneLineStep; - DestPtr += PlaneLineStep; - } - } - } -} - diff --git a/Engine/lib/libtheora/lib/enc/pb.c b/Engine/lib/libtheora/lib/enc/pb.c deleted file mode 100644 index 42047249a..000000000 --- a/Engine/lib/libtheora/lib/enc/pb.c +++ /dev/null @@ -1,89 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: pb.c 14372 2008-01-05 23:52:28Z giles $ - - ********************************************************************/ - -#include -#include -#include "codec_internal.h" - -void ClearTmpBuffers(PB_INSTANCE * pbi){ - - if(pbi->ReconDataBuffer) - _ogg_free(pbi->ReconDataBuffer); - if(pbi->DequantBuffer) - _ogg_free(pbi->DequantBuffer); - if(pbi->TmpDataBuffer) - _ogg_free(pbi->TmpDataBuffer); - if(pbi->TmpReconBuffer) - _ogg_free(pbi->TmpReconBuffer); - - - pbi->ReconDataBuffer=0; - pbi->DequantBuffer = 0; - pbi->TmpDataBuffer = 0; - pbi->TmpReconBuffer = 0; - -} - -void InitTmpBuffers(PB_INSTANCE * pbi){ - - /* clear any existing info */ - ClearTmpBuffers(pbi); - - /* Adjust the position of all of our temporary */ - pbi->ReconDataBuffer = - _ogg_malloc(64*sizeof(*pbi->ReconDataBuffer)); - - pbi->DequantBuffer = - _ogg_malloc(64 * sizeof(*pbi->DequantBuffer)); - - pbi->TmpDataBuffer = - _ogg_malloc(64 * sizeof(*pbi->TmpDataBuffer)); - - pbi->TmpReconBuffer = - _ogg_malloc(64 * sizeof(*pbi->TmpReconBuffer)); - -} - -void ClearPBInstance(PB_INSTANCE *pbi){ - if(pbi){ - ClearTmpBuffers(pbi); - if (pbi->opb) { - _ogg_free(pbi->opb); - } - } -} - -void InitPBInstance(PB_INSTANCE *pbi){ - /* initialize whole structure to 0 */ - memset(pbi, 0, sizeof(*pbi)); - - InitTmpBuffers(pbi); - - /* allocate memory for the oggpack_buffer */ - pbi->opb = _ogg_malloc(sizeof(oggpack_buffer)); - - /* variables needing initialization (not being set to 0) */ - - pbi->ModifierPointer[0] = &pbi->Modifier[0][255]; - pbi->ModifierPointer[1] = &pbi->Modifier[1][255]; - pbi->ModifierPointer[2] = &pbi->Modifier[2][255]; - pbi->ModifierPointer[3] = &pbi->Modifier[3][255]; - - pbi->DecoderErrorCode = 0; - pbi->KeyFrameType = DCT_KEY_FRAME; - pbi->FramesHaveBeenSkipped = 0; -} diff --git a/Engine/lib/libtheora/lib/enc/pp.c b/Engine/lib/libtheora/lib/enc/pp.c deleted file mode 100644 index c45289703..000000000 --- a/Engine/lib/libtheora/lib/enc/pp.c +++ /dev/null @@ -1,951 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: pp.c 15057 2008-06-22 21:07:32Z xiphmont $ - - ********************************************************************/ - -#include -#include -#include "codec_internal.h" -#include "pp.h" -#include "dsp.h" - -#define MAX(a, b) ((a>b)?a:b) -#define MIN(a, b) ((aScanPixelIndexTable) _ogg_free(ppi->ScanPixelIndexTable); - ppi->ScanPixelIndexTable=0; - - if(ppi->ScanDisplayFragments) _ogg_free(ppi->ScanDisplayFragments); - ppi->ScanDisplayFragments=0; - - for(i = 0 ; i < MAX_PREV_FRAMES ; i ++) - if(ppi->PrevFragments[i]){ - _ogg_free(ppi->PrevFragments[i]); - ppi->PrevFragments[i]=0; - } - - if(ppi->FragScores) _ogg_free(ppi->FragScores); - ppi->FragScores=0; - - if(ppi->SameGreyDirPixels) _ogg_free(ppi->SameGreyDirPixels); - ppi->SameGreyDirPixels=0; - - if(ppi->FragDiffPixels) _ogg_free(ppi->FragDiffPixels); - ppi->FragDiffPixels=0; - - if(ppi->BarBlockMap) _ogg_free(ppi->BarBlockMap); - ppi->BarBlockMap=0; - - if(ppi->TmpCodedMap) _ogg_free(ppi->TmpCodedMap); - ppi->TmpCodedMap=0; - - if(ppi->RowChangedPixels) _ogg_free(ppi->RowChangedPixels); - ppi->RowChangedPixels=0; - - if(ppi->PixelScores) _ogg_free(ppi->PixelScores); - ppi->PixelScores=0; - - if(ppi->PixelChangedMap) _ogg_free(ppi->PixelChangedMap); - ppi->PixelChangedMap=0; - - if(ppi->ChLocals) _ogg_free(ppi->ChLocals); - ppi->ChLocals=0; - - if(ppi->yuv_differences) _ogg_free(ppi->yuv_differences); - ppi->yuv_differences=0; - -} - -void PInitFrameInfo(PP_INSTANCE * ppi){ - int i; - PClearFrameInfo(ppi); - - ppi->ScanPixelIndexTable = - _ogg_malloc(ppi->ScanFrameFragments*sizeof(*ppi->ScanPixelIndexTable)); - - ppi->ScanDisplayFragments = - _ogg_malloc(ppi->ScanFrameFragments*sizeof(*ppi->ScanDisplayFragments)); - - for(i = 0 ; i < MAX_PREV_FRAMES ; i ++) - ppi->PrevFragments[i] = - _ogg_malloc(ppi->ScanFrameFragments*sizeof(*ppi->PrevFragments)); - - ppi->FragScores = - _ogg_malloc(ppi->ScanFrameFragments*sizeof(*ppi->FragScores)); - - ppi->SameGreyDirPixels = - _ogg_malloc(ppi->ScanFrameFragments*sizeof(*ppi->SameGreyDirPixels)); - - ppi->FragDiffPixels = - _ogg_malloc(ppi->ScanFrameFragments*sizeof(*ppi->FragScores)); - - ppi->BarBlockMap= - _ogg_malloc(3 * ppi->ScanHFragments*sizeof(*ppi->BarBlockMap)); - - ppi->TmpCodedMap = - _ogg_malloc(ppi->ScanHFragments*sizeof(*ppi->TmpCodedMap)); - - ppi->RowChangedPixels = - _ogg_malloc(3 * ppi->ScanConfig.VideoFrameHeight* - sizeof(*ppi->RowChangedPixels)); - - ppi->PixelScores = - _ogg_malloc(ppi->ScanConfig.VideoFrameWidth* - sizeof(*ppi->PixelScores) * PSCORE_CB_ROWS); - - ppi->PixelChangedMap = - _ogg_malloc(ppi->ScanConfig.VideoFrameWidth* - sizeof(*ppi->PixelChangedMap) * PMAP_CB_ROWS); - - ppi->ChLocals = - _ogg_malloc(ppi->ScanConfig.VideoFrameWidth* - sizeof(*ppi->ChLocals) * CHLOCALS_CB_ROWS); - - ppi->yuv_differences = - _ogg_malloc(ppi->ScanConfig.VideoFrameWidth* - sizeof(*ppi->yuv_differences) * YDIFF_CB_ROWS); -} - -void ClearPPInstance(PP_INSTANCE *ppi){ - PClearFrameInfo(ppi); -} - - -void InitPPInstance(PP_INSTANCE *ppi, DspFunctions *funcs){ - - memset(ppi,0,sizeof(*ppi)); - - memcpy(&ppi->dsp, funcs, sizeof(DspFunctions)); - - /* Initializations */ - ppi->PrevFrameLimit = 3; /* Must not exceed MAX_PREV_FRAMES (Note - that this number includes the current - frame so "1 = no effect") */ - - /* Scan control variables. */ - ppi->HFragPixels = 8; - ppi->VFragPixels = 8; - - ppi->SRFGreyThresh = 4; - ppi->SRFColThresh = 5; - ppi->NoiseSupLevel = 3; - ppi->SgcLevelThresh = 3; - ppi->SuvcLevelThresh = 4; - - /* Variables controlling S.A.D. breakouts. */ - ppi->GrpLowSadThresh = 10; - ppi->GrpHighSadThresh = 64; - ppi->PrimaryBlockThreshold = 5; - ppi->SgcThresh = 16; /* (Default values for 8x8 blocks). */ - - ppi->UVBlockThreshCorrection = 1.25; - ppi->UVSgcCorrection = 1.5; - - ppi->MaxLineSearchLen = MAX_SEARCH_LINE_LEN; -} - -static void DeringBlockStrong(unsigned char *SrcPtr, - unsigned char *DstPtr, - ogg_int32_t Pitch, - ogg_uint32_t FragQIndex, - const ogg_uint32_t *QuantScale){ - - ogg_int16_t UDMod[72]; - ogg_int16_t LRMod[72]; - unsigned int j,k,l; - const unsigned char * Src; - unsigned int QValue = QuantScale[FragQIndex]; - - unsigned char p; - unsigned char pl; - unsigned char pr; - unsigned char pu; - unsigned char pd; - - int al; - int ar; - int au; - int ad; - - int atot; - int B; - int newVal; - - const unsigned char *curRow = SrcPtr - 1; /* avoid negative array indexes */ - unsigned char *dstRow = DstPtr; - const unsigned char *lastRow = SrcPtr-Pitch; - const unsigned char *nextRow = SrcPtr+Pitch; - - unsigned int rowOffset = 0; - unsigned int round = (1<<6); - - int High; - int Low; - int TmpMod; - - int Sharpen = SharpenModifier[FragQIndex]; - High = 3 * QValue; - if(High>32)High=32; - Low = 0; - - - /* Initialize the Mod Data */ - Src = SrcPtr-Pitch; - for(k=0;k<9;k++){ - for(j=0;j<8;j++){ - - TmpMod = 32 + QValue - (abs(Src[j+Pitch]-Src[j])); - - if(TmpMod< -64) - TmpMod = Sharpen; - - else if(TmpModHigh) - TmpMod = High; - - UDMod[k*8+j] = (ogg_int16_t)TmpMod; - } - Src +=Pitch; - } - - Src = SrcPtr-1; - - for(k=0;k<8;k++){ - for(j=0;j<9;j++){ - TmpMod = 32 + QValue - (abs(Src[j+1]-Src[j])); - - if(TmpMod< -64 ) - TmpMod = Sharpen; - - else if(TmpMod<0) - TmpMod = Low; - - else if(TmpMod>High) - TmpMod = High; - - LRMod[k*9+j] = (ogg_int16_t)TmpMod; - } - Src+=Pitch; - } - - for(k=0;k<8;k++){ - /* In the case that this function called with same buffer for - source and destination, To keep the c and the mmx version to have - consistant results, intermediate buffer is used to store the - eight pixel value before writing them to destination - (i.e. Overwriting souce for the speical case) */ - for(l=0;l<8;l++){ - - atot = 128; - B = round; - p = curRow[ rowOffset +l +1]; - - pl = curRow[ rowOffset +l]; - al = LRMod[k*9+l]; - atot -= al; - B += al * pl; - - pu = lastRow[ rowOffset +l]; - au = UDMod[k*8+l]; - atot -= au; - B += au * pu; - - pd = nextRow[ rowOffset +l]; - ad = UDMod[(k+1)*8+l]; - atot -= ad; - B += ad * pd; - - pr = curRow[ rowOffset +l+2]; - ar = LRMod[k*9+l+1]; - atot -= ar; - B += ar * pr; - - newVal = ( atot * p + B) >> 7; - - dstRow[ rowOffset +l]= clamp255( newVal ); - } - rowOffset += Pitch; - } -} - -static void DeringBlockWeak(unsigned char *SrcPtr, - unsigned char *DstPtr, - ogg_int32_t Pitch, - ogg_uint32_t FragQIndex, - const ogg_uint32_t *QuantScale){ - - ogg_int16_t UDMod[72]; - ogg_int16_t LRMod[72]; - unsigned int j,k; - const unsigned char * Src; - unsigned int QValue = QuantScale[FragQIndex]; - - unsigned char p; - unsigned char pl; - unsigned char pr; - unsigned char pu; - unsigned char pd; - - int al; - int ar; - int au; - int ad; - - int atot; - int B; - int newVal; - - const unsigned char *curRow = SrcPtr-1; - unsigned char *dstRow = DstPtr; - const unsigned char *lastRow = SrcPtr-Pitch; - const unsigned char *nextRow = SrcPtr+Pitch; - - unsigned int rowOffset = 0; - unsigned int round = (1<<6); - - int High; - int Low; - int TmpMod; - int Sharpen = SharpenModifier[FragQIndex]; - - High = 3 * QValue; - if(High>24) - High=24; - Low = 0 ; - - /* Initialize the Mod Data */ - Src=SrcPtr-Pitch; - for(k=0;k<9;k++) { - for(j=0;j<8;j++) { - - TmpMod = 32 + QValue - 2*(abs(Src[j+Pitch]-Src[j])); - - if(TmpMod< -64) - TmpMod = Sharpen; - - else if(TmpModHigh) - TmpMod = High; - - UDMod[k*8+j] = (ogg_int16_t)TmpMod; - } - Src +=Pitch; - } - - Src = SrcPtr-1; - - for(k=0;k<8;k++){ - for(j=0;j<9;j++){ - TmpMod = 32 + QValue - 2*(abs(Src[j+1]-Src[j])); - - if(TmpMod< -64 ) - TmpMod = Sharpen; - - else if(TmpModHigh) - TmpMod = High; - - LRMod[k*9+j] = (ogg_int16_t)TmpMod; - } - Src+=Pitch; - } - - for(k=0;k<8;k++) { - for(j=0;j<8;j++){ - atot = 128; - B = round; - p = curRow[ rowOffset +j+1]; - - pl = curRow[ rowOffset +j]; - al = LRMod[k*9+j]; - atot -= al; - B += al * pl; - - pu = lastRow[ rowOffset +j]; - au = UDMod[k*8+j]; - atot -= au; - B += au * pu; - - pd = nextRow[ rowOffset +j]; - ad = UDMod[(k+1)*8+j]; - atot -= ad; - B += ad * pd; - - pr = curRow[ rowOffset +j+2]; - ar = LRMod[k*9+j+1]; - atot -= ar; - B += ar * pr; - - newVal = ( atot * p + B) >> 7; - - dstRow[ rowOffset +j] = clamp255( newVal ); - } - - rowOffset += Pitch; - } -} - -static void DeringFrame(PB_INSTANCE *pbi, - unsigned char *Src, unsigned char *Dst){ - ogg_uint32_t col,row; - unsigned char *SrcPtr; - unsigned char *DestPtr; - ogg_uint32_t BlocksAcross,BlocksDown; - const ogg_uint32_t *QuantScale; - ogg_uint32_t Block; - ogg_uint32_t LineLength; - - ogg_int32_t Thresh1,Thresh2,Thresh3,Thresh4; - - Thresh1 = 384; - Thresh2 = 4 * Thresh1; - Thresh3 = 5 * Thresh2/4; - Thresh4 = 5 * Thresh2/2; - - QuantScale = DeringModifierV1; - - BlocksAcross = pbi->HFragments; - BlocksDown = pbi->VFragments; - - SrcPtr = Src + pbi->ReconYDataOffset; - DestPtr = Dst + pbi->ReconYDataOffset; - LineLength = pbi->YStride; - - Block = 0; - - for ( row = 0 ; row < BlocksDown; row ++){ - for (col = 0; col < BlocksAcross; col ++){ - ogg_uint32_t Quality = pbi->FragQIndex[Block]; - ogg_int32_t Variance = pbi->FragmentVariances[Block]; - - if( pbi->PostProcessingLevel >5 && Variance > Thresh3 ){ - DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - - if( (col > 0 && - pbi->FragmentVariances[Block-1] > Thresh4 ) || - (col + 1 < BlocksAcross && - pbi->FragmentVariances[Block+1] > Thresh4 ) || - (row + 1 < BlocksDown && - pbi->FragmentVariances[Block+BlocksAcross] > Thresh4) || - (row > 0 && - pbi->FragmentVariances[Block-BlocksAcross] > Thresh4) ){ - - DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - } - } else if(Variance > Thresh2 ) { - - DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - } else if(Variance > Thresh1 ) { - - DeringBlockWeak(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - - } else { - - dsp_copy8x8(pbi->dsp, SrcPtr + 8 * col, DestPtr + 8 * col, LineLength); - - } - - ++Block; - - } - SrcPtr += 8 * LineLength; - DestPtr += 8 * LineLength; - } - - /* Then U */ - - BlocksAcross /= 2; - BlocksDown /= 2; - LineLength /= 2; - - SrcPtr = Src + pbi->ReconUDataOffset; - DestPtr = Dst + pbi->ReconUDataOffset; - for ( row = 0 ; row < BlocksDown; row ++) { - for (col = 0; col < BlocksAcross; col ++) { - ogg_uint32_t Quality = pbi->FragQIndex[Block]; - ogg_int32_t Variance = pbi->FragmentVariances[Block]; - - if( pbi->PostProcessingLevel >5 && Variance > Thresh4 ) { - DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - - }else if(Variance > Thresh2 ){ - DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - }else if(Variance > Thresh1 ){ - DeringBlockWeak(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - }else{ - dsp_copy8x8(pbi->dsp, SrcPtr + 8 * col, DestPtr + 8 * col, LineLength); - } - - ++Block; - - } - SrcPtr += 8 * LineLength; - DestPtr += 8 * LineLength; - } - - /* Then V */ - SrcPtr = Src + pbi->ReconVDataOffset; - DestPtr = Dst + pbi->ReconVDataOffset; - - for ( row = 0 ; row < BlocksDown; row ++){ - for (col = 0; col < BlocksAcross; col ++){ - - ogg_uint32_t Quality = pbi->FragQIndex[Block]; - ogg_int32_t Variance = pbi->FragmentVariances[Block]; - - - if( pbi->PostProcessingLevel >5 && Variance > Thresh4 ) { - DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - - }else if(Variance > Thresh2 ){ - DeringBlockStrong(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - }else if(Variance > Thresh1 ){ - DeringBlockWeak(SrcPtr + 8 * col, DestPtr + 8 * col, - LineLength,Quality,QuantScale); - }else{ - dsp_copy8x8(pbi->dsp, SrcPtr + 8 * col, DestPtr + 8 * col, LineLength); - } - - ++Block; - - } - SrcPtr += 8 * LineLength; - DestPtr += 8 * LineLength; - - } - -} - -void UpdateFragQIndex(PB_INSTANCE *pbi){ - - ogg_uint32_t ThisFrameQIndex; - ogg_uint32_t i; - - /* Check this frame quality index */ - ThisFrameQIndex = pbi->FrameQIndex; - - - /* It is not a key frame, so only reset those are coded */ - for( i = 0; i < pbi->UnitFragments; i++ ) - if( pbi->display_fragments[i]) - pbi->FragQIndex[i] = ThisFrameQIndex; - -} - -static void DeblockLoopFilteredBand(PB_INSTANCE *pbi, - unsigned char *SrcPtr, - unsigned char *DesPtr, - ogg_uint32_t PlaneLineStep, - ogg_uint32_t FragsAcross, - ogg_uint32_t StartFrag, - const ogg_uint32_t *QuantScale){ - ogg_uint32_t j,k; - ogg_uint32_t CurrentFrag=StartFrag; - ogg_int32_t QStep; - ogg_int32_t FLimit; - unsigned char *Src, *Des; - ogg_int32_t x[10]; - ogg_int32_t Sum1, Sum2; - - while(CurrentFrag < StartFrag + FragsAcross){ - - Src=SrcPtr+8*(CurrentFrag-StartFrag)-PlaneLineStep*5; - Des=DesPtr+8*(CurrentFrag-StartFrag)-PlaneLineStep*4; - - QStep = QuantScale[pbi->FragQIndex[CurrentFrag+FragsAcross]]; - FLimit = ( QStep * 3 ) >> 2; - - for( j=0; j<8 ; j++){ - x[0] = Src[0]; - x[1] = Src[PlaneLineStep]; - x[2] = Src[PlaneLineStep*2]; - x[3] = Src[PlaneLineStep*3]; - x[4] = Src[PlaneLineStep*4]; - x[5] = Src[PlaneLineStep*5]; - x[6] = Src[PlaneLineStep*6]; - x[7] = Src[PlaneLineStep*7]; - x[8] = Src[PlaneLineStep*8]; - x[9] = Src[PlaneLineStep*9]; - - Sum1=Sum2=0; - - for(k=1;k<=4;k++){ - Sum1 += abs(x[k]-x[k-1]); - Sum2 += abs(x[k+4]-x[k+5]); - } - - pbi->FragmentVariances[CurrentFrag] +=((Sum1>255)?255:Sum1); - pbi->FragmentVariances[CurrentFrag + FragsAcross] += ((Sum2>255)?255:Sum2); - - if( Sum1 < FLimit && - Sum2 < FLimit && - (x[5] - x[4]) < QStep && - (x[4] - x[5]) < QStep ){ - - /* low pass filtering (LPF7: 1 1 1 2 1 1 1) */ - Des[0 ] = (x[0] + x[0] +x[0] + x[1] * 2 + - x[2] + x[3] +x[4] + 4) >> 3; - Des[PlaneLineStep ] = (x[0] + x[0] +x[1] + x[2] * 2 + - x[3] + x[4] +x[5] + 4) >> 3; - Des[PlaneLineStep*2] = (x[0] + x[1] +x[2] + x[3] * 2 + - x[4] + x[5] +x[6] + 4) >> 3; - Des[PlaneLineStep*3] = (x[1] + x[2] +x[3] + x[4] * 2 + - x[5] + x[6] +x[7] + 4) >> 3; - Des[PlaneLineStep*4] = (x[2] + x[3] +x[4] + x[5] * 2 + - x[6] + x[7] +x[8] + 4) >> 3; - Des[PlaneLineStep*5] = (x[3] + x[4] +x[5] + x[6] * 2 + - x[7] + x[8] +x[9] + 4) >> 3; - Des[PlaneLineStep*6] = (x[4] + x[5] +x[6] + x[7] * 2 + - x[8] + x[9] +x[9] + 4) >> 3; - Des[PlaneLineStep*7] = (x[5] + x[6] +x[7] + x[8] * 2 + - x[9] + x[9] +x[9] + 4) >> 3; - - }else { - /* copy the pixels to destination */ - Des[0 ]= (unsigned char)x[1]; - Des[PlaneLineStep ]= (unsigned char)x[2]; - Des[PlaneLineStep*2]= (unsigned char)x[3]; - Des[PlaneLineStep*3]= (unsigned char)x[4]; - Des[PlaneLineStep*4]= (unsigned char)x[5]; - Des[PlaneLineStep*5]= (unsigned char)x[6]; - Des[PlaneLineStep*6]= (unsigned char)x[7]; - Des[PlaneLineStep*7]= (unsigned char)x[8]; - } - Src ++; - Des ++; - } - - - /* done with filtering the horizontal edge, now let's do the - vertical one */ - /* skip the first one */ - if(CurrentFrag==StartFrag) - CurrentFrag++; - else{ - Des=DesPtr-8*PlaneLineStep+8*(CurrentFrag-StartFrag); - Src=Des-5; - Des-=4; - - QStep = QuantScale[pbi->FragQIndex[CurrentFrag]]; - FLimit = ( QStep * 3 ) >> 2; - - for( j=0; j<8 ; j++){ - x[0] = Src[0]; - x[1] = Src[1]; - x[2] = Src[2]; - x[3] = Src[3]; - x[4] = Src[4]; - x[5] = Src[5]; - x[6] = Src[6]; - x[7] = Src[7]; - x[8] = Src[8]; - x[9] = Src[9]; - - Sum1=Sum2=0; - - for(k=1;k<=4;k++){ - Sum1 += abs(x[k]-x[k-1]); - Sum2 += abs(x[k+4]-x[k+5]); - } - - pbi->FragmentVariances[CurrentFrag-1] += ((Sum1>255)?255:Sum1); - pbi->FragmentVariances[CurrentFrag] += ((Sum2>255)?255:Sum2); - - if( Sum1 < FLimit && - Sum2 < FLimit && - (x[5] - x[4]) < QStep && - (x[4] - x[5]) < QStep ){ - - /* low pass filtering (LPF7: 1 1 1 2 1 1 1) */ - Des[0] = (x[0] + x[0] +x[0] + x[1] * 2 + x[2] + x[3] +x[4] + 4) >> 3; - Des[1] = (x[0] + x[0] +x[1] + x[2] * 2 + x[3] + x[4] +x[5] + 4) >> 3; - Des[2] = (x[0] + x[1] +x[2] + x[3] * 2 + x[4] + x[5] +x[6] + 4) >> 3; - Des[3] = (x[1] + x[2] +x[3] + x[4] * 2 + x[5] + x[6] +x[7] + 4) >> 3; - Des[4] = (x[2] + x[3] +x[4] + x[5] * 2 + x[6] + x[7] +x[8] + 4) >> 3; - Des[5] = (x[3] + x[4] +x[5] + x[6] * 2 + x[7] + x[8] +x[9] + 4) >> 3; - Des[6] = (x[4] + x[5] +x[6] + x[7] * 2 + x[8] + x[9] +x[9] + 4) >> 3; - Des[7] = (x[5] + x[6] +x[7] + x[8] * 2 + x[9] + x[9] +x[9] + 4) >> 3; - } - - Src += PlaneLineStep; - Des += PlaneLineStep; - } - CurrentFrag ++; - } - } -} - -static void DeblockVerticalEdgesInLoopFilteredBand(PB_INSTANCE *pbi, - unsigned char *SrcPtr, - unsigned char *DesPtr, - ogg_uint32_t PlaneLineStep, - ogg_uint32_t FragsAcross, - ogg_uint32_t StartFrag, - const ogg_uint32_t *QuantScale){ - ogg_uint32_t j,k; - ogg_uint32_t CurrentFrag=StartFrag; - ogg_int32_t QStep; - ogg_int32_t FLimit; - unsigned char *Src, *Des; - ogg_int32_t x[10]; - ogg_int32_t Sum1, Sum2; - - while(CurrentFrag < StartFrag + FragsAcross-1) { - - Src=SrcPtr+8*(CurrentFrag-StartFrag+1)-5; - Des=DesPtr+8*(CurrentFrag-StartFrag+1)-4; - - QStep = QuantScale[pbi->FragQIndex[CurrentFrag+1]]; - FLimit = ( QStep * 3)>>2 ; - - for( j=0; j<8 ; j++){ - x[0] = Src[0]; - x[1] = Src[1]; - x[2] = Src[2]; - x[3] = Src[3]; - x[4] = Src[4]; - x[5] = Src[5]; - x[6] = Src[6]; - x[7] = Src[7]; - x[8] = Src[8]; - x[9] = Src[9]; - - Sum1=Sum2=0; - - for(k=1;k<=4;k++){ - Sum1 += abs(x[k]-x[k-1]); - Sum2 += abs(x[k+4]-x[k+5]); - } - - pbi->FragmentVariances[CurrentFrag] += ((Sum1>255)?255:Sum1); - pbi->FragmentVariances[CurrentFrag+1] += ((Sum2>255)?255:Sum2); - - - if( Sum1 < FLimit && - Sum2 < FLimit && - (x[5] - x[4]) < QStep && - (x[4] - x[5]) < QStep ){ - - /* low pass filtering (LPF7: 1 1 1 2 1 1 1) */ - Des[0] = (x[0] + x[0] +x[0] + x[1] * 2 + x[2] + x[3] +x[4] + 4) >> 3; - Des[1] = (x[0] + x[0] +x[1] + x[2] * 2 + x[3] + x[4] +x[5] + 4) >> 3; - Des[2] = (x[0] + x[1] +x[2] + x[3] * 2 + x[4] + x[5] +x[6] + 4) >> 3; - Des[3] = (x[1] + x[2] +x[3] + x[4] * 2 + x[5] + x[6] +x[7] + 4) >> 3; - Des[4] = (x[2] + x[3] +x[4] + x[5] * 2 + x[6] + x[7] +x[8] + 4) >> 3; - Des[5] = (x[3] + x[4] +x[5] + x[6] * 2 + x[7] + x[8] +x[9] + 4) >> 3; - Des[6] = (x[4] + x[5] +x[6] + x[7] * 2 + x[8] + x[9] +x[9] + 4) >> 3; - Des[7] = (x[5] + x[6] +x[7] + x[8] * 2 + x[9] + x[9] +x[9] + 4) >> 3; - } - Src +=PlaneLineStep; - Des +=PlaneLineStep; - - } - CurrentFrag ++; - } -} - -static void DeblockPlane(PB_INSTANCE *pbi, - unsigned char *SourceBuffer, - unsigned char *DestinationBuffer, - ogg_uint32_t Channel ){ - - ogg_uint32_t i,k; - ogg_uint32_t PlaneLineStep=0; - ogg_uint32_t StartFrag =0; - ogg_uint32_t PixelIndex=0; - unsigned char * SrcPtr=0, * DesPtr=0; - ogg_uint32_t FragsAcross=0; - ogg_uint32_t FragsDown=0; - const ogg_uint32_t *QuantScale=0; - - switch( Channel ){ - case 0: - /* Get the parameters */ - PlaneLineStep = pbi->YStride; - FragsAcross = pbi->HFragments; - FragsDown = pbi->VFragments; - StartFrag = 0; - PixelIndex = pbi->ReconYDataOffset; - SrcPtr = & SourceBuffer[PixelIndex]; - DesPtr = & DestinationBuffer[PixelIndex]; - break; - - case 1: - /* Get the parameters */ - PlaneLineStep = pbi->UVStride; - FragsAcross = pbi->HFragments / 2; - FragsDown = pbi->VFragments / 2; - StartFrag = pbi->YPlaneFragments; - - PixelIndex = pbi->ReconUDataOffset; - SrcPtr = & SourceBuffer[PixelIndex]; - DesPtr = & DestinationBuffer[PixelIndex]; - break; - - default: - /* Get the parameters */ - PlaneLineStep = pbi->UVStride; - FragsAcross = pbi->HFragments / 2; - FragsDown = pbi->VFragments / 2; - StartFrag = pbi->YPlaneFragments + pbi->UVPlaneFragments; - - PixelIndex = pbi->ReconVDataOffset; - SrcPtr = & SourceBuffer[PixelIndex]; - DesPtr = & DestinationBuffer[PixelIndex]; - break; - } - - QuantScale = DcQuantScaleV1; - - for(i=0;i<4;i++) - memcpy(DesPtr+i*PlaneLineStep, SrcPtr+i*PlaneLineStep, PlaneLineStep); - - k = 1; - - while( k < FragsDown ){ - - SrcPtr += 8*PlaneLineStep; - DesPtr += 8*PlaneLineStep; - - /* Filter both the horizontal and vertical block edges inside the band */ - DeblockLoopFilteredBand(pbi, SrcPtr, DesPtr, PlaneLineStep, - FragsAcross, StartFrag, QuantScale); - - /* Move Pointers */ - StartFrag += FragsAcross; - - k ++; - } - - /* The Last band */ - for(i=0;i<4;i++) - memcpy(DesPtr+(i+4)*PlaneLineStep, - SrcPtr+(i+4)*PlaneLineStep, - PlaneLineStep); - - DeblockVerticalEdgesInLoopFilteredBand(pbi,SrcPtr,DesPtr,PlaneLineStep, - FragsAcross,StartFrag,QuantScale); - -} - -static void DeblockFrame(PB_INSTANCE *pbi, unsigned char *SourceBuffer, - unsigned char *DestinationBuffer){ - - memset(pbi->FragmentVariances, 0 , sizeof(ogg_int32_t) * pbi->UnitFragments); - - - UpdateFragQIndex(pbi); - - /* Y */ - DeblockPlane( pbi, SourceBuffer, DestinationBuffer, 0); - - /* U */ - DeblockPlane( pbi, SourceBuffer, DestinationBuffer, 1); - - /* V */ - DeblockPlane( pbi, SourceBuffer, DestinationBuffer, 2); - -} - -void PostProcess(PB_INSTANCE *pbi){ - - switch (pbi->PostProcessingLevel){ - case 8: - /* on a slow machine, use a simpler and faster deblocking filter */ - DeblockFrame(pbi, pbi->LastFrameRecon,pbi->PostProcessBuffer); - break; - - case 6: - DeblockFrame(pbi, pbi->LastFrameRecon,pbi->PostProcessBuffer); - UpdateUMVBorder(pbi, pbi->PostProcessBuffer ); - DeringFrame(pbi, pbi->PostProcessBuffer, pbi->PostProcessBuffer); - break; - - case 5: - DeblockFrame(pbi, pbi->LastFrameRecon,pbi->PostProcessBuffer); - UpdateUMVBorder(pbi, pbi->PostProcessBuffer ); - DeringFrame(pbi, pbi->PostProcessBuffer, pbi->PostProcessBuffer); - break; - case 4: - DeblockFrame(pbi, pbi->LastFrameRecon, pbi->PostProcessBuffer); - break; - case 1: - UpdateFragQIndex(pbi); - break; - - case 0: - break; - - default: - DeblockFrame(pbi, pbi->LastFrameRecon, pbi->PostProcessBuffer); - UpdateUMVBorder(pbi, pbi->PostProcessBuffer ); - DeringFrame(pbi, pbi->PostProcessBuffer, pbi->PostProcessBuffer); - break; - } -} - diff --git a/Engine/lib/libtheora/lib/enc/pp.h b/Engine/lib/libtheora/lib/enc/pp.h deleted file mode 100644 index 6eb3a7604..000000000 --- a/Engine/lib/libtheora/lib/enc/pp.h +++ /dev/null @@ -1,48 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: pp.h 13884 2007-09-22 08:38:10Z giles $ - - ********************************************************************/ - -/* Constants. */ -#define INTERNAL_BLOCK_HEIGHT 8 -#define INTERNAL_BLOCK_WIDTH 8 - - -/* NEW Line search values. */ -#define UP 0 -#define DOWN 1 -#define LEFT 2 -#define RIGHT 3 - -#define FIRST_ROW 0 -#define NOT_EDGE_ROW 1 -#define LAST_ROW 2 - -#define YDIFF_CB_ROWS (INTERNAL_BLOCK_HEIGHT * 3) -#define CHLOCALS_CB_ROWS (INTERNAL_BLOCK_HEIGHT * 3) -#define PMAP_CB_ROWS (INTERNAL_BLOCK_HEIGHT * 3) -#define PSCORE_CB_ROWS (INTERNAL_BLOCK_HEIGHT * 4) - -/* Status values in block coding map */ -#define CANDIDATE_BLOCK_LOW -2 -#define CANDIDATE_BLOCK -1 -#define BLOCK_NOT_CODED 0 -#define BLOCK_CODED_BAR 3 -#define BLOCK_CODED_SGC 4 -#define BLOCK_CODED_LOW 4 -#define BLOCK_CODED 5 - -#define MAX_PREV_FRAMES 16 -#define MAX_SEARCH_LINE_LEN 7 diff --git a/Engine/lib/libtheora/lib/enc/quant_lookup.h b/Engine/lib/libtheora/lib/enc/quant_lookup.h deleted file mode 100644 index 04bbce910..000000000 --- a/Engine/lib/libtheora/lib/enc/quant_lookup.h +++ /dev/null @@ -1,43 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: quant_lookup.h 13884 2007-09-22 08:38:10Z giles $ - - ********************************************************************/ - -#include "codec_internal.h" - -#define MIN16 ((1<<16)-1) -#define SHIFT16 (1<<16) - -#define MIN_LEGAL_QUANT_ENTRY 8 -#define MIN_DEQUANT_VAL 2 -#define IDCT_SCALE_FACTOR 2 /* Shift left bits to improve IDCT precision */ -#define OLD_SCHEME 1 - - -/****************************** - * lookup table for DCT coefficient zig-zag ordering - * ****************************/ - -static const ogg_uint32_t dezigzag_index[64] = { - 0, 1, 8, 16, 9, 2, 3, 10, - 17, 24, 32, 25, 18, 11, 4, 5, - 12, 19, 26, 33, 40, 48, 41, 34, - 27, 20, 13, 6, 7, 14, 21, 28, - 35, 42, 49, 56, 57, 50, 43, 36, - 29, 22, 15, 23, 30, 37, 44, 51, - 58, 59, 52, 45, 38, 31, 39, 46, - 53, 60, 61, 54, 47, 55, 62, 63 -}; - diff --git a/Engine/lib/libtheora/lib/enc/reconstruct.c b/Engine/lib/libtheora/lib/enc/reconstruct.c deleted file mode 100644 index 5602884af..000000000 --- a/Engine/lib/libtheora/lib/enc/reconstruct.c +++ /dev/null @@ -1,110 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: reconstruct.c 13884 2007-09-22 08:38:10Z giles $ - - ********************************************************************/ - -#include "codec_internal.h" - -static void copy8x8__c (unsigned char *src, - unsigned char *dest, - unsigned int stride) -{ - int j; - for ( j = 0; j < 8; j++ ){ - ((ogg_uint32_t*)dest)[0] = ((ogg_uint32_t*)src)[0]; - ((ogg_uint32_t*)dest)[1] = ((ogg_uint32_t*)src)[1]; - src+=stride; - dest+=stride; - } -} - -static void recon_intra8x8__c (unsigned char *ReconPtr, ogg_int16_t *ChangePtr, - ogg_uint32_t LineStep) -{ - ogg_uint32_t i; - - for (i = 8; i; i--){ - /* Convert the data back to 8 bit unsigned */ - /* Saturate the output to unsigend 8 bit values */ - ReconPtr[0] = clamp255( ChangePtr[0] + 128 ); - ReconPtr[1] = clamp255( ChangePtr[1] + 128 ); - ReconPtr[2] = clamp255( ChangePtr[2] + 128 ); - ReconPtr[3] = clamp255( ChangePtr[3] + 128 ); - ReconPtr[4] = clamp255( ChangePtr[4] + 128 ); - ReconPtr[5] = clamp255( ChangePtr[5] + 128 ); - ReconPtr[6] = clamp255( ChangePtr[6] + 128 ); - ReconPtr[7] = clamp255( ChangePtr[7] + 128 ); - - ReconPtr += LineStep; - ChangePtr += 8; - } -} - -static void recon_inter8x8__c (unsigned char *ReconPtr, unsigned char *RefPtr, - ogg_int16_t *ChangePtr, ogg_uint32_t LineStep) -{ - ogg_uint32_t i; - - for (i = 8; i; i--){ - ReconPtr[0] = clamp255(RefPtr[0] + ChangePtr[0]); - ReconPtr[1] = clamp255(RefPtr[1] + ChangePtr[1]); - ReconPtr[2] = clamp255(RefPtr[2] + ChangePtr[2]); - ReconPtr[3] = clamp255(RefPtr[3] + ChangePtr[3]); - ReconPtr[4] = clamp255(RefPtr[4] + ChangePtr[4]); - ReconPtr[5] = clamp255(RefPtr[5] + ChangePtr[5]); - ReconPtr[6] = clamp255(RefPtr[6] + ChangePtr[6]); - ReconPtr[7] = clamp255(RefPtr[7] + ChangePtr[7]); - - ChangePtr += 8; - ReconPtr += LineStep; - RefPtr += LineStep; - } -} - -static void recon_inter8x8_half__c (unsigned char *ReconPtr, unsigned char *RefPtr1, - unsigned char *RefPtr2, ogg_int16_t *ChangePtr, - ogg_uint32_t LineStep) -{ - ogg_uint32_t i; - - for (i = 8; i; i--){ - ReconPtr[0] = clamp255((((int)RefPtr1[0] + (int)RefPtr2[0]) >> 1) + ChangePtr[0] ); - ReconPtr[1] = clamp255((((int)RefPtr1[1] + (int)RefPtr2[1]) >> 1) + ChangePtr[1] ); - ReconPtr[2] = clamp255((((int)RefPtr1[2] + (int)RefPtr2[2]) >> 1) + ChangePtr[2] ); - ReconPtr[3] = clamp255((((int)RefPtr1[3] + (int)RefPtr2[3]) >> 1) + ChangePtr[3] ); - ReconPtr[4] = clamp255((((int)RefPtr1[4] + (int)RefPtr2[4]) >> 1) + ChangePtr[4] ); - ReconPtr[5] = clamp255((((int)RefPtr1[5] + (int)RefPtr2[5]) >> 1) + ChangePtr[5] ); - ReconPtr[6] = clamp255((((int)RefPtr1[6] + (int)RefPtr2[6]) >> 1) + ChangePtr[6] ); - ReconPtr[7] = clamp255((((int)RefPtr1[7] + (int)RefPtr2[7]) >> 1) + ChangePtr[7] ); - - ChangePtr += 8; - ReconPtr += LineStep; - RefPtr1 += LineStep; - RefPtr2 += LineStep; - } -} - -void dsp_recon_init (DspFunctions *funcs, ogg_uint32_t cpu_flags) -{ - funcs->copy8x8 = copy8x8__c; - funcs->recon_intra8x8 = recon_intra8x8__c; - funcs->recon_inter8x8 = recon_inter8x8__c; - funcs->recon_inter8x8_half = recon_inter8x8_half__c; -#if defined(USE_ASM) - if (cpu_flags & OC_CPU_X86_MMX) { - dsp_mmx_recon_init(funcs); - } -#endif -} diff --git a/Engine/lib/libtheora/lib/enc/scan.c b/Engine/lib/libtheora/lib/enc/scan.c deleted file mode 100644 index 5466ca438..000000000 --- a/Engine/lib/libtheora/lib/enc/scan.c +++ /dev/null @@ -1,2301 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: scan.c 13884 2007-09-22 08:38:10Z giles $ - - ********************************************************************/ - -#include -#include -#include -#include "codec_internal.h" -#include "dsp.h" - -#define MAX_SEARCH_LINE_LEN 7 - -#define SET8_0(ptr) \ - ((ogg_uint32_t *)ptr)[0] = 0x00000000; \ - ((ogg_uint32_t *)ptr)[1] = 0x00000000; -#define SET8_1(ptr) \ - ((ogg_uint32_t *)ptr)[0] = 0x01010101; \ - ((ogg_uint32_t *)ptr)[1] = 0x01010101; -#define SET8_8(ptr) \ - ((ogg_uint32_t *)ptr)[0] = 0x08080808; \ - ((ogg_uint32_t *)ptr)[1] = 0x08080808; - -static ogg_uint32_t LineLengthScores[ MAX_SEARCH_LINE_LEN + 1 ] = { - 0, 0, 0, 0, 2, 4, 12, 24 -}; - -static ogg_uint32_t BodyNeighbourScore = 8; -static double DiffDevisor = 0.0625; -#define HISTORY_BLOCK_FACTOR 2 -#define MIN_STEP_THRESH 6 -#define SCORE_MULT_LOW 0.5 -#define SCORE_MULT_HIGH 4 - -#define UP 0 -#define DOWN 1 -#define LEFT 2 -#define RIGHT 3 - -#define INTERNAL_BLOCK_HEIGHT 8 -#define INTERNAL_BLOCK_WIDTH 8 - -#define BLOCK_NOT_CODED 0 -#define BLOCK_CODED_BAR 3 -#define BLOCK_CODED_SGC 4 -#define BLOCK_CODED_LOW 4 -#define BLOCK_CODED 5 - -#define CANDIDATE_BLOCK_LOW -2 -#define CANDIDATE_BLOCK -1 - -#define FIRST_ROW 0 -#define NOT_EDGE_ROW 1 -#define LAST_ROW 2 - -#define YDIFF_CB_ROWS (INTERNAL_BLOCK_HEIGHT * 3) -#define CHLOCALS_CB_ROWS (INTERNAL_BLOCK_HEIGHT * 3) -#define PMAP_CB_ROWS (INTERNAL_BLOCK_HEIGHT * 3) - -void ConfigurePP( PP_INSTANCE *ppi, int Level ) { - switch ( Level ){ - case 0: - ppi->SRFGreyThresh = 1; - ppi->SRFColThresh = 1; - ppi->NoiseSupLevel = 2; - ppi->SgcLevelThresh = 1; - ppi->SuvcLevelThresh = 1; - ppi->GrpLowSadThresh = 6; - ppi->GrpHighSadThresh = 24; - ppi->PrimaryBlockThreshold = 2; - ppi->SgcThresh = 10; - - ppi->PAKEnabled = 0; - break; - - case 1: - ppi->SRFGreyThresh = 2; - ppi->SRFColThresh = 2; - ppi->NoiseSupLevel = 2; - ppi->SgcLevelThresh = 2; - ppi->SuvcLevelThresh = 2; - ppi->GrpLowSadThresh = 8; - ppi->GrpHighSadThresh = 32; - ppi->PrimaryBlockThreshold = 5; - ppi->SgcThresh = 12; - - ppi->PAKEnabled = 1; - break; - - case 2: /* Default VP3 settings */ - ppi->SRFGreyThresh = 3; - ppi->SRFColThresh = 3; - ppi->NoiseSupLevel = 2; - ppi->SgcLevelThresh = 2; - ppi->SuvcLevelThresh = 2; - ppi->GrpLowSadThresh = 8; - ppi->GrpHighSadThresh = 32; - ppi->PrimaryBlockThreshold = 5; - ppi->SgcThresh = 16; - - ppi->PAKEnabled = 1; - break; - - case 3: - ppi->SRFGreyThresh = 4; - ppi->SRFColThresh = 4; - ppi->NoiseSupLevel = 3; - ppi->SgcLevelThresh = 3; - ppi->SuvcLevelThresh = 3; - ppi->GrpLowSadThresh = 10; - ppi->GrpHighSadThresh = 48; - ppi->PrimaryBlockThreshold = 5; - ppi->SgcThresh = 18; - - ppi->PAKEnabled = 1; - break; - - case 4: - ppi->SRFGreyThresh = 5; - ppi->SRFColThresh = 5; - ppi->NoiseSupLevel = 3; - ppi->SgcLevelThresh = 4; - ppi->SuvcLevelThresh = 4; - ppi->GrpLowSadThresh = 12; - ppi->GrpHighSadThresh = 48; - ppi->PrimaryBlockThreshold = 5; - ppi->SgcThresh = 20; - - ppi->PAKEnabled = 1; - break; - - case 5: - ppi->SRFGreyThresh = 6; - ppi->SRFColThresh = 6; - ppi->NoiseSupLevel = 3; - ppi->SgcLevelThresh = 4; - ppi->SuvcLevelThresh = 4; - ppi->GrpLowSadThresh = 12; - ppi->GrpHighSadThresh = 64; - ppi->PrimaryBlockThreshold = 10; - ppi->SgcThresh = 24; - - ppi->PAKEnabled = 1; - break; - - case 6: - ppi->SRFGreyThresh = 6; - ppi->SRFColThresh = 7; - ppi->NoiseSupLevel = 3; - ppi->SgcLevelThresh = 4; - ppi->SuvcLevelThresh = 4; - ppi->GrpLowSadThresh = 12; - ppi->GrpHighSadThresh = 64; - ppi->PrimaryBlockThreshold = 10; - ppi->SgcThresh = 24; - - ppi->PAKEnabled = 1; - break; - - default: - ppi->SRFGreyThresh = 3; - ppi->SRFColThresh = 3; - ppi->NoiseSupLevel = 2; - ppi->SgcLevelThresh = 2; - ppi->SuvcLevelThresh = 2; - ppi->GrpLowSadThresh = 10; - ppi->GrpHighSadThresh = 32; - ppi->PrimaryBlockThreshold = 5; - ppi->SgcThresh = 16; - ppi->PAKEnabled = 1; - break; - } -} - -static void ScanCalcPixelIndexTable(PP_INSTANCE *ppi){ - ogg_uint32_t i; - ogg_uint32_t * PixelIndexTablePtr = ppi->ScanPixelIndexTable; - - /* If appropriate add on extra inices for U and V planes. */ - for ( i = 0; i < (ppi->ScanYPlaneFragments); i++ ) { - PixelIndexTablePtr[ i ] = - ((i / ppi->ScanHFragments) * - VFRAGPIXELS * ppi->ScanConfig.VideoFrameWidth); - PixelIndexTablePtr[ i ] += - ((i % ppi->ScanHFragments) * HFRAGPIXELS); - } - - PixelIndexTablePtr = &ppi->ScanPixelIndexTable[ppi->ScanYPlaneFragments]; - - for ( i = 0; i < (ppi->ScanUVPlaneFragments * 2); i++ ){ - PixelIndexTablePtr[ i ] = - ((i / (ppi->ScanHFragments >> 1) ) * - (VFRAGPIXELS * (ppi->ScanConfig.VideoFrameWidth >> 1)) ); - PixelIndexTablePtr[ i ] += - ((i % (ppi->ScanHFragments >> 1) ) * - HFRAGPIXELS) + ppi->YFramePixels; - } -} - -static void InitScanMapArrays(PP_INSTANCE *ppi){ - int i; - unsigned char StepThresh; - - /* Clear down the fragment level map arrays for the current frame. */ - memset( ppi->FragScores, 0, - ppi->ScanFrameFragments * sizeof(*ppi->FragScores) ); - memset( ppi->SameGreyDirPixels, 0, - ppi->ScanFrameFragments ); - memset( ppi->FragDiffPixels, 0, - ppi->ScanFrameFragments ); - memset( ppi->RowChangedPixels, 0, - 3* ppi->ScanConfig.VideoFrameHeight*sizeof(*ppi->RowChangedPixels)); - - memset( ppi->ScanDisplayFragments, BLOCK_NOT_CODED, ppi->ScanFrameFragments); - - /* Threshold used in setting up ppi->NoiseScoreBoostTable[] */ - StepThresh = (unsigned int)(ppi->SRFGreyThresh >> 1); - if ( StepThresh < MIN_STEP_THRESH ) - StepThresh = MIN_STEP_THRESH; - ppi->SrfThresh = (int)ppi->SRFGreyThresh; - - /* Set up various tables used to tweak pixel score values and - scoring rules based upon absolute value of a pixel change */ - for ( i = 0; i < 256; i++ ){ - /* Score multiplier table indexed by absolute difference. */ - ppi->AbsDiff_ScoreMultiplierTable[i] = (double)i * DiffDevisor; - if ( ppi->AbsDiff_ScoreMultiplierTable[i] < SCORE_MULT_LOW ) - ppi->AbsDiff_ScoreMultiplierTable[i] = SCORE_MULT_LOW; - else if ( ppi->AbsDiff_ScoreMultiplierTable[i] > SCORE_MULT_HIGH) - ppi->AbsDiff_ScoreMultiplierTable[i] = SCORE_MULT_HIGH; - - /* Table that facilitates a relaxation of the changed locals rules - in NoiseScoreRow() for pixels that have changed by a large - amount. */ - if ( i < (ppi->SrfThresh + StepThresh) ) - ppi->NoiseScoreBoostTable[i] = 0; - else if ( i < (ppi->SrfThresh + (StepThresh * 4)) ) - ppi->NoiseScoreBoostTable[i] = 1; - else if ( i < (ppi->SrfThresh + (StepThresh * 6)) ) - ppi->NoiseScoreBoostTable[i] = 2; - else - ppi->NoiseScoreBoostTable[i] = 3; - - } - - /* Set various other threshold parameters. */ - - /* Set variables that control access to the line search algorithms. */ - ppi->LineSearchTripTresh = 16; - if ( ppi->LineSearchTripTresh > ppi->PrimaryBlockThreshold ) - ppi->LineSearchTripTresh = (unsigned int)(ppi->PrimaryBlockThreshold + 1); - - /* Adjust line search length if block threshold low */ - ppi->MaxLineSearchLen = MAX_SEARCH_LINE_LEN; - while ( (ppi->MaxLineSearchLen > 0) && - (LineLengthScores[ppi->MaxLineSearchLen-1] > - ppi->PrimaryBlockThreshold) ) - ppi->MaxLineSearchLen -= 1; - -} - -void ScanYUVInit( PP_INSTANCE * ppi, SCAN_CONFIG_DATA * ScanConfigPtr){ - int i; - - /* Set up the various imported data structure pointers. */ - ppi->ScanConfig.Yuv0ptr = ScanConfigPtr->Yuv0ptr; - ppi->ScanConfig.Yuv1ptr = ScanConfigPtr->Yuv1ptr; - ppi->ScanConfig.SrfWorkSpcPtr = ScanConfigPtr->SrfWorkSpcPtr; - ppi->ScanConfig.disp_fragments = ScanConfigPtr->disp_fragments; - - ppi->ScanConfig.RegionIndex = ScanConfigPtr->RegionIndex; - - ppi->ScanConfig.VideoFrameWidth = ScanConfigPtr->VideoFrameWidth; - ppi->ScanConfig.VideoFrameHeight = ScanConfigPtr->VideoFrameHeight; - - /* UV plane sizes. */ - ppi->VideoUVPlaneWidth = ScanConfigPtr->VideoFrameWidth / 2; - ppi->VideoUVPlaneHeight = ScanConfigPtr->VideoFrameHeight / 2; - - /* Note the size of each plane in pixels. */ - ppi->YFramePixels = ppi->ScanConfig.VideoFrameWidth * - ppi->ScanConfig.VideoFrameHeight; - ppi->UVFramePixels = ppi->VideoUVPlaneWidth * ppi->VideoUVPlaneHeight; - - /* Work out various fragment related values. */ - ppi->ScanYPlaneFragments = ppi->YFramePixels / - (HFRAGPIXELS * VFRAGPIXELS); - ppi->ScanUVPlaneFragments = ppi->UVFramePixels / - (HFRAGPIXELS * VFRAGPIXELS);; - ppi->ScanHFragments = ppi->ScanConfig.VideoFrameWidth / HFRAGPIXELS; - ppi->ScanVFragments = ppi->ScanConfig.VideoFrameHeight / VFRAGPIXELS; - ppi->ScanFrameFragments = ppi->ScanYPlaneFragments + - (2 * ppi->ScanUVPlaneFragments); - - PInitFrameInfo(ppi); - - /* Set up the scan pixel index table. */ - ScanCalcPixelIndexTable(ppi); - - /* Initialise the previous frame block history lists */ - for ( i = 0; i < MAX_PREV_FRAMES; i++ ) - memset( ppi->PrevFragments[i], BLOCK_NOT_CODED, ppi->ScanFrameFragments); - - /* YUVAnalyseFrame() is not called for the first frame in a sequence - (a key frame obviously). This memset insures that for the second - frame all blocks are marked for coding in line with the behaviour - for other key frames. */ - memset( ppi->PrevFragments[ppi->PrevFrameLimit-1], - BLOCK_CODED, ppi->ScanFrameFragments ); - - /* Initialise scan arrays */ - InitScanMapArrays(ppi); -} - -static void SetFromPrevious(PP_INSTANCE *ppi) { - unsigned int i,j; - - /* We buld up the list of previously updated blocks in the zero - index list of PrevFragments[] so we must start by reseting its - contents */ - memset( ppi->PrevFragments[0], BLOCK_NOT_CODED, ppi->ScanFrameFragments ); - - if ( ppi->PrevFrameLimit > 1 ){ - /* Now build up PrevFragments[0] from PrevFragments[1 to PrevFrameLimit] */ - for ( i = 0; i < ppi->ScanFrameFragments; i++ ){ - for ( j = 1; j < ppi->PrevFrameLimit; j++ ){ - if ( ppi->PrevFragments[j][i] > BLOCK_CODED_BAR ){ - ppi->PrevFragments[0][i] = BLOCK_CODED; - break; - } - } - } - } -} - -static void UpdatePreviousBlockLists(PP_INSTANCE *ppi) { - int i; - - /* Shift previous frame block lists along. */ - for ( i = ppi->PrevFrameLimit; i > 1; i-- ){ - memcpy( ppi->PrevFragments[i], ppi->PrevFragments[i-1], - ppi->ScanFrameFragments ); - } - - /* Now copy in this frames block list */ - memcpy( ppi->PrevFragments[1], ppi->ScanDisplayFragments, - ppi->ScanFrameFragments ); -} - -static void CreateOutputDisplayMap( PP_INSTANCE *ppi, - signed char *InternalFragmentsPtr, - signed char *RecentHistoryPtr, - unsigned char *ExternalFragmentsPtr ) { - ogg_uint32_t i; - ogg_uint32_t HistoryBlocksAdded = 0; - ogg_uint32_t YBand = (ppi->ScanYPlaneFragments/8); /* 1/8th of Y image. */ - - ppi->OutputBlocksUpdated = 0; - for ( i = 0; i < ppi->ScanFrameFragments; i++ ) { - if ( InternalFragmentsPtr[i] > BLOCK_NOT_CODED ) { - ppi->OutputBlocksUpdated ++; - ExternalFragmentsPtr[i] = 1; - }else if ( RecentHistoryPtr[i] == BLOCK_CODED ){ - HistoryBlocksAdded ++; - ExternalFragmentsPtr[i] = 1; - }else{ - ExternalFragmentsPtr[i] = 0; - } - } - - /* Add in a weighting for the history blocks that have been added */ - ppi->OutputBlocksUpdated += (HistoryBlocksAdded / HISTORY_BLOCK_FACTOR); - - /* Now calculate a key frame candidate indicator. This is based - upon Y data only and ignores the top and bottom 1/8 of the - image. Also ignore history blocks and BAR blocks. */ - ppi->KFIndicator = 0; - for ( i = YBand; i < (ppi->ScanYPlaneFragments - YBand); i++ ) - if ( InternalFragmentsPtr[i] > BLOCK_CODED_BAR ) - ppi->KFIndicator ++; - - /* Convert the KF score to a range 0-100 */ - ppi->KFIndicator = ((ppi->KFIndicator*100)/((ppi->ScanYPlaneFragments*3)/4)); -} - -static int RowSadScan( PP_INSTANCE *ppi, - unsigned char * YuvPtr1, - unsigned char * YuvPtr2, - signed char * DispFragPtr){ - ogg_int32_t i, j; - ogg_uint32_t GrpSad; - ogg_uint32_t LocalGrpLowSadThresh = ppi->ModifiedGrpLowSadThresh; - ogg_uint32_t LocalGrpHighSadThresh = ppi->ModifiedGrpHighSadThresh; - signed char *LocalDispFragPtr; - unsigned char *LocalYuvPtr1; - unsigned char *LocalYuvPtr2; - - int InterestingBlocksInRow = 0; - - /* For each row of pixels in the row of blocks */ - for ( j = 0; j < VFRAGPIXELS; j++ ){ - /* Set local block map pointer. */ - LocalDispFragPtr = DispFragPtr; - - /* Set the local pixel data pointers for this row.*/ - LocalYuvPtr1 = YuvPtr1; - LocalYuvPtr2 = YuvPtr2; - - /* Scan along the row of pixels If the block to which a group of - pixels belongs is already marked for update then do nothing. */ - for ( i = 0; i < ppi->PlaneHFragments; i ++ ){ - if ( *LocalDispFragPtr <= BLOCK_NOT_CODED ){ - /* Calculate the SAD score for the block row */ - GrpSad = dsp_row_sad8(ppi->dsp, LocalYuvPtr1,LocalYuvPtr2); - - /* Now test the group SAD score */ - if ( GrpSad > LocalGrpLowSadThresh ){ - /* If SAD very high we must update else we have candidate block */ - if ( GrpSad > LocalGrpHighSadThresh ){ - /* Force update */ - *LocalDispFragPtr = BLOCK_CODED; - }else{ - /* Possible Update required */ - *LocalDispFragPtr = CANDIDATE_BLOCK; - } - InterestingBlocksInRow = 1; - } - } - LocalDispFragPtr++; - - LocalYuvPtr1 += 8; - LocalYuvPtr2 += 8; - } - - /* Increment the base data pointers to the start of the next line. */ - YuvPtr1 += ppi->PlaneStride; - YuvPtr2 += ppi->PlaneStride; - } - - return InterestingBlocksInRow; - -} - -static int ColSadScan( PP_INSTANCE *ppi, - unsigned char * YuvPtr1, - unsigned char * YuvPtr2, - signed char * DispFragPtr ){ - ogg_int32_t i; - ogg_uint32_t MaxSad; - ogg_uint32_t LocalGrpLowSadThresh = ppi->ModifiedGrpLowSadThresh; - ogg_uint32_t LocalGrpHighSadThresh = ppi->ModifiedGrpHighSadThresh; - signed char * LocalDispFragPtr; - - unsigned char * LocalYuvPtr1; - unsigned char * LocalYuvPtr2; - - int InterestingBlocksInRow = 0; - - /* Set the local pixel data pointers for this row. */ - LocalYuvPtr1 = YuvPtr1; - LocalYuvPtr2 = YuvPtr2; - - /* Set local block map pointer. */ - LocalDispFragPtr = DispFragPtr; - - /* Scan along the row of blocks */ - for ( i = 0; i < ppi->PlaneHFragments; i ++ ){ - /* Skip if block already marked to be coded. */ - if ( *LocalDispFragPtr <= BLOCK_NOT_CODED ){ - /* Calculate the SAD score for the block column */ - MaxSad = dsp_col_sad8x8(ppi->dsp, LocalYuvPtr1, LocalYuvPtr2, ppi->PlaneStride ); - - /* Now test the group SAD score */ - if ( MaxSad > LocalGrpLowSadThresh ){ - /* If SAD very high we must update else we have candidate block */ - if ( MaxSad > LocalGrpHighSadThresh ){ - /* Force update */ - *LocalDispFragPtr = BLOCK_CODED; - }else{ - /* Possible Update required */ - *LocalDispFragPtr = CANDIDATE_BLOCK; - } - InterestingBlocksInRow = 1; - } - } - - /* Increment the block map pointer. */ - LocalDispFragPtr++; - - /* Step data pointers on ready for next block */ - LocalYuvPtr1 += HFRAGPIXELS; - LocalYuvPtr2 += HFRAGPIXELS; - } - - return InterestingBlocksInRow; -} - -static void SadPass2( PP_INSTANCE *ppi, - ogg_int32_t RowNumber, - signed char * DispFragPtr ){ - ogg_int32_t i; - - /* First row */ - if ( RowNumber == 0 ) { - /* First block in row. */ - if ( DispFragPtr[0] == CANDIDATE_BLOCK ){ - if ( (DispFragPtr[1] == BLOCK_CODED) || - (DispFragPtr[ppi->PlaneHFragments] == BLOCK_CODED) || - (DispFragPtr[ppi->PlaneHFragments+1] == BLOCK_CODED) ){ - ppi->TmpCodedMap[0] = BLOCK_CODED_LOW; - }else{ - ppi->TmpCodedMap[0] = DispFragPtr[0]; - } - }else{ - ppi->TmpCodedMap[0] = DispFragPtr[0]; - } - - /* All but first and last in row */ - for ( i = 1; (i < ppi->PlaneHFragments-1); i++ ){ - if ( DispFragPtr[i] == CANDIDATE_BLOCK ){ - if ( (DispFragPtr[i-1] == BLOCK_CODED) || - (DispFragPtr[i+1] == BLOCK_CODED) || - (DispFragPtr[i+ppi->PlaneHFragments] == BLOCK_CODED) || - (DispFragPtr[i+ppi->PlaneHFragments-1] == BLOCK_CODED) || - (DispFragPtr[i+ppi->PlaneHFragments+1] == BLOCK_CODED) ){ - ppi->TmpCodedMap[i] = BLOCK_CODED_LOW; - }else{ - ppi->TmpCodedMap[i] = DispFragPtr[i]; - } - }else{ - ppi->TmpCodedMap[i] = DispFragPtr[i]; - } - } - - /* Last block in row. */ - i = ppi->PlaneHFragments-1; - if ( DispFragPtr[i] == CANDIDATE_BLOCK ){ - if ( (DispFragPtr[i-1] == BLOCK_CODED) || - (DispFragPtr[i+ppi->PlaneHFragments] == BLOCK_CODED) || - (DispFragPtr[i+ppi->PlaneHFragments-1] == BLOCK_CODED) ){ - ppi->TmpCodedMap[i] = BLOCK_CODED_LOW; - }else{ - ppi->TmpCodedMap[i] = DispFragPtr[i]; - } - }else{ - ppi->TmpCodedMap[i] = DispFragPtr[i]; - } - }else if ( RowNumber < (ppi->PlaneVFragments - 1) ){ - /* General case */ - /* First block in row. */ - if ( DispFragPtr[0] == CANDIDATE_BLOCK ){ - if ( (DispFragPtr[1] == BLOCK_CODED) || - (DispFragPtr[(-ppi->PlaneHFragments)] == BLOCK_CODED) || - (DispFragPtr[(-ppi->PlaneHFragments)+1] == BLOCK_CODED) || - (DispFragPtr[ppi->PlaneHFragments] == BLOCK_CODED) || - (DispFragPtr[ppi->PlaneHFragments+1] == BLOCK_CODED) ){ - ppi->TmpCodedMap[0] = BLOCK_CODED_LOW; - }else{ - ppi->TmpCodedMap[0] = DispFragPtr[0]; - } - }else{ - ppi->TmpCodedMap[0] = DispFragPtr[0]; - } - - /* All but first and last in row */ - for ( i = 1; (i < ppi->PlaneHFragments-1); i++ ){ - if ( DispFragPtr[i] == CANDIDATE_BLOCK ){ - if ( (DispFragPtr[i-1] == BLOCK_CODED) || - (DispFragPtr[i+1] == BLOCK_CODED) || - (DispFragPtr[i-ppi->PlaneHFragments] == BLOCK_CODED) || - (DispFragPtr[i-ppi->PlaneHFragments-1] == BLOCK_CODED) || - (DispFragPtr[i-ppi->PlaneHFragments+1] == BLOCK_CODED) || - (DispFragPtr[i+ppi->PlaneHFragments] == BLOCK_CODED) || - (DispFragPtr[i+ppi->PlaneHFragments-1] == BLOCK_CODED) || - (DispFragPtr[i+ppi->PlaneHFragments+1] == BLOCK_CODED) ){ - ppi->TmpCodedMap[i] = BLOCK_CODED_LOW; - }else{ - ppi->TmpCodedMap[i] = DispFragPtr[i]; - } - }else{ - ppi->TmpCodedMap[i] = DispFragPtr[i]; - } - } - - /* Last block in row. */ - i = ppi->PlaneHFragments-1; - if ( DispFragPtr[i] == CANDIDATE_BLOCK ){ - if ( (DispFragPtr[i-1] == BLOCK_CODED) || - (DispFragPtr[i-ppi->PlaneHFragments] == BLOCK_CODED) || - (DispFragPtr[i-ppi->PlaneHFragments-1] == BLOCK_CODED) || - (DispFragPtr[i+ppi->PlaneHFragments] == BLOCK_CODED) || - (DispFragPtr[i+ppi->PlaneHFragments-1] == BLOCK_CODED) ){ - ppi->TmpCodedMap[i] = BLOCK_CODED_LOW; - }else{ - ppi->TmpCodedMap[i] = DispFragPtr[i]; - } - }else{ - ppi->TmpCodedMap[i] = DispFragPtr[i]; - } - }else{ - /* Last row */ - /* First block in row. */ - if ( DispFragPtr[0] == CANDIDATE_BLOCK ){ - if ( (DispFragPtr[1] == BLOCK_CODED) || - (DispFragPtr[(-ppi->PlaneHFragments)] == BLOCK_CODED) || - (DispFragPtr[(-ppi->PlaneHFragments)+1] == BLOCK_CODED)){ - ppi->TmpCodedMap[0] = BLOCK_CODED_LOW; - }else{ - ppi->TmpCodedMap[0] = DispFragPtr[0]; - } - }else{ - ppi->TmpCodedMap[0] = DispFragPtr[0]; - } - - /* All but first and last in row */ - for ( i = 1; (i < ppi->PlaneHFragments-1); i++ ){ - if ( DispFragPtr[i] == CANDIDATE_BLOCK ){ - if ( (DispFragPtr[i-1] == BLOCK_CODED) || - (DispFragPtr[i+1] == BLOCK_CODED) || - (DispFragPtr[i-ppi->PlaneHFragments] == BLOCK_CODED) || - (DispFragPtr[i-ppi->PlaneHFragments-1] == BLOCK_CODED) || - (DispFragPtr[i-ppi->PlaneHFragments+1] == BLOCK_CODED) ){ - ppi->TmpCodedMap[i] = BLOCK_CODED_LOW; - }else{ - ppi->TmpCodedMap[i] = DispFragPtr[i]; - } - }else{ - ppi->TmpCodedMap[i] = DispFragPtr[i]; - } - } - - /* Last block in row. */ - i = ppi->PlaneHFragments-1; - if ( DispFragPtr[i] == CANDIDATE_BLOCK ){ - if ( (DispFragPtr[i-1] == BLOCK_CODED) || - (DispFragPtr[i-ppi->PlaneHFragments] == BLOCK_CODED) || - (DispFragPtr[i-ppi->PlaneHFragments-1] == BLOCK_CODED) ){ - ppi->TmpCodedMap[i] = BLOCK_CODED_LOW; - }else{ - ppi->TmpCodedMap[i] = DispFragPtr[i]; - } - }else{ - ppi->TmpCodedMap[i] = DispFragPtr[i]; - } - } - - /* Now copy back the modified Fragment data */ - memcpy( &DispFragPtr[0], &ppi->TmpCodedMap[0], (ppi->PlaneHFragments) ); -} - -static unsigned char ApplyPakLowPass( PP_INSTANCE *ppi, - unsigned char * SrcPtr ){ - unsigned char * SrcPtr1 = SrcPtr - 1; - unsigned char * SrcPtr0 = SrcPtr1 - ppi->PlaneStride; /* Note the - use of - stride not - width. */ - unsigned char * SrcPtr2 = SrcPtr1 + ppi->PlaneStride; - - return (unsigned char)( ( (ogg_uint32_t)SrcPtr0[0] + - (ogg_uint32_t)SrcPtr0[1] + - (ogg_uint32_t)SrcPtr0[2] + - (ogg_uint32_t)SrcPtr1[0] + - (ogg_uint32_t)SrcPtr1[2] + - (ogg_uint32_t)SrcPtr2[0] + - (ogg_uint32_t)SrcPtr2[1] + - (ogg_uint32_t)SrcPtr2[2] ) >> 3 ); - -} - -static void RowDiffScan( PP_INSTANCE *ppi, - unsigned char * YuvPtr1, - unsigned char * YuvPtr2, - ogg_int16_t * YUVDiffsPtr, - unsigned char * bits_map_ptr, - signed char * SgcPtr, - signed char * DispFragPtr, - unsigned char * FDiffPixels, - ogg_int32_t * RowDiffsPtr, - unsigned char * ChLocalsPtr, int EdgeRow ){ - - ogg_int32_t i,j; - ogg_int32_t FragChangedPixels; - - ogg_int16_t Diff; /* Temp local workspace. */ - - /* Cannot use kernel if at edge or if PAK disabled */ - if ( (!ppi->PAKEnabled) || EdgeRow ){ - for ( i = 0; i < ppi->PlaneWidth; i += HFRAGPIXELS ){ - /* Reset count of pixels changed for the current fragment. */ - FragChangedPixels = 0; - - /* Test for break out conditions to save time. */ - if (*DispFragPtr == CANDIDATE_BLOCK){ - - /* Clear down entries in changed locals array */ - SET8_0(ChLocalsPtr); - - for ( j = 0; j < HFRAGPIXELS; j++ ){ - /* Take a local copy of the measured difference. */ - Diff = (int)YuvPtr1[j] - (int)YuvPtr2[j]; - - /* Store the actual difference value */ - YUVDiffsPtr[j] = Diff; - - /* Test against the Level thresholds and record the results */ - SgcPtr[0] += ppi->SgcThreshTable[Diff+255]; - - /* Test against the SRF thresholds */ - bits_map_ptr[j] = ppi->SrfThreshTable[Diff+255]; - FragChangedPixels += ppi->SrfThreshTable[Diff+255]; - } - }else{ - /* If we are breaking out here mark all pixels as changed. */ - if ( *DispFragPtr > BLOCK_NOT_CODED ){ - SET8_1(bits_map_ptr); - SET8_8(ChLocalsPtr); - }else{ - SET8_0(ChLocalsPtr); - } - } - - *RowDiffsPtr += FragChangedPixels; - *FDiffPixels += (unsigned char)FragChangedPixels; - - YuvPtr1 += HFRAGPIXELS; - YuvPtr2 += HFRAGPIXELS; - bits_map_ptr += HFRAGPIXELS; - ChLocalsPtr += HFRAGPIXELS; - YUVDiffsPtr += HFRAGPIXELS; - SgcPtr ++; - FDiffPixels ++; - - /* If we have a lot of changed pixels for this fragment on this - row then the fragment is almost sure to be picked (e.g. through - the line search) so we can mark it as selected and then ignore - it. */ - if (FragChangedPixels >= 7){ - *DispFragPtr = BLOCK_CODED_LOW; - } - DispFragPtr++; - } - }else{ - - /*************************************************************/ - /* First fragment of row !! */ - - i = 0; - /* Reset count of pixels changed for the current fragment. */ - FragChangedPixels = 0; - - /* Test for break out conditions to save time. */ - if (*DispFragPtr == CANDIDATE_BLOCK){ - /* Clear down entries in changed locals array */ - SET8_0(ChLocalsPtr); - - for ( j = 0; j < HFRAGPIXELS; j++ ){ - /* Take a local copy of the measured difference. */ - Diff = (int)YuvPtr1[j] - (int)YuvPtr2[j]; - - /* Store the actual difference value */ - YUVDiffsPtr[j] = Diff; - - /* Test against the Level thresholds and record the results */ - SgcPtr[0] += ppi->SgcThreshTable[Diff+255]; - - if (j>0 && ppi->SrfPakThreshTable[Diff+255] ) - Diff = (int)ApplyPakLowPass( ppi, &YuvPtr1[j] ) - - (int)ApplyPakLowPass( ppi, &YuvPtr2[j] ); - - /* Test against the SRF thresholds */ - bits_map_ptr[j] = ppi->SrfThreshTable[Diff+255]; - FragChangedPixels += ppi->SrfThreshTable[Diff+255]; - } - }else{ - /* If we are breaking out here mark all pixels as changed. */ - if ( *DispFragPtr > BLOCK_NOT_CODED ){ - SET8_1(bits_map_ptr); - SET8_8(ChLocalsPtr); - }else{ - SET8_0(ChLocalsPtr); - } - } - - *RowDiffsPtr += FragChangedPixels; - *FDiffPixels += (unsigned char)FragChangedPixels; - - YuvPtr1 += HFRAGPIXELS; - YuvPtr2 += HFRAGPIXELS; - bits_map_ptr += HFRAGPIXELS; - ChLocalsPtr += HFRAGPIXELS; - YUVDiffsPtr += HFRAGPIXELS; - SgcPtr ++; - FDiffPixels ++; - - /* If we have a lot of changed pixels for this fragment on this - row then the fragment is almost sure to be picked - (e.g. through the line search) so we can mark it as selected - and then ignore it. */ - if (FragChangedPixels >= 7){ - *DispFragPtr = BLOCK_CODED_LOW; - } - DispFragPtr++; - /*************************************************************/ - /* Fragment in between!! */ - - for ( i = HFRAGPIXELS ; i < ppi->PlaneWidth-HFRAGPIXELS; - i += HFRAGPIXELS ){ - /* Reset count of pixels changed for the current fragment. */ - FragChangedPixels = 0; - - /* Test for break out conditions to save time. */ - if (*DispFragPtr == CANDIDATE_BLOCK){ - /* Clear down entries in changed locals array */ - SET8_0(ChLocalsPtr); - for ( j = 0; j < HFRAGPIXELS; j++ ){ - /* Take a local copy of the measured difference. */ - Diff = (int)YuvPtr1[j] - (int)YuvPtr2[j]; - - /* Store the actual difference value */ - YUVDiffsPtr[j] = Diff; - - /* Test against the Level thresholds and record the results */ - SgcPtr[0] += ppi->SgcThreshTable[Diff+255]; - - if (ppi->SrfPakThreshTable[Diff+255] ) - Diff = (int)ApplyPakLowPass( ppi, &YuvPtr1[j] ) - - (int)ApplyPakLowPass( ppi, &YuvPtr2[j] ); - - - /* Test against the SRF thresholds */ - bits_map_ptr[j] = ppi->SrfThreshTable[Diff+255]; - FragChangedPixels += ppi->SrfThreshTable[Diff+255]; - } - }else{ - /* If we are breaking out here mark all pixels as changed. */ - if ( *DispFragPtr > BLOCK_NOT_CODED ){ - SET8_1(bits_map_ptr); - SET8_8(ChLocalsPtr); - }else{ - SET8_0(ChLocalsPtr); - } - } - - *RowDiffsPtr += FragChangedPixels; - *FDiffPixels += (unsigned char)FragChangedPixels; - - YuvPtr1 += HFRAGPIXELS; - YuvPtr2 += HFRAGPIXELS; - bits_map_ptr += HFRAGPIXELS; - ChLocalsPtr += HFRAGPIXELS; - YUVDiffsPtr += HFRAGPIXELS; - SgcPtr ++; - FDiffPixels ++; - - /* If we have a lot of changed pixels for this fragment on this - row then the fragment is almost sure to be picked - (e.g. through the line search) so we can mark it as selected - and then ignore it. */ - if (FragChangedPixels >= 7){ - *DispFragPtr = BLOCK_CODED_LOW; - } - DispFragPtr++; - } - /*************************************************************/ - /* Last fragment of row !! */ - - /* Reset count of pixels changed for the current fragment. */ - FragChangedPixels = 0; - - /* Test for break out conditions to save time. */ - if (*DispFragPtr == CANDIDATE_BLOCK){ - /* Clear down entries in changed locals array */ - SET8_0(ChLocalsPtr); - - for ( j = 0; j < HFRAGPIXELS; j++ ){ - /* Take a local copy of the measured difference. */ - Diff = (int)YuvPtr1[j] - (int)YuvPtr2[j]; - - /* Store the actual difference value */ - YUVDiffsPtr[j] = Diff; - - /* Test against the Level thresholds and record the results */ - SgcPtr[0] += ppi->SgcThreshTable[Diff+255]; - - if (j<7 && ppi->SrfPakThreshTable[Diff+255] ) - Diff = (int)ApplyPakLowPass( ppi, &YuvPtr1[j] ) - - (int)ApplyPakLowPass( ppi, &YuvPtr2[j] ); - - - /* Test against the SRF thresholds */ - bits_map_ptr[j] = ppi->SrfThreshTable[Diff+255]; - FragChangedPixels += ppi->SrfThreshTable[Diff+255]; - } - }else{ - /* If we are breaking out here mark all pixels as changed.*/ - if ( *DispFragPtr > BLOCK_NOT_CODED ) { - SET8_1(bits_map_ptr); - SET8_8(ChLocalsPtr); - }else{ - SET8_0(ChLocalsPtr); - } - } - /* If we have a lot of changed pixels for this fragment on this - row then the fragment is almost sure to be picked (e.g. through - the line search) so we can mark it as selected and then ignore - it. */ - *RowDiffsPtr += FragChangedPixels; - *FDiffPixels += (unsigned char)FragChangedPixels; - - /* If we have a lot of changed pixels for this fragment on this - row then the fragment is almost sure to be picked (e.g. through - the line search) so we can mark it as selected and then ignore - it. */ - if (FragChangedPixels >= 7){ - *DispFragPtr = BLOCK_CODED_LOW; - } - DispFragPtr++; - - } -} - -static void ConsolidateDiffScanResults( PP_INSTANCE *ppi, - unsigned char * FDiffPixels, - signed char * SgcScoresPtr, - signed char * DispFragPtr ){ - ogg_int32_t i; - - for ( i = 0; i < ppi->PlaneHFragments; i ++ ){ - /* Consider only those blocks that were candidates in the - difference scan. Ignore definite YES and NO cases. */ - if ( DispFragPtr[i] == CANDIDATE_BLOCK ){ - if ( ((ogg_uint32_t)abs(SgcScoresPtr[i]) > ppi->BlockSgcThresh) ){ - /* Block marked for update due to Sgc change */ - DispFragPtr[i] = BLOCK_CODED_SGC; - }else if ( FDiffPixels[i] == 0 ){ - /* Block is no longer a candidate for the main tests but will - still be considered a candidate in RowBarEnhBlockMap() */ - DispFragPtr[i] = CANDIDATE_BLOCK_LOW; - } - } - } -} - -static void RowChangedLocalsScan( PP_INSTANCE *ppi, - unsigned char * PixelMapPtr, - unsigned char * ChLocalsPtr, - signed char * DispFragPtr, - unsigned char RowType ){ - - unsigned char changed_locals = 0; - unsigned char * PixelsChangedPtr0; - unsigned char * PixelsChangedPtr1; - unsigned char * PixelsChangedPtr2; - ogg_int32_t i, j; - ogg_int32_t LastRowIndex = ppi->PlaneWidth - 1; - - /* Set up the line based pointers into the bits changed map. */ - PixelsChangedPtr0 = PixelMapPtr - ppi->PlaneWidth; - if ( PixelsChangedPtr0 < ppi->PixelChangedMap ) - PixelsChangedPtr0 += ppi->PixelMapCircularBufferSize; - PixelsChangedPtr0 -= 1; - - PixelsChangedPtr1 = PixelMapPtr - 1; - - PixelsChangedPtr2 = PixelMapPtr + ppi->PlaneWidth; - if ( PixelsChangedPtr2 >= - (ppi->PixelChangedMap + ppi->PixelMapCircularBufferSize) ) - PixelsChangedPtr2 -= ppi->PixelMapCircularBufferSize; - PixelsChangedPtr2 -= 1; - - if ( RowType == NOT_EDGE_ROW ){ - /* Scan through the row of pixels and calculate changed locals. */ - for ( i = 0; i < ppi->PlaneWidth; i += HFRAGPIXELS ){ - /* Skip a group of 8 pixels if the assosciated fragment has no - pixels of interest. */ - if ( *DispFragPtr == CANDIDATE_BLOCK ){ - for ( j = 0; j < HFRAGPIXELS; j++ ){ - changed_locals = 0; - - /* If the pixel itself has changed */ - if ( PixelsChangedPtr1[1] ){ - if ( (i > 0) || (j > 0) ){ - changed_locals += PixelsChangedPtr0[0]; - changed_locals += PixelsChangedPtr1[0]; - changed_locals += PixelsChangedPtr2[0]; - } - - changed_locals += PixelsChangedPtr0[1]; - changed_locals += PixelsChangedPtr2[1]; - - if ( (i + j) < LastRowIndex ){ - changed_locals += PixelsChangedPtr0[2]; - changed_locals += PixelsChangedPtr1[2]; - changed_locals += PixelsChangedPtr2[2]; - } - - /* Store the number of changed locals */ - *ChLocalsPtr |= changed_locals; - } - - /* Increment to next pixel in the row */ - ChLocalsPtr++; - PixelsChangedPtr0++; - PixelsChangedPtr1++; - PixelsChangedPtr2++; - } - }else{ - if ( *DispFragPtr > BLOCK_NOT_CODED ) - SET8_0(ChLocalsPtr); - - /* Step pointers */ - ChLocalsPtr += HFRAGPIXELS; - PixelsChangedPtr0 += HFRAGPIXELS; - PixelsChangedPtr1 += HFRAGPIXELS; - PixelsChangedPtr2 += HFRAGPIXELS; - } - - /* Move on to next fragment. */ - DispFragPtr++; - - } - }else{ - /* Scan through the row of pixels and calculate changed locals. */ - for ( i = 0; i < ppi->PlaneWidth; i += HFRAGPIXELS ){ - /* Skip a group of 8 pixels if the assosciated fragment has no - pixels of interest */ - if ( *DispFragPtr == CANDIDATE_BLOCK ){ - for ( j = 0; j < HFRAGPIXELS; j++ ){ - changed_locals = 0; - - /* If the pixel itself has changed */ - if ( PixelsChangedPtr1[1] ){ - if ( RowType == FIRST_ROW ){ - if ( (i > 0) || (j > 0) ){ - changed_locals += PixelsChangedPtr1[0]; - changed_locals += PixelsChangedPtr2[0]; - } - - changed_locals += PixelsChangedPtr2[1]; - - if ( (i + j) < LastRowIndex ){ - changed_locals += PixelsChangedPtr1[2]; - changed_locals += PixelsChangedPtr2[2]; - } - }else{ - if ( (i > 0) || (j > 0 ) ){ - changed_locals += PixelsChangedPtr0[0]; - changed_locals += PixelsChangedPtr1[0]; - } - - changed_locals += PixelsChangedPtr0[1]; - - if ( (i + j) < LastRowIndex ){ - changed_locals += PixelsChangedPtr0[2]; - changed_locals += PixelsChangedPtr1[2]; - } - } - - /* Store the number of changed locals */ - *ChLocalsPtr |= changed_locals; - } - - /* Increment to next pixel in the row */ - ChLocalsPtr++; - PixelsChangedPtr0++; - PixelsChangedPtr1++; - PixelsChangedPtr2++; - } - }else{ - if ( *DispFragPtr > BLOCK_NOT_CODED ) - SET8_0(ChLocalsPtr); - - /* Step pointers */ - ChLocalsPtr += HFRAGPIXELS; - PixelsChangedPtr0 += HFRAGPIXELS; - PixelsChangedPtr1 += HFRAGPIXELS; - PixelsChangedPtr2 += HFRAGPIXELS; - } - - /* Move on to next fragment. */ - DispFragPtr++; - } - } -} - -static void NoiseScoreRow( PP_INSTANCE *ppi, - unsigned char * PixelMapPtr, - unsigned char * ChLocalsPtr, - ogg_int16_t * YUVDiffsPtr, - unsigned char * PixelNoiseScorePtr, - ogg_uint32_t * FragScorePtr, - signed char * DispFragPtr, - ogg_int32_t * RowDiffsPtr ){ - ogg_int32_t i,j; - unsigned char changed_locals = 0; - ogg_int32_t Score; - ogg_uint32_t FragScore; - ogg_int32_t AbsDiff; - - /* For each pixel in the row */ - for ( i = 0; i < ppi->PlaneWidth; i += HFRAGPIXELS ){ - /* Skip a group of 8 pixels if the assosciated fragment has no - pixels of interest. */ - if ( *DispFragPtr == CANDIDATE_BLOCK ){ - /* Reset the cumulative fragment score. */ - FragScore = 0; - - /* Pixels grouped along the row into fragments */ - for ( j = 0; j < HFRAGPIXELS; j++ ){ - if ( PixelMapPtr[j] ){ - AbsDiff = (ogg_int32_t)( abs(YUVDiffsPtr[j]) ); - changed_locals = ChLocalsPtr[j]; - - /* Give this pixel a score based on changed locals and level - of its own change. */ - Score = (1 + ((ogg_int32_t)(changed_locals + - ppi->NoiseScoreBoostTable[AbsDiff]) - - ppi->NoiseSupLevel)); - - /* For no zero scores adjust by a level based score multiplier. */ - if ( Score > 0 ){ - Score = ((double)Score * - ppi->AbsDiff_ScoreMultiplierTable[AbsDiff] ); - if ( Score < 1 ) - Score = 1; - }else{ - /* Set -ve values to 0 */ - Score = 0; - - /* If there are no changed locals then clear the pixel - changed flag and decrement the pixels changed in - fragment count to speed later stages. */ - if ( changed_locals == 0 ){ - PixelMapPtr[j] = 0; - *RowDiffsPtr -= 1; - } - } - - /* Update the pixel scores etc. */ - PixelNoiseScorePtr[j] = (unsigned char)Score; - FragScore += (ogg_uint32_t)Score; - } - } - - /* Add fragment score (with plane correction factor) into main - data structure */ - *FragScorePtr += (ogg_int32_t)(FragScore * - ppi->YUVPlaneCorrectionFactor); - - /* If score is greater than trip threshold then mark blcok for update. */ - if ( *FragScorePtr > ppi->BlockThreshold ){ - *DispFragPtr = BLOCK_CODED_LOW; - } - } - - /* Increment the various pointers */ - FragScorePtr++; - DispFragPtr++; - PixelNoiseScorePtr += HFRAGPIXELS; - PixelMapPtr += HFRAGPIXELS; - ChLocalsPtr += HFRAGPIXELS; - YUVDiffsPtr += HFRAGPIXELS; - } -} - -static void PrimaryEdgeScoreRow( PP_INSTANCE *ppi, - unsigned char * ChangedLocalsPtr, - ogg_int16_t * YUVDiffsPtr, - unsigned char * PixelNoiseScorePtr, - ogg_uint32_t * FragScorePtr, - signed char * DispFragPtr, - unsigned char RowType ){ - ogg_uint32_t BodyNeighbours; - ogg_uint32_t AbsDiff; - unsigned char changed_locals = 0; - ogg_int32_t Score; - ogg_uint32_t FragScore; - unsigned char * CHLocalsPtr0; - unsigned char * CHLocalsPtr1; - unsigned char * CHLocalsPtr2; - ogg_int32_t i,j; - ogg_int32_t LastRowIndex = ppi->PlaneWidth - 1; - - /* Set up pointers into the current previous and next row of the - changed locals data structure. */ - CHLocalsPtr0 = ChangedLocalsPtr - ppi->PlaneWidth; - if ( CHLocalsPtr0 < ppi->ChLocals ) - CHLocalsPtr0 += ppi->ChLocalsCircularBufferSize; - CHLocalsPtr0 -= 1; - - CHLocalsPtr1 = ChangedLocalsPtr - 1; - - CHLocalsPtr2 = ChangedLocalsPtr + ppi->PlaneWidth; - if ( CHLocalsPtr2 >= (ppi->ChLocals + ppi->ChLocalsCircularBufferSize) ) - CHLocalsPtr2 -= ppi->ChLocalsCircularBufferSize; - CHLocalsPtr2 -= 1; - - - /* The defining rule used here is as follows. */ - /* An edge pixels has 3-5 changed locals. */ - /* And one or more of these changed locals has itself got 7-8 - changed locals. */ - - if ( RowType == NOT_EDGE_ROW ){ - /* Loop for all pixels in the row. */ - for ( i = 0; i < ppi->PlaneWidth; i += HFRAGPIXELS ){ - /* Does the fragment contain anything interesting to work with. */ - if ( *DispFragPtr == CANDIDATE_BLOCK ){ - /* Reset the cumulative fragment score. */ - FragScore = 0; - - /* Pixels grouped along the row into fragments */ - for ( j = 0; j < HFRAGPIXELS; j++ ){ - /* How many changed locals has the current pixel got. */ - changed_locals = ChangedLocalsPtr[j]; - - /* Is the pixel a suitable candidate */ - if ( (changed_locals > 2) && (changed_locals < 6) ){ - /* The pixel may qualify... have a closer look. */ - BodyNeighbours = 0; - - /* Count the number of "BodyNeighbours" .. Pixels that - have 7 or more changed neighbours. */ - if ( (i > 0) || (j > 0 ) ){ - if ( CHLocalsPtr0[0] >= 7 ) - BodyNeighbours++; - if ( CHLocalsPtr1[0] >= 7 ) - BodyNeighbours++; - if ( CHLocalsPtr2[0] >= 7 ) - BodyNeighbours++; - } - - if ( CHLocalsPtr0[1] >= 7 ) - BodyNeighbours++; - if ( CHLocalsPtr2[1] >= 7 ) - BodyNeighbours++; - - if ( (i + j) < LastRowIndex ){ - if ( CHLocalsPtr0[2] >= 7 ) - BodyNeighbours++; - if ( CHLocalsPtr1[2] >= 7 ) - BodyNeighbours++; - if ( CHLocalsPtr2[2] >= 7 ) - BodyNeighbours++; - } - - if ( BodyNeighbours > 0 ){ - AbsDiff = abs( YUVDiffsPtr[j] ); - Score = (ogg_int32_t) - ( (double)(BodyNeighbours * - BodyNeighbourScore) * - ppi->AbsDiff_ScoreMultiplierTable[AbsDiff] ); - if ( Score < 1 ) - Score = 1; - - /* Increment the score by a value determined by the - number of body neighbours. */ - PixelNoiseScorePtr[j] += (unsigned char)Score; - FragScore += (ogg_uint32_t)Score; - } - } - - /* Increment pointers into changed locals buffer */ - CHLocalsPtr0 ++; - CHLocalsPtr1 ++; - CHLocalsPtr2 ++; - } - - /* Add fragment score (with plane correction factor) into main - data structure */ - *FragScorePtr += (ogg_int32_t)(FragScore * - ppi->YUVPlaneCorrectionFactor); - - /* If score is greater than trip threshold then mark blcok for - update. */ - if ( *FragScorePtr > ppi->BlockThreshold ){ - *DispFragPtr = BLOCK_CODED_LOW; - } - - }else{ - /* Nothing to do for this fragment group */ - /* Advance pointers into changed locals buffer */ - CHLocalsPtr0 += HFRAGPIXELS; - CHLocalsPtr1 += HFRAGPIXELS; - CHLocalsPtr2 += HFRAGPIXELS; - } - - /* Increment the various pointers */ - FragScorePtr++; - DispFragPtr++; - PixelNoiseScorePtr += HFRAGPIXELS; - ChangedLocalsPtr += HFRAGPIXELS; - YUVDiffsPtr += HFRAGPIXELS; - } - }else{ - /* This is either the top or bottom row of pixels in a plane. */ - /* Loop for all pixels in the row. */ - for ( i = 0; i < ppi->PlaneWidth; i += HFRAGPIXELS ){ - /* Does the fragment contain anything interesting to work with. */ - if ( *DispFragPtr == CANDIDATE_BLOCK ){ - /* Reset the cumulative fragment score. */ - FragScore = 0; - - /* Pixels grouped along the row into fragments */ - for ( j = 0; j < HFRAGPIXELS; j++ ){ - /* How many changed locals has the current pixel got. */ - changed_locals = ChangedLocalsPtr[j]; - - /* Is the pixel a suitable candidate */ - if ( (changed_locals > 2) && (changed_locals < 6) ){ - /* The pixel may qualify... have a closer look. */ - BodyNeighbours = 0; - - /* Count the number of "BodyNeighbours" .. Pixels - that have 7 or more changed neighbours. */ - if ( RowType == LAST_ROW ){ - /* Test for cases where it could be the first pixel on - the line */ - if ( (i > 0) || (j > 0) ){ - if ( CHLocalsPtr0[0] >= 7 ) - BodyNeighbours++; - if ( CHLocalsPtr1[0] >= 7 ) - BodyNeighbours++; - } - - if ( CHLocalsPtr0[1] >= 7 ) - BodyNeighbours++; - - /* Test for the end of line case */ - if ( (i + j) < LastRowIndex ){ - if ( CHLocalsPtr0[2] >= 7 ) - BodyNeighbours++; - - if ( CHLocalsPtr1[2] >= 7 ) - BodyNeighbours++; - } - }else{ - /* First Row */ - /* Test for cases where it could be the first pixel on - the line */ - if ( (i > 0) || (j > 0) ){ - if ( CHLocalsPtr1[0] >= 7 ) - BodyNeighbours++; - if ( CHLocalsPtr2[0] >= 7 ) - BodyNeighbours++; - } - - /* Test for the end of line case */ - if ( CHLocalsPtr2[1] >= 7 ) - BodyNeighbours++; - - if ( (i + j) < LastRowIndex ){ - if ( CHLocalsPtr1[2] >= 7 ) - BodyNeighbours++; - if ( CHLocalsPtr2[2] >= 7 ) - BodyNeighbours++; - } - } - - /* Allocate a score according to the number of Body neighbours. */ - if ( BodyNeighbours > 0 ){ - AbsDiff = abs( YUVDiffsPtr[j] ); - Score = (ogg_int32_t) - ( (double)(BodyNeighbours * BodyNeighbourScore) * - ppi->AbsDiff_ScoreMultiplierTable[AbsDiff] ); - if ( Score < 1 ) - Score = 1; - - PixelNoiseScorePtr[j] += (unsigned char)Score; - FragScore += (ogg_uint32_t)Score; - } - } - - /* Increment pointers into changed locals buffer */ - CHLocalsPtr0 ++; - CHLocalsPtr1 ++; - CHLocalsPtr2 ++; - } - - /* Add fragment score (with plane correction factor) into main - data structure */ - *FragScorePtr += - (ogg_int32_t)(FragScore * ppi->YUVPlaneCorrectionFactor); - - /* If score is greater than trip threshold then mark blcok for - update. */ - if ( *FragScorePtr > ppi->BlockThreshold ){ - *DispFragPtr = BLOCK_CODED_LOW; - } - - }else{ - /* Nothing to do for this fragment group */ - /* Advance pointers into changed locals buffer */ - CHLocalsPtr0 += HFRAGPIXELS; - CHLocalsPtr1 += HFRAGPIXELS; - CHLocalsPtr2 += HFRAGPIXELS; - } - - /* Increment the various pointers */ - FragScorePtr++; - DispFragPtr++; - PixelNoiseScorePtr += HFRAGPIXELS; - ChangedLocalsPtr += HFRAGPIXELS; - YUVDiffsPtr += HFRAGPIXELS; - } - } -} - -static void PixelLineSearch( PP_INSTANCE *ppi, - unsigned char * ChangedLocalsPtr, - ogg_int32_t RowNumber, - ogg_int32_t ColNumber, - unsigned char direction, - ogg_uint32_t * line_length ){ - /* Exit if the pixel does not qualify or we have fallen off the edge - of either the image plane or the row. */ - if ( (RowNumber < 0) || - (RowNumber >= ppi->PlaneHeight) || - (ColNumber < 0) || - (ColNumber >= ppi->PlaneWidth) || - ((*ChangedLocalsPtr) <= 1) || - ((*ChangedLocalsPtr) >= 6) ){ - /* If not then it isn't part of any line. */ - return; - } - - if (*line_length < ppi->MaxLineSearchLen){ - ogg_uint32_t TmpLineLength; - ogg_uint32_t BestLineLength; - unsigned char * search_ptr; - - /* Increment the line length to include this pixel. */ - *line_length += 1; - BestLineLength = *line_length; - - /* Continue search */ - /* up */ - if ( direction == UP ){ - TmpLineLength = *line_length; - - search_ptr = ChangedLocalsPtr - ppi->PlaneWidth; - if ( search_ptr < ppi->ChLocals ) - search_ptr += ppi->ChLocalsCircularBufferSize; - - PixelLineSearch( ppi, search_ptr, RowNumber - 1, ColNumber, - direction, &TmpLineLength ); - - if ( TmpLineLength > BestLineLength ) - BestLineLength = TmpLineLength; - } - - /* up and left */ - if ( (BestLineLength < ppi->MaxLineSearchLen) && - ((direction == UP) || (direction == LEFT)) ){ - TmpLineLength = *line_length; - - search_ptr = ChangedLocalsPtr - ppi->PlaneWidth; - if ( search_ptr < ppi->ChLocals ) - search_ptr += ppi->ChLocalsCircularBufferSize; - search_ptr -= 1; - - PixelLineSearch( ppi, search_ptr, RowNumber - 1, ColNumber - 1, - direction, &TmpLineLength ); - - if ( TmpLineLength > BestLineLength ) - BestLineLength = TmpLineLength; - } - - /* up and right */ - if ( (BestLineLength < ppi->MaxLineSearchLen) && - ((direction == UP) || (direction == RIGHT)) ){ - TmpLineLength = *line_length; - - search_ptr = ChangedLocalsPtr - ppi->PlaneWidth; - if ( search_ptr < ppi->ChLocals ) - search_ptr += ppi->ChLocalsCircularBufferSize; - search_ptr += 1; - - PixelLineSearch( ppi, search_ptr, RowNumber - 1, ColNumber + 1, - direction, &TmpLineLength ); - - if ( TmpLineLength > BestLineLength ) - BestLineLength = TmpLineLength; - } - - /* left */ - if ( (BestLineLength < ppi->MaxLineSearchLen) && ( direction == LEFT ) ){ - TmpLineLength = *line_length; - PixelLineSearch( ppi, ChangedLocalsPtr - 1, RowNumber, ColNumber - 1, - direction, &TmpLineLength ); - - if ( TmpLineLength > BestLineLength ) - BestLineLength = TmpLineLength; - } - - /* right */ - if ( (BestLineLength < ppi->MaxLineSearchLen) && ( direction == RIGHT ) ){ - TmpLineLength = *line_length; - PixelLineSearch( ppi, ChangedLocalsPtr + 1, RowNumber, ColNumber + 1, - direction, &TmpLineLength ); - - if ( TmpLineLength > BestLineLength ) - BestLineLength = TmpLineLength; - } - - /* Down */ - if ( BestLineLength < ppi->MaxLineSearchLen ){ - TmpLineLength = *line_length; - if ( direction == DOWN ){ - search_ptr = ChangedLocalsPtr + ppi->PlaneWidth; - if ( search_ptr >= (ppi->ChLocals + ppi->ChLocalsCircularBufferSize) ) - search_ptr -= ppi->ChLocalsCircularBufferSize; - - PixelLineSearch( ppi, search_ptr, RowNumber + 1, ColNumber, direction, - &TmpLineLength ); - - if ( TmpLineLength > BestLineLength ) - BestLineLength = TmpLineLength; - } - - - /* down and left */ - if ( (BestLineLength < ppi->MaxLineSearchLen) && - ((direction == DOWN) || (direction == LEFT)) ){ - TmpLineLength = *line_length; - - search_ptr = ChangedLocalsPtr + ppi->PlaneWidth; - if ( search_ptr >= (ppi->ChLocals + ppi->ChLocalsCircularBufferSize) ) - search_ptr -= ppi->ChLocalsCircularBufferSize; - search_ptr -= 1; - - PixelLineSearch( ppi, search_ptr, RowNumber + 1, ColNumber - 1, - direction, &TmpLineLength ); - - if ( TmpLineLength > BestLineLength ) - BestLineLength = TmpLineLength; - } - - /* down and right */ - if ( (BestLineLength < ppi->MaxLineSearchLen) && - ((direction == DOWN) || (direction == RIGHT)) ){ - TmpLineLength = *line_length; - - search_ptr = ChangedLocalsPtr + ppi->PlaneWidth; - if ( search_ptr >= (ppi->ChLocals + ppi->ChLocalsCircularBufferSize) ) - search_ptr -= ppi->ChLocalsCircularBufferSize; - search_ptr += 1; - - PixelLineSearch( ppi, search_ptr, RowNumber + 1, ColNumber + 1, - direction, &TmpLineLength ); - - if ( TmpLineLength > BestLineLength ) - BestLineLength = TmpLineLength; - } - } - - /* Note the search value for this pixel. */ - *line_length = BestLineLength; - } -} - -static unsigned char LineSearchScorePixel( PP_INSTANCE *ppi, - unsigned char * ChangedLocalsPtr, - ogg_int32_t RowNumber, - ogg_int32_t ColNumber ){ - ogg_uint32_t line_length = 0; - ogg_uint32_t line_length2 = 0; - ogg_uint32_t line_length_score = 0; - ogg_uint32_t tmp_line_length = 0; - ogg_uint32_t tmp_line_length2 = 0; - - /* Look UP and Down */ - PixelLineSearch( ppi, ChangedLocalsPtr, RowNumber, - ColNumber, UP, &tmp_line_length ); - - if (tmp_line_length < ppi->MaxLineSearchLen) { - /* Look DOWN */ - PixelLineSearch( ppi, ChangedLocalsPtr, RowNumber, - ColNumber, DOWN, &tmp_line_length2 ); - line_length = tmp_line_length + tmp_line_length2 - 1; - - if ( line_length > ppi->MaxLineSearchLen ) - line_length = ppi->MaxLineSearchLen; - }else - line_length = tmp_line_length; - - /* If no max length line found then look left and right */ - if ( line_length < ppi->MaxLineSearchLen ){ - tmp_line_length = 0; - tmp_line_length2 = 0; - - PixelLineSearch( ppi, ChangedLocalsPtr, RowNumber, - ColNumber, LEFT, &tmp_line_length ); - if (tmp_line_length < ppi->MaxLineSearchLen){ - PixelLineSearch( ppi, ChangedLocalsPtr, RowNumber, - ColNumber, RIGHT, &tmp_line_length2 ); - line_length2 = tmp_line_length + tmp_line_length2 - 1; - - if ( line_length2 > ppi->MaxLineSearchLen ) - line_length2 = ppi->MaxLineSearchLen; - }else - line_length2 = tmp_line_length; - - } - - /* Take the largest line length */ - if ( line_length2 > line_length ) - line_length = line_length2; - - /* Create line length score */ - line_length_score = LineLengthScores[line_length]; - - return (unsigned char)line_length_score; -} - -static void LineSearchScoreRow( PP_INSTANCE *ppi, - unsigned char * ChangedLocalsPtr, - ogg_int16_t * YUVDiffsPtr, - unsigned char * PixelNoiseScorePtr, - ogg_uint32_t * FragScorePtr, - signed char * DispFragPtr, - ogg_int32_t RowNumber ){ - ogg_uint32_t AbsDiff; - unsigned char changed_locals = 0; - ogg_int32_t Score; - ogg_uint32_t FragScore; - ogg_int32_t i,j; - - /* The defining rule used here is as follows. */ - /* An edge pixels has 2-5 changed locals. */ - /* And one or more of these changed locals has itself got 7-8 - changed locals. */ - - /* Loop for all pixels in the row. */ - for ( i = 0; i < ppi->PlaneWidth; i += HFRAGPIXELS ){ - /* Does the fragment contain anything interesting to work with. */ - if ( *DispFragPtr == CANDIDATE_BLOCK ){ - /* Reset the cumulative fragment score. */ - FragScore = 0; - - /* Pixels grouped along the row into fragments */ - for ( j = 0; j < HFRAGPIXELS; j++ ){ - /* How many changed locals has the current pixel got. */ - changed_locals = ChangedLocalsPtr[j]; - - /* Is the pixel a suitable candidate for edge enhancement */ - if ( (changed_locals > 1) && (changed_locals < 6) && - (PixelNoiseScorePtr[j] < ppi->LineSearchTripTresh) ) { - Score = (ogg_int32_t) - LineSearchScorePixel( ppi, &ChangedLocalsPtr[j], RowNumber, i+j ); - - if ( Score ){ - AbsDiff = abs( YUVDiffsPtr[j] ); - Score = (ogg_int32_t) - ( (double)Score * ppi->AbsDiff_ScoreMultiplierTable[AbsDiff] ); - if ( Score < 1 ) - Score = 1; - - PixelNoiseScorePtr[j] += (unsigned char)Score; - FragScore += (ogg_uint32_t)Score; - } - } - } - - /* Add fragment score (with plane correction factor) into main - data structure */ - *FragScorePtr += - (ogg_int32_t)(FragScore * ppi->YUVPlaneCorrectionFactor); - - /* If score is greater than trip threshold then mark blcok for update. */ - if ( *FragScorePtr > ppi->BlockThreshold ){ - *DispFragPtr = BLOCK_CODED_LOW; - } - } - - /* Increment the various pointers */ - FragScorePtr++; - DispFragPtr++; - PixelNoiseScorePtr += HFRAGPIXELS; - ChangedLocalsPtr += HFRAGPIXELS; - YUVDiffsPtr += HFRAGPIXELS; - - } -} - -static void RowCopy( PP_INSTANCE *ppi, ogg_uint32_t BlockMapIndex ){ - - ogg_uint32_t i,j; - - ogg_uint32_t PixelIndex = ppi->ScanPixelIndexTable[BlockMapIndex]; - signed char * BlockMapPtr = &ppi->ScanDisplayFragments[BlockMapIndex]; - signed char * PrevFragmentsPtr = &ppi->PrevFragments[0][BlockMapIndex]; - - unsigned char * SourcePtr; - unsigned char * DestPtr; - - /* Copy pixels from changed blocks back to reference frame. */ - for ( i = 0; i < (ogg_uint32_t)ppi->PlaneHFragments; i ++ ){ - /* If the fragement is marked for update or was recently marked - for update (PrevFragmentsPtr[i]) */ - if ( (BlockMapPtr[i] > BLOCK_NOT_CODED) || - (PrevFragmentsPtr[i] == BLOCK_CODED) ){ - /* Set up the various pointers required. */ - SourcePtr = &ppi->ScanConfig.Yuv1ptr[PixelIndex]; - DestPtr = &ppi->ScanConfig.SrfWorkSpcPtr[PixelIndex]; - - /* For each row of the block */ - for ( j = 0; j < VFRAGPIXELS; j++ ){ - /* Copy the data unaltered from source to destination */ - memcpy(DestPtr,SourcePtr,8); - - /* Increment pointers for next line in the block */ - SourcePtr += ppi->PlaneWidth; - DestPtr += ppi->PlaneWidth; - } - } - - /* Increment pixel index for next block. */ - PixelIndex += HFRAGPIXELS; - } -} - -static void RowBarEnhBlockMap( PP_INSTANCE *ppi, - signed char * UpdatedBlockMapPtr, - signed char * BarBlockMapPtr, - ogg_uint32_t RowNumber ){ - int i; - - /* Start by blanking the row in the bar block map structure. */ - memset( BarBlockMapPtr, BLOCK_NOT_CODED, ppi->PlaneHFragments ); - - /* First row */ - if ( RowNumber == 0 ){ - - /* For each fragment in the row. */ - for ( i = 0; i < ppi->PlaneHFragments; i ++ ){ - /* Test for CANDIDATE_BLOCK or CANDIDATE_BLOCK_LOW. Uncoded or - coded blocks will be ignored. */ - if ( UpdatedBlockMapPtr[i] <= CANDIDATE_BLOCK ){ - /* Is one of the immediate neighbours updated in the main map. */ - /* Note special cases for blocks at the start and end of rows. */ - if ( i == 0 ){ - - if ((UpdatedBlockMapPtr[i+1] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i+ppi->PlaneHFragments]>BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i+ppi->PlaneHFragments+1]>BLOCK_NOT_CODED ) ) - BarBlockMapPtr[i] = BLOCK_CODED_BAR; - - - }else if ( i == (ppi->PlaneHFragments - 1) ){ - - if ((UpdatedBlockMapPtr[i-1] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i+ppi->PlaneHFragments-1]>BLOCK_NOT_CODED) || - (UpdatedBlockMapPtr[i+ppi->PlaneHFragments]>BLOCK_NOT_CODED) ) - BarBlockMapPtr[i] = BLOCK_CODED_BAR; - - }else{ - if((UpdatedBlockMapPtr[i-1] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i+1] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i+ppi->PlaneHFragments-1] > BLOCK_NOT_CODED)|| - (UpdatedBlockMapPtr[i+ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i+ppi->PlaneHFragments+1] > BLOCK_NOT_CODED) ) - BarBlockMapPtr[i] = BLOCK_CODED_BAR; - } - } - } - - } else if ( RowNumber == (ogg_uint32_t)(ppi->PlaneVFragments-1)) { - - /* Last row */ - /* Used to read PlaneHFragments */ - - /* For each fragment in the row. */ - for ( i = 0; i < ppi->PlaneHFragments; i ++ ){ - /* Test for CANDIDATE_BLOCK or CANDIDATE_BLOCK_LOW - Uncoded or coded blocks will be ignored. */ - if ( UpdatedBlockMapPtr[i] <= CANDIDATE_BLOCK ){ - /* Is one of the immediate neighbours updated in the main map. */ - /* Note special cases for blocks at the start and end of rows. */ - if ( i == 0 ){ - if((UpdatedBlockMapPtr[i+1] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i-ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i-ppi->PlaneHFragments+1] > BLOCK_NOT_CODED )) - BarBlockMapPtr[i] = BLOCK_CODED_BAR; - - }else if ( i == (ppi->PlaneHFragments - 1) ){ - if((UpdatedBlockMapPtr[i-1] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i-ppi->PlaneHFragments-1] > BLOCK_NOT_CODED)|| - (UpdatedBlockMapPtr[i-ppi->PlaneHFragments] > BLOCK_NOT_CODED ) ) - BarBlockMapPtr[i] = BLOCK_CODED_BAR; - }else{ - if((UpdatedBlockMapPtr[i-1] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i+1] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i-ppi->PlaneHFragments-1] > BLOCK_NOT_CODED)|| - (UpdatedBlockMapPtr[i-ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i-ppi->PlaneHFragments+1] > BLOCK_NOT_CODED) ) - BarBlockMapPtr[i] = BLOCK_CODED_BAR; - } - } - } - - }else{ - /* All other rows */ - /* For each fragment in the row. */ - for ( i = 0; i < ppi->PlaneHFragments; i ++ ){ - /* Test for CANDIDATE_BLOCK or CANDIDATE_BLOCK_LOW */ - /* Uncoded or coded blocks will be ignored. */ - if ( UpdatedBlockMapPtr[i] <= CANDIDATE_BLOCK ){ - /* Is one of the immediate neighbours updated in the main map. */ - /* Note special cases for blocks at the start and end of rows. */ - if ( i == 0 ){ - - if((UpdatedBlockMapPtr[i+1] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i-ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i-ppi->PlaneHFragments+1] > BLOCK_NOT_CODED)|| - (UpdatedBlockMapPtr[i+ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i+ppi->PlaneHFragments+1] > BLOCK_NOT_CODED) ) - BarBlockMapPtr[i] = BLOCK_CODED_BAR; - - }else if ( i == (ppi->PlaneHFragments - 1) ){ - - if((UpdatedBlockMapPtr[i-1] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i-ppi->PlaneHFragments-1] > BLOCK_NOT_CODED)|| - (UpdatedBlockMapPtr[i-ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i+ppi->PlaneHFragments-1] > BLOCK_NOT_CODED)|| - (UpdatedBlockMapPtr[i+ppi->PlaneHFragments] > BLOCK_NOT_CODED ) ) - BarBlockMapPtr[i] = BLOCK_CODED_BAR; - - }else{ - if((UpdatedBlockMapPtr[i-1] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i+1] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i-ppi->PlaneHFragments-1] > BLOCK_NOT_CODED)|| - (UpdatedBlockMapPtr[i-ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i-ppi->PlaneHFragments+1] > BLOCK_NOT_CODED)|| - (UpdatedBlockMapPtr[i+ppi->PlaneHFragments-1] > BLOCK_NOT_CODED)|| - (UpdatedBlockMapPtr[i+ppi->PlaneHFragments] > BLOCK_NOT_CODED ) || - (UpdatedBlockMapPtr[i+ppi->PlaneHFragments+1] > BLOCK_NOT_CODED )) - BarBlockMapPtr[i] = BLOCK_CODED_BAR; - } - } - } - } -} - -static void BarCopyBack( PP_INSTANCE *ppi, - signed char * UpdatedBlockMapPtr, - signed char * BarBlockMapPtr ){ - ogg_int32_t i; - - /* For each fragment in the row. */ - for ( i = 0; i < ppi->PlaneHFragments; i ++ ){ - if ( BarBlockMapPtr[i] > BLOCK_NOT_CODED ){ - UpdatedBlockMapPtr[i] = BarBlockMapPtr[i]; - } - } -} - -static void AnalysePlane( PP_INSTANCE *ppi, - unsigned char * PlanePtr0, - unsigned char * PlanePtr1, - ogg_uint32_t FragArrayOffset, - ogg_uint32_t PWidth, - ogg_uint32_t PHeight, - ogg_uint32_t PStride ) { - unsigned char * RawPlanePtr0; - unsigned char * RawPlanePtr1; - - ogg_int16_t * YUVDiffsPtr; - ogg_int16_t * YUVDiffsPtr1; - ogg_int16_t * YUVDiffsPtr2; - - ogg_uint32_t FragIndex; - ogg_uint32_t ScoreFragIndex1; - ogg_uint32_t ScoreFragIndex2; - ogg_uint32_t ScoreFragIndex3; - ogg_uint32_t ScoreFragIndex4; - - int UpdatedOrCandidateBlocks = 0; - - unsigned char * ChLocalsPtr0; - unsigned char * ChLocalsPtr1; - unsigned char * ChLocalsPtr2; - - unsigned char * PixelsChangedPtr0; - unsigned char * PixelsChangedPtr1; - - unsigned char * PixelScoresPtr1; - unsigned char * PixelScoresPtr2; - - signed char * DispFragPtr0; - signed char * DispFragPtr1; - signed char * DispFragPtr2; - - ogg_uint32_t * FragScoresPtr1; - ogg_uint32_t * FragScoresPtr2; - - ogg_int32_t * RowDiffsPtr; - ogg_int32_t * RowDiffsPtr1; - ogg_int32_t * RowDiffsPtr2; - - ogg_int32_t i,j; - - ogg_int32_t RowNumber1; - ogg_int32_t RowNumber2; - ogg_int32_t RowNumber3; - ogg_int32_t RowNumber4; - - int EdgeRow; - ogg_int32_t LineSearchRowNumber = 0; - - /* Variables used as temporary stores for frequently used values. */ - ogg_int32_t Row0Mod3; - ogg_int32_t Row1Mod3; - ogg_int32_t Row2Mod3; - ogg_int32_t BlockRowPixels; - - /* Set pixel difference threshold */ - if ( FragArrayOffset == 0 ){ - /* Luminance */ - ppi->LevelThresh = (int)ppi->SgcLevelThresh; - ppi->NegLevelThresh = -ppi->LevelThresh; - - ppi->SrfThresh = (int)ppi->SRFGreyThresh; - ppi->NegSrfThresh = -ppi->SrfThresh; - - /* Scores correction for Y pixels. */ - ppi->YUVPlaneCorrectionFactor = 1.0; - - ppi->BlockThreshold = ppi->PrimaryBlockThreshold; - ppi->BlockSgcThresh = ppi->SgcThresh; - }else{ - /* Chrominance */ - ppi->LevelThresh = (int)ppi->SuvcLevelThresh; - ppi->NegLevelThresh = -ppi->LevelThresh; - - ppi->SrfThresh = (int)ppi->SRFColThresh; - ppi->NegSrfThresh = -ppi->SrfThresh; - - /* Scores correction for UV pixels. */ - ppi->YUVPlaneCorrectionFactor = 1.5; - - /* Block threholds different for subsampled U and V blocks */ - ppi->BlockThreshold = - (ppi->PrimaryBlockThreshold / ppi->UVBlockThreshCorrection); - ppi->BlockSgcThresh = - (ppi->SgcThresh / ppi->UVSgcCorrection); - } - - /* Initialise the SRF thresh table and pointer. */ - memset( ppi->SrfThreshTable, 1, 512 ); - for ( i = ppi->NegSrfThresh; i <= ppi->SrfThresh; i++ ) - ppi->SrfThreshTable[i+255] = 0; - - /* Initialise the PAK thresh table. */ - for ( i = -255; i <= 255; i++ ) - if ( ppi->SrfThreshTable[i+255] && - (i <= ppi->HighChange) && - (i >= ppi->NegHighChange) ) - ppi->SrfPakThreshTable[i+255] = 1; - else - ppi->SrfPakThreshTable[i+255] = 0; - - /* Initialise the SGc lookup table */ - for ( i = -255; i <= 255; i++ ){ - if ( i <= ppi->NegLevelThresh ) - ppi->SgcThreshTable[i+255] = (unsigned char) -1; - else if ( i >= ppi->LevelThresh ) - ppi->SgcThreshTable[i+255] = 1; - else - ppi->SgcThreshTable[i+255] = 0; - } - - /* Set up plane dimension variables */ - ppi->PlaneHFragments = PWidth / HFRAGPIXELS; - ppi->PlaneVFragments = PHeight / VFRAGPIXELS; - ppi->PlaneWidth = PWidth; - ppi->PlaneHeight = PHeight; - ppi->PlaneStride = PStride; - - /* Set up local pointers into the raw image data. */ - RawPlanePtr0 = PlanePtr0; - RawPlanePtr1 = PlanePtr1; - - /* Note size and endo points for circular buffers. */ - ppi->YuvDiffsCircularBufferSize = YDIFF_CB_ROWS * ppi->PlaneWidth; - ppi->ChLocalsCircularBufferSize = CHLOCALS_CB_ROWS * ppi->PlaneWidth; - ppi->PixelMapCircularBufferSize = PMAP_CB_ROWS * ppi->PlaneWidth; - - /* Set high change thresh where PAK not needed */ - ppi->HighChange = ppi->SrfThresh * 4; - ppi->NegHighChange = -ppi->HighChange; - - /* Set up row difference pointers. */ - RowDiffsPtr = ppi->RowChangedPixels; - RowDiffsPtr1 = ppi->RowChangedPixels; - RowDiffsPtr2 = ppi->RowChangedPixels; - - BlockRowPixels = ppi->PlaneWidth * VFRAGPIXELS; - - for ( i = 0; i < (ppi->PlaneVFragments + 4); i++ ){ - RowNumber1 = (i - 1); - RowNumber2 = (i - 2); - RowNumber3 = (i - 3); - RowNumber4 = (i - 4); - - /* Pre calculate some frequently used values */ - Row0Mod3 = i % 3; - Row1Mod3 = RowNumber1 % 3; - Row2Mod3 = RowNumber2 % 3; - - /* For row diff scan last two iterations are invalid */ - if ( i < ppi->PlaneVFragments ){ - FragIndex = (i * ppi->PlaneHFragments) + FragArrayOffset; - YUVDiffsPtr = &ppi->yuv_differences[Row0Mod3 * BlockRowPixels]; - - PixelsChangedPtr0 = (&ppi->PixelChangedMap[Row0Mod3 * BlockRowPixels]); - DispFragPtr0 = &ppi->ScanDisplayFragments[FragIndex]; - - ChLocalsPtr0 = (&ppi->ChLocals[Row0Mod3 * BlockRowPixels]); - - } - - /* Set up the changed locals pointer to trail behind by one row of - fragments. */ - if ( i > 0 ){ - /* For last iteration the ch locals and noise scans are invalid */ - if ( RowNumber1 < ppi->PlaneVFragments ){ - ScoreFragIndex1 = (RowNumber1 * ppi->PlaneHFragments) + - FragArrayOffset; - - ChLocalsPtr1 = &ppi->ChLocals[Row1Mod3 * BlockRowPixels]; - PixelsChangedPtr1 = - &ppi->PixelChangedMap[(Row1Mod3) * BlockRowPixels]; - - PixelScoresPtr1 = &ppi->PixelScores[(RowNumber1 % 4) * BlockRowPixels]; - - YUVDiffsPtr1 = &ppi->yuv_differences[Row1Mod3 * BlockRowPixels]; - FragScoresPtr1 = &ppi->FragScores[ScoreFragIndex1]; - DispFragPtr1 = &ppi->ScanDisplayFragments[ScoreFragIndex1]; - - } - - if ( RowNumber2 >= 0 ){ - ScoreFragIndex2 = (RowNumber2 * ppi->PlaneHFragments) + - FragArrayOffset; - ChLocalsPtr2 = (&ppi->ChLocals[Row2Mod3 * BlockRowPixels]); - YUVDiffsPtr2 = &ppi->yuv_differences[Row2Mod3 * BlockRowPixels]; - - PixelScoresPtr2 = &ppi->PixelScores[(RowNumber2 % 4) * BlockRowPixels]; - - FragScoresPtr2 = &ppi->FragScores[ScoreFragIndex2]; - DispFragPtr2 = &ppi->ScanDisplayFragments[ScoreFragIndex2]; - }else{ - ChLocalsPtr2 = NULL; - } - }else{ - ChLocalsPtr1 = NULL; - ChLocalsPtr2 = NULL; - } - - /* Fast break out test for obvious yes and no cases in this row of - blocks */ - if ( i < ppi->PlaneVFragments ){ - dsp_save_fpu (ppi->dsp); - UpdatedOrCandidateBlocks = - RowSadScan( ppi, RawPlanePtr0, RawPlanePtr1, DispFragPtr0 ); - UpdatedOrCandidateBlocks |= - ColSadScan( ppi, RawPlanePtr0, RawPlanePtr1, DispFragPtr0 ); - dsp_restore_fpu (ppi->dsp); - }else{ - /* Make sure we still call other functions if RowSadScan() disabled */ - UpdatedOrCandidateBlocks = 1; - } - - /* Consolidation and fast break ot tests at Row 1 level */ - if ( (i > 0) && (RowNumber1 < ppi->PlaneVFragments) ){ - /* Mark as coded any candidate block that lies adjacent to a - coded block. */ - SadPass2( ppi, RowNumber1, DispFragPtr1 ); - - /* Check results of diff scan in last set of blocks. */ - /* Eliminate NO cases and add in +SGC cases */ - ConsolidateDiffScanResults( ppi, &ppi->FragDiffPixels[ScoreFragIndex1], - &ppi->SameGreyDirPixels[ScoreFragIndex1], - DispFragPtr1 - ); - } - - for ( j = 0; j < VFRAGPIXELS; j++ ){ - /* Last two iterations do not apply */ - if ( i < ppi->PlaneVFragments ){ - /* Is the current fragment at an edge. */ - EdgeRow = ( ( (i == 0) && (j == 0) ) || - ( (i == (ppi->PlaneVFragments - 1)) && - (j == (VFRAGPIXELS - 1)) ) ); - - /* Clear the arrays that will be used for the changed pixels maps */ - memset( PixelsChangedPtr0, 0, ppi->PlaneWidth ); - - /* Difference scan and map each row */ - if ( UpdatedOrCandidateBlocks ){ - /* Scan the row for interesting differences */ - /* Also clear the array that will be used for changed locals map */ - RowDiffScan( ppi, RawPlanePtr0, RawPlanePtr1, - YUVDiffsPtr, PixelsChangedPtr0, - &ppi->SameGreyDirPixels[FragIndex], - DispFragPtr0, &ppi->FragDiffPixels[FragIndex], - RowDiffsPtr, ChLocalsPtr0, EdgeRow); - }else{ - /* Clear the array that will be used for changed locals map */ - memset( ChLocalsPtr0, 0, ppi->PlaneWidth ); - } - - /* The actual image plane pointers must be incremented by - stride as this may be different (more) than the plane - width. Our own internal buffers use ppi->PlaneWidth. */ - RawPlanePtr0 += ppi->PlaneStride; - RawPlanePtr1 += ppi->PlaneStride; - PixelsChangedPtr0 += ppi->PlaneWidth; - ChLocalsPtr0 += ppi->PlaneWidth; - YUVDiffsPtr += ppi->PlaneWidth; - RowDiffsPtr++; - } - - /* Run behind calculating the changed locals data and noise scores. */ - if ( ChLocalsPtr1 != NULL ){ - /* Last few iterations do not apply */ - if ( RowNumber1 < ppi->PlaneVFragments ){ - /* Blank the next row in the pixel scores data structure. */ - memset( PixelScoresPtr1, 0, ppi->PlaneWidth ); - - /* Don't bother doing anything if there are no changed - pixels in this row */ - if ( *RowDiffsPtr1 ){ - /* Last valid row is a special case */ - if ( i < ppi->PlaneVFragments ) - RowChangedLocalsScan( ppi, PixelsChangedPtr1, ChLocalsPtr1, - DispFragPtr1, - ( (((i-1)==0) && (j==0)) ? - FIRST_ROW : NOT_EDGE_ROW) ); - else - RowChangedLocalsScan( ppi, PixelsChangedPtr1, ChLocalsPtr1, - DispFragPtr1, - ((j==(VFRAGPIXELS-1)) ? - LAST_ROW : NOT_EDGE_ROW) ); - - NoiseScoreRow( ppi, PixelsChangedPtr1, ChLocalsPtr1, YUVDiffsPtr1, - PixelScoresPtr1, FragScoresPtr1, DispFragPtr1, - RowDiffsPtr1 ); - } - - ChLocalsPtr1 += ppi->PlaneWidth; - PixelsChangedPtr1 += ppi->PlaneWidth; - YUVDiffsPtr1 += ppi->PlaneWidth; - PixelScoresPtr1 += ppi->PlaneWidth; - RowDiffsPtr1 ++; - } - - /* Run edge enhancement algorithms */ - if ( RowNumber2 < ppi->PlaneVFragments ){ - if ( ChLocalsPtr2 != NULL ){ - /* Don't bother doing anything if there are no changed - pixels in this row */ - if ( *RowDiffsPtr2 ){ - if ( RowNumber1 < ppi->PlaneVFragments ){ - PrimaryEdgeScoreRow( ppi, ChLocalsPtr2, YUVDiffsPtr2, - PixelScoresPtr2, FragScoresPtr2, - DispFragPtr2, - ( (((i-2)==0) && (j==0)) ? - FIRST_ROW : NOT_EDGE_ROW) ); - }else{ - /* Edge enhancement */ - PrimaryEdgeScoreRow( ppi, ChLocalsPtr2, YUVDiffsPtr2, - PixelScoresPtr2, FragScoresPtr2, - DispFragPtr2, - ((j==(VFRAGPIXELS-1)) ? - LAST_ROW : NOT_EDGE_ROW) ); - } - - /* Recursive line search */ - LineSearchScoreRow( ppi, ChLocalsPtr2, YUVDiffsPtr2, - PixelScoresPtr2, FragScoresPtr2, - DispFragPtr2, - LineSearchRowNumber ); - } - - ChLocalsPtr2 += ppi->PlaneWidth; - YUVDiffsPtr2 += ppi->PlaneWidth; - PixelScoresPtr2 += ppi->PlaneWidth; - LineSearchRowNumber += 1; - RowDiffsPtr2 ++; - } - } - } - } - - /* BAR algorithm */ - if ( (RowNumber3 >= 0) && (RowNumber3 < ppi->PlaneVFragments) ){ - ScoreFragIndex3 = (RowNumber3 * ppi->PlaneHFragments) + FragArrayOffset; - RowBarEnhBlockMap(ppi, - &ppi->ScanDisplayFragments[ScoreFragIndex3], - &ppi->BarBlockMap[(RowNumber3 % 3) * - ppi->PlaneHFragments], - RowNumber3 ); - } - - /* BAR copy back and "ppi->SRF filtering" or "pixel copy back" */ - if ( (RowNumber4 >= 0) && (RowNumber4 < ppi->PlaneVFragments) ){ - /* BAR copy back stage must lag by one more row to avoid BAR blocks - being used in BAR descisions. */ - ScoreFragIndex4 = (RowNumber4 * ppi->PlaneHFragments) + FragArrayOffset; - - BarCopyBack(ppi, &ppi->ScanDisplayFragments[ScoreFragIndex4], - &ppi->BarBlockMap[(RowNumber4 % 3) * ppi->PlaneHFragments]); - - /* Copy over the data from any blocks marked for update into the - output buffer. */ - RowCopy(ppi, ScoreFragIndex4); - } - } -} - -ogg_uint32_t YUVAnalyseFrame( PP_INSTANCE *ppi, ogg_uint32_t * KFIndicator ){ - - /* Initialise the map arrays. */ - InitScanMapArrays(ppi); - - /* If the motion level in the previous frame was high then adjust - the high and low SAD thresholds to speed things up. */ - ppi->ModifiedGrpLowSadThresh = ppi->GrpLowSadThresh; - ppi->ModifiedGrpHighSadThresh = ppi->GrpHighSadThresh; - - - /* Set up the internal plane height and width variables. */ - ppi->VideoYPlaneWidth = ppi->ScanConfig.VideoFrameWidth; - ppi->VideoYPlaneHeight = ppi->ScanConfig.VideoFrameHeight; - ppi->VideoUVPlaneWidth = ppi->ScanConfig.VideoFrameWidth / 2; - ppi->VideoUVPlaneHeight = ppi->ScanConfig.VideoFrameHeight / 2; - - /* To start with the strides will be set from the widths */ - ppi->VideoYPlaneStride = ppi->VideoYPlaneWidth; - ppi->VideoUPlaneStride = ppi->VideoUVPlaneWidth; - ppi->VideoVPlaneStride = ppi->VideoUVPlaneWidth; - - /* Set up the plane pointers */ - ppi->YPlanePtr0 = ppi->ScanConfig.Yuv0ptr; - ppi->YPlanePtr1 = ppi->ScanConfig.Yuv1ptr; - ppi->UPlanePtr0 = (ppi->ScanConfig.Yuv0ptr + ppi->YFramePixels); - ppi->UPlanePtr1 = (ppi->ScanConfig.Yuv1ptr + ppi->YFramePixels); - ppi->VPlanePtr0 = (ppi->ScanConfig.Yuv0ptr + ppi->YFramePixels + - ppi->UVFramePixels); - ppi->VPlanePtr1 = (ppi->ScanConfig.Yuv1ptr + ppi->YFramePixels + - ppi->UVFramePixels); - - /* Check previous frame lists and if necessary mark extra blocks for - update. */ - SetFromPrevious(ppi); - - /* Ananlyse the U and V palnes. */ - AnalysePlane( ppi, ppi->UPlanePtr0, ppi->UPlanePtr1, - ppi->ScanYPlaneFragments, ppi->VideoUVPlaneWidth, - ppi->VideoUVPlaneHeight, ppi->VideoUPlaneStride ); - AnalysePlane( ppi, ppi->VPlanePtr0, ppi->VPlanePtr1, - (ppi->ScanYPlaneFragments + ppi->ScanUVPlaneFragments), - ppi->VideoUVPlaneWidth, ppi->VideoUVPlaneHeight, - ppi->VideoVPlaneStride ); - - /* Now analyse the Y plane. */ - AnalysePlane( ppi, ppi->YPlanePtr0, ppi->YPlanePtr1, 0, - ppi->VideoYPlaneWidth, ppi->VideoYPlaneHeight, - ppi->VideoYPlaneStride ); - - /* Update the list of previous frame block updates. */ - UpdatePreviousBlockLists(ppi); - - /* Create an output block map for the calling process. */ - CreateOutputDisplayMap( ppi, ppi->ScanDisplayFragments, - ppi->PrevFragments[0], - ppi->ScanConfig.disp_fragments ); - - /* Set the candidate key frame indicator (0-100) */ - *KFIndicator = ppi->KFIndicator; - - /* Return the normalised block count (this is actually a motion - level weighting not a true block count). */ - return ppi->OutputBlocksUpdated; -} - diff --git a/Engine/lib/libtheora/lib/enc/toplevel_lookup.h b/Engine/lib/libtheora/lib/enc/toplevel_lookup.h deleted file mode 100644 index bf83a15b6..000000000 --- a/Engine/lib/libtheora/lib/enc/toplevel_lookup.h +++ /dev/null @@ -1,40 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: toplevel_lookup.h 13884 2007-09-22 08:38:10Z giles $ - - ********************************************************************/ - -#include "codec_internal.h" - -const ogg_uint32_t PriorKeyFrameWeight[KEY_FRAME_CONTEXT] = { 1,2,3,4,5 }; - -/* Data structures controlling addition of residue blocks */ -const ogg_uint32_t ResidueErrorThresh[Q_TABLE_SIZE] = { - 750, 700, 650, 600, 590, 580, 570, 560, - 550, 540, 530, 520, 510, 500, 490, 480, - 470, 460, 450, 440, 430, 420, 410, 400, - 390, 380, 370, 360, 350, 340, 330, 320, - 310, 300, 290, 280, 270, 260, 250, 245, - 240, 235, 230, 225, 220, 215, 210, 205, - 200, 195, 190, 185, 180, 175, 170, 165, - 160, 155, 150, 145, 140, 135, 130, 130 }; -const ogg_uint32_t ResidueBlockFactor[Q_TABLE_SIZE] = { - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 3, 3, 3, 3, 3, 3, 3, 3, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2, - 2, 2, 2, 2, 2, 2, 2, 2 }; diff --git a/Engine/lib/libtheora/lib/enc/x86_32/dct_decode_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32/dct_decode_mmx.c deleted file mode 100644 index 547e974e3..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_32/dct_decode_mmx.c +++ /dev/null @@ -1,409 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2008 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: dct_decode_mmx.c 15400 2008-10-15 12:10:58Z tterribe $ - - ********************************************************************/ - -#include - -#include "../codec_internal.h" - -#if defined(USE_ASM) - -static const __attribute__((aligned(8),used)) ogg_int64_t OC_V3= - 0x0003000300030003LL; -static const __attribute__((aligned(8),used)) ogg_int64_t OC_V4= - 0x0004000400040004LL; - -static void loop_filter_v(unsigned char *_pix,int _ystride, - const ogg_int16_t *_ll){ - long esi; - _pix-=_ystride*2; - __asm__ __volatile__( - /*mm0=0*/ - "pxor %%mm0,%%mm0\n\t" - /*esi=_ystride*3*/ - "lea (%[ystride],%[ystride],2),%[s]\n\t" - /*mm7=_pix[0...8]*/ - "movq (%[pix]),%%mm7\n\t" - /*mm4=_pix[0...8+_ystride*3]*/ - "movq (%[pix],%[s]),%%mm4\n\t" - /*mm6=_pix[0...8]*/ - "movq %%mm7,%%mm6\n\t" - /*Expand unsigned _pix[0...3] to 16 bits.*/ - "punpcklbw %%mm0,%%mm6\n\t" - "movq %%mm4,%%mm5\n\t" - /*Expand unsigned _pix[4...8] to 16 bits.*/ - "punpckhbw %%mm0,%%mm7\n\t" - /*Expand other arrays too.*/ - "punpcklbw %%mm0,%%mm4\n\t" - "punpckhbw %%mm0,%%mm5\n\t" - /*mm7:mm6=_p[0...8]-_p[0...8+_ystride*3]:*/ - "psubw %%mm4,%%mm6\n\t" - "psubw %%mm5,%%mm7\n\t" - /*mm5=mm4=_pix[0...8+_ystride]*/ - "movq (%[pix],%[ystride]),%%mm4\n\t" - /*mm1=mm3=mm2=_pix[0..8]+_ystride*2]*/ - "movq (%[pix],%[ystride],2),%%mm2\n\t" - "movq %%mm4,%%mm5\n\t" - "movq %%mm2,%%mm3\n\t" - "movq %%mm2,%%mm1\n\t" - /*Expand these arrays.*/ - "punpckhbw %%mm0,%%mm5\n\t" - "punpcklbw %%mm0,%%mm4\n\t" - "punpckhbw %%mm0,%%mm3\n\t" - "punpcklbw %%mm0,%%mm2\n\t" - /*Preload...*/ - "movq %[OC_V3],%%mm0\n\t" - /*mm3:mm2=_pix[0...8+_ystride*2]-_pix[0...8+_ystride]*/ - "psubw %%mm5,%%mm3\n\t" - "psubw %%mm4,%%mm2\n\t" - /*Scale by 3.*/ - "pmullw %%mm0,%%mm3\n\t" - "pmullw %%mm0,%%mm2\n\t" - /*Preload...*/ - "movq %[OC_V4],%%mm0\n\t" - /*f=mm3:mm2==_pix[0...8]-_pix[0...8+_ystride*3]+ - 3*(_pix[0...8+_ystride*2]-_pix[0...8+_ystride])*/ - "paddw %%mm7,%%mm3\n\t" - "paddw %%mm6,%%mm2\n\t" - /*Add 4.*/ - "paddw %%mm0,%%mm3\n\t" - "paddw %%mm0,%%mm2\n\t" - /*"Divide" by 8.*/ - "psraw $3,%%mm3\n\t" - "psraw $3,%%mm2\n\t" - /*Now compute lflim of mm3:mm2 cf. Section 7.10 of the sepc.*/ - /*Free up mm5.*/ - "packuswb %%mm5,%%mm4\n\t" - /*mm0=L L L L*/ - "movq (%[ll]),%%mm0\n\t" - /*if(R_i<-2L||R_i>2L)R_i=0:*/ - "movq %%mm2,%%mm5\n\t" - "pxor %%mm6,%%mm6\n\t" - "movq %%mm0,%%mm7\n\t" - "psubw %%mm0,%%mm6\n\t" - "psllw $1,%%mm7\n\t" - "psllw $1,%%mm6\n\t" - /*mm2==R_3 R_2 R_1 R_0*/ - /*mm5==R_3 R_2 R_1 R_0*/ - /*mm6==-2L -2L -2L -2L*/ - /*mm7==2L 2L 2L 2L*/ - "pcmpgtw %%mm2,%%mm7\n\t" - "pcmpgtw %%mm6,%%mm5\n\t" - "pand %%mm7,%%mm2\n\t" - "movq %%mm0,%%mm7\n\t" - "pand %%mm5,%%mm2\n\t" - "psllw $1,%%mm7\n\t" - "movq %%mm3,%%mm5\n\t" - /*mm3==R_7 R_6 R_5 R_4*/ - /*mm5==R_7 R_6 R_5 R_4*/ - /*mm6==-2L -2L -2L -2L*/ - /*mm7==2L 2L 2L 2L*/ - "pcmpgtw %%mm3,%%mm7\n\t" - "pcmpgtw %%mm6,%%mm5\n\t" - "pand %%mm7,%%mm3\n\t" - "movq %%mm0,%%mm7\n\t" - "pand %%mm5,%%mm3\n\t" - /*if(R_i<-L)R_i'=R_i+2L; - if(R_i>L)R_i'=R_i-2L; - if(R_i<-L||R_i>L)R_i=-R_i':*/ - "psraw $1,%%mm6\n\t" - "movq %%mm2,%%mm5\n\t" - "psllw $1,%%mm7\n\t" - /*mm2==R_3 R_2 R_1 R_0*/ - /*mm5==R_3 R_2 R_1 R_0*/ - /*mm6==-L -L -L -L*/ - /*mm0==L L L L*/ - /*mm5=R_i>L?FF:00*/ - "pcmpgtw %%mm0,%%mm5\n\t" - /*mm6=-L>R_i?FF:00*/ - "pcmpgtw %%mm2,%%mm6\n\t" - /*mm7=R_i>L?2L:0*/ - "pand %%mm5,%%mm7\n\t" - /*mm2=R_i>L?R_i-2L:R_i*/ - "psubw %%mm7,%%mm2\n\t" - "movq %%mm0,%%mm7\n\t" - /*mm5=-L>R_i||R_i>L*/ - "por %%mm6,%%mm5\n\t" - "psllw $1,%%mm7\n\t" - /*mm7=-L>R_i?2L:0*/ - "pand %%mm6,%%mm7\n\t" - "pxor %%mm6,%%mm6\n\t" - /*mm2=-L>R_i?R_i+2L:R_i*/ - "paddw %%mm7,%%mm2\n\t" - "psubw %%mm0,%%mm6\n\t" - /*mm5=-L>R_i||R_i>L?-R_i':0*/ - "pand %%mm2,%%mm5\n\t" - "movq %%mm0,%%mm7\n\t" - /*mm2=-L>R_i||R_i>L?0:R_i*/ - "psubw %%mm5,%%mm2\n\t" - "psllw $1,%%mm7\n\t" - /*mm2=-L>R_i||R_i>L?-R_i':R_i*/ - "psubw %%mm5,%%mm2\n\t" - "movq %%mm3,%%mm5\n\t" - /*mm3==R_7 R_6 R_5 R_4*/ - /*mm5==R_7 R_6 R_5 R_4*/ - /*mm6==-L -L -L -L*/ - /*mm0==L L L L*/ - /*mm6=-L>R_i?FF:00*/ - "pcmpgtw %%mm3,%%mm6\n\t" - /*mm5=R_i>L?FF:00*/ - "pcmpgtw %%mm0,%%mm5\n\t" - /*mm7=R_i>L?2L:0*/ - "pand %%mm5,%%mm7\n\t" - /*mm2=R_i>L?R_i-2L:R_i*/ - "psubw %%mm7,%%mm3\n\t" - "psllw $1,%%mm0\n\t" - /*mm5=-L>R_i||R_i>L*/ - "por %%mm6,%%mm5\n\t" - /*mm0=-L>R_i?2L:0*/ - "pand %%mm6,%%mm0\n\t" - /*mm3=-L>R_i?R_i+2L:R_i*/ - "paddw %%mm0,%%mm3\n\t" - /*mm5=-L>R_i||R_i>L?-R_i':0*/ - "pand %%mm3,%%mm5\n\t" - /*mm2=-L>R_i||R_i>L?0:R_i*/ - "psubw %%mm5,%%mm3\n\t" - /*mm2=-L>R_i||R_i>L?-R_i':R_i*/ - "psubw %%mm5,%%mm3\n\t" - /*Unfortunately, there's no unsigned byte+signed byte with unsigned - saturation op code, so we have to promote things back 16 bits.*/ - "pxor %%mm0,%%mm0\n\t" - "movq %%mm4,%%mm5\n\t" - "punpcklbw %%mm0,%%mm4\n\t" - "punpckhbw %%mm0,%%mm5\n\t" - "movq %%mm1,%%mm6\n\t" - "punpcklbw %%mm0,%%mm1\n\t" - "punpckhbw %%mm0,%%mm6\n\t" - /*_pix[0...8+_ystride]+=R_i*/ - "paddw %%mm2,%%mm4\n\t" - "paddw %%mm3,%%mm5\n\t" - /*_pix[0...8+_ystride*2]-=R_i*/ - "psubw %%mm2,%%mm1\n\t" - "psubw %%mm3,%%mm6\n\t" - "packuswb %%mm5,%%mm4\n\t" - "packuswb %%mm6,%%mm1\n\t" - /*Write it back out.*/ - "movq %%mm4,(%[pix],%[ystride])\n\t" - "movq %%mm1,(%[pix],%[ystride],2)\n\t" - :[s]"=&S"(esi) - :[pix]"r"(_pix),[ystride]"r"((long)_ystride),[ll]"r"(_ll), - [OC_V3]"m"(OC_V3),[OC_V4]"m"(OC_V4) - :"memory" - ); -} - -/*This code implements the bulk of loop_filter_h(). - Data are striped p0 p1 p2 p3 ... p0 p1 p2 p3 ..., so in order to load all - four p0's to one register we must transpose the values in four mmx regs. - When half is done we repeat this for the rest.*/ -static void loop_filter_h4(unsigned char *_pix,long _ystride, - const ogg_int16_t *_ll){ - long esi; - long edi; - __asm__ __volatile__( - /*x x x x 3 2 1 0*/ - "movd (%[pix]),%%mm0\n\t" - /*esi=_ystride*3*/ - "lea (%[ystride],%[ystride],2),%[s]\n\t" - /*x x x x 7 6 5 4*/ - "movd (%[pix],%[ystride]),%%mm1\n\t" - /*x x x x B A 9 8*/ - "movd (%[pix],%[ystride],2),%%mm2\n\t" - /*x x x x F E D C*/ - "movd (%[pix],%[s]),%%mm3\n\t" - /*mm0=7 3 6 2 5 1 4 0*/ - "punpcklbw %%mm1,%%mm0\n\t" - /*mm2=F B E A D 9 C 8*/ - "punpcklbw %%mm3,%%mm2\n\t" - /*mm1=7 3 6 2 5 1 4 0*/ - "movq %%mm0,%%mm1\n\t" - /*mm0=F B 7 3 E A 6 2*/ - "punpckhwd %%mm2,%%mm0\n\t" - /*mm1=D 9 5 1 C 8 4 0*/ - "punpcklwd %%mm2,%%mm1\n\t" - "pxor %%mm7,%%mm7\n\t" - /*mm5=D 9 5 1 C 8 4 0*/ - "movq %%mm1,%%mm5\n\t" - /*mm1=x C x 8 x 4 x 0==pix[0]*/ - "punpcklbw %%mm7,%%mm1\n\t" - /*mm5=x D x 9 x 5 x 1==pix[1]*/ - "punpckhbw %%mm7,%%mm5\n\t" - /*mm3=F B 7 3 E A 6 2*/ - "movq %%mm0,%%mm3\n\t" - /*mm0=x E x A x 6 x 2==pix[2]*/ - "punpcklbw %%mm7,%%mm0\n\t" - /*mm3=x F x B x 7 x 3==pix[3]*/ - "punpckhbw %%mm7,%%mm3\n\t" - /*mm1=mm1-mm3==pix[0]-pix[3]*/ - "psubw %%mm3,%%mm1\n\t" - /*Save a copy of pix[2] for later.*/ - "movq %%mm0,%%mm4\n\t" - /*mm0=mm0-mm5==pix[2]-pix[1]*/ - "psubw %%mm5,%%mm0\n\t" - /*Scale by 3.*/ - "pmullw %[OC_V3],%%mm0\n\t" - /*f=mm1==_pix[0]-_pix[3]+ 3*(_pix[2]-_pix[1])*/ - "paddw %%mm1,%%mm0\n\t" - /*Add 4.*/ - "paddw %[OC_V4],%%mm0\n\t" - /*"Divide" by 8, producing the residuals R_i.*/ - "psraw $3,%%mm0\n\t" - /*Now compute lflim of mm0 cf. Section 7.10 of the sepc.*/ - /*mm6=L L L L*/ - "movq (%[ll]),%%mm6\n\t" - /*if(R_i<-2L||R_i>2L)R_i=0:*/ - "movq %%mm0,%%mm1\n\t" - "pxor %%mm2,%%mm2\n\t" - "movq %%mm6,%%mm3\n\t" - "psubw %%mm6,%%mm2\n\t" - "psllw $1,%%mm3\n\t" - "psllw $1,%%mm2\n\t" - /*mm0==R_3 R_2 R_1 R_0*/ - /*mm1==R_3 R_2 R_1 R_0*/ - /*mm2==-2L -2L -2L -2L*/ - /*mm3==2L 2L 2L 2L*/ - "pcmpgtw %%mm0,%%mm3\n\t" - "pcmpgtw %%mm2,%%mm1\n\t" - "pand %%mm3,%%mm0\n\t" - "pand %%mm1,%%mm0\n\t" - /*if(R_i<-L)R_i'=R_i+2L; - if(R_i>L)R_i'=R_i-2L; - if(R_i<-L||R_i>L)R_i=-R_i':*/ - "psraw $1,%%mm2\n\t" - "movq %%mm0,%%mm1\n\t" - "movq %%mm6,%%mm3\n\t" - /*mm0==R_3 R_2 R_1 R_0*/ - /*mm1==R_3 R_2 R_1 R_0*/ - /*mm2==-L -L -L -L*/ - /*mm6==L L L L*/ - /*mm2=-L>R_i?FF:00*/ - "pcmpgtw %%mm0,%%mm2\n\t" - /*mm1=R_i>L?FF:00*/ - "pcmpgtw %%mm6,%%mm1\n\t" - /*mm3=2L 2L 2L 2L*/ - "psllw $1,%%mm3\n\t" - /*mm6=2L 2L 2L 2L*/ - "psllw $1,%%mm6\n\t" - /*mm3=R_i>L?2L:0*/ - "pand %%mm1,%%mm3\n\t" - /*mm6=-L>R_i?2L:0*/ - "pand %%mm2,%%mm6\n\t" - /*mm0=R_i>L?R_i-2L:R_i*/ - "psubw %%mm3,%%mm0\n\t" - /*mm1=-L>R_i||R_i>L*/ - "por %%mm2,%%mm1\n\t" - /*mm0=-L>R_i?R_i+2L:R_i*/ - "paddw %%mm6,%%mm0\n\t" - /*mm1=-L>R_i||R_i>L?R_i':0*/ - "pand %%mm0,%%mm1\n\t" - /*mm0=-L>R_i||R_i>L?0:R_i*/ - "psubw %%mm1,%%mm0\n\t" - /*mm0=-L>R_i||R_i>L?-R_i':R_i*/ - "psubw %%mm1,%%mm0\n\t" - /*_pix[1]+=R_i;*/ - "paddw %%mm0,%%mm5\n\t" - /*_pix[2]-=R_i;*/ - "psubw %%mm0,%%mm4\n\t" - /*mm5=x x x x D 9 5 1*/ - "packuswb %%mm7,%%mm5\n\t" - /*mm4=x x x x E A 6 2*/ - "packuswb %%mm7,%%mm4\n\t" - /*mm5=E D A 9 6 5 2 1*/ - "punpcklbw %%mm4,%%mm5\n\t" - /*edi=6 5 2 1*/ - "movd %%mm5,%%edi\n\t" - "movw %%di,1(%[pix])\n\t" - /*Why is there such a big stall here?*/ - "psrlq $32,%%mm5\n\t" - "shrl $16,%%edi\n\t" - "movw %%di,1(%[pix],%[ystride])\n\t" - /*edi=E D A 9*/ - "movd %%mm5,%%edi\n\t" - "movw %%di,1(%[pix],%[ystride],2)\n\t" - "shrl $16,%%edi\n\t" - "movw %%di,1(%[pix],%[s])\n\t" - :[s]"=&S"(esi),[d]"=&D"(edi), - [pix]"+r"(_pix),[ystride]"+r"(_ystride),[ll]"+r"(_ll) - :[OC_V3]"m"(OC_V3),[OC_V4]"m"(OC_V4) - :"memory" - ); -} - -static void loop_filter_h(unsigned char *_pix,int _ystride, - const ogg_int16_t *_ll){ - _pix-=2; - loop_filter_h4(_pix,_ystride,_ll); - loop_filter_h4(_pix+(_ystride<<2),_ystride,_ll); -} - -static void loop_filter_mmx(PB_INSTANCE *pbi, int FLimit){ - int j; - ogg_int16_t __attribute__((aligned(8))) ll[4]; - unsigned char *cp = pbi->display_fragments; - ogg_uint32_t *bp = pbi->recon_pixel_index_table; - - if ( FLimit == 0 ) return; - ll[0]=ll[1]=ll[2]=ll[3]=FLimit; - - for ( j = 0; j < 3 ; j++){ - ogg_uint32_t *bp_begin = bp; - ogg_uint32_t *bp_end; - int stride; - int h; - - switch(j) { - case 0: /* y */ - bp_end = bp + pbi->YPlaneFragments; - h = pbi->HFragments; - stride = pbi->YStride; - break; - default: /* u,v, 4:20 specific */ - bp_end = bp + pbi->UVPlaneFragments; - h = pbi->HFragments >> 1; - stride = pbi->UVStride; - break; - } - - while(bpbp_left) - loop_filter_h(&pbi->LastFrameRecon[bp[0]],stride,ll); - if(bp_left>bp_begin) - loop_filter_v(&pbi->LastFrameRecon[bp[0]],stride,ll); - if(bp+1LastFrameRecon[bp[0]]+8,stride,ll); - if(bp+hLastFrameRecon[bp[h]],stride,ll); - } - bp++; - cp++; - } - } - } - - __asm__ __volatile__("emms\n\t"); -} - -/* install our implementation in the function table */ -void dsp_mmx_dct_decode_init(DspFunctions *funcs) -{ - funcs->LoopFilter = loop_filter_mmx; -} - -#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_32/dsp_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32/dsp_mmx.c deleted file mode 100644 index 3c8a46e6a..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_32/dsp_mmx.c +++ /dev/null @@ -1,666 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: dsp_mmx.c 15153 2008-08-04 18:37:55Z tterribe $ - - ********************************************************************/ - -#include - -#include "../codec_internal.h" -#include "../dsp.h" - -#if defined(USE_ASM) - -static const __attribute__ ((aligned(8),used)) ogg_int64_t V128 = 0x0080008000800080LL; - -#define DSP_OP_AVG(a,b) ((((int)(a)) + ((int)(b)))/2) -#define DSP_OP_DIFF(a,b) (((int)(a)) - ((int)(b))) -#define DSP_OP_ABS_DIFF(a,b) abs((((int)(a)) - ((int)(b)))) - -#define SUB_LOOP \ - " movq (%0), %%mm0 \n\t" /* mm0 = FiltPtr */ \ - " movq (%1), %%mm1 \n\t" /* mm1 = ReconPtr */ \ - " movq %%mm0, %%mm2 \n\t" /* dup to prepare for up conversion */\ - " movq %%mm1, %%mm3 \n\t" /* dup to prepare for up conversion */\ - /* convert from UINT8 to INT16 */ \ - " punpcklbw %%mm7, %%mm0 \n\t" /* mm0 = INT16(FiltPtr) */ \ - " punpcklbw %%mm7, %%mm1 \n\t" /* mm1 = INT16(ReconPtr) */ \ - " punpckhbw %%mm7, %%mm2 \n\t" /* mm2 = INT16(FiltPtr) */ \ - " punpckhbw %%mm7, %%mm3 \n\t" /* mm3 = INT16(ReconPtr) */ \ - /* start calculation */ \ - " psubw %%mm1, %%mm0 \n\t" /* mm0 = FiltPtr - ReconPtr */ \ - " psubw %%mm3, %%mm2 \n\t" /* mm2 = FiltPtr - ReconPtr */ \ - " movq %%mm0, (%2) \n\t" /* write answer out */ \ - " movq %%mm2, 8(%2) \n\t" /* write answer out */ \ - /* Increment pointers */ \ - " add $16, %2 \n\t" \ - " add %3, %0 \n\t" \ - " add %4, %1 \n\t" - -static void sub8x8__mmx (unsigned char *FiltPtr, unsigned char *ReconPtr, - ogg_int16_t *DctInputPtr, ogg_uint32_t PixelsPerLine, - ogg_uint32_t ReconPixelsPerLine) -{ - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " pxor %%mm7, %%mm7 \n\t" - SUB_LOOP - SUB_LOOP - SUB_LOOP - SUB_LOOP - SUB_LOOP - SUB_LOOP - SUB_LOOP - SUB_LOOP - : "+r" (FiltPtr), - "+r" (ReconPtr), - "+r" (DctInputPtr) - : "m" (PixelsPerLine), - "m" (ReconPixelsPerLine) - : "memory" - ); -} - -#define SUB_128_LOOP \ - " movq (%0), %%mm0 \n\t" /* mm0 = FiltPtr */ \ - " movq %%mm0, %%mm2 \n\t" /* dup to prepare for up conversion */\ - /* convert from UINT8 to INT16 */ \ - " punpcklbw %%mm7, %%mm0 \n\t" /* mm0 = INT16(FiltPtr) */ \ - " punpckhbw %%mm7, %%mm2 \n\t" /* mm2 = INT16(FiltPtr) */ \ - /* start calculation */ \ - " psubw %%mm1, %%mm0 \n\t" /* mm0 = FiltPtr - 128 */ \ - " psubw %%mm1, %%mm2 \n\t" /* mm2 = FiltPtr - 128 */ \ - " movq %%mm0, (%1) \n\t" /* write answer out */ \ - " movq %%mm2, 8(%1) \n\t" /* write answer out */ \ - /* Increment pointers */ \ - " add $16, %1 \n\t" \ - " add %2, %0 \n\t" - - -static void sub8x8_128__mmx (unsigned char *FiltPtr, ogg_int16_t *DctInputPtr, - ogg_uint32_t PixelsPerLine) -{ - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " pxor %%mm7, %%mm7 \n\t" - " movq %[V128], %%mm1 \n\t" - SUB_128_LOOP - SUB_128_LOOP - SUB_128_LOOP - SUB_128_LOOP - SUB_128_LOOP - SUB_128_LOOP - SUB_128_LOOP - SUB_128_LOOP - : "+r" (FiltPtr), - "+r" (DctInputPtr) - : "m" (PixelsPerLine), - [V128] "m" (V128) - : "memory" - ); -} - -#define SUB_AVG2_LOOP \ - " movq (%0), %%mm0 \n\t" /* mm0 = FiltPtr */ \ - " movq (%1), %%mm1 \n\t" /* mm1 = ReconPtr1 */ \ - " movq (%2), %%mm4 \n\t" /* mm1 = ReconPtr2 */ \ - " movq %%mm0, %%mm2 \n\t" /* dup to prepare for up conversion */\ - " movq %%mm1, %%mm3 \n\t" /* dup to prepare for up conversion */\ - " movq %%mm4, %%mm5 \n\t" /* dup to prepare for up conversion */\ - /* convert from UINT8 to INT16 */ \ - " punpcklbw %%mm7, %%mm0 \n\t" /* mm0 = INT16(FiltPtr) */ \ - " punpcklbw %%mm7, %%mm1 \n\t" /* mm1 = INT16(ReconPtr1) */ \ - " punpcklbw %%mm7, %%mm4 \n\t" /* mm1 = INT16(ReconPtr2) */ \ - " punpckhbw %%mm7, %%mm2 \n\t" /* mm2 = INT16(FiltPtr) */ \ - " punpckhbw %%mm7, %%mm3 \n\t" /* mm3 = INT16(ReconPtr1) */ \ - " punpckhbw %%mm7, %%mm5 \n\t" /* mm3 = INT16(ReconPtr2) */ \ - /* average ReconPtr1 and ReconPtr2 */ \ - " paddw %%mm4, %%mm1 \n\t" /* mm1 = ReconPtr1 + ReconPtr2 */ \ - " paddw %%mm5, %%mm3 \n\t" /* mm3 = ReconPtr1 + ReconPtr2 */ \ - " psrlw $1, %%mm1 \n\t" /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ \ - " psrlw $1, %%mm3 \n\t" /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ \ - " psubw %%mm1, %%mm0 \n\t" /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ \ - " psubw %%mm3, %%mm2 \n\t" /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ \ - " movq %%mm0, (%3) \n\t" /* write answer out */ \ - " movq %%mm2, 8(%3) \n\t" /* write answer out */ \ - /* Increment pointers */ \ - " add $16, %3 \n\t" \ - " add %4, %0 \n\t" \ - " add %5, %1 \n\t" \ - " add %5, %2 \n\t" - - -static void sub8x8avg2__mmx (unsigned char *FiltPtr, unsigned char *ReconPtr1, - unsigned char *ReconPtr2, ogg_int16_t *DctInputPtr, - ogg_uint32_t PixelsPerLine, - ogg_uint32_t ReconPixelsPerLine) -{ - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " pxor %%mm7, %%mm7 \n\t" - SUB_AVG2_LOOP - SUB_AVG2_LOOP - SUB_AVG2_LOOP - SUB_AVG2_LOOP - SUB_AVG2_LOOP - SUB_AVG2_LOOP - SUB_AVG2_LOOP - SUB_AVG2_LOOP - : "+r" (FiltPtr), - "+r" (ReconPtr1), - "+r" (ReconPtr2), - "+r" (DctInputPtr) - : "m" (PixelsPerLine), - "m" (ReconPixelsPerLine) - : "memory" - ); -} - -static ogg_uint32_t row_sad8__mmx (unsigned char *Src1, unsigned char *Src2) -{ - ogg_uint32_t MaxSad; - - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " pxor %%mm6, %%mm6 \n\t" /* zero out mm6 for unpack */ - " pxor %%mm7, %%mm7 \n\t" /* zero out mm7 for unpack */ - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ - " movq (%2), %%mm1 \n\t" - - " movq %%mm0, %%mm2 \n\t" - " psubusb %%mm1, %%mm0 \n\t" /* A - B */ - " psubusb %%mm2, %%mm1 \n\t" /* B - A */ - " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ - - " movq %%mm0, %%mm1 \n\t" - - " punpcklbw %%mm6, %%mm0 \n\t" /* ; unpack low four bytes to higher precision */ - " punpckhbw %%mm7, %%mm1 \n\t" /* ; unpack high four bytes to higher precision */ - - " movq %%mm0, %%mm2 \n\t" - " movq %%mm1, %%mm3 \n\t" - " psrlq $32, %%mm2 \n\t" /* fold and add */ - " psrlq $32, %%mm3 \n\t" - " paddw %%mm2, %%mm0 \n\t" - " paddw %%mm3, %%mm1 \n\t" - " movq %%mm0, %%mm2 \n\t" - " movq %%mm1, %%mm3 \n\t" - " psrlq $16, %%mm2 \n\t" - " psrlq $16, %%mm3 \n\t" - " paddw %%mm2, %%mm0 \n\t" - " paddw %%mm3, %%mm1 \n\t" - - " psubusw %%mm0, %%mm1 \n\t" - " paddw %%mm0, %%mm1 \n\t" /* mm1 = max(mm1, mm0) */ - " movd %%mm1, %0 \n\t" - " andl $0xffff, %0 \n\t" - - : "=m" (MaxSad), - "+r" (Src1), - "+r" (Src2) - : - : "memory" - ); - return MaxSad; -} - -static ogg_uint32_t col_sad8x8__mmx (unsigned char *Src1, unsigned char *Src2, - ogg_uint32_t stride) -{ - ogg_uint32_t MaxSad; - - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " pxor %%mm3, %%mm3 \n\t" /* zero out mm3 for unpack */ - " pxor %%mm4, %%mm4 \n\t" /* mm4 low sum */ - " pxor %%mm5, %%mm5 \n\t" /* mm5 high sum */ - " pxor %%mm6, %%mm6 \n\t" /* mm6 low sum */ - " pxor %%mm7, %%mm7 \n\t" /* mm7 high sum */ - " mov $4, %%edi \n\t" /* 4 rows */ - "1: \n\t" - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ - " movq (%2), %%mm1 \n\t" /* take 8 bytes */ - - " movq %%mm0, %%mm2 \n\t" - " psubusb %%mm1, %%mm0 \n\t" /* A - B */ - " psubusb %%mm2, %%mm1 \n\t" /* B - A */ - " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ - " movq %%mm0, %%mm1 \n\t" - - " punpcklbw %%mm3, %%mm0 \n\t" /* unpack to higher precision for accumulation */ - " paddw %%mm0, %%mm4 \n\t" /* accumulate difference... */ - " punpckhbw %%mm3, %%mm1 \n\t" /* unpack high four bytes to higher precision */ - " paddw %%mm1, %%mm5 \n\t" /* accumulate difference... */ - " add %3, %1 \n\t" /* Inc pointer into the new data */ - " add %3, %2 \n\t" /* Inc pointer into the new data */ - - " dec %%edi \n\t" - " jnz 1b \n\t" - - " mov $4, %%edi \n\t" /* 4 rows */ - "2: \n\t" - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ - " movq (%2), %%mm1 \n\t" /* take 8 bytes */ - - " movq %%mm0, %%mm2 \n\t" - " psubusb %%mm1, %%mm0 \n\t" /* A - B */ - " psubusb %%mm2, %%mm1 \n\t" /* B - A */ - " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ - " movq %%mm0, %%mm1 \n\t" - - " punpcklbw %%mm3, %%mm0 \n\t" /* unpack to higher precision for accumulation */ - " paddw %%mm0, %%mm6 \n\t" /* accumulate difference... */ - " punpckhbw %%mm3, %%mm1 \n\t" /* unpack high four bytes to higher precision */ - " paddw %%mm1, %%mm7 \n\t" /* accumulate difference... */ - " add %3, %1 \n\t" /* Inc pointer into the new data */ - " add %3, %2 \n\t" /* Inc pointer into the new data */ - - " dec %%edi \n\t" - " jnz 2b \n\t" - - " psubusw %%mm6, %%mm7 \n\t" - " paddw %%mm6, %%mm7 \n\t" /* mm7 = max(mm7, mm6) */ - " psubusw %%mm4, %%mm5 \n\t" - " paddw %%mm4, %%mm5 \n\t" /* mm5 = max(mm5, mm4) */ - " psubusw %%mm5, %%mm7 \n\t" - " paddw %%mm5, %%mm7 \n\t" /* mm7 = max(mm5, mm7) */ - " movq %%mm7, %%mm6 \n\t" - " psrlq $32, %%mm6 \n\t" - " psubusw %%mm6, %%mm7 \n\t" - " paddw %%mm6, %%mm7 \n\t" /* mm7 = max(mm5, mm7) */ - " movq %%mm7, %%mm6 \n\t" - " psrlq $16, %%mm6 \n\t" - " psubusw %%mm6, %%mm7 \n\t" - " paddw %%mm6, %%mm7 \n\t" /* mm7 = max(mm5, mm7) */ - " movd %%mm7, %0 \n\t" - " andl $0xffff, %0 \n\t" - - : "=r" (MaxSad), - "+r" (Src1), - "+r" (Src2) - : "r" (stride) - : "memory", "edi" - ); - - return MaxSad; -} - -#define SAD_LOOP \ - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ \ - " movq (%2), %%mm1 \n\t" \ - " movq %%mm0, %%mm2 \n\t" \ - " psubusb %%mm1, %%mm0 \n\t" /* A - B */ \ - " psubusb %%mm2, %%mm1 \n\t" /* B - A */ \ - " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ \ - " movq %%mm0, %%mm1 \n\t" \ - " punpcklbw %%mm6, %%mm0 \n\t" /* unpack to higher precision for accumulation */ \ - " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ \ - " punpckhbw %%mm6, %%mm1 \n\t" /* unpack high four bytes to higher precision */ \ - " add %3, %1 \n\t" /* Inc pointer into the new data */ \ - " paddw %%mm1, %%mm7 \n\t" /* accumulate difference... */ \ - " add %4, %2 \n\t" /* Inc pointer into ref data */ - -static ogg_uint32_t sad8x8__mmx (unsigned char *ptr1, ogg_uint32_t stride1, - unsigned char *ptr2, ogg_uint32_t stride2) -{ - ogg_uint32_t DiffVal; - - __asm__ __volatile__ ( - " .p2align 4 \n\t" - " pxor %%mm6, %%mm6 \n\t" /* zero out mm6 for unpack */ - " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ - SAD_LOOP - SAD_LOOP - SAD_LOOP - SAD_LOOP - SAD_LOOP - SAD_LOOP - SAD_LOOP - SAD_LOOP - " movq %%mm7, %%mm0 \n\t" - " psrlq $32, %%mm7 \n\t" - " paddw %%mm0, %%mm7 \n\t" - " movq %%mm7, %%mm0 \n\t" - " psrlq $16, %%mm7 \n\t" - " paddw %%mm0, %%mm7 \n\t" - " movd %%mm7, %0 \n\t" - " andl $0xffff, %0 \n\t" - - : "=m" (DiffVal), - "+r" (ptr1), - "+r" (ptr2) - : "r" (stride1), - "r" (stride2) - : "memory" - ); - - return DiffVal; -} - -static ogg_uint32_t sad8x8_thres__mmx (unsigned char *ptr1, ogg_uint32_t stride1, - unsigned char *ptr2, ogg_uint32_t stride2, - ogg_uint32_t thres) -{ - return sad8x8__mmx (ptr1, stride1, ptr2, stride2); -} - -static ogg_uint32_t sad8x8_xy2_thres__mmx (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr1, - unsigned char *RefDataPtr2, ogg_uint32_t RefStride, - ogg_uint32_t thres) -{ - ogg_uint32_t DiffVal; - - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " pcmpeqd %%mm5, %%mm5 \n\t" /* fefefefefefefefe in mm5 */ - " paddb %%mm5, %%mm5 \n\t" - - " pxor %%mm6, %%mm6 \n\t" /* zero out mm6 for unpack */ - " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ - " mov $8, %%edi \n\t" /* 8 rows */ - "1: \n\t" - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ - - " movq (%2), %%mm2 \n\t" - " movq (%3), %%mm3 \n\t" /* take average of mm2 and mm3 */ - " movq %%mm2, %%mm1 \n\t" - " pand %%mm3, %%mm1 \n\t" - " pxor %%mm2, %%mm3 \n\t" - " pand %%mm5, %%mm3 \n\t" - " psrlq $1, %%mm3 \n\t" - " paddb %%mm3, %%mm1 \n\t" - - " movq %%mm0, %%mm2 \n\t" - - " psubusb %%mm1, %%mm0 \n\t" /* A - B */ - " psubusb %%mm2, %%mm1 \n\t" /* B - A */ - " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ - " movq %%mm0, %%mm1 \n\t" - - " punpcklbw %%mm6, %%mm0 \n\t" /* unpack to higher precision for accumulation */ - " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ - " punpckhbw %%mm6, %%mm1 \n\t" /* unpack high four bytes to higher precision */ - " add %4, %1 \n\t" /* Inc pointer into the new data */ - " paddw %%mm1, %%mm7 \n\t" /* accumulate difference... */ - " add %5, %2 \n\t" /* Inc pointer into ref data */ - " add %5, %3 \n\t" /* Inc pointer into ref data */ - - " dec %%edi \n\t" - " jnz 1b \n\t" - - " movq %%mm7, %%mm0 \n\t" - " psrlq $32, %%mm7 \n\t" - " paddw %%mm0, %%mm7 \n\t" - " movq %%mm7, %%mm0 \n\t" - " psrlq $16, %%mm7 \n\t" - " paddw %%mm0, %%mm7 \n\t" - " movd %%mm7, %0 \n\t" - " andl $0xffff, %0 \n\t" - - : "=m" (DiffVal), - "+r" (SrcData), - "+r" (RefDataPtr1), - "+r" (RefDataPtr2) - : "m" (SrcStride), - "m" (RefStride) - : "edi", "memory" - ); - - return DiffVal; -} - -static ogg_uint32_t intra8x8_err__mmx (unsigned char *DataPtr, ogg_uint32_t Stride) -{ - ogg_uint32_t XSum; - ogg_uint32_t XXSum; - - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " pxor %%mm5, %%mm5 \n\t" - " pxor %%mm6, %%mm6 \n\t" - " pxor %%mm7, %%mm7 \n\t" - " mov $8, %%edi \n\t" - "1: \n\t" - " movq (%2), %%mm0 \n\t" /* take 8 bytes */ - " movq %%mm0, %%mm2 \n\t" - - " punpcklbw %%mm6, %%mm0 \n\t" - " punpckhbw %%mm6, %%mm2 \n\t" - - " paddw %%mm0, %%mm5 \n\t" - " paddw %%mm2, %%mm5 \n\t" - - " pmaddwd %%mm0, %%mm0 \n\t" - " pmaddwd %%mm2, %%mm2 \n\t" - - " paddd %%mm0, %%mm7 \n\t" - " paddd %%mm2, %%mm7 \n\t" - - " add %3, %2 \n\t" /* Inc pointer into src data */ - - " dec %%edi \n\t" - " jnz 1b \n\t" - - " movq %%mm5, %%mm0 \n\t" - " psrlq $32, %%mm5 \n\t" - " paddw %%mm0, %%mm5 \n\t" - " movq %%mm5, %%mm0 \n\t" - " psrlq $16, %%mm5 \n\t" - " paddw %%mm0, %%mm5 \n\t" - " movd %%mm5, %%edi \n\t" - " movsx %%di, %%edi \n\t" - " movl %%edi, %0 \n\t" - - " movq %%mm7, %%mm0 \n\t" - " psrlq $32, %%mm7 \n\t" - " paddd %%mm0, %%mm7 \n\t" - " movd %%mm7, %1 \n\t" - - : "=r" (XSum), - "=r" (XXSum), - "+r" (DataPtr) - : "r" (Stride) - : "edi", "memory" - ); - - /* Compute population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum ) ); -} - -static ogg_uint32_t inter8x8_err__mmx (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr, ogg_uint32_t RefStride) -{ - ogg_uint32_t XSum; - ogg_uint32_t XXSum; - - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " pxor %%mm5, %%mm5 \n\t" - " pxor %%mm6, %%mm6 \n\t" - " pxor %%mm7, %%mm7 \n\t" - " mov $8, %%edi \n\t" - "1: \n\t" - " movq (%2), %%mm0 \n\t" /* take 8 bytes */ - " movq (%3), %%mm1 \n\t" - " movq %%mm0, %%mm2 \n\t" - " movq %%mm1, %%mm3 \n\t" - - " punpcklbw %%mm6, %%mm0 \n\t" - " punpcklbw %%mm6, %%mm1 \n\t" - " punpckhbw %%mm6, %%mm2 \n\t" - " punpckhbw %%mm6, %%mm3 \n\t" - - " psubsw %%mm1, %%mm0 \n\t" - " psubsw %%mm3, %%mm2 \n\t" - - " paddw %%mm0, %%mm5 \n\t" - " paddw %%mm2, %%mm5 \n\t" - - " pmaddwd %%mm0, %%mm0 \n\t" - " pmaddwd %%mm2, %%mm2 \n\t" - - " paddd %%mm0, %%mm7 \n\t" - " paddd %%mm2, %%mm7 \n\t" - - " add %4, %2 \n\t" /* Inc pointer into src data */ - " add %5, %3 \n\t" /* Inc pointer into ref data */ - - " dec %%edi \n\t" - " jnz 1b \n\t" - - " movq %%mm5, %%mm0 \n\t" - " psrlq $32, %%mm5 \n\t" - " paddw %%mm0, %%mm5 \n\t" - " movq %%mm5, %%mm0 \n\t" - " psrlq $16, %%mm5 \n\t" - " paddw %%mm0, %%mm5 \n\t" - " movd %%mm5, %%edi \n\t" - " movsx %%di, %%edi \n\t" - " movl %%edi, %0 \n\t" - - " movq %%mm7, %%mm0 \n\t" - " psrlq $32, %%mm7 \n\t" - " paddd %%mm0, %%mm7 \n\t" - " movd %%mm7, %1 \n\t" - - : "=m" (XSum), - "=m" (XXSum), - "+r" (SrcData), - "+r" (RefDataPtr) - : "m" (SrcStride), - "m" (RefStride) - : "edi", "memory" - ); - - /* Compute and return population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum )); -} - -static ogg_uint32_t inter8x8_err_xy2__mmx (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr1, - unsigned char *RefDataPtr2, ogg_uint32_t RefStride) -{ - ogg_uint32_t XSum; - ogg_uint32_t XXSum; - - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " pcmpeqd %%mm4, %%mm4 \n\t" /* fefefefefefefefe in mm4 */ - " paddb %%mm4, %%mm4 \n\t" - " pxor %%mm5, %%mm5 \n\t" - " pxor %%mm6, %%mm6 \n\t" - " pxor %%mm7, %%mm7 \n\t" - " mov $8, %%edi \n\t" - "1: \n\t" - " movq (%2), %%mm0 \n\t" /* take 8 bytes */ - - " movq (%3), %%mm2 \n\t" - " movq (%4), %%mm3 \n\t" /* take average of mm2 and mm3 */ - " movq %%mm2, %%mm1 \n\t" - " pand %%mm3, %%mm1 \n\t" - " pxor %%mm2, %%mm3 \n\t" - " pand %%mm4, %%mm3 \n\t" - " psrlq $1, %%mm3 \n\t" - " paddb %%mm3, %%mm1 \n\t" - - " movq %%mm0, %%mm2 \n\t" - " movq %%mm1, %%mm3 \n\t" - - " punpcklbw %%mm6, %%mm0 \n\t" - " punpcklbw %%mm6, %%mm1 \n\t" - " punpckhbw %%mm6, %%mm2 \n\t" - " punpckhbw %%mm6, %%mm3 \n\t" - - " psubsw %%mm1, %%mm0 \n\t" - " psubsw %%mm3, %%mm2 \n\t" - - " paddw %%mm0, %%mm5 \n\t" - " paddw %%mm2, %%mm5 \n\t" - - " pmaddwd %%mm0, %%mm0 \n\t" - " pmaddwd %%mm2, %%mm2 \n\t" - - " paddd %%mm0, %%mm7 \n\t" - " paddd %%mm2, %%mm7 \n\t" - - " add %5, %2 \n\t" /* Inc pointer into src data */ - " add %6, %3 \n\t" /* Inc pointer into ref data */ - " add %6, %4 \n\t" /* Inc pointer into ref data */ - - " dec %%edi \n\t" - " jnz 1b \n\t" - - " movq %%mm5, %%mm0 \n\t" - " psrlq $32, %%mm5 \n\t" - " paddw %%mm0, %%mm5 \n\t" - " movq %%mm5, %%mm0 \n\t" - " psrlq $16, %%mm5 \n\t" - " paddw %%mm0, %%mm5 \n\t" - " movd %%mm5, %%edi \n\t" - " movsx %%di, %%edi \n\t" - " movl %%edi, %0 \n\t" - - " movq %%mm7, %%mm0 \n\t" - " psrlq $32, %%mm7 \n\t" - " paddd %%mm0, %%mm7 \n\t" - " movd %%mm7, %1 \n\t" - - : "=m" (XSum), - "=m" (XXSum), - "+r" (SrcData), - "+r" (RefDataPtr1), - "+r" (RefDataPtr2) - : "m" (SrcStride), - "m" (RefStride) - : "edi", "memory" - ); - - /* Compute and return population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum )); -} - -static void restore_fpu (void) -{ - __asm__ __volatile__ ( - " emms \n\t" - ); -} - -void dsp_mmx_init(DspFunctions *funcs) -{ - funcs->restore_fpu = restore_fpu; - funcs->sub8x8 = sub8x8__mmx; - funcs->sub8x8_128 = sub8x8_128__mmx; - funcs->sub8x8avg2 = sub8x8avg2__mmx; - funcs->row_sad8 = row_sad8__mmx; - funcs->col_sad8x8 = col_sad8x8__mmx; - funcs->sad8x8 = sad8x8__mmx; - funcs->sad8x8_thres = sad8x8_thres__mmx; - funcs->sad8x8_xy2_thres = sad8x8_xy2_thres__mmx; - funcs->intra8x8_err = intra8x8_err__mmx; - funcs->inter8x8_err = inter8x8_err__mmx; - funcs->inter8x8_err_xy2 = inter8x8_err_xy2__mmx; -} - -#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_32/dsp_mmxext.c b/Engine/lib/libtheora/lib/enc/x86_32/dsp_mmxext.c deleted file mode 100644 index 297c3213a..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_32/dsp_mmxext.c +++ /dev/null @@ -1,347 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: dsp_mmxext.c 15153 2008-08-04 18:37:55Z tterribe $ - - ********************************************************************/ - -#include - -#include "../codec_internal.h" -#include "../dsp.h" - -#if defined(USE_ASM) - -#define SAD_MMXEXT_LOOP \ - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ \ - " movq (%2), %%mm1 \n\t" \ - " psadbw %%mm1, %%mm0 \n\t" \ - " add %3, %1 \n\t" /* Inc pointer into the new data */ \ - " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ \ - " add %4, %2 \n\t" /* Inc pointer into ref data */ - - -static ogg_uint32_t sad8x8__mmxext (unsigned char *ptr1, ogg_uint32_t stride1, - unsigned char *ptr2, ogg_uint32_t stride2) -{ - ogg_uint32_t DiffVal; - - __asm__ __volatile__ ( - " .p2align 4 \n\t" - " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ - - SAD_MMXEXT_LOOP - SAD_MMXEXT_LOOP - SAD_MMXEXT_LOOP - SAD_MMXEXT_LOOP - SAD_MMXEXT_LOOP - SAD_MMXEXT_LOOP - SAD_MMXEXT_LOOP - - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ - " movq (%2), %%mm1 \n\t" - " psadbw %%mm1, %%mm0 \n\t" - " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ - " movd %%mm7, %0 \n\t" - - : "=r" (DiffVal), - "+r" (ptr1), - "+r" (ptr2) - : "r" (stride1), - "r" (stride2) - : "memory" - ); - - return DiffVal; -} - -#define SAD_TRES_LOOP \ - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ \ - " movq (%2), %%mm1 \n\t" \ - " psadbw %%mm1, %%mm0 \n\t" \ - " add %3, %1 \n\t" /* Inc pointer into the new data */ \ - " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ \ - " add %4, %2 \n\t" /* Inc pointer into ref data */ - - -static ogg_uint32_t sad8x8_thres__mmxext (unsigned char *ptr1, ogg_uint32_t stride1, - unsigned char *ptr2, ogg_uint32_t stride2, - ogg_uint32_t thres) -{ - ogg_uint32_t DiffVal; - - __asm__ __volatile__ ( - " .p2align 4 \n\t" - " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ - - SAD_TRES_LOOP - SAD_TRES_LOOP - SAD_TRES_LOOP - SAD_TRES_LOOP - SAD_TRES_LOOP - SAD_TRES_LOOP - SAD_TRES_LOOP - SAD_TRES_LOOP - - " movd %%mm7, %0 \n\t" - - : "=r" (DiffVal), - "+r" (ptr1), - "+r" (ptr2) - : "r" (stride1), - "r" (stride2) - : "memory" - ); - - return DiffVal; -} - -#define SAD_XY2_TRES \ - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ \ - " movq (%2), %%mm1 \n\t" \ - " movq (%3), %%mm2 \n\t" \ - " pavgb %%mm2, %%mm1 \n\t" \ - " psadbw %%mm1, %%mm0 \n\t" \ - \ - " add %4, %1 \n\t" /* Inc pointer into the new data */ \ - " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ \ - " add %5, %2 \n\t" /* Inc pointer into ref data */ \ - " add %5, %3 \n\t" /* Inc pointer into ref data */ - - -static ogg_uint32_t sad8x8_xy2_thres__mmxext (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr1, - unsigned char *RefDataPtr2, ogg_uint32_t RefStride, - ogg_uint32_t thres) -{ - ogg_uint32_t DiffVal; - - __asm__ __volatile__ ( - " .p2align 4 \n\t" - " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ - SAD_XY2_TRES - SAD_XY2_TRES - SAD_XY2_TRES - SAD_XY2_TRES - SAD_XY2_TRES - SAD_XY2_TRES - SAD_XY2_TRES - SAD_XY2_TRES - - " movd %%mm7, %0 \n\t" - : "=m" (DiffVal), - "+r" (SrcData), - "+r" (RefDataPtr1), - "+r" (RefDataPtr2) - : "m" (SrcStride), - "m" (RefStride) - : "memory" - ); - - return DiffVal; -} - -static ogg_uint32_t row_sad8__mmxext (unsigned char *Src1, unsigned char *Src2) -{ - ogg_uint32_t MaxSad; - - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " movd (%1), %%mm0 \n\t" - " movd (%2), %%mm1 \n\t" - " psadbw %%mm0, %%mm1 \n\t" - " movd 4(%1), %%mm2 \n\t" - " movd 4(%2), %%mm3 \n\t" - " psadbw %%mm2, %%mm3 \n\t" - - " pmaxsw %%mm1, %%mm3 \n\t" - " movd %%mm3, %0 \n\t" - " andl $0xffff, %0 \n\t" - - : "=m" (MaxSad), - "+r" (Src1), - "+r" (Src2) - : - : "memory" - ); - - return MaxSad; -} - -static ogg_uint32_t col_sad8x8__mmxext (unsigned char *Src1, unsigned char *Src2, - ogg_uint32_t stride) -{ - ogg_uint32_t MaxSad; - - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " pxor %%mm3, %%mm3 \n\t" /* zero out mm3 for unpack */ - " pxor %%mm4, %%mm4 \n\t" /* mm4 low sum */ - " pxor %%mm5, %%mm5 \n\t" /* mm5 high sum */ - " pxor %%mm6, %%mm6 \n\t" /* mm6 low sum */ - " pxor %%mm7, %%mm7 \n\t" /* mm7 high sum */ - " mov $4, %%edi \n\t" /* 4 rows */ - "1: \n\t" - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ - " movq (%2), %%mm1 \n\t" /* take 8 bytes */ - - " movq %%mm0, %%mm2 \n\t" - " psubusb %%mm1, %%mm0 \n\t" /* A - B */ - " psubusb %%mm2, %%mm1 \n\t" /* B - A */ - " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ - " movq %%mm0, %%mm1 \n\t" - - " punpcklbw %%mm3, %%mm0 \n\t" /* unpack to higher precision for accumulation */ - " paddw %%mm0, %%mm4 \n\t" /* accumulate difference... */ - " punpckhbw %%mm3, %%mm1 \n\t" /* unpack high four bytes to higher precision */ - " paddw %%mm1, %%mm5 \n\t" /* accumulate difference... */ - " add %3, %1 \n\t" /* Inc pointer into the new data */ - " add %3, %2 \n\t" /* Inc pointer into the new data */ - - " dec %%edi \n\t" - " jnz 1b \n\t" - - " mov $4, %%edi \n\t" /* 4 rows */ - "2: \n\t" - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ - " movq (%2), %%mm1 \n\t" /* take 8 bytes */ - - " movq %%mm0, %%mm2 \n\t" - " psubusb %%mm1, %%mm0 \n\t" /* A - B */ - " psubusb %%mm2, %%mm1 \n\t" /* B - A */ - " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ - " movq %%mm0, %%mm1 \n\t" - - " punpcklbw %%mm3, %%mm0 \n\t" /* unpack to higher precision for accumulation */ - " paddw %%mm0, %%mm6 \n\t" /* accumulate difference... */ - " punpckhbw %%mm3, %%mm1 \n\t" /* unpack high four bytes to higher precision */ - " paddw %%mm1, %%mm7 \n\t" /* accumulate difference... */ - " add %3, %1 \n\t" /* Inc pointer into the new data */ - " add %3, %2 \n\t" /* Inc pointer into the new data */ - - " dec %%edi \n\t" - " jnz 2b \n\t" - - " pmaxsw %%mm6, %%mm7 \n\t" - " pmaxsw %%mm4, %%mm5 \n\t" - " pmaxsw %%mm5, %%mm7 \n\t" - " movq %%mm7, %%mm6 \n\t" - " psrlq $32, %%mm6 \n\t" - " pmaxsw %%mm6, %%mm7 \n\t" - " movq %%mm7, %%mm6 \n\t" - " psrlq $16, %%mm6 \n\t" - " pmaxsw %%mm6, %%mm7 \n\t" - " movd %%mm7, %0 \n\t" - " andl $0xffff, %0 \n\t" - - : "=r" (MaxSad), - "+r" (Src1), - "+r" (Src2) - : "r" (stride) - : "memory", "edi" - ); - - return MaxSad; -} - -static ogg_uint32_t inter8x8_err_xy2__mmxext (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr1, - unsigned char *RefDataPtr2, ogg_uint32_t RefStride) -{ - ogg_uint32_t XSum; - ogg_uint32_t XXSum; - - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " pxor %%mm4, %%mm4 \n\t" - " pxor %%mm5, %%mm5 \n\t" - " pxor %%mm6, %%mm6 \n\t" - " pxor %%mm7, %%mm7 \n\t" - " mov $8, %%edi \n\t" - "1: \n\t" - " movq (%2), %%mm0 \n\t" /* take 8 bytes */ - - " movq (%3), %%mm2 \n\t" - " movq (%4), %%mm1 \n\t" /* take average of mm2 and mm1 */ - " pavgb %%mm2, %%mm1 \n\t" - - " movq %%mm0, %%mm2 \n\t" - " movq %%mm1, %%mm3 \n\t" - - " punpcklbw %%mm6, %%mm0 \n\t" - " punpcklbw %%mm4, %%mm1 \n\t" - " punpckhbw %%mm6, %%mm2 \n\t" - " punpckhbw %%mm4, %%mm3 \n\t" - - " psubsw %%mm1, %%mm0 \n\t" - " psubsw %%mm3, %%mm2 \n\t" - - " paddw %%mm0, %%mm5 \n\t" - " paddw %%mm2, %%mm5 \n\t" - - " pmaddwd %%mm0, %%mm0 \n\t" - " pmaddwd %%mm2, %%mm2 \n\t" - - " paddd %%mm0, %%mm7 \n\t" - " paddd %%mm2, %%mm7 \n\t" - - " add %5, %2 \n\t" /* Inc pointer into src data */ - " add %6, %3 \n\t" /* Inc pointer into ref data */ - " add %6, %4 \n\t" /* Inc pointer into ref data */ - - " dec %%edi \n\t" - " jnz 1b \n\t" - - " movq %%mm5, %%mm0 \n\t" - " psrlq $32, %%mm5 \n\t" - " paddw %%mm0, %%mm5 \n\t" - " movq %%mm5, %%mm0 \n\t" - " psrlq $16, %%mm5 \n\t" - " paddw %%mm0, %%mm5 \n\t" - " movd %%mm5, %%edi \n\t" - " movsx %%di, %%edi \n\t" - " movl %%edi, %0 \n\t" - - " movq %%mm7, %%mm0 \n\t" - " psrlq $32, %%mm7 \n\t" - " paddd %%mm0, %%mm7 \n\t" - " movd %%mm7, %1 \n\t" - - : "=m" (XSum), - "=m" (XXSum), - "+r" (SrcData), - "+r" (RefDataPtr1), - "+r" (RefDataPtr2) - : "m" (SrcStride), - "m" (RefStride) - : "edi", "memory" - ); - - /* Compute and return population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum )); -} - -void dsp_mmxext_init(DspFunctions *funcs) -{ - funcs->row_sad8 = row_sad8__mmxext; - funcs->col_sad8x8 = col_sad8x8__mmxext; - funcs->sad8x8 = sad8x8__mmxext; - funcs->sad8x8_thres = sad8x8_thres__mmxext; - funcs->sad8x8_xy2_thres = sad8x8_xy2_thres__mmxext; - funcs->inter8x8_err_xy2 = inter8x8_err_xy2__mmxext; -} - -#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_32/fdct_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32/fdct_mmx.c deleted file mode 100644 index 8de691f81..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_32/fdct_mmx.c +++ /dev/null @@ -1,339 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: fdct_mmx.c 15153 2008-08-04 18:37:55Z tterribe $ - - ********************************************************************/ - -/* mmx fdct implementation */ - -#include "theora/theora.h" -#include "../codec_internal.h" -#include "../dsp.h" - -#if defined(USE_ASM) - -static const __attribute__ ((aligned(8),used)) ogg_int64_t xC1S7 = 0x0fb15fb15fb15fb15LL; -static const __attribute__ ((aligned(8),used)) ogg_int64_t xC2S6 = 0x0ec83ec83ec83ec83LL; -static const __attribute__ ((aligned(8),used)) ogg_int64_t xC3S5 = 0x0d4dbd4dbd4dbd4dbLL; -static const __attribute__ ((aligned(8),used)) ogg_int64_t xC4S4 = 0x0b505b505b505b505LL; -static const __attribute__ ((aligned(8),used)) ogg_int64_t xC5S3 = 0x08e3a8e3a8e3a8e3aLL; -static const __attribute__ ((aligned(8),used)) ogg_int64_t xC6S2 = 0x061f861f861f861f8LL; -static const __attribute__ ((aligned(8),used)) ogg_int64_t xC7S1 = 0x031f131f131f131f1LL; - -/* execute stage 1 of forward DCT */ -#define Fdct_mmx(ip0,ip1,ip2,ip3,ip4,ip5,ip6,ip7,temp) \ - " movq " #ip0 ", %%mm0 \n\t" \ - " movq " #ip1 ", %%mm1 \n\t" \ - " movq " #ip3 ", %%mm2 \n\t" \ - " movq " #ip5 ", %%mm3 \n\t" \ - " movq %%mm0, %%mm4 \n\t" \ - " movq %%mm1, %%mm5 \n\t" \ - " movq %%mm2, %%mm6 \n\t" \ - " movq %%mm3, %%mm7 \n\t" \ - \ - " paddsw " #ip7 ", %%mm0 \n\t" /* mm0 = ip0 + ip7 = is07 */ \ - " paddsw " #ip2 ", %%mm1 \n\t" /* mm1 = ip1 + ip2 = is12 */ \ - " paddsw " #ip4 ", %%mm2 \n\t" /* mm2 = ip3 + ip4 = is34 */ \ - " paddsw " #ip6 ", %%mm3 \n\t" /* mm3 = ip5 + ip6 = is56 */ \ - " psubsw " #ip7 ", %%mm4 \n\t" /* mm4 = ip0 - ip7 = id07 */ \ - " psubsw " #ip2 ", %%mm5 \n\t" /* mm5 = ip1 - ip2 = id12 */ \ - \ - " psubsw %%mm2, %%mm0 \n\t" /* mm0 = is07 - is34 */ \ - \ - " paddsw %%mm2, %%mm2 \n\t" \ - \ - " psubsw " #ip4 ", %%mm6 \n\t" /* mm6 = ip3 - ip4 = id34 */ \ - \ - " paddsw %%mm0, %%mm2 \n\t" /* mm2 = is07 + is34 = is0734 */ \ - " psubsw %%mm3, %%mm1 \n\t" /* mm1 = is12 - is56 */ \ - " movq %%mm0," #temp " \n\t" /* Save is07 - is34 to free mm0; */ \ - " paddsw %%mm3, %%mm3 \n\t" \ - " paddsw %%mm1, %%mm3 \n\t" /* mm3 = is12 + 1s56 = is1256 */ \ - \ - " psubsw " #ip6 ", %%mm7 \n\t" /* mm7 = ip5 - ip6 = id56 */ \ - /* ------------------------------------------------------------------- */ \ - " psubsw %%mm7, %%mm5 \n\t" /* mm5 = id12 - id56 */ \ - " paddsw %%mm7, %%mm7 \n\t" \ - " paddsw %%mm5, %%mm7 \n\t" /* mm7 = id12 + id56 */ \ - /* ------------------------------------------------------------------- */ \ - " psubsw %%mm3, %%mm2 \n\t" /* mm2 = is0734 - is1256 */ \ - " paddsw %%mm3, %%mm3 \n\t" \ - \ - " movq %%mm2, %%mm0 \n\t" /* make a copy */ \ - " paddsw %%mm2, %%mm3 \n\t" /* mm3 = is0734 + is1256 */ \ - \ - " pmulhw %[xC4S4], %%mm0 \n\t" /* mm0 = xC4S4 * ( is0734 - is1256 ) - ( is0734 - is1256 ) */ \ - " paddw %%mm2, %%mm0 \n\t" /* mm0 = xC4S4 * ( is0734 - is1256 ) */ \ - " psrlw $15, %%mm2 \n\t" \ - " paddw %%mm2, %%mm0 \n\t" /* Truncate mm0, now it is op[4] */ \ - \ - " movq %%mm3, %%mm2 \n\t" \ - " movq %%mm0," #ip4 " \n\t" /* save ip4, now mm0,mm2 are free */ \ - \ - " movq %%mm3, %%mm0 \n\t" \ - " pmulhw %[xC4S4], %%mm3 \n\t" /* mm3 = xC4S4 * ( is0734 +is1256 ) - ( is0734 +is1256 ) */ \ - \ - " psrlw $15, %%mm2 \n\t" \ - " paddw %%mm0, %%mm3 \n\t" /* mm3 = xC4S4 * ( is0734 +is1256 ) */ \ - " paddw %%mm2, %%mm3 \n\t" /* Truncate mm3, now it is op[0] */ \ - \ - " movq %%mm3," #ip0 " \n\t" \ - /* ------------------------------------------------------------------- */ \ - " movq " #temp ", %%mm3 \n\t" /* mm3 = irot_input_y */ \ - " pmulhw %[xC2S6], %%mm3 \n\t" /* mm3 = xC2S6 * irot_input_y - irot_input_y */ \ - \ - " movq " #temp ", %%mm2 \n\t" \ - " movq %%mm2, %%mm0 \n\t" \ - \ - " psrlw $15, %%mm2 \n\t" /* mm3 = xC2S6 * irot_input_y */ \ - " paddw %%mm0, %%mm3 \n\t" \ - \ - " paddw %%mm2, %%mm3 \n\t" /* Truncated */ \ - " movq %%mm5, %%mm0 \n\t" \ - \ - " movq %%mm5, %%mm2 \n\t" \ - " pmulhw %[xC6S2], %%mm0 \n\t" /* mm0 = xC6S2 * irot_input_x */ \ - \ - " psrlw $15, %%mm2 \n\t" \ - " paddw %%mm2, %%mm0 \n\t" /* Truncated */ \ - \ - " paddsw %%mm0, %%mm3 \n\t" /* ip[2] */ \ - " movq %%mm3," #ip2 " \n\t" /* Save ip2 */ \ - \ - " movq %%mm5, %%mm0 \n\t" \ - " movq %%mm5, %%mm2 \n\t" \ - \ - " pmulhw %[xC2S6], %%mm5 \n\t" /* mm5 = xC2S6 * irot_input_x - irot_input_x */ \ - " psrlw $15, %%mm2 \n\t" \ - \ - " movq " #temp ", %%mm3 \n\t" \ - " paddw %%mm0, %%mm5 \n\t" /* mm5 = xC2S6 * irot_input_x */ \ - \ - " paddw %%mm2, %%mm5 \n\t" /* Truncated */ \ - " movq %%mm3, %%mm2 \n\t" \ - \ - " pmulhw %[xC6S2], %%mm3 \n\t" /* mm3 = xC6S2 * irot_input_y */ \ - " psrlw $15, %%mm2 \n\t" \ - \ - " paddw %%mm2, %%mm3 \n\t" /* Truncated */ \ - " psubsw %%mm5, %%mm3 \n\t" \ - \ - " movq %%mm3," #ip6 " \n\t" \ - /* ------------------------------------------------------------------- */ \ - " movq %[xC4S4], %%mm0 \n\t" \ - " movq %%mm1, %%mm2 \n\t" \ - " movq %%mm1, %%mm3 \n\t" \ - \ - " pmulhw %%mm0, %%mm1 \n\t" /* mm0 = xC4S4 * ( is12 - is56 ) - ( is12 - is56 ) */ \ - " psrlw $15, %%mm2 \n\t" \ - \ - " paddw %%mm3, %%mm1 \n\t" /* mm0 = xC4S4 * ( is12 - is56 ) */ \ - " paddw %%mm2, %%mm1 \n\t" /* Truncate mm1, now it is icommon_product1 */ \ - \ - " movq %%mm7, %%mm2 \n\t" \ - " movq %%mm7, %%mm3 \n\t" \ - \ - " pmulhw %%mm0, %%mm7 \n\t" /* mm7 = xC4S4 * ( id12 + id56 ) - ( id12 + id56 ) */ \ - " psrlw $15, %%mm2 \n\t" \ - \ - " paddw %%mm3, %%mm7 \n\t" /* mm7 = xC4S4 * ( id12 + id56 ) */ \ - " paddw %%mm2, %%mm7 \n\t" /* Truncate mm7, now it is icommon_product2 */ \ - /* ------------------------------------------------------------------- */ \ - " pxor %%mm0, %%mm0 \n\t" /* Clear mm0 */ \ - " psubsw %%mm6, %%mm0 \n\t" /* mm0 = - id34 */ \ - \ - " psubsw %%mm7, %%mm0 \n\t" /* mm0 = - ( id34 + idcommon_product2 ) */ \ - " paddsw %%mm6, %%mm6 \n\t" \ - " paddsw %%mm0, %%mm6 \n\t" /* mm6 = id34 - icommon_product2 */ \ - \ - " psubsw %%mm1, %%mm4 \n\t" /* mm4 = id07 - icommon_product1 */ \ - " paddsw %%mm1, %%mm1 \n\t" \ - " paddsw %%mm4, %%mm1 \n\t" /* mm1 = id07 + icommon_product1 */ \ - /* ------------------------------------------------------------------- */ \ - " movq %[xC1S7], %%mm7 \n\t" \ - " movq %%mm1, %%mm2 \n\t" \ - \ - " movq %%mm1, %%mm3 \n\t" \ - " pmulhw %%mm7, %%mm1 \n\t" /* mm1 = xC1S7 * irot_input_x - irot_input_x */ \ - \ - " movq %[xC7S1], %%mm7 \n\t" \ - " psrlw $15, %%mm2 \n\t" \ - \ - " paddw %%mm3, %%mm1 \n\t" /* mm1 = xC1S7 * irot_input_x */ \ - " paddw %%mm2, %%mm1 \n\t" /* Trucated */ \ - \ - " pmulhw %%mm7, %%mm3 \n\t" /* mm3 = xC7S1 * irot_input_x */ \ - " paddw %%mm2, %%mm3 \n\t" /* Truncated */ \ - \ - " movq %%mm0, %%mm5 \n\t" \ - " movq %%mm0, %%mm2 \n\t" \ - \ - " movq %[xC1S7], %%mm7 \n\t" \ - " pmulhw %%mm7, %%mm0 \n\t" /* mm0 = xC1S7 * irot_input_y - irot_input_y */ \ - \ - " movq %[xC7S1], %%mm7 \n\t" \ - " psrlw $15, %%mm2 \n\t" \ - \ - " paddw %%mm5, %%mm0 \n\t" /* mm0 = xC1S7 * irot_input_y */ \ - " paddw %%mm2, %%mm0 \n\t" /* Truncated */ \ - \ - " pmulhw %%mm7, %%mm5 \n\t" /* mm5 = xC7S1 * irot_input_y */ \ - " paddw %%mm2, %%mm5 \n\t" /* Truncated */ \ - \ - " psubsw %%mm5, %%mm1 \n\t" /* mm1 = xC1S7 * irot_input_x - xC7S1 * irot_input_y = ip1 */ \ - " paddsw %%mm0, %%mm3 \n\t" /* mm3 = xC7S1 * irot_input_x - xC1S7 * irot_input_y = ip7 */ \ - \ - " movq %%mm1," #ip1 " \n\t" \ - " movq %%mm3," #ip7 " \n\t" \ - /* ------------------------------------------------------------------- */ \ - " movq %[xC3S5], %%mm0 \n\t" \ - " movq %[xC5S3], %%mm1 \n\t" \ - \ - " movq %%mm6, %%mm5 \n\t" \ - " movq %%mm6, %%mm7 \n\t" \ - \ - " movq %%mm4, %%mm2 \n\t" \ - " movq %%mm4, %%mm3 \n\t" \ - \ - " pmulhw %%mm0, %%mm4 \n\t" /* mm4 = xC3S5 * irot_input_x - irot_input_x */ \ - " pmulhw %%mm1, %%mm6 \n\t" /* mm6 = xC5S3 * irot_input_y - irot_input_y */ \ - \ - " psrlw $15, %%mm2 \n\t" \ - " psrlw $15, %%mm5 \n\t" \ - \ - " paddw %%mm3, %%mm4 \n\t" /* mm4 = xC3S5 * irot_input_x */ \ - " paddw %%mm7, %%mm6 \n\t" /* mm6 = xC5S3 * irot_input_y */ \ - \ - " paddw %%mm2, %%mm4 \n\t" /* Truncated */ \ - " paddw %%mm5, %%mm6 \n\t" /* Truncated */ \ - \ - " psubsw %%mm6, %%mm4 \n\t" /* ip3 */ \ - " movq %%mm4," #ip3 " \n\t" \ - \ - " movq %%mm3, %%mm4 \n\t" \ - " movq %%mm7, %%mm6 \n\t" \ - \ - " pmulhw %%mm1, %%mm3 \n\t" /* mm3 = xC5S3 * irot_input_x - irot_input_x */ \ - " pmulhw %%mm0, %%mm7 \n\t" /* mm7 = xC3S5 * irot_input_y - irot_input_y */ \ - \ - " paddw %%mm2, %%mm4 \n\t" \ - " paddw %%mm5, %%mm6 \n\t" \ - \ - " paddw %%mm4, %%mm3 \n\t" /* mm3 = xC5S3 * irot_input_x */ \ - " paddw %%mm6, %%mm7 \n\t" /* mm7 = xC3S5 * irot_input_y */ \ - \ - " paddw %%mm7, %%mm3 \n\t" /* ip5 */ \ - " movq %%mm3," #ip5 " \n\t" - -#define Transpose_mmx(ip0,ip1,ip2,ip3,ip4,ip5,ip6,ip7, \ - op0,op1,op2,op3,op4,op5,op6,op7) \ - " movq " #ip0 ", %%mm0 \n\t" /* mm0 = a0 a1 a2 a3 */ \ - " movq " #ip4 ", %%mm4 \n\t" /* mm4 = e4 e5 e6 e7 */ \ - " movq " #ip1 ", %%mm1 \n\t" /* mm1 = b0 b1 b2 b3 */ \ - " movq " #ip5 ", %%mm5 \n\t" /* mm5 = f4 f5 f6 f7 */ \ - " movq " #ip2 ", %%mm2 \n\t" /* mm2 = c0 c1 c2 c3 */ \ - " movq " #ip6 ", %%mm6 \n\t" /* mm6 = g4 g5 g6 g7 */ \ - " movq " #ip3 ", %%mm3 \n\t" /* mm3 = d0 d1 d2 d3 */ \ - " movq %%mm1," #op1 " \n\t" /* save b0 b1 b2 b3 */ \ - " movq " #ip7 ", %%mm7 \n\t" /* mm7 = h0 h1 h2 h3 */ \ - /* Transpose 2x8 block */ \ - " movq %%mm4, %%mm1 \n\t" /* mm1 = e3 e2 e1 e0 */ \ - " punpcklwd %%mm5, %%mm4 \n\t" /* mm4 = f1 e1 f0 e0 */ \ - " movq %%mm0," #op0 " \n\t" /* save a3 a2 a1 a0 */ \ - " punpckhwd %%mm5, %%mm1 \n\t" /* mm1 = f3 e3 f2 e2 */ \ - " movq %%mm6, %%mm0 \n\t" /* mm0 = g3 g2 g1 g0 */ \ - " punpcklwd %%mm7, %%mm6 \n\t" /* mm6 = h1 g1 h0 g0 */ \ - " movq %%mm4, %%mm5 \n\t" /* mm5 = f1 e1 f0 e0 */ \ - " punpckldq %%mm6, %%mm4 \n\t" /* mm4 = h0 g0 f0 e0 = MM4 */ \ - " punpckhdq %%mm6, %%mm5 \n\t" /* mm5 = h1 g1 f1 e1 = MM5 */ \ - " movq %%mm1, %%mm6 \n\t" /* mm6 = f3 e3 f2 e2 */ \ - " movq %%mm4," #op4 " \n\t" \ - " punpckhwd %%mm7, %%mm0 \n\t" /* mm0 = h3 g3 h2 g2 */ \ - " movq %%mm5," #op5 " \n\t" \ - " punpckhdq %%mm0, %%mm6 \n\t" /* mm6 = h3 g3 f3 e3 = MM7 */ \ - " movq " #op0 ", %%mm4 \n\t" /* mm4 = a3 a2 a1 a0 */ \ - " punpckldq %%mm0, %%mm1 \n\t" /* mm1 = h2 g2 f2 e2 = MM6 */ \ - " movq " #op1 ", %%mm5 \n\t" /* mm5 = b3 b2 b1 b0 */ \ - " movq %%mm4, %%mm0 \n\t" /* mm0 = a3 a2 a1 a0 */ \ - " movq %%mm6," #op7 " \n\t" \ - " punpcklwd %%mm5, %%mm0 \n\t" /* mm0 = b1 a1 b0 a0 */ \ - " movq %%mm1," #op6 " \n\t" \ - " punpckhwd %%mm5, %%mm4 \n\t" /* mm4 = b3 a3 b2 a2 */ \ - " movq %%mm2, %%mm5 \n\t" /* mm5 = c3 c2 c1 c0 */ \ - " punpcklwd %%mm3, %%mm2 \n\t" /* mm2 = d1 c1 d0 c0 */ \ - " movq %%mm0, %%mm1 \n\t" /* mm1 = b1 a1 b0 a0 */ \ - " punpckldq %%mm2, %%mm0 \n\t" /* mm0 = d0 c0 b0 a0 = MM0 */ \ - " punpckhdq %%mm2, %%mm1 \n\t" /* mm1 = d1 c1 b1 a1 = MM1 */ \ - " movq %%mm4, %%mm2 \n\t" /* mm2 = b3 a3 b2 a2 */ \ - " movq %%mm0," #op0 " \n\t" \ - " punpckhwd %%mm3, %%mm5 \n\t" /* mm5 = d3 c3 d2 c2 */ \ - " movq %%mm1," #op1 " \n\t" \ - " punpckhdq %%mm5, %%mm4 \n\t" /* mm4 = d3 c3 b3 a3 = MM3 */ \ - " punpckldq %%mm5, %%mm2 \n\t" /* mm2 = d2 c2 b2 a2 = MM2 */ \ - " movq %%mm4," #op3 " \n\t" \ - " movq %%mm2," #op2 " \n\t" - - -/* This performs a 2D Forward DCT on an 8x8 block with short - coefficients. We try to do the truncation to match the C - version. */ -static void fdct_short__mmx ( ogg_int16_t *InputData, ogg_int16_t *OutputData) -{ - ogg_int16_t __attribute__((aligned(8))) temp[8*8]; - - __asm__ __volatile__ ( - " .p2align 4 \n\t" - /* - * Input data is an 8x8 block. To make processing of the data more efficent - * we will transpose the block of data to two 4x8 blocks??? - */ - Transpose_mmx ( (%0), 16(%0), 32(%0), 48(%0), 8(%0), 24(%0), 40(%0), 56(%0), - (%1), 16(%1), 32(%1), 48(%1), 8(%1), 24(%1), 40(%1), 56(%1)) - Fdct_mmx ( (%1), 16(%1), 32(%1), 48(%1), 8(%1), 24(%1), 40(%1), 56(%1), (%2)) - - Transpose_mmx (64(%0), 80(%0), 96(%0),112(%0), 72(%0), 88(%0),104(%0),120(%0), - 64(%1), 80(%1), 96(%1),112(%1), 72(%1), 88(%1),104(%1),120(%1)) - Fdct_mmx (64(%1), 80(%1), 96(%1),112(%1), 72(%1), 88(%1),104(%1),120(%1), (%2)) - - Transpose_mmx ( 0(%1), 16(%1), 32(%1), 48(%1), 64(%1), 80(%1), 96(%1),112(%1), - 0(%1), 16(%1), 32(%1), 48(%1), 64(%1), 80(%1), 96(%1),112(%1)) - Fdct_mmx ( 0(%1), 16(%1), 32(%1), 48(%1), 64(%1), 80(%1), 96(%1),112(%1), (%2)) - - Transpose_mmx ( 8(%1), 24(%1), 40(%1), 56(%1), 72(%1), 88(%1),104(%1),120(%1), - 8(%1), 24(%1), 40(%1), 56(%1), 72(%1), 88(%1),104(%1),120(%1)) - Fdct_mmx ( 8(%1), 24(%1), 40(%1), 56(%1), 72(%1), 88(%1),104(%1),120(%1), (%2)) - - " emms \n\t" - - : "+r" (InputData), - "+r" (OutputData) - : "r" (temp), - [xC1S7] "m" (xC1S7), /* gcc 3.1+ allows named asm parameters */ - [xC2S6] "m" (xC2S6), - [xC3S5] "m" (xC3S5), - [xC4S4] "m" (xC4S4), - [xC5S3] "m" (xC5S3), - [xC6S2] "m" (xC6S2), - [xC7S1] "m" (xC7S1) - : "memory" - ); -} - -/* install our implementation in the function table */ -void dsp_mmx_fdct_init(DspFunctions *funcs) -{ - funcs->fdct_short = fdct_short__mmx; -} - -#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_32/idct_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32/idct_mmx.c deleted file mode 100644 index 5fc6a1f66..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_32/idct_mmx.c +++ /dev/null @@ -1,1452 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: idct_mmx.c 15153 2008-08-04 18:37:55Z tterribe $ - - ********************************************************************/ - -#include "../codec_internal.h" - -#if defined(USE_ASM) - -#define ASM asm - -/**************************************************************************** -* -* Description : IDCT with multiple versions based on # of non 0 coeffs -* -***************************************************************************** -*/ - -// Dequantization + inverse discrete cosine transform. - -// Constants used in MMX implementation of dequantization and idct. -// All the MMX stuff works with 4 16-bit quantities at a time and -// we create 11 constants of size 4 x 16 bits. -// The first 4 are used to mask the individual 16-bit words within a group -// and are used in the address-shuffling part of the dequantization. -// The last 7 are fixed-point approximations to the cosines of angles -// occurring in the DCT; each of these contains 4 copies of the same value. - -// There is only one (statically initialized) instance of this object -// wrapped in an allocator object that forces its starting address -// to be evenly divisible by 32. Hence the actual object occupies 2.75 -// cache lines on a Pentium processor. - -// Offsets in bytes used by the assembler code below -// must of course agree with the idctConstants constructor. - -#define MaskOffset 0 // 4 masks come in order low word to high -#define CosineOffset 32 // 7 cosines come in order pi/16 * (1 ... 7) -#define EightOffset 88 -#define IdctAdjustBeforeShift 8 - -/* -UINT16 idctcosTbl[ 7] = -{ - 64277, 60547, 54491, 46341, 36410, 25080, 12785 -}; - -void fillidctconstants(void) -{ - int j = 16; - UINT16 * p; - do - { - idctconstants[ --j] = 0; - } - while( j); - - idctconstants[0] = idctconstants[5] = idctconstants[10] = idctconstants[15] = 65535; - - j = 1; - do - { - p = idctconstants + ( (j+3) << 2); - p[0] = p[1] = p[2] = p[3] = idctcosTbl[ j - 1]; - } - while( ++j <= 7); - - idctconstants[44] = idctconstants[45] = idctconstants[46] = idctconstants[47] = IdctAdjustBeforeShift; -} -*/ - -ogg_uint16_t idctconstants[(4+7+1) * 4] = { - 65535, 0, 0, 0, 0, 65535, 0, 0, - 0, 0, 65535, 0, 0, 0, 0, 65535, - 64277, 64277, 64277, 64277, 60547, 60547, 60547, 60547, - 54491, 54491, 54491, 54491, 46341, 46341, 46341, 46341, - 36410, 36410, 36410, 36410, 25080, 25080, 25080, 25080, - 12785, 12785, 12785, 12785, 8, 8, 8, 8, -}; - -/* Dequantization + inverse DCT. - - Dequantization multiplies user's 16-bit signed indices (range -512 to +511) - by unsigned 16-bit quantization table entries. - These table entries are upscaled by 4, max is 30 * 128 * 4 < 2^14. - Result is scaled signed DCT coefficients (abs value < 2^15). - - In the data stream, the coefficients are sent in order of increasing - total (horizontal + vertical) frequency. The exact picture is as follows: - - 00 01 05 06 16 17 33 34 - 02 04 07 15 20 32 35 52 - 03 10 14 21 31 36 51 53 - 11 13 22 30 37 50 54 65 - - 12 23 27 40 47 55 64 66 - 24 26 41 46 56 63 67 74 - 25 42 45 57 62 70 73 75 - 43 44 60 61 71 72 76 77 - - Here the position in the matrix corresponds to the (horiz,vert) - freqency indices and the octal entry in the matrix is the position - of the coefficient in the data stream. Thus the coefficients are sent - in sort of a diagonal "snake". - - The dequantization stage "uncurls the snake" and stores the expanded - coefficients in more convenient positions. These are not exactly the - natural positions given above but take into account our implementation - of the idct, which basically requires two one-dimensional idcts and - two transposes. - - We fold the first transpose into the storage of the expanded coefficients. - We don't actually do a full transpose because this would require doubling - the size of the idct buffer; rather, we just transpose each of the 4x4 - subblocks. Using slightly varying addressing schemes in each of the - four 4x8 idcts then allows these transforms to be done in place. - - Transposing the 4x4 subblocks in the matrix above gives - - 00 02 03 11 16 20 31 37 - 01 04 10 13 17 32 36 50 - 05 07 14 22 33 35 51 54 - 06 15 21 30 34 52 53 65 - - 12 24 25 43 47 56 62 71 - 23 26 42 44 55 63 70 72 - 27 41 45 60 64 67 73 76 - 40 46 57 61 66 74 75 77 - - Finally, we reverse the words in each 4 word group to clarify - direction of shifts. - - 11 03 02 00 37 31 20 16 - 13 10 04 01 50 36 32 17 - 22 14 07 05 54 51 35 33 - 30 21 15 06 65 53 52 34 - - 43 25 24 12 71 62 56 47 - 44 42 26 23 72 70 63 55 - 60 45 41 27 76 73 67 64 - 61 57 46 40 77 75 74 66 - - This matrix then shows the 16 4x16 destination words in terms of - the 16 4x16 input words. - - We implement this algorithm by manipulation of mmx registers, - which seems to be the fastest way to proceed. It is completely - hand-written; there does not seem to be enough recurrence to - reasonably compartmentalize any of it. Hence the resulting - program is ugly and bloated. Furthermore, due to the absence of - register pressure, it is boring and artless. I hate it. - - The idct itself is more interesting. Since the two-dimensional dct - basis functions are products of the one-dimesional dct basis functions, - we can compute an inverse (or forward) dct via two 1-D transforms, - on rows then on columns. To exploit MMX parallelism, we actually do - both operations on columns, interposing a (partial) transpose between - the two 1-D transforms, the first transpose being done by the expansion - described above. - - The 8-sample one-dimensional DCT is a standard orthogonal expansion using - the (unnormalized) basis functions - - b[k]( i) = cos( pi * k * (2i + 1) / 16); - - here k = 0 ... 7 is the frequency and i = 0 ... 7 is the spatial coordinate. - To normalize, b[0] should be multiplied by 1/sqrt( 8) and the other b[k] - should be multiplied by 1/2. - - The 8x8 two-dimensional DCT is just the product of one-dimensional DCTs - in each direction. The (unnormalized) basis functions are - - B[k,l]( i, j) = b[k]( i) * b[l]( j); - - this time k and l are the horizontal and vertical frequencies, - i and j are the horizontal and vertical spatial coordinates; - all indices vary from 0 ... 7 (as above) - and there are now 4 cases of normalization. - - Our 1-D idct expansion uses constants C1 ... C7 given by - - (*) Ck = C(-k) = cos( pi * k/16) = S(8-k) = -S(k-8) = sin( pi * (8-k)/16) - - and the following 1-D algorithm transforming I0 ... I7 to R0 ... R7 : - - A = (C1 * I1) + (C7 * I7) B = (C7 * I1) - (C1 * I7) - C = (C3 * I3) + (C5 * I5) D = (C3 * I5) - (C5 * I3) - A. = C4 * (A - C) B. = C4 * (B - D) - C. = A + C D. = B + D - - E = C4 * (I0 + I4) F = C4 * (I0 - I4) - G = (C2 * I2) + (C6 * I6) H = (C6 * I2) - (C2 * I6) - E. = E - G - G. = E + G - - A.. = F + A. B.. = B. - H - F. = F - A. H. = B. + H - - R0 = G. + C. R1 = A.. + H. R3 = E. + D. R5 = F. + B.. - R7 = G. - C. R2 = A.. - H. R4 = E. - D. R6 = F. - B.. - - It is due to Vetterli and Lightenberg and may be found in the JPEG - reference book by Pennebaker and Mitchell. - - Correctness of the algorithm follows from (*) together with the - addition formulas for sine and cosine: - - cos( A + B) = cos( A) * cos( B) - sin( A) * sin( B) - sin( A + B) = sin( A) * cos( B) + cos( A) * sin( B) - - Note that this implementation absorbs the difference in normalization - between the 0th and higher frequencies, although the results produced - are actually twice as big as they should be. Since we do this for each - dimension, the 2-D idct results are 4x the desired results. Finally, - taking into account that the dequantization multiplies by 4 as well, - our actual results are 16x too big. We fix this by shifting the final - results right by 4 bits. - - High precision version approximates C1 ... C7 to 16 bits. - Since MMX only provides a signed multiply, C1 ... C5 appear to be - negative and multiplies involving them must be adjusted to compensate - for this. C6 and C7 do not require this adjustment since - they are < 1/2 and are correctly treated as positive numbers. - - Following macro does four 8-sample one-dimensional idcts in parallel. - This is actually not such a difficult program to write once you - make a couple of observations (I of course was unable to make these - observations until I'd half-written a couple of other versions). - - 1. Everything is easy once you are done with the multiplies. - This is because, given X and Y in registers, one may easily - calculate X+Y and X-Y using just those 2 registers. - - 2. You always need at least 2 extra registers to calculate products, - so storing 2 temporaries is inevitable. C. and D. seem to be - the best candidates. - - 3. The products should be calculated in decreasing order of complexity - (which translates into register pressure). Since C1 ... C5 require - adjustment (and C6, C7 do not), we begin by calculating C and D. -*/ - -/************************************************************************************** - * - * Routine: BeginIDCT - * - * Description: The Macro does IDct on 4 1-D Dcts - * - * Input: None - * - * Output: None - * - * Return: None - * - * Special Note: None - * - * Error: None - * - *************************************************************************************** - */ - -#define MtoSTR(s) #s - -#define Dump "call MMX_dump\n" - -#define BeginIDCT "#BeginIDCT\n" \ - \ - " movq " I(3)","r2"\n" \ - \ - " movq " C(3)","r6"\n" \ - " movq " r2","r4"\n" \ - " movq " J(5)","r7"\n" \ - " pmulhw " r6","r4"\n" \ - " movq " C(5)","r1"\n" \ - " pmulhw " r7","r6"\n" \ - " movq " r1","r5"\n" \ - " pmulhw " r2","r1"\n" \ - " movq " I(1)","r3"\n" \ - " pmulhw " r7","r5"\n" \ - " movq " C(1)","r0"\n" \ - " paddw " r2","r4"\n" \ - " paddw " r7","r6"\n" \ - " paddw " r1","r2"\n" \ - " movq " J(7)","r1"\n" \ - " paddw " r5","r7"\n" \ - " movq " r0","r5"\n" \ - " pmulhw " r3","r0"\n" \ - " paddsw " r7","r4"\n" \ - " pmulhw " r1","r5"\n" \ - " movq " C(7)","r7"\n" \ - " psubsw " r2","r6"\n" \ - " paddw " r3","r0"\n" \ - " pmulhw " r7","r3"\n" \ - " movq " I(2)","r2"\n" \ - " pmulhw " r1","r7"\n" \ - " paddw " r1","r5"\n" \ - " movq " r2","r1"\n" \ - " pmulhw " C(2)","r2"\n" \ - " psubsw " r5","r3"\n" \ - " movq " J(6)","r5"\n" \ - " paddsw " r7","r0"\n" \ - " movq " r5","r7"\n" \ - " psubsw " r4","r0"\n" \ - " pmulhw " C(2)","r5"\n" \ - " paddw " r1","r2"\n" \ - " pmulhw " C(6)","r1"\n" \ - " paddsw " r4","r4"\n" \ - " paddsw " r0","r4"\n" \ - " psubsw " r6","r3"\n" \ - " paddw " r7","r5"\n" \ - " paddsw " r6","r6"\n" \ - " pmulhw " C(6)","r7"\n" \ - " paddsw " r3","r6"\n" \ - " movq " r4","I(1)"\n" \ - " psubsw " r5","r1"\n" \ - " movq " C(4)","r4"\n" \ - " movq " r3","r5"\n" \ - " pmulhw " r4","r3"\n" \ - " paddsw " r2","r7"\n" \ - " movq " r6","I(2)"\n" \ - " movq " r0","r2"\n" \ - " movq " I(0)","r6"\n" \ - " pmulhw " r4","r0"\n" \ - " paddw " r3","r5"\n" \ - "\n" \ - " movq " J(4)","r3"\n" \ - " psubsw " r1","r5"\n" \ - " paddw " r0","r2"\n" \ - " psubsw " r3","r6"\n" \ - " movq " r6","r0"\n" \ - " pmulhw " r4","r6"\n" \ - " paddsw " r3","r3"\n" \ - " paddsw " r1","r1"\n" \ - " paddsw " r0","r3"\n" \ - " paddsw " r5","r1"\n" \ - " pmulhw " r3","r4"\n" \ - " paddsw " r0","r6"\n" \ - " psubsw " r2","r6"\n" \ - " paddsw " r2","r2"\n" \ - " movq " I(1)","r0"\n" \ - " paddsw " r6","r2"\n" \ - " paddw " r3","r4"\n" \ - " psubsw " r1","r2"\n" \ - "#end BeginIDCT\n" -// end BeginIDCT macro (38 cycles). - - -// Two versions of the end of the idct depending on whether we're feeding -// into a transpose or dividing the final results by 16 and storing them. - -/************************************************************************************** - * - * Routine: RowIDCT - * - * Description: The Macro does 1-D IDct on 4 Rows - * - * Input: None - * - * Output: None - * - * Return: None - * - * Special Note: None - * - * Error: None - * - *************************************************************************************** - */ - -// RowIDCT gets ready to transpose. - -#define RowIDCT ASM("\n"\ - "#RowIDCT\n" \ - BeginIDCT \ - "\n" \ - " movq "I(2)","r3"\n" /* r3 = D. */ \ - " psubsw "r7","r4"\n" /* r4 = E. = E - G */ \ - " paddsw "r1","r1"\n" /* r1 = H. + H. */ \ - " paddsw "r7","r7"\n" /* r7 = G + G */ \ - " paddsw "r2","r1"\n" /* r1 = R1 = A.. + H. */\ - " paddsw "r4","r7"\n" /* r7 = G. = E + G */ \ - " psubsw "r3","r4"\n" /* r4 = R4 = E. - D. */ \ - " paddsw "r3","r3"\n" \ - " psubsw "r5","r6"\n" /* r6 = R6 = F. - B.. */\ - " paddsw "r5","r5"\n" \ - " paddsw "r4","r3"\n" /* r3 = R3 = E. + D. */ \ - " paddsw "r6","r5"\n" /* r5 = R5 = F. + B.. */\ - " psubsw "r0","r7"\n" /* r7 = R7 = G. - C. */ \ - " paddsw "r0","r0"\n" \ - " movq "r1","I(1)"\n" /* save R1 */ \ - " paddsw "r7","r0"\n" /* r0 = R0 = G. + C. */ \ - "#end RowIDCT" \ -); -// end RowIDCT macro (8 + 38 = 46 cycles) - - -/************************************************************************************** - * - * Routine: ColumnIDCT - * - * Description: The Macro does 1-D IDct on 4 columns - * - * Input: None - * - * Output: None - * - * Return: None - * - * Special Note: None - * - * Error: None - * - *************************************************************************************** - */ -// Column IDCT normalizes and stores final results. - -#define ColumnIDCT ASM("\n" \ - "#ColumnIDCT\n" \ - BeginIDCT \ - "\n" \ - " paddsw "Eight","r2"\n" \ - " paddsw "r1","r1"\n" /* r1 = H. + H. */ \ - " paddsw "r2","r1"\n" /* r1 = R1 = A.. + H. */\ - " psraw ""$4"","r2"\n" /* r2 = NR2 */ \ - " psubsw "r7","r4"\n" /* r4 = E. = E - G */ \ - " psraw ""$4"","r1"\n" /* r1 = NR1 */ \ - " movq "I(2)","r3"\n" /* r3 = D. */ \ - " paddsw "r7","r7"\n" /* r7 = G + G */ \ - " movq "r2","I(2)"\n" /* store NR2 at I2 */ \ - " paddsw "r4","r7"\n" /* r7 = G. = E + G */ \ - " movq "r1","I(1)"\n" /* store NR1 at I1 */ \ - " psubsw "r3","r4"\n" /* r4 = R4 = E. - D. */ \ - " paddsw "Eight","r4"\n" \ - " paddsw "r3","r3"\n" /* r3 = D. + D. */ \ - " paddsw "r4","r3"\n" /* r3 = R3 = E. + D. */ \ - " psraw ""$4"","r4"\n" /* r4 = NR4 */ \ - " psubsw "r5","r6"\n" /* r6 = R6 = F. - B.. */\ - " psraw ""$4"","r3"\n" /* r3 = NR3 */ \ - " paddsw "Eight","r6"\n" \ - " paddsw "r5","r5"\n" /* r5 = B.. + B.. */ \ - " paddsw "r6","r5"\n" /* r5 = R5 = F. + B.. */\ - " psraw ""$4"","r6"\n" /* r6 = NR6 */ \ - " movq "r4","J(4)"\n" /* store NR4 at J4 */ \ - " psraw ""$4"","r5"\n" /* r5 = NR5 */ \ - " movq "r3","I(3)"\n" /* store NR3 at I3 */ \ - " psubsw "r0","r7"\n" /* r7 = R7 = G. - C. */ \ - " paddsw "Eight","r7"\n" \ - " paddsw "r0","r0"\n" /* r0 = C. + C. */ \ - " paddsw "r7","r0"\n" /* r0 = R0 = G. + C. */ \ - " psraw ""$4"","r7"\n" /* r7 = NR7 */ \ - " movq "r6","J(6)"\n" /* store NR6 at J6 */ \ - " psraw ""$4"","r0"\n" /* r0 = NR0 */ \ - " movq "r5","J(5)"\n" /* store NR5 at J5 */ \ - " movq "r7","J(7)"\n" /* store NR7 at J7 */ \ - " movq "r0","I(0)"\n" /* store NR0 at I0 */ \ - "#end ColumnIDCT\n" \ -); -// end ColumnIDCT macro (38 + 19 = 57 cycles) - -/************************************************************************************** - * - * Routine: Transpose - * - * Description: The Macro does two 4x4 transposes in place. - * - * Input: None - * - * Output: None - * - * Return: None - * - * Special Note: None - * - * Error: None - * - *************************************************************************************** - */ - -/* Following macro does two 4x4 transposes in place. - - At entry (we assume): - - r0 = a3 a2 a1 a0 - I(1) = b3 b2 b1 b0 - r2 = c3 c2 c1 c0 - r3 = d3 d2 d1 d0 - - r4 = e3 e2 e1 e0 - r5 = f3 f2 f1 f0 - r6 = g3 g2 g1 g0 - r7 = h3 h2 h1 h0 - - At exit, we have: - - I(0) = d0 c0 b0 a0 - I(1) = d1 c1 b1 a1 - I(2) = d2 c2 b2 a2 - I(3) = d3 c3 b3 a3 - - J(4) = h0 g0 f0 e0 - J(5) = h1 g1 f1 e1 - J(6) = h2 g2 f2 e2 - J(7) = h3 g3 f3 e3 - - I(0) I(1) I(2) I(3) is the transpose of r0 I(1) r2 r3. - J(4) J(5) J(6) J(7) is the transpose of r4 r5 r6 r7. - - Since r1 is free at entry, we calculate the Js first. */ - - -#define Transpose ASM("\n#Transpose\n" \ - \ - " movq "r4","r1"\n" \ - " punpcklwd "r5","r4"\n" \ - " movq "r0","I(0)"\n" \ - " punpckhwd "r5","r1"\n" \ - " movq "r6","r0"\n" \ - " punpcklwd "r7","r6"\n" \ - " movq "r4","r5"\n" \ - " punpckldq "r6","r4"\n" \ - " punpckhdq "r6","r5"\n" \ - " movq "r1","r6"\n" \ - " movq "r4","J(4)"\n" \ - " punpckhwd "r7","r0"\n" \ - " movq "r5","J(5)"\n" \ - " punpckhdq "r0","r6"\n" \ - " movq "I(0)","r4"\n" \ - " punpckldq "r0","r1"\n" \ - " movq "I(1)","r5"\n" \ - " movq "r4","r0"\n" \ - " movq "r6","J(7)"\n" \ - " punpcklwd "r5","r0"\n" \ - " movq "r1","J(6)"\n" \ - " punpckhwd "r5","r4"\n" \ - " movq "r2","r5"\n" \ - " punpcklwd "r3","r2"\n" \ - " movq "r0","r1"\n" \ - " punpckldq "r2","r0"\n" \ - " punpckhdq "r2","r1"\n" \ - " movq "r4","r2"\n" \ - " movq "r0","I(0)"\n" \ - " punpckhwd "r3","r5"\n" \ - " movq "r1","I(1)"\n" \ - " punpckhdq "r5","r4"\n" \ - " punpckldq "r5","r2"\n" \ - \ - " movq "r4","I(3)"\n" \ - \ - " movq "r2","I(2)"\n" \ - "#end Transpose\n" \ -); -// end Transpose macro (19 cycles). - -/* -static void MMX_dump() -{ - ASM - ("\ - movq %mm0,(%edi)\n\ - movq %mm1,8(%edi)\n\ - movq %mm2,16(%edi)\n\ - movq %mm3,24(%edi)\n\ - movq %mm4,32(%edi)\n\ - movq %mm5,40(%edi)\n\ - movq %mm6,48(%edi)\n\ - movq %mm7,56(%edi)\n\ - ret" - ); -} -*/ - -/************************************************************************************** - * - * Routine: MMX_idct - * - * Description: Perform IDCT on a 8x8 block - * - * Input: Pointer to input and output buffer - * - * Output: None - * - * Return: None - * - * Special Note: The input coefficients are in ZigZag order - * - * Error: None - * - *************************************************************************************** - */ -void IDctSlow__mmx( Q_LIST_ENTRY * InputData, - ogg_int16_t *QuantMatrix, - ogg_int16_t * OutputData ) { - -# define MIDM(M,I) MtoSTR(M+I*8(%ecx)) -# define M(I) MIDM( MaskOffset , I ) -# define MIDC(M,I) MtoSTR(M+(I-1)*8(%ecx)) -# define C(I) MIDC( CosineOffset , I ) -# define MIDEight(M) MtoSTR(M(%ecx)) -# define Eight MIDEight(EightOffset) - -# define r0 "%mm0" -# define r1 "%mm1" -# define r2 "%mm2" -# define r3 "%mm3" -# define r4 "%mm4" -# define r5 "%mm5" -# define r6 "%mm6" -# define r7 "%mm7" - - __asm__ __volatile__ ( - /* eax = quantized input */ - /* esi = quantization table */ - /* edx = destination (= idct buffer) */ - /* ecx = idctconstants */ - "" - : - :"a"(InputData), "S"(QuantMatrix), "d"(OutputData), "c"(idctconstants) - ); - - ASM( - "movq (%eax), "r0"\n" - "pmullw (%esi), "r0"\n" /* r0 = 03 02 01 00 */ - "movq 16(%eax), "r1"\n" - "pmullw 16(%esi), "r1"\n" /* r1 = 13 12 11 10 */ - "movq "M(0)", "r2"\n" /* r2 = __ __ __ FF */ - "movq "r0", "r3"\n" /* r3 = 03 02 01 00 */ - "movq 8(%eax), "r4"\n" - "psrlq $16, "r0"\n" /* r0 = __ 03 02 01 */ - "pmullw 8(%esi), "r4"\n" /* r4 = 07 06 05 04 */ - "pand "r2", "r3"\n" /* r3 = __ __ __ 00 */ - "movq "r0", "r5"\n" /* r5 = __ 03 02 01 */ - "movq "r1", "r6"\n" /* r6 = 13 12 11 10 */ - "pand "r2", "r5"\n" /* r5 = __ __ __ 01 */ - "psllq $32, "r6"\n" /* r6 = 11 10 __ __ */ - "movq "M(3)", "r7"\n" /* r7 = FF __ __ __ */ - "pxor "r5", "r0"\n" /* r0 = __ 03 02 __ */ - "pand "r6", "r7"\n" /* r7 = 11 __ __ __ */ - "por "r3", "r0"\n" /* r0 = __ 03 02 00 */ - "pxor "r7", "r6"\n" /* r6 = __ 10 __ __ */ - "por "r7", "r0"\n" /* r0 = 11 03 02 00 = R0 */ - "movq "M(3)", "r7"\n" /* r7 = FF __ __ __ */ - "movq "r4", "r3"\n" /* r3 = 07 06 05 04 */ - "movq "r0", (%edx)\n" /* write R0 = r0 */ - "pand "r2", "r3"\n" /* r3 = __ __ __ 04 */ - "movq 32(%eax), "r0"\n" - "psllq $16, "r3"\n" /* r3 = __ __ 04 __ */ - "pmullw 32(%esi), "r0"\n" /* r0 = 23 22 21 20 */ - "pand "r1", "r7"\n" /* r7 = 13 __ __ __ */ - "por "r3", "r5"\n" /* r5 = __ __ 04 01 */ - "por "r6", "r7"\n" /* r7 = 13 10 __ __ */ - "movq 24(%eax), "r3"\n" - "por "r5", "r7"\n" /* r7 = 13 10 04 01 = R1 */ - "pmullw 24(%esi), "r3"\n" /* r3 = 17 16 15 14 */ - "psrlq $16, "r4"\n" /* r4 = __ 07 06 05 */ - "movq "r7", 16(%edx)\n" /* write R1 = r7 */ - "movq "r4", "r5"\n" /* r5 = __ 07 06 05 */ - "movq "r0", "r7"\n" /* r7 = 23 22 21 20 */ - "psrlq $16, "r4"\n" /* r4 = __ __ 07 06 */ - "psrlq $48, "r7"\n" /* r7 = __ __ __ 23 */ - "movq "r2", "r6"\n" /* r6 = __ __ __ FF */ - "pand "r2", "r5"\n" /* r5 = __ __ __ 05 */ - "pand "r4", "r6"\n" /* r6 = __ __ __ 06 */ - "movq "r7", 80(%edx)\n" /* partial R9 = __ __ __ 23 */ - "pxor "r6", "r4"\n" /* r4 = __ __ 07 __ */ - "psrlq $32, "r1"\n" /* r1 = __ __ 13 12 */ - "por "r5", "r4"\n" /* r4 = __ __ 07 05 */ - "movq "M(3)", "r7"\n" /* r7 = FF __ __ __ */ - "pand "r2", "r1"\n" /* r1 = __ __ __ 12 */ - "movq 48(%eax), "r5"\n" - "psllq $16, "r0"\n" /* r0 = 22 21 20 __ */ - "pmullw 48(%esi), "r5"\n" /* r5 = 33 32 31 30 */ - "pand "r0", "r7"\n" /* r7 = 22 __ __ __ */ - "movq "r1", 64(%edx)\n" /* partial R8 = __ __ __ 12 */ - "por "r4", "r7"\n" /* r7 = 22 __ 07 05 */ - "movq "r3", "r4"\n" /* r4 = 17 16 15 14 */ - "pand "r2", "r3"\n" /* r3 = __ __ __ 14 */ - "movq "M(2)", "r1"\n" /* r1 = __ FF __ __ */ - "psllq $32, "r3"\n" /* r3 = __ 14 __ __ */ - "por "r3", "r7"\n" /* r7 = 22 14 07 05 = R2 */ - "movq "r5", "r3"\n" /* r3 = 33 32 31 30 */ - "psllq $48, "r3"\n" /* r3 = 30 __ __ __ */ - "pand "r0", "r1"\n" /* r1 = __ 21 __ __ */ - "movq "r7", 32(%edx)\n" /* write R2 = r7 */ - "por "r3", "r6"\n" /* r6 = 30 __ __ 06 */ - "movq "M(1)", "r7"\n" /* r7 = __ __ FF __ */ - "por "r1", "r6"\n" /* r6 = 30 21 __ 06 */ - "movq 56(%eax), "r1"\n" - "pand "r4", "r7"\n" /* r7 = __ __ 15 __ */ - "pmullw 56(%esi), "r1"\n" /* r1 = 37 36 35 34 */ - "por "r6", "r7"\n" /* r7 = 30 21 15 06 = R3 */ - "pand "M(1)", "r0"\n" /* r0 = __ __ 20 __ */ - "psrlq $32, "r4"\n" /* r4 = __ __ 17 16 */ - "movq "r7", 48(%edx)\n" /* write R3 = r7 */ - "movq "r4", "r6"\n" /* r6 = __ __ 17 16 */ - "movq "M(3)", "r7"\n" /* r7 = FF __ __ __ */ - "pand "r2", "r4"\n" /* r4 = __ __ __ 16 */ - "movq "M(1)", "r3"\n" /* r3 = __ __ FF __ */ - "pand "r1", "r7"\n" /* r7 = 37 __ __ __ */ - "pand "r5", "r3"\n" /* r3 = __ __ 31 __ */ - "por "r4", "r0"\n" /* r0 = __ __ 20 16 */ - "psllq $16, "r3"\n" /* r3 = __ 31 __ __ */ - "por "r0", "r7"\n" /* r7 = 37 __ 20 16 */ - "movq "M(2)", "r4"\n" /* r4 = __ FF __ __ */ - "por "r3", "r7"\n" /* r7 = 37 31 20 16 = R4 */ - "movq 80(%eax), "r0"\n" - "movq "r4", "r3"\n" /* r3 = __ __ FF __ */ - "pmullw 80(%esi), "r0"\n" /* r0 = 53 52 51 50 */ - "pand "r5", "r4"\n" /* r4 = __ 32 __ __ */ - "movq "r7", 8(%edx)\n" /* write R4 = r7 */ - "por "r4", "r6"\n" /* r6 = __ 32 17 16 */ - "movq "r3", "r4"\n" /* r4 = __ FF __ __ */ - "psrlq $16, "r6"\n" /* r6 = __ __ 32 17 */ - "movq "r0", "r7"\n" /* r7 = 53 52 51 50 */ - "pand "r1", "r4"\n" /* r4 = __ 36 __ __ */ - "psllq $48, "r7"\n" /* r7 = 50 __ __ __ */ - "por "r4", "r6"\n" /* r6 = __ 36 32 17 */ - "movq 88(%eax), "r4"\n" - "por "r6", "r7"\n" /* r7 = 50 36 32 17 = R5 */ - "pmullw 88(%esi), "r4"\n" /* r4 = 57 56 55 54 */ - "psrlq $16, "r3"\n" /* r3 = __ __ FF __ */ - "movq "r7", 24(%edx)\n" /* write R5 = r7 */ - "pand "r1", "r3"\n" /* r3 = __ __ 35 __ */ - "psrlq $48, "r5"\n" /* r5 = __ __ __ 33 */ - "pand "r2", "r1"\n" /* r1 = __ __ __ 34 */ - "movq 104(%eax), "r6"\n" - "por "r3", "r5"\n" /* r5 = __ __ 35 33 */ - "pmullw 104(%esi), "r6"\n" /* r6 = 67 66 65 64 */ - "psrlq $16, "r0"\n" /* r0 = __ 53 52 51 */ - "movq "r4", "r7"\n" /* r7 = 57 56 55 54 */ - "movq "r2", "r3"\n" /* r3 = __ __ __ FF */ - "psllq $48, "r7"\n" /* r7 = 54 __ __ __ */ - "pand "r0", "r3"\n" /* r3 = __ __ __ 51 */ - "pxor "r3", "r0"\n" /* r0 = __ 53 52 __ */ - "psllq $32, "r3"\n" /* r3 = __ 51 __ __ */ - "por "r5", "r7"\n" /* r7 = 54 __ 35 33 */ - "movq "r6", "r5"\n" /* r5 = 67 66 65 64 */ - "pand "M(1)", "r6"\n" /* r6 = __ __ 65 __ */ - "por "r3", "r7"\n" /* r7 = 54 51 35 33 = R6 */ - "psllq $32, "r6"\n" /* r6 = 65 __ __ __ */ - "por "r1", "r0"\n" /* r0 = __ 53 52 34 */ - "movq "r7", 40(%edx)\n" /* write R6 = r7 */ - "por "r6", "r0"\n" /* r0 = 65 53 52 34 = R7 */ - "movq 120(%eax), "r7"\n" - "movq "r5", "r6"\n" /* r6 = 67 66 65 64 */ - "pmullw 120(%esi), "r7"\n" /* r7 = 77 76 75 74 */ - "psrlq $32, "r5"\n" /* r5 = __ __ 67 66 */ - "pand "r2", "r6"\n" /* r6 = __ __ __ 64 */ - "movq "r5", "r1"\n" /* r1 = __ __ 67 66 */ - "movq "r0", 56(%edx)\n" /* write R7 = r0 */ - "pand "r2", "r1"\n" /* r1 = __ __ __ 66 */ - "movq 112(%eax), "r0"\n" - "movq "r7", "r3"\n" /* r3 = 77 76 75 74 */ - "pmullw 112(%esi), "r0"\n" /* r0 = 73 72 71 70 */ - "psllq $16, "r3"\n" /* r3 = 76 75 74 __ */ - "pand "M(3)", "r7"\n" /* r7 = 77 __ __ __ */ - "pxor "r1", "r5"\n" /* r5 = __ __ 67 __ */ - "por "r5", "r6"\n" /* r6 = __ __ 67 64 */ - "movq "r3", "r5"\n" /* r5 = 76 75 74 __ */ - "pand "M(3)", "r5"\n" /* r5 = 76 __ __ __ */ - "por "r1", "r7"\n" /* r7 = 77 __ __ 66 */ - "movq 96(%eax), "r1"\n" - "pxor "r5", "r3"\n" /* r3 = __ 75 74 __ */ - "pmullw 96(%esi), "r1"\n" /* r1 = 63 62 61 60 */ - "por "r3", "r7"\n" /* r7 = 77 75 74 66 = R15 */ - "por "r5", "r6"\n" /* r6 = 76 __ 67 64 */ - "movq "r0", "r5"\n" /* r5 = 73 72 71 70 */ - "movq "r7", 120(%edx)\n" /* store R15 = r7 */ - "psrlq $16, "r5"\n" /* r5 = __ 73 72 71 */ - "pand "M(2)", "r5"\n" /* r5 = __ 73 __ __ */ - "movq "r0", "r7"\n" /* r7 = 73 72 71 70 */ - "por "r5", "r6"\n" /* r6 = 76 73 67 64 = R14 */ - "pand "r2", "r0"\n" /* r0 = __ __ __ 70 */ - "pxor "r0", "r7"\n" /* r7 = 73 72 71 __ */ - "psllq $32, "r0"\n" /* r0 = __ 70 __ __ */ - "movq "r6", 104(%edx)\n" /* write R14 = r6 */ - "psrlq $16, "r4"\n" /* r4 = __ 57 56 55 */ - "movq 72(%eax), "r5"\n" - "psllq $16, "r7"\n" /* r7 = 72 71 __ __ */ - "pmullw 72(%esi), "r5"\n" /* r5 = 47 46 45 44 */ - "movq "r7", "r6"\n" /* r6 = 72 71 __ __ */ - "movq "M(2)", "r3"\n" /* r3 = __ FF __ __ */ - "psllq $16, "r6"\n" /* r6 = 71 __ __ __ */ - "pand "M(3)", "r7"\n" /* r7 = 72 __ __ __ */ - "pand "r1", "r3"\n" /* r3 = __ 62 __ __ */ - "por "r0", "r7"\n" /* r7 = 72 70 __ __ */ - "movq "r1", "r0"\n" /* r0 = 63 62 61 60 */ - "pand "M(3)", "r1"\n" /* r1 = 63 __ __ __ */ - "por "r3", "r6"\n" /* r6 = 71 62 __ __ */ - "movq "r4", "r3"\n" /* r3 = __ 57 56 55 */ - "psrlq $32, "r1"\n" /* r1 = __ __ 63 __ */ - "pand "r2", "r3"\n" /* r3 = __ __ __ 55 */ - "por "r1", "r7"\n" /* r7 = 72 70 63 __ */ - "por "r3", "r7"\n" /* r7 = 72 70 63 55 = R13 */ - "movq "r4", "r3"\n" /* r3 = __ 57 56 55 */ - "pand "M(1)", "r3"\n" /* r3 = __ __ 56 __ */ - "movq "r5", "r1"\n" /* r1 = 47 46 45 44 */ - "movq "r7", 88(%edx)\n" /* write R13 = r7 */ - "psrlq $48, "r5"\n" /* r5 = __ __ __ 47 */ - "movq 64(%eax), "r7"\n" - "por "r3", "r6"\n" /* r6 = 71 62 56 __ */ - "pmullw 64(%esi), "r7"\n" /* r7 = 43 42 41 40 */ - "por "r5", "r6"\n" /* r6 = 71 62 56 47 = R12 */ - "pand "M(2)", "r4"\n" /* r4 = __ 57 __ __ */ - "psllq $32, "r0"\n" /* r0 = 61 60 __ __ */ - "movq "r6", 72(%edx)\n" /* write R12 = r6 */ - "movq "r0", "r6"\n" /* r6 = 61 60 __ __ */ - "pand "M(3)", "r0"\n" /* r0 = 61 __ __ __ */ - "psllq $16, "r6"\n" /* r6 = 60 __ __ __ */ - "movq 40(%eax), "r5"\n" - "movq "r1", "r3"\n" /* r3 = 47 46 45 44 */ - "pmullw 40(%esi), "r5"\n" /* r5 = 27 26 25 24 */ - "psrlq $16, "r1"\n" /* r1 = __ 47 46 45 */ - "pand "M(1)", "r1"\n" /* r1 = __ __ 46 __ */ - "por "r4", "r0"\n" /* r0 = 61 57 __ __ */ - "pand "r7", "r2"\n" /* r2 = __ __ __ 40 */ - "por "r1", "r0"\n" /* r0 = 61 57 46 __ */ - "por "r2", "r0"\n" /* r0 = 61 57 46 40 = R11 */ - "psllq $16, "r3"\n" /* r3 = 46 45 44 __ */ - "movq "r3", "r4"\n" /* r4 = 46 45 44 __ */ - "movq "r5", "r2"\n" /* r2 = 27 26 25 24 */ - "movq "r0", 112(%edx)\n" /* write R11 = r0 */ - "psrlq $48, "r2"\n" /* r2 = __ __ __ 27 */ - "pand "M(2)", "r4"\n" /* r4 = __ 45 __ __ */ - "por "r2", "r6"\n" /* r6 = 60 __ __ 27 */ - "movq "M(1)", "r2"\n" /* r2 = __ __ FF __ */ - "por "r4", "r6"\n" /* r6 = 60 45 __ 27 */ - "pand "r7", "r2"\n" /* r2 = __ __ 41 __ */ - "psllq $32, "r3"\n" /* r3 = 44 __ __ __ */ - "por 80(%edx), "r3"\n" /* r3 = 44 __ __ 23 */ - "por "r2", "r6"\n" /* r6 = 60 45 41 27 = R10 */ - "movq "M(3)", "r2"\n" /* r2 = FF __ __ __ */ - "psllq $16, "r5"\n" /* r5 = 26 25 24 __ */ - "movq "r6", 96(%edx)\n" /* store R10 = r6 */ - "pand "r5", "r2"\n" /* r2 = 26 __ __ __ */ - "movq "M(2)", "r6"\n" /* r6 = __ FF __ __ */ - "pxor "r2", "r5"\n" /* r5 = __ 25 24 __ */ - "pand "r7", "r6"\n" /* r6 = __ 42 __ __ */ - "psrlq $32, "r2"\n" /* r2 = __ __ 26 __ */ - "pand "M(3)", "r7"\n" /* r7 = 43 __ __ __ */ - "por "r2", "r3"\n" /* r3 = 44 __ 26 23 */ - "por 64(%edx), "r7"\n" /* r7 = 43 __ __ 12 */ - "por "r3", "r6"\n" /* r6 = 44 42 26 23 = R9 */ - "por "r5", "r7"\n" /* r7 = 43 25 24 12 = R8 */ - "movq "r6", 80(%edx)\n" /* store R9 = r6 */ - "movq "r7", 64(%edx)\n" /* store R8 = r7 */ - ); - /* 123c ( / 64 coeffs < 2c / coeff) */ -# undef M - -/* Done w/dequant + descramble + partial transpose; now do the idct itself. */ - -# define I( K) MtoSTR(K*16(%edx)) -# define J( K) MtoSTR(((K - 4)*16)+8(%edx)) - - RowIDCT /* 46 c */ - Transpose /* 19 c */ - -# undef I -# undef J -# define I( K) MtoSTR((K*16)+64(%edx)) -# define J( K) MtoSTR(((K-4)*16)+72(%edx)) - - RowIDCT /* 46 c */ - Transpose /* 19 c */ - -# undef I -# undef J -# define I( K) MtoSTR((K * 16)(%edx)) -# define J( K) I( K) - - ColumnIDCT /* 57 c */ - -# undef I -# undef J -# define I( K) MtoSTR((K*16)+8(%edx)) -# define J( K) I( K) - - ColumnIDCT /* 57 c */ - -# undef I -# undef J - /* 368 cycles ( / 64 coeff < 6 c / coeff) */ - - ASM("emms\n"); -} - -/************************************************************************************** - * - * Routine: MMX_idct10 - * - * Description: Perform IDCT on a 8x8 block with at most 10 nonzero coefficients - * - * Input: Pointer to input and output buffer - * - * Output: None - * - * Return: None - * - * Special Note: The input coefficients are in transposed ZigZag order - * - * Error: None - * - *************************************************************************************** - */ -/* --------------------------------------------------------------- */ -// This macro does four 4-sample one-dimensional idcts in parallel. Inputs -// 4 thru 7 are assumed to be zero. -#define BeginIDCT_10 "#BeginIDCT_10\n" \ - " movq "I(3)","r2"\n" \ - \ - " movq "C(3)","r6"\n" \ - " movq "r2","r4"\n" \ - \ - " movq "C(5)","r1"\n" \ - " pmulhw "r6","r4"\n" \ - \ - " movq "I(1)","r3"\n" \ - " pmulhw "r2","r1"\n" \ - \ - " movq "C(1)","r0"\n" \ - " paddw "r2","r4"\n" \ - \ - " pxor "r6","r6"\n" \ - " paddw "r1","r2"\n" \ - \ - " movq "I(2)","r5"\n" \ - " pmulhw "r3","r0"\n" \ - \ - " movq "r5","r1"\n" \ - " paddw "r3","r0"\n" \ - \ - " pmulhw "C(7)","r3"\n" \ - " psubsw "r2","r6"\n" \ - \ - " pmulhw "C(2)","r5"\n" \ - " psubsw "r4","r0"\n" \ - \ - " movq "I(2)","r7"\n" \ - " paddsw "r4","r4"\n" \ - \ - " paddw "r5","r7"\n" \ - " paddsw "r0","r4"\n" \ - \ - " pmulhw "C(6)","r1"\n" \ - " psubsw "r6","r3"\n" \ - \ - " movq "r4","I(1)"\n" \ - " paddsw "r6","r6"\n" \ - \ - " movq "C(4)","r4"\n" \ - " paddsw "r3","r6"\n" \ - \ - " movq "r3","r5"\n" \ - " pmulhw "r4","r3"\n" \ - \ - " movq "r6","I(2)"\n" \ - " movq "r0","r2"\n" \ - \ - " movq "I(0)","r6"\n" \ - " pmulhw "r4","r0"\n" \ - \ - " paddw "r3","r5"\n" \ - " paddw "r0","r2"\n" \ - \ - " psubsw "r1","r5"\n" \ - " pmulhw "r4","r6"\n" \ - \ - " paddw "I(0)","r6"\n" \ - " paddsw "r1","r1"\n" \ - \ - " movq "r6","r4"\n" \ - " paddsw "r5","r1"\n" \ - \ - " psubsw "r2","r6"\n" \ - " paddsw "r2","r2"\n" \ - \ - " movq "I(1)","r0"\n" \ - " paddsw "r6","r2"\n" \ - \ - " psubsw "r1","r2"\n" \ - "#end BeginIDCT_10\n" -// end BeginIDCT_10 macro (25 cycles). - -#define RowIDCT_10 ASM("\n" \ - "#RowIDCT_10\n" \ - BeginIDCT_10 \ - "\n" \ - " movq "I(2)","r3"\n" /* r3 = D. */ \ - " psubsw "r7","r4"\n" /* r4 = E. = E - G */ \ - " paddsw "r1","r1"\n" /* r1 = H. + H. */ \ - " paddsw "r7","r7"\n" /* r7 = G + G */ \ - " paddsw "r2","r1"\n" /* r1 = R1 = A.. + H. */\ - " paddsw "r4","r7"\n" /* r7 = G. = E + G */ \ - " psubsw "r3","r4"\n" /* r4 = R4 = E. - D. */ \ - " paddsw "r3","r3"\n" \ - " psubsw "r5","r6"\n" /* r6 = R6 = F. - B.. */\ - " paddsw "r5","r5"\n" \ - " paddsw "r4","r3"\n" /* r3 = R3 = E. + D. */ \ - " paddsw "r6","r5"\n" /* r5 = R5 = F. + B.. */\ - " psubsw "r0","r7"\n" /* r7 = R7 = G. - C. */ \ - " paddsw "r0","r0"\n" \ - " movq "r1","I(1)"\n" /* save R1 */ \ - " paddsw "r7","r0"\n" /* r0 = R0 = G. + C. */ \ - "#end RowIDCT_10\n" \ -); -// end RowIDCT macro (8 + 38 = 46 cycles) - -// Column IDCT normalizes and stores final results. - -#define ColumnIDCT_10 ASM("\n" \ - "#ColumnIDCT_10\n" \ - BeginIDCT_10 \ - "\n" \ - " paddsw "Eight","r2"\n" \ - " paddsw "r1","r1"\n" /* r1 = H. + H. */ \ - " paddsw "r2","r1"\n" /* r1 = R1 = A.. + H. */\ - " psraw ""$4"","r2"\n" /* r2 = NR2 */ \ - " psubsw "r7","r4"\n" /* r4 = E. = E - G */ \ - " psraw ""$4"","r1"\n" /* r1 = NR1 */ \ - " movq "I(2)","r3"\n" /* r3 = D. */ \ - " paddsw "r7","r7"\n" /* r7 = G + G */ \ - " movq "r2","I(2)"\n" /* store NR2 at I2 */ \ - " paddsw "r4","r7"\n" /* r7 = G. = E + G */ \ - " movq "r1","I(1)"\n" /* store NR1 at I1 */ \ - " psubsw "r3","r4"\n" /* r4 = R4 = E. - D. */ \ - " paddsw "Eight","r4"\n" \ - " paddsw "r3","r3"\n" /* r3 = D. + D. */ \ - " paddsw "r4","r3"\n" /* r3 = R3 = E. + D. */ \ - " psraw ""$4"","r4"\n" /* r4 = NR4 */ \ - " psubsw "r5","r6"\n" /* r6 = R6 = F. - B.. */\ - " psraw ""$4"","r3"\n" /* r3 = NR3 */ \ - " paddsw "Eight","r6"\n" \ - " paddsw "r5","r5"\n" /* r5 = B.. + B.. */ \ - " paddsw "r6","r5"\n" /* r5 = R5 = F. + B.. */\ - " psraw ""$4"","r6"\n" /* r6 = NR6 */ \ - " movq "r4","J(4)"\n" /* store NR4 at J4 */ \ - " psraw ""$4"","r5"\n" /* r5 = NR5 */ \ - " movq "r3","I(3)"\n" /* store NR3 at I3 */ \ - " psubsw "r0","r7"\n" /* r7 = R7 = G. - C. */ \ - " paddsw "Eight","r7"\n" \ - " paddsw "r0","r0"\n" /* r0 = C. + C. */ \ - " paddsw "r7","r0"\n" /* r0 = R0 = G. + C. */ \ - " psraw ""$4"","r7"\n" /* r7 = NR7 */ \ - " movq "r6","J(6)"\n" /* store NR6 at J6 */ \ - " psraw ""$4"","r0"\n" /* r0 = NR0 */ \ - " movq "r5","J(5)"\n" /* store NR5 at J5 */ \ - \ - " movq "r7","J(7)"\n" /* store NR7 at J7 */ \ - \ - " movq "r0","I(0)"\n" /* store NR0 at I0 */ \ - "#end ColumnIDCT_10\n" \ -); -// end ColumnIDCT macro (38 + 19 = 57 cycles) -/* --------------------------------------------------------------- */ - - -/* --------------------------------------------------------------- */ -/* IDCT 10 */ -void IDct10__mmx( Q_LIST_ENTRY * InputData, - ogg_int16_t *QuantMatrix, - ogg_int16_t * OutputData ) { - -# define MIDM(M,I) MtoSTR(M+I*8(%ecx)) -# define M(I) MIDM( MaskOffset , I ) -# define MIDC(M,I) MtoSTR(M+(I-1)*8(%ecx)) -# define C(I) MIDC( CosineOffset , I ) -# define MIDEight(M) MtoSTR(M(%ecx)) -# define Eight MIDEight(EightOffset) - -# define r0 "%mm0" -# define r1 "%mm1" -# define r2 "%mm2" -# define r3 "%mm3" -# define r4 "%mm4" -# define r5 "%mm5" -# define r6 "%mm6" -# define r7 "%mm7" - - __asm__ __volatile__ ( - /* eax = quantized input */ - /* esi = quantization table */ - /* edx = destination (= idct buffer) */ - /* ecx = idctconstants */ - "" - : - :"a"(InputData), "S"(QuantMatrix), "d"(OutputData), "c"(idctconstants) - ); - - ASM( - "movq (%eax), "r0"\n" - "pmullw (%esi), "r0"\n" /* r0 = 03 02 01 00 */ - "movq 16(%eax), "r1"\n" - "pmullw 16(%esi), "r1"\n" /* r1 = 13 12 11 10 */ - "movq "M(0)", "r2"\n" /* r2 = __ __ __ FF */ - "movq "r0", "r3"\n" /* r3 = 03 02 01 00 */ - "movq 8(%eax), "r4"\n" - "psrlq $16, "r0"\n" /* r0 = __ 03 02 01 */ - "pmullw 8(%esi), "r4"\n" /* r4 = 07 06 05 04 */ - "pand "r2", "r3"\n" /* r3 = __ __ __ 00 */ - "movq "r0", "r5"\n" /* r5 = __ 03 02 01 */ - "pand "r2", "r5"\n" /* r5 = __ __ __ 01 */ - "psllq $32, "r1"\n" /* r1 = 11 10 __ __ */ - "movq "M(3)", "r7"\n" /* r7 = FF __ __ __ */ - "pxor "r5", "r0"\n" /* r0 = __ 03 02 __ */ - "pand "r1", "r7"\n" /* r7 = 11 __ __ __ */ - "por "r3", "r0"\n" /* r0 = __ 03 02 00 */ - "pxor "r7", "r1"\n" /* r1 = __ 10 __ __ */ - "por "r7", "r0"\n" /* r0 = 11 03 02 00 = R0 */ - "movq "r4", "r3"\n" /* r3 = 07 06 05 04 */ - "movq "r0", (%edx)\n" /* write R0 = r0 */ - "pand "r2", "r3"\n" /* r3 = __ __ __ 04 */ - "psllq $16, "r3"\n" /* r3 = __ __ 04 __ */ - "por "r3", "r5"\n" /* r5 = __ __ 04 01 */ - "por "r5", "r1"\n" /* r1 = __ 10 04 01 = R1 */ - "psrlq $16, "r4"\n" /* r4 = __ 07 06 05 */ - "movq "r1", 16(%edx)\n" /* write R1 = r1 */ - "movq "r4", "r5"\n" /* r5 = __ 07 06 05 */ - "psrlq $16, "r4"\n" /* r4 = __ __ 07 06 */ - "movq "r2", "r6"\n" /* r6 = __ __ __ FF */ - "pand "r2", "r5"\n" /* r5 = __ __ __ 05 */ - "pand "r4", "r6"\n" /* r6 = __ __ __ 06 */ - "pxor "r6", "r4"\n" /* r4 = __ __ 07 __ */ - "por "r5", "r4"\n" /* r4 = __ __ 07 05 */ - "movq "r4", 32(%edx)\n" /* write R2 = r4 */ - "movq "r6", 48(%edx)\n" /* write R3 = r6 */ - ); -# undef M - -/* Done w/dequant + descramble + partial transpose; now do the idct itself. */ - -# define I( K) MtoSTR((K*16)(%edx)) -# define J( K) MtoSTR(((K - 4) * 16)+8(%edx)) - - RowIDCT_10 /* 33 c */ - Transpose /* 19 c */ - -# undef I -# undef J -//# define I( K) [edx + ( K * 16) + 64] -//# define J( K) [edx + ( (K - 4) * 16) + 72] - -// RowIDCT ; 46 c -// Transpose ; 19 c - -//# undef I -//# undef J -# define I( K) MtoSTR((K * 16)(%edx)) -# define J( K) I( K) - - ColumnIDCT_10 /* 44 c */ - -# undef I -# undef J -# define I( K) MtoSTR((K * 16)+8(%edx)) -# define J( K) I( K) - - ColumnIDCT_10 /* 44 c */ - -# undef I -# undef J - - ASM("emms\n"); -} - -/************************************************************************************** - * - * Routine: MMX_idct3 - * - * Description: Perform IDCT on a 8x8 block with at most 3 nonzero coefficients - * - * Input: Pointer to input and output buffer - * - * Output: None - * - * Return: None - * - * Special Note: Only works for three nonzero coefficients. - * - * Error: None - * - *************************************************************************************** - */ -/*************************************************************************************** - In IDCT 3, we are dealing with only three Non-Zero coefficients in the 8x8 block. - In the case that we work in the fashion RowIDCT -> ColumnIDCT, we only have to - do 1-D row idcts on the first two rows, the rest six rows remain zero anyway. - After row IDCTs, since every column could have nonzero coefficients, we need do - eight 1-D column IDCT. However, for each column, there are at most two nonzero - coefficients, coefficient 0 and coefficient 1. Same for the coefficents for the - two 1-d row idcts. For this reason, the process of a 1-D IDCT is simplified - - from a full version: - - A = (C1 * I1) + (C7 * I7) B = (C7 * I1) - (C1 * I7) - C = (C3 * I3) + (C5 * I5) D = (C3 * I5) - (C5 * I3) - A. = C4 * (A - C) B. = C4 * (B - D) - C. = A + C D. = B + D - - E = C4 * (I0 + I4) F = C4 * (I0 - I4) - G = (C2 * I2) + (C6 * I6) H = (C6 * I2) - (C2 * I6) - E. = E - G - G. = E + G - - A.. = F + A. B.. = B. - H - F. = F - A. H. = B. + H - - R0 = G. + C. R1 = A.. + H. R3 = E. + D. R5 = F. + B.. - R7 = G. - C. R2 = A.. - H. R4 = E. - D. R6 = F. - B.. - - To: - - - A = (C1 * I1) B = (C7 * I1) - C = 0 D = 0 - A. = C4 * A B. = C4 * B - C. = A D. = B - - E = C4 * I0 F = E - G = 0 H = 0 - E. = E - G. = E - - A.. = E + A. B.. = B. - F. = E - A. H. = B. - - R0 = E + A R1 = E + A. + B. R3 = E + B R5 = E - A. + B. - R7 = E - A R2 = E + A. - B. R4 = E - B R6 = F - A. - B. - -******************************************************************************************/ - -#define RowIDCT_3 ASM("\n"\ - "#RowIDCT_3\n"\ - " movq "I(1)","r7"\n" /* r7 = I1 */ \ - " movq "C(1)","r0"\n" /* r0 = C1 */ \ - " movq "C(7)","r3"\n" /* r3 = C7 */ \ - " pmulhw "r7","r0"\n" /* r0 = C1 * I1 - I1 */ \ - " pmulhw "r7","r3"\n" /* r3 = C7 * I1 = B, D. */ \ - " movq "I(0)","r6"\n" /* r6 = I0 */ \ - " movq "C(4)","r4"\n" /* r4 = C4 */ \ - " paddw "r7","r0"\n" /* r0 = C1 * I1 = A, C. */ \ - " movq "r6","r1"\n" /* make a copy of I0 */ \ - " pmulhw "r4","r6"\n" /* r2 = C4 * I0 - I0 */ \ - " movq "r0","r2"\n" /* make a copy of A */ \ - " movq "r3","r5"\n" /* make a copy of B */ \ - " pmulhw "r4","r2"\n" /* r2 = C4 * A - A */ \ - " pmulhw "r4","r5"\n" /* r5 = C4 * B - B */ \ - " paddw "r1","r6"\n" /* r2 = C4 * I0 = E, F */ \ - " movq "r6","r4"\n" /* r4 = E */ \ - " paddw "r0","r2"\n" /* r2 = A. */ \ - " paddw "r3","r5"\n" /* r5 = B. */ \ - " movq "r6","r7"\n" /* r7 = E */ \ - " movq "r5","r1"\n" /* r1 = B. */ \ - /* r0 = A */ \ - /* r3 = B */ \ - /* r2 = A. */ \ - /* r5 = B. */ \ - /* r6 = E */ \ - /* r4 = E */ \ - /* r7 = E */ \ - /* r1 = B. */ \ - " psubw "r2","r6"\n" /* r6 = E - A. */ \ - " psubw "r3","r4"\n" /* r4 = E - B ----R4 */ \ - " psubw "r0","r7"\n" /* r7 = E - A ----R7 */ \ - " paddw "r2","r2"\n" /* r2 = A. + A. */ \ - " paddw "r3","r3"\n" /* r3 = B + B */ \ - " paddw "r0","r0"\n" /* r0 = A + A */ \ - " paddw "r6","r2"\n" /* r2 = E + A. */ \ - " paddw "r4","r3"\n" /* r3 = E + B ----R3 */ \ - " psubw "r1","r2"\n" /* r2 = E + A. - B. ----R2 */ \ - " psubw "r5","r6"\n" /* r6 = E - A. - B. ----R6 */ \ - " paddw "r1","r1"\n" /* r1 = B. + B. */ \ - " paddw "r5","r5"\n" /* r5 = B. + B. */ \ - " paddw "r7","r0"\n" /* r0 = E + A ----R0 */ \ - " paddw "r2","r1"\n" /* r1 = E + A. + B. -----R1 */ \ - " movq "r1","I(1)"\n" /* save r1 */ \ - " paddw "r6","r5"\n" /* r5 = E - A. + B. -----R5 */ \ - "#end RowIDCT_3\n"\ -); -//End of RowIDCT_3 - -#define ColumnIDCT_3 ASM("\n"\ - "#ColumnIDCT_3\n"\ - " movq "I(1)","r7"\n" /* r7 = I1 */ \ - " movq "C(1)","r0"\n" /* r0 = C1 */ \ - " movq "C(7)","r3"\n" /* r3 = C7 */ \ - " pmulhw "r7","r0"\n" /* r0 = C1 * I1 - I1 */ \ - " pmulhw "r7","r3"\n" /* r3 = C7 * I1 = B, D. */ \ - " movq "I(0)","r6"\n" /* r6 = I0 */ \ - " movq "C(4)","r4"\n" /* r4 = C4 */ \ - " paddw "r7","r0"\n" /* r0 = C1 * I1 = A, C. */ \ - " movq "r6","r1"\n" /* make a copy of I0 */ \ - " pmulhw "r4","r6"\n" /* r2 = C4 * I0 - I0 */ \ - " movq "r0","r2"\n" /* make a copy of A */ \ - " movq "r3","r5"\n" /* make a copy of B */ \ - " pmulhw "r4","r2"\n" /* r2 = C4 * A - A */ \ - " pmulhw "r4","r5"\n" /* r5 = C4 * B - B */ \ - " paddw "r1","r6"\n" /* r2 = C4 * I0 = E, F */ \ - " movq "r6","r4"\n" /* r4 = E */ \ - " paddw "Eight","r6"\n" /* +8 for shift */ \ - " paddw "Eight","r4"\n" /* +8 for shift */ \ - " paddw "r0","r2"\n" /* r2 = A. */ \ - " paddw "r3","r5"\n" /* r5 = B. */ \ - " movq "r6","r7"\n" /* r7 = E */ \ - " movq "r5","r1"\n" /* r1 = B. */ \ -/* r0 = A */ \ -/* r3 = B */ \ -/* r2 = A. */ \ -/* r5 = B. */ \ -/* r6 = E */ \ -/* r4 = E */ \ -/* r7 = E */ \ -/* r1 = B. */ \ - " psubw "r2","r6"\n" /* r6 = E - A. */ \ - " psubw "r3","r4"\n" /* r4 = E - B ----R4 */ \ - " psubw "r0","r7"\n" /* r7 = E - A ----R7 */ \ - " paddw "r2","r2"\n" /* r2 = A. + A. */ \ - " paddw "r3","r3"\n" /* r3 = B + B */ \ - " paddw "r0","r0"\n" /* r0 = A + A */ \ - " paddw "r6","r2"\n" /* r2 = E + A. */ \ - " paddw "r4","r3"\n" /* r3 = E + B ----R3 */ \ - " psraw $4,"r4"\n" /* shift */ \ - " movq "r4","J(4)"\n" /* store R4 at J4 */ \ - " psraw $4,"r3"\n" /* shift */ \ - " movq "r3","I(3)"\n" /* store R3 at I3 */ \ - " psubw "r1","r2"\n" /* r2 = E + A. - B. ----R2 */ \ - " psubw "r5","r6"\n" /* r6 = E - A. - B. ----R6 */ \ - " paddw "r1","r1"\n" /* r1 = B. + B. */ \ - " paddw "r5","r5"\n" /* r5 = B. + B. */ \ - " paddw "r7","r0"\n" /* r0 = E + A ----R0 */ \ - " paddw "r2","r1"\n" /* r1 = E + A. + B. -----R1 */ \ - " psraw $4,"r7"\n" /* shift */ \ - " psraw $4,"r2"\n" /* shift */ \ - " psraw $4,"r0"\n" /* shift */ \ - " psraw $4,"r1"\n" /* shift */ \ - " movq "r7","J(7)"\n" /* store R7 to J7 */ \ - " movq "r0","I(0)"\n" /* store R0 to I0 */ \ - " movq "r1","I(1)"\n" /* store R1 to I1 */ \ - " movq "r2","I(2)"\n" /* store R2 to I2 */ \ - " movq "r1","I(1)"\n" /* save r1 */ \ - " paddw "r6","r5"\n" /* r5 = E - A. + B. -----R5 */ \ - " psraw $4,"r5"\n" /* shift */ \ - " movq "r5","J(5)"\n" /* store R5 at J5 */ \ - " psraw $4,"r6"\n" /* shift */ \ - " movq "r6","J(6)"\n" /* store R6 at J6 */ \ - "#end ColumnIDCT_3\n"\ -); -//End of ColumnIDCT_3 - -void IDct3__mmx( Q_LIST_ENTRY * InputData, - ogg_int16_t *QuantMatrix, - ogg_int16_t * OutputData ) { - -# define MIDM(M,I) MtoSTR(M+I*8(%ecx)) -# define M(I) MIDM( MaskOffset , I ) -# define MIDC(M,I) MtoSTR(M+(I-1)*8(%ecx)) -# define C(I) MIDC( CosineOffset , I ) -# define MIDEight(M) MtoSTR(M(%ecx)) -# define Eight MIDEight(EightOffset) - -# define r0 "%mm0" -# define r1 "%mm1" -# define r2 "%mm2" -# define r3 "%mm3" -# define r4 "%mm4" -# define r5 "%mm5" -# define r6 "%mm6" -# define r7 "%mm7" - - __asm__ __volatile__ ( - /* eax = quantized input */ - /* esi = quantization table */ - /* edx = destination (= idct buffer) */ - /* ecx = idctconstants */ - "" - : - :"a"(InputData), "S"(QuantMatrix), "d"(OutputData), "c"(idctconstants) - ); - - ASM( - "movq (%eax), "r0"\n" - "pmullw (%esi), "r0"\n" /* r0 = 03 02 01 00 */ - "movq "M(0)", "r2"\n" /* r2 = __ __ __ FF */ - "movq "r0", "r3"\n" /* r3 = 03 02 01 00 */ - "psrlq $16, "r0"\n" /* r0 = __ 03 02 01 */ - "pand "r2", "r3"\n" /* r3 = __ __ __ 00 */ - "movq "r0", "r5"\n" /* r5 = __ 03 02 01 */ - "pand "r2", "r5"\n" /* r5 = __ __ __ 01 */ - "pxor "r5", "r0"\n" /* r0 = __ 03 02 __ */ - "por "r3", "r0"\n" /* r0 = __ 03 02 00 */ - "movq "r0", (%edx)\n" /* write R0 = r0 */ - "movq "r5", 16(%edx)\n" /* write R1 = r5 */ - ); -# undef M - -/* Done partial transpose; now do the idct itself. */ - -# define I( K) MtoSTR(K*16(%edx)) -# define J( K) MtoSTR(((K - 4)*16)+8(%edx)) - - RowIDCT_3 /* 33 c */ - Transpose /* 19 c */ - -# undef I -# undef J -//# define I( K) [edx + ( K * 16) + 64] -//# define J( K) [edx + ( (K - 4) * 16) + 72] - -// RowIDCT ; 46 c -// Transpose ; 19 c - -//# undef I -//# undef J -# define I( K) MtoSTR((K * 16)(%edx)) -# define J( K) I( K) - - ColumnIDCT_3 /* 44 c */ - -# undef I -# undef J -# define I( K) MtoSTR((K*16)+8(%edx)) -# define J( K) I( K) - - ColumnIDCT_3 /* 44 c */ - -# undef I -# undef J - - ASM("emms\n"); -} - - -/* install our implementation in the function table */ -void dsp_mmx_idct_init(DspFunctions *funcs) -{ - funcs->IDctSlow = IDctSlow__mmx; - funcs->IDct10 = IDct10__mmx; - funcs->IDct3 = IDct3__mmx; -} - -#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_32/recon_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32/recon_mmx.c deleted file mode 100644 index 7a931afe4..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_32/recon_mmx.c +++ /dev/null @@ -1,182 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: recon_mmx.c 15153 2008-08-04 18:37:55Z tterribe $ - - ********************************************************************/ - -#include "../codec_internal.h" - -#if defined(USE_ASM) - -static const __attribute__ ((aligned(8),used)) ogg_int64_t V128 = 0x8080808080808080LL; - -static void copy8x8__mmx (unsigned char *src, - unsigned char *dest, - unsigned int stride) -{ - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " lea (%2, %2, 2), %%edi \n\t" - - " movq (%1), %%mm0 \n\t" - " movq (%1, %2), %%mm1 \n\t" - " movq (%1, %2, 2), %%mm2 \n\t" - " movq (%1, %%edi), %%mm3 \n\t" - - " lea (%1, %2, 4), %1 \n\t" - - " movq %%mm0, (%0) \n\t" - " movq %%mm1, (%0, %2) \n\t" - " movq %%mm2, (%0, %2, 2) \n\t" - " movq %%mm3, (%0, %%edi) \n\t" - - " lea (%0, %2, 4), %0 \n\t" - - " movq (%1), %%mm0 \n\t" - " movq (%1, %2), %%mm1 \n\t" - " movq (%1, %2, 2), %%mm2 \n\t" - " movq (%1, %%edi), %%mm3 \n\t" - - " movq %%mm0, (%0) \n\t" - " movq %%mm1, (%0, %2) \n\t" - " movq %%mm2, (%0, %2, 2) \n\t" - " movq %%mm3, (%0, %%edi) \n\t" - : "+a" (dest) - : "c" (src), - "d" (stride) - : "memory", "edi" - ); -} - -static void recon_intra8x8__mmx (unsigned char *ReconPtr, ogg_int16_t *ChangePtr, - ogg_uint32_t LineStep) -{ - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " movq %[V128], %%mm0 \n\t" /* Set mm0 to 0x8080808080808080 */ - - " lea 128(%1), %%edi \n\t" /* Endpoint in input buffer */ - "1: \n\t" - " movq (%1), %%mm2 \n\t" /* First four input values */ - - " packsswb 8(%1), %%mm2 \n\t" /* pack with next(high) four values */ - " por %%mm0, %%mm0 \n\t" - " pxor %%mm0, %%mm2 \n\t" /* Convert result to unsigned (same as add 128) */ - " lea 16(%1), %1 \n\t" /* Step source buffer */ - " cmp %%edi, %1 \n\t" /* are we done */ - - " movq %%mm2, (%0) \n\t" /* store results */ - - " lea (%0, %2), %0 \n\t" /* Step output buffer */ - " jc 1b \n\t" /* Loop back if we are not done */ - : "+r" (ReconPtr) - : "r" (ChangePtr), - "r" (LineStep), - [V128] "m" (V128) - : "memory", "edi" - ); -} - -static void recon_inter8x8__mmx (unsigned char *ReconPtr, unsigned char *RefPtr, - ogg_int16_t *ChangePtr, ogg_uint32_t LineStep) -{ - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " pxor %%mm0, %%mm0 \n\t" - " lea 128(%1), %%edi \n\t" - - "1: \n\t" - " movq (%2), %%mm2 \n\t" /* (+3 misaligned) 8 reference pixels */ - - " movq (%1), %%mm4 \n\t" /* first 4 changes */ - " movq %%mm2, %%mm3 \n\t" - " movq 8(%1), %%mm5 \n\t" /* last 4 changes */ - " punpcklbw %%mm0, %%mm2 \n\t" /* turn first 4 refs into positive 16-bit #s */ - " paddsw %%mm4, %%mm2 \n\t" /* add in first 4 changes */ - " punpckhbw %%mm0, %%mm3 \n\t" /* turn last 4 refs into positive 16-bit #s */ - " paddsw %%mm5, %%mm3 \n\t" /* add in last 4 changes */ - " add %3, %2 \n\t" /* next row of reference pixels */ - " packuswb %%mm3, %%mm2 \n\t" /* pack result to unsigned 8-bit values */ - " lea 16(%1), %1 \n\t" /* next row of changes */ - " cmp %%edi, %1 \n\t" /* are we done? */ - - " movq %%mm2, (%0) \n\t" /* store result */ - - " lea (%0, %3), %0 \n\t" /* next row of output */ - " jc 1b \n\t" - : "+r" (ReconPtr) - : "r" (ChangePtr), - "r" (RefPtr), - "r" (LineStep) - : "memory", "edi" - ); -} - -static void recon_inter8x8_half__mmx (unsigned char *ReconPtr, unsigned char *RefPtr1, - unsigned char *RefPtr2, ogg_int16_t *ChangePtr, - ogg_uint32_t LineStep) -{ - __asm__ __volatile__ ( - " .p2align 4 \n\t" - - " pxor %%mm0, %%mm0 \n\t" - " lea 128(%1), %%edi \n\t" - - "1: \n\t" - " movq (%2), %%mm2 \n\t" /* (+3 misaligned) 8 reference pixels */ - " movq (%3), %%mm4 \n\t" /* (+3 misaligned) 8 reference pixels */ - - " movq %%mm2, %%mm3 \n\t" - " punpcklbw %%mm0, %%mm2 \n\t" /* mm2 = start ref1 as positive 16-bit #s */ - " movq %%mm4, %%mm5 \n\t" - " movq (%1), %%mm6 \n\t" /* first 4 changes */ - " punpckhbw %%mm0, %%mm3 \n\t" /* mm3 = end ref1 as positive 16-bit #s */ - " movq 8(%1), %%mm7 \n\t" /* last 4 changes */ - " punpcklbw %%mm0, %%mm4 \n\t" /* mm4 = start ref2 as positive 16-bit #s */ - " punpckhbw %%mm0, %%mm5 \n\t" /* mm5 = end ref2 as positive 16-bit #s */ - " paddw %%mm4, %%mm2 \n\t" /* mm2 = start (ref1 + ref2) */ - " paddw %%mm5, %%mm3 \n\t" /* mm3 = end (ref1 + ref2) */ - " psrlw $1, %%mm2 \n\t" /* mm2 = start (ref1 + ref2)/2 */ - " psrlw $1, %%mm3 \n\t" /* mm3 = end (ref1 + ref2)/2 */ - " paddw %%mm6, %%mm2 \n\t" /* add changes to start */ - " paddw %%mm7, %%mm3 \n\t" /* add changes to end */ - " lea 16(%1), %1 \n\t" /* next row of changes */ - " packuswb %%mm3, %%mm2 \n\t" /* pack start|end to unsigned 8-bit */ - " add %4, %2 \n\t" /* next row of reference pixels */ - " add %4, %3 \n\t" /* next row of reference pixels */ - " movq %%mm2, (%0) \n\t" /* store result */ - " add %4, %0 \n\t" /* next row of output */ - " cmp %%edi, %1 \n\t" /* are we done? */ - " jc 1b \n\t" - : "+r" (ReconPtr) - : "r" (ChangePtr), - "r" (RefPtr1), - "r" (RefPtr2), - "m" (LineStep) - : "memory", "edi" - ); -} - -void dsp_mmx_recon_init(DspFunctions *funcs) -{ - funcs->copy8x8 = copy8x8__mmx; - funcs->recon_intra8x8 = recon_intra8x8__mmx; - funcs->recon_inter8x8 = recon_inter8x8__mmx; - funcs->recon_inter8x8_half = recon_inter8x8_half__mmx; -} - -#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_32_vs/dsp_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32_vs/dsp_mmx.c deleted file mode 100644 index cecc0eb76..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_32_vs/dsp_mmx.c +++ /dev/null @@ -1,1605 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: mcomp.c,v 1.8 2003/12/03 08:59:41 arc Exp $ - - ********************************************************************/ - -#include - -#include "../codec_internal.h" -#include "../dsp.h" - -#if 0 -//These are to let me selectively enable the C versions, these are needed -#define DSP_OP_AVG(a,b) ((((int)(a)) + ((int)(b)))/2) -#define DSP_OP_DIFF(a,b) (((int)(a)) - ((int)(b))) -#define DSP_OP_ABS_DIFF(a,b) abs((((int)(a)) - ((int)(b)))) -#endif - - -static const ogg_int64_t V128 = 0x0080008000800080; - -static void sub8x8__mmx (unsigned char *FiltPtr, unsigned char *ReconPtr, - ogg_int16_t *DctInputPtr, ogg_uint32_t PixelsPerLine, - ogg_uint32_t ReconPixelsPerLine) -{ - - //Make non-zero to use the C-version -#if 0 - int i; - - /* For each block row */ - for (i=8; i; i--) { - DctInputPtr[0] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[0], ReconPtr[0]); - DctInputPtr[1] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[1], ReconPtr[1]); - DctInputPtr[2] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[2], ReconPtr[2]); - DctInputPtr[3] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[3], ReconPtr[3]); - DctInputPtr[4] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[4], ReconPtr[4]); - DctInputPtr[5] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[5], ReconPtr[5]); - DctInputPtr[6] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[6], ReconPtr[6]); - DctInputPtr[7] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[7], ReconPtr[7]); - - /* Start next row */ - FiltPtr += PixelsPerLine; - ReconPtr += ReconPixelsPerLine; - DctInputPtr += 8; - } -#else - __asm { - align 16 - - pxor mm7, mm7 - - mov eax, FiltPtr - mov ebx, ReconPtr - mov edx, DctInputPtr - - /* You can't use rept in inline masm and macro parsing seems screwed with inline asm*/ - - /* ITERATION 1 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm1, [ebx] /* mm1 = ReconPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - movq mm3, mm1 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ - psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ - movq [edx], mm0 /* write answer out */ - movq [8 + edx], mm2 /* write answer out */ - /* Increment pointers */ - add edx, 16 - add eax, PixelsPerLine - add ebx, ReconPixelsPerLine - - - /* ITERATION 2 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm1, [ebx] /* mm1 = ReconPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - movq mm3, mm1 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ - psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ - movq [edx], mm0 /* write answer out */ - movq [8 + edx], mm2 /* write answer out */ - /* Increment pointers */ - add edx, 16 - add eax, PixelsPerLine - add ebx, ReconPixelsPerLine - - - /* ITERATION 3 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm1, [ebx] /* mm1 = ReconPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - movq mm3, mm1 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ - psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ - movq [edx], mm0 /* write answer out */ - movq [8 + edx], mm2 /* write answer out */ - /* Increment pointers */ - add edx, 16 - add eax, PixelsPerLine - add ebx, ReconPixelsPerLine - - - /* ITERATION 4 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm1, [ebx] /* mm1 = ReconPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - movq mm3, mm1 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ - psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ - movq [edx], mm0 /* write answer out */ - movq [8 + edx], mm2 /* write answer out */ - /* Increment pointers */ - add edx, 16 - add eax, PixelsPerLine - add ebx, ReconPixelsPerLine - - - /* ITERATION 5 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm1, [ebx] /* mm1 = ReconPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - movq mm3, mm1 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ - psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ - movq [edx], mm0 /* write answer out */ - movq [8 + edx], mm2 /* write answer out */ - /* Increment pointers */ - add edx, 16 - add eax, PixelsPerLine - add ebx, ReconPixelsPerLine - - - /* ITERATION 6 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm1, [ebx] /* mm1 = ReconPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - movq mm3, mm1 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ - psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ - movq [edx], mm0 /* write answer out */ - movq [8 + edx], mm2 /* write answer out */ - /* Increment pointers */ - add edx, 16 - add eax, PixelsPerLine - add ebx, ReconPixelsPerLine - - - /* ITERATION 7 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm1, [ebx] /* mm1 = ReconPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - movq mm3, mm1 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ - psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ - movq [edx], mm0 /* write answer out */ - movq [8 + edx], mm2 /* write answer out */ - /* Increment pointers */ - add edx, 16 - add eax, PixelsPerLine - add ebx, ReconPixelsPerLine - - - /* ITERATION 8 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm1, [ebx] /* mm1 = ReconPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - movq mm3, mm1 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 /* mm1 = INT16(ReconPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 /* mm3 = INT16(ReconPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - ReconPtr */ - psubw mm2, mm3 /* mm2 = FiltPtr - ReconPtr */ - movq [edx], mm0 /* write answer out */ - movq [8 + edx], mm2 /* write answer out */ - /* Increment pointers */ - add edx, 16 - add eax, PixelsPerLine - add ebx, ReconPixelsPerLine - - - - - - }; - -#endif -} - -static void sub8x8_128__mmx (unsigned char *FiltPtr, ogg_int16_t *DctInputPtr, - ogg_uint32_t PixelsPerLine) -{ - -#if 0 - int i; - /* For each block row */ - for (i=8; i; i--) { - /* INTRA mode so code raw image data */ - /* We convert the data to 8 bit signed (by subtracting 128) as - this reduces the internal precision requirments in the DCT - transform. */ - DctInputPtr[0] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[0], 128); - DctInputPtr[1] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[1], 128); - DctInputPtr[2] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[2], 128); - DctInputPtr[3] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[3], 128); - DctInputPtr[4] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[4], 128); - DctInputPtr[5] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[5], 128); - DctInputPtr[6] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[6], 128); - DctInputPtr[7] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[7], 128); - - /* Start next row */ - FiltPtr += PixelsPerLine; - DctInputPtr += 8; - } - -#else - __asm { - align 16 - - pxor mm7, mm7 - - mov eax, FiltPtr - mov ebx, DctInputPtr - - movq mm1, V128 - - /* ITERATION 1 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ - psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ - movq [ebx], mm0 /* write answer out */ - movq [8 + ebx], mm2 /* write answer out */ - /* Increment pointers */ - add ebx, 16 - add eax, PixelsPerLine - - - /* ITERATION 2 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ - psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ - movq [ebx], mm0 /* write answer out */ - movq [8 + ebx], mm2 /* write answer out */ - /* Increment pointers */ - add ebx, 16 - add eax, PixelsPerLine - - - /* ITERATION 3 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ - psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ - movq [ebx], mm0 /* write answer out */ - movq [8 + ebx], mm2 /* write answer out */ - /* Increment pointers */ - add ebx, 16 - add eax, PixelsPerLine - - - /* ITERATION 4 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ - psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ - movq [ebx], mm0 /* write answer out */ - movq [8 + ebx], mm2 /* write answer out */ - /* Increment pointers */ - add ebx, 16 - add eax, PixelsPerLine - - - /* ITERATION 5 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ - psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ - movq [ebx], mm0 /* write answer out */ - movq [8 + ebx], mm2 /* write answer out */ - /* Increment pointers */ - add ebx, 16 - add eax, PixelsPerLine - - - /* ITERATION 6 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ - psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ - movq [ebx], mm0 /* write answer out */ - movq [8 + ebx], mm2 /* write answer out */ - /* Increment pointers */ - add ebx, 16 - add eax, PixelsPerLine - - - /* ITERATION 7 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ - psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ - movq [ebx], mm0 /* write answer out */ - movq [8 + ebx], mm2 /* write answer out */ - /* Increment pointers */ - add ebx, 16 - add eax, PixelsPerLine - - - /* ITERATION 8 */ - movq mm0, [eax] /* mm0 = FiltPtr */ - movq mm2, mm0 /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 /* mm0 = INT16(FiltPtr) */ - punpckhbw mm2, mm7 /* mm2 = INT16(FiltPtr) */ - /* start calculation */ - psubw mm0, mm1 /* mm0 = FiltPtr - 128 */ - psubw mm2, mm1 /* mm2 = FiltPtr - 128 */ - movq [ebx], mm0 /* write answer out */ - movq [8 + ebx], mm2 /* write answer out */ - /* Increment pointers */ - add ebx, 16 - add eax, PixelsPerLine - - }; - -#endif -} - - - - -static void sub8x8avg2__mmx (unsigned char *FiltPtr, unsigned char *ReconPtr1, - unsigned char *ReconPtr2, ogg_int16_t *DctInputPtr, - ogg_uint32_t PixelsPerLine, - ogg_uint32_t ReconPixelsPerLine) -{ - -#if 0 - int i; - - /* For each block row */ - for (i=8; i; i--) { - DctInputPtr[0] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[0], DSP_OP_AVG (ReconPtr1[0], ReconPtr2[0])); - DctInputPtr[1] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[1], DSP_OP_AVG (ReconPtr1[1], ReconPtr2[1])); - DctInputPtr[2] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[2], DSP_OP_AVG (ReconPtr1[2], ReconPtr2[2])); - DctInputPtr[3] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[3], DSP_OP_AVG (ReconPtr1[3], ReconPtr2[3])); - DctInputPtr[4] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[4], DSP_OP_AVG (ReconPtr1[4], ReconPtr2[4])); - DctInputPtr[5] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[5], DSP_OP_AVG (ReconPtr1[5], ReconPtr2[5])); - DctInputPtr[6] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[6], DSP_OP_AVG (ReconPtr1[6], ReconPtr2[6])); - DctInputPtr[7] = (ogg_int16_t) DSP_OP_DIFF (FiltPtr[7], DSP_OP_AVG (ReconPtr1[7], ReconPtr2[7])); - - /* Start next row */ - FiltPtr += PixelsPerLine; - ReconPtr1 += ReconPixelsPerLine; - ReconPtr2 += ReconPixelsPerLine; - DctInputPtr += 8; - } -#else - - __asm { - align 16 - - pxor mm7, mm7 - - mov eax, FiltPtr - mov ebx, ReconPtr1 - mov ecx, ReconPtr2 - mov edx, DctInputPtr - - /* ITERATION 1 */ - movq mm0, [eax] ; /* mm0 = FiltPtr */ - movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ - movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ - movq mm2, mm0 ; /* dup to prepare for up conversion */ - movq mm3, mm1 ; /* dup to prepare for up conversion */ - movq mm5, mm4 ; /* dup to prepare for up conversion */ - ; /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ - punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ - punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ - punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ - ; /* average ReconPtr1 and ReconPtr2 */ - paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ - paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ - psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ - psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ - psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - movq [edx], mm0 ; /* write answer out */ - movq [8 + edx], mm2 ; /* write answer out */ - ; /* Increment pointers */ - add edx, 16 ; - add eax, PixelsPerLine ; - add ebx, ReconPixelsPerLine ; - add ecx, ReconPixelsPerLine ; - - - /* ITERATION 2 */ - movq mm0, [eax] ; /* mm0 = FiltPtr */ - movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ - movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ - movq mm2, mm0 ; /* dup to prepare for up conversion */ - movq mm3, mm1 ; /* dup to prepare for up conversion */ - movq mm5, mm4 ; /* dup to prepare for up conversion */ - ; /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ - punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ - punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ - punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ - ; /* average ReconPtr1 and ReconPtr2 */ - paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ - paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ - psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ - psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ - psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - movq [edx], mm0 ; /* write answer out */ - movq [8 + edx], mm2 ; /* write answer out */ - ; /* Increment pointers */ - add edx, 16 ; - add eax, PixelsPerLine ; - add ebx, ReconPixelsPerLine ; - add ecx, ReconPixelsPerLine ; - - - /* ITERATION 3 */ - movq mm0, [eax] ; /* mm0 = FiltPtr */ - movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ - movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ - movq mm2, mm0 ; /* dup to prepare for up conversion */ - movq mm3, mm1 ; /* dup to prepare for up conversion */ - movq mm5, mm4 ; /* dup to prepare for up conversion */ - ; /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ - punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ - punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ - punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ - ; /* average ReconPtr1 and ReconPtr2 */ - paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ - paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ - psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ - psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ - psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - movq [edx], mm0 ; /* write answer out */ - movq [8 + edx], mm2 ; /* write answer out */ - ; /* Increment pointers */ - add edx, 16 ; - add eax, PixelsPerLine ; - add ebx, ReconPixelsPerLine ; - add ecx, ReconPixelsPerLine ; - - - /* ITERATION 4 */ - movq mm0, [eax] ; /* mm0 = FiltPtr */ - movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ - movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ - movq mm2, mm0 ; /* dup to prepare for up conversion */ - movq mm3, mm1 ; /* dup to prepare for up conversion */ - movq mm5, mm4 ; /* dup to prepare for up conversion */ - ; /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ - punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ - punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ - punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ - ; /* average ReconPtr1 and ReconPtr2 */ - paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ - paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ - psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ - psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ - psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - movq [edx], mm0 ; /* write answer out */ - movq [8 + edx], mm2 ; /* write answer out */ - ; /* Increment pointers */ - add edx, 16 ; - add eax, PixelsPerLine ; - add ebx, ReconPixelsPerLine ; - add ecx, ReconPixelsPerLine ; - - - /* ITERATION 5 */ - movq mm0, [eax] ; /* mm0 = FiltPtr */ - movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ - movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ - movq mm2, mm0 ; /* dup to prepare for up conversion */ - movq mm3, mm1 ; /* dup to prepare for up conversion */ - movq mm5, mm4 ; /* dup to prepare for up conversion */ - ; /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ - punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ - punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ - punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ - ; /* average ReconPtr1 and ReconPtr2 */ - paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ - paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ - psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ - psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ - psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - movq [edx], mm0 ; /* write answer out */ - movq [8 + edx], mm2 ; /* write answer out */ - ; /* Increment pointers */ - add edx, 16 ; - add eax, PixelsPerLine ; - add ebx, ReconPixelsPerLine ; - add ecx, ReconPixelsPerLine ; - - - /* ITERATION 6 */ - movq mm0, [eax] ; /* mm0 = FiltPtr */ - movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ - movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ - movq mm2, mm0 ; /* dup to prepare for up conversion */ - movq mm3, mm1 ; /* dup to prepare for up conversion */ - movq mm5, mm4 ; /* dup to prepare for up conversion */ - ; /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ - punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ - punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ - punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ - ; /* average ReconPtr1 and ReconPtr2 */ - paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ - paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ - psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ - psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ - psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - movq [edx], mm0 ; /* write answer out */ - movq [8 + edx], mm2 ; /* write answer out */ - ; /* Increment pointers */ - add edx, 16 ; - add eax, PixelsPerLine ; - add ebx, ReconPixelsPerLine ; - add ecx, ReconPixelsPerLine ; - - - /* ITERATION 7 */ - movq mm0, [eax] ; /* mm0 = FiltPtr */ - movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ - movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ - movq mm2, mm0 ; /* dup to prepare for up conversion */ - movq mm3, mm1 ; /* dup to prepare for up conversion */ - movq mm5, mm4 ; /* dup to prepare for up conversion */ - ; /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ - punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ - punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ - punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ - ; /* average ReconPtr1 and ReconPtr2 */ - paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ - paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ - psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ - psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ - psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - movq [edx], mm0 ; /* write answer out */ - movq [8 + edx], mm2 ; /* write answer out */ - ; /* Increment pointers */ - add edx, 16 ; - add eax, PixelsPerLine ; - add ebx, ReconPixelsPerLine ; - add ecx, ReconPixelsPerLine ; - - - /* ITERATION 8 */ - movq mm0, [eax] ; /* mm0 = FiltPtr */ - movq mm1, [ebx] ; /* mm1 = ReconPtr1 */ - movq mm4, [ecx] ; /* mm1 = ReconPtr2 */ - movq mm2, mm0 ; /* dup to prepare for up conversion */ - movq mm3, mm1 ; /* dup to prepare for up conversion */ - movq mm5, mm4 ; /* dup to prepare for up conversion */ - ; /* convert from UINT8 to INT16 */ - punpcklbw mm0, mm7 ; /* mm0 = INT16(FiltPtr) */ - punpcklbw mm1, mm7 ; /* mm1 = INT16(ReconPtr1) */ - punpcklbw mm4, mm7 ; /* mm1 = INT16(ReconPtr2) */ - punpckhbw mm2, mm7 ; /* mm2 = INT16(FiltPtr) */ - punpckhbw mm3, mm7 ; /* mm3 = INT16(ReconPtr1) */ - punpckhbw mm5, mm7 ; /* mm3 = INT16(ReconPtr2) */ - ; /* average ReconPtr1 and ReconPtr2 */ - paddw mm1, mm4 ; /* mm1 = ReconPtr1 + ReconPtr2 */ - paddw mm3, mm5 ; /* mm3 = ReconPtr1 + ReconPtr2 */ - psrlw mm1, 1 ; /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ - psrlw mm3, 1 ; /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ - psubw mm0, mm1 ; /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - psubw mm2, mm3 ; /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - movq [edx], mm0 ; /* write answer out */ - movq [8 + edx], mm2 ; /* write answer out */ - ; /* Increment pointers */ - add edx, 16 ; - add eax, PixelsPerLine ; - add ebx, ReconPixelsPerLine ; - add ecx, ReconPixelsPerLine ; - - }; - - - - - -#endif -} - -static ogg_uint32_t row_sad8__mmx (unsigned char *Src1, unsigned char *Src2) -{ - -#if 0 - ogg_uint32_t SadValue; - ogg_uint32_t SadValue1; - - SadValue = DSP_OP_ABS_DIFF (Src1[0], Src2[0]) + - DSP_OP_ABS_DIFF (Src1[1], Src2[1]) + - DSP_OP_ABS_DIFF (Src1[2], Src2[2]) + - DSP_OP_ABS_DIFF (Src1[3], Src2[3]); - - SadValue1 = DSP_OP_ABS_DIFF (Src1[4], Src2[4]) + - DSP_OP_ABS_DIFF (Src1[5], Src2[5]) + - DSP_OP_ABS_DIFF (Src1[6], Src2[6]) + - DSP_OP_ABS_DIFF (Src1[7], Src2[7]); - - SadValue = ( SadValue > SadValue1 ) ? SadValue : SadValue1; - - return SadValue; - -#else - ogg_uint32_t MaxSad; - - - __asm { - align 16 - mov ebx, Src1 - mov ecx, Src2 - - - pxor mm6, mm6 ; /* zero out mm6 for unpack */ - pxor mm7, mm7 ; /* zero out mm7 for unpack */ - movq mm0, [ebx] ; /* take 8 bytes */ - movq mm1, [ecx] ; - - movq mm2, mm0 ; - psubusb mm0, mm1 ; /* A - B */ - psubusb mm1, mm2 ; /* B - A */ - por mm0, mm1 ; /* and or gives abs difference */ - - movq mm1, mm0 ; - - punpcklbw mm0, mm6 ; /* ; unpack low four bytes to higher precision */ - punpckhbw mm1, mm7 ; /* ; unpack high four bytes to higher precision */ - - movq mm2, mm0 ; - movq mm3, mm1 ; - psrlq mm2, 32 ; /* fold and add */ - psrlq mm3, 32 ; - paddw mm0, mm2 ; - paddw mm1, mm3 ; - movq mm2, mm0 ; - movq mm3, mm1 ; - psrlq mm2, 16 ; - psrlq mm3, 16 ; - paddw mm0, mm2 ; - paddw mm1, mm3 ; - - psubusw mm1, mm0 ; - paddw mm1, mm0 ; /* mm1 = max(mm1, mm0) */ - movd eax, mm1 ; - - and eax, 0xffff - mov MaxSad, eax - }; - return MaxSad; - - - - - -#endif -} - - - - -static ogg_uint32_t col_sad8x8__mmx (unsigned char *Src1, unsigned char *Src2, - ogg_uint32_t stride) -{ - -#if 0 - ogg_uint32_t SadValue[8] = {0,0,0,0,0,0,0,0}; - ogg_uint32_t SadValue2[8] = {0,0,0,0,0,0,0,0}; - ogg_uint32_t MaxSad = 0; - ogg_uint32_t i; - - for ( i = 0; i < 4; i++ ){ - SadValue[0] += abs(Src1[0] - Src2[0]); - SadValue[1] += abs(Src1[1] - Src2[1]); - SadValue[2] += abs(Src1[2] - Src2[2]); - SadValue[3] += abs(Src1[3] - Src2[3]); - SadValue[4] += abs(Src1[4] - Src2[4]); - SadValue[5] += abs(Src1[5] - Src2[5]); - SadValue[6] += abs(Src1[6] - Src2[6]); - SadValue[7] += abs(Src1[7] - Src2[7]); - - Src1 += stride; - Src2 += stride; - } - - for ( i = 0; i < 4; i++ ){ - SadValue2[0] += abs(Src1[0] - Src2[0]); - SadValue2[1] += abs(Src1[1] - Src2[1]); - SadValue2[2] += abs(Src1[2] - Src2[2]); - SadValue2[3] += abs(Src1[3] - Src2[3]); - SadValue2[4] += abs(Src1[4] - Src2[4]); - SadValue2[5] += abs(Src1[5] - Src2[5]); - SadValue2[6] += abs(Src1[6] - Src2[6]); - SadValue2[7] += abs(Src1[7] - Src2[7]); - - Src1 += stride; - Src2 += stride; - } - - for ( i = 0; i < 8; i++ ){ - if ( SadValue[i] > MaxSad ) - MaxSad = SadValue[i]; - if ( SadValue2[i] > MaxSad ) - MaxSad = SadValue2[i]; - } - - return MaxSad; -#else - ogg_uint32_t MaxSad; - - - __asm { - align 16 - mov ebx, Src1 - mov ecx, Src2 - - pxor mm3, mm3 ; /* zero out mm3 for unpack */ - pxor mm4, mm4 ; /* mm4 low sum */ - pxor mm5, mm5 ; /* mm5 high sum */ - pxor mm6, mm6 ; /* mm6 low sum */ - pxor mm7, mm7 ; /* mm7 high sum */ - mov edi, 4 ; /* 4 rows */ - label_1: ; - movq mm0, [ebx] ; /* take 8 bytes */ - movq mm1, [ecx] ; /* take 8 bytes */ - - movq mm2, mm0 ; - psubusb mm0, mm1 ; /* A - B */ - psubusb mm1, mm2 ; /* B - A */ - por mm0, mm1 ; /* and or gives abs difference */ - movq mm1, mm0 ; - - punpcklbw mm0, mm3 ; /* unpack to higher precision for accumulation */ - paddw mm4, mm0 ; /* accumulate difference... */ - punpckhbw mm1, mm3 ; /* unpack high four bytes to higher precision */ - paddw mm5, mm1 ; /* accumulate difference... */ - add ebx, stride ; /* Inc pointer into the new data */ - add ecx, stride ; /* Inc pointer into the new data */ - - dec edi ; - jnz label_1 ; - - mov edi, 4 ; /* 4 rows */ - label_2: ; - movq mm0, [ebx] ; /* take 8 bytes */ - movq mm1, [ecx] ; /* take 8 bytes */ - - movq mm2, mm0 ; - psubusb mm0, mm1 ; /* A - B */ - psubusb mm1, mm2 ; /* B - A */ - por mm0, mm1 ; /* and or gives abs difference */ - movq mm1, mm0 ; - - punpcklbw mm0, mm3 ; /* unpack to higher precision for accumulation */ - paddw mm6, mm0 ; /* accumulate difference... */ - punpckhbw mm1, mm3 ; /* unpack high four bytes to higher precision */ - paddw mm7, mm1 ; /* accumulate difference... */ - add ebx, stride ; /* Inc pointer into the new data */ - add ecx, stride ; /* Inc pointer into the new data */ - - dec edi ; - jnz label_2 ; - - psubusw mm7, mm6 ; - paddw mm7, mm6 ; /* mm7 = max(mm7, mm6) */ - psubusw mm5, mm4 ; - paddw mm5, mm4 ; /* mm5 = max(mm5, mm4) */ - psubusw mm7, mm5 ; - paddw mm7, mm5 ; /* mm7 = max(mm5, mm7) */ - movq mm6, mm7 ; - psrlq mm6, 32 ; - psubusw mm7, mm6 ; - paddw mm7, mm6 ; /* mm7 = max(mm5, mm7) */ - movq mm6, mm7 ; - psrlq mm6, 16 ; - psubusw mm7, mm6 ; - paddw mm7, mm6 ; /* mm7 = max(mm5, mm7) */ - movd eax, mm7 ; - and eax, 0xffff ; - - mov MaxSad, eax - }; - - return MaxSad; - - -#endif -} - -static ogg_uint32_t sad8x8__mmx (unsigned char *ptr1, ogg_uint32_t stride1, - unsigned char *ptr2, ogg_uint32_t stride2) -{ - -#if 0 - ogg_uint32_t i; - ogg_uint32_t sad = 0; - - for (i=8; i; i--) { - sad += DSP_OP_ABS_DIFF(ptr1[0], ptr2[0]); - sad += DSP_OP_ABS_DIFF(ptr1[1], ptr2[1]); - sad += DSP_OP_ABS_DIFF(ptr1[2], ptr2[2]); - sad += DSP_OP_ABS_DIFF(ptr1[3], ptr2[3]); - sad += DSP_OP_ABS_DIFF(ptr1[4], ptr2[4]); - sad += DSP_OP_ABS_DIFF(ptr1[5], ptr2[5]); - sad += DSP_OP_ABS_DIFF(ptr1[6], ptr2[6]); - sad += DSP_OP_ABS_DIFF(ptr1[7], ptr2[7]); - - /* Step to next row of block. */ - ptr1 += stride1; - ptr2 += stride2; - } - - return sad; -#else - ogg_uint32_t DiffVal; - - __asm { - align 16 - - mov ebx, ptr1 - mov edx, ptr2 - - pxor mm6, mm6 ; /* zero out mm6 for unpack */ - pxor mm7, mm7 ; /* mm7 contains the result */ - - ; /* ITERATION 1 */ - movq mm0, [ebx] ; /* take 8 bytes */ - movq mm1, [edx] ; - movq mm2, mm0 ; - - psubusb mm0, mm1 ; /* A - B */ - psubusb mm1, mm2 ; /* B - A */ - por mm0, mm1 ; /* and or gives abs difference */ - movq mm1, mm0 ; - - punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ - paddw mm7, mm0 ; /* accumulate difference... */ - punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ - add ebx, stride1 ; /* Inc pointer into the new data */ - paddw mm7, mm1 ; /* accumulate difference... */ - add edx, stride2 ; /* Inc pointer into ref data */ - - ; /* ITERATION 2 */ - movq mm0, [ebx] ; /* take 8 bytes */ - movq mm1, [edx] ; - movq mm2, mm0 ; - - psubusb mm0, mm1 ; /* A - B */ - psubusb mm1, mm2 ; /* B - A */ - por mm0, mm1 ; /* and or gives abs difference */ - movq mm1, mm0 ; - - punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ - paddw mm7, mm0 ; /* accumulate difference... */ - punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ - add ebx, stride1 ; /* Inc pointer into the new data */ - paddw mm7, mm1 ; /* accumulate difference... */ - add edx, stride2 ; /* Inc pointer into ref data */ - - - ; /* ITERATION 3 */ - movq mm0, [ebx] ; /* take 8 bytes */ - movq mm1, [edx] ; - movq mm2, mm0 ; - - psubusb mm0, mm1 ; /* A - B */ - psubusb mm1, mm2 ; /* B - A */ - por mm0, mm1 ; /* and or gives abs difference */ - movq mm1, mm0 ; - - punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ - paddw mm7, mm0 ; /* accumulate difference... */ - punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ - add ebx, stride1 ; /* Inc pointer into the new data */ - paddw mm7, mm1 ; /* accumulate difference... */ - add edx, stride2 ; /* Inc pointer into ref data */ - - ; /* ITERATION 4 */ - movq mm0, [ebx] ; /* take 8 bytes */ - movq mm1, [edx] ; - movq mm2, mm0 ; - - psubusb mm0, mm1 ; /* A - B */ - psubusb mm1, mm2 ; /* B - A */ - por mm0, mm1 ; /* and or gives abs difference */ - movq mm1, mm0 ; - - punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ - paddw mm7, mm0 ; /* accumulate difference... */ - punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ - add ebx, stride1 ; /* Inc pointer into the new data */ - paddw mm7, mm1 ; /* accumulate difference... */ - add edx, stride2 ; /* Inc pointer into ref data */ - - - ; /* ITERATION 5 */ - movq mm0, [ebx] ; /* take 8 bytes */ - movq mm1, [edx] ; - movq mm2, mm0 ; - - psubusb mm0, mm1 ; /* A - B */ - psubusb mm1, mm2 ; /* B - A */ - por mm0, mm1 ; /* and or gives abs difference */ - movq mm1, mm0 ; - - punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ - paddw mm7, mm0 ; /* accumulate difference... */ - punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ - add ebx, stride1 ; /* Inc pointer into the new data */ - paddw mm7, mm1 ; /* accumulate difference... */ - add edx, stride2 ; /* Inc pointer into ref data */ - - - ; /* ITERATION 6 */ - movq mm0, [ebx] ; /* take 8 bytes */ - movq mm1, [edx] ; - movq mm2, mm0 ; - - psubusb mm0, mm1 ; /* A - B */ - psubusb mm1, mm2 ; /* B - A */ - por mm0, mm1 ; /* and or gives abs difference */ - movq mm1, mm0 ; - - punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ - paddw mm7, mm0 ; /* accumulate difference... */ - punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ - add ebx, stride1 ; /* Inc pointer into the new data */ - paddw mm7, mm1 ; /* accumulate difference... */ - add edx, stride2 ; /* Inc pointer into ref data */ - - - ; /* ITERATION 7 */ - movq mm0, [ebx] ; /* take 8 bytes */ - movq mm1, [edx] ; - movq mm2, mm0 ; - - psubusb mm0, mm1 ; /* A - B */ - psubusb mm1, mm2 ; /* B - A */ - por mm0, mm1 ; /* and or gives abs difference */ - movq mm1, mm0 ; - - punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ - paddw mm7, mm0 ; /* accumulate difference... */ - punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ - add ebx, stride1 ; /* Inc pointer into the new data */ - paddw mm7, mm1 ; /* accumulate difference... */ - add edx, stride2 ; /* Inc pointer into ref data */ - - - - ; /* ITERATION 8 */ - movq mm0, [ebx] ; /* take 8 bytes */ - movq mm1, [edx] ; - movq mm2, mm0 ; - - psubusb mm0, mm1 ; /* A - B */ - psubusb mm1, mm2 ; /* B - A */ - por mm0, mm1 ; /* and or gives abs difference */ - movq mm1, mm0 ; - - punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ - paddw mm7, mm0 ; /* accumulate difference... */ - punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ - add ebx, stride1 ; /* Inc pointer into the new data */ - paddw mm7, mm1 ; /* accumulate difference... */ - add edx, stride2 ; /* Inc pointer into ref data */ - - - - ; /* ------ */ - - movq mm0, mm7 ; - psrlq mm7, 32 ; - paddw mm7, mm0 ; - movq mm0, mm7 ; - psrlq mm7, 16 ; - paddw mm7, mm0 ; - movd eax, mm7 ; - and eax, 0xffff ; - - mov DiffVal, eax - }; - - return DiffVal; - - - -#endif -} - -static ogg_uint32_t sad8x8_thres__mmx (unsigned char *ptr1, ogg_uint32_t stride1, - unsigned char *ptr2, ogg_uint32_t stride2, - ogg_uint32_t thres) -{ -#if 0 - ogg_uint32_t i; - ogg_uint32_t sad = 0; - - for (i=8; i; i--) { - sad += DSP_OP_ABS_DIFF(ptr1[0], ptr2[0]); - sad += DSP_OP_ABS_DIFF(ptr1[1], ptr2[1]); - sad += DSP_OP_ABS_DIFF(ptr1[2], ptr2[2]); - sad += DSP_OP_ABS_DIFF(ptr1[3], ptr2[3]); - sad += DSP_OP_ABS_DIFF(ptr1[4], ptr2[4]); - sad += DSP_OP_ABS_DIFF(ptr1[5], ptr2[5]); - sad += DSP_OP_ABS_DIFF(ptr1[6], ptr2[6]); - sad += DSP_OP_ABS_DIFF(ptr1[7], ptr2[7]); - - if (sad > thres ) - break; - - /* Step to next row of block. */ - ptr1 += stride1; - ptr2 += stride2; - } - - return sad; -#else - return sad8x8__mmx (ptr1, stride1, ptr2, stride2); -#endif -} - - -static ogg_uint32_t sad8x8_xy2_thres__mmx (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr1, - unsigned char *RefDataPtr2, ogg_uint32_t RefStride, - ogg_uint32_t thres) -{ -#if 0 - ogg_uint32_t i; - ogg_uint32_t sad = 0; - - for (i=8; i; i--) { - sad += DSP_OP_ABS_DIFF(SrcData[0], DSP_OP_AVG (RefDataPtr1[0], RefDataPtr2[0])); - sad += DSP_OP_ABS_DIFF(SrcData[1], DSP_OP_AVG (RefDataPtr1[1], RefDataPtr2[1])); - sad += DSP_OP_ABS_DIFF(SrcData[2], DSP_OP_AVG (RefDataPtr1[2], RefDataPtr2[2])); - sad += DSP_OP_ABS_DIFF(SrcData[3], DSP_OP_AVG (RefDataPtr1[3], RefDataPtr2[3])); - sad += DSP_OP_ABS_DIFF(SrcData[4], DSP_OP_AVG (RefDataPtr1[4], RefDataPtr2[4])); - sad += DSP_OP_ABS_DIFF(SrcData[5], DSP_OP_AVG (RefDataPtr1[5], RefDataPtr2[5])); - sad += DSP_OP_ABS_DIFF(SrcData[6], DSP_OP_AVG (RefDataPtr1[6], RefDataPtr2[6])); - sad += DSP_OP_ABS_DIFF(SrcData[7], DSP_OP_AVG (RefDataPtr1[7], RefDataPtr2[7])); - - if ( sad > thres ) - break; - - /* Step to next row of block. */ - SrcData += SrcStride; - RefDataPtr1 += RefStride; - RefDataPtr2 += RefStride; - } - - return sad; -#else - ogg_uint32_t DiffVal; - - __asm { - align 16 - - mov ebx, SrcData - mov ecx, RefDataPtr1 - mov edx, RefDataPtr2 - - - pcmpeqd mm5, mm5 ; /* fefefefefefefefe in mm5 */ - paddb mm5, mm5 ; - ; - pxor mm6, mm6 ; /* zero out mm6 for unpack */ - pxor mm7, mm7 ; /* mm7 contains the result */ - mov edi, 8 ; /* 8 rows */ - loop_start: ; - movq mm0, [ebx] ; /* take 8 bytes */ - - movq mm2, [ecx] ; - movq mm3, [edx] ; /* take average of mm2 and mm3 */ - movq mm1, mm2 ; - pand mm1, mm3 ; - pxor mm3, mm2 ; - pand mm3, mm5 ; - psrlq mm3, 1 ; - paddb mm1, mm3 ; - - movq mm2, mm0 ; - - psubusb mm0, mm1 ; /* A - B */ - psubusb mm1, mm2 ; /* B - A */ - por mm0, mm1 ; /* and or gives abs difference */ - movq mm1, mm0 ; - - punpcklbw mm0, mm6 ; /* unpack to higher precision for accumulation */ - paddw mm7, mm0 ; /* accumulate difference... */ - punpckhbw mm1, mm6 ; /* unpack high four bytes to higher precision */ - add ebx, SrcStride ; /* Inc pointer into the new data */ - paddw mm7, mm1 ; /* accumulate difference... */ - add ecx, RefStride ; /* Inc pointer into ref data */ - add edx, RefStride ; /* Inc pointer into ref data */ - - dec edi ; - jnz loop_start ; - - movq mm0, mm7 ; - psrlq mm7, 32 ; - paddw mm7, mm0 ; - movq mm0, mm7 ; - psrlq mm7, 16 ; - paddw mm7, mm0 ; - movd eax, mm7 ; - and eax, 0xffff ; - - mov DiffVal, eax - }; - - return DiffVal; - - - -#endif -} - -static ogg_uint32_t intra8x8_err__mmx (unsigned char *DataPtr, ogg_uint32_t Stride) -{ -#if 0 - ogg_uint32_t i; - ogg_uint32_t XSum=0; - ogg_uint32_t XXSum=0; - - for (i=8; i; i--) { - /* Examine alternate pixel locations. */ - XSum += DataPtr[0]; - XXSum += DataPtr[0]*DataPtr[0]; - XSum += DataPtr[1]; - XXSum += DataPtr[1]*DataPtr[1]; - XSum += DataPtr[2]; - XXSum += DataPtr[2]*DataPtr[2]; - XSum += DataPtr[3]; - XXSum += DataPtr[3]*DataPtr[3]; - XSum += DataPtr[4]; - XXSum += DataPtr[4]*DataPtr[4]; - XSum += DataPtr[5]; - XXSum += DataPtr[5]*DataPtr[5]; - XSum += DataPtr[6]; - XXSum += DataPtr[6]*DataPtr[6]; - XSum += DataPtr[7]; - XXSum += DataPtr[7]*DataPtr[7]; - - /* Step to next row of block. */ - DataPtr += Stride; - } - - /* Compute population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum ) ); -#else - ogg_uint32_t XSum; - ogg_uint32_t XXSum; - - __asm { - align 16 - - mov ecx, DataPtr - - pxor mm5, mm5 ; - pxor mm6, mm6 ; - pxor mm7, mm7 ; - mov edi, 8 ; - loop_start: - movq mm0, [ecx] ; /* take 8 bytes */ - movq mm2, mm0 ; - - punpcklbw mm0, mm6 ; - punpckhbw mm2, mm6 ; - - paddw mm5, mm0 ; - paddw mm5, mm2 ; - - pmaddwd mm0, mm0 ; - pmaddwd mm2, mm2 ; - ; - paddd mm7, mm0 ; - paddd mm7, mm2 ; - - add ecx, Stride ; /* Inc pointer into src data */ - - dec edi ; - jnz loop_start ; - - movq mm0, mm5 ; - psrlq mm5, 32 ; - paddw mm5, mm0 ; - movq mm0, mm5 ; - psrlq mm5, 16 ; - paddw mm5, mm0 ; - movd edi, mm5 ; - movsx edi, di ; - mov eax, edi ; - - movq mm0, mm7 ; - psrlq mm7, 32 ; - paddd mm7, mm0 ; - movd ebx, mm7 ; - - mov XSum, eax - mov XXSum, ebx; - - }; - /* Compute population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum ) ); - - - -#endif -} - -static ogg_uint32_t inter8x8_err__mmx (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr, ogg_uint32_t RefStride) -{ - -#if 0 - ogg_uint32_t i; - ogg_uint32_t XSum=0; - ogg_uint32_t XXSum=0; - ogg_int32_t DiffVal; - - for (i=8; i; i--) { - DiffVal = DSP_OP_DIFF (SrcData[0], RefDataPtr[0]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF (SrcData[1], RefDataPtr[1]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF (SrcData[2], RefDataPtr[2]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF (SrcData[3], RefDataPtr[3]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF (SrcData[4], RefDataPtr[4]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF (SrcData[5], RefDataPtr[5]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF (SrcData[6], RefDataPtr[6]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF (SrcData[7], RefDataPtr[7]); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - /* Step to next row of block. */ - SrcData += SrcStride; - RefDataPtr += RefStride; - } - - /* Compute and return population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum )); -#else - ogg_uint32_t XSum; - ogg_uint32_t XXSum; - - - __asm { - align 16 - - mov ecx, SrcData - mov edx, RefDataPtr - - pxor mm5, mm5 ; - pxor mm6, mm6 ; - pxor mm7, mm7 ; - mov edi, 8 ; - loop_start: ; - movq mm0, [ecx] ; /* take 8 bytes */ - movq mm1, [edx] ; - movq mm2, mm0 ; - movq mm3, mm1 ; - - punpcklbw mm0, mm6 ; - punpcklbw mm1, mm6 ; - punpckhbw mm2, mm6 ; - punpckhbw mm3, mm6 ; - - psubsw mm0, mm1 ; - psubsw mm2, mm3 ; - - paddw mm5, mm0 ; - paddw mm5, mm2 ; - - pmaddwd mm0, mm0 ; - pmaddwd mm2, mm2 ; - ; - paddd mm7, mm0 ; - paddd mm7, mm2 ; - - add ecx, SrcStride ; /* Inc pointer into src data */ - add edx, RefStride ; /* Inc pointer into ref data */ - - dec edi ; - jnz loop_start ; - - movq mm0, mm5 ; - psrlq mm5, 32 ; - paddw mm5, mm0 ; - movq mm0, mm5 ; - psrlq mm5, 16 ; - paddw mm5, mm0 ; - movd edi, mm5 ; - movsx edi, di ; - mov eax, edi ; - - movq mm0, mm7 ; - psrlq mm7, 32 ; - paddd mm7, mm0 ; - movd ebx, mm7 ; - - mov XSum, eax - mov XXSum, ebx - - }; - - /* Compute and return population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum )); - - -#endif -} - -static ogg_uint32_t inter8x8_err_xy2__mmx (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr1, - unsigned char *RefDataPtr2, ogg_uint32_t RefStride) -{ -#if 0 - ogg_uint32_t i; - ogg_uint32_t XSum=0; - ogg_uint32_t XXSum=0; - ogg_int32_t DiffVal; - - for (i=8; i; i--) { - DiffVal = DSP_OP_DIFF(SrcData[0], DSP_OP_AVG (RefDataPtr1[0], RefDataPtr2[0])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF(SrcData[1], DSP_OP_AVG (RefDataPtr1[1], RefDataPtr2[1])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF(SrcData[2], DSP_OP_AVG (RefDataPtr1[2], RefDataPtr2[2])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF(SrcData[3], DSP_OP_AVG (RefDataPtr1[3], RefDataPtr2[3])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF(SrcData[4], DSP_OP_AVG (RefDataPtr1[4], RefDataPtr2[4])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF(SrcData[5], DSP_OP_AVG (RefDataPtr1[5], RefDataPtr2[5])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF(SrcData[6], DSP_OP_AVG (RefDataPtr1[6], RefDataPtr2[6])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - DiffVal = DSP_OP_DIFF(SrcData[7], DSP_OP_AVG (RefDataPtr1[7], RefDataPtr2[7])); - XSum += DiffVal; - XXSum += DiffVal*DiffVal; - - /* Step to next row of block. */ - SrcData += SrcStride; - RefDataPtr1 += RefStride; - RefDataPtr2 += RefStride; - } - - /* Compute and return population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum )); -#else - ogg_uint32_t XSum; - ogg_uint32_t XXSum; - - __asm { - align 16 - - mov ebx, SrcData - mov ecx, RefDataPtr1 - mov edx, RefDataPtr2 - - pcmpeqd mm4, mm4 ; /* fefefefefefefefe in mm4 */ - paddb mm4, mm4 ; - pxor mm5, mm5 ; - pxor mm6, mm6 ; - pxor mm7, mm7 ; - mov edi, 8 ; - loop_start: ; - movq mm0, [ebx] ; /* take 8 bytes */ - - movq mm2, [ecx] ; - movq mm3, [edx] ; /* take average of mm2 and mm3 */ - movq mm1, mm2 ; - pand mm1, mm3 ; - pxor mm3, mm2 ; - pand mm3, mm4 ; - psrlq mm3, 1 ; - paddb mm1, mm3 ; - - movq mm2, mm0 ; - movq mm3, mm1 ; - - punpcklbw mm0, mm6 ; - punpcklbw mm1, mm6 ; - punpckhbw mm2, mm6 ; - punpckhbw mm3, mm6 ; - - psubsw mm0, mm1 ; - psubsw mm2, mm3 ; - - paddw mm5, mm0 ; - paddw mm5, mm2 ; - - pmaddwd mm0, mm0 ; - pmaddwd mm2, mm2 ; - ; - paddd mm7, mm0 ; - paddd mm7, mm2 ; - - add ebx, SrcStride ; /* Inc pointer into src data */ - add ecx, RefStride ; /* Inc pointer into ref data */ - add edx, RefStride ; /* Inc pointer into ref data */ - - dec edi ; - jnz loop_start ; - - movq mm0, mm5 ; - psrlq mm5, 32 ; - paddw mm5, mm0 ; - movq mm0, mm5 ; - psrlq mm5, 16 ; - paddw mm5, mm0 ; - movd edi, mm5 ; - movsx edi, di ; - mov XSum, edi ; /* movl eax, edi ; Modified for vc to resuse eax*/ - - movq mm0, mm7 ; - psrlq mm7, 32 ; - paddd mm7, mm0 ; - movd XXSum, mm7 ; /*movd eax, mm7 ; Modified for vc to reuse eax */ - }; - - return (( (XXSum<<6) - XSum*XSum )); - -#endif -} - -static void restore_fpu (void) -{ - - __asm { - emms - } - -} - -void dsp_mmx_init(DspFunctions *funcs) -{ - funcs->restore_fpu = restore_fpu; - funcs->sub8x8 = sub8x8__mmx; - funcs->sub8x8_128 = sub8x8_128__mmx; - funcs->sub8x8avg2 = sub8x8avg2__mmx; - funcs->row_sad8 = row_sad8__mmx; - funcs->col_sad8x8 = col_sad8x8__mmx; - funcs->sad8x8 = sad8x8__mmx; - funcs->sad8x8_thres = sad8x8_thres__mmx; - funcs->sad8x8_xy2_thres = sad8x8_xy2_thres__mmx; - funcs->intra8x8_err = intra8x8_err__mmx; - funcs->inter8x8_err = inter8x8_err__mmx; - funcs->inter8x8_err_xy2 = inter8x8_err_xy2__mmx; -} - diff --git a/Engine/lib/libtheora/lib/enc/x86_32_vs/fdct_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32_vs/fdct_mmx.c deleted file mode 100644 index 65cd9c367..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_32_vs/fdct_mmx.c +++ /dev/null @@ -1,333 +0,0 @@ -;//========================================================================== -;// -;// THIS CODE AND INFORMATION IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY -;// KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE -;// IMPLIED WARRANTIES OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR -;// PURPOSE. -;// -;// Copyright (c) 1999 - 2001 On2 Technologies Inc. All Rights Reserved. -;// -;//-------------------------------------------------------------------------- - -#include "theora/theora.h" -#include "../codec_internal.h" -#include "../dsp.h" - - -static const ogg_int64_t xC1S7 = 0x0fb15fb15fb15fb15; -static const ogg_int64_t xC2S6 = 0x0ec83ec83ec83ec83; -static const ogg_int64_t xC3S5 = 0x0d4dbd4dbd4dbd4db; -static const ogg_int64_t xC4S4 = 0x0b505b505b505b505; -static const ogg_int64_t xC5S3 = 0x08e3a8e3a8e3a8e3a; -static const ogg_int64_t xC6S2 = 0x061f861f861f861f8; -static const ogg_int64_t xC7S1 = 0x031f131f131f131f1; - - -static __inline void Transpose_mmx( ogg_int16_t *InputData1, ogg_int16_t *OutputData1, - ogg_int16_t *InputData2, ogg_int16_t *OutputData2) -{ - - __asm { - align 16 - mov eax, InputData1 - mov ebx, InputData2 - mov ecx, OutputData1 - mov edx, OutputData2 - - - movq mm0, [eax] ; /* mm0 = a0 a1 a2 a3 */ - movq mm4, [ebx] ; /* mm4 = e4 e5 e6 e7 */ - movq mm1, [16 + eax] ; /* mm1 = b0 b1 b2 b3 */ - movq mm5, [16 + ebx] ; /* mm5 = f4 f5 f6 f7 */ - movq mm2, [32 + eax] ; /* mm2 = c0 c1 c2 c3 */ - movq mm6, [32 + ebx] ; /* mm6 = g4 g5 g6 g7 */ - movq mm3, [48 + eax] ; /* mm3 = d0 d1 d2 d3 */ - movq [16 + ecx], mm1 ; /* save b0 b1 b2 b3 */ - movq mm7, [48 + ebx] ; /* mm7 = h0 h1 h2 h3 */ - ; /* Transpose 2x8 block */ - movq mm1, mm4 ; /* mm1 = e3 e2 e1 e0 */ - punpcklwd mm4, mm5 ; /* mm4 = f1 e1 f0 e0 */ - movq [ecx], mm0 ; /* save a3 a2 a1 a0 */ - punpckhwd mm1, mm5 ; /* mm1 = f3 e3 f2 e2 */ - movq mm0, mm6 ; /* mm0 = g3 g2 g1 g0 */ - punpcklwd mm6, mm7 ; /* mm6 = h1 g1 h0 g0 */ - movq mm5, mm4 ; /* mm5 = f1 e1 f0 e0 */ - punpckldq mm4, mm6 ; /* mm4 = h0 g0 f0 e0 = MM4 */ - punpckhdq mm5, mm6 ; /* mm5 = h1 g1 f1 e1 = MM5 */ - movq mm6, mm1 ; /* mm6 = f3 e3 f2 e2 */ - movq [edx], mm4 ; - punpckhwd mm0, mm7 ; /* mm0 = h3 g3 h2 g2 */ - movq [16 + edx], mm5 ; - punpckhdq mm6, mm0 ; /* mm6 = h3 g3 f3 e3 = MM7 */ - movq mm4, [ecx] ; /* mm4 = a3 a2 a1 a0 */ - punpckldq mm1, mm0 ; /* mm1 = h2 g2 f2 e2 = MM6 */ - movq mm5, [16 + ecx] ; /* mm5 = b3 b2 b1 b0 */ - movq mm0, mm4 ; /* mm0 = a3 a2 a1 a0 */ - movq [48 + edx], mm6 ; - punpcklwd mm0, mm5 ; /* mm0 = b1 a1 b0 a0 */ - movq [32 + edx], mm1 ; - punpckhwd mm4, mm5 ; /* mm4 = b3 a3 b2 a2 */ - movq mm5, mm2 ; /* mm5 = c3 c2 c1 c0 */ - punpcklwd mm2, mm3 ; /* mm2 = d1 c1 d0 c0 */ - movq mm1, mm0 ; /* mm1 = b1 a1 b0 a0 */ - punpckldq mm0, mm2 ; /* mm0 = d0 c0 b0 a0 = MM0 */ - punpckhdq mm1, mm2 ; /* mm1 = d1 c1 b1 a1 = MM1 */ - movq mm2, mm4 ; /* mm2 = b3 a3 b2 a2 */ - movq [ecx], mm0 ; - punpckhwd mm5, mm3 ; /* mm5 = d3 c3 d2 c2 */ - movq [16 + ecx], mm1 ; - punpckhdq mm4, mm5 ; /* mm4 = d3 c3 b3 a3 = MM3 */ - punpckldq mm2, mm5 ; /* mm2 = d2 c2 b2 a2 = MM2 */ - movq [48 + ecx], mm4 ; - movq [32 + ecx], mm2 ; - - }; - - -} - -static __inline void Fdct_mmx( ogg_int16_t *InputData1, ogg_int16_t *InputData2, ogg_int16_t *temp) -{ - - __asm { - align 16 - - - mov eax, InputData1 - mov ebx, InputData2 - mov ecx, temp - movq mm0, [eax] ; - movq mm1, [16 + eax] ; - movq mm2, [48 + eax] ; - movq mm3, [16 + ebx] ; - movq mm4, mm0 ; - movq mm5, mm1 ; - movq mm6, mm2 ; - movq mm7, mm3 ; - ; - paddsw mm0, [48 + ebx] ; /* mm0 = ip0 + ip7 = is07 */ - paddsw mm1, [32 + eax] ; /* mm1 = ip1 + ip2 = is12 */ - paddsw mm2, [ebx] ; /* mm2 = ip3 + ip4 = is34 */ - paddsw mm3, [32 + ebx] ; /* mm3 = ip5 + ip6 = is56 */ - psubsw mm4, [48 + ebx] ; /* mm4 = ip0 - ip7 = id07 */ - psubsw mm5, [32 + eax] ; /* mm5 = ip1 - ip2 = id12 */ - ; - psubsw mm0, mm2 ; /* mm0 = is07 - is34 */ - ; - paddsw mm2, mm2 ; - ; - psubsw mm6, [ebx] ; /* mm6 = ip3 - ip4 = id34 */ - ; - paddsw mm2, mm0 ; /* mm2 = is07 + is34 = is0734 */ - psubsw mm1, mm3 ; /* mm1 = is12 - is56 */ - movq [ecx], mm0 ; /* Save is07 - is34 to free mm0; */ - paddsw mm3, mm3 ; - paddsw mm3, mm1 ; /* mm3 = is12 + 1s56 = is1256 */ - ; - psubsw mm7, [32 + ebx] ; /* mm7 = ip5 - ip6 = id56 */ - ; /* ------------------------------------------------------------------- */ - psubsw mm5, mm7 ; /* mm5 = id12 - id56 */ - paddsw mm7, mm7 ; - paddsw mm7, mm5 ; /* mm7 = id12 + id56 */ - ; /* ------------------------------------------------------------------- */ - psubsw mm2, mm3 ; /* mm2 = is0734 - is1256 */ - paddsw mm3, mm3 ; - ; - movq mm0, mm2 ; /* make a copy */ - paddsw mm3, mm2 ; /* mm3 = is0734 + is1256 */ - ; - pmulhw mm0, xC4S4 ; /* mm0 = xC4S4 * ( is0734 - is1256 ) - ( is0734 - is1256 ) */ - paddw mm0, mm2 ; /* mm0 = xC4S4 * ( is0734 - is1256 ) */ - psrlw mm2, 15 ; - paddw mm0, mm2 ; /* Truncate mm0, now it is op[4] */ - ; - movq mm2, mm3 ; - movq [ebx], mm0 ; /* save ip4, now mm0,mm2 are free */ - ; - movq mm0, mm3 ; - pmulhw mm3, xC4S4 ; /* mm3 = xC4S4 * ( is0734 +is1256 ) - ( is0734 +is1256 ) */ - ; - psrlw mm2, 15 ; - paddw mm3, mm0 ; /* mm3 = xC4S4 * ( is0734 +is1256 ) */ - paddw mm3, mm2 ; /* Truncate mm3, now it is op[0] */ - ; - movq [eax], mm3 ; - ; /* ------------------------------------------------------------------- */ - movq mm3, [ecx] ; /* mm3 = irot_input_y */ - pmulhw mm3, xC2S6 ; /* mm3 = xC2S6 * irot_input_y - irot_input_y */ - ; - movq mm2, [ecx] ; - movq mm0, mm2 ; - ; - psrlw mm2, 15 ; /* mm3 = xC2S6 * irot_input_y */ - paddw mm3, mm0 ; - ; - paddw mm3, mm2 ; /* Truncated */ - movq mm0, mm5 ; - ; - movq mm2, mm5 ; - pmulhw mm0, xC6S2 ; /* mm0 = xC6S2 * irot_input_x */ - ; - psrlw mm2, 15 ; - paddw mm0, mm2 ; /* Truncated */ - ; - paddsw mm3, mm0 ; /* ip[2] */ - movq [32 + eax], mm3 ; /* Save ip2 */ - ; - movq mm0, mm5 ; - movq mm2, mm5 ; - ; - pmulhw mm5, xC2S6 ; /* mm5 = xC2S6 * irot_input_x - irot_input_x */ - psrlw mm2, 15 ; - ; - movq mm3, [ecx] ; - paddw mm5, mm0 ; /* mm5 = xC2S6 * irot_input_x */ - ; - paddw mm5, mm2 ; /* Truncated */ - movq mm2, mm3 ; - ; - pmulhw mm3, xC6S2 ; /* mm3 = xC6S2 * irot_input_y */ - psrlw mm2, 15 ; - ; - paddw mm3, mm2 ; /* Truncated */ - psubsw mm3, mm5 ; - ; - movq [32 + ebx], mm3 ; - ; /* ------------------------------------------------------------------- */ - movq mm0, xC4S4 ; - movq mm2, mm1 ; - movq mm3, mm1 ; - ; - pmulhw mm1, mm0 ; /* mm0 = xC4S4 * ( is12 - is56 ) - ( is12 - is56 ) */ - psrlw mm2, 15 ; - ; - paddw mm1, mm3 ; /* mm0 = xC4S4 * ( is12 - is56 ) */ - paddw mm1, mm2 ; /* Truncate mm1, now it is icommon_product1 */ - ; - movq mm2, mm7 ; - movq mm3, mm7 ; - ; - pmulhw mm7, mm0 ; /* mm7 = xC4S4 * ( id12 + id56 ) - ( id12 + id56 ) */ - psrlw mm2, 15 ; - ; - paddw mm7, mm3 ; /* mm7 = xC4S4 * ( id12 + id56 ) */ - paddw mm7, mm2 ; /* Truncate mm7, now it is icommon_product2 */ - ; /* ------------------------------------------------------------------- */ - pxor mm0, mm0 ; /* Clear mm0 */ - psubsw mm0, mm6 ; /* mm0 = - id34 */ - ; - psubsw mm0, mm7 ; /* mm0 = - ( id34 + idcommon_product2 ) */ - paddsw mm6, mm6 ; - paddsw mm6, mm0 ; /* mm6 = id34 - icommon_product2 */ - ; - psubsw mm4, mm1 ; /* mm4 = id07 - icommon_product1 */ - paddsw mm1, mm1 ; - paddsw mm1, mm4 ; /* mm1 = id07 + icommon_product1 */ - ; /* ------------------------------------------------------------------- */ - movq mm7, xC1S7 ; - movq mm2, mm1 ; - ; - movq mm3, mm1 ; - pmulhw mm1, mm7 ; /* mm1 = xC1S7 * irot_input_x - irot_input_x */ - ; - movq mm7, xC7S1 ; - psrlw mm2, 15 ; - ; - paddw mm1, mm3 ; /* mm1 = xC1S7 * irot_input_x */ - paddw mm1, mm2 ; /* Trucated */ - ; - pmulhw mm3, mm7 ; /* mm3 = xC7S1 * irot_input_x */ - paddw mm3, mm2 ; /* Truncated */ - ; - movq mm5, mm0 ; - movq mm2, mm0 ; - ; - movq mm7, xC1S7 ; - pmulhw mm0, mm7 ; /* mm0 = xC1S7 * irot_input_y - irot_input_y */ - ; - movq mm7, xC7S1 ; - psrlw mm2, 15 ; - ; - paddw mm0, mm5 ; /* mm0 = xC1S7 * irot_input_y */ - paddw mm0, mm2 ; /* Truncated */ - ; - pmulhw mm5, mm7 ; /* mm5 = xC7S1 * irot_input_y */ - paddw mm5, mm2 ; /* Truncated */ - ; - psubsw mm1, mm5 ; /* mm1 = xC1S7 * irot_input_x - xC7S1 * irot_input_y = ip1 */ - paddsw mm3, mm0 ; /* mm3 = xC7S1 * irot_input_x - xC1S7 * irot_input_y = ip7 */ - ; - movq [16 + eax], mm1 ; - movq [48 + ebx], mm3 ; - ; /* ------------------------------------------------------------------- */ - movq mm0, xC3S5 ; - movq mm1, xC5S3 ; - ; - movq mm5, mm6 ; - movq mm7, mm6 ; - ; - movq mm2, mm4 ; - movq mm3, mm4 ; - ; - pmulhw mm4, mm0 ; /* mm4 = xC3S5 * irot_input_x - irot_input_x */ - pmulhw mm6, mm1 ; /* mm6 = xC5S3 * irot_input_y - irot_input_y */ - ; - psrlw mm2, 15 ; - psrlw mm5, 15 ; - ; - paddw mm4, mm3 ; /* mm4 = xC3S5 * irot_input_x */ - paddw mm6, mm7 ; /* mm6 = xC5S3 * irot_input_y */ - ; - paddw mm4, mm2 ; /* Truncated */ - paddw mm6, mm5 ; /* Truncated */ - ; - psubsw mm4, mm6 ; /* ip3 */ - movq [48 + eax], mm4 ; - ; - movq mm4, mm3 ; - movq mm6, mm7 ; - ; - pmulhw mm3, mm1 ; /* mm3 = xC5S3 * irot_input_x - irot_input_x */ - pmulhw mm7, mm0 ; /* mm7 = xC3S5 * irot_input_y - irot_input_y */ - ; - paddw mm4, mm2 ; - paddw mm6, mm5 ; - ; - paddw mm3, mm4 ; /* mm3 = xC5S3 * irot_input_x */ - paddw mm7, mm6 ; /* mm7 = xC3S5 * irot_input_y */ - ; - paddw mm3, mm7 ; /* ip5 */ - movq [16 + ebx], mm3 ; - -}; - -} - - -static void fdct_short__mmx ( ogg_int16_t *InputData, ogg_int16_t *OutputData) -{ - - static ogg_int16_t tmp[32]; - ogg_int16_t* align_tmp = (ogg_int16_t*)((unsigned char*)tmp + (16 - ((int)tmp)&15)); - - - Transpose_mmx(InputData, OutputData, InputData + 4, OutputData + 4); - Fdct_mmx(OutputData, OutputData + 4, align_tmp); - - Transpose_mmx(InputData + 32, OutputData + 32, InputData + 36, OutputData + 36); - Fdct_mmx(OutputData+32, OutputData + 36, align_tmp); - - Transpose_mmx(OutputData, OutputData, OutputData + 32, OutputData + 32); - Fdct_mmx(OutputData, OutputData + 32, align_tmp); - - Transpose_mmx(OutputData + 4, OutputData + 4, OutputData + 36, OutputData + 36); - Fdct_mmx(OutputData + 4, OutputData + 36, align_tmp); - - __asm emms - -} - -void dsp_mmx_fdct_init(DspFunctions *funcs) -{ - funcs->fdct_short = fdct_short__mmx; -} diff --git a/Engine/lib/libtheora/lib/enc/x86_32_vs/recon_mmx.c b/Engine/lib/libtheora/lib/enc/x86_32_vs/recon_mmx.c deleted file mode 100644 index 1e0f1f095..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_32_vs/recon_mmx.c +++ /dev/null @@ -1,197 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: reconstruct.c,v 1.6 2003/12/03 08:59:41 arc Exp $ - - ********************************************************************/ - -#include "../codec_internal.h" - - -static const unsigned __int64 V128 = 0x8080808080808080; - -static void copy8x8__mmx (unsigned char *src, - unsigned char *dest, - unsigned int stride) -{ - - //Is this even the fastest way to do this? - __asm { - align 16 - - mov eax, src - mov ebx, dest - mov ecx, stride - - lea edi, [ecx + ecx * 2] - movq mm0, [eax] - movq mm1, [eax + ecx] - movq mm2, [eax + ecx * 2] - movq mm3, [eax + edi] - lea eax, [eax + ecx * 4] - movq [ebx], mm0 - movq [ebx + ecx], mm1 - movq [ebx + ecx * 2], mm2 - movq [ebx + edi], mm3 - lea ebx, [ebx + ecx * 4] - movq mm0, [eax] - movq mm1, [eax + ecx] - movq mm2, [eax + ecx * 2] - movq mm3, [eax + edi] - movq [ebx], mm0 - movq [ebx + ecx], mm1 - movq [ebx + ecx * 2], mm2 - movq [ebx + edi], mm3 - - }; - -} - -static void recon_intra8x8__mmx (unsigned char *ReconPtr, ogg_int16_t *ChangePtr, - ogg_uint32_t LineStep) -{ - - __asm { - align 16 - - mov eax, ReconPtr - mov ebx, ChangePtr - mov ecx, LineStep - - movq mm0, V128 - - lea edi, [128 + ebx] - loop_start: - movq mm2, [ebx] - - packsswb mm2, [8 + ebx] - por mm0, mm0 - pxor mm2, mm0 - lea ebx, [16 + ebx] - cmp ebx, edi - - movq [eax], mm2 - - - - lea eax, [eax + ecx] - jc loop_start - - - }; - -} - - - - - -static void recon_inter8x8__mmx (unsigned char *ReconPtr, unsigned char *RefPtr, - ogg_int16_t *ChangePtr, ogg_uint32_t LineStep) -{ - - __asm { - - align 16 - - mov eax, ReconPtr - mov ebx, ChangePtr - mov ecx, LineStep - mov edx, RefPtr - - pxor mm0, mm0 - lea edi, [128 + ebx] - - loop_start: - movq mm2, [edx] - - movq mm4, [ebx] - movq mm3, mm2 - movq mm5, [8 + ebx] - punpcklbw mm2, mm0 - paddsw mm2, mm4 - punpckhbw mm3, mm0 - paddsw mm3, mm5 - add edx, ecx - packuswb mm2, mm3 - lea ebx, [16 + ebx] - cmp ebx, edi - - movq [eax], mm2 - - lea eax, [eax + ecx] - jc loop_start - - }; -} - - - - -static void recon_inter8x8_half__mmx (unsigned char *ReconPtr, unsigned char *RefPtr1, - unsigned char *RefPtr2, ogg_int16_t *ChangePtr, - ogg_uint32_t LineStep) -{ - __asm { - align 16 - - mov eax, ReconPtr - mov ebx, ChangePtr - mov ecx, RefPtr1 - mov edx, RefPtr2 - - pxor mm0, mm0 - lea edi, [128 + ebx] - - loop_start: - movq mm2, [ecx] - movq mm4, [edx] - - movq mm3, mm2 - punpcklbw mm2, mm0 - movq mm5, mm4 - movq mm6, [ebx] - punpckhbw mm3, mm0 - movq mm7, [8 + ebx] - punpcklbw mm4, mm0 - punpckhbw mm5, mm0 - paddw mm2, mm4 - paddw mm3, mm5 - psrlw mm2, 1 - psrlw mm3, 1 - paddw mm2, mm6 - paddw mm3, mm7 - lea ebx, [16 + ebx] - packuswb mm2, mm3 - add ecx, LineStep - add edx, LineStep - movq [eax], mm2 - add eax, LineStep - cmp ebx, edi - jc loop_start - - }; - -} - - - - -void dsp_mmx_recon_init(DspFunctions *funcs) -{ - funcs->copy8x8 = copy8x8__mmx; - funcs->recon_intra8x8 = recon_intra8x8__mmx; - funcs->recon_inter8x8 = recon_inter8x8__mmx; - funcs->recon_inter8x8_half = recon_inter8x8_half__mmx; -} - diff --git a/Engine/lib/libtheora/lib/enc/x86_64/dct_decode_mmx.c b/Engine/lib/libtheora/lib/enc/x86_64/dct_decode_mmx.c deleted file mode 100644 index 547e974e3..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_64/dct_decode_mmx.c +++ /dev/null @@ -1,409 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2008 * - * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: dct_decode_mmx.c 15400 2008-10-15 12:10:58Z tterribe $ - - ********************************************************************/ - -#include - -#include "../codec_internal.h" - -#if defined(USE_ASM) - -static const __attribute__((aligned(8),used)) ogg_int64_t OC_V3= - 0x0003000300030003LL; -static const __attribute__((aligned(8),used)) ogg_int64_t OC_V4= - 0x0004000400040004LL; - -static void loop_filter_v(unsigned char *_pix,int _ystride, - const ogg_int16_t *_ll){ - long esi; - _pix-=_ystride*2; - __asm__ __volatile__( - /*mm0=0*/ - "pxor %%mm0,%%mm0\n\t" - /*esi=_ystride*3*/ - "lea (%[ystride],%[ystride],2),%[s]\n\t" - /*mm7=_pix[0...8]*/ - "movq (%[pix]),%%mm7\n\t" - /*mm4=_pix[0...8+_ystride*3]*/ - "movq (%[pix],%[s]),%%mm4\n\t" - /*mm6=_pix[0...8]*/ - "movq %%mm7,%%mm6\n\t" - /*Expand unsigned _pix[0...3] to 16 bits.*/ - "punpcklbw %%mm0,%%mm6\n\t" - "movq %%mm4,%%mm5\n\t" - /*Expand unsigned _pix[4...8] to 16 bits.*/ - "punpckhbw %%mm0,%%mm7\n\t" - /*Expand other arrays too.*/ - "punpcklbw %%mm0,%%mm4\n\t" - "punpckhbw %%mm0,%%mm5\n\t" - /*mm7:mm6=_p[0...8]-_p[0...8+_ystride*3]:*/ - "psubw %%mm4,%%mm6\n\t" - "psubw %%mm5,%%mm7\n\t" - /*mm5=mm4=_pix[0...8+_ystride]*/ - "movq (%[pix],%[ystride]),%%mm4\n\t" - /*mm1=mm3=mm2=_pix[0..8]+_ystride*2]*/ - "movq (%[pix],%[ystride],2),%%mm2\n\t" - "movq %%mm4,%%mm5\n\t" - "movq %%mm2,%%mm3\n\t" - "movq %%mm2,%%mm1\n\t" - /*Expand these arrays.*/ - "punpckhbw %%mm0,%%mm5\n\t" - "punpcklbw %%mm0,%%mm4\n\t" - "punpckhbw %%mm0,%%mm3\n\t" - "punpcklbw %%mm0,%%mm2\n\t" - /*Preload...*/ - "movq %[OC_V3],%%mm0\n\t" - /*mm3:mm2=_pix[0...8+_ystride*2]-_pix[0...8+_ystride]*/ - "psubw %%mm5,%%mm3\n\t" - "psubw %%mm4,%%mm2\n\t" - /*Scale by 3.*/ - "pmullw %%mm0,%%mm3\n\t" - "pmullw %%mm0,%%mm2\n\t" - /*Preload...*/ - "movq %[OC_V4],%%mm0\n\t" - /*f=mm3:mm2==_pix[0...8]-_pix[0...8+_ystride*3]+ - 3*(_pix[0...8+_ystride*2]-_pix[0...8+_ystride])*/ - "paddw %%mm7,%%mm3\n\t" - "paddw %%mm6,%%mm2\n\t" - /*Add 4.*/ - "paddw %%mm0,%%mm3\n\t" - "paddw %%mm0,%%mm2\n\t" - /*"Divide" by 8.*/ - "psraw $3,%%mm3\n\t" - "psraw $3,%%mm2\n\t" - /*Now compute lflim of mm3:mm2 cf. Section 7.10 of the sepc.*/ - /*Free up mm5.*/ - "packuswb %%mm5,%%mm4\n\t" - /*mm0=L L L L*/ - "movq (%[ll]),%%mm0\n\t" - /*if(R_i<-2L||R_i>2L)R_i=0:*/ - "movq %%mm2,%%mm5\n\t" - "pxor %%mm6,%%mm6\n\t" - "movq %%mm0,%%mm7\n\t" - "psubw %%mm0,%%mm6\n\t" - "psllw $1,%%mm7\n\t" - "psllw $1,%%mm6\n\t" - /*mm2==R_3 R_2 R_1 R_0*/ - /*mm5==R_3 R_2 R_1 R_0*/ - /*mm6==-2L -2L -2L -2L*/ - /*mm7==2L 2L 2L 2L*/ - "pcmpgtw %%mm2,%%mm7\n\t" - "pcmpgtw %%mm6,%%mm5\n\t" - "pand %%mm7,%%mm2\n\t" - "movq %%mm0,%%mm7\n\t" - "pand %%mm5,%%mm2\n\t" - "psllw $1,%%mm7\n\t" - "movq %%mm3,%%mm5\n\t" - /*mm3==R_7 R_6 R_5 R_4*/ - /*mm5==R_7 R_6 R_5 R_4*/ - /*mm6==-2L -2L -2L -2L*/ - /*mm7==2L 2L 2L 2L*/ - "pcmpgtw %%mm3,%%mm7\n\t" - "pcmpgtw %%mm6,%%mm5\n\t" - "pand %%mm7,%%mm3\n\t" - "movq %%mm0,%%mm7\n\t" - "pand %%mm5,%%mm3\n\t" - /*if(R_i<-L)R_i'=R_i+2L; - if(R_i>L)R_i'=R_i-2L; - if(R_i<-L||R_i>L)R_i=-R_i':*/ - "psraw $1,%%mm6\n\t" - "movq %%mm2,%%mm5\n\t" - "psllw $1,%%mm7\n\t" - /*mm2==R_3 R_2 R_1 R_0*/ - /*mm5==R_3 R_2 R_1 R_0*/ - /*mm6==-L -L -L -L*/ - /*mm0==L L L L*/ - /*mm5=R_i>L?FF:00*/ - "pcmpgtw %%mm0,%%mm5\n\t" - /*mm6=-L>R_i?FF:00*/ - "pcmpgtw %%mm2,%%mm6\n\t" - /*mm7=R_i>L?2L:0*/ - "pand %%mm5,%%mm7\n\t" - /*mm2=R_i>L?R_i-2L:R_i*/ - "psubw %%mm7,%%mm2\n\t" - "movq %%mm0,%%mm7\n\t" - /*mm5=-L>R_i||R_i>L*/ - "por %%mm6,%%mm5\n\t" - "psllw $1,%%mm7\n\t" - /*mm7=-L>R_i?2L:0*/ - "pand %%mm6,%%mm7\n\t" - "pxor %%mm6,%%mm6\n\t" - /*mm2=-L>R_i?R_i+2L:R_i*/ - "paddw %%mm7,%%mm2\n\t" - "psubw %%mm0,%%mm6\n\t" - /*mm5=-L>R_i||R_i>L?-R_i':0*/ - "pand %%mm2,%%mm5\n\t" - "movq %%mm0,%%mm7\n\t" - /*mm2=-L>R_i||R_i>L?0:R_i*/ - "psubw %%mm5,%%mm2\n\t" - "psllw $1,%%mm7\n\t" - /*mm2=-L>R_i||R_i>L?-R_i':R_i*/ - "psubw %%mm5,%%mm2\n\t" - "movq %%mm3,%%mm5\n\t" - /*mm3==R_7 R_6 R_5 R_4*/ - /*mm5==R_7 R_6 R_5 R_4*/ - /*mm6==-L -L -L -L*/ - /*mm0==L L L L*/ - /*mm6=-L>R_i?FF:00*/ - "pcmpgtw %%mm3,%%mm6\n\t" - /*mm5=R_i>L?FF:00*/ - "pcmpgtw %%mm0,%%mm5\n\t" - /*mm7=R_i>L?2L:0*/ - "pand %%mm5,%%mm7\n\t" - /*mm2=R_i>L?R_i-2L:R_i*/ - "psubw %%mm7,%%mm3\n\t" - "psllw $1,%%mm0\n\t" - /*mm5=-L>R_i||R_i>L*/ - "por %%mm6,%%mm5\n\t" - /*mm0=-L>R_i?2L:0*/ - "pand %%mm6,%%mm0\n\t" - /*mm3=-L>R_i?R_i+2L:R_i*/ - "paddw %%mm0,%%mm3\n\t" - /*mm5=-L>R_i||R_i>L?-R_i':0*/ - "pand %%mm3,%%mm5\n\t" - /*mm2=-L>R_i||R_i>L?0:R_i*/ - "psubw %%mm5,%%mm3\n\t" - /*mm2=-L>R_i||R_i>L?-R_i':R_i*/ - "psubw %%mm5,%%mm3\n\t" - /*Unfortunately, there's no unsigned byte+signed byte with unsigned - saturation op code, so we have to promote things back 16 bits.*/ - "pxor %%mm0,%%mm0\n\t" - "movq %%mm4,%%mm5\n\t" - "punpcklbw %%mm0,%%mm4\n\t" - "punpckhbw %%mm0,%%mm5\n\t" - "movq %%mm1,%%mm6\n\t" - "punpcklbw %%mm0,%%mm1\n\t" - "punpckhbw %%mm0,%%mm6\n\t" - /*_pix[0...8+_ystride]+=R_i*/ - "paddw %%mm2,%%mm4\n\t" - "paddw %%mm3,%%mm5\n\t" - /*_pix[0...8+_ystride*2]-=R_i*/ - "psubw %%mm2,%%mm1\n\t" - "psubw %%mm3,%%mm6\n\t" - "packuswb %%mm5,%%mm4\n\t" - "packuswb %%mm6,%%mm1\n\t" - /*Write it back out.*/ - "movq %%mm4,(%[pix],%[ystride])\n\t" - "movq %%mm1,(%[pix],%[ystride],2)\n\t" - :[s]"=&S"(esi) - :[pix]"r"(_pix),[ystride]"r"((long)_ystride),[ll]"r"(_ll), - [OC_V3]"m"(OC_V3),[OC_V4]"m"(OC_V4) - :"memory" - ); -} - -/*This code implements the bulk of loop_filter_h(). - Data are striped p0 p1 p2 p3 ... p0 p1 p2 p3 ..., so in order to load all - four p0's to one register we must transpose the values in four mmx regs. - When half is done we repeat this for the rest.*/ -static void loop_filter_h4(unsigned char *_pix,long _ystride, - const ogg_int16_t *_ll){ - long esi; - long edi; - __asm__ __volatile__( - /*x x x x 3 2 1 0*/ - "movd (%[pix]),%%mm0\n\t" - /*esi=_ystride*3*/ - "lea (%[ystride],%[ystride],2),%[s]\n\t" - /*x x x x 7 6 5 4*/ - "movd (%[pix],%[ystride]),%%mm1\n\t" - /*x x x x B A 9 8*/ - "movd (%[pix],%[ystride],2),%%mm2\n\t" - /*x x x x F E D C*/ - "movd (%[pix],%[s]),%%mm3\n\t" - /*mm0=7 3 6 2 5 1 4 0*/ - "punpcklbw %%mm1,%%mm0\n\t" - /*mm2=F B E A D 9 C 8*/ - "punpcklbw %%mm3,%%mm2\n\t" - /*mm1=7 3 6 2 5 1 4 0*/ - "movq %%mm0,%%mm1\n\t" - /*mm0=F B 7 3 E A 6 2*/ - "punpckhwd %%mm2,%%mm0\n\t" - /*mm1=D 9 5 1 C 8 4 0*/ - "punpcklwd %%mm2,%%mm1\n\t" - "pxor %%mm7,%%mm7\n\t" - /*mm5=D 9 5 1 C 8 4 0*/ - "movq %%mm1,%%mm5\n\t" - /*mm1=x C x 8 x 4 x 0==pix[0]*/ - "punpcklbw %%mm7,%%mm1\n\t" - /*mm5=x D x 9 x 5 x 1==pix[1]*/ - "punpckhbw %%mm7,%%mm5\n\t" - /*mm3=F B 7 3 E A 6 2*/ - "movq %%mm0,%%mm3\n\t" - /*mm0=x E x A x 6 x 2==pix[2]*/ - "punpcklbw %%mm7,%%mm0\n\t" - /*mm3=x F x B x 7 x 3==pix[3]*/ - "punpckhbw %%mm7,%%mm3\n\t" - /*mm1=mm1-mm3==pix[0]-pix[3]*/ - "psubw %%mm3,%%mm1\n\t" - /*Save a copy of pix[2] for later.*/ - "movq %%mm0,%%mm4\n\t" - /*mm0=mm0-mm5==pix[2]-pix[1]*/ - "psubw %%mm5,%%mm0\n\t" - /*Scale by 3.*/ - "pmullw %[OC_V3],%%mm0\n\t" - /*f=mm1==_pix[0]-_pix[3]+ 3*(_pix[2]-_pix[1])*/ - "paddw %%mm1,%%mm0\n\t" - /*Add 4.*/ - "paddw %[OC_V4],%%mm0\n\t" - /*"Divide" by 8, producing the residuals R_i.*/ - "psraw $3,%%mm0\n\t" - /*Now compute lflim of mm0 cf. Section 7.10 of the sepc.*/ - /*mm6=L L L L*/ - "movq (%[ll]),%%mm6\n\t" - /*if(R_i<-2L||R_i>2L)R_i=0:*/ - "movq %%mm0,%%mm1\n\t" - "pxor %%mm2,%%mm2\n\t" - "movq %%mm6,%%mm3\n\t" - "psubw %%mm6,%%mm2\n\t" - "psllw $1,%%mm3\n\t" - "psllw $1,%%mm2\n\t" - /*mm0==R_3 R_2 R_1 R_0*/ - /*mm1==R_3 R_2 R_1 R_0*/ - /*mm2==-2L -2L -2L -2L*/ - /*mm3==2L 2L 2L 2L*/ - "pcmpgtw %%mm0,%%mm3\n\t" - "pcmpgtw %%mm2,%%mm1\n\t" - "pand %%mm3,%%mm0\n\t" - "pand %%mm1,%%mm0\n\t" - /*if(R_i<-L)R_i'=R_i+2L; - if(R_i>L)R_i'=R_i-2L; - if(R_i<-L||R_i>L)R_i=-R_i':*/ - "psraw $1,%%mm2\n\t" - "movq %%mm0,%%mm1\n\t" - "movq %%mm6,%%mm3\n\t" - /*mm0==R_3 R_2 R_1 R_0*/ - /*mm1==R_3 R_2 R_1 R_0*/ - /*mm2==-L -L -L -L*/ - /*mm6==L L L L*/ - /*mm2=-L>R_i?FF:00*/ - "pcmpgtw %%mm0,%%mm2\n\t" - /*mm1=R_i>L?FF:00*/ - "pcmpgtw %%mm6,%%mm1\n\t" - /*mm3=2L 2L 2L 2L*/ - "psllw $1,%%mm3\n\t" - /*mm6=2L 2L 2L 2L*/ - "psllw $1,%%mm6\n\t" - /*mm3=R_i>L?2L:0*/ - "pand %%mm1,%%mm3\n\t" - /*mm6=-L>R_i?2L:0*/ - "pand %%mm2,%%mm6\n\t" - /*mm0=R_i>L?R_i-2L:R_i*/ - "psubw %%mm3,%%mm0\n\t" - /*mm1=-L>R_i||R_i>L*/ - "por %%mm2,%%mm1\n\t" - /*mm0=-L>R_i?R_i+2L:R_i*/ - "paddw %%mm6,%%mm0\n\t" - /*mm1=-L>R_i||R_i>L?R_i':0*/ - "pand %%mm0,%%mm1\n\t" - /*mm0=-L>R_i||R_i>L?0:R_i*/ - "psubw %%mm1,%%mm0\n\t" - /*mm0=-L>R_i||R_i>L?-R_i':R_i*/ - "psubw %%mm1,%%mm0\n\t" - /*_pix[1]+=R_i;*/ - "paddw %%mm0,%%mm5\n\t" - /*_pix[2]-=R_i;*/ - "psubw %%mm0,%%mm4\n\t" - /*mm5=x x x x D 9 5 1*/ - "packuswb %%mm7,%%mm5\n\t" - /*mm4=x x x x E A 6 2*/ - "packuswb %%mm7,%%mm4\n\t" - /*mm5=E D A 9 6 5 2 1*/ - "punpcklbw %%mm4,%%mm5\n\t" - /*edi=6 5 2 1*/ - "movd %%mm5,%%edi\n\t" - "movw %%di,1(%[pix])\n\t" - /*Why is there such a big stall here?*/ - "psrlq $32,%%mm5\n\t" - "shrl $16,%%edi\n\t" - "movw %%di,1(%[pix],%[ystride])\n\t" - /*edi=E D A 9*/ - "movd %%mm5,%%edi\n\t" - "movw %%di,1(%[pix],%[ystride],2)\n\t" - "shrl $16,%%edi\n\t" - "movw %%di,1(%[pix],%[s])\n\t" - :[s]"=&S"(esi),[d]"=&D"(edi), - [pix]"+r"(_pix),[ystride]"+r"(_ystride),[ll]"+r"(_ll) - :[OC_V3]"m"(OC_V3),[OC_V4]"m"(OC_V4) - :"memory" - ); -} - -static void loop_filter_h(unsigned char *_pix,int _ystride, - const ogg_int16_t *_ll){ - _pix-=2; - loop_filter_h4(_pix,_ystride,_ll); - loop_filter_h4(_pix+(_ystride<<2),_ystride,_ll); -} - -static void loop_filter_mmx(PB_INSTANCE *pbi, int FLimit){ - int j; - ogg_int16_t __attribute__((aligned(8))) ll[4]; - unsigned char *cp = pbi->display_fragments; - ogg_uint32_t *bp = pbi->recon_pixel_index_table; - - if ( FLimit == 0 ) return; - ll[0]=ll[1]=ll[2]=ll[3]=FLimit; - - for ( j = 0; j < 3 ; j++){ - ogg_uint32_t *bp_begin = bp; - ogg_uint32_t *bp_end; - int stride; - int h; - - switch(j) { - case 0: /* y */ - bp_end = bp + pbi->YPlaneFragments; - h = pbi->HFragments; - stride = pbi->YStride; - break; - default: /* u,v, 4:20 specific */ - bp_end = bp + pbi->UVPlaneFragments; - h = pbi->HFragments >> 1; - stride = pbi->UVStride; - break; - } - - while(bpbp_left) - loop_filter_h(&pbi->LastFrameRecon[bp[0]],stride,ll); - if(bp_left>bp_begin) - loop_filter_v(&pbi->LastFrameRecon[bp[0]],stride,ll); - if(bp+1LastFrameRecon[bp[0]]+8,stride,ll); - if(bp+hLastFrameRecon[bp[h]],stride,ll); - } - bp++; - cp++; - } - } - } - - __asm__ __volatile__("emms\n\t"); -} - -/* install our implementation in the function table */ -void dsp_mmx_dct_decode_init(DspFunctions *funcs) -{ - funcs->LoopFilter = loop_filter_mmx; -} - -#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_64/dsp_mmx.c b/Engine/lib/libtheora/lib/enc/x86_64/dsp_mmx.c deleted file mode 100644 index 6c2689e63..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_64/dsp_mmx.c +++ /dev/null @@ -1,303 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: dsp_mmx.c 15397 2008-10-14 02:06:24Z tterribe $ - - ********************************************************************/ - -#include - -#include "../codec_internal.h" -#include "../dsp.h" - -#if defined(USE_ASM) - -typedef unsigned long long ogg_uint64_t; - -static const __attribute__ ((aligned(8),used)) ogg_int64_t V128 = 0x0080008000800080LL; - -#define DSP_OP_AVG(a,b) ((((int)(a)) + ((int)(b)))/2) -#define DSP_OP_DIFF(a,b) (((int)(a)) - ((int)(b))) -#define DSP_OP_ABS_DIFF(a,b) abs((((int)(a)) - ((int)(b)))) - -static void sub8x8__mmx (unsigned char *FiltPtr, unsigned char *ReconPtr, - ogg_int16_t *DctInputPtr, ogg_uint32_t PixelsPerLine, - ogg_uint32_t ReconPixelsPerLine) -{ - __asm__ __volatile__ ( - " .balign 16 \n\t" - - " pxor %%mm7, %%mm7 \n\t" - - ".rept 8 \n\t" - " movq (%0), %%mm0 \n\t" /* mm0 = FiltPtr */ - " movq (%1), %%mm1 \n\t" /* mm1 = ReconPtr */ - " movq %%mm0, %%mm2 \n\t" /* dup to prepare for up conversion */ - " movq %%mm1, %%mm3 \n\t" /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - " punpcklbw %%mm7, %%mm0 \n\t" /* mm0 = INT16(FiltPtr) */ - " punpcklbw %%mm7, %%mm1 \n\t" /* mm1 = INT16(ReconPtr) */ - " punpckhbw %%mm7, %%mm2 \n\t" /* mm2 = INT16(FiltPtr) */ - " punpckhbw %%mm7, %%mm3 \n\t" /* mm3 = INT16(ReconPtr) */ - /* start calculation */ - " psubw %%mm1, %%mm0 \n\t" /* mm0 = FiltPtr - ReconPtr */ - " psubw %%mm3, %%mm2 \n\t" /* mm2 = FiltPtr - ReconPtr */ - " movq %%mm0, (%2) \n\t" /* write answer out */ - " movq %%mm2, 8(%2) \n\t" /* write answer out */ - /* Increment pointers */ - " add $16, %2 \n\t" - " add %3, %0 \n\t" - " add %4, %1 \n\t" - ".endr \n\t" - - : "+r" (FiltPtr), - "+r" (ReconPtr), - "+r" (DctInputPtr) - : "r" ((ogg_uint64_t)PixelsPerLine), - "r" ((ogg_uint64_t)ReconPixelsPerLine) - : "memory" - ); -} - -static void sub8x8_128__mmx (unsigned char *FiltPtr, ogg_int16_t *DctInputPtr, - ogg_uint32_t PixelsPerLine) -{ - ogg_uint64_t ppl = PixelsPerLine; - - __asm__ __volatile__ ( - " .balign 16 \n\t" - - " pxor %%mm7, %%mm7 \n\t" - " movq %[V128], %%mm1 \n\t" - - ".rept 8 \n\t" - " movq (%0), %%mm0 \n\t" /* mm0 = FiltPtr */ - " movq %%mm0, %%mm2 \n\t" /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - " punpcklbw %%mm7, %%mm0 \n\t" /* mm0 = INT16(FiltPtr) */ - " punpckhbw %%mm7, %%mm2 \n\t" /* mm2 = INT16(FiltPtr) */ - /* start calculation */ - " psubw %%mm1, %%mm0 \n\t" /* mm0 = FiltPtr - 128 */ - " psubw %%mm1, %%mm2 \n\t" /* mm2 = FiltPtr - 128 */ - " movq %%mm0, (%1) \n\t" /* write answer out */ - " movq %%mm2, 8(%1) \n\t" /* write answer out */ - /* Increment pointers */ - " add $16, %1 \n\t" - " add %2, %0 \n\t" - ".endr \n\t" - - : "+r" (FiltPtr), - "+r" (DctInputPtr) - : "r" (ppl), /* gcc bug? a cast won't work here, e.g. (ogg_uint64_t)PixelsPerLine */ - [V128] "m" (V128) - : "memory" - ); -} - -static void sub8x8avg2__mmx (unsigned char *FiltPtr, unsigned char *ReconPtr1, - unsigned char *ReconPtr2, ogg_int16_t *DctInputPtr, - ogg_uint32_t PixelsPerLine, - ogg_uint32_t ReconPixelsPerLine) -{ - __asm__ __volatile__ ( - " .balign 16 \n\t" - - " pxor %%mm7, %%mm7 \n\t" - - ".rept 8 \n\t" - " movq (%0), %%mm0 \n\t" /* mm0 = FiltPtr */ - " movq (%1), %%mm1 \n\t" /* mm1 = ReconPtr1 */ - " movq (%2), %%mm4 \n\t" /* mm1 = ReconPtr2 */ - " movq %%mm0, %%mm2 \n\t" /* dup to prepare for up conversion */ - " movq %%mm1, %%mm3 \n\t" /* dup to prepare for up conversion */ - " movq %%mm4, %%mm5 \n\t" /* dup to prepare for up conversion */ - /* convert from UINT8 to INT16 */ - " punpcklbw %%mm7, %%mm0 \n\t" /* mm0 = INT16(FiltPtr) */ - " punpcklbw %%mm7, %%mm1 \n\t" /* mm1 = INT16(ReconPtr1) */ - " punpcklbw %%mm7, %%mm4 \n\t" /* mm1 = INT16(ReconPtr2) */ - " punpckhbw %%mm7, %%mm2 \n\t" /* mm2 = INT16(FiltPtr) */ - " punpckhbw %%mm7, %%mm3 \n\t" /* mm3 = INT16(ReconPtr1) */ - " punpckhbw %%mm7, %%mm5 \n\t" /* mm3 = INT16(ReconPtr2) */ - /* average ReconPtr1 and ReconPtr2 */ - " paddw %%mm4, %%mm1 \n\t" /* mm1 = ReconPtr1 + ReconPtr2 */ - " paddw %%mm5, %%mm3 \n\t" /* mm3 = ReconPtr1 + ReconPtr2 */ - " psrlw $1, %%mm1 \n\t" /* mm1 = (ReconPtr1 + ReconPtr2) / 2 */ - " psrlw $1, %%mm3 \n\t" /* mm3 = (ReconPtr1 + ReconPtr2) / 2 */ - " psubw %%mm1, %%mm0 \n\t" /* mm0 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - " psubw %%mm3, %%mm2 \n\t" /* mm2 = FiltPtr - ((ReconPtr1 + ReconPtr2) / 2) */ - " movq %%mm0, (%3) \n\t" /* write answer out */ - " movq %%mm2, 8(%3) \n\t" /* write answer out */ - /* Increment pointers */ - " add $16, %3 \n\t" - " add %4, %0 \n\t" - " add %5, %1 \n\t" - " add %5, %2 \n\t" - ".endr \n\t" - - : "+r" (FiltPtr), - "+r" (ReconPtr1), - "+r" (ReconPtr2), - "+r" (DctInputPtr) - : "r" ((ogg_uint64_t)PixelsPerLine), - "r" ((ogg_uint64_t)ReconPixelsPerLine) - : "memory" - ); -} - -static ogg_uint32_t intra8x8_err__mmx (unsigned char *DataPtr, ogg_uint32_t Stride) -{ - ogg_uint64_t XSum; - ogg_uint64_t XXSum; - - __asm__ __volatile__ ( - " .balign 16 \n\t" - - " pxor %%mm5, %%mm5 \n\t" - " pxor %%mm6, %%mm6 \n\t" - " pxor %%mm7, %%mm7 \n\t" - " mov $8, %%rdi \n\t" - "1: \n\t" - " movq (%2), %%mm0 \n\t" /* take 8 bytes */ - " movq %%mm0, %%mm2 \n\t" - - " punpcklbw %%mm6, %%mm0 \n\t" - " punpckhbw %%mm6, %%mm2 \n\t" - - " paddw %%mm0, %%mm5 \n\t" - " paddw %%mm2, %%mm5 \n\t" - - " pmaddwd %%mm0, %%mm0 \n\t" - " pmaddwd %%mm2, %%mm2 \n\t" - - " paddd %%mm0, %%mm7 \n\t" - " paddd %%mm2, %%mm7 \n\t" - - " add %3, %2 \n\t" /* Inc pointer into src data */ - - " dec %%rdi \n\t" - " jnz 1b \n\t" - - " movq %%mm5, %%mm0 \n\t" - " psrlq $32, %%mm5 \n\t" - " paddw %%mm0, %%mm5 \n\t" - " movq %%mm5, %%mm0 \n\t" - " psrlq $16, %%mm5 \n\t" - " paddw %%mm0, %%mm5 \n\t" - " movd %%mm5, %%rdi \n\t" - " movsx %%di, %%rdi \n\t" - " mov %%rdi, %0 \n\t" - - " movq %%mm7, %%mm0 \n\t" - " psrlq $32, %%mm7 \n\t" - " paddd %%mm0, %%mm7 \n\t" - " movd %%mm7, %1 \n\t" - - : "=r" (XSum), - "=r" (XXSum), - "+r" (DataPtr) - : "r" ((ogg_uint64_t)Stride) - : "rdi", "memory" - ); - - /* Compute population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum ) ); -} - -static ogg_uint32_t inter8x8_err__mmx (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr, ogg_uint32_t RefStride) -{ - ogg_uint64_t XSum; - ogg_uint64_t XXSum; - - __asm__ __volatile__ ( - " .balign 16 \n\t" - - " pxor %%mm5, %%mm5 \n\t" - " pxor %%mm6, %%mm6 \n\t" - " pxor %%mm7, %%mm7 \n\t" - " mov $8, %%rdi \n\t" - "1: \n\t" - " movq (%2), %%mm0 \n\t" /* take 8 bytes */ - " movq (%3), %%mm1 \n\t" - " movq %%mm0, %%mm2 \n\t" - " movq %%mm1, %%mm3 \n\t" - - " punpcklbw %%mm6, %%mm0 \n\t" - " punpcklbw %%mm6, %%mm1 \n\t" - " punpckhbw %%mm6, %%mm2 \n\t" - " punpckhbw %%mm6, %%mm3 \n\t" - - " psubsw %%mm1, %%mm0 \n\t" - " psubsw %%mm3, %%mm2 \n\t" - - " paddw %%mm0, %%mm5 \n\t" - " paddw %%mm2, %%mm5 \n\t" - - " pmaddwd %%mm0, %%mm0 \n\t" - " pmaddwd %%mm2, %%mm2 \n\t" - - " paddd %%mm0, %%mm7 \n\t" - " paddd %%mm2, %%mm7 \n\t" - - " add %4, %2 \n\t" /* Inc pointer into src data */ - " add %5, %3 \n\t" /* Inc pointer into ref data */ - - " dec %%rdi \n\t" - " jnz 1b \n\t" - - " movq %%mm5, %%mm0 \n\t" - " psrlq $32, %%mm5 \n\t" - " paddw %%mm0, %%mm5 \n\t" - " movq %%mm5, %%mm0 \n\t" - " psrlq $16, %%mm5 \n\t" - " paddw %%mm0, %%mm5 \n\t" - " movd %%mm5, %%rdi \n\t" - " movsx %%di, %%rdi \n\t" - " mov %%rdi, %0 \n\t" - - " movq %%mm7, %%mm0 \n\t" - " psrlq $32, %%mm7 \n\t" - " paddd %%mm0, %%mm7 \n\t" - " movd %%mm7, %1 \n\t" - - : "=m" (XSum), - "=m" (XXSum), - "+r" (SrcData), - "+r" (RefDataPtr) - : "r" ((ogg_uint64_t)SrcStride), - "r" ((ogg_uint64_t)RefStride) - : "rdi", "memory" - ); - - /* Compute and return population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum )); -} - -static void restore_fpu (void) -{ - __asm__ __volatile__ ( - " emms \n\t" - ); -} - -void dsp_mmx_init(DspFunctions *funcs) -{ - funcs->restore_fpu = restore_fpu; - funcs->sub8x8 = sub8x8__mmx; - funcs->sub8x8_128 = sub8x8_128__mmx; - funcs->sub8x8avg2 = sub8x8avg2__mmx; - funcs->intra8x8_err = intra8x8_err__mmx; - funcs->inter8x8_err = inter8x8_err__mmx; -} - -#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_64/dsp_mmxext.c b/Engine/lib/libtheora/lib/enc/x86_64/dsp_mmxext.c deleted file mode 100644 index f0aeed96e..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_64/dsp_mmxext.c +++ /dev/null @@ -1,323 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: dsp_mmxext.c 15397 2008-10-14 02:06:24Z tterribe $ - - ********************************************************************/ - -#include - -#include "../codec_internal.h" -#include "../dsp.h" - -#if defined(USE_ASM) - -typedef unsigned long long ogg_uint64_t; - -static ogg_uint32_t sad8x8__mmxext (unsigned char *ptr1, ogg_uint32_t stride1, - unsigned char *ptr2, ogg_uint32_t stride2) -{ - ogg_uint32_t DiffVal; - - __asm__ __volatile__ ( - " .balign 16 \n\t" - " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ - - ".rept 7 \n\t" - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ - " movq (%2), %%mm1 \n\t" - " psadbw %%mm1, %%mm0 \n\t" - " add %3, %1 \n\t" /* Inc pointer into the new data */ - " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ - " add %4, %2 \n\t" /* Inc pointer into ref data */ - ".endr \n\t" - - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ - " movq (%2), %%mm1 \n\t" - " psadbw %%mm1, %%mm0 \n\t" - " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ - " movd %%mm7, %0 \n\t" - - : "=r" (DiffVal), - "+r" (ptr1), - "+r" (ptr2) - : "r" ((ogg_uint64_t)stride1), - "r" ((ogg_uint64_t)stride2) - : "memory" - ); - - return DiffVal; -} - -static ogg_uint32_t sad8x8_thres__mmxext (unsigned char *ptr1, ogg_uint32_t stride1, - unsigned char *ptr2, ogg_uint32_t stride2, - ogg_uint32_t thres) -{ - ogg_uint32_t DiffVal; - - __asm__ __volatile__ ( - " .balign 16 \n\t" - " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ - - ".rept 8 \n\t" - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ - " movq (%2), %%mm1 \n\t" - " psadbw %%mm1, %%mm0 \n\t" - " add %3, %1 \n\t" /* Inc pointer into the new data */ - " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ - " add %4, %2 \n\t" /* Inc pointer into ref data */ - ".endr \n\t" - - " movd %%mm7, %0 \n\t" - - : "=r" (DiffVal), - "+r" (ptr1), - "+r" (ptr2) - : "r" ((ogg_uint64_t)stride1), - "r" ((ogg_uint64_t)stride2) - : "memory" - ); - - return DiffVal; -} - -static ogg_uint32_t sad8x8_xy2_thres__mmxext (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr1, - unsigned char *RefDataPtr2, ogg_uint32_t RefStride, - ogg_uint32_t thres) -{ - ogg_uint32_t DiffVal; - - __asm__ __volatile__ ( - " .balign 16 \n\t" - " pxor %%mm7, %%mm7 \n\t" /* mm7 contains the result */ - ".rept 8 \n\t" - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ - " movq (%2), %%mm1 \n\t" - " movq (%3), %%mm2 \n\t" - " pavgb %%mm2, %%mm1 \n\t" - " psadbw %%mm1, %%mm0 \n\t" - - " add %4, %1 \n\t" /* Inc pointer into the new data */ - " paddw %%mm0, %%mm7 \n\t" /* accumulate difference... */ - " add %5, %2 \n\t" /* Inc pointer into ref data */ - " add %5, %3 \n\t" /* Inc pointer into ref data */ - ".endr \n\t" - - " movd %%mm7, %0 \n\t" - : "=m" (DiffVal), - "+r" (SrcData), - "+r" (RefDataPtr1), - "+r" (RefDataPtr2) - : "r" ((ogg_uint64_t)SrcStride), - "r" ((ogg_uint64_t)RefStride) - : "memory" - ); - - return DiffVal; -} - -static ogg_uint32_t row_sad8__mmxext (unsigned char *Src1, unsigned char *Src2) -{ - ogg_uint32_t MaxSad; - - __asm__ __volatile__ ( - " .balign 16 \n\t" - - " movd (%1), %%mm0 \n\t" - " movd (%2), %%mm1 \n\t" - " psadbw %%mm0, %%mm1 \n\t" - " movd 4(%1), %%mm2 \n\t" - " movd 4(%2), %%mm3 \n\t" - " psadbw %%mm2, %%mm3 \n\t" - - " pmaxsw %%mm1, %%mm3 \n\t" - " movd %%mm3, %0 \n\t" - " andl $0xffff, %0 \n\t" - - : "=m" (MaxSad), - "+r" (Src1), - "+r" (Src2) - : - : "memory" - ); - - return MaxSad; -} - -static ogg_uint32_t col_sad8x8__mmxext (unsigned char *Src1, unsigned char *Src2, - ogg_uint32_t stride) -{ - ogg_uint32_t MaxSad; - - __asm__ __volatile__ ( - " .balign 16 \n\t" - - " pxor %%mm3, %%mm3 \n\t" /* zero out mm3 for unpack */ - " pxor %%mm4, %%mm4 \n\t" /* mm4 low sum */ - " pxor %%mm5, %%mm5 \n\t" /* mm5 high sum */ - " pxor %%mm6, %%mm6 \n\t" /* mm6 low sum */ - " pxor %%mm7, %%mm7 \n\t" /* mm7 high sum */ - " mov $4, %%rdi \n\t" /* 4 rows */ - "1: \n\t" - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ - " movq (%2), %%mm1 \n\t" /* take 8 bytes */ - - " movq %%mm0, %%mm2 \n\t" - " psubusb %%mm1, %%mm0 \n\t" /* A - B */ - " psubusb %%mm2, %%mm1 \n\t" /* B - A */ - " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ - " movq %%mm0, %%mm1 \n\t" - - " punpcklbw %%mm3, %%mm0 \n\t" /* unpack to higher precision for accumulation */ - " paddw %%mm0, %%mm4 \n\t" /* accumulate difference... */ - " punpckhbw %%mm3, %%mm1 \n\t" /* unpack high four bytes to higher precision */ - " paddw %%mm1, %%mm5 \n\t" /* accumulate difference... */ - " add %3, %1 \n\t" /* Inc pointer into the new data */ - " add %3, %2 \n\t" /* Inc pointer into the new data */ - - " dec %%rdi \n\t" - " jnz 1b \n\t" - - " mov $4, %%rdi \n\t" /* 4 rows */ - "2: \n\t" - " movq (%1), %%mm0 \n\t" /* take 8 bytes */ - " movq (%2), %%mm1 \n\t" /* take 8 bytes */ - - " movq %%mm0, %%mm2 \n\t" - " psubusb %%mm1, %%mm0 \n\t" /* A - B */ - " psubusb %%mm2, %%mm1 \n\t" /* B - A */ - " por %%mm1, %%mm0 \n\t" /* and or gives abs difference */ - " movq %%mm0, %%mm1 \n\t" - - " punpcklbw %%mm3, %%mm0 \n\t" /* unpack to higher precision for accumulation */ - " paddw %%mm0, %%mm6 \n\t" /* accumulate difference... */ - " punpckhbw %%mm3, %%mm1 \n\t" /* unpack high four bytes to higher precision */ - " paddw %%mm1, %%mm7 \n\t" /* accumulate difference... */ - " add %3, %1 \n\t" /* Inc pointer into the new data */ - " add %3, %2 \n\t" /* Inc pointer into the new data */ - - " dec %%rdi \n\t" - " jnz 2b \n\t" - - " pmaxsw %%mm6, %%mm7 \n\t" - " pmaxsw %%mm4, %%mm5 \n\t" - " pmaxsw %%mm5, %%mm7 \n\t" - " movq %%mm7, %%mm6 \n\t" - " psrlq $32, %%mm6 \n\t" - " pmaxsw %%mm6, %%mm7 \n\t" - " movq %%mm7, %%mm6 \n\t" - " psrlq $16, %%mm6 \n\t" - " pmaxsw %%mm6, %%mm7 \n\t" - " movd %%mm7, %0 \n\t" - " andl $0xffff, %0 \n\t" - - : "=r" (MaxSad), - "+r" (Src1), - "+r" (Src2) - : "r" ((ogg_uint64_t)stride) - : "memory", "rdi" - ); - - return MaxSad; -} - -static ogg_uint32_t inter8x8_err_xy2__mmxext (unsigned char *SrcData, ogg_uint32_t SrcStride, - unsigned char *RefDataPtr1, - unsigned char *RefDataPtr2, ogg_uint32_t RefStride) -{ - ogg_uint64_t XSum; - ogg_uint64_t XXSum; - - __asm__ __volatile__ ( - " .balign 16 \n\t" - - " pxor %%mm4, %%mm4 \n\t" - " pxor %%mm5, %%mm5 \n\t" - " pxor %%mm6, %%mm6 \n\t" - " pxor %%mm7, %%mm7 \n\t" - " mov $8, %%rdi \n\t" - "1: \n\t" - " movq (%2), %%mm0 \n\t" /* take 8 bytes */ - - " movq (%3), %%mm2 \n\t" - " movq (%4), %%mm1 \n\t" /* take average of mm2 and mm1 */ - " pavgb %%mm2, %%mm1 \n\t" - - " movq %%mm0, %%mm2 \n\t" - " movq %%mm1, %%mm3 \n\t" - - " punpcklbw %%mm6, %%mm0 \n\t" - " punpcklbw %%mm4, %%mm1 \n\t" - " punpckhbw %%mm6, %%mm2 \n\t" - " punpckhbw %%mm4, %%mm3 \n\t" - - " psubsw %%mm1, %%mm0 \n\t" - " psubsw %%mm3, %%mm2 \n\t" - - " paddw %%mm0, %%mm5 \n\t" - " paddw %%mm2, %%mm5 \n\t" - - " pmaddwd %%mm0, %%mm0 \n\t" - " pmaddwd %%mm2, %%mm2 \n\t" - - " paddd %%mm0, %%mm7 \n\t" - " paddd %%mm2, %%mm7 \n\t" - - " add %5, %2 \n\t" /* Inc pointer into src data */ - " add %6, %3 \n\t" /* Inc pointer into ref data */ - " add %6, %4 \n\t" /* Inc pointer into ref data */ - - " dec %%rdi \n\t" - " jnz 1b \n\t" - - " movq %%mm5, %%mm0 \n\t" - " psrlq $32, %%mm5 \n\t" - " paddw %%mm0, %%mm5 \n\t" - " movq %%mm5, %%mm0 \n\t" - " psrlq $16, %%mm5 \n\t" - " paddw %%mm0, %%mm5 \n\t" - " movd %%mm5, %%edi \n\t" - " movsx %%di, %%edi \n\t" - " movl %%edi, %0 \n\t" - - " movq %%mm7, %%mm0 \n\t" - " psrlq $32, %%mm7 \n\t" - " paddd %%mm0, %%mm7 \n\t" - " movd %%mm7, %1 \n\t" - - : "=m" (XSum), - "=m" (XXSum), - "+r" (SrcData), - "+r" (RefDataPtr1), - "+r" (RefDataPtr2) - : "r" ((ogg_uint64_t)SrcStride), - "r" ((ogg_uint64_t)RefStride) - : "rdi", "memory" - ); - - /* Compute and return population variance as mis-match metric. */ - return (( (XXSum<<6) - XSum*XSum )); -} - -void dsp_mmxext_init(DspFunctions *funcs) -{ - funcs->row_sad8 = row_sad8__mmxext; - funcs->col_sad8x8 = col_sad8x8__mmxext; - funcs->sad8x8 = sad8x8__mmxext; - funcs->sad8x8_thres = sad8x8_thres__mmxext; - funcs->sad8x8_xy2_thres = sad8x8_xy2_thres__mmxext; - funcs->inter8x8_err_xy2 = inter8x8_err_xy2__mmxext; -} - -#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_64/fdct_mmx.c b/Engine/lib/libtheora/lib/enc/x86_64/fdct_mmx.c deleted file mode 100644 index 3765561cf..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_64/fdct_mmx.c +++ /dev/null @@ -1,342 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 1999-2006 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ********************************************************************/ - -/* mmx fdct implementation for x86_64 */ -/* $Id: fdct_mmx.c 15397 2008-10-14 02:06:24Z tterribe $ */ - -#include "theora/theora.h" -#include "../codec_internal.h" -#include "../dsp.h" - -#if defined(USE_ASM) - -static const __attribute__ ((aligned(8),used)) ogg_int64_t xC1S7 = 0x0fb15fb15fb15fb15LL; -static const __attribute__ ((aligned(8),used)) ogg_int64_t xC2S6 = 0x0ec83ec83ec83ec83LL; -static const __attribute__ ((aligned(8),used)) ogg_int64_t xC3S5 = 0x0d4dbd4dbd4dbd4dbLL; -static const __attribute__ ((aligned(8),used)) ogg_int64_t xC4S4 = 0x0b505b505b505b505LL; -static const __attribute__ ((aligned(8),used)) ogg_int64_t xC5S3 = 0x08e3a8e3a8e3a8e3aLL; -static const __attribute__ ((aligned(8),used)) ogg_int64_t xC6S2 = 0x061f861f861f861f8LL; -static const __attribute__ ((aligned(8),used)) ogg_int64_t xC7S1 = 0x031f131f131f131f1LL; - -#if defined(__MINGW32__) || defined(__CYGWIN__) || \ - defined(__OS2__) || (defined (__OpenBSD__) && !defined(__ELF__)) -# define M(a) "_" #a -#else -# define M(a) #a -#endif - -/* execute stage 1 of forward DCT */ -#define Fdct_mmx(ip0,ip1,ip2,ip3,ip4,ip5,ip6,ip7,temp) \ - " movq " #ip0 ", %%mm0 \n\t" \ - " movq " #ip1 ", %%mm1 \n\t" \ - " movq " #ip3 ", %%mm2 \n\t" \ - " movq " #ip5 ", %%mm3 \n\t" \ - " movq %%mm0, %%mm4 \n\t" \ - " movq %%mm1, %%mm5 \n\t" \ - " movq %%mm2, %%mm6 \n\t" \ - " movq %%mm3, %%mm7 \n\t" \ - \ - " paddsw " #ip7 ", %%mm0 \n\t" /* mm0 = ip0 + ip7 = is07 */ \ - " paddsw " #ip2 ", %%mm1 \n\t" /* mm1 = ip1 + ip2 = is12 */ \ - " paddsw " #ip4 ", %%mm2 \n\t" /* mm2 = ip3 + ip4 = is34 */ \ - " paddsw " #ip6 ", %%mm3 \n\t" /* mm3 = ip5 + ip6 = is56 */ \ - " psubsw " #ip7 ", %%mm4 \n\t" /* mm4 = ip0 - ip7 = id07 */ \ - " psubsw " #ip2 ", %%mm5 \n\t" /* mm5 = ip1 - ip2 = id12 */ \ - \ - " psubsw %%mm2, %%mm0 \n\t" /* mm0 = is07 - is34 */ \ - \ - " paddsw %%mm2, %%mm2 \n\t" \ - \ - " psubsw " #ip4 ", %%mm6 \n\t" /* mm6 = ip3 - ip4 = id34 */ \ - \ - " paddsw %%mm0, %%mm2 \n\t" /* mm2 = is07 + is34 = is0734 */ \ - " psubsw %%mm3, %%mm1 \n\t" /* mm1 = is12 - is56 */ \ - " movq %%mm0," #temp " \n\t" /* Save is07 - is34 to free mm0; */ \ - " paddsw %%mm3, %%mm3 \n\t" \ - " paddsw %%mm1, %%mm3 \n\t" /* mm3 = is12 + 1s56 = is1256 */ \ - \ - " psubsw " #ip6 ", %%mm7 \n\t" /* mm7 = ip5 - ip6 = id56 */ \ - /* ------------------------------------------------------------------- */ \ - " psubsw %%mm7, %%mm5 \n\t" /* mm5 = id12 - id56 */ \ - " paddsw %%mm7, %%mm7 \n\t" \ - " paddsw %%mm5, %%mm7 \n\t" /* mm7 = id12 + id56 */ \ - /* ------------------------------------------------------------------- */ \ - " psubsw %%mm3, %%mm2 \n\t" /* mm2 = is0734 - is1256 */ \ - " paddsw %%mm3, %%mm3 \n\t" \ - \ - " movq %%mm2, %%mm0 \n\t" /* make a copy */ \ - " paddsw %%mm2, %%mm3 \n\t" /* mm3 = is0734 + is1256 */ \ - \ - " pmulhw %[xC4S4], %%mm0 \n\t" /* mm0 = xC4S4 * ( is0734 - is1256 ) - ( is0734 - is1256 ) */ \ - " paddw %%mm2, %%mm0 \n\t" /* mm0 = xC4S4 * ( is0734 - is1256 ) */ \ - " psrlw $15, %%mm2 \n\t" \ - " paddw %%mm2, %%mm0 \n\t" /* Truncate mm0, now it is op[4] */ \ - \ - " movq %%mm3, %%mm2 \n\t" \ - " movq %%mm0," #ip4 " \n\t" /* save ip4, now mm0,mm2 are free */ \ - \ - " movq %%mm3, %%mm0 \n\t" \ - " pmulhw %[xC4S4], %%mm3 \n\t" /* mm3 = xC4S4 * ( is0734 +is1256 ) - ( is0734 +is1256 ) */ \ - \ - " psrlw $15, %%mm2 \n\t" \ - " paddw %%mm0, %%mm3 \n\t" /* mm3 = xC4S4 * ( is0734 +is1256 ) */ \ - " paddw %%mm2, %%mm3 \n\t" /* Truncate mm3, now it is op[0] */ \ - \ - " movq %%mm3," #ip0 " \n\t" \ - /* ------------------------------------------------------------------- */ \ - " movq " #temp ", %%mm3 \n\t" /* mm3 = irot_input_y */ \ - " pmulhw %[xC2S6], %%mm3 \n\t" /* mm3 = xC2S6 * irot_input_y - irot_input_y */ \ - \ - " movq " #temp ", %%mm2 \n\t" \ - " movq %%mm2, %%mm0 \n\t" \ - \ - " psrlw $15, %%mm2 \n\t" /* mm3 = xC2S6 * irot_input_y */ \ - " paddw %%mm0, %%mm3 \n\t" \ - \ - " paddw %%mm2, %%mm3 \n\t" /* Truncated */ \ - " movq %%mm5, %%mm0 \n\t" \ - \ - " movq %%mm5, %%mm2 \n\t" \ - " pmulhw %[xC6S2], %%mm0 \n\t" /* mm0 = xC6S2 * irot_input_x */ \ - \ - " psrlw $15, %%mm2 \n\t" \ - " paddw %%mm2, %%mm0 \n\t" /* Truncated */ \ - \ - " paddsw %%mm0, %%mm3 \n\t" /* ip[2] */ \ - " movq %%mm3," #ip2 " \n\t" /* Save ip2 */ \ - \ - " movq %%mm5, %%mm0 \n\t" \ - " movq %%mm5, %%mm2 \n\t" \ - \ - " pmulhw %[xC2S6], %%mm5 \n\t" /* mm5 = xC2S6 * irot_input_x - irot_input_x */ \ - " psrlw $15, %%mm2 \n\t" \ - \ - " movq " #temp ", %%mm3 \n\t" \ - " paddw %%mm0, %%mm5 \n\t" /* mm5 = xC2S6 * irot_input_x */ \ - \ - " paddw %%mm2, %%mm5 \n\t" /* Truncated */ \ - " movq %%mm3, %%mm2 \n\t" \ - \ - " pmulhw %[xC6S2], %%mm3 \n\t" /* mm3 = xC6S2 * irot_input_y */ \ - " psrlw $15, %%mm2 \n\t" \ - \ - " paddw %%mm2, %%mm3 \n\t" /* Truncated */ \ - " psubsw %%mm5, %%mm3 \n\t" \ - \ - " movq %%mm3," #ip6 " \n\t" \ - /* ------------------------------------------------------------------- */ \ - " movq %[xC4S4], %%mm0 \n\t" \ - " movq %%mm1, %%mm2 \n\t" \ - " movq %%mm1, %%mm3 \n\t" \ - \ - " pmulhw %%mm0, %%mm1 \n\t" /* mm0 = xC4S4 * ( is12 - is56 ) - ( is12 - is56 ) */ \ - " psrlw $15, %%mm2 \n\t" \ - \ - " paddw %%mm3, %%mm1 \n\t" /* mm0 = xC4S4 * ( is12 - is56 ) */ \ - " paddw %%mm2, %%mm1 \n\t" /* Truncate mm1, now it is icommon_product1 */ \ - \ - " movq %%mm7, %%mm2 \n\t" \ - " movq %%mm7, %%mm3 \n\t" \ - \ - " pmulhw %%mm0, %%mm7 \n\t" /* mm7 = xC4S4 * ( id12 + id56 ) - ( id12 + id56 ) */ \ - " psrlw $15, %%mm2 \n\t" \ - \ - " paddw %%mm3, %%mm7 \n\t" /* mm7 = xC4S4 * ( id12 + id56 ) */ \ - " paddw %%mm2, %%mm7 \n\t" /* Truncate mm7, now it is icommon_product2 */ \ - /* ------------------------------------------------------------------- */ \ - " pxor %%mm0, %%mm0 \n\t" /* Clear mm0 */ \ - " psubsw %%mm6, %%mm0 \n\t" /* mm0 = - id34 */ \ - \ - " psubsw %%mm7, %%mm0 \n\t" /* mm0 = - ( id34 + idcommon_product2 ) */ \ - " paddsw %%mm6, %%mm6 \n\t" \ - " paddsw %%mm0, %%mm6 \n\t" /* mm6 = id34 - icommon_product2 */ \ - \ - " psubsw %%mm1, %%mm4 \n\t" /* mm4 = id07 - icommon_product1 */ \ - " paddsw %%mm1, %%mm1 \n\t" \ - " paddsw %%mm4, %%mm1 \n\t" /* mm1 = id07 + icommon_product1 */ \ - /* ------------------------------------------------------------------- */ \ - " movq %[xC1S7], %%mm7 \n\t" \ - " movq %%mm1, %%mm2 \n\t" \ - \ - " movq %%mm1, %%mm3 \n\t" \ - " pmulhw %%mm7, %%mm1 \n\t" /* mm1 = xC1S7 * irot_input_x - irot_input_x */ \ - \ - " movq %[xC7S1], %%mm7 \n\t" \ - " psrlw $15, %%mm2 \n\t" \ - \ - " paddw %%mm3, %%mm1 \n\t" /* mm1 = xC1S7 * irot_input_x */ \ - " paddw %%mm2, %%mm1 \n\t" /* Trucated */ \ - \ - " pmulhw %%mm7, %%mm3 \n\t" /* mm3 = xC7S1 * irot_input_x */ \ - " paddw %%mm2, %%mm3 \n\t" /* Truncated */ \ - \ - " movq %%mm0, %%mm5 \n\t" \ - " movq %%mm0, %%mm2 \n\t" \ - \ - " movq %[xC1S7], %%mm7 \n\t" \ - " pmulhw %%mm7, %%mm0 \n\t" /* mm0 = xC1S7 * irot_input_y - irot_input_y */ \ - \ - " movq %[xC7S1], %%mm7 \n\t" \ - " psrlw $15, %%mm2 \n\t" \ - \ - " paddw %%mm5, %%mm0 \n\t" /* mm0 = xC1S7 * irot_input_y */ \ - " paddw %%mm2, %%mm0 \n\t" /* Truncated */ \ - \ - " pmulhw %%mm7, %%mm5 \n\t" /* mm5 = xC7S1 * irot_input_y */ \ - " paddw %%mm2, %%mm5 \n\t" /* Truncated */ \ - \ - " psubsw %%mm5, %%mm1 \n\t" /* mm1 = xC1S7 * irot_input_x - xC7S1 * irot_input_y = ip1 */ \ - " paddsw %%mm0, %%mm3 \n\t" /* mm3 = xC7S1 * irot_input_x - xC1S7 * irot_input_y = ip7 */ \ - \ - " movq %%mm1," #ip1 " \n\t" \ - " movq %%mm3," #ip7 " \n\t" \ - /* ------------------------------------------------------------------- */ \ - " movq %[xC3S5], %%mm0 \n\t" \ - " movq %[xC5S3], %%mm1 \n\t" \ - \ - " movq %%mm6, %%mm5 \n\t" \ - " movq %%mm6, %%mm7 \n\t" \ - \ - " movq %%mm4, %%mm2 \n\t" \ - " movq %%mm4, %%mm3 \n\t" \ - \ - " pmulhw %%mm0, %%mm4 \n\t" /* mm4 = xC3S5 * irot_input_x - irot_input_x */ \ - " pmulhw %%mm1, %%mm6 \n\t" /* mm6 = xC5S3 * irot_input_y - irot_input_y */ \ - \ - " psrlw $15, %%mm2 \n\t" \ - " psrlw $15, %%mm5 \n\t" \ - \ - " paddw %%mm3, %%mm4 \n\t" /* mm4 = xC3S5 * irot_input_x */ \ - " paddw %%mm7, %%mm6 \n\t" /* mm6 = xC5S3 * irot_input_y */ \ - \ - " paddw %%mm2, %%mm4 \n\t" /* Truncated */ \ - " paddw %%mm5, %%mm6 \n\t" /* Truncated */ \ - \ - " psubsw %%mm6, %%mm4 \n\t" /* ip3 */ \ - " movq %%mm4," #ip3 " \n\t" \ - \ - " movq %%mm3, %%mm4 \n\t" \ - " movq %%mm7, %%mm6 \n\t" \ - \ - " pmulhw %%mm1, %%mm3 \n\t" /* mm3 = xC5S3 * irot_input_x - irot_input_x */ \ - " pmulhw %%mm0, %%mm7 \n\t" /* mm7 = xC3S5 * irot_input_y - irot_input_y */ \ - \ - " paddw %%mm2, %%mm4 \n\t" \ - " paddw %%mm5, %%mm6 \n\t" \ - \ - " paddw %%mm4, %%mm3 \n\t" /* mm3 = xC5S3 * irot_input_x */ \ - " paddw %%mm6, %%mm7 \n\t" /* mm7 = xC3S5 * irot_input_y */ \ - \ - " paddw %%mm7, %%mm3 \n\t" /* ip5 */ \ - " movq %%mm3," #ip5 " \n\t" - -#define Transpose_mmx(ip0,ip1,ip2,ip3,ip4,ip5,ip6,ip7, \ - op0,op1,op2,op3,op4,op5,op6,op7) \ - " movq " #ip0 ", %%mm0 \n\t" /* mm0 = a0 a1 a2 a3 */ \ - " movq " #ip4 ", %%mm4 \n\t" /* mm4 = e4 e5 e6 e7 */ \ - " movq " #ip1 ", %%mm1 \n\t" /* mm1 = b0 b1 b2 b3 */ \ - " movq " #ip5 ", %%mm5 \n\t" /* mm5 = f4 f5 f6 f7 */ \ - " movq " #ip2 ", %%mm2 \n\t" /* mm2 = c0 c1 c2 c3 */ \ - " movq " #ip6 ", %%mm6 \n\t" /* mm6 = g4 g5 g6 g7 */ \ - " movq " #ip3 ", %%mm3 \n\t" /* mm3 = d0 d1 d2 d3 */ \ - " movq %%mm1," #op1 " \n\t" /* save b0 b1 b2 b3 */ \ - " movq " #ip7 ", %%mm7 \n\t" /* mm7 = h0 h1 h2 h3 */ \ - /* Transpose 2x8 block */ \ - " movq %%mm4, %%mm1 \n\t" /* mm1 = e3 e2 e1 e0 */ \ - " punpcklwd %%mm5, %%mm4 \n\t" /* mm4 = f1 e1 f0 e0 */ \ - " movq %%mm0," #op0 " \n\t" /* save a3 a2 a1 a0 */ \ - " punpckhwd %%mm5, %%mm1 \n\t" /* mm1 = f3 e3 f2 e2 */ \ - " movq %%mm6, %%mm0 \n\t" /* mm0 = g3 g2 g1 g0 */ \ - " punpcklwd %%mm7, %%mm6 \n\t" /* mm6 = h1 g1 h0 g0 */ \ - " movq %%mm4, %%mm5 \n\t" /* mm5 = f1 e1 f0 e0 */ \ - " punpckldq %%mm6, %%mm4 \n\t" /* mm4 = h0 g0 f0 e0 = MM4 */ \ - " punpckhdq %%mm6, %%mm5 \n\t" /* mm5 = h1 g1 f1 e1 = MM5 */ \ - " movq %%mm1, %%mm6 \n\t" /* mm6 = f3 e3 f2 e2 */ \ - " movq %%mm4," #op4 " \n\t" \ - " punpckhwd %%mm7, %%mm0 \n\t" /* mm0 = h3 g3 h2 g2 */ \ - " movq %%mm5," #op5 " \n\t" \ - " punpckhdq %%mm0, %%mm6 \n\t" /* mm6 = h3 g3 f3 e3 = MM7 */ \ - " movq " #op0 ", %%mm4 \n\t" /* mm4 = a3 a2 a1 a0 */ \ - " punpckldq %%mm0, %%mm1 \n\t" /* mm1 = h2 g2 f2 e2 = MM6 */ \ - " movq " #op1 ", %%mm5 \n\t" /* mm5 = b3 b2 b1 b0 */ \ - " movq %%mm4, %%mm0 \n\t" /* mm0 = a3 a2 a1 a0 */ \ - " movq %%mm6," #op7 " \n\t" \ - " punpcklwd %%mm5, %%mm0 \n\t" /* mm0 = b1 a1 b0 a0 */ \ - " movq %%mm1," #op6 " \n\t" \ - " punpckhwd %%mm5, %%mm4 \n\t" /* mm4 = b3 a3 b2 a2 */ \ - " movq %%mm2, %%mm5 \n\t" /* mm5 = c3 c2 c1 c0 */ \ - " punpcklwd %%mm3, %%mm2 \n\t" /* mm2 = d1 c1 d0 c0 */ \ - " movq %%mm0, %%mm1 \n\t" /* mm1 = b1 a1 b0 a0 */ \ - " punpckldq %%mm2, %%mm0 \n\t" /* mm0 = d0 c0 b0 a0 = MM0 */ \ - " punpckhdq %%mm2, %%mm1 \n\t" /* mm1 = d1 c1 b1 a1 = MM1 */ \ - " movq %%mm4, %%mm2 \n\t" /* mm2 = b3 a3 b2 a2 */ \ - " movq %%mm0," #op0 " \n\t" \ - " punpckhwd %%mm3, %%mm5 \n\t" /* mm5 = d3 c3 d2 c2 */ \ - " movq %%mm1," #op1 " \n\t" \ - " punpckhdq %%mm5, %%mm4 \n\t" /* mm4 = d3 c3 b3 a3 = MM3 */ \ - " punpckldq %%mm5, %%mm2 \n\t" /* mm2 = d2 c2 b2 a2 = MM2 */ \ - " movq %%mm4," #op3 " \n\t" \ - " movq %%mm2," #op2 " \n\t" - - -/* This performs a 2D Forward DCT on an 8x8 block with short - coefficients. We try to do the truncation to match the C - version. */ -static void fdct_short__mmx ( ogg_int16_t *InputData, ogg_int16_t *OutputData) -{ - ogg_int16_t __attribute__((aligned(8))) temp[8*8]; - - __asm__ __volatile__ ( - " .balign 16 \n\t" - /* - * Input data is an 8x8 block. To make processing of the data more efficent - * we will transpose the block of data to two 4x8 blocks??? - */ - Transpose_mmx ( (%0), 16(%0), 32(%0), 48(%0), 8(%0), 24(%0), 40(%0), 56(%0), - (%1), 16(%1), 32(%1), 48(%1), 8(%1), 24(%1), 40(%1), 56(%1)) - Fdct_mmx ( (%1), 16(%1), 32(%1), 48(%1), 8(%1), 24(%1), 40(%1), 56(%1), (%2)) - - Transpose_mmx (64(%0), 80(%0), 96(%0),112(%0), 72(%0), 88(%0),104(%0),120(%0), - 64(%1), 80(%1), 96(%1),112(%1), 72(%1), 88(%1),104(%1),120(%1)) - Fdct_mmx (64(%1), 80(%1), 96(%1),112(%1), 72(%1), 88(%1),104(%1),120(%1), (%2)) - - Transpose_mmx ( 0(%1), 16(%1), 32(%1), 48(%1), 64(%1), 80(%1), 96(%1),112(%1), - 0(%1), 16(%1), 32(%1), 48(%1), 64(%1), 80(%1), 96(%1),112(%1)) - Fdct_mmx ( 0(%1), 16(%1), 32(%1), 48(%1), 64(%1), 80(%1), 96(%1),112(%1), (%2)) - - Transpose_mmx ( 8(%1), 24(%1), 40(%1), 56(%1), 72(%1), 88(%1),104(%1),120(%1), - 8(%1), 24(%1), 40(%1), 56(%1), 72(%1), 88(%1),104(%1),120(%1)) - Fdct_mmx ( 8(%1), 24(%1), 40(%1), 56(%1), 72(%1), 88(%1),104(%1),120(%1), (%2)) - - " emms \n\t" - - : "+r" (InputData), - "+r" (OutputData) - : "r" (temp), - [xC1S7] "m" (xC1S7), /* gcc 3.1+ allows named asm parameters */ - [xC2S6] "m" (xC2S6), - [xC3S5] "m" (xC3S5), - [xC4S4] "m" (xC4S4), - [xC5S3] "m" (xC5S3), - [xC6S2] "m" (xC6S2), - [xC7S1] "m" (xC7S1) - : "memory" - ); -} - -/* install our implementation in the function table */ -void dsp_mmx_fdct_init(DspFunctions *funcs) -{ - funcs->fdct_short = fdct_short__mmx; -} - -#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_64/idct_mmx.c b/Engine/lib/libtheora/lib/enc/x86_64/idct_mmx.c deleted file mode 100644 index b87db6085..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_64/idct_mmx.c +++ /dev/null @@ -1,27 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: idct_mmx.c 15397 2008-10-14 02:06:24Z tterribe $ - - ********************************************************************/ - -#include "../codec_internal.h" - -#if defined(USE_ASM) - -/* nothing implemented right now */ -void dsp_mmx_idct_init(DspFunctions *funcs) -{ -} - -#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/enc/x86_64/recon_mmx.c b/Engine/lib/libtheora/lib/enc/x86_64/recon_mmx.c deleted file mode 100644 index b9b86e982..000000000 --- a/Engine/lib/libtheora/lib/enc/x86_64/recon_mmx.c +++ /dev/null @@ -1,184 +0,0 @@ -/******************************************************************** - * * - * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * - * by the Xiph.Org Foundation http://www.xiph.org/ * - * * - ******************************************************************** - - function: - last mod: $Id: recon_mmx.c 15397 2008-10-14 02:06:24Z tterribe $ - - ********************************************************************/ - -#include "../codec_internal.h" - -#if defined(USE_ASM) - -typedef unsigned long long ogg_uint64_t; - -static const __attribute__ ((aligned(8),used)) ogg_int64_t V128 = 0x8080808080808080LL; - -static void copy8x8__mmx (unsigned char *src, - unsigned char *dest, - ogg_uint32_t stride) -{ - __asm__ __volatile__ ( - " .balign 16 \n\t" - - " lea (%2, %2, 2), %%rdi \n\t" - - " movq (%1), %%mm0 \n\t" - " movq (%1, %2), %%mm1 \n\t" - " movq (%1, %2, 2), %%mm2 \n\t" - " movq (%1, %%rdi), %%mm3 \n\t" - - " lea (%1, %2, 4), %1 \n\t" - - " movq %%mm0, (%0) \n\t" - " movq %%mm1, (%0, %2) \n\t" - " movq %%mm2, (%0, %2, 2) \n\t" - " movq %%mm3, (%0, %%rdi) \n\t" - - " lea (%0, %2, 4), %0 \n\t" - - " movq (%1), %%mm0 \n\t" - " movq (%1, %2), %%mm1 \n\t" - " movq (%1, %2, 2), %%mm2 \n\t" - " movq (%1, %%rdi), %%mm3 \n\t" - - " movq %%mm0, (%0) \n\t" - " movq %%mm1, (%0, %2) \n\t" - " movq %%mm2, (%0, %2, 2) \n\t" - " movq %%mm3, (%0, %%rdi) \n\t" - : "+a" (dest) - : "c" (src), - "d" ((ogg_uint64_t)stride) - : "memory", "rdi" - ); -} - -static void recon_intra8x8__mmx (unsigned char *ReconPtr, ogg_int16_t *ChangePtr, - ogg_uint32_t LineStep) -{ - __asm__ __volatile__ ( - " .balign 16 \n\t" - - " movq %[V128], %%mm0 \n\t" /* Set mm0 to 0x8080808080808080 */ - - " lea 128(%1), %%rdi \n\t" /* Endpoint in input buffer */ - "1: \n\t" - " movq (%1), %%mm2 \n\t" /* First four input values */ - - " packsswb 8(%1), %%mm2 \n\t" /* pack with next(high) four values */ - " por %%mm0, %%mm0 \n\t" - " pxor %%mm0, %%mm2 \n\t" /* Convert result to unsigned (same as add 128) */ - " lea 16(%1), %1 \n\t" /* Step source buffer */ - " cmp %%rdi, %1 \n\t" /* are we done */ - - " movq %%mm2, (%0) \n\t" /* store results */ - - " lea (%0, %2), %0 \n\t" /* Step output buffer */ - " jc 1b \n\t" /* Loop back if we are not done */ - : "+r" (ReconPtr) - : "r" (ChangePtr), - "r" ((ogg_uint64_t)LineStep), - [V128] "m" (V128) - : "memory", "rdi" - ); -} - -static void recon_inter8x8__mmx (unsigned char *ReconPtr, unsigned char *RefPtr, - ogg_int16_t *ChangePtr, ogg_uint32_t LineStep) -{ - __asm__ __volatile__ ( - " .balign 16 \n\t" - - " pxor %%mm0, %%mm0 \n\t" - " lea 128(%1), %%rdi \n\t" - - "1: \n\t" - " movq (%2), %%mm2 \n\t" /* (+3 misaligned) 8 reference pixels */ - - " movq (%1), %%mm4 \n\t" /* first 4 changes */ - " movq %%mm2, %%mm3 \n\t" - " movq 8(%1), %%mm5 \n\t" /* last 4 changes */ - " punpcklbw %%mm0, %%mm2 \n\t" /* turn first 4 refs into positive 16-bit #s */ - " paddsw %%mm4, %%mm2 \n\t" /* add in first 4 changes */ - " punpckhbw %%mm0, %%mm3 \n\t" /* turn last 4 refs into positive 16-bit #s */ - " paddsw %%mm5, %%mm3 \n\t" /* add in last 4 changes */ - " add %3, %2 \n\t" /* next row of reference pixels */ - " packuswb %%mm3, %%mm2 \n\t" /* pack result to unsigned 8-bit values */ - " lea 16(%1), %1 \n\t" /* next row of changes */ - " cmp %%rdi, %1 \n\t" /* are we done? */ - - " movq %%mm2, (%0) \n\t" /* store result */ - - " lea (%0, %3), %0 \n\t" /* next row of output */ - " jc 1b \n\t" - : "+r" (ReconPtr) - : "r" (ChangePtr), - "r" (RefPtr), - "r" ((ogg_uint64_t)LineStep) - : "memory", "rdi" - ); -} - -static void recon_inter8x8_half__mmx (unsigned char *ReconPtr, unsigned char *RefPtr1, - unsigned char *RefPtr2, ogg_int16_t *ChangePtr, - ogg_uint32_t LineStep) -{ - __asm__ __volatile__ ( - " .balign 16 \n\t" - - " pxor %%mm0, %%mm0 \n\t" - " lea 128(%1), %%rdi \n\t" - - "1: \n\t" - " movq (%2), %%mm2 \n\t" /* (+3 misaligned) 8 reference pixels */ - " movq (%3), %%mm4 \n\t" /* (+3 misaligned) 8 reference pixels */ - - " movq %%mm2, %%mm3 \n\t" - " punpcklbw %%mm0, %%mm2 \n\t" /* mm2 = start ref1 as positive 16-bit #s */ - " movq %%mm4, %%mm5 \n\t" - " movq (%1), %%mm6 \n\t" /* first 4 changes */ - " punpckhbw %%mm0, %%mm3 \n\t" /* mm3 = end ref1 as positive 16-bit #s */ - " movq 8(%1), %%mm7 \n\t" /* last 4 changes */ - " punpcklbw %%mm0, %%mm4 \n\t" /* mm4 = start ref2 as positive 16-bit #s */ - " punpckhbw %%mm0, %%mm5 \n\t" /* mm5 = end ref2 as positive 16-bit #s */ - " paddw %%mm4, %%mm2 \n\t" /* mm2 = start (ref1 + ref2) */ - " paddw %%mm5, %%mm3 \n\t" /* mm3 = end (ref1 + ref2) */ - " psrlw $1, %%mm2 \n\t" /* mm2 = start (ref1 + ref2)/2 */ - " psrlw $1, %%mm3 \n\t" /* mm3 = end (ref1 + ref2)/2 */ - " paddw %%mm6, %%mm2 \n\t" /* add changes to start */ - " paddw %%mm7, %%mm3 \n\t" /* add changes to end */ - " lea 16(%1), %1 \n\t" /* next row of changes */ - " packuswb %%mm3, %%mm2 \n\t" /* pack start|end to unsigned 8-bit */ - " add %4, %2 \n\t" /* next row of reference pixels */ - " add %4, %3 \n\t" /* next row of reference pixels */ - " movq %%mm2, (%0) \n\t" /* store result */ - " add %4, %0 \n\t" /* next row of output */ - " cmp %%rdi, %1 \n\t" /* are we done? */ - " jc 1b \n\t" - : "+r" (ReconPtr) - : "r" (ChangePtr), - "r" (RefPtr1), - "r" (RefPtr2), - "r" ((ogg_uint64_t)LineStep) - : "memory", "rdi" - ); -} - -void dsp_mmx_recon_init(DspFunctions *funcs) -{ - funcs->copy8x8 = copy8x8__mmx; - funcs->recon_intra8x8 = recon_intra8x8__mmx; - funcs->recon_inter8x8 = recon_inter8x8__mmx; - funcs->recon_inter8x8_half = recon_inter8x8_half__mmx; -} - -#endif /* USE_ASM */ diff --git a/Engine/lib/libtheora/lib/encapiwrapper.c b/Engine/lib/libtheora/lib/encapiwrapper.c new file mode 100644 index 000000000..874f12442 --- /dev/null +++ b/Engine/lib/libtheora/lib/encapiwrapper.c @@ -0,0 +1,168 @@ +#include +#include +#include +#include "apiwrapper.h" +#include "encint.h" +#include "theora/theoraenc.h" + + + +static void th_enc_api_clear(th_api_wrapper *_api){ + if(_api->encode)th_encode_free(_api->encode); + memset(_api,0,sizeof(*_api)); +} + +static void theora_encode_clear(theora_state *_te){ + if(_te->i!=NULL)theora_info_clear(_te->i); + memset(_te,0,sizeof(*_te)); +} + +static int theora_encode_control(theora_state *_te,int _req, + void *_buf,size_t _buf_sz){ + return th_encode_ctl(((th_api_wrapper *)_te->i->codec_setup)->encode, + _req,_buf,_buf_sz); +} + +static ogg_int64_t theora_encode_granule_frame(theora_state *_te, + ogg_int64_t _gp){ + return th_granule_frame(((th_api_wrapper *)_te->i->codec_setup)->encode,_gp); +} + +static double theora_encode_granule_time(theora_state *_te,ogg_int64_t _gp){ + return th_granule_time(((th_api_wrapper *)_te->i->codec_setup)->encode,_gp); +} + +static const oc_state_dispatch_vtable OC_ENC_DISPATCH_VTBL={ + (oc_state_clear_func)theora_encode_clear, + (oc_state_control_func)theora_encode_control, + (oc_state_granule_frame_func)theora_encode_granule_frame, + (oc_state_granule_time_func)theora_encode_granule_time, +}; + +int theora_encode_init(theora_state *_te,theora_info *_ci){ + th_api_info *apiinfo; + th_info info; + ogg_uint32_t keyframe_frequency_force; + /*Allocate our own combined API wrapper/theora_info struct. + We put them both in one malloc'd block so that when the API wrapper is + freed, the info struct goes with it. + This avoids having to figure out whether or not we need to free the info + struct in either theora_info_clear() or theora_clear().*/ + apiinfo=(th_api_info *)_ogg_malloc(sizeof(*apiinfo)); + if(apiinfo==NULL)return TH_EFAULT; + /*Make our own copy of the info struct, since its lifetime should be + independent of the one we were passed in.*/ + *&apiinfo->info=*_ci; + oc_theora_info2th_info(&info,_ci); + apiinfo->api.encode=th_encode_alloc(&info); + if(apiinfo->api.encode==NULL){ + _ogg_free(apiinfo); + return OC_EINVAL; + } + apiinfo->api.clear=(oc_setup_clear_func)th_enc_api_clear; + /*Provide entry points for ABI compatibility with old decoder shared libs.*/ + _te->internal_encode=(void *)&OC_ENC_DISPATCH_VTBL; + _te->internal_decode=NULL; + _te->granulepos=0; + _te->i=&apiinfo->info; + _te->i->codec_setup=&apiinfo->api; + /*Set the precise requested keyframe frequency.*/ + keyframe_frequency_force=_ci->keyframe_auto_p? + _ci->keyframe_frequency_force:_ci->keyframe_frequency; + th_encode_ctl(apiinfo->api.encode, + TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE, + &keyframe_frequency_force,sizeof(keyframe_frequency_force)); + /*TODO: Additional codec setup using the extra fields in theora_info.*/ + return 0; +} + +int theora_encode_YUVin(theora_state *_te,yuv_buffer *_yuv){ + th_api_wrapper *api; + th_ycbcr_buffer buf; + int ret; + api=(th_api_wrapper *)_te->i->codec_setup; + buf[0].width=_yuv->y_width; + buf[0].height=_yuv->y_height; + buf[0].stride=_yuv->y_stride; + buf[0].data=_yuv->y; + buf[1].width=_yuv->uv_width; + buf[1].height=_yuv->uv_height; + buf[1].stride=_yuv->uv_stride; + buf[1].data=_yuv->u; + buf[2].width=_yuv->uv_width; + buf[2].height=_yuv->uv_height; + buf[2].stride=_yuv->uv_stride; + buf[2].data=_yuv->v; + ret=th_encode_ycbcr_in(api->encode,buf); + if(ret<0)return ret; + _te->granulepos=api->encode->state.granpos; + return ret; +} + +int theora_encode_packetout(theora_state *_te,int _last_p,ogg_packet *_op){ + th_api_wrapper *api; + api=(th_api_wrapper *)_te->i->codec_setup; + return th_encode_packetout(api->encode,_last_p,_op); +} + +int theora_encode_header(theora_state *_te,ogg_packet *_op){ + oc_enc_ctx *enc; + th_api_wrapper *api; + int ret; + api=(th_api_wrapper *)_te->i->codec_setup; + enc=api->encode; + /*If we've already started encoding, fail.*/ + if(enc->packet_state>OC_PACKET_EMPTY||enc->state.granpos!=0){ + return TH_EINVAL; + } + /*Reset the state to make sure we output an info packet.*/ + enc->packet_state=OC_PACKET_INFO_HDR; + ret=th_encode_flushheader(api->encode,NULL,_op); + return ret>=0?0:ret; +} + +int theora_encode_comment(theora_comment *_tc,ogg_packet *_op){ + oggpack_buffer opb; + void *buf; + int packet_state; + int ret; + packet_state=OC_PACKET_COMMENT_HDR; + oggpackB_writeinit(&opb); + ret=oc_state_flushheader(NULL,&packet_state,&opb,NULL,NULL, + th_version_string(),(th_comment *)_tc,_op); + if(ret>=0){ + /*The oggpack_buffer's lifetime ends with this function, so we have to + copy out the packet contents. + Presumably the application knows it is supposed to free this. + This part works nothing like the Vorbis API, and the documentation on it + has been wrong for some time, claiming libtheora owned the memory.*/ + buf=_ogg_malloc(_op->bytes); + if(buf==NULL){ + _op->packet=NULL; + ret=TH_EFAULT; + } + else{ + memcpy(buf,_op->packet,_op->bytes); + _op->packet=buf; + ret=0; + } + } + oggpack_writeclear(&opb); + return ret; +} + +int theora_encode_tables(theora_state *_te,ogg_packet *_op){ + oc_enc_ctx *enc; + th_api_wrapper *api; + int ret; + api=(th_api_wrapper *)_te->i->codec_setup; + enc=api->encode; + /*If we've already started encoding, fail.*/ + if(enc->packet_state>OC_PACKET_EMPTY||enc->state.granpos!=0){ + return TH_EINVAL; + } + /*Reset the state to make sure we output a setup packet.*/ + enc->packet_state=OC_PACKET_SETUP_HDR; + ret=th_encode_flushheader(api->encode,NULL,_op); + return ret>=0?0:ret; +} diff --git a/Engine/lib/libtheora/lib/encfrag.c b/Engine/lib/libtheora/lib/encfrag.c new file mode 100644 index 000000000..bb814c8e4 --- /dev/null +++ b/Engine/lib/libtheora/lib/encfrag.c @@ -0,0 +1,388 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: encfrag.c 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ +#include +#include +#include "encint.h" + + +void oc_enc_frag_sub(const oc_enc_ctx *_enc,ogg_int16_t _diff[64], + const unsigned char *_src,const unsigned char *_ref,int _ystride){ + (*_enc->opt_vtable.frag_sub)(_diff,_src,_ref,_ystride); +} + +void oc_enc_frag_sub_c(ogg_int16_t _diff[64],const unsigned char *_src, + const unsigned char *_ref,int _ystride){ + int i; + for(i=0;i<8;i++){ + int j; + for(j=0;j<8;j++)_diff[i*8+j]=(ogg_int16_t)(_src[j]-_ref[j]); + _src+=_ystride; + _ref+=_ystride; + } +} + +void oc_enc_frag_sub_128(const oc_enc_ctx *_enc,ogg_int16_t _diff[64], + const unsigned char *_src,int _ystride){ + (*_enc->opt_vtable.frag_sub_128)(_diff,_src,_ystride); +} + +void oc_enc_frag_sub_128_c(ogg_int16_t *_diff, + const unsigned char *_src,int _ystride){ + int i; + for(i=0;i<8;i++){ + int j; + for(j=0;j<8;j++)_diff[i*8+j]=(ogg_int16_t)(_src[j]-128); + _src+=_ystride; + } +} + +unsigned oc_enc_frag_sad(const oc_enc_ctx *_enc,const unsigned char *_x, + const unsigned char *_y,int _ystride){ + return (*_enc->opt_vtable.frag_sad)(_x,_y,_ystride); +} + +unsigned oc_enc_frag_sad_c(const unsigned char *_src, + const unsigned char *_ref,int _ystride){ + unsigned sad; + int i; + sad=0; + for(i=8;i-->0;){ + int j; + for(j=0;j<8;j++)sad+=abs(_src[j]-_ref[j]); + _src+=_ystride; + _ref+=_ystride; + } + return sad; +} + +unsigned oc_enc_frag_sad_thresh(const oc_enc_ctx *_enc, + const unsigned char *_src,const unsigned char *_ref,int _ystride, + unsigned _thresh){ + return (*_enc->opt_vtable.frag_sad_thresh)(_src,_ref,_ystride,_thresh); +} + +unsigned oc_enc_frag_sad_thresh_c(const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _thresh){ + unsigned sad; + int i; + sad=0; + for(i=8;i-->0;){ + int j; + for(j=0;j<8;j++)sad+=abs(_src[j]-_ref[j]); + if(sad>_thresh)break; + _src+=_ystride; + _ref+=_ystride; + } + return sad; +} + +unsigned oc_enc_frag_sad2_thresh(const oc_enc_ctx *_enc, + const unsigned char *_src,const unsigned char *_ref1, + const unsigned char *_ref2,int _ystride,unsigned _thresh){ + return (*_enc->opt_vtable.frag_sad2_thresh)(_src,_ref1,_ref2,_ystride, + _thresh); +} + +unsigned oc_enc_frag_sad2_thresh_c(const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride, + unsigned _thresh){ + unsigned sad; + int i; + sad=0; + for(i=8;i-->0;){ + int j; + for(j=0;j<8;j++)sad+=abs(_src[j]-(_ref1[j]+_ref2[j]>>1)); + if(sad>_thresh)break; + _src+=_ystride; + _ref1+=_ystride; + _ref2+=_ystride; + } + return sad; +} + +static void oc_diff_hadamard(ogg_int16_t _buf[64],const unsigned char *_src, + const unsigned char *_ref,int _ystride){ + int i; + for(i=0;i<8;i++){ + int t0; + int t1; + int t2; + int t3; + int t4; + int t5; + int t6; + int t7; + int r; + /*Hadamard stage 1:*/ + t0=_src[0]-_ref[0]+_src[4]-_ref[4]; + t4=_src[0]-_ref[0]-_src[4]+_ref[4]; + t1=_src[1]-_ref[1]+_src[5]-_ref[5]; + t5=_src[1]-_ref[1]-_src[5]+_ref[5]; + t2=_src[2]-_ref[2]+_src[6]-_ref[6]; + t6=_src[2]-_ref[2]-_src[6]+_ref[6]; + t3=_src[3]-_ref[3]+_src[7]-_ref[7]; + t7=_src[3]-_ref[3]-_src[7]+_ref[7]; + /*Hadamard stage 2:*/ + r=t0; + t0+=t2; + t2=r-t2; + r=t1; + t1+=t3; + t3=r-t3; + r=t4; + t4+=t6; + t6=r-t6; + r=t5; + t5+=t7; + t7=r-t7; + /*Hadamard stage 3:*/ + _buf[0*8+i]=(ogg_int16_t)(t0+t1); + _buf[1*8+i]=(ogg_int16_t)(t0-t1); + _buf[2*8+i]=(ogg_int16_t)(t2+t3); + _buf[3*8+i]=(ogg_int16_t)(t2-t3); + _buf[4*8+i]=(ogg_int16_t)(t4+t5); + _buf[5*8+i]=(ogg_int16_t)(t4-t5); + _buf[6*8+i]=(ogg_int16_t)(t6+t7); + _buf[7*8+i]=(ogg_int16_t)(t6-t7); + _src+=_ystride; + _ref+=_ystride; + } +} + +static void oc_diff_hadamard2(ogg_int16_t _buf[64],const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride){ + int i; + for(i=0;i<8;i++){ + int t0; + int t1; + int t2; + int t3; + int t4; + int t5; + int t6; + int t7; + int r; + /*Hadamard stage 1:*/ + r=_ref1[0]+_ref2[0]>>1; + t4=_ref1[4]+_ref2[4]>>1; + t0=_src[0]-r+_src[4]-t4; + t4=_src[0]-r-_src[4]+t4; + r=_ref1[1]+_ref2[1]>>1; + t5=_ref1[5]+_ref2[5]>>1; + t1=_src[1]-r+_src[5]-t5; + t5=_src[1]-r-_src[5]+t5; + r=_ref1[2]+_ref2[2]>>1; + t6=_ref1[6]+_ref2[6]>>1; + t2=_src[2]-r+_src[6]-t6; + t6=_src[2]-r-_src[6]+t6; + r=_ref1[3]+_ref2[3]>>1; + t7=_ref1[7]+_ref2[7]>>1; + t3=_src[3]-r+_src[7]-t7; + t7=_src[3]-r-_src[7]+t7; + /*Hadamard stage 2:*/ + r=t0; + t0+=t2; + t2=r-t2; + r=t1; + t1+=t3; + t3=r-t3; + r=t4; + t4+=t6; + t6=r-t6; + r=t5; + t5+=t7; + t7=r-t7; + /*Hadamard stage 3:*/ + _buf[0*8+i]=(ogg_int16_t)(t0+t1); + _buf[1*8+i]=(ogg_int16_t)(t0-t1); + _buf[2*8+i]=(ogg_int16_t)(t2+t3); + _buf[3*8+i]=(ogg_int16_t)(t2-t3); + _buf[4*8+i]=(ogg_int16_t)(t4+t5); + _buf[5*8+i]=(ogg_int16_t)(t4-t5); + _buf[6*8+i]=(ogg_int16_t)(t6+t7); + _buf[7*8+i]=(ogg_int16_t)(t6-t7); + _src+=_ystride; + _ref1+=_ystride; + _ref2+=_ystride; + } +} + +static void oc_intra_hadamard(ogg_int16_t _buf[64],const unsigned char *_src, + int _ystride){ + int i; + for(i=0;i<8;i++){ + int t0; + int t1; + int t2; + int t3; + int t4; + int t5; + int t6; + int t7; + int r; + /*Hadamard stage 1:*/ + t0=_src[0]+_src[4]; + t4=_src[0]-_src[4]; + t1=_src[1]+_src[5]; + t5=_src[1]-_src[5]; + t2=_src[2]+_src[6]; + t6=_src[2]-_src[6]; + t3=_src[3]+_src[7]; + t7=_src[3]-_src[7]; + /*Hadamard stage 2:*/ + r=t0; + t0+=t2; + t2=r-t2; + r=t1; + t1+=t3; + t3=r-t3; + r=t4; + t4+=t6; + t6=r-t6; + r=t5; + t5+=t7; + t7=r-t7; + /*Hadamard stage 3:*/ + _buf[0*8+i]=(ogg_int16_t)(t0+t1); + _buf[1*8+i]=(ogg_int16_t)(t0-t1); + _buf[2*8+i]=(ogg_int16_t)(t2+t3); + _buf[3*8+i]=(ogg_int16_t)(t2-t3); + _buf[4*8+i]=(ogg_int16_t)(t4+t5); + _buf[5*8+i]=(ogg_int16_t)(t4-t5); + _buf[6*8+i]=(ogg_int16_t)(t6+t7); + _buf[7*8+i]=(ogg_int16_t)(t6-t7); + _src+=_ystride; + } +} + +unsigned oc_hadamard_sad_thresh(const ogg_int16_t _buf[64],unsigned _thresh){ + unsigned sad; + int t0; + int t1; + int t2; + int t3; + int t4; + int t5; + int t6; + int t7; + int r; + int i; + sad=0; + for(i=0;i<8;i++){ + /*Hadamard stage 1:*/ + t0=_buf[i*8+0]+_buf[i*8+4]; + t4=_buf[i*8+0]-_buf[i*8+4]; + t1=_buf[i*8+1]+_buf[i*8+5]; + t5=_buf[i*8+1]-_buf[i*8+5]; + t2=_buf[i*8+2]+_buf[i*8+6]; + t6=_buf[i*8+2]-_buf[i*8+6]; + t3=_buf[i*8+3]+_buf[i*8+7]; + t7=_buf[i*8+3]-_buf[i*8+7]; + /*Hadamard stage 2:*/ + r=t0; + t0+=t2; + t2=r-t2; + r=t1; + t1+=t3; + t3=r-t3; + r=t4; + t4+=t6; + t6=r-t6; + r=t5; + t5+=t7; + t7=r-t7; + /*Hadamard stage 3:*/ + r=abs(t0+t1); + r+=abs(t0-t1); + r+=abs(t2+t3); + r+=abs(t2-t3); + r+=abs(t4+t5); + r+=abs(t4-t5); + r+=abs(t6+t7); + r+=abs(t6-t7); + sad+=r; + if(sad>_thresh)break; + } + return sad; +} + +unsigned oc_enc_frag_satd_thresh(const oc_enc_ctx *_enc, + const unsigned char *_src,const unsigned char *_ref,int _ystride, + unsigned _thresh){ + return (*_enc->opt_vtable.frag_satd_thresh)(_src,_ref,_ystride,_thresh); +} + +unsigned oc_enc_frag_satd_thresh_c(const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _thresh){ + ogg_int16_t buf[64]; + oc_diff_hadamard(buf,_src,_ref,_ystride); + return oc_hadamard_sad_thresh(buf,_thresh); +} + +unsigned oc_enc_frag_satd2_thresh(const oc_enc_ctx *_enc, + const unsigned char *_src,const unsigned char *_ref1, + const unsigned char *_ref2,int _ystride,unsigned _thresh){ + return (*_enc->opt_vtable.frag_satd2_thresh)(_src,_ref1,_ref2,_ystride, + _thresh); +} + +unsigned oc_enc_frag_satd2_thresh_c(const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride, + unsigned _thresh){ + ogg_int16_t buf[64]; + oc_diff_hadamard2(buf,_src,_ref1,_ref2,_ystride); + return oc_hadamard_sad_thresh(buf,_thresh); +} + +unsigned oc_enc_frag_intra_satd(const oc_enc_ctx *_enc, + const unsigned char *_src,int _ystride){ + return (*_enc->opt_vtable.frag_intra_satd)(_src,_ystride); +} + +unsigned oc_enc_frag_intra_satd_c(const unsigned char *_src,int _ystride){ + ogg_int16_t buf[64]; + oc_intra_hadamard(buf,_src,_ystride); + return oc_hadamard_sad_thresh(buf,UINT_MAX) + -abs(buf[0]+buf[1]+buf[2]+buf[3]+buf[4]+buf[5]+buf[6]+buf[7]); +} + +void oc_enc_frag_copy2(const oc_enc_ctx *_enc,unsigned char *_dst, + const unsigned char *_src1,const unsigned char *_src2,int _ystride){ + (*_enc->opt_vtable.frag_copy2)(_dst,_src1,_src2,_ystride); +} + +void oc_enc_frag_copy2_c(unsigned char *_dst, + const unsigned char *_src1,const unsigned char *_src2,int _ystride){ + int i; + int j; + for(i=8;i-->0;){ + for(j=0;j<8;j++)_dst[j]=_src1[j]+_src2[j]>>1; + _dst+=_ystride; + _src1+=_ystride; + _src2+=_ystride; + } +} + +void oc_enc_frag_recon_intra(const oc_enc_ctx *_enc, + unsigned char *_dst,int _ystride,const ogg_int16_t _residue[64]){ + (*_enc->opt_vtable.frag_recon_intra)(_dst,_ystride,_residue); +} + +void oc_enc_frag_recon_inter(const oc_enc_ctx *_enc,unsigned char *_dst, + const unsigned char *_src,int _ystride,const ogg_int16_t _residue[64]){ + (*_enc->opt_vtable.frag_recon_inter)(_dst,_src,_ystride,_residue); +} diff --git a/Engine/lib/libtheora/lib/encinfo.c b/Engine/lib/libtheora/lib/encinfo.c new file mode 100644 index 000000000..83be1dae7 --- /dev/null +++ b/Engine/lib/libtheora/lib/encinfo.c @@ -0,0 +1,121 @@ +#include +#include +#include "internal.h" +#include "enquant.h" +#include "huffenc.h" + + + +/*Packs a series of octets from a given byte array into the pack buffer. + _opb: The pack buffer to store the octets in. + _buf: The byte array containing the bytes to pack. + _len: The number of octets to pack.*/ +static void oc_pack_octets(oggpack_buffer *_opb,const char *_buf,int _len){ + int i; + for(i=0;i<_len;i++)oggpackB_write(_opb,_buf[i],8); +} + + + +int oc_state_flushheader(oc_theora_state *_state,int *_packet_state, + oggpack_buffer *_opb,const th_quant_info *_qinfo, + const th_huff_code _codes[TH_NHUFFMAN_TABLES][TH_NDCT_TOKENS], + const char *_vendor,th_comment *_tc,ogg_packet *_op){ + unsigned char *packet; + int b_o_s; + if(_op==NULL)return TH_EFAULT; + switch(*_packet_state){ + /*Codec info header.*/ + case OC_PACKET_INFO_HDR:{ + if(_state==NULL)return TH_EFAULT; + oggpackB_reset(_opb); + /*Mark this packet as the info header.*/ + oggpackB_write(_opb,0x80,8); + /*Write the codec string.*/ + oc_pack_octets(_opb,"theora",6); + /*Write the codec bitstream version.*/ + oggpackB_write(_opb,TH_VERSION_MAJOR,8); + oggpackB_write(_opb,TH_VERSION_MINOR,8); + oggpackB_write(_opb,TH_VERSION_SUB,8); + /*Describe the encoded frame.*/ + oggpackB_write(_opb,_state->info.frame_width>>4,16); + oggpackB_write(_opb,_state->info.frame_height>>4,16); + oggpackB_write(_opb,_state->info.pic_width,24); + oggpackB_write(_opb,_state->info.pic_height,24); + oggpackB_write(_opb,_state->info.pic_x,8); + oggpackB_write(_opb,_state->info.pic_y,8); + oggpackB_write(_opb,_state->info.fps_numerator,32); + oggpackB_write(_opb,_state->info.fps_denominator,32); + oggpackB_write(_opb,_state->info.aspect_numerator,24); + oggpackB_write(_opb,_state->info.aspect_denominator,24); + oggpackB_write(_opb,_state->info.colorspace,8); + oggpackB_write(_opb,_state->info.target_bitrate,24); + oggpackB_write(_opb,_state->info.quality,6); + oggpackB_write(_opb,_state->info.keyframe_granule_shift,5); + oggpackB_write(_opb,_state->info.pixel_fmt,2); + /*Spare configuration bits.*/ + oggpackB_write(_opb,0,3); + b_o_s=1; + }break; + /*Comment header.*/ + case OC_PACKET_COMMENT_HDR:{ + int vendor_len; + int i; + if(_tc==NULL)return TH_EFAULT; + vendor_len=strlen(_vendor); + oggpackB_reset(_opb); + /*Mark this packet as the comment header.*/ + oggpackB_write(_opb,0x81,8); + /*Write the codec string.*/ + oc_pack_octets(_opb,"theora",6); + /*Write the vendor string.*/ + oggpack_write(_opb,vendor_len,32); + oc_pack_octets(_opb,_vendor,vendor_len); + oggpack_write(_opb,_tc->comments,32); + for(i=0;i<_tc->comments;i++){ + if(_tc->user_comments[i]!=NULL){ + oggpack_write(_opb,_tc->comment_lengths[i],32); + oc_pack_octets(_opb,_tc->user_comments[i],_tc->comment_lengths[i]); + } + else oggpack_write(_opb,0,32); + } + b_o_s=0; + }break; + /*Codec setup header.*/ + case OC_PACKET_SETUP_HDR:{ + int ret; + oggpackB_reset(_opb); + /*Mark this packet as the setup header.*/ + oggpackB_write(_opb,0x82,8); + /*Write the codec string.*/ + oc_pack_octets(_opb,"theora",6); + /*Write the quantizer tables.*/ + oc_quant_params_pack(_opb,_qinfo); + /*Write the huffman codes.*/ + ret=oc_huff_codes_pack(_opb,_codes); + /*This should never happen, because we validate the tables when they + are set. + If you see, it's a good chance memory is being corrupted.*/ + if(ret<0)return ret; + b_o_s=0; + }break; + /*No more headers to emit.*/ + default:return 0; + } + /*This is kind of fugly: we hand the user a buffer which they do not own. + We will overwrite it when the next packet is output, so the user better be + done with it by then. + Vorbis is little better: it hands back buffers that it will free the next + time the headers are requested, or when the encoder is cleared. + Hopefully libogg2 will make this much cleaner.*/ + packet=oggpackB_get_buffer(_opb); + /*If there's no packet, malloc failed while writing.*/ + if(packet==NULL)return TH_EFAULT; + _op->packet=packet; + _op->bytes=oggpackB_bytes(_opb); + _op->b_o_s=b_o_s; + _op->e_o_s=0; + _op->granulepos=0; + _op->packetno=*_packet_state+3; + return ++(*_packet_state)+3; +} diff --git a/Engine/lib/libtheora/lib/encint.h b/Engine/lib/libtheora/lib/encint.h new file mode 100644 index 000000000..97897d5a0 --- /dev/null +++ b/Engine/lib/libtheora/lib/encint.h @@ -0,0 +1,493 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: encint.h 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ +#if !defined(_encint_H) +# define _encint_H (1) +# if defined(HAVE_CONFIG_H) +# include "config.h" +# endif +# include "theora/theoraenc.h" +# include "internal.h" +# include "ocintrin.h" +# include "mathops.h" +# include "enquant.h" +# include "huffenc.h" +/*# define OC_COLLECT_METRICS*/ + + + +typedef oc_mv oc_mv2[2]; + +typedef struct oc_enc_opt_vtable oc_enc_opt_vtable; +typedef struct oc_mb_enc_info oc_mb_enc_info; +typedef struct oc_mode_scheme_chooser oc_mode_scheme_chooser; +typedef struct oc_iir_filter oc_iir_filter; +typedef struct oc_frame_metrics oc_frame_metrics; +typedef struct oc_rc_state oc_rc_state; +typedef struct th_enc_ctx oc_enc_ctx; +typedef struct oc_token_checkpoint oc_token_checkpoint; + + + +/*Constants for the packet-out state machine specific to the encoder.*/ + +/*Next packet to emit: Data packet, but none are ready yet.*/ +#define OC_PACKET_EMPTY (0) +/*Next packet to emit: Data packet, and one is ready.*/ +#define OC_PACKET_READY (1) + +/*All features enabled.*/ +#define OC_SP_LEVEL_SLOW (0) +/*Enable early skip.*/ +#define OC_SP_LEVEL_EARLY_SKIP (1) +/*Disable motion compensation.*/ +#define OC_SP_LEVEL_NOMC (2) +/*Maximum valid speed level.*/ +#define OC_SP_LEVEL_MAX (2) + + +/*The bits used for each of the MB mode codebooks.*/ +extern const unsigned char OC_MODE_BITS[2][OC_NMODES]; + +/*The bits used for each of the MV codebooks.*/ +extern const unsigned char OC_MV_BITS[2][64]; + +/*The minimum value that can be stored in a SB run for each codeword. + The last entry is the upper bound on the length of a single SB run.*/ +extern const ogg_uint16_t OC_SB_RUN_VAL_MIN[8]; +/*The bits used for each SB run codeword.*/ +extern const unsigned char OC_SB_RUN_CODE_NBITS[7]; + +/*The bits used for each block run length (starting with 1).*/ +extern const unsigned char OC_BLOCK_RUN_CODE_NBITS[30]; + + + +/*Encoder specific functions with accelerated variants.*/ +struct oc_enc_opt_vtable{ + unsigned (*frag_sad)(const unsigned char *_src, + const unsigned char *_ref,int _ystride); + unsigned (*frag_sad_thresh)(const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _thresh); + unsigned (*frag_sad2_thresh)(const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride, + unsigned _thresh); + unsigned (*frag_satd_thresh)(const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _thresh); + unsigned (*frag_satd2_thresh)(const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride, + unsigned _thresh); + unsigned (*frag_intra_satd)(const unsigned char *_src,int _ystride); + void (*frag_sub)(ogg_int16_t _diff[64],const unsigned char *_src, + const unsigned char *_ref,int _ystride); + void (*frag_sub_128)(ogg_int16_t _diff[64], + const unsigned char *_src,int _ystride); + void (*frag_copy2)(unsigned char *_dst, + const unsigned char *_src1,const unsigned char *_src2,int _ystride); + void (*frag_recon_intra)(unsigned char *_dst,int _ystride, + const ogg_int16_t _residue[64]); + void (*frag_recon_inter)(unsigned char *_dst, + const unsigned char *_src,int _ystride,const ogg_int16_t _residue[64]); + void (*fdct8x8)(ogg_int16_t _y[64],const ogg_int16_t _x[64]); +}; + + +void oc_enc_vtable_init(oc_enc_ctx *_enc); + + + +/*Encoder-specific macroblock information.*/ +struct oc_mb_enc_info{ + /*Neighboring macro blocks that have MVs available from the current frame.*/ + unsigned cneighbors[4]; + /*Neighboring macro blocks to use for MVs from the previous frame.*/ + unsigned pneighbors[4]; + /*The number of current-frame neighbors.*/ + unsigned char ncneighbors; + /*The number of previous-frame neighbors.*/ + unsigned char npneighbors; + /*Flags indicating which MB modes have been refined.*/ + unsigned char refined; + /*Motion vectors for a macro block for the current frame and the + previous two frames. + Each is a set of 2 vectors against OC_FRAME_GOLD and OC_FRAME_PREV, which + can be used to estimate constant velocity and constant acceleration + predictors. + Uninitialized MVs are (0,0).*/ + oc_mv2 analysis_mv[3]; + /*Current unrefined analysis MVs.*/ + oc_mv unref_mv[2]; + /*Unrefined block MVs.*/ + oc_mv block_mv[4]; + /*Refined block MVs.*/ + oc_mv ref_mv[4]; + /*Minimum motion estimation error from the analysis stage.*/ + ogg_uint16_t error[2]; + /*MB error for half-pel refinement for each frame type.*/ + unsigned satd[2]; + /*Block error for half-pel refinement.*/ + unsigned block_satd[4]; +}; + + + +/*State machine to estimate the opportunity cost of coding a MB mode.*/ +struct oc_mode_scheme_chooser{ + /*Pointers to the a list containing the index of each mode in the mode + alphabet used by each scheme. + The first entry points to the dynamic scheme0_ranks, while the remaining 7 + point to the constant entries stored in OC_MODE_SCHEMES.*/ + const unsigned char *mode_ranks[8]; + /*The ranks for each mode when coded with scheme 0. + These are optimized so that the more frequent modes have lower ranks.*/ + unsigned char scheme0_ranks[OC_NMODES]; + /*The list of modes, sorted in descending order of frequency, that + corresponds to the ranks above.*/ + unsigned char scheme0_list[OC_NMODES]; + /*The number of times each mode has been chosen so far.*/ + int mode_counts[OC_NMODES]; + /*The list of mode coding schemes, sorted in ascending order of bit cost.*/ + unsigned char scheme_list[8]; + /*The number of bits used by each mode coding scheme.*/ + ptrdiff_t scheme_bits[8]; +}; + + +void oc_mode_scheme_chooser_init(oc_mode_scheme_chooser *_chooser); + + + +/*A 2nd order low-pass Bessel follower. + We use this for rate control because it has fast reaction time, but is + critically damped.*/ +struct oc_iir_filter{ + ogg_int32_t c[2]; + ogg_int64_t g; + ogg_int32_t x[2]; + ogg_int32_t y[2]; +}; + + + +/*The 2-pass metrics associated with a single frame.*/ +struct oc_frame_metrics{ + /*The log base 2 of the scale factor for this frame in Q24 format.*/ + ogg_int32_t log_scale; + /*The number of application-requested duplicates of this frame.*/ + unsigned dup_count:31; + /*The frame type from pass 1.*/ + unsigned frame_type:1; +}; + + + +/*Rate control state information.*/ +struct oc_rc_state{ + /*The target average bits per frame.*/ + ogg_int64_t bits_per_frame; + /*The current buffer fullness (bits available to be used).*/ + ogg_int64_t fullness; + /*The target buffer fullness. + This is where we'd like to be by the last keyframe the appears in the next + buf_delay frames.*/ + ogg_int64_t target; + /*The maximum buffer fullness (total size of the buffer).*/ + ogg_int64_t max; + /*The log of the number of pixels in a frame in Q57 format.*/ + ogg_int64_t log_npixels; + /*The exponent used in the rate model in Q8 format.*/ + unsigned exp[2]; + /*The number of frames to distribute the buffer usage over.*/ + int buf_delay; + /*The total drop count from the previous frame. + This includes duplicates explicitly requested via the + TH_ENCCTL_SET_DUP_COUNT API as well as frames we chose to drop ourselves.*/ + ogg_uint32_t prev_drop_count; + /*The log of an estimated scale factor used to obtain the real framerate, for + VFR sources or, e.g., 12 fps content doubled to 24 fps, etc.*/ + ogg_int64_t log_drop_scale; + /*The log of estimated scale factor for the rate model in Q57 format.*/ + ogg_int64_t log_scale[2]; + /*The log of the target quantizer level in Q57 format.*/ + ogg_int64_t log_qtarget; + /*Will we drop frames to meet bitrate target?*/ + unsigned char drop_frames; + /*Do we respect the maximum buffer fullness?*/ + unsigned char cap_overflow; + /*Can the reservoir go negative?*/ + unsigned char cap_underflow; + /*Second-order lowpass filters to track scale and VFR.*/ + oc_iir_filter scalefilter[2]; + int inter_count; + int inter_delay; + int inter_delay_target; + oc_iir_filter vfrfilter; + /*Two-pass mode state. + 0 => 1-pass encoding. + 1 => 1st pass of 2-pass encoding. + 2 => 2nd pass of 2-pass encoding.*/ + int twopass; + /*Buffer for current frame metrics.*/ + unsigned char twopass_buffer[48]; + /*The number of bytes in the frame metrics buffer. + When 2-pass encoding is enabled, this is set to 0 after each frame is + submitted, and must be non-zero before the next frame will be accepted.*/ + int twopass_buffer_bytes; + int twopass_buffer_fill; + /*Whether or not to force the next frame to be a keyframe.*/ + unsigned char twopass_force_kf; + /*The metrics for the previous frame.*/ + oc_frame_metrics prev_metrics; + /*The metrics for the current frame.*/ + oc_frame_metrics cur_metrics; + /*The buffered metrics for future frames.*/ + oc_frame_metrics *frame_metrics; + int nframe_metrics; + int cframe_metrics; + /*The index of the current frame in the circular metric buffer.*/ + int frame_metrics_head; + /*The frame count of each type (keyframes, delta frames, and dup frames); + 32 bits limits us to 2.268 years at 60 fps.*/ + ogg_uint32_t frames_total[3]; + /*The number of frames of each type yet to be processed.*/ + ogg_uint32_t frames_left[3]; + /*The sum of the scale values for each frame type.*/ + ogg_int64_t scale_sum[2]; + /*The start of the window over which the current scale sums are taken.*/ + int scale_window0; + /*The end of the window over which the current scale sums are taken.*/ + int scale_window_end; + /*The frame count of each type in the current 2-pass window; this does not + include dup frames.*/ + int nframes[3]; + /*The total accumulated estimation bias.*/ + ogg_int64_t rate_bias; +}; + + +void oc_rc_state_init(oc_rc_state *_rc,oc_enc_ctx *_enc); +void oc_rc_state_clear(oc_rc_state *_rc); + +void oc_enc_rc_resize(oc_enc_ctx *_enc); +int oc_enc_select_qi(oc_enc_ctx *_enc,int _qti,int _clamp); +void oc_enc_calc_lambda(oc_enc_ctx *_enc,int _frame_type); +int oc_enc_update_rc_state(oc_enc_ctx *_enc, + long _bits,int _qti,int _qi,int _trial,int _droppable); +int oc_enc_rc_2pass_out(oc_enc_ctx *_enc,unsigned char **_buf); +int oc_enc_rc_2pass_in(oc_enc_ctx *_enc,unsigned char *_buf,size_t _bytes); + + + +/*The internal encoder state.*/ +struct th_enc_ctx{ + /*Shared encoder/decoder state.*/ + oc_theora_state state; + /*Buffer in which to assemble packets.*/ + oggpack_buffer opb; + /*Encoder-specific macroblock information.*/ + oc_mb_enc_info *mb_info; + /*DC coefficients after prediction.*/ + ogg_int16_t *frag_dc; + /*The list of coded macro blocks, in coded order.*/ + unsigned *coded_mbis; + /*The number of coded macro blocks.*/ + size_t ncoded_mbis; + /*Whether or not packets are ready to be emitted. + This takes on negative values while there are remaining header packets to + be emitted, reaches 0 when the codec is ready for input, and becomes + positive when a frame has been processed and data packets are ready.*/ + int packet_state; + /*The maximum distance between keyframes.*/ + ogg_uint32_t keyframe_frequency_force; + /*The number of duplicates to produce for the next frame.*/ + ogg_uint32_t dup_count; + /*The number of duplicates remaining to be emitted for the current frame.*/ + ogg_uint32_t nqueued_dups; + /*The number of duplicates emitted for the last frame.*/ + ogg_uint32_t prev_dup_count; + /*The current speed level.*/ + int sp_level; + /*Whether or not VP3 compatibility mode has been enabled.*/ + unsigned char vp3_compatible; + /*Whether or not any INTER frames have been coded.*/ + unsigned char coded_inter_frame; + /*Whether or not previous frame was dropped.*/ + unsigned char prevframe_dropped; + /*Stores most recently chosen Huffman tables for each frame type, DC and AC + coefficients, and luma and chroma tokens. + The actual Huffman table used for a given coefficient depends not only on + the choice made here, but also its index in the zig-zag ordering.*/ + unsigned char huff_idxs[2][2][2]; + /*Current count of bits used by each MV coding mode.*/ + size_t mv_bits[2]; + /*The mode scheme chooser for estimating mode coding costs.*/ + oc_mode_scheme_chooser chooser; + /*The number of vertical super blocks in an MCU.*/ + int mcu_nvsbs; + /*The SSD error for skipping each fragment in the current MCU.*/ + unsigned *mcu_skip_ssd; + /*The DCT token lists for each coefficient and each plane.*/ + unsigned char **dct_tokens[3]; + /*The extra bits associated with each DCT token.*/ + ogg_uint16_t **extra_bits[3]; + /*The number of DCT tokens for each coefficient for each plane.*/ + ptrdiff_t ndct_tokens[3][64]; + /*Pending EOB runs for each coefficient for each plane.*/ + ogg_uint16_t eob_run[3][64]; + /*The offset of the first DCT token for each coefficient for each plane.*/ + unsigned char dct_token_offs[3][64]; + /*The last DC coefficient for each plane and reference frame.*/ + int dc_pred_last[3][3]; +#if defined(OC_COLLECT_METRICS) + /*Fragment SATD statistics for MB mode estimation metrics.*/ + unsigned *frag_satd; + /*Fragment SSD statistics for MB mode estimation metrics.*/ + unsigned *frag_ssd; +#endif + /*The R-D optimization parameter.*/ + int lambda; + /*The huffman tables in use.*/ + th_huff_code huff_codes[TH_NHUFFMAN_TABLES][TH_NDCT_TOKENS]; + /*The quantization parameters in use.*/ + th_quant_info qinfo; + oc_iquant *enquant_tables[64][3][2]; + oc_iquant_table enquant_table_data[64][3][2]; + /*An "average" quantizer for each quantizer type (INTRA or INTER) and qi + value. + This is used to paramterize the rate control decisions. + They are kept in the log domain to simplify later processing. + Keep in mind these are DCT domain quantizers, and so are scaled by an + additional factor of 4 from the pixel domain.*/ + ogg_int64_t log_qavg[2][64]; + /*The buffer state used to drive rate control.*/ + oc_rc_state rc; + /*Table for encoder acceleration functions.*/ + oc_enc_opt_vtable opt_vtable; +}; + + +void oc_enc_analyze_intra(oc_enc_ctx *_enc,int _recode); +int oc_enc_analyze_inter(oc_enc_ctx *_enc,int _allow_keyframe,int _recode); +#if defined(OC_COLLECT_METRICS) +void oc_enc_mode_metrics_collect(oc_enc_ctx *_enc); +void oc_enc_mode_metrics_dump(oc_enc_ctx *_enc); +#endif + + + +/*Perform fullpel motion search for a single MB against both reference frames.*/ +void oc_mcenc_search(oc_enc_ctx *_enc,int _mbi); +/*Refine a MB MV for one frame.*/ +void oc_mcenc_refine1mv(oc_enc_ctx *_enc,int _mbi,int _frame); +/*Refine the block MVs.*/ +void oc_mcenc_refine4mv(oc_enc_ctx *_enc,int _mbi); + + + +/*Used to rollback a tokenlog transaction when we retroactively decide to skip + a fragment. + A checkpoint is taken right before each token is added.*/ +struct oc_token_checkpoint{ + /*The color plane the token was added to.*/ + unsigned char pli; + /*The zig-zag index the token was added to.*/ + unsigned char zzi; + /*The outstanding EOB run count before the token was added.*/ + ogg_uint16_t eob_run; + /*The token count before the token was added.*/ + ptrdiff_t ndct_tokens; +}; + + + +void oc_enc_tokenize_start(oc_enc_ctx *_enc); +int oc_enc_tokenize_ac(oc_enc_ctx *_enc,int _pli,ptrdiff_t _fragi, + ogg_int16_t *_qdct,const ogg_uint16_t *_dequant,const ogg_int16_t *_dct, + int _zzi,oc_token_checkpoint **_stack,int _acmin); +void oc_enc_tokenlog_rollback(oc_enc_ctx *_enc, + const oc_token_checkpoint *_stack,int _n); +void oc_enc_pred_dc_frag_rows(oc_enc_ctx *_enc, + int _pli,int _fragy0,int _frag_yend); +void oc_enc_tokenize_dc_frag_list(oc_enc_ctx *_enc,int _pli, + const ptrdiff_t *_coded_fragis,ptrdiff_t _ncoded_fragis, + int _prev_ndct_tokens1,int _prev_eob_run1); +void oc_enc_tokenize_finish(oc_enc_ctx *_enc); + + + +/*Utility routine to encode one of the header packets.*/ +int oc_state_flushheader(oc_theora_state *_state,int *_packet_state, + oggpack_buffer *_opb,const th_quant_info *_qinfo, + const th_huff_code _codes[TH_NHUFFMAN_TABLES][TH_NDCT_TOKENS], + const char *_vendor,th_comment *_tc,ogg_packet *_op); + + + +/*Encoder-specific accelerated functions.*/ +void oc_enc_frag_sub(const oc_enc_ctx *_enc,ogg_int16_t _diff[64], + const unsigned char *_src,const unsigned char *_ref,int _ystride); +void oc_enc_frag_sub_128(const oc_enc_ctx *_enc,ogg_int16_t _diff[64], + const unsigned char *_src,int _ystride); +unsigned oc_enc_frag_sad(const oc_enc_ctx *_enc,const unsigned char *_src, + const unsigned char *_ref,int _ystride); +unsigned oc_enc_frag_sad_thresh(const oc_enc_ctx *_enc, + const unsigned char *_src,const unsigned char *_ref,int _ystride, + unsigned _thresh); +unsigned oc_enc_frag_sad2_thresh(const oc_enc_ctx *_enc, + const unsigned char *_src,const unsigned char *_ref1, + const unsigned char *_ref2,int _ystride,unsigned _thresh); +unsigned oc_enc_frag_satd_thresh(const oc_enc_ctx *_enc, + const unsigned char *_src,const unsigned char *_ref,int _ystride, + unsigned _thresh); +unsigned oc_enc_frag_satd2_thresh(const oc_enc_ctx *_enc, + const unsigned char *_src,const unsigned char *_ref1, + const unsigned char *_ref2,int _ystride,unsigned _thresh); +unsigned oc_enc_frag_intra_satd(const oc_enc_ctx *_enc, + const unsigned char *_src,int _ystride); +void oc_enc_frag_copy2(const oc_enc_ctx *_enc,unsigned char *_dst, + const unsigned char *_src1,const unsigned char *_src2,int _ystride); +void oc_enc_frag_recon_intra(const oc_enc_ctx *_enc, + unsigned char *_dst,int _ystride,const ogg_int16_t _residue[64]); +void oc_enc_frag_recon_inter(const oc_enc_ctx *_enc,unsigned char *_dst, + const unsigned char *_src,int _ystride,const ogg_int16_t _residue[64]); +void oc_enc_fdct8x8(const oc_enc_ctx *_enc,ogg_int16_t _y[64], + const ogg_int16_t _x[64]); + +/*Default pure-C implementations.*/ +void oc_enc_vtable_init_c(oc_enc_ctx *_enc); + +void oc_enc_frag_sub_c(ogg_int16_t _diff[64], + const unsigned char *_src,const unsigned char *_ref,int _ystride); +void oc_enc_frag_sub_128_c(ogg_int16_t _diff[64], + const unsigned char *_src,int _ystride); +void oc_enc_frag_copy2_c(unsigned char *_dst, + const unsigned char *_src1,const unsigned char *_src2,int _ystride); +unsigned oc_enc_frag_sad_c(const unsigned char *_src, + const unsigned char *_ref,int _ystride); +unsigned oc_enc_frag_sad_thresh_c(const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _thresh); +unsigned oc_enc_frag_sad2_thresh_c(const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride, + unsigned _thresh); +unsigned oc_enc_frag_satd_thresh_c(const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _thresh); +unsigned oc_enc_frag_satd2_thresh_c(const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride, + unsigned _thresh); +unsigned oc_enc_frag_intra_satd_c(const unsigned char *_src,int _ystride); +void oc_enc_fdct8x8_c(ogg_int16_t _y[64],const ogg_int16_t _x[64]); + +#endif diff --git a/Engine/lib/libtheora/lib/encode.c b/Engine/lib/libtheora/lib/encode.c new file mode 100644 index 000000000..0c5ea6a17 --- /dev/null +++ b/Engine/lib/libtheora/lib/encode.c @@ -0,0 +1,1615 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: encode.c 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ +#include +#include +#include "encint.h" +#if defined(OC_X86_ASM) +# include "x86/x86enc.h" +#endif + + + +/*The default quantization parameters used by VP3.1.*/ +static const int OC_VP31_RANGE_SIZES[1]={63}; +static const th_quant_base OC_VP31_BASES_INTRA_Y[2]={ + { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 58, 68,109,103, 77, + 24, 35, 55, 64, 81,104,113, 92, + 49, 64, 78, 87,103,121,120,101, + 72, 92, 95, 98,112,100,103, 99 + }, + { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 58, 68,109,103, 77, + 24, 35, 55, 64, 81,104,113, 92, + 49, 64, 78, 87,103,121,120,101, + 72, 92, 95, 98,112,100,103, 99 + } +}; +static const th_quant_base OC_VP31_BASES_INTRA_C[2]={ + { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + }, + { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + } +}; +static const th_quant_base OC_VP31_BASES_INTER[2]={ + { + 16, 16, 16, 20, 24, 28, 32, 40, + 16, 16, 20, 24, 28, 32, 40, 48, + 16, 20, 24, 28, 32, 40, 48, 64, + 20, 24, 28, 32, 40, 48, 64, 64, + 24, 28, 32, 40, 48, 64, 64, 64, + 28, 32, 40, 48, 64, 64, 64, 96, + 32, 40, 48, 64, 64, 64, 96,128, + 40, 48, 64, 64, 64, 96,128,128 + }, + { + 16, 16, 16, 20, 24, 28, 32, 40, + 16, 16, 20, 24, 28, 32, 40, 48, + 16, 20, 24, 28, 32, 40, 48, 64, + 20, 24, 28, 32, 40, 48, 64, 64, + 24, 28, 32, 40, 48, 64, 64, 64, + 28, 32, 40, 48, 64, 64, 64, 96, + 32, 40, 48, 64, 64, 64, 96,128, + 40, 48, 64, 64, 64, 96,128,128 + } +}; + +const th_quant_info TH_VP31_QUANT_INFO={ + { + 220,200,190,180,170,170,160,160, + 150,150,140,140,130,130,120,120, + 110,110,100,100, 90, 90, 90, 80, + 80, 80, 70, 70, 70, 60, 60, 60, + 60, 50, 50, 50, 50, 40, 40, 40, + 40, 40, 30, 30, 30, 30, 30, 30, + 30, 20, 20, 20, 20, 20, 20, 20, + 20, 10, 10, 10, 10, 10, 10, 10 + }, + { + 500,450,400,370,340,310,285,265, + 245,225,210,195,185,180,170,160, + 150,145,135,130,125,115,110,107, + 100, 96, 93, 89, 85, 82, 75, 74, + 70, 68, 64, 60, 57, 56, 52, 50, + 49, 45, 44, 43, 40, 38, 37, 35, + 33, 32, 30, 29, 28, 25, 24, 22, + 21, 19, 18, 17, 15, 13, 12, 10 + }, + { + 30,25,20,20,15,15,14,14, + 13,13,12,12,11,11,10,10, + 9, 9, 8, 8, 7, 7, 7, 7, + 6, 6, 6, 6, 5, 5, 5, 5, + 4, 4, 4, 4, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + { + {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTRA_Y}, + {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTRA_C}, + {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTRA_C} + }, + { + {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTER}, + {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTER}, + {1,OC_VP31_RANGE_SIZES,OC_VP31_BASES_INTER} + } + } +}; + +/*The current default quantization parameters.*/ +static const int OC_DEF_QRANGE_SIZES[3]={32,16,15}; +static const th_quant_base OC_DEF_BASES_INTRA_Y[4]={ + { + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + 15, 15, 15, 15, 15, 15, 15, 15, + }, + { + 15, 12, 12, 15, 18, 20, 20, 21, + 13, 13, 14, 17, 18, 21, 21, 20, + 14, 14, 15, 18, 20, 21, 21, 21, + 14, 16, 17, 19, 20, 21, 21, 21, + 16, 17, 20, 21, 21, 21, 21, 21, + 18, 19, 20, 21, 21, 21, 21, 21, + 20, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21 + }, + { + 16, 12, 11, 16, 20, 25, 27, 28, + 13, 13, 14, 18, 21, 28, 28, 27, + 14, 13, 16, 20, 25, 28, 28, 28, + 14, 16, 19, 22, 27, 29, 29, 28, + 17, 19, 25, 28, 28, 30, 30, 29, + 20, 24, 27, 28, 29, 30, 30, 29, + 27, 28, 29, 29, 30, 30, 30, 30, + 29, 29, 29, 29, 30, 30, 30, 29 + }, + { + 16, 11, 10, 16, 24, 40, 51, 61, + 12, 12, 14, 19, 26, 58, 60, 55, + 14, 13, 16, 24, 40, 57, 69, 56, + 14, 17, 22, 29, 51, 87, 80, 62, + 18, 22, 37, 58, 68,109,103, 77, + 24, 35, 55, 64, 81,104,113, 92, + 49, 64, 78, 87,103,121,120,101, + 72, 92, 95, 98,112,100,103, 99 + } +}; +static const th_quant_base OC_DEF_BASES_INTRA_C[4]={ + { + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19, + 19, 19, 19, 19, 19, 19, 19, 19 + }, + { + 18, 18, 21, 25, 26, 26, 26, 26, + 18, 20, 22, 26, 26, 26, 26, 26, + 21, 22, 25, 26, 26, 26, 26, 26, + 25, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26, + 26, 26, 26, 26, 26, 26, 26, 26 + }, + { + 17, 18, 22, 31, 36, 36, 36, 36, + 18, 20, 24, 34, 36, 36, 36, 36, + 22, 24, 33, 36, 36, 36, 36, 36, + 31, 34, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36, + 36, 36, 36, 36, 36, 36, 36, 36 + }, + { + 17, 18, 24, 47, 99, 99, 99, 99, + 18, 21, 26, 66, 99, 99, 99, 99, + 24, 26, 56, 99, 99, 99, 99, 99, + 47, 66, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99, + 99, 99, 99, 99, 99, 99, 99, 99 + } +}; +static const th_quant_base OC_DEF_BASES_INTER[4]={ + { + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21, + 21, 21, 21, 21, 21, 21, 21, 21 + }, + { + 18, 18, 18, 21, 23, 24, 25, 27, + 18, 18, 21, 23, 24, 25, 27, 28, + 18, 21, 23, 24, 25, 27, 28, 29, + 21, 23, 24, 25, 27, 28, 29, 29, + 23, 24, 25, 27, 28, 29, 29, 29, + 24, 25, 27, 28, 29, 29, 29, 30, + 25, 27, 28, 29, 29, 29, 30, 30, + 27, 28, 29, 29, 29, 30, 30, 30 + }, + { + 17, 17, 17, 20, 23, 26, 28, 32, + 17, 17, 20, 23, 26, 28, 32, 34, + 17, 20, 23, 26, 28, 32, 34, 37, + 20, 23, 26, 28, 32, 34, 37, 37, + 23, 26, 28, 32, 34, 37, 37, 37, + 26, 28, 32, 34, 37, 37, 37, 41, + 28, 32, 34, 37, 37, 37, 41, 42, + 32, 34, 37, 37, 37, 41, 42, 42 + }, + { + 16, 16, 16, 20, 24, 28, 32, 40, + 16, 16, 20, 24, 28, 32, 40, 48, + 16, 20, 24, 28, 32, 40, 48, 64, + 20, 24, 28, 32, 40, 48, 64, 64, + 24, 28, 32, 40, 48, 64, 64, 64, + 28, 32, 40, 48, 64, 64, 64, 96, + 32, 40, 48, 64, 64, 64, 96,128, + 40, 48, 64, 64, 64, 96,128,128 + } +}; + +const th_quant_info TH_DEF_QUANT_INFO={ + { + 365,348,333,316,300,287,277,265, + 252,240,229,219,206,197,189,180, + 171,168,160,153,146,139,132,127, + 121,115,110,107,101, 97, 94, 89, + 85, 83, 78, 73, 72, 67, 66, 62, + 60, 59, 56, 53, 52, 48, 47, 43, + 42, 40, 36, 35, 34, 33, 31, 30, + 28, 25, 24, 22, 20, 17, 14, 10 + }, + { + 365,348,333,316,300,287,277,265, + 252,240,229,219,206,197,189,180, + 171,168,160,153,146,139,132,127, + 121,115,110,107,101, 97, 94, 89, + 85, 83, 78, 73, 72, 67, 66, 62, + 60, 59, 56, 53, 52, 48, 47, 43, + 42, 40, 36, 35, 34, 33, 31, 30, + 28, 25, 24, 22, 20, 17, 14, 10 + }, + { + 30,25,20,20,15,15,14,14, + 13,13,12,12,11,11,10,10, + 9, 9, 8, 8, 7, 7, 7, 7, + 6, 6, 6, 6, 5, 5, 5, 5, + 4, 4, 4, 4, 3, 3, 3, 3, + 2, 2, 2, 2, 2, 2, 2, 2, + 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0 + }, + { + { + {3,OC_DEF_QRANGE_SIZES,OC_DEF_BASES_INTRA_Y}, + {3,OC_DEF_QRANGE_SIZES,OC_DEF_BASES_INTRA_C}, + {3,OC_DEF_QRANGE_SIZES,OC_DEF_BASES_INTRA_C} + }, + { + {3,OC_DEF_QRANGE_SIZES,OC_DEF_BASES_INTER}, + {3,OC_DEF_QRANGE_SIZES,OC_DEF_BASES_INTER}, + {3,OC_DEF_QRANGE_SIZES,OC_DEF_BASES_INTER} + } + } +}; + + + +/*The Huffman codes used for macro block modes.*/ + +const unsigned char OC_MODE_BITS[2][OC_NMODES]={ + /*Codebook 0: a maximally skewed prefix code.*/ + {1,2,3,4,5,6,7,7}, + /*Codebook 1: a fixed-length code.*/ + {3,3,3,3,3,3,3,3} +}; + +static const unsigned char OC_MODE_CODES[2][OC_NMODES]={ + /*Codebook 0: a maximally skewed prefix code.*/ + {0x00,0x02,0x06,0x0E,0x1E,0x3E,0x7E,0x7F}, + /*Codebook 1: a fixed-length code.*/ + {0x00,0x01,0x02,0x03,0x04,0x05,0x06,0x07} +}; + + +/*The Huffman codes used for motion vectors.*/ + +const unsigned char OC_MV_BITS[2][64]={ + /*Codebook 0: VLC code.*/ + { + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8, + 8,7,7,7,7,7,7,7,7,6,6,6,6,4,4,3, + 3, + 3,4,4,6,6,6,6,7,7,7,7,7,7,7,7,8, + 8,8,8,8,8,8,8,8,8,8,8,8,8,8,8 + }, + /*Codebook 1: (5 bit magnitude, 1 bit sign). + This wastes a code word (0x01, negative zero), or a bit (0x00, positive + zero, requires only 5 bits to uniquely decode), but is hopefully not used + very often.*/ + { + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6,6, + 6,6,6,6,6,6,6,6,6,6,6,6,6,6,6 + } +}; + +static const unsigned char OC_MV_CODES[2][64]={ + /*Codebook 0: VLC code.*/ + { + 0xFF,0xFD,0xFB,0xF9,0xF7,0xF5,0xF3, + 0xF1,0xEF,0xED,0xEB,0xE9,0xE7,0xE5,0xE3, + 0xE1,0x6F,0x6D,0x6B,0x69,0x67,0x65,0x63, + 0x61,0x2F,0x2D,0x2B,0x29,0x09,0x07,0x02, + 0x00, + 0x01,0x06,0x08,0x28,0x2A,0x2C,0x2E,0x60, + 0x62,0x64,0x66,0x68,0x6A,0x6C,0x6E,0xE0, + 0xE2,0xE4,0xE6,0xE8,0xEA,0xEC,0xEE,0xF0, + 0xF2,0xF4,0xF6,0xF8,0xFA,0xFC,0xFE + }, + /*Codebook 1: (5 bit magnitude, 1 bit sign).*/ + { + 0x3F,0x3D,0x3B,0x39,0x37,0x35,0x33, + 0x31,0x2F,0x2D,0x2B,0x29,0x27,0x25,0x23, + 0x21,0x1F,0x1D,0x1B,0x19,0x17,0x15,0x13, + 0x11,0x0F,0x0D,0x0B,0x09,0x07,0x05,0x03, + 0x00, + 0x02,0x04,0x06,0x08,0x0A,0x0C,0x0E,0x10, + 0x12,0x14,0x16,0x18,0x1A,0x1C,0x1E,0x20, + 0x22,0x24,0x26,0x28,0x2A,0x2C,0x2E,0x30, + 0x32,0x34,0x36,0x38,0x3A,0x3C,0x3E + } +}; + + + +/*Super block run coding scheme: + Codeword Run Length + 0 1 + 10x 2-3 + 110x 4-5 + 1110xx 6-9 + 11110xxx 10-17 + 111110xxxx 18-33 + 111111xxxxxxxxxxxx 34-4129*/ +const ogg_uint16_t OC_SB_RUN_VAL_MIN[8]={1,2,4,6,10,18,34,4130}; +static const unsigned OC_SB_RUN_CODE_PREFIX[7]={ + 0,4,0xC,0x38,0xF0,0x3E0,0x3F000 +}; +const unsigned char OC_SB_RUN_CODE_NBITS[7]={1,3,4,6,8,10,18}; + + +/*Writes the bit pattern for the run length of a super block run to the given + oggpack_buffer. + _opb: The buffer to write to. + _run_count: The length of the run, which must be positive. + _flag: The current flag. + _done: Whether or not more flags are to be encoded.*/ +static void oc_sb_run_pack(oggpack_buffer *_opb,ptrdiff_t _run_count, + int _flag,int _done){ + int i; + if(_run_count>=4129){ + do{ + oggpackB_write(_opb,0x3FFFF,18); + _run_count-=4129; + if(_run_count>0)oggpackB_write(_opb,_flag,1); + else if(!_done)oggpackB_write(_opb,!_flag,1); + } + while(_run_count>=4129); + if(_run_count<=0)return; + } + for(i=0;_run_count>=OC_SB_RUN_VAL_MIN[i+1];i++); + oggpackB_write(_opb,OC_SB_RUN_CODE_PREFIX[i]+_run_count-OC_SB_RUN_VAL_MIN[i], + OC_SB_RUN_CODE_NBITS[i]); +} + + + +/*Block run coding scheme: + Codeword Run Length + 0x 1-2 + 10x 3-4 + 110x 5-6 + 1110xx 7-10 + 11110xx 11-14 + 11111xxxx 15-30*/ +const unsigned char OC_BLOCK_RUN_CODE_NBITS[30]={ + 2,2,3,3,4,4,6,6,6,6,7,7,7,7,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9 +}; +static const ogg_uint16_t OC_BLOCK_RUN_CODE_PATTERN[30]={ + 0x000,0x001,0x004,0x005,0x00C,0x00D,0x038, + 0x039,0x03A,0x03B,0x078,0x079,0x07A,0x07B,0x1F0, + 0x1F1,0x1F2,0x1F3,0x1F4,0x1F5,0x1F6,0x1F7,0x1F8, + 0x1F9,0x1FA,0x1FB,0x1FC,0x1FD,0x1FE,0x1FF +}; + + +/*Writes the bit pattern for the run length of a block run to the given + oggpack_buffer. + _opb: The buffer to write to. + _run_count: The length of the run. + This must be positive, and no more than 30.*/ +static void oc_block_run_pack(oggpack_buffer *_opb,int _run_count){ + oggpackB_write(_opb,OC_BLOCK_RUN_CODE_PATTERN[_run_count-1], + OC_BLOCK_RUN_CODE_NBITS[_run_count-1]); +} + + + +static void oc_enc_frame_header_pack(oc_enc_ctx *_enc){ + /*Mark this as a data packet.*/ + oggpackB_write(&_enc->opb,0,1); + /*Output the frame type (key frame or delta frame).*/ + oggpackB_write(&_enc->opb,_enc->state.frame_type,1); + /*Write out the current qi list.*/ + oggpackB_write(&_enc->opb,_enc->state.qis[0],6); + if(_enc->state.nqis>1){ + oggpackB_write(&_enc->opb,1,1); + oggpackB_write(&_enc->opb,_enc->state.qis[1],6); + if(_enc->state.nqis>2){ + oggpackB_write(&_enc->opb,1,1); + oggpackB_write(&_enc->opb,_enc->state.qis[2],6); + } + else oggpackB_write(&_enc->opb,0,1); + } + else oggpackB_write(&_enc->opb,0,1); + if(_enc->state.frame_type==OC_INTRA_FRAME){ + /*Key frames have 3 unused configuration bits, holdovers from the VP3 days. + Most of the other unused bits in the VP3 headers were eliminated. + Monty kept these to leave us some wiggle room for future expansion, + though a single bit in all frames would have been far more useful.*/ + oggpackB_write(&_enc->opb,0,3); + } +} + +/*Writes the bit flags for whether or not each super block is partially coded + or not. + These flags are run-length encoded, with the flag value alternating between + each run. + Return: The number partially coded SBs.*/ +static unsigned oc_enc_partial_sb_flags_pack(oc_enc_ctx *_enc){ + const oc_sb_flags *sb_flags; + unsigned nsbs; + unsigned sbi; + unsigned npartial; + int flag; + sb_flags=_enc->state.sb_flags; + nsbs=_enc->state.nsbs; + flag=sb_flags[0].coded_partially; + oggpackB_write(&_enc->opb,flag,1); + sbi=npartial=0; + do{ + unsigned run_count; + for(run_count=0;sbiopb,run_count,flag,sbi>=nsbs); + flag=!flag; + } + while(sbistate.sb_flags; + nsbs=_enc->state.nsbs; + /*Skip partially coded super blocks; their flags have already been coded.*/ + for(sbi=0;sb_flags[sbi].coded_partially;sbi++); + flag=sb_flags[sbi].coded_fully; + oggpackB_write(&_enc->opb,flag,1); + do{ + unsigned run_count; + for(run_count=0;sbiopb,run_count,flag,sbi>=nsbs); + flag=!flag; + } + while(sbistate.nsbs)oc_enc_coded_sb_flags_pack(_enc); + sb_maps=(const oc_sb_map *)_enc->state.sb_maps; + sb_flags=_enc->state.sb_flags; + nsbs=_enc->state.nsbs; + frags=_enc->state.frags; + for(sbi=0;sbiopb,flag,1); + run_count=0; + nsbs=sbi=0; + for(pli=0;pli<3;pli++){ + nsbs+=_enc->state.fplanes[pli].nsbs; + for(;sbi=0){ + if(frags[fragi].coded!=flag){ + oc_block_run_pack(&_enc->opb,run_count); + flag=!flag; + run_count=1; + } + else run_count++; + } + } + } + } + } + } + /*Flush any trailing block coded run.*/ + if(run_count>0)oc_block_run_pack(&_enc->opb,run_count); + } +} + +static void oc_enc_mb_modes_pack(oc_enc_ctx *_enc){ + const unsigned char *mode_codes; + const unsigned char *mode_bits; + const unsigned char *mode_ranks; + unsigned *coded_mbis; + size_t ncoded_mbis; + const signed char *mb_modes; + unsigned mbii; + int scheme; + int mb_mode; + scheme=_enc->chooser.scheme_list[0]; + /*Encode the best scheme.*/ + oggpackB_write(&_enc->opb,scheme,3); + /*If the chosen scheme is scheme 0, send the mode frequency ordering.*/ + if(scheme==0){ + for(mb_mode=0;mb_modeopb,_enc->chooser.scheme0_ranks[mb_mode],3); + } + } + mode_ranks=_enc->chooser.mode_ranks[scheme]; + mode_bits=OC_MODE_BITS[scheme+1>>3]; + mode_codes=OC_MODE_CODES[scheme+1>>3]; + coded_mbis=_enc->coded_mbis; + ncoded_mbis=_enc->ncoded_mbis; + mb_modes=_enc->state.mb_modes; + for(mbii=0;mbiiopb,mode_codes[rank],mode_bits[rank]); + } +} + +static void oc_enc_mv_pack(oc_enc_ctx *_enc,int _mv_scheme,int _dx,int _dy){ + oggpackB_write(&_enc->opb, + OC_MV_CODES[_mv_scheme][_dx+31],OC_MV_BITS[_mv_scheme][_dx+31]); + oggpackB_write(&_enc->opb, + OC_MV_CODES[_mv_scheme][_dy+31],OC_MV_BITS[_mv_scheme][_dy+31]); +} + +static void oc_enc_mvs_pack(oc_enc_ctx *_enc){ + const unsigned *coded_mbis; + size_t ncoded_mbis; + const oc_mb_map *mb_maps; + const signed char *mb_modes; + const oc_fragment *frags; + const oc_mv *frag_mvs; + unsigned mbii; + int mv_scheme; + /*Choose the coding scheme.*/ + mv_scheme=_enc->mv_bits[1]<_enc->mv_bits[0]; + oggpackB_write(&_enc->opb,mv_scheme,1); + /*Encode the motion vectors. + Macro blocks are iterated in Hilbert scan order, but the MVs within the + macro block are coded in raster order.*/ + coded_mbis=_enc->coded_mbis; + ncoded_mbis=_enc->ncoded_mbis; + mb_modes=_enc->state.mb_modes; + mb_maps=(const oc_mb_map *)_enc->state.mb_maps; + frags=_enc->state.frags; + frag_mvs=(const oc_mv *)_enc->state.frag_mvs; + for(mbii=0;mbiistate.nqis<=1)return; + ncoded_fragis=_enc->state.ntotal_coded_fragis; + if(ncoded_fragis<=0)return; + coded_fragis=_enc->state.coded_fragis; + frags=_enc->state.frags; + flag=!!frags[coded_fragis[0]].qii; + oggpackB_write(&_enc->opb,flag,1); + nqi0=0; + for(fragii=0;fragiiopb,run_count,flag,fragii>=ncoded_fragis); + flag=!flag; + } + if(_enc->state.nqis<3||nqi0>=ncoded_fragis)return; + for(fragii=0;!frags[coded_fragis[fragii]].qii;fragii++); + flag=frags[coded_fragis[fragii]].qii-1; + oggpackB_write(&_enc->opb,flag,1); + while(fragiiopb,run_count,flag,fragii>=ncoded_fragis); + flag=!flag; + } +} + +/*Counts the tokens of each type used for the given range of coefficient + indices in zig-zag order. + _zzi_start: The first zig-zag index to include. + _zzi_end: The first zig-zag index to not include. + _token_counts_y: Returns the token counts for the Y' plane. + _token_counts_c: Returns the token counts for the Cb and Cr planes.*/ +static void oc_enc_count_tokens(oc_enc_ctx *_enc,int _zzi_start,int _zzi_end, + ptrdiff_t _token_counts_y[32],ptrdiff_t _token_counts_c[32]){ + const unsigned char *dct_tokens; + ptrdiff_t ndct_tokens; + int pli; + int zzi; + ptrdiff_t ti; + memset(_token_counts_y,0,32*sizeof(*_token_counts_y)); + memset(_token_counts_c,0,32*sizeof(*_token_counts_c)); + for(zzi=_zzi_start;zzi<_zzi_end;zzi++){ + dct_tokens=_enc->dct_tokens[0][zzi]; + ndct_tokens=_enc->ndct_tokens[0][zzi]; + for(ti=_enc->dct_token_offs[0][zzi];tidct_tokens[pli][zzi]; + ndct_tokens=_enc->ndct_tokens[pli][zzi]; + for(ti=_enc->dct_token_offs[pli][zzi];tihuff_codes[huffi+huff_offs][token].nbits; + } + } +} + +/*Returns the Huffman index using the fewest number of bits.*/ +static int oc_select_huff_idx(size_t _bit_counts[16]){ + int best_huffi; + int huffi; + best_huffi=0; + for(huffi=1;huffi<16;huffi++)if(_bit_counts[huffi]<_bit_counts[best_huffi]){ + best_huffi=huffi; + } + return best_huffi; +} + +static void oc_enc_huff_group_pack(oc_enc_ctx *_enc, + int _zzi_start,int _zzi_end,const int _huff_idxs[2]){ + int zzi; + for(zzi=_zzi_start;zzi<_zzi_end;zzi++){ + int pli; + for(pli=0;pli<3;pli++){ + const unsigned char *dct_tokens; + const ogg_uint16_t *extra_bits; + ptrdiff_t ndct_tokens; + const th_huff_code *huff_codes; + ptrdiff_t ti; + dct_tokens=_enc->dct_tokens[pli][zzi]; + extra_bits=_enc->extra_bits[pli][zzi]; + ndct_tokens=_enc->ndct_tokens[pli][zzi]; + huff_codes=_enc->huff_codes[_huff_idxs[pli+1>>1]]; + for(ti=_enc->dct_token_offs[pli][zzi];tiopb,huff_codes[token].pattern, + huff_codes[token].nbits); + neb=OC_DCT_TOKEN_EXTRA_BITS[token]; + if(neb)oggpackB_write(&_enc->opb,extra_bits[ti],neb); + } + } + } +} + +static void oc_enc_residual_tokens_pack(oc_enc_ctx *_enc){ + static const unsigned char OC_HUFF_GROUP_MIN[6]={0,1,6,15,28,64}; + static const unsigned char *OC_HUFF_GROUP_MAX=OC_HUFF_GROUP_MIN+1; + ptrdiff_t token_counts_y[32]; + ptrdiff_t token_counts_c[32]; + size_t bits_y[16]; + size_t bits_c[16]; + int huff_idxs[2]; + int frame_type; + int hgi; + frame_type=_enc->state.frame_type; + /*Choose which Huffman tables to use for the DC token list.*/ + oc_enc_count_tokens(_enc,0,1,token_counts_y,token_counts_c); + memset(bits_y,0,sizeof(bits_y)); + memset(bits_c,0,sizeof(bits_c)); + oc_enc_count_bits(_enc,0,token_counts_y,bits_y); + oc_enc_count_bits(_enc,0,token_counts_c,bits_c); + huff_idxs[0]=oc_select_huff_idx(bits_y); + huff_idxs[1]=oc_select_huff_idx(bits_c); + /*Write the DC token list with the chosen tables.*/ + oggpackB_write(&_enc->opb,huff_idxs[0],4); + oggpackB_write(&_enc->opb,huff_idxs[1],4); + _enc->huff_idxs[frame_type][0][0]=(unsigned char)huff_idxs[0]; + _enc->huff_idxs[frame_type][0][1]=(unsigned char)huff_idxs[1]; + oc_enc_huff_group_pack(_enc,0,1,huff_idxs); + /*Choose which Huffman tables to use for the AC token lists.*/ + memset(bits_y,0,sizeof(bits_y)); + memset(bits_c,0,sizeof(bits_c)); + for(hgi=1;hgi<5;hgi++){ + oc_enc_count_tokens(_enc,OC_HUFF_GROUP_MIN[hgi],OC_HUFF_GROUP_MAX[hgi], + token_counts_y,token_counts_c); + oc_enc_count_bits(_enc,hgi,token_counts_y,bits_y); + oc_enc_count_bits(_enc,hgi,token_counts_c,bits_c); + } + huff_idxs[0]=oc_select_huff_idx(bits_y); + huff_idxs[1]=oc_select_huff_idx(bits_c); + /*Write the AC token lists using the chosen tables.*/ + oggpackB_write(&_enc->opb,huff_idxs[0],4); + oggpackB_write(&_enc->opb,huff_idxs[1],4); + _enc->huff_idxs[frame_type][1][0]=(unsigned char)huff_idxs[0]; + _enc->huff_idxs[frame_type][1][1]=(unsigned char)huff_idxs[1]; + for(hgi=1;hgi<5;hgi++){ + huff_idxs[0]+=16; + huff_idxs[1]+=16; + oc_enc_huff_group_pack(_enc, + OC_HUFF_GROUP_MIN[hgi],OC_HUFF_GROUP_MAX[hgi],huff_idxs); + } +} + +static void oc_enc_frame_pack(oc_enc_ctx *_enc){ + oggpackB_reset(&_enc->opb); + /*Only proceed if we have some coded blocks. + If there are no coded blocks, we can drop this frame simply by emitting a + 0 byte packet.*/ + if(_enc->state.ntotal_coded_fragis>0){ + oc_enc_frame_header_pack(_enc); + if(_enc->state.frame_type==OC_INTER_FRAME){ + /*Coded block flags, MB modes, and MVs are only needed for delta frames.*/ + oc_enc_coded_flags_pack(_enc); + oc_enc_mb_modes_pack(_enc); + oc_enc_mvs_pack(_enc); + } + oc_enc_block_qis_pack(_enc); + oc_enc_tokenize_finish(_enc); + oc_enc_residual_tokens_pack(_enc); + } + /*Success: Mark the packet as ready to be flushed.*/ + _enc->packet_state=OC_PACKET_READY; +#if defined(OC_COLLECT_METRICS) + oc_enc_mode_metrics_collect(_enc); +#endif +} + + +void oc_enc_vtable_init_c(oc_enc_ctx *_enc){ + /*The implementations prefixed with oc_enc_ are encoder-specific. + The rest we re-use from the decoder.*/ + _enc->opt_vtable.frag_sad=oc_enc_frag_sad_c; + _enc->opt_vtable.frag_sad_thresh=oc_enc_frag_sad_thresh_c; + _enc->opt_vtable.frag_sad2_thresh=oc_enc_frag_sad2_thresh_c; + _enc->opt_vtable.frag_satd_thresh=oc_enc_frag_satd_thresh_c; + _enc->opt_vtable.frag_satd2_thresh=oc_enc_frag_satd2_thresh_c; + _enc->opt_vtable.frag_intra_satd=oc_enc_frag_intra_satd_c; + _enc->opt_vtable.frag_sub=oc_enc_frag_sub_c; + _enc->opt_vtable.frag_sub_128=oc_enc_frag_sub_128_c; + _enc->opt_vtable.frag_copy2=oc_enc_frag_copy2_c; + _enc->opt_vtable.frag_recon_intra=oc_frag_recon_intra_c; + _enc->opt_vtable.frag_recon_inter=oc_frag_recon_inter_c; + _enc->opt_vtable.fdct8x8=oc_enc_fdct8x8_c; +} + +/*Initialize the macro block neighbor lists for MC analysis. + This assumes that the entire mb_info memory region has been initialized with + zeros.*/ +static void oc_enc_mb_info_init(oc_enc_ctx *_enc){ + oc_mb_enc_info *embs; + const signed char *mb_modes; + unsigned nhsbs; + unsigned nvsbs; + unsigned nhmbs; + unsigned nvmbs; + unsigned sby; + mb_modes=_enc->state.mb_modes; + embs=_enc->mb_info; + nhsbs=_enc->state.fplanes[0].nhsbs; + nvsbs=_enc->state.fplanes[0].nvsbs; + nhmbs=_enc->state.nhmbs; + nvmbs=_enc->state.nvmbs; + for(sby=0;sby>1); + mby=2*sby+(quadi+1>>1&1); + /*Fill in the neighbors with current motion vectors available.*/ + for(ni=0;ni=nhmbs||nmby<0||nmby>=nvmbs)continue; + nmbi=(nmby&~1)*nhmbs+((nmbx&~1)<<1)+OC_MB_MAP[nmby&1][nmbx&1]; + if(mb_modes[nmbi]==OC_MODE_INVALID)continue; + embs[mbi].cneighbors[embs[mbi].ncneighbors++]=nmbi; + } + /*Fill in the neighbors with previous motion vectors available.*/ + for(ni=0;ni<4;ni++){ + nmbx=mbx+PDX[ni]; + nmby=mby+PDY[ni]; + if(nmbx<0||nmbx>=nhmbs||nmby<0||nmby>=nvmbs)continue; + nmbi=(nmby&~1)*nhmbs+((nmbx&~1)<<1)+OC_MB_MAP[nmby&1][nmbx&1]; + if(mb_modes[nmbi]==OC_MODE_INVALID)continue; + embs[mbi].pneighbors[embs[mbi].npneighbors++]=nmbi; + } + } + } + } +} + +static int oc_enc_set_huffman_codes(oc_enc_ctx *_enc, + const th_huff_code _codes[TH_NHUFFMAN_TABLES][TH_NDCT_TOKENS]){ + int ret; + if(_enc==NULL)return TH_EFAULT; + if(_enc->packet_state>OC_PACKET_SETUP_HDR)return TH_EINVAL; + if(_codes==NULL)_codes=TH_VP31_HUFF_CODES; + /*Validate the codes.*/ + oggpackB_reset(&_enc->opb); + ret=oc_huff_codes_pack(&_enc->opb,_codes); + if(ret<0)return ret; + memcpy(_enc->huff_codes,_codes,sizeof(_enc->huff_codes)); + return 0; +} + +/*Sets the quantization parameters to use. + This may only be called before the setup header is written. + If it is called multiple times, only the last call has any effect. + _qinfo: The quantization parameters. + These are described in more detail in theoraenc.h. + This can be NULL, in which case the default quantization parameters + will be used.*/ +static int oc_enc_set_quant_params(oc_enc_ctx *_enc, + const th_quant_info *_qinfo){ + int qi; + int pli; + int qti; + if(_enc==NULL)return TH_EFAULT; + if(_enc->packet_state>OC_PACKET_SETUP_HDR)return TH_EINVAL; + if(_qinfo==NULL)_qinfo=&TH_DEF_QUANT_INFO; + /*TODO: Analyze for packing purposes instead of just doing a shallow copy.*/ + memcpy(&_enc->qinfo,_qinfo,sizeof(_enc->qinfo)); + for(qi=0;qi<64;qi++)for(pli=0;pli<3;pli++)for(qti=0;qti<2;qti++){ + _enc->state.dequant_tables[qi][pli][qti]= + _enc->state.dequant_table_data[qi][pli][qti]; + _enc->enquant_tables[qi][pli][qti]=_enc->enquant_table_data[qi][pli][qti]; + } + oc_enquant_tables_init(_enc->state.dequant_tables, + _enc->enquant_tables,_qinfo); + memcpy(_enc->state.loop_filter_limits,_qinfo->loop_filter_limits, + sizeof(_enc->state.loop_filter_limits)); + oc_enquant_qavg_init(_enc->log_qavg,_enc->state.dequant_tables, + _enc->state.info.pixel_fmt); + return 0; +} + +static void oc_enc_clear(oc_enc_ctx *_enc); + +static int oc_enc_init(oc_enc_ctx *_enc,const th_info *_info){ + th_info info; + size_t mcu_nmbs; + ptrdiff_t mcu_nfrags; + int hdec; + int vdec; + int ret; + int pli; + /*Clean up the requested settings.*/ + memcpy(&info,_info,sizeof(info)); + info.version_major=TH_VERSION_MAJOR; + info.version_minor=TH_VERSION_MINOR; + info.version_subminor=TH_VERSION_SUB; + if(info.quality>63)info.quality=63; + if(info.quality<0)info.quality=32; + if(info.target_bitrate<0)info.target_bitrate=0; + /*Initialize the shared encoder/decoder state.*/ + ret=oc_state_init(&_enc->state,&info,4); + if(ret<0)return ret; + _enc->mb_info=_ogg_calloc(_enc->state.nmbs,sizeof(*_enc->mb_info)); + _enc->frag_dc=_ogg_calloc(_enc->state.nfrags,sizeof(*_enc->frag_dc)); + _enc->coded_mbis= + (unsigned *)_ogg_malloc(_enc->state.nmbs*sizeof(*_enc->coded_mbis)); + hdec=!(_enc->state.info.pixel_fmt&1); + vdec=!(_enc->state.info.pixel_fmt&2); + /*If chroma is sub-sampled in the vertical direction, we have to encode two + super block rows of Y' for each super block row of Cb and Cr.*/ + _enc->mcu_nvsbs=1<mcu_nvsbs*_enc->state.fplanes[0].nhsbs*(size_t)4; + mcu_nfrags=4*mcu_nmbs+(8*mcu_nmbs>>hdec+vdec); + _enc->mcu_skip_ssd=(unsigned *)_ogg_malloc( + mcu_nfrags*sizeof(*_enc->mcu_skip_ssd)); + for(pli=0;pli<3;pli++){ + _enc->dct_tokens[pli]=(unsigned char **)oc_malloc_2d(64, + _enc->state.fplanes[pli].nfrags,sizeof(**_enc->dct_tokens)); + _enc->extra_bits[pli]=(ogg_uint16_t **)oc_malloc_2d(64, + _enc->state.fplanes[pli].nfrags,sizeof(**_enc->extra_bits)); + } +#if defined(OC_COLLECT_METRICS) + _enc->frag_satd=_ogg_calloc(_enc->state.nfrags,sizeof(*_enc->frag_satd)); + _enc->frag_ssd=_ogg_calloc(_enc->state.nfrags,sizeof(*_enc->frag_ssd)); +#endif +#if defined(OC_X86_ASM) + oc_enc_vtable_init_x86(_enc); +#else + oc_enc_vtable_init_c(_enc); +#endif + _enc->keyframe_frequency_force=1<<_enc->state.info.keyframe_granule_shift; + _enc->state.qis[0]=_enc->state.info.quality; + _enc->state.nqis=1; + oc_rc_state_init(&_enc->rc,_enc); + oggpackB_writeinit(&_enc->opb); + if(_enc->mb_info==NULL||_enc->frag_dc==NULL||_enc->coded_mbis==NULL|| + _enc->mcu_skip_ssd==NULL||_enc->dct_tokens[0]==NULL|| + _enc->dct_tokens[1]==NULL||_enc->dct_tokens[2]==NULL|| + _enc->extra_bits[0]==NULL||_enc->extra_bits[1]==NULL|| + _enc->extra_bits[2]==NULL +#if defined(OC_COLLECT_METRICS) + ||_enc->frag_satd==NULL||_enc->frag_ssd==NULL +#endif + ){ + oc_enc_clear(_enc); + return TH_EFAULT; + } + oc_mode_scheme_chooser_init(&_enc->chooser); + oc_enc_mb_info_init(_enc); + memset(_enc->huff_idxs,0,sizeof(_enc->huff_idxs)); + /*Reset the packet-out state machine.*/ + _enc->packet_state=OC_PACKET_INFO_HDR; + _enc->dup_count=0; + _enc->nqueued_dups=0; + _enc->prev_dup_count=0; + /*Enable speed optimizations up through early skip by default.*/ + _enc->sp_level=OC_SP_LEVEL_EARLY_SKIP; + /*Disable VP3 compatibility by default.*/ + _enc->vp3_compatible=0; + /*No INTER frames coded yet.*/ + _enc->coded_inter_frame=0; + memcpy(_enc->huff_codes,TH_VP31_HUFF_CODES,sizeof(_enc->huff_codes)); + oc_enc_set_quant_params(_enc,NULL); + return 0; +} + +static void oc_enc_clear(oc_enc_ctx *_enc){ + int pli; + oc_rc_state_clear(&_enc->rc); +#if defined(OC_COLLECT_METRICS) + oc_enc_mode_metrics_dump(_enc); +#endif + oggpackB_writeclear(&_enc->opb); +#if defined(OC_COLLECT_METRICS) + _ogg_free(_enc->frag_ssd); + _ogg_free(_enc->frag_satd); +#endif + for(pli=3;pli-->0;){ + oc_free_2d(_enc->extra_bits[pli]); + oc_free_2d(_enc->dct_tokens[pli]); + } + _ogg_free(_enc->mcu_skip_ssd); + _ogg_free(_enc->coded_mbis); + _ogg_free(_enc->frag_dc); + _ogg_free(_enc->mb_info); + oc_state_clear(&_enc->state); +} + +static void oc_enc_drop_frame(th_enc_ctx *_enc){ + /*Use the previous frame's reconstruction.*/ + _enc->state.ref_frame_idx[OC_FRAME_SELF]= + _enc->state.ref_frame_idx[OC_FRAME_PREV]; + /*Flag motion vector analysis about the frame drop.*/ + _enc->prevframe_dropped=1; + /*Zero the packet.*/ + oggpackB_reset(&_enc->opb); +} + +static void oc_enc_compress_keyframe(oc_enc_ctx *_enc,int _recode){ + if(_enc->state.info.target_bitrate>0){ + _enc->state.qis[0]=oc_enc_select_qi(_enc,OC_INTRA_FRAME, + _enc->state.curframe_num>0); + _enc->state.nqis=1; + } + oc_enc_calc_lambda(_enc,OC_INTRA_FRAME); + oc_enc_analyze_intra(_enc,_recode); + oc_enc_frame_pack(_enc); + /*On the first frame, the previous call was an initial dry-run to prime + feed-forward statistics.*/ + if(!_recode&&_enc->state.curframe_num==0){ + if(_enc->state.info.target_bitrate>0){ + oc_enc_update_rc_state(_enc,oggpackB_bytes(&_enc->opb)<<3, + OC_INTRA_FRAME,_enc->state.qis[0],1,0); + } + oc_enc_compress_keyframe(_enc,1); + } +} + +static void oc_enc_compress_frame(oc_enc_ctx *_enc,int _recode){ + if(_enc->state.info.target_bitrate>0){ + _enc->state.qis[0]=oc_enc_select_qi(_enc,OC_INTER_FRAME,1); + _enc->state.nqis=1; + } + oc_enc_calc_lambda(_enc,OC_INTER_FRAME); + if(oc_enc_analyze_inter(_enc,_enc->rc.twopass!=2,_recode)){ + /*Mode analysis thinks this should have been a keyframe; start over.*/ + oc_enc_compress_keyframe(_enc,1); + } + else{ + oc_enc_frame_pack(_enc); + if(!_enc->coded_inter_frame){ + /*On the first INTER frame, the previous call was an initial dry-run to + prime feed-forward statistics.*/ + _enc->coded_inter_frame=1; + if(_enc->state.info.target_bitrate>0){ + /*Rate control also needs to prime.*/ + oc_enc_update_rc_state(_enc,oggpackB_bytes(&_enc->opb)<<3, + OC_INTER_FRAME,_enc->state.qis[0],1,0); + } + oc_enc_compress_frame(_enc,1); + } + } +} + +/*Set the granule position for the next packet to output based on the current + internal state.*/ +static void oc_enc_set_granpos(oc_enc_ctx *_enc){ + unsigned dup_offs; + /*Add an offset for the number of duplicate frames we've emitted so far.*/ + dup_offs=_enc->prev_dup_count-_enc->nqueued_dups; + /*If the current frame was a keyframe, use it for the high part.*/ + if(_enc->state.frame_type==OC_INTRA_FRAME){ + _enc->state.granpos=(_enc->state.curframe_num+_enc->state.granpos_bias<< + _enc->state.info.keyframe_granule_shift)+dup_offs; + } + /*Otherwise use the last keyframe in the high part and put the current frame + in the low part.*/ + else{ + _enc->state.granpos= + (_enc->state.keyframe_num+_enc->state.granpos_bias<< + _enc->state.info.keyframe_granule_shift) + +_enc->state.curframe_num-_enc->state.keyframe_num+dup_offs; + } +} + + +th_enc_ctx *th_encode_alloc(const th_info *_info){ + oc_enc_ctx *enc; + if(_info==NULL)return NULL; + enc=_ogg_malloc(sizeof(*enc)); + if(enc==NULL||oc_enc_init(enc,_info)<0){ + _ogg_free(enc); + return NULL; + } + return enc; +} + +void th_encode_free(th_enc_ctx *_enc){ + if(_enc!=NULL){ + oc_enc_clear(_enc); + _ogg_free(_enc); + } +} + +int th_encode_ctl(th_enc_ctx *_enc,int _req,void *_buf,size_t _buf_sz){ + switch(_req){ + case TH_ENCCTL_SET_HUFFMAN_CODES:{ + if(_buf==NULL&&_buf_sz!=0|| + _buf!=NULL&&_buf_sz!=sizeof(th_huff_table)*TH_NHUFFMAN_TABLES){ + return TH_EINVAL; + } + return oc_enc_set_huffman_codes(_enc,(const th_huff_table *)_buf); + }break; + case TH_ENCCTL_SET_QUANT_PARAMS:{ + if(_buf==NULL&&_buf_sz!=0|| + _buf!=NULL&&_buf_sz!=sizeof(th_quant_info)){ + return TH_EINVAL; + } + return oc_enc_set_quant_params(_enc,(th_quant_info *)_buf); + }break; + case TH_ENCCTL_SET_KEYFRAME_FREQUENCY_FORCE:{ + ogg_uint32_t keyframe_frequency_force; + if(_enc==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(keyframe_frequency_force))return TH_EINVAL; + keyframe_frequency_force=*(ogg_uint32_t *)_buf; + if(keyframe_frequency_force<=0)keyframe_frequency_force=1; + if(_enc->packet_state==OC_PACKET_INFO_HDR){ + /*It's still early enough to enlarge keyframe_granule_shift.*/ + _enc->state.info.keyframe_granule_shift=OC_CLAMPI( + _enc->state.info.keyframe_granule_shift, + OC_ILOG_32(keyframe_frequency_force-1),31); + } + _enc->keyframe_frequency_force=OC_MINI(keyframe_frequency_force, + (ogg_uint32_t)1U<<_enc->state.info.keyframe_granule_shift); + *(ogg_uint32_t *)_buf=_enc->keyframe_frequency_force; + return 0; + }break; + case TH_ENCCTL_SET_VP3_COMPATIBLE:{ + int vp3_compatible; + if(_enc==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(vp3_compatible))return TH_EINVAL; + vp3_compatible=*(int *)_buf; + _enc->vp3_compatible=vp3_compatible; + if(oc_enc_set_huffman_codes(_enc,TH_VP31_HUFF_CODES)<0)vp3_compatible=0; + if(oc_enc_set_quant_params(_enc,&TH_VP31_QUANT_INFO)<0)vp3_compatible=0; + if(_enc->state.info.pixel_fmt!=TH_PF_420|| + _enc->state.info.pic_width<_enc->state.info.frame_width|| + _enc->state.info.pic_height<_enc->state.info.frame_height|| + /*If we have more than 4095 super blocks, VP3's RLE coding might + overflow. + We could overcome this by ensuring we flip the coded/not-coded flags on + at least one super block in the frame, but we pick the simple solution + of just telling the user the stream will be incompatible instead. + It's unlikely the old VP3 codec would be able to decode streams at this + resolution in real time in the first place.*/ + _enc->state.nsbs>4095){ + vp3_compatible=0; + } + *(int *)_buf=vp3_compatible; + return 0; + }break; + case TH_ENCCTL_GET_SPLEVEL_MAX:{ + if(_enc==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(int))return TH_EINVAL; + *(int *)_buf=OC_SP_LEVEL_MAX; + return 0; + }break; + case TH_ENCCTL_SET_SPLEVEL:{ + int speed; + if(_enc==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(speed))return TH_EINVAL; + speed=*(int *)_buf; + if(speed<0||speed>OC_SP_LEVEL_MAX)return TH_EINVAL; + _enc->sp_level=speed; + return 0; + }break; + case TH_ENCCTL_GET_SPLEVEL:{ + if(_enc==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(int))return TH_EINVAL; + *(int *)_buf=_enc->sp_level; + return 0; + } + case TH_ENCCTL_SET_DUP_COUNT:{ + int dup_count; + if(_enc==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(dup_count))return TH_EINVAL; + dup_count=*(int *)_buf; + if(dup_count>=_enc->keyframe_frequency_force)return TH_EINVAL; + _enc->dup_count=OC_MAXI(dup_count,0); + return 0; + }break; + case TH_ENCCTL_SET_QUALITY:{ + int qi; + if(_enc==NULL||_buf==NULL)return TH_EFAULT; + if(_enc->state.info.target_bitrate>0)return TH_EINVAL; + qi=*(int *)_buf; + if(qi<0||qi>63)return TH_EINVAL; + _enc->state.info.quality=qi; + _enc->state.qis[0]=(unsigned char)qi; + _enc->state.nqis=1; + return 0; + }break; + case TH_ENCCTL_SET_BITRATE:{ + long bitrate; + int reset; + if(_enc==NULL||_buf==NULL)return TH_EFAULT; + bitrate=*(long *)_buf; + if(bitrate<=0)return TH_EINVAL; + reset=_enc->state.info.target_bitrate<=0; + _enc->state.info.target_bitrate=bitrate>INT_MAX?INT_MAX:bitrate; + if(reset)oc_rc_state_init(&_enc->rc,_enc); + else oc_enc_rc_resize(_enc); + return 0; + }break; + case TH_ENCCTL_SET_RATE_FLAGS:{ + int set; + if(_enc==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(set))return TH_EINVAL; + if(_enc->state.info.target_bitrate<=0)return TH_EINVAL; + set=*(int *)_buf; + _enc->rc.drop_frames=set&TH_RATECTL_DROP_FRAMES; + _enc->rc.cap_overflow=set&TH_RATECTL_CAP_OVERFLOW; + _enc->rc.cap_underflow=set&TH_RATECTL_CAP_UNDERFLOW; + return 0; + }break; + case TH_ENCCTL_SET_RATE_BUFFER:{ + int set; + if(_enc==NULL||_buf==NULL)return TH_EFAULT; + if(_buf_sz!=sizeof(set))return TH_EINVAL; + if(_enc->state.info.target_bitrate<=0)return TH_EINVAL; + set=*(int *)_buf; + _enc->rc.buf_delay=set; + oc_enc_rc_resize(_enc); + *(int *)_buf=_enc->rc.buf_delay; + return 0; + }break; + case TH_ENCCTL_2PASS_OUT:{ + if(_enc==NULL||_buf==NULL)return TH_EFAULT; + if(_enc->state.info.target_bitrate<=0|| + _enc->state.curframe_num>=0&&_enc->rc.twopass!=1|| + _buf_sz!=sizeof(unsigned char *)){ + return TH_EINVAL; + } + return oc_enc_rc_2pass_out(_enc,(unsigned char **)_buf); + }break; + case TH_ENCCTL_2PASS_IN:{ + if(_enc==NULL)return TH_EFAULT; + if(_enc->state.info.target_bitrate<=0|| + _enc->state.curframe_num>=0&&_enc->rc.twopass!=2){ + return TH_EINVAL; + } + return oc_enc_rc_2pass_in(_enc,_buf,_buf_sz); + }break; + default:return TH_EIMPL; + } +} + +int th_encode_flushheader(th_enc_ctx *_enc,th_comment *_tc,ogg_packet *_op){ + if(_enc==NULL)return TH_EFAULT; + return oc_state_flushheader(&_enc->state,&_enc->packet_state,&_enc->opb, + &_enc->qinfo,(const th_huff_table *)_enc->huff_codes,th_version_string(), + _tc,_op); +} + +static void oc_img_plane_copy_pad(th_img_plane *_dst,th_img_plane *_src, + ogg_int32_t _pic_x,ogg_int32_t _pic_y, + ogg_int32_t _pic_width,ogg_int32_t _pic_height){ + unsigned char *dst; + int dstride; + ogg_uint32_t frame_width; + ogg_uint32_t frame_height; + ogg_uint32_t y; + frame_width=_dst->width; + frame_height=_dst->height; + /*If we have _no_ data, just encode a dull green.*/ + if(_pic_width==0||_pic_height==0){ + dst=_dst->data; + dstride=_dst->stride; + for(y=0;ystride; + sstride=_src->stride; + dst_data=_dst->data; + src_data=_src->data; + dst=dst_data+_pic_y*(ptrdiff_t)dstride+_pic_x; + src=src_data+_pic_y*(ptrdiff_t)sstride+_pic_x; + for(y=0;y<_pic_height;y++){ + memcpy(dst,src,_pic_width); + dst+=dstride; + src+=sstride; + } + /*Step 2: Perform a low-pass extension into the padding region.*/ + /*Left side.*/ + for(x=_pic_x;x-->0;){ + dst=dst_data+_pic_y*(ptrdiff_t)dstride+x; + for(y=0;y<_pic_height;y++){ + dst[0]=(dst[1]<<1)+(dst-(dstride&-(y>0)))[1] + +(dst+(dstride&-(y+1<_pic_height)))[1]+2>>2; + dst+=dstride; + } + } + /*Right side.*/ + for(x=_pic_x+_pic_width;x0)))[0] + +(dst+(dstride&-(y+1<_pic_height)))[0]+2>>2; + dst+=dstride; + } + } + /*Top.*/ + dst=dst_data+_pic_y*(ptrdiff_t)dstride; + for(y=_pic_y;y-->0;){ + for(x=0;x0)] + +dst[x+(x+1>2; + } + dst-=dstride; + } + /*Bottom.*/ + dst=dst_data+(_pic_y+_pic_height)*(ptrdiff_t)dstride; + for(y=_pic_y+_pic_height;y0)] + +(dst-dstride)[x+(x+1>2; + } + dst+=dstride; + } + } +} + +int th_encode_ycbcr_in(th_enc_ctx *_enc,th_ycbcr_buffer _img){ + th_ycbcr_buffer img; + int cframe_width; + int cframe_height; + int cpic_width; + int cpic_height; + int cpic_x; + int cpic_y; + int hdec; + int vdec; + int pli; + int refi; + int drop; + /*Step 1: validate parameters.*/ + if(_enc==NULL||_img==NULL)return TH_EFAULT; + if(_enc->packet_state==OC_PACKET_DONE)return TH_EINVAL; + if(_enc->rc.twopass&&_enc->rc.twopass_buffer_bytes==0)return TH_EINVAL; + if((ogg_uint32_t)_img[0].width!=_enc->state.info.frame_width|| + (ogg_uint32_t)_img[0].height!=_enc->state.info.frame_height){ + return TH_EINVAL; + } + hdec=!(_enc->state.info.pixel_fmt&1); + vdec=!(_enc->state.info.pixel_fmt&2); + cframe_width=_enc->state.info.frame_width>>hdec; + cframe_height=_enc->state.info.frame_height>>vdec; + if(_img[1].width!=cframe_width||_img[2].width!=cframe_width|| + _img[1].height!=cframe_height||_img[2].height!=cframe_height){ + return TH_EINVAL; + } + /*Step 2: Copy the input to our internal buffer. + This lets us add padding, if necessary, so we don't have to worry about + dereferencing possibly invalid addresses, and allows us to use the same + strides and fragment offsets for both the input frame and the reference + frames.*/ + /*Flip the input buffer upside down.*/ + oc_ycbcr_buffer_flip(img,_img); + oc_img_plane_copy_pad(_enc->state.ref_frame_bufs[OC_FRAME_IO]+0,img+0, + _enc->state.info.pic_x,_enc->state.info.pic_y, + _enc->state.info.pic_width,_enc->state.info.pic_height); + cpic_x=_enc->state.info.pic_x>>hdec; + cpic_y=_enc->state.info.pic_y>>vdec; + cpic_width=(_enc->state.info.pic_x+_enc->state.info.pic_width+hdec>>hdec) + -cpic_x; + cpic_height=(_enc->state.info.pic_y+_enc->state.info.pic_height+vdec>>vdec) + -cpic_y; + for(pli=1;pli<3;pli++){ + oc_img_plane_copy_pad(_enc->state.ref_frame_bufs[OC_FRAME_IO]+pli,img+pli, + cpic_x,cpic_y,cpic_width,cpic_height); + } + /*Step 3: Update the buffer state.*/ + if(_enc->state.ref_frame_idx[OC_FRAME_SELF]>=0){ + _enc->state.ref_frame_idx[OC_FRAME_PREV]= + _enc->state.ref_frame_idx[OC_FRAME_SELF]; + if(_enc->state.frame_type==OC_INTRA_FRAME){ + /*The new frame becomes both the previous and gold reference frames.*/ + _enc->state.keyframe_num=_enc->state.curframe_num; + _enc->state.ref_frame_idx[OC_FRAME_GOLD]= + _enc->state.ref_frame_idx[OC_FRAME_SELF]; + } + } + /*Select a free buffer to use for the reconstructed version of this frame.*/ + for(refi=0;refi==_enc->state.ref_frame_idx[OC_FRAME_GOLD]|| + refi==_enc->state.ref_frame_idx[OC_FRAME_PREV];refi++); + _enc->state.ref_frame_idx[OC_FRAME_SELF]=refi; + _enc->state.curframe_num+=_enc->prev_dup_count+1; + /*Step 4: Compress the frame.*/ + /*Start with a keyframe, and don't allow the generation of invalid files that + overflow the keyframe_granule_shift.*/ + if(_enc->rc.twopass_force_kf||_enc->state.curframe_num==0|| + _enc->state.curframe_num-_enc->state.keyframe_num+_enc->dup_count>= + _enc->keyframe_frequency_force){ + oc_enc_compress_keyframe(_enc,0); + drop=0; + } + else{ + oc_enc_compress_frame(_enc,0); + drop=1; + } + oc_restore_fpu(&_enc->state); + /*drop currently indicates if the frame is droppable.*/ + if(_enc->state.info.target_bitrate>0){ + drop=oc_enc_update_rc_state(_enc,oggpackB_bytes(&_enc->opb)<<3, + _enc->state.frame_type,_enc->state.qis[0],0,drop); + } + else drop=0; + /*drop now indicates if the frame was dropped.*/ + if(drop)oc_enc_drop_frame(_enc); + else _enc->prevframe_dropped=0; + _enc->packet_state=OC_PACKET_READY; + _enc->prev_dup_count=_enc->nqueued_dups=_enc->dup_count; + _enc->dup_count=0; +#if defined(OC_DUMP_IMAGES) + oc_enc_set_granpos(_enc); + oc_state_dump_frame(&_enc->state,OC_FRAME_IO,"src"); + oc_state_dump_frame(&_enc->state,OC_FRAME_SELF,"rec"); +#endif + return 0; +} + +int th_encode_packetout(th_enc_ctx *_enc,int _last_p,ogg_packet *_op){ + if(_enc==NULL||_op==NULL)return TH_EFAULT; + if(_enc->packet_state==OC_PACKET_READY){ + _enc->packet_state=OC_PACKET_EMPTY; + if(_enc->rc.twopass!=1){ + unsigned char *packet; + packet=oggpackB_get_buffer(&_enc->opb); + /*If there's no packet, malloc failed while writing; it's lost forever.*/ + if(packet==NULL)return TH_EFAULT; + _op->packet=packet; + _op->bytes=oggpackB_bytes(&_enc->opb); + } + /*For the first pass in 2-pass mode, don't emit any packet data.*/ + else{ + _op->packet=NULL; + _op->bytes=0; + } + } + else if(_enc->packet_state==OC_PACKET_EMPTY){ + if(_enc->nqueued_dups>0){ + _enc->nqueued_dups--; + _op->packet=NULL; + _op->bytes=0; + } + else{ + if(_last_p)_enc->packet_state=OC_PACKET_DONE; + return 0; + } + } + else return 0; + _last_p=_last_p&&_enc->nqueued_dups<=0; + _op->b_o_s=0; + _op->e_o_s=_last_p; + oc_enc_set_granpos(_enc); + _op->packetno=th_granule_frame(_enc,_enc->state.granpos)+3; + _op->granulepos=_enc->state.granpos; + if(_last_p)_enc->packet_state=OC_PACKET_DONE; + return 1+_enc->nqueued_dups; +} diff --git a/Engine/lib/libtheora/lib/enquant.c b/Engine/lib/libtheora/lib/enquant.c new file mode 100644 index 000000000..3372fed22 --- /dev/null +++ b/Engine/lib/libtheora/lib/enquant.c @@ -0,0 +1,274 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: enquant.c 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ +#include +#include +#include "encint.h" + + + +void oc_quant_params_pack(oggpack_buffer *_opb,const th_quant_info *_qinfo){ + const th_quant_ranges *qranges; + const th_quant_base *base_mats[2*3*64]; + int indices[2][3][64]; + int nbase_mats; + int nbits; + int ci; + int qi; + int qri; + int qti; + int pli; + int qtj; + int plj; + int bmi; + int i; + i=_qinfo->loop_filter_limits[0]; + for(qi=1;qi<64;qi++)i=OC_MAXI(i,_qinfo->loop_filter_limits[qi]); + nbits=OC_ILOG_32(i); + oggpackB_write(_opb,nbits,3); + for(qi=0;qi<64;qi++){ + oggpackB_write(_opb,_qinfo->loop_filter_limits[qi],nbits); + } + /*580 bits for VP3.*/ + i=1; + for(qi=0;qi<64;qi++)i=OC_MAXI(_qinfo->ac_scale[qi],i); + nbits=OC_ILOGNZ_32(i); + oggpackB_write(_opb,nbits-1,4); + for(qi=0;qi<64;qi++)oggpackB_write(_opb,_qinfo->ac_scale[qi],nbits); + /*516 bits for VP3.*/ + i=1; + for(qi=0;qi<64;qi++)i=OC_MAXI(_qinfo->dc_scale[qi],i); + nbits=OC_ILOGNZ_32(i); + oggpackB_write(_opb,nbits-1,4); + for(qi=0;qi<64;qi++)oggpackB_write(_opb,_qinfo->dc_scale[qi],nbits); + /*Consolidate any duplicate base matrices.*/ + nbase_mats=0; + for(qti=0;qti<2;qti++)for(pli=0;pli<3;pli++){ + qranges=_qinfo->qi_ranges[qti]+pli; + for(qri=0;qri<=qranges->nranges;qri++){ + for(bmi=0;;bmi++){ + if(bmi>=nbase_mats){ + base_mats[bmi]=qranges->base_matrices+qri; + indices[qti][pli][qri]=nbase_mats++; + break; + } + else if(memcmp(base_mats[bmi][0],qranges->base_matrices[qri], + sizeof(base_mats[bmi][0]))==0){ + indices[qti][pli][qri]=bmi; + break; + } + } + } + } + /*Write out the list of unique base matrices. + 1545 bits for VP3 matrices.*/ + oggpackB_write(_opb,nbase_mats-1,9); + for(bmi=0;bmiqi_ranges[qti]+pli; + if(i>0){ + if(qti>0){ + if(qranges->nranges==_qinfo->qi_ranges[qti-1][pli].nranges&& + memcmp(qranges->sizes,_qinfo->qi_ranges[qti-1][pli].sizes, + qranges->nranges*sizeof(qranges->sizes[0]))==0&& + memcmp(indices[qti][pli],indices[qti-1][pli], + (qranges->nranges+1)*sizeof(indices[qti][pli][0]))==0){ + oggpackB_write(_opb,1,2); + continue; + } + } + qtj=(i-1)/3; + plj=(i-1)%3; + if(qranges->nranges==_qinfo->qi_ranges[qtj][plj].nranges&& + memcmp(qranges->sizes,_qinfo->qi_ranges[qtj][plj].sizes, + qranges->nranges*sizeof(qranges->sizes[0]))==0&& + memcmp(indices[qti][pli],indices[qtj][plj], + (qranges->nranges+1)*sizeof(indices[qti][pli][0]))==0){ + oggpackB_write(_opb,0,1+(qti>0)); + continue; + } + oggpackB_write(_opb,1,1); + } + oggpackB_write(_opb,indices[qti][pli][0],nbits); + for(qi=qri=0;qi<63;qri++){ + oggpackB_write(_opb,qranges->sizes[qri]-1,OC_ILOG_32(62-qi)); + qi+=qranges->sizes[qri]; + oggpackB_write(_opb,indices[qti][pli][qri+1],nbits); + } + } +} + +static void oc_iquant_init(oc_iquant *_this,ogg_uint16_t _d){ + ogg_uint32_t t; + int l; + _d<<=1; + l=OC_ILOGNZ_32(_d)-1; + t=1+((ogg_uint32_t)1<<16+l)/_d; + _this->m=(ogg_int16_t)(t-0x10000); + _this->l=l; +} + +/*See comments at oc_dequant_tables_init() for how the quantization tables' + storage should be initialized.*/ +void oc_enquant_tables_init(ogg_uint16_t *_dequant[64][3][2], + oc_iquant *_enquant[64][3][2],const th_quant_info *_qinfo){ + int qi; + int pli; + int qti; + /*Initialize the dequantization tables first.*/ + oc_dequant_tables_init(_dequant,NULL,_qinfo); + /*Derive the quantization tables directly from the dequantization tables.*/ + for(qi=0;qi<64;qi++)for(qti=0;qti<2;qti++)for(pli=0;pli<3;pli++){ + int zzi; + int plj; + int qtj; + int dupe; + dupe=0; + for(qtj=0;qtj<=qti;qtj++){ + for(plj=0;plj<(qtj>1))/qd; + qp+=rq*(ogg_uint32_t)rq; + } + q2+=OC_PCD[_pixel_fmt][pli]*(ogg_int64_t)qp; + } + /*qavg=1.0/sqrt(q2).*/ + _log_qavg[qti][qi]=OC_Q57(48)-oc_blog64(q2)>>1; + } +} diff --git a/Engine/lib/libtheora/lib/enquant.h b/Engine/lib/libtheora/lib/enquant.h new file mode 100644 index 000000000..d62df10d1 --- /dev/null +++ b/Engine/lib/libtheora/lib/enquant.h @@ -0,0 +1,27 @@ +#if !defined(_enquant_H) +# define _enquant_H (1) +# include "quant.h" + +typedef struct oc_iquant oc_iquant; + +#define OC_QUANT_MAX_LOG (OC_Q57(OC_STATIC_ILOG_32(OC_QUANT_MAX)-1)) + +/*Used to compute x/d via ((x*m>>16)+x>>l)+(x<0)) + (i.e., one 16x16->16 mul, 2 shifts, and 2 adds). + This is not an approximation; for 16-bit x and d, it is exact.*/ +struct oc_iquant{ + ogg_int16_t m; + ogg_int16_t l; +}; + +typedef oc_iquant oc_iquant_table[64]; + + + +void oc_quant_params_pack(oggpack_buffer *_opb,const th_quant_info *_qinfo); +void oc_enquant_tables_init(ogg_uint16_t *_dequant[64][3][2], + oc_iquant *_enquant[64][3][2],const th_quant_info *_qinfo); +void oc_enquant_qavg_init(ogg_int64_t _log_qavg[2][64], + ogg_uint16_t *_dequant[64][3][2],int _pixel_fmt); + +#endif diff --git a/Engine/lib/libtheora/lib/fdct.c b/Engine/lib/libtheora/lib/fdct.c new file mode 100644 index 000000000..dc3a66f24 --- /dev/null +++ b/Engine/lib/libtheora/lib/fdct.c @@ -0,0 +1,422 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: fdct.c 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ +#include "encint.h" +#include "dct.h" + + + +/*Performs a forward 8 point Type-II DCT transform. + The output is scaled by a factor of 2 from the orthonormal version of the + transform. + _y: The buffer to store the result in. + Data will be placed the first 8 entries (e.g., in a row of an 8x8 block). + _x: The input coefficients. + Every 8th entry is used (e.g., from a column of an 8x8 block).*/ +static void oc_fdct8(ogg_int16_t _y[8],const ogg_int16_t *_x){ + int t0; + int t1; + int t2; + int t3; + int t4; + int t5; + int t6; + int t7; + int r; + int s; + int u; + int v; + /*Stage 1:*/ + /*0-7 butterfly.*/ + t0=_x[0<<3]+(int)_x[7<<3]; + t7=_x[0<<3]-(int)_x[7<<3]; + /*1-6 butterfly.*/ + t1=_x[1<<3]+(int)_x[6<<3]; + t6=_x[1<<3]-(int)_x[6<<3]; + /*2-5 butterfly.*/ + t2=_x[2<<3]+(int)_x[5<<3]; + t5=_x[2<<3]-(int)_x[5<<3]; + /*3-4 butterfly.*/ + t3=_x[3<<3]+(int)_x[4<<3]; + t4=_x[3<<3]-(int)_x[4<<3]; + /*Stage 2:*/ + /*0-3 butterfly.*/ + r=t0+t3; + t3=t0-t3; + t0=r; + /*1-2 butterfly.*/ + r=t1+t2; + t2=t1-t2; + t1=r; + /*6-5 butterfly.*/ + r=t6+t5; + t5=t6-t5; + t6=r; + /*Stages 3 and 4 are where all the approximation occurs. + These are chosen to be as close to an exact inverse of the approximations + made in the iDCT as possible, while still using mostly 16-bit arithmetic. + We use some 16x16->32 signed MACs, but those still commonly execute in 1 + cycle on a 16-bit DSP. + For example, s=(27146*t5+0x4000>>16)+t5+(t5!=0) is an exact inverse of + t5=(OC_C4S4*s>>16). + That is, applying the latter to the output of the former will recover t5 + exactly (over the valid input range of t5, -23171...23169). + We increase the rounding bias to 0xB500 in this particular case so that + errors inverting the subsequent butterfly are not one-sided (e.g., the + mean error is very close to zero). + The (t5!=0) term could be replaced simply by 1, but we want to send 0 to 0. + The fDCT of an all-zeros block will still not be zero, because of the + biases we added at the very beginning of the process, but it will be close + enough that it is guaranteed to round to zero.*/ + /*Stage 3:*/ + /*4-5 butterfly.*/ + s=(27146*t5+0xB500>>16)+t5+(t5!=0)>>1; + r=t4+s; + t5=t4-s; + t4=r; + /*7-6 butterfly.*/ + s=(27146*t6+0xB500>>16)+t6+(t6!=0)>>1; + r=t7+s; + t6=t7-s; + t7=r; + /*Stage 4:*/ + /*0-1 butterfly.*/ + r=(27146*t0+0x4000>>16)+t0+(t0!=0); + s=(27146*t1+0xB500>>16)+t1+(t1!=0); + u=r+s>>1; + v=r-u; + _y[0]=u; + _y[4]=v; + /*3-2 rotation by 6pi/16*/ + u=(OC_C6S2*t2+OC_C2S6*t3+0x6CB7>>16)+(t3!=0); + s=(OC_C6S2*u>>16)-t2; + v=(s*21600+0x2800>>18)+s+(s!=0); + _y[2]=u; + _y[6]=v; + /*6-5 rotation by 3pi/16*/ + u=(OC_C5S3*t6+OC_C3S5*t5+0x0E3D>>16)+(t5!=0); + s=t6-(OC_C5S3*u>>16); + v=(s*26568+0x3400>>17)+s+(s!=0); + _y[5]=u; + _y[3]=v; + /*7-4 rotation by 7pi/16*/ + u=(OC_C7S1*t4+OC_C1S7*t7+0x7B1B>>16)+(t7!=0); + s=(OC_C7S1*u>>16)-t4; + v=(s*20539+0x3000>>20)+s+(s!=0); + _y[1]=u; + _y[7]=v; +} + +void oc_enc_fdct8x8(const oc_enc_ctx *_enc,ogg_int16_t _y[64], + const ogg_int16_t _x[64]){ + (*_enc->opt_vtable.fdct8x8)(_y,_x); +} + +/*Performs a forward 8x8 Type-II DCT transform. + The output is scaled by a factor of 4 relative to the orthonormal version + of the transform. + _y: The buffer to store the result in. + This may be the same as _x. + _x: The input coefficients. */ +void oc_enc_fdct8x8_c(ogg_int16_t _y[64],const ogg_int16_t _x[64]){ + const ogg_int16_t *in; + ogg_int16_t *end; + ogg_int16_t *out; + ogg_int16_t w[64]; + int i; + /*Add two extra bits of working precision to improve accuracy; any more and + we could overflow.*/ + for(i=0;i<64;i++)w[i]=_x[i]<<2; + /*These biases correct for some systematic error that remains in the full + fDCT->iDCT round trip.*/ + w[0]+=(w[0]!=0)+1; + w[1]++; + w[8]--; + /*Transform columns of w into rows of _y.*/ + for(in=w,out=_y,end=out+64;out>2; +} + + + +/*This does not seem to outperform simple LFE border padding before MC. + It yields higher PSNR, but much higher bitrate usage.*/ +#if 0 +typedef struct oc_extension_info oc_extension_info; + + + +/*Information needed to pad boundary blocks. + We multiply each row/column by an extension matrix that fills in the padding + values as a linear combination of the active values, so that an equivalent + number of coefficients are forced to zero. + This costs at most 16 multiplies, the same as a 1-D fDCT itself, and as + little as 7 multiplies. + We compute the extension matrices for every possible shape in advance, as + there are only 35. + The coefficients for all matrices are stored in a single array to take + advantage of the overlap and repetitiveness of many of the shapes. + A similar technique is applied to the offsets into this array. + This reduces the required table storage by about 48%. + See tools/extgen.c for details. + We could conceivably do the same for all 256 possible shapes.*/ +struct oc_extension_info{ + /*The mask of the active pixels in the shape.*/ + short mask; + /*The number of active pixels in the shape.*/ + short na; + /*The extension matrix. + This is (8-na)xna*/ + const ogg_int16_t *const *ext; + /*The pixel indices: na active pixels followed by 8-na padding pixels.*/ + unsigned char pi[8]; + /*The coefficient indices: na unconstrained coefficients followed by 8-na + coefficients to be forced to zero.*/ + unsigned char ci[8]; +}; + + +/*The number of shapes we need.*/ +#define OC_NSHAPES (35) + +static const ogg_int16_t OC_EXT_COEFFS[229]={ + 0x7FFF,0xE1F8,0x6903,0xAA79,0x5587,0x7FFF,0x1E08,0x7FFF, + 0x5587,0xAA79,0x6903,0xE1F8,0x7FFF,0x0000,0x0000,0x0000, + 0x7FFF,0x0000,0x0000,0x7FFF,0x8000,0x7FFF,0x0000,0x0000, + 0x7FFF,0xE1F8,0x1E08,0xB0A7,0xAA1D,0x337C,0x7FFF,0x4345, + 0x2267,0x4345,0x7FFF,0x337C,0xAA1D,0xB0A7,0x8A8C,0x4F59, + 0x03B4,0xE2D6,0x7FFF,0x2CF3,0x7FFF,0xE2D6,0x03B4,0x4F59, + 0x8A8C,0x1103,0x7AEF,0x5225,0xDF60,0xC288,0xDF60,0x5225, + 0x7AEF,0x1103,0x668A,0xD6EE,0x3A16,0x0E6C,0xFA07,0x0E6C, + 0x3A16,0xD6EE,0x668A,0x2A79,0x2402,0x980F,0x50F5,0x4882, + 0x50F5,0x980F,0x2402,0x2A79,0xF976,0x2768,0x5F22,0x2768, + 0xF976,0x1F91,0x76C1,0xE9AE,0x76C1,0x1F91,0x7FFF,0xD185, + 0x0FC8,0xD185,0x7FFF,0x4F59,0x4345,0xED62,0x4345,0x4F59, + 0xF574,0x5D99,0x2CF3,0x5D99,0xF574,0x5587,0x3505,0x30FC, + 0xF482,0x953C,0xEAC4,0x7FFF,0x4F04,0x7FFF,0xEAC4,0x953C, + 0xF482,0x30FC,0x4F04,0x273D,0xD8C3,0x273D,0x1E09,0x61F7, + 0x1E09,0x273D,0xD8C3,0x273D,0x4F04,0x30FC,0xA57E,0x153C, + 0x6AC4,0x3C7A,0x1E08,0x3C7A,0x6AC4,0x153C,0xA57E,0x7FFF, + 0xA57E,0x5A82,0x6AC4,0x153C,0xC386,0xE1F8,0xC386,0x153C, + 0x6AC4,0x5A82,0xD8C3,0x273D,0x7FFF,0xE1F7,0x7FFF,0x273D, + 0xD8C3,0x4F04,0x30FC,0xD8C3,0x273D,0xD8C3,0x30FC,0x4F04, + 0x1FC8,0x67AD,0x1853,0xE038,0x1853,0x67AD,0x1FC8,0x4546, + 0xE038,0x1FC8,0x3ABA,0x1FC8,0xE038,0x4546,0x3505,0x5587, + 0xF574,0xBC11,0x78F4,0x4AFB,0xE6F3,0x4E12,0x3C11,0xF8F4, + 0x4AFB,0x3C7A,0xF88B,0x3C11,0x78F4,0xCAFB,0x7FFF,0x08CC, + 0x070C,0x236D,0x5587,0x236D,0x070C,0xF88B,0x3C7A,0x4AFB, + 0xF8F4,0x3C11,0x7FFF,0x153C,0xCAFB,0x153C,0x7FFF,0x1E08, + 0xE1F8,0x7FFF,0x08CC,0x7FFF,0xCAFB,0x78F4,0x3C11,0x4E12, + 0xE6F3,0x4AFB,0x78F4,0xBC11,0xFE3D,0x7FFF,0xFE3D,0x2F3A, + 0x7FFF,0x2F3A,0x89BC,0x7FFF,0x89BC +}; + +static const ogg_int16_t *const OC_EXT_ROWS[96]={ + OC_EXT_COEFFS+ 0,OC_EXT_COEFFS+ 0,OC_EXT_COEFFS+ 0,OC_EXT_COEFFS+ 0, + OC_EXT_COEFFS+ 0,OC_EXT_COEFFS+ 0,OC_EXT_COEFFS+ 0,OC_EXT_COEFFS+ 6, + OC_EXT_COEFFS+ 27,OC_EXT_COEFFS+ 38,OC_EXT_COEFFS+ 43,OC_EXT_COEFFS+ 32, + OC_EXT_COEFFS+ 49,OC_EXT_COEFFS+ 58,OC_EXT_COEFFS+ 67,OC_EXT_COEFFS+ 71, + OC_EXT_COEFFS+ 62,OC_EXT_COEFFS+ 53,OC_EXT_COEFFS+ 12,OC_EXT_COEFFS+ 15, + OC_EXT_COEFFS+ 14,OC_EXT_COEFFS+ 13,OC_EXT_COEFFS+ 76,OC_EXT_COEFFS+ 81, + OC_EXT_COEFFS+ 86,OC_EXT_COEFFS+ 91,OC_EXT_COEFFS+ 96,OC_EXT_COEFFS+ 98, + OC_EXT_COEFFS+ 93,OC_EXT_COEFFS+ 88,OC_EXT_COEFFS+ 83,OC_EXT_COEFFS+ 78, + OC_EXT_COEFFS+ 12,OC_EXT_COEFFS+ 15,OC_EXT_COEFFS+ 15,OC_EXT_COEFFS+ 12, + OC_EXT_COEFFS+ 12,OC_EXT_COEFFS+ 15,OC_EXT_COEFFS+ 12,OC_EXT_COEFFS+ 15, + OC_EXT_COEFFS+ 15,OC_EXT_COEFFS+ 12,OC_EXT_COEFFS+ 103,OC_EXT_COEFFS+ 108, + OC_EXT_COEFFS+ 126,OC_EXT_COEFFS+ 16,OC_EXT_COEFFS+ 137,OC_EXT_COEFFS+ 141, + OC_EXT_COEFFS+ 20,OC_EXT_COEFFS+ 130,OC_EXT_COEFFS+ 113,OC_EXT_COEFFS+ 116, + OC_EXT_COEFFS+ 146,OC_EXT_COEFFS+ 153,OC_EXT_COEFFS+ 160,OC_EXT_COEFFS+ 167, + OC_EXT_COEFFS+ 170,OC_EXT_COEFFS+ 163,OC_EXT_COEFFS+ 156,OC_EXT_COEFFS+ 149, + OC_EXT_COEFFS+ 119,OC_EXT_COEFFS+ 122,OC_EXT_COEFFS+ 174,OC_EXT_COEFFS+ 177, + OC_EXT_COEFFS+ 182,OC_EXT_COEFFS+ 187,OC_EXT_COEFFS+ 192,OC_EXT_COEFFS+ 197, + OC_EXT_COEFFS+ 202,OC_EXT_COEFFS+ 207,OC_EXT_COEFFS+ 210,OC_EXT_COEFFS+ 215, + OC_EXT_COEFFS+ 179,OC_EXT_COEFFS+ 189,OC_EXT_COEFFS+ 24,OC_EXT_COEFFS+ 204, + OC_EXT_COEFFS+ 184,OC_EXT_COEFFS+ 194,OC_EXT_COEFFS+ 212,OC_EXT_COEFFS+ 199, + OC_EXT_COEFFS+ 217,OC_EXT_COEFFS+ 100,OC_EXT_COEFFS+ 134,OC_EXT_COEFFS+ 135, + OC_EXT_COEFFS+ 135,OC_EXT_COEFFS+ 12,OC_EXT_COEFFS+ 15,OC_EXT_COEFFS+ 134, + OC_EXT_COEFFS+ 134,OC_EXT_COEFFS+ 135,OC_EXT_COEFFS+ 220,OC_EXT_COEFFS+ 223, + OC_EXT_COEFFS+ 226,OC_EXT_COEFFS+ 227,OC_EXT_COEFFS+ 224,OC_EXT_COEFFS+ 221 +}; + +static const oc_extension_info OC_EXTENSION_INFO[OC_NSHAPES]={ + {0x7F,7,OC_EXT_ROWS+ 0,{0,1,2,3,4,5,6,7},{0,1,2,4,5,6,7,3}}, + {0xFE,7,OC_EXT_ROWS+ 7,{1,2,3,4,5,6,7,0},{0,1,2,4,5,6,7,3}}, + {0x3F,6,OC_EXT_ROWS+ 8,{0,1,2,3,4,5,7,6},{0,1,3,4,6,7,5,2}}, + {0xFC,6,OC_EXT_ROWS+ 10,{2,3,4,5,6,7,1,0},{0,1,3,4,6,7,5,2}}, + {0x1F,5,OC_EXT_ROWS+ 12,{0,1,2,3,4,7,6,5},{0,2,3,5,7,6,4,1}}, + {0xF8,5,OC_EXT_ROWS+ 15,{3,4,5,6,7,2,1,0},{0,2,3,5,7,6,4,1}}, + {0x0F,4,OC_EXT_ROWS+ 18,{0,1,2,3,7,6,5,4},{0,2,4,6,7,5,3,1}}, + {0xF0,4,OC_EXT_ROWS+ 18,{4,5,6,7,3,2,1,0},{0,2,4,6,7,5,3,1}}, + {0x07,3,OC_EXT_ROWS+ 22,{0,1,2,7,6,5,4,3},{0,3,6,7,5,4,2,1}}, + {0xE0,3,OC_EXT_ROWS+ 27,{5,6,7,4,3,2,1,0},{0,3,6,7,5,4,2,1}}, + {0x03,2,OC_EXT_ROWS+ 32,{0,1,7,6,5,4,3,2},{0,4,7,6,5,3,2,1}}, + {0xC0,2,OC_EXT_ROWS+ 32,{6,7,5,4,3,2,1,0},{0,4,7,6,5,3,2,1}}, + {0x01,1,OC_EXT_ROWS+ 0,{0,7,6,5,4,3,2,1},{0,7,6,5,4,3,2,1}}, + {0x80,1,OC_EXT_ROWS+ 0,{7,6,5,4,3,2,1,0},{0,7,6,5,4,3,2,1}}, + {0x7E,6,OC_EXT_ROWS+ 42,{1,2,3,4,5,6,7,0},{0,1,2,5,6,7,4,3}}, + {0x7C,5,OC_EXT_ROWS+ 44,{2,3,4,5,6,7,1,0},{0,1,4,5,7,6,3,2}}, + {0x3E,5,OC_EXT_ROWS+ 47,{1,2,3,4,5,7,6,0},{0,1,4,5,7,6,3,2}}, + {0x78,4,OC_EXT_ROWS+ 50,{3,4,5,6,7,2,1,0},{0,4,5,7,6,3,2,1}}, + {0x3C,4,OC_EXT_ROWS+ 54,{2,3,4,5,7,6,1,0},{0,3,4,7,6,5,2,1}}, + {0x1E,4,OC_EXT_ROWS+ 58,{1,2,3,4,7,6,5,0},{0,4,5,7,6,3,2,1}}, + {0x70,3,OC_EXT_ROWS+ 62,{4,5,6,7,3,2,1,0},{0,5,7,6,4,3,2,1}}, + {0x38,3,OC_EXT_ROWS+ 67,{3,4,5,7,6,2,1,0},{0,5,6,7,4,3,2,1}}, + {0x1C,3,OC_EXT_ROWS+ 72,{2,3,4,7,6,5,1,0},{0,5,6,7,4,3,2,1}}, + {0x0E,3,OC_EXT_ROWS+ 77,{1,2,3,7,6,5,4,0},{0,5,7,6,4,3,2,1}}, + {0x60,2,OC_EXT_ROWS+ 82,{5,6,7,4,3,2,1,0},{0,2,7,6,5,4,3,1}}, + {0x30,2,OC_EXT_ROWS+ 36,{4,5,7,6,3,2,1,0},{0,4,7,6,5,3,2,1}}, + {0x18,2,OC_EXT_ROWS+ 90,{3,4,7,6,5,2,1,0},{0,1,7,6,5,4,3,2}}, + {0x0C,2,OC_EXT_ROWS+ 34,{2,3,7,6,5,4,1,0},{0,4,7,6,5,3,2,1}}, + {0x06,2,OC_EXT_ROWS+ 84,{1,2,7,6,5,4,3,0},{0,2,7,6,5,4,3,1}}, + {0x40,1,OC_EXT_ROWS+ 0,{6,7,5,4,3,2,1,0},{0,7,6,5,4,3,2,1}}, + {0x20,1,OC_EXT_ROWS+ 0,{5,7,6,4,3,2,1,0},{0,7,6,5,4,3,2,1}}, + {0x10,1,OC_EXT_ROWS+ 0,{4,7,6,5,3,2,1,0},{0,7,6,5,4,3,2,1}}, + {0x08,1,OC_EXT_ROWS+ 0,{3,7,6,5,4,2,1,0},{0,7,6,5,4,3,2,1}}, + {0x04,1,OC_EXT_ROWS+ 0,{2,7,6,5,4,3,1,0},{0,7,6,5,4,3,2,1}}, + {0x02,1,OC_EXT_ROWS+ 0,{1,7,6,5,4,3,2,0},{0,7,6,5,4,3,2,1}} +}; + + + +/*Pads a single column of a partial block and then performs a forward Type-II + DCT on the result. + The input is scaled by a factor of 4 and biased appropriately for the current + fDCT implementation. + The output is scaled by an additional factor of 2 from the orthonormal + version of the transform. + _y: The buffer to store the result in. + Data will be placed the first 8 entries (e.g., in a row of an 8x8 block). + _x: The input coefficients. + Every 8th entry is used (e.g., from a column of an 8x8 block). + _e: The extension information for the shape.*/ +static void oc_fdct8_ext(ogg_int16_t _y[8],ogg_int16_t *_x, + const oc_extension_info *_e){ + const unsigned char *pi; + int na; + na=_e->na; + pi=_e->pi; + if(na==1){ + int ci; + /*While the branch below is still correct for shapes with na==1, we can + perform the entire transform with just 1 multiply in this case instead + of 23.*/ + _y[0]=(ogg_int16_t)(OC_DIV2_16(OC_C4S4*(_x[pi[0]]))); + for(ci=1;ci<8;ci++)_y[ci]=0; + } + else{ + const ogg_int16_t *const *ext; + int zpi; + int api; + int nz; + /*First multiply by the extension matrix to compute the padding values.*/ + nz=8-na; + ext=_e->ext; + for(zpi=0;zpi>16)+1>>1; + } + oc_fdct8(_y,_x); + } +} + +/*Performs a forward 8x8 Type-II DCT transform on blocks which overlap the + border of the picture region. + This method ONLY works with rectangular regions. + _border: A description of which pixels are inside the border. + _y: The buffer to store the result in. + This may be the same as _x. + _x: The input pixel values. + Pixel values outside the border will be ignored.*/ +void oc_fdct8x8_border(const oc_border_info *_border, + ogg_int16_t _y[64],const ogg_int16_t _x[64]){ + ogg_int16_t *in; + ogg_int16_t *out; + ogg_int16_t w[64]; + ogg_int64_t mask; + const oc_extension_info *cext; + const oc_extension_info *rext; + int cmask; + int rmask; + int ri; + int ci; + /*Identify the shapes of the non-zero rows and columns.*/ + rmask=cmask=0; + mask=_border->mask; + for(ri=0;ri<8;ri++){ + /*This aggregation is _only_ correct for rectangular masks.*/ + cmask|=((mask&0xFF)!=0)<>=8; + } + /*Find the associated extension info for these shapes.*/ + if(cmask==0xFF)cext=NULL; + else for(cext=OC_EXTENSION_INFO;cext->mask!=cmask;){ + /*If we somehow can't find the shape, then just do an unpadded fDCT. + It won't be efficient, but it should still be correct.*/ + if(++cext>=OC_EXTENSION_INFO+OC_NSHAPES){ + oc_enc_fdct8x8_c(_y,_x); + return; + } + } + if(rmask==0xFF)rext=NULL; + else for(rext=OC_EXTENSION_INFO;rext->mask!=rmask;){ + /*If we somehow can't find the shape, then just do an unpadded fDCT. + It won't be efficient, but it should still be correct.*/ + if(++rext>=OC_EXTENSION_INFO+OC_NSHAPES){ + oc_enc_fdct8x8_c(_y,_x); + return; + } + } + /*Add two extra bits of working precision to improve accuracy; any more and + we could overflow.*/ + for(ci=0;ci<64;ci++)w[ci]=_x[ci]<<2; + /*These biases correct for some systematic error that remains in the full + fDCT->iDCT round trip. + We can safely add them before padding, since if these pixel values are + overwritten, we didn't care what they were anyway (and the unbiased values + will usually yield smaller DCT coefficient magnitudes).*/ + w[0]+=(w[0]!=0)+1; + w[1]++; + w[8]--; + /*Transform the columns. + We can ignore zero columns without a problem.*/ + in=w; + out=_y; + if(cext==NULL)for(ci=0;ci<8;ci++)oc_fdct8(out+(ci<<3),in+ci); + else for(ci=0;ci<8;ci++)if(rmask&(1<>2; +} +#endif diff --git a/Engine/lib/libtheora/lib/fragment.c b/Engine/lib/libtheora/lib/fragment.c new file mode 100644 index 000000000..15372e9d9 --- /dev/null +++ b/Engine/lib/libtheora/lib/fragment.c @@ -0,0 +1,87 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: fragment.c 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ +#include +#include "internal.h" + +void oc_frag_copy(const oc_theora_state *_state,unsigned char *_dst, + const unsigned char *_src,int _ystride){ + (*_state->opt_vtable.frag_copy)(_dst,_src,_ystride); +} + +void oc_frag_copy_c(unsigned char *_dst,const unsigned char *_src,int _ystride){ + int i; + for(i=8;i-->0;){ + memcpy(_dst,_src,8*sizeof(*_dst)); + _dst+=_ystride; + _src+=_ystride; + } +} + +void oc_frag_recon_intra(const oc_theora_state *_state,unsigned char *_dst, + int _ystride,const ogg_int16_t _residue[64]){ + _state->opt_vtable.frag_recon_intra(_dst,_ystride,_residue); +} + +void oc_frag_recon_intra_c(unsigned char *_dst,int _ystride, + const ogg_int16_t _residue[64]){ + int i; + for(i=0;i<8;i++){ + int j; + for(j=0;j<8;j++)_dst[j]=OC_CLAMP255(_residue[i*8+j]+128); + _dst+=_ystride; + } +} + +void oc_frag_recon_inter(const oc_theora_state *_state,unsigned char *_dst, + const unsigned char *_src,int _ystride,const ogg_int16_t _residue[64]){ + _state->opt_vtable.frag_recon_inter(_dst,_src,_ystride,_residue); +} + +void oc_frag_recon_inter_c(unsigned char *_dst, + const unsigned char *_src,int _ystride,const ogg_int16_t _residue[64]){ + int i; + for(i=0;i<8;i++){ + int j; + for(j=0;j<8;j++)_dst[j]=OC_CLAMP255(_residue[i*8+j]+_src[j]); + _dst+=_ystride; + _src+=_ystride; + } +} + +void oc_frag_recon_inter2(const oc_theora_state *_state,unsigned char *_dst, + const unsigned char *_src1,const unsigned char *_src2,int _ystride, + const ogg_int16_t _residue[64]){ + _state->opt_vtable.frag_recon_inter2(_dst,_src1,_src2,_ystride,_residue); +} + +void oc_frag_recon_inter2_c(unsigned char *_dst,const unsigned char *_src1, + const unsigned char *_src2,int _ystride,const ogg_int16_t _residue[64]){ + int i; + for(i=0;i<8;i++){ + int j; + for(j=0;j<8;j++)_dst[j]=OC_CLAMP255(_residue[i*8+j]+(_src1[j]+_src2[j]>>1)); + _dst+=_ystride; + _src1+=_ystride; + _src2+=_ystride; + } +} + +void oc_restore_fpu(const oc_theora_state *_state){ + _state->opt_vtable.restore_fpu(); +} + +void oc_restore_fpu_c(void){} diff --git a/Engine/lib/libtheora/lib/huffdec.c b/Engine/lib/libtheora/lib/huffdec.c new file mode 100644 index 000000000..8cf27f034 --- /dev/null +++ b/Engine/lib/libtheora/lib/huffdec.c @@ -0,0 +1,489 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: huffdec.c 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ + +#include +#include +#include +#include "huffdec.h" +#include "decint.h" + + +/*The ANSI offsetof macro is broken on some platforms (e.g., older DECs).*/ +#define _ogg_offsetof(_type,_field)\ + ((size_t)((char *)&((_type *)0)->_field-(char *)0)) + +/*The number of internal tokens associated with each of the spec tokens.*/ +static const unsigned char OC_DCT_TOKEN_MAP_ENTRIES[TH_NDCT_TOKENS]={ + 1,1,1,4,8,1,1,8,1,1,1,1,1,2,2,2,2,4,8,2,2,2,4,2,2,2,2,2,8,2,4,8 +}; + +/*The map from external spec-defined tokens to internal tokens. + This is constructed so that any extra bits read with the original token value + can be masked off the least significant bits of its internal token index. + In addition, all of the tokens which require additional extra bits are placed + at the start of the list, and grouped by type. + OC_DCT_REPEAT_RUN3_TOKEN is placed first, as it is an extra-special case, so + giving it index 0 may simplify comparisons on some architectures. + These requirements require some substantial reordering.*/ +static const unsigned char OC_DCT_TOKEN_MAP[TH_NDCT_TOKENS]={ + /*OC_DCT_EOB1_TOKEN (0 extra bits)*/ + 15, + /*OC_DCT_EOB2_TOKEN (0 extra bits)*/ + 16, + /*OC_DCT_EOB3_TOKEN (0 extra bits)*/ + 17, + /*OC_DCT_REPEAT_RUN0_TOKEN (2 extra bits)*/ + 88, + /*OC_DCT_REPEAT_RUN1_TOKEN (3 extra bits)*/ + 80, + /*OC_DCT_REPEAT_RUN2_TOKEN (4 extra bits)*/ + 1, + /*OC_DCT_REPEAT_RUN3_TOKEN (12 extra bits)*/ + 0, + /*OC_DCT_SHORT_ZRL_TOKEN (3 extra bits)*/ + 48, + /*OC_DCT_ZRL_TOKEN (6 extra bits)*/ + 14, + /*OC_ONE_TOKEN (0 extra bits)*/ + 56, + /*OC_MINUS_ONE_TOKEN (0 extra bits)*/ + 57, + /*OC_TWO_TOKEN (0 extra bits)*/ + 58, + /*OC_MINUS_TWO_TOKEN (0 extra bits)*/ + 59, + /*OC_DCT_VAL_CAT2 (1 extra bit)*/ + 60, + 62, + 64, + 66, + /*OC_DCT_VAL_CAT3 (2 extra bits)*/ + 68, + /*OC_DCT_VAL_CAT4 (3 extra bits)*/ + 72, + /*OC_DCT_VAL_CAT5 (4 extra bits)*/ + 2, + /*OC_DCT_VAL_CAT6 (5 extra bits)*/ + 4, + /*OC_DCT_VAL_CAT7 (6 extra bits)*/ + 6, + /*OC_DCT_VAL_CAT8 (10 extra bits)*/ + 8, + /*OC_DCT_RUN_CAT1A (1 extra bit)*/ + 18, + 20, + 22, + 24, + 26, + /*OC_DCT_RUN_CAT1B (3 extra bits)*/ + 32, + /*OC_DCT_RUN_CAT1C (4 extra bits)*/ + 12, + /*OC_DCT_RUN_CAT2A (2 extra bits)*/ + 28, + /*OC_DCT_RUN_CAT2B (3 extra bits)*/ + 40 +}; + +/*These three functions are really part of the bitpack.c module, but + they are only used here. + Declaring local static versions so they can be inlined saves considerable + function call overhead.*/ + +static oc_pb_window oc_pack_refill(oc_pack_buf *_b,int _bits){ + const unsigned char *ptr; + const unsigned char *stop; + oc_pb_window window; + int available; + window=_b->window; + available=_b->bits; + ptr=_b->ptr; + stop=_b->stop; + /*This version of _refill() doesn't bother setting eof because we won't + check for it after we've started decoding DCT tokens.*/ + if(ptr>=stop)available=OC_LOTS_OF_BITS; + while(available<=OC_PB_WINDOW_SIZE-8){ + available+=8; + window|=(oc_pb_window)*ptr++<=stop)available=OC_LOTS_OF_BITS; + } + _b->ptr=ptr; + if(_bits>available)window|=*ptr>>(available&7); + _b->bits=available; + return window; +} + + +/*Read in bits without advancing the bit pointer. + Here we assume 0<=_bits&&_bits<=32.*/ +static long oc_pack_look(oc_pack_buf *_b,int _bits){ + oc_pb_window window; + int available; + long result; + window=_b->window; + available=_b->bits; + if(_bits==0)return 0; + if(_bits>available)_b->window=window=oc_pack_refill(_b,_bits); + result=window>>OC_PB_WINDOW_SIZE-_bits; + return result; +} + +/*Advance the bit pointer.*/ +static void oc_pack_adv(oc_pack_buf *_b,int _bits){ + /*We ignore the special cases for _bits==0 and _bits==32 here, since they are + never used actually used. + OC_HUFF_SLUSH (defined below) would have to be at least 27 to actually read + 32 bits in a single go, and would require a 32 GB lookup table (assuming + 8 byte pointers, since 4 byte pointers couldn't fit such a table).*/ + _b->window<<=_bits; + _b->bits-=_bits; +} + + +/*The log_2 of the size of a lookup table is allowed to grow to relative to + the number of unique nodes it contains. + E.g., if OC_HUFF_SLUSH is 2, then at most 75% of the space in the tree is + wasted (each node will have an amortized cost of at most 20 bytes when using + 4-byte pointers). + Larger numbers can decode tokens with fewer read operations, while smaller + numbers may save more space (requiring as little as 8 bytes amortized per + node, though there will be more nodes). + With a sample file: + 32233473 read calls are required when no tree collapsing is done (100.0%). + 19269269 read calls are required when OC_HUFF_SLUSH is 0 (59.8%). + 11144969 read calls are required when OC_HUFF_SLUSH is 1 (34.6%). + 10538563 read calls are required when OC_HUFF_SLUSH is 2 (32.7%). + 10192578 read calls are required when OC_HUFF_SLUSH is 3 (31.6%). + Since a value of 1 gets us the vast majority of the speed-up with only a + small amount of wasted memory, this is what we use.*/ +#define OC_HUFF_SLUSH (1) + + +/*Determines the size in bytes of a Huffman tree node that represents a + subtree of depth _nbits. + _nbits: The depth of the subtree. + If this is 0, the node is a leaf node. + Otherwise 1<<_nbits pointers are allocated for children. + Return: The number of bytes required to store the node.*/ +static size_t oc_huff_node_size(int _nbits){ + size_t size; + size=_ogg_offsetof(oc_huff_node,nodes); + if(_nbits>0)size+=sizeof(oc_huff_node *)*(1<<_nbits); + return size; +} + +static oc_huff_node *oc_huff_node_init(char **_storage,size_t _size,int _nbits){ + oc_huff_node *ret; + ret=(oc_huff_node *)*_storage; + ret->nbits=(unsigned char)_nbits; + (*_storage)+=_size; + return ret; +} + + +/*Determines the size in bytes of a Huffman tree. + _nbits: The depth of the subtree. + If this is 0, the node is a leaf node. + Otherwise storage for 1<<_nbits pointers are added for children. + Return: The number of bytes required to store the tree.*/ +static size_t oc_huff_tree_size(const oc_huff_node *_node){ + size_t size; + size=oc_huff_node_size(_node->nbits); + if(_node->nbits){ + int nchildren; + int i; + nchildren=1<<_node->nbits; + for(i=0;inbits-_node->nodes[i]->depth){ + size+=oc_huff_tree_size(_node->nodes[i]); + } + } + return size; +} + + +/*Unpacks a sub-tree from the given buffer. + _opb: The buffer to unpack from. + _binodes: The nodes to store the sub-tree in. + _nbinodes: The number of nodes available for the sub-tree. + Return: 0 on success, or a negative value on error.*/ +static int oc_huff_tree_unpack(oc_pack_buf *_opb, + oc_huff_node *_binodes,int _nbinodes){ + oc_huff_node *binode; + long bits; + int nused; + if(_nbinodes<1)return TH_EBADHEADER; + binode=_binodes; + nused=0; + bits=oc_pack_read1(_opb); + if(oc_pack_bytes_left(_opb)<0)return TH_EBADHEADER; + /*Read an internal node:*/ + if(!bits){ + int ret; + nused++; + binode->nbits=1; + binode->depth=1; + binode->nodes[0]=_binodes+nused; + ret=oc_huff_tree_unpack(_opb,_binodes+nused,_nbinodes-nused); + if(ret>=0){ + nused+=ret; + binode->nodes[1]=_binodes+nused; + ret=oc_huff_tree_unpack(_opb,_binodes+nused,_nbinodes-nused); + } + if(ret<0)return ret; + nused+=ret; + } + /*Read a leaf node:*/ + else{ + int ntokens; + int token; + int i; + bits=oc_pack_read(_opb,OC_NDCT_TOKEN_BITS); + if(oc_pack_bytes_left(_opb)<0)return TH_EBADHEADER; + /*Find out how many internal tokens we translate this external token into.*/ + ntokens=OC_DCT_TOKEN_MAP_ENTRIES[bits]; + if(_nbinodes<2*ntokens-1)return TH_EBADHEADER; + /*Fill in a complete binary tree pointing to the internal tokens.*/ + for(i=1;inbits=0; + binode->depth=1; + binode->token=token+i; + } + } + return nused; +} + +/*Finds the depth of shortest branch of the given sub-tree. + The tree must be binary. + _binode: The root of the given sub-tree. + _binode->nbits must be 0 or 1. + Return: The smallest depth of a leaf node in this sub-tree. + 0 indicates this sub-tree is a leaf node.*/ +static int oc_huff_tree_mindepth(oc_huff_node *_binode){ + int depth0; + int depth1; + if(_binode->nbits==0)return 0; + depth0=oc_huff_tree_mindepth(_binode->nodes[0]); + depth1=oc_huff_tree_mindepth(_binode->nodes[1]); + return OC_MINI(depth0,depth1)+1; +} + +/*Finds the number of internal nodes at a given depth, plus the number of + leaves at that depth or shallower. + The tree must be binary. + _binode: The root of the given sub-tree. + _binode->nbits must be 0 or 1. + Return: The number of entries that would be contained in a jump table of the + given depth.*/ +static int oc_huff_tree_occupancy(oc_huff_node *_binode,int _depth){ + if(_binode->nbits==0||_depth<=0)return 1; + else{ + return oc_huff_tree_occupancy(_binode->nodes[0],_depth-1)+ + oc_huff_tree_occupancy(_binode->nodes[1],_depth-1); + } +} + +/*Makes a copy of the given Huffman tree. + _node: The Huffman tree to copy. + Return: The copy of the Huffman tree.*/ +static oc_huff_node *oc_huff_tree_copy(const oc_huff_node *_node, + char **_storage){ + oc_huff_node *ret; + ret=oc_huff_node_init(_storage,oc_huff_node_size(_node->nbits),_node->nbits); + ret->depth=_node->depth; + if(_node->nbits){ + int nchildren; + int i; + int inext; + nchildren=1<<_node->nbits; + for(i=0;inodes[i]=oc_huff_tree_copy(_node->nodes[i],_storage); + inext=i+(1<<_node->nbits-ret->nodes[i]->depth); + while(++inodes[i]=ret->nodes[i-1]; + } + } + else ret->token=_node->token; + return ret; +} + +static size_t oc_huff_tree_collapse_size(oc_huff_node *_binode,int _depth){ + size_t size; + int mindepth; + int depth; + int loccupancy; + int occupancy; + if(_binode->nbits!=0&&_depth>0){ + return oc_huff_tree_collapse_size(_binode->nodes[0],_depth-1)+ + oc_huff_tree_collapse_size(_binode->nodes[1],_depth-1); + } + depth=mindepth=oc_huff_tree_mindepth(_binode); + occupancy=1<loccupancy&&occupancy>=1<0){ + size+=oc_huff_tree_collapse_size(_binode->nodes[0],depth-1); + size+=oc_huff_tree_collapse_size(_binode->nodes[1],depth-1); + } + return size; +} + +static oc_huff_node *oc_huff_tree_collapse(oc_huff_node *_binode, + char **_storage); + +/*Fills the given nodes table with all the children in the sub-tree at the + given depth. + The nodes in the sub-tree with a depth less than that stored in the table + are freed. + The sub-tree must be binary and complete up until the given depth. + _nodes: The nodes table to fill. + _binode: The root of the sub-tree to fill it with. + _binode->nbits must be 0 or 1. + _level: The current level in the table. + 0 indicates that the current node should be stored, regardless of + whether it is a leaf node or an internal node. + _depth: The depth of the nodes to fill the table with, relative to their + parent.*/ +static void oc_huff_node_fill(oc_huff_node **_nodes, + oc_huff_node *_binode,int _level,int _depth,char **_storage){ + if(_level<=0||_binode->nbits==0){ + int i; + _binode->depth=(unsigned char)(_depth-_level); + _nodes[0]=oc_huff_tree_collapse(_binode,_storage); + for(i=1;i<1<<_level;i++)_nodes[i]=_nodes[0]; + } + else{ + _level--; + oc_huff_node_fill(_nodes,_binode->nodes[0],_level,_depth,_storage); + _nodes+=1<<_level; + oc_huff_node_fill(_nodes,_binode->nodes[1],_level,_depth,_storage); + } +} + +/*Finds the largest complete sub-tree rooted at the current node and collapses + it into a single node. + This procedure is then applied recursively to all the children of that node. + _binode: The root of the sub-tree to collapse. + _binode->nbits must be 0 or 1. + Return: The new root of the collapsed sub-tree.*/ +static oc_huff_node *oc_huff_tree_collapse(oc_huff_node *_binode, + char **_storage){ + oc_huff_node *root; + size_t size; + int mindepth; + int depth; + int loccupancy; + int occupancy; + depth=mindepth=oc_huff_tree_mindepth(_binode); + occupancy=1<loccupancy&&occupancy>=1<depth=_binode->depth; + oc_huff_node_fill(root->nodes,_binode,depth,depth,_storage); + return root; +} + +/*Unpacks a set of Huffman trees, and reduces them to a collapsed + representation. + _opb: The buffer to unpack the trees from. + _nodes: The table to fill with the Huffman trees. + Return: 0 on success, or a negative value on error.*/ +int oc_huff_trees_unpack(oc_pack_buf *_opb, + oc_huff_node *_nodes[TH_NHUFFMAN_TABLES]){ + int i; + for(i=0;i0)_ogg_free(_dst[i]); + return TH_EFAULT; + } + _dst[i]=oc_huff_tree_copy(_src[i],&storage); + } + return 0; +} + +/*Frees the memory used by a set of Huffman trees. + _nodes: The array of trees to free.*/ +void oc_huff_trees_clear(oc_huff_node *_nodes[TH_NHUFFMAN_TABLES]){ + int i; + for(i=0;inbits!=0){ + bits=oc_pack_look(_opb,_node->nbits); + _node=_node->nodes[bits]; + oc_pack_adv(_opb,_node->depth); + } + return _node->token; +} diff --git a/Engine/lib/libtheora/lib/dec/huffdec.h b/Engine/lib/libtheora/lib/huffdec.h similarity index 91% rename from Engine/lib/libtheora/lib/dec/huffdec.h rename to Engine/lib/libtheora/lib/huffdec.h index cc87b4092..d7ffa0e99 100644 --- a/Engine/lib/libtheora/lib/dec/huffdec.h +++ b/Engine/lib/libtheora/lib/huffdec.h @@ -5,19 +5,20 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: huffdec.h 15400 2008-10-15 12:10:58Z tterribe $ + last mod: $Id: huffdec.h 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ #if !defined(_huffdec_H) # define _huffdec_H (1) # include "huffman.h" +# include "bitpack.h" @@ -75,17 +76,17 @@ struct oc_huff_node{ The ACTUAL size of this array is 1< #include -#include "theora/theoraenc.h" -#include "theora/theora.h" -#include "codec_internal.h" -#include "../dec/ocintrin.h" - -/*Wrapper to translate the new API into the old API. - Eventually we need to convert the old functions to support the new API - natively and do the translation the other way. - theora-exp already the necessary code to do so.*/ +#include +#include "huffenc.h" -/*The default Huffman codes used for VP3.1. - It's kind of useless to include this, as TH_ENCCTL_SET_HUFFMAN_CODES is not - actually implemented in the old encoder, but it's part of the public API.*/ +/*The default Huffman codes used for VP3.1.*/ const th_huff_code TH_VP31_HUFF_CODES[TH_NHUFFMAN_TABLES][TH_NDCT_TOKENS]={ { {0x002D, 6},{0x0026, 7},{0x0166, 9},{0x004E, 8}, @@ -819,323 +811,100 @@ const th_huff_code TH_VP31_HUFF_CODES[TH_NHUFFMAN_TABLES][TH_NDCT_TOKENS]={ -static void th_info2theora_info(theora_info *_ci,const th_info *_info){ - _ci->version_major=_info->version_major; - _ci->version_minor=_info->version_minor; - _ci->version_subminor=_info->version_subminor; - _ci->width=_info->frame_width; - _ci->height=_info->frame_height; - _ci->frame_width=_info->pic_width; - _ci->frame_height=_info->pic_height; - _ci->offset_x=_info->pic_x; - _ci->offset_y=_info->pic_y; - _ci->fps_numerator=_info->fps_numerator; - _ci->fps_denominator=_info->fps_denominator; - _ci->aspect_numerator=_info->aspect_numerator; - _ci->aspect_denominator=_info->aspect_denominator; - switch(_info->colorspace){ - case TH_CS_ITU_REC_470M:_ci->colorspace=OC_CS_ITU_REC_470M;break; - case TH_CS_ITU_REC_470BG:_ci->colorspace=OC_CS_ITU_REC_470BG;break; - default:_ci->colorspace=OC_CS_UNSPECIFIED;break; - } - switch(_info->pixel_fmt){ - case TH_PF_420:_ci->pixelformat=OC_PF_420;break; - case TH_PF_422:_ci->pixelformat=OC_PF_422;break; - case TH_PF_444:_ci->pixelformat=OC_PF_444;break; - default:_ci->pixelformat=OC_PF_RSVD; - } - _ci->target_bitrate=_info->target_bitrate; - _ci->quality=_info->quality; - _ci->codec_setup=NULL; - /*Defaults from old encoder_example... eventually most of these should go - away when we make the encoder no longer use them.*/ - _ci->dropframes_p=0; - _ci->keyframe_auto_p=1; - _ci->keyframe_frequency=1<<_info->keyframe_granule_shift; - _ci->keyframe_frequency_force=1<<_info->keyframe_granule_shift; - _ci->keyframe_data_target_bitrate= - _info->target_bitrate+(_info->target_bitrate>>1); - _ci->keyframe_auto_threshold=80; - _ci->keyframe_mindistance=8; - _ci->noise_sensitivity=1; - _ci->sharpness=0; - _ci->quick_p=1; +/*A description of a Huffman code value used when encoding the tree.*/ +typedef struct{ + /*The bit pattern, left-shifted so that the MSB of all patterns is + aligned.*/ + ogg_uint32_t pattern; + /*The amount the bit pattern was shifted.*/ + int shift; + /*The token this bit pattern represents.*/ + int token; +}oc_huff_entry; + + + +/*Compares two oc_huff_entry structures by their bit patterns. + _c1: The first entry to compare. + _c2: The second entry to compare. + Return: <0 if _c1<_c2, >0 if _c1>_c2.*/ +static int huff_entry_cmp(const void *_c1,const void *_c2){ + ogg_uint32_t b1; + ogg_uint32_t b2; + b1=((const oc_huff_entry *)_c1)->pattern; + b2=((const oc_huff_entry *)_c2)->pattern; + return b1b2?1:0; } -static int _ilog(unsigned _v){ - int ret; - for(ret=0;_v;ret++)_v>>=1; - return ret; -} - - - -struct th_enc_ctx{ - /*This is required at the start of the struct for the common functions to - work.*/ - th_info info; - /*The actual encoder.*/ - theora_state state; - /*A temporary buffer for input frames. - This is needed if the U and V strides differ, or padding is required.*/ - unsigned char *buf; -}; - - -th_enc_ctx *th_encode_alloc(const th_info *_info){ - theora_info ci; - th_enc_ctx *enc; - th_info2theora_info(&ci,_info); - /*Do a bunch of checks the new API does, but the old one didn't.*/ - if((_info->frame_width&0xF)||(_info->frame_height&0xF)|| - _info->frame_width>=0x100000||_info->frame_height>=0x100000|| - _info->pic_x+_info->pic_width>_info->frame_width|| - _info->pic_y+_info->pic_height>_info->frame_height|| - _info->pic_x>255|| - _info->frame_height-_info->pic_height-_info->pic_y>255|| - _info->colorspace<0||_info->colorspace>=TH_CS_NSPACES|| - _info->pixel_fmt<0||_info->pixel_fmt>=TH_PF_NFORMATS){ - enc=NULL; - } - else{ - enc=(th_enc_ctx *)_ogg_malloc(sizeof(*enc)); - if(theora_encode_init(&enc->state,&ci)<0){ - _ogg_free(enc); - enc=NULL; +/*Encodes a description of the given Huffman tables. + Although the codes are stored in the encoder as flat arrays, in the bit + stream and in the decoder they are structured as a tree. + This function recovers the tree structure from the flat array and then + writes it out. + Note that the codes MUST form a Huffman code, and not merely a prefix-free + code, since the binary tree is assumed to be full. + _opb: The buffer to store the tree in. + _codes: The Huffman tables to pack. + Return: 0 on success, or a negative value if one of the given Huffman tables + does not form a full, prefix-free code.*/ +int oc_huff_codes_pack(oggpack_buffer *_opb, + const th_huff_code _codes[TH_NHUFFMAN_TABLES][TH_NDCT_TOKENS]){ + int i; + for(i=0;iframe_width>_info->pic_width|| - _info->frame_height>_info->pic_height){ - enc->buf=_ogg_malloc((_info->frame_width*_info->frame_height+ - ((_info->frame_width>>!(_info->pixel_fmt&1))* - (_info->frame_height>>!(_info->pixel_fmt&2))<<1))*sizeof(*enc->buf)); - } - else enc->buf=NULL; - memcpy(&enc->info,_info,sizeof(enc->info)); - /*Overwrite values theora_encode_init() can change; don't trust the user.*/ - enc->info.version_major=ci.version_major; - enc->info.version_minor=ci.version_minor; - enc->info.version_subminor=ci.version_subminor; - enc->info.quality=ci.quality; - enc->info.target_bitrate=ci.target_bitrate; - enc->info.fps_numerator=ci.fps_numerator; - enc->info.fps_denominator=ci.fps_denominator; - enc->info.keyframe_granule_shift=_ilog(ci.keyframe_frequency_force-1); + mask=(1<<(maxlen>>1)<<(maxlen+1>>1))-1; + /*Copy over the codes into our temporary workspace. + The bit patterns are aligned, and the original entry each code is from + is stored as well.*/ + for(j=0;jstate,_req,_buf,_buf_sz); -} - -int th_encode_flushheader(th_enc_ctx *_enc,th_comment *_comments, - ogg_packet *_op){ - theora_state *te; - CP_INSTANCE *cpi; - if(_enc==NULL||_op==NULL)return OC_FAULT; - te=&_enc->state; - cpi=(CP_INSTANCE *)te->internal_encode; - switch(cpi->doneflag){ - case -3:{ - theora_encode_header(te,_op); - return -cpi->doneflag++; - }break; - case -2:{ - if(_comments==NULL)return OC_FAULT; - theora_encode_comment((theora_comment *)_comments,_op); - /*The old API does not require a theora_state struct when writing the - comment header, so it can't use its internal buffer and relies on the - application to free it. - The old documentation is wrong on this subject, and this breaks on - Windows when linking against multiple versions of libc (which is - almost always done when, e.g., using DLLs built with mingw32). - The new API _does_ require a th_enc_ctx, and states that libtheora owns - the memory. - Thus we move the contents of this packet into our internal - oggpack_buffer so it can be properly reclaimed.*/ - oggpackB_reset(cpi->oggbuffer); - oggpackB_writecopy(cpi->oggbuffer,_op->packet,_op->bytes*8); - _ogg_free(_op->packet); - _op->packet=oggpackB_get_buffer(cpi->oggbuffer); - return -cpi->doneflag++; - }break; - case -1:{ - theora_encode_tables(te,_op); - return -cpi->doneflag++; - }break; - case 0:return 0; - default:return OC_EINVAL; - } -} - -/*Copies the picture region of the _src image plane into _dst and pads the rest - of _dst using a diffusion extension method. - We could do much better (e.g., the DCT-based low frequency extension method - in theora-exp's fdct.c) if we were to pad after motion compensation, but - that would require significant changes to the encoder.*/ -static unsigned char *th_encode_copy_pad_plane(th_img_plane *_dst, - unsigned char *_buf,th_img_plane *_src, - ogg_uint32_t _pic_x,ogg_uint32_t _pic_y, - ogg_uint32_t _pic_width,ogg_uint32_t _pic_height){ - size_t buf_sz; - _dst->width=_src->width; - _dst->height=_src->height; - _dst->stride=_src->width; - _dst->data=_buf; - buf_sz=_dst->width*_dst->height*sizeof(*_dst->data); - /*If we have _no_ data, just encode a dull green.*/ - if(_pic_width==0||_pic_height==0)memset(_dst->data,0,buf_sz); - else{ - unsigned char *dst; - unsigned char *src; - ogg_uint32_t x; - ogg_uint32_t y; - int dstride; - int sstride; - /*Step 1: Copy the data we do have.*/ - dstride=_dst->stride; - sstride=_src->stride; - dst=_dst->data+_pic_y*dstride+_pic_x; - src=_src->data+_pic_y*sstride+_pic_x; - for(y=0;y<_pic_height;y++){ - memcpy(dst,src,_pic_width); - dst+=dstride; - src+=sstride; - } - /*Step 2: Copy the border into any blocks that are 100% padding. - There's probably smarter things we could do than this.*/ - /*Left side.*/ - for(x=_pic_x;x-->0;){ - dst=_dst->data+_pic_y*dstride+x; - for(y=0;y<_pic_height;y++){ - dst[0]=(dst[1]<<1)+(dst-(dstride&-(y>0)))[1]+ - (dst+(dstride&-(y+1<_pic_height)))[1]+2>>2; - dst+=dstride; + /*Sort the codes into ascending order. + This is the order the leaves of the tree will be traversed.*/ + qsort(entries,TH_NDCT_TOKENS,sizeof(entries[0]),huff_entry_cmp); + /*For each leaf of the tree:*/ + bpos=maxlen; + for(j=0;jentries[j].shift;bpos--)oggpackB_write(_opb,0,1); + /*Mark this as a leaf node, and write its value.*/ + oggpackB_write(_opb,1,1); + oggpackB_write(_opb,entries[j].token,5); + /*For each 1 branch we've descended, back up the tree until we reach a + 0 branch.*/ + bit=1<width;x++){ - dst=_dst->data+_pic_y*dstride+x-1; - for(y=0;y<_pic_height;y++){ - dst[1]=(dst[0]<<1)+(dst-(dstride&-(y>0)))[0]+ - (dst+(dstride&-(y+1<_pic_height)))[0]+2>>2; - dst+=dstride; - } - } - /*Top.*/ - dst=_dst->data+_pic_y*dstride; - for(y=_pic_y;y-->0;){ - for(x=0;x<_dst->width;x++){ - (dst-dstride)[x]=(dst[x]<<1)+dst[x-(x>0)]+dst[x+(x+1<_dst->width)]+2>>2; - } - dst-=dstride; - } - /*Bottom.*/ - dst=_dst->data+(_pic_y+_pic_height)*dstride; - for(y=_pic_y+_pic_height;y<_dst->height;y++){ - for(x=0;x<_dst->width;x++){ - dst[x]=((dst-dstride)[x]<<1)+(dst-dstride)[x-(x>0)]+ - (dst-dstride)[x+(x+1<_dst->width)]+2>>2; - } - dst+=dstride; - } - } - _buf+=buf_sz; - return _buf; -} - -int th_encode_ycbcr_in(th_enc_ctx *_enc,th_ycbcr_buffer _ycbcr){ - CP_INSTANCE *cpi; - theora_state *te; - th_img_plane *pycbcr; - th_ycbcr_buffer ycbcr; - yuv_buffer yuv; - ogg_uint32_t pic_width; - ogg_uint32_t pic_height; - int hdec; - int vdec; - int ret; - if(_enc==NULL||_ycbcr==NULL)return OC_FAULT; - te=&_enc->state; - /*theora_encode_YUVin() does not bother to check uv_width and uv_height, and - then uses them. - This is arguably okay (it will most likely lead to a crash if they're - wrong, which will make the developer who passed them fix the problem), but - our API promises to return an error code instead.*/ - cpi=(CP_INSTANCE *)te->internal_encode; - hdec=!(cpi->pb.info.pixelformat&1); - vdec=!(cpi->pb.info.pixelformat&2); - if(_ycbcr[0].width!=cpi->pb.info.width|| - _ycbcr[0].height!=cpi->pb.info.height|| - _ycbcr[1].width!=_ycbcr[0].width>>hdec|| - _ycbcr[1].height!=_ycbcr[0].height>>vdec|| - _ycbcr[2].width!=_ycbcr[1].width||_ycbcr[2].height!=_ycbcr[1].height){ - return OC_EINVAL; - } - pic_width=cpi->pb.info.frame_width; - pic_height=cpi->pb.info.frame_height; - /*We can only directly use the input buffer if no padding is required (since - the new API is documented not to use values outside the picture region) - and if the strides for the Cb and Cr planes are the same, since the old - API had no way to specify different ones.*/ - if(_ycbcr[0].width==pic_width&&_ycbcr[0].height==pic_height&& - _ycbcr[1].stride==_ycbcr[2].stride){ - pycbcr=_ycbcr; - } - else{ - unsigned char *buf; - int pic_x; - int pic_y; - int pli; - pic_x=cpi->pb.info.offset_x; - pic_y=cpi->pb.info.offset_y; - if(_ycbcr[0].width>pic_width||_ycbcr[0].height>pic_height){ - buf=th_encode_copy_pad_plane(ycbcr+0,_enc->buf,_ycbcr+0, - pic_x,pic_y,pic_width,pic_height); - } - else{ - /*If only the strides differ, we can still avoid copying the luma plane.*/ - memcpy(ycbcr+0,_ycbcr+0,sizeof(ycbcr[0])); - if(_enc->buf==NULL){ - _enc->buf=(unsigned char *)_ogg_malloc( - (_ycbcr[1].width*_ycbcr[1].height<<1)*sizeof(*_enc->buf)); - } - buf=_enc->buf; - } - for(pli=1;pli<3;pli++){ - int x0; - int y0; - x0=pic_x>>hdec; - y0=pic_y>>vdec; - buf=th_encode_copy_pad_plane(ycbcr+pli,buf,_ycbcr+pli, - x0,y0,(pic_x+pic_width+hdec>>hdec)-x0,(pic_y+pic_height+vdec>>vdec)-y0); - } - pycbcr=ycbcr; - } - yuv.y_width=pycbcr[0].width; - yuv.y_height=pycbcr[0].height; - yuv.uv_width=pycbcr[1].width; - yuv.uv_height=pycbcr[1].height; - yuv.y_stride=pycbcr[0].stride; - yuv.y=pycbcr[0].data; - yuv.uv_stride=pycbcr[1].stride; - yuv.u=pycbcr[1].data; - yuv.v=pycbcr[2].data; - ret=theora_encode_YUVin(te,&yuv); - return ret; -} - -int th_encode_packetout(th_enc_ctx *_enc,int _last,ogg_packet *_op){ - if(_enc==NULL)return OC_FAULT; - return theora_encode_packetout(&_enc->state,_last,_op); -} - -void th_encode_free(th_enc_ctx *_enc){ - if(_enc!=NULL){ - theora_clear(&_enc->state); - _ogg_free(_enc->buf); - _ogg_free(_enc); } + return 0; } diff --git a/Engine/lib/libtheora/lib/huffenc.h b/Engine/lib/libtheora/lib/huffenc.h new file mode 100644 index 000000000..c5a3956f1 --- /dev/null +++ b/Engine/lib/libtheora/lib/huffenc.h @@ -0,0 +1,19 @@ +#if !defined(_huffenc_H) +# define _huffenc_H (1) +# include "huffman.h" + + + +typedef th_huff_code th_huff_table[TH_NDCT_TOKENS]; + + + +extern const th_huff_code + TH_VP31_HUFF_CODES[TH_NHUFFMAN_TABLES][TH_NDCT_TOKENS]; + + + +int oc_huff_codes_pack(oggpack_buffer *_opb, + const th_huff_code _codes[TH_NHUFFMAN_TABLES][TH_NDCT_TOKENS]); + +#endif diff --git a/Engine/lib/libtheora/lib/dec/huffman.h b/Engine/lib/libtheora/lib/huffman.h similarity index 92% rename from Engine/lib/libtheora/lib/dec/huffman.h rename to Engine/lib/libtheora/lib/huffman.h index 59096e1e8..36cf7572e 100644 --- a/Engine/lib/libtheora/lib/dec/huffman.h +++ b/Engine/lib/libtheora/lib/huffman.h @@ -5,13 +5,13 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: huffman.h 15400 2008-10-15 12:10:58Z tterribe $ + last mod: $Id: huffman.h 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ @@ -65,6 +65,6 @@ #define OC_NDCT_RUN_MAX (32) #define OC_NDCT_RUN_CAT1A_MAX (28) -extern const int OC_DCT_TOKEN_EXTRA_BITS[TH_NDCT_TOKENS]; +extern const unsigned char OC_DCT_TOKEN_EXTRA_BITS[TH_NDCT_TOKENS]; #endif diff --git a/Engine/lib/libtheora/lib/dec/idct.c b/Engine/lib/libtheora/lib/idct.c similarity index 70% rename from Engine/lib/libtheora/lib/dec/idct.c rename to Engine/lib/libtheora/lib/idct.c index 21ac83f14..0e68ac765 100644 --- a/Engine/lib/libtheora/lib/dec/idct.c +++ b/Engine/lib/libtheora/lib/idct.c @@ -5,20 +5,19 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: idct.c 15400 2008-10-15 12:10:58Z tterribe $ + last mod: $Id: idct.c 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ #include -#include +#include "internal.h" #include "dct.h" -#include "idct.h" /*Performs an inverse 8 point Type-II DCT transform. The output is scaled by a factor of 2 relative to the orthonormal version of @@ -220,19 +219,29 @@ static void idct8_1(ogg_int16_t *_y,const ogg_int16_t _x[1]){ /*Performs an inverse 8x8 Type-II DCT transform. The input is assumed to be scaled by a factor of 4 relative to orthonormal version of the transform. + All coefficients but the first 3 in zig-zag scan order are assumed to be 0: + x x 0 0 0 0 0 0 + x 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 + 0 0 0 0 0 0 0 0 _y: The buffer to store the result in. This may be the same as _x. - _x: The input coefficients. */ -void oc_idct8x8_c(ogg_int16_t _y[64],const ogg_int16_t _x[64]){ + _x: The input coefficients.*/ +static void oc_idct8x8_3(ogg_int16_t _y[64],const ogg_int16_t _x[64]){ const ogg_int16_t *in; ogg_int16_t *end; ogg_int16_t *out; ogg_int16_t w[64]; /*Transform rows of x into columns of w.*/ - for(in=_x,out=w,end=out+8;out>4); } @@ -250,8 +259,8 @@ void oc_idct8x8_c(ogg_int16_t _y[64],const ogg_int16_t _x[64]){ 0 0 0 0 0 0 0 0 _y: The buffer to store the result in. This may be the same as _x. - _x: The input coefficients. */ -void oc_idct8x8_10_c(ogg_int16_t _y[64],const ogg_int16_t _x[64]){ + _x: The input coefficients.*/ +static void oc_idct8x8_10(ogg_int16_t _y[64],const ogg_int16_t _x[64]){ const ogg_int16_t *in; ogg_int16_t *end; ogg_int16_t *out; @@ -263,6 +272,64 @@ void oc_idct8x8_10_c(ogg_int16_t _y[64],const ogg_int16_t _x[64]){ idct8_1(w+3,_x+24); /*Transform rows of w into columns of y.*/ for(in=w,out=_y,end=out+8;out>4); } + +/*Performs an inverse 8x8 Type-II DCT transform. + The input is assumed to be scaled by a factor of 4 relative to orthonormal + version of the transform. + _y: The buffer to store the result in. + This may be the same as _x. + _x: The input coefficients.*/ +static void oc_idct8x8_slow(ogg_int16_t _y[64],const ogg_int16_t _x[64]){ + const ogg_int16_t *in; + ogg_int16_t *end; + ogg_int16_t *out; + ogg_int16_t w[64]; + /*Transform rows of x into columns of w.*/ + for(in=_x,out=w,end=out+8;out>4); +} + +void oc_idct8x8(const oc_theora_state *_state,ogg_int16_t _y[64], + int _last_zzi){ + (*_state->opt_vtable.idct8x8)(_y,_last_zzi); +} + +/*Performs an inverse 8x8 Type-II DCT transform. + The input is assumed to be scaled by a factor of 4 relative to orthonormal + version of the transform.*/ +void oc_idct8x8_c(ogg_int16_t _y[64],int _last_zzi){ + /*_last_zzi is subtly different from an actual count of the number of + coefficients we decoded for this block. + It contains the value of zzi BEFORE the final token in the block was + decoded. + In most cases this is an EOB token (the continuation of an EOB run from a + previous block counts), and so this is the same as the coefficient count. + However, in the case that the last token was NOT an EOB token, but filled + the block up with exactly 64 coefficients, _last_zzi will be less than 64. + Provided the last token was not a pure zero run, the minimum value it can + be is 46, and so that doesn't affect any of the cases in this routine. + However, if the last token WAS a pure zero run of length 63, then _last_zzi + will be 1 while the number of coefficients decoded is 64. + Thus, we will trigger the following special case, where the real + coefficient count would not. + Note also that a zero run of length 64 will give _last_zzi a value of 0, + but we still process the DC coefficient, which might have a non-zero value + due to DC prediction. + Although convoluted, this is arguably the correct behavior: it allows us to + use a smaller transform when the block ends with a long zero run instead + of a normal EOB token. + It could be smarter... multiple separate zero runs at the end of a block + will fool it, but an encoder that generates these really deserves what it + gets. + Needless to say we inherited this approach from VP3.*/ + /*Then perform the iDCT.*/ + if(_last_zzi<3)oc_idct8x8_3(_y,_y); + else if(_last_zzi<10)oc_idct8x8_10(_y,_y); + else oc_idct8x8_slow(_y,_y); +} diff --git a/Engine/lib/libtheora/lib/dec/info.c b/Engine/lib/libtheora/lib/info.c similarity index 83% rename from Engine/lib/libtheora/lib/dec/info.c rename to Engine/lib/libtheora/lib/info.c index 26e7f42a9..6b9762978 100644 --- a/Engine/lib/libtheora/lib/dec/info.c +++ b/Engine/lib/libtheora/lib/info.c @@ -5,20 +5,20 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: info.c 15400 2008-10-15 12:10:58Z tterribe $ + last mod: $Id: info.c 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ #include #include #include -#include "../internal.h" +#include "internal.h" @@ -55,14 +55,21 @@ void th_comment_init(th_comment *_tc){ } void th_comment_add(th_comment *_tc,char *_comment){ - int comment_len; - _tc->user_comments=_ogg_realloc(_tc->user_comments, + char **user_comments; + int *comment_lengths; + int comment_len; + user_comments=_ogg_realloc(_tc->user_comments, (_tc->comments+2)*sizeof(*_tc->user_comments)); - _tc->comment_lengths=_ogg_realloc(_tc->comment_lengths, + if(user_comments==NULL)return; + _tc->user_comments=user_comments; + comment_lengths=_ogg_realloc(_tc->comment_lengths, (_tc->comments+2)*sizeof(*_tc->comment_lengths)); + if(comment_lengths==NULL)return; + _tc->comment_lengths=comment_lengths; comment_len=strlen(_comment); - _tc->comment_lengths[_tc->comments]=comment_len; - _tc->user_comments[_tc->comments]=_ogg_malloc(comment_len+1); + comment_lengths[_tc->comments]=comment_len; + user_comments[_tc->comments]=_ogg_malloc(comment_len+1); + if(user_comments[_tc->comments]==NULL)return; memcpy(_tc->user_comments[_tc->comments],_comment,comment_len+1); _tc->comments++; _tc->user_comments[_tc->comments]=NULL; @@ -76,6 +83,7 @@ void th_comment_add_tag(th_comment *_tc,char *_tag,char *_val){ val_len=strlen(_val); /*+2 for '=' and '\0'.*/ comment=_ogg_malloc(tag_len+val_len+2); + if(comment==NULL)return; memcpy(comment,_tag,tag_len); comment[tag_len]='='; memcpy(comment+tag_len+1,_val,val_len+1); diff --git a/Engine/lib/libtheora/lib/dec/internal.c b/Engine/lib/libtheora/lib/internal.c similarity index 62% rename from Engine/lib/libtheora/lib/dec/internal.c rename to Engine/lib/libtheora/lib/internal.c index 3fe62e55b..0fe4f63e7 100644 --- a/Engine/lib/libtheora/lib/dec/internal.c +++ b/Engine/lib/libtheora/lib/internal.c @@ -5,21 +5,20 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: internal.c 15400 2008-10-15 12:10:58Z tterribe $ + last mod: $Id: internal.c 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ #include #include #include -#include "../internal.h" -#include "idct.h" +#include "internal.h" @@ -27,7 +26,7 @@ block. All zig zag indices beyond 63 are sent to coefficient 64, so that zero runs past the end of a block in bogus streams get mapped to a known location.*/ -const int OC_FZIG_ZAG[128]={ +const unsigned char OC_FZIG_ZAG[128]={ 0, 1, 8,16, 9, 2, 3,10, 17,24,32,25,18,11, 4, 5, 12,19,26,33,40,48,41,34, @@ -48,7 +47,7 @@ const int OC_FZIG_ZAG[128]={ /*A map from the coefficient number in a block to its index in the zig zag scan.*/ -const int OC_IZIG_ZAG[64]={ +const unsigned char OC_IZIG_ZAG[64]={ 0, 1, 5, 6,14,15,27,28, 2, 4, 7,13,16,26,29,42, 3, 8,12,17,25,30,41,43, @@ -59,33 +58,13 @@ const int OC_IZIG_ZAG[64]={ 35,36,48,49,57,58,62,63 }; -/*The predictor frame to use for each macro block mode.*/ -const int OC_FRAME_FOR_MODE[8]={ - /*OC_MODE_INTER_NOMV*/ - OC_FRAME_PREV, - /*OC_MODE_INTRA*/ - OC_FRAME_SELF, - /*OC_MODE_INTER_MV*/ - OC_FRAME_PREV, - /*OC_MODE_INTER_MV_LAST*/ - OC_FRAME_PREV, - /*OC_MODE_INTER_MV_LAST2*/ - OC_FRAME_PREV, - /*OC_MODE_GOLDEN*/ - OC_FRAME_GOLD, - /*OC_MODE_GOLDEN_MV*/ - OC_FRAME_GOLD, - /*OC_MODE_INTER_MV_FOUR*/ - OC_FRAME_PREV, -}; - /*A map from physical macro block ordering to bitstream macro block ordering within a super block.*/ -const int OC_MB_MAP[2][2]={{0,3},{1,2}}; +const unsigned char OC_MB_MAP[2][2]={{0,3},{1,2}}; /*A list of the indices in the oc_mb.map array that can be valid for each of the various chroma decimation types.*/ -const int OC_MB_MAP_IDXS[TH_PF_NFORMATS][12]={ +const unsigned char OC_MB_MAP_IDXS[TH_PF_NFORMATS][12]={ {0,1,2,3,4,8}, {0,1,2,3,4,5,8,9}, {0,1,2,3,4,6,8,10}, @@ -94,13 +73,13 @@ const int OC_MB_MAP_IDXS[TH_PF_NFORMATS][12]={ /*The number of indices in the oc_mb.map array that can be valid for each of the various chroma decimation types.*/ -const int OC_MB_MAP_NIDXS[TH_PF_NFORMATS]={6,8,8,12}; +const unsigned char OC_MB_MAP_NIDXS[TH_PF_NFORMATS]={6,8,8,12}; /*The number of extra bits that are coded with each of the DCT tokens. Each DCT token has some fixed number of additional bits (possibly 0) stored after the token itself, containing, for example, coefficient magnitude, sign bits, etc.*/ -const int OC_DCT_TOKEN_EXTRA_BITS[TH_NDCT_TOKENS]={ +const unsigned char OC_DCT_TOKEN_EXTRA_BITS[TH_NDCT_TOKENS]={ 0,0,0,2,3,4,12,3,6, 0,0,0,0, 1,1,1,1,2,3,4,5,6,10, @@ -118,113 +97,10 @@ int oc_ilog(unsigned _v){ -/*Determines the number of blocks or coefficients to be skipped for a given - token value. - _token: The token value to skip. - _extra_bits: The extra bits attached to this token. - Return: A positive value indicates that number of coefficients are to be - skipped in the current block. - Otherwise, the negative of the return value indicates that number of - blocks are to be ended.*/ -typedef int (*oc_token_skip_func)(int _token,int _extra_bits); - -/*Handles the simple end of block tokens.*/ -static int oc_token_skip_eob(int _token,int _extra_bits){ - static const int NBLOCKS_ADJUST[OC_NDCT_EOB_TOKEN_MAX]={1,2,3,4,8,16,0}; - return -_extra_bits-NBLOCKS_ADJUST[_token]; -} - -/*The last EOB token has a special case, where an EOB run of size zero ends all - the remaining blocks in the frame.*/ -static int oc_token_skip_eob6(int _token,int _extra_bits){ - if(!_extra_bits)return -INT_MAX; - return -_extra_bits; -} - -/*Handles the pure zero run tokens.*/ -static int oc_token_skip_zrl(int _token,int _extra_bits){ - return _extra_bits+1; -} - -/*Handles a normal coefficient value token.*/ -static int oc_token_skip_val(void){ - return 1; -} - -/*Handles a category 1A zero run/coefficient value combo token.*/ -static int oc_token_skip_run_cat1a(int _token){ - return _token-OC_DCT_RUN_CAT1A+2; -} - -/*Handles category 1b and 2 zero run/coefficient value combo tokens.*/ -static int oc_token_skip_run(int _token,int _extra_bits){ - static const int NCOEFFS_ADJUST[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ - 7,11,2,3 - }; - static const int NCOEFFS_MASK[OC_NDCT_RUN_MAX-OC_DCT_RUN_CAT1B]={ - 3,7,0,1 - }; - _token-=OC_DCT_RUN_CAT1B; - return (_extra_bits&NCOEFFS_MASK[_token])+NCOEFFS_ADJUST[_token]; -} - -/*A jump table for computing the number of coefficients or blocks to skip for - a given token value. - This reduces all the conditional branches, etc., needed to parse these token - values down to one indirect jump.*/ -static const oc_token_skip_func OC_TOKEN_SKIP_TABLE[TH_NDCT_TOKENS]={ - oc_token_skip_eob, - oc_token_skip_eob, - oc_token_skip_eob, - oc_token_skip_eob, - oc_token_skip_eob, - oc_token_skip_eob, - oc_token_skip_eob6, - oc_token_skip_zrl, - oc_token_skip_zrl, - (oc_token_skip_func)oc_token_skip_val, - (oc_token_skip_func)oc_token_skip_val, - (oc_token_skip_func)oc_token_skip_val, - (oc_token_skip_func)oc_token_skip_val, - (oc_token_skip_func)oc_token_skip_val, - (oc_token_skip_func)oc_token_skip_val, - (oc_token_skip_func)oc_token_skip_val, - (oc_token_skip_func)oc_token_skip_val, - (oc_token_skip_func)oc_token_skip_val, - (oc_token_skip_func)oc_token_skip_val, - (oc_token_skip_func)oc_token_skip_val, - (oc_token_skip_func)oc_token_skip_val, - (oc_token_skip_func)oc_token_skip_val, - (oc_token_skip_func)oc_token_skip_val, - (oc_token_skip_func)oc_token_skip_run_cat1a, - (oc_token_skip_func)oc_token_skip_run_cat1a, - (oc_token_skip_func)oc_token_skip_run_cat1a, - (oc_token_skip_func)oc_token_skip_run_cat1a, - (oc_token_skip_func)oc_token_skip_run_cat1a, - oc_token_skip_run, - oc_token_skip_run, - oc_token_skip_run, - oc_token_skip_run -}; - -/*Determines the number of blocks or coefficients to be skipped for a given - token value. - _token: The token value to skip. - _extra_bits: The extra bits attached to this token. - Return: A positive value indicates that number of coefficients are to be - skipped in the current block. - Otherwise, the negative of the return value indicates that number of - blocks are to be ended. - 0 will never be returned, so that at least one coefficient in one - block will always be decoded for every token.*/ -int oc_dct_token_skip(int _token,int _extra_bits){ - return (*OC_TOKEN_SKIP_TABLE[_token])(_token,_extra_bits); -} - - /*The function used to fill in the chroma plane motion vectors for a macro block when 4 different motion vectors are specified in the luma plane. - This version is for use with chroma decimated in the X and Y directions. + This version is for use with chroma decimated in the X and Y directions + (4:2:0). _cbmvs: The chroma block-level motion vectors to fill in. _lbmvs: The luma block-level motion vectors.*/ static void oc_set_chroma_mvs00(oc_mv _cbmvs[4],const oc_mv _lbmvs[4]){ @@ -256,7 +132,7 @@ static void oc_set_chroma_mvs01(oc_mv _cbmvs[4],const oc_mv _lbmvs[4]){ /*The function used to fill in the chroma plane motion vectors for a macro block when 4 different motion vectors are specified in the luma plane. - This version is for use with chroma decimated in the X direction. + This version is for use with chroma decimated in the X direction (4:2:2). _cbmvs: The chroma block-level motion vectors to fill in. _lbmvs: The luma block-level motion vectors.*/ static void oc_set_chroma_mvs10(oc_mv _cbmvs[4],const oc_mv _lbmvs[4]){ @@ -274,7 +150,7 @@ static void oc_set_chroma_mvs10(oc_mv _cbmvs[4],const oc_mv _lbmvs[4]){ /*The function used to fill in the chroma plane motion vectors for a macro block when 4 different motion vectors are specified in the luma plane. - This version is for use with no chroma decimation. + This version is for use with no chroma decimation (4:4:4). _cbmvs: The chroma block-level motion vectors to fill in. _lmbmv: The luma macro-block level motion vector to fill in for use in prediction. @@ -305,6 +181,7 @@ void **oc_malloc_2d(size_t _height,size_t _width,size_t _sz){ datsz=rowsz*_height; /*Alloc array and row pointers.*/ ret=(char *)_ogg_malloc(datsz+colsz); + if(ret==NULL)return NULL; /*Initialize the array.*/ if(ret!=NULL){ size_t i; @@ -327,6 +204,7 @@ void **oc_calloc_2d(size_t _height,size_t _width,size_t _sz){ datsz=rowsz*_height; /*Alloc array and row pointers.*/ ret=(char *)_ogg_calloc(datsz+colsz,1); + if(ret==NULL)return NULL; /*Initialize the array.*/ if(ret!=NULL){ size_t i; @@ -355,7 +233,8 @@ void oc_ycbcr_buffer_flip(th_ycbcr_buffer _dst, _dst[pli].width=_src[pli].width; _dst[pli].height=_src[pli].height; _dst[pli].stride=-_src[pli].stride; - _dst[pli].data=_src[pli].data+(1-_dst[pli].height)*_dst[pli].stride; + _dst[pli].data=_src[pli].data + +(1-_dst[pli].height)*(ptrdiff_t)_dst[pli].stride; } } @@ -364,7 +243,7 @@ const char *th_version_string(void){ } ogg_uint32_t th_version_number(void){ - return (TH_VERSION_MAJOR<<16)+(TH_VERSION_MINOR<<8)+(TH_VERSION_SUB); + return (TH_VERSION_MAJOR<<16)+(TH_VERSION_MINOR<<8)+TH_VERSION_SUB; } /*Determines the packet type. diff --git a/Engine/lib/libtheora/lib/internal.h b/Engine/lib/libtheora/lib/internal.h index 0413a355a..d81263e13 100644 --- a/Engine/lib/libtheora/lib/internal.h +++ b/Engine/lib/libtheora/lib/internal.h @@ -5,36 +5,74 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: internal.h 15469 2008-10-30 12:49:42Z tterribe $ + last mod: $Id: internal.h 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ - #if !defined(_internal_H) # define _internal_H (1) # include +# include # if defined(HAVE_CONFIG_H) # include # endif # include "theora/codec.h" # include "theora/theora.h" -# include "dec/ocintrin.h" -# include "dec/huffman.h" -# include "dec/quant.h" -/*Thank you Microsoft, I know the order of operations.*/ # if defined(_MSC_VER) -# pragma warning(disable:4554) /* order of operations */ -# pragma warning(disable:4799) /* disable missing EMMS warnings */ +/*Disable missing EMMS warnings.*/ +# pragma warning(disable:4799) +/*Thank you Microsoft, I know the order of operations.*/ +# pragma warning(disable:4554) +# endif +/*You, too, gcc.*/ +# if defined(__GNUC_PREREQ) +# if __GNUC_PREREQ(4,2) +# pragma GCC diagnostic ignored "-Wparentheses" +# endif # endif +# include "ocintrin.h" +# include "huffman.h" +# include "quant.h" + +/*Some assembly constructs require aligned operands.*/ +# if defined(OC_X86_ASM) +# if defined(__GNUC__) +# define OC_ALIGN8(expr) expr __attribute__((aligned(8))) +# define OC_ALIGN16(expr) expr __attribute__((aligned(16))) +# elif defined(_MSC_VER) +# define OC_ALIGN8(expr) __declspec (align(8)) expr +# define OC_ALIGN16(expr) __declspec (align(16)) expr +# endif +# endif +# if !defined(OC_ALIGN8) +# define OC_ALIGN8(expr) expr +# endif +# if !defined(OC_ALIGN16) +# define OC_ALIGN16(expr) expr +# endif + + + +typedef struct oc_sb_flags oc_sb_flags; +typedef struct oc_border_info oc_border_info; +typedef struct oc_fragment oc_fragment; +typedef struct oc_fragment_plane oc_fragment_plane; +typedef struct oc_base_opt_vtable oc_base_opt_vtable; +typedef struct oc_base_opt_data oc_base_opt_data; +typedef struct oc_state_dispatch_vtable oc_state_dispatch_vtable; +typedef struct oc_theora_state oc_theora_state; + + + /*This library's version.*/ -# define OC_VENDOR_STRING "Xiph.Org libTheora I 20081020 3 2 1" +# define OC_VENDOR_STRING "Xiph.Org libtheora 1.1 20090822 (Thusnelda)" /*Theora bitstream version.*/ # define TH_VERSION_MAJOR (3) @@ -97,18 +135,10 @@ /*The number of (coded) modes.*/ #define OC_NMODES (8) -/*Macro block is not coded.*/ -#define OC_MODE_NOT_CODED (8) - -/*Predictor bit flags.*/ -/*Left.*/ -#define OC_PL (1) -/*Upper-left.*/ -#define OC_PUL (2) -/*Up.*/ -#define OC_PU (4) -/*Upper-right.*/ -#define OC_PUR (8) +/*Determines the reference frame used for a given MB mode.*/ +#define OC_FRAME_FOR_MODE(_x) \ + OC_UNIBBLE_TABLE32(OC_FRAME_PREV,OC_FRAME_SELF,OC_FRAME_PREV,OC_FRAME_PREV, \ + OC_FRAME_PREV,OC_FRAME_GOLD,OC_FRAME_GOLD,OC_FRAME_PREV,(_x)) /*Constants for the packet state machine common between encoder and decoder.*/ @@ -123,21 +153,7 @@ -typedef struct oc_theora_state oc_theora_state; - - - -/*A map from a super block to fragment numbers.*/ -typedef int oc_sb_map[4][4]; -/*A map from a macro block to fragment numbers.*/ -typedef int oc_mb_map[3][4]; -/*A motion vector.*/ -typedef signed char oc_mv[2]; - - - -/*Super block information. - Super blocks are 32x32 segments of pixels in a single color plane indexed +/*Super blocks are 32x32 segments of pixels in a single color plane indexed in image order. Internally, super blocks are broken up into four quadrants, each of which contains a 2x2 pattern of blocks, each of which is an 8x8 block of pixels. @@ -148,194 +164,201 @@ typedef signed char oc_mv[2]; the regular image order indexing strategy, blocks indexed in image order are called "fragments". Fragments are indexed in image order, left to right, then bottom to top, - from Y plane to Cb plane to Cr plane.*/ -typedef struct{ - unsigned coded_fully:1; - unsigned coded_partially:1; - unsigned quad_valid:4; - oc_sb_map map; -}oc_sb; + from Y' plane to Cb plane to Cr plane. - - -/*Macro block information. - The co-located fragments in all image planes corresponding to the location of - a single luma plane super block quadrant forms a macro block. - Thus there is only a single set of macro blocks for all planes, which + The co-located fragments in all image planes corresponding to the location + of a single quadrant of a luma plane super block form a macro block. + Thus there is only a single set of macro blocks for all planes, each of which contains between 6 and 12 fragments, depending on the pixel format. - Therefore macro block information is kept in a separate array from super - blocks, to avoid unused space in the other planes.*/ -typedef struct{ - /*The current macro block mode. - A negative number indicates the macro block lies entirely outside the - coded frame.*/ - int mode; - /*The X location of the macro block's upper-left hand pixel.*/ - int x; - /*The Y location of the macro block's upper-right hand pixel.*/ - int y; - /*The fragments that belong to this macro block in each color plane. - Fragments are stored in image order (left to right then top to bottom). - When chroma components are decimated, the extra fragments have an index of - -1.*/ - oc_mb_map map; -}oc_mb; + Therefore macro block information is kept in a separate set of arrays from + super blocks to avoid unused space in the other planes. + The lists are indexed in super block order. + That is, the macro block corresponding to the macro block mbi in (luma plane) + super block sbi is at index (sbi<<2|mbi). + Thus the number of macro blocks in each dimension is always twice the number + of super blocks, even when only an odd number fall inside the coded frame. + These "extra" macro blocks are just an artifact of our internal data layout, + and not part of the coded stream; they are flagged with a negative MB mode.*/ + + + +/*A single quadrant of the map from a super block to fragment numbers.*/ +typedef ptrdiff_t oc_sb_map_quad[4]; +/*A map from a super block to fragment numbers.*/ +typedef oc_sb_map_quad oc_sb_map[4]; +/*A single plane of the map from a macro block to fragment numbers.*/ +typedef ptrdiff_t oc_mb_map_plane[4]; +/*A map from a macro block to fragment numbers.*/ +typedef oc_mb_map_plane oc_mb_map[3]; +/*A motion vector.*/ +typedef signed char oc_mv[2]; + + + +/*Super block information.*/ +struct oc_sb_flags{ + unsigned char coded_fully:1; + unsigned char coded_partially:1; + unsigned char quad_valid:4; +}; /*Information about a fragment which intersects the border of the displayable region. - This marks which pixels belong to the displayable region, and is used to - ensure that pixels outside of this region are never referenced. - This allows applications to pass in buffers that are really the size of the - displayable region without causing a seg fault.*/ -typedef struct{ + This marks which pixels belong to the displayable region.*/ +struct oc_border_info{ /*A bit mask marking which pixels are in the displayable region. Pixel (x,y) corresponds to bit (y<<3|x).*/ ogg_int64_t mask; /*The number of pixels in the displayable region. This is always positive, and always less than 64.*/ int npixels; -}oc_border_info; +}; /*Fragment information.*/ -typedef struct{ +struct oc_fragment{ /*A flag indicating whether or not this fragment is coded.*/ - unsigned coded:1; - /*A flag indicating that all of this fragment lies outside the displayable + unsigned coded:1; + /*A flag indicating that this entire fragment lies outside the displayable region of the frame. Note the contrast with an invalid macro block, which is outside the coded - frame, not just the displayable one.*/ - unsigned invalid:1; - /*The quality index used for this fragment's AC coefficients.*/ - unsigned qi:6; - /*The mode of the macroblock this fragment belongs to. + frame, not just the displayable one. + There are no fragments outside the coded frame by construction.*/ + unsigned invalid:1; + /*The index of the quality index used for this fragment's AC coefficients.*/ + unsigned qii:6; + /*The mode of the macroblock this fragment belongs to.*/ + unsigned mb_mode:3; + /*The index of the associated border information for fragments which lie + partially outside the displayable region. + For fragments completely inside or outside this region, this is -1. Note that the C standard requires an explicit signed keyword for bitfield types, since some compilers may treat them as unsigned without it.*/ - signed int mbmode:8; + signed int borderi:5; /*The prediction-corrected DC component. Note that the C standard requires an explicit signed keyword for bitfield types, since some compilers may treat them as unsigned without it.*/ - signed int dc:16; - /*A pointer to the portion of an image covered by this fragment in several - images. - The first three are reconstructed frame buffers, while the last is the - input image buffer. - The appropriate stride value is determined by the color plane the fragment - belongs in.*/ - unsigned char *buffer[4]; - /*Information for fragments which lie partially outside the displayable - region. - For fragments completely inside or outside this region, this is NULL.*/ - oc_border_info *border; - /*The motion vector used for this fragment.*/ - oc_mv mv; -}oc_fragment; + signed int dc:16; +}; /*A description of each fragment plane.*/ -typedef struct{ +struct oc_fragment_plane{ /*The number of fragments in the horizontal direction.*/ - int nhfrags; + int nhfrags; /*The number of fragments in the vertical direction.*/ - int nvfrags; + int nvfrags; /*The offset of the first fragment in the plane.*/ - int froffset; + ptrdiff_t froffset; /*The total number of fragments in the plane.*/ - int nfrags; + ptrdiff_t nfrags; /*The number of super blocks in the horizontal direction.*/ - int nhsbs; + unsigned nhsbs; /*The number of super blocks in the vertical direction.*/ - int nvsbs; + unsigned nvsbs; /*The offset of the first super block in the plane.*/ - int sboffset; + unsigned sboffset; /*The total number of super blocks in the plane.*/ - int nsbs; -}oc_fragment_plane; + unsigned nsbs; +}; /*The shared (encoder and decoder) functions that have accelerated variants.*/ -typedef struct{ - void (*frag_recon_intra)(unsigned char *_dst,int _dst_ystride, - const ogg_int16_t *_residue); - void (*frag_recon_inter)(unsigned char *_dst,int _dst_ystride, - const unsigned char *_src,int _src_ystride,const ogg_int16_t *_residue); - void (*frag_recon_inter2)(unsigned char *_dst,int _dst_ystride, - const unsigned char *_src1,int _src1_ystride,const unsigned char *_src2, - int _src2_ystride,const ogg_int16_t *_residue); - void (*state_frag_copy)(const oc_theora_state *_state, - const int *_fragis,int _nfragis,int _dst_frame,int _src_frame,int _pli); - void (*state_frag_recon)(oc_theora_state *_state,oc_fragment *_frag, - int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, - ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]); +struct oc_base_opt_vtable{ + void (*frag_copy)(unsigned char *_dst, + const unsigned char *_src,int _ystride); + void (*frag_recon_intra)(unsigned char *_dst,int _ystride, + const ogg_int16_t _residue[64]); + void (*frag_recon_inter)(unsigned char *_dst, + const unsigned char *_src,int _ystride,const ogg_int16_t _residue[64]); + void (*frag_recon_inter2)(unsigned char *_dst,const unsigned char *_src1, + const unsigned char *_src2,int _ystride,const ogg_int16_t _residue[64]); + void (*idct8x8)(ogg_int16_t _y[64],int _last_zzi); + void (*state_frag_recon)(const oc_theora_state *_state,ptrdiff_t _fragi, + int _pli,ogg_int16_t _dct_coeffs[64],int _last_zzi,ogg_uint16_t _dc_quant); + void (*state_frag_copy_list)(const oc_theora_state *_state, + const ptrdiff_t *_fragis,ptrdiff_t _nfragis, + int _dst_frame,int _src_frame,int _pli); + void (*state_loop_filter_frag_rows)(const oc_theora_state *_state, + int _bv[256],int _refi,int _pli,int _fragy0,int _fragy_end); void (*restore_fpu)(void); - void (*state_loop_filter_frag_rows)(oc_theora_state *_state,int *_bv, - int _refi,int _pli,int _fragy0,int _fragy_end); -}oc_base_opt_vtable; +}; + +/*The shared (encoder and decoder) tables that vary according to which variants + of the above functions are used.*/ +struct oc_base_opt_data{ + const unsigned char *dct_fzig_zag; +}; - -/*Common state information between the encoder and decoder.*/ +/*State information common to both the encoder and decoder.*/ struct oc_theora_state{ /*The stream information.*/ th_info info; /*Table for shared accelerated functions.*/ oc_base_opt_vtable opt_vtable; + /*Table for shared data used by accelerated functions.*/ + oc_base_opt_data opt_data; /*CPU flags to detect the presence of extended instruction sets.*/ ogg_uint32_t cpu_flags; /*The fragment plane descriptions.*/ oc_fragment_plane fplanes[3]; - /*The total number of fragments in a single frame.*/ - int nfrags; /*The list of fragments, indexed in image order.*/ oc_fragment *frags; + /*The the offset into the reference frame buffer to the upper-left pixel of + each fragment.*/ + ptrdiff_t *frag_buf_offs; + /*The motion vector for each fragment.*/ + oc_mv *frag_mvs; + /*The total number of fragments in a single frame.*/ + ptrdiff_t nfrags; + /*The list of super block maps, indexed in image order.*/ + oc_sb_map *sb_maps; + /*The list of super block flags, indexed in image order.*/ + oc_sb_flags *sb_flags; /*The total number of super blocks in a single frame.*/ - int nsbs; - /*The list of super blocks, indexed in image order.*/ - oc_sb *sbs; + unsigned nsbs; + /*The fragments from each color plane that belong to each macro block. + Fragments are stored in image order (left to right then top to bottom). + When chroma components are decimated, the extra fragments have an index of + -1.*/ + oc_mb_map *mb_maps; + /*The list of macro block modes. + A negative number indicates the macro block lies entirely outside the + coded frame.*/ + signed char *mb_modes; /*The number of macro blocks in the X direction.*/ - int nhmbs; + unsigned nhmbs; /*The number of macro blocks in the Y direction.*/ - int nvmbs; + unsigned nvmbs; /*The total number of macro blocks.*/ - int nmbs; - /*The list of macro blocks, indexed in super block order. - That is, the macro block corresponding to the macro block mbi in (luma - plane) super block sbi is (sbi<<2|mbi).*/ - oc_mb *mbs; - /*The list of coded fragments, in coded order.*/ - int *coded_fragis; + size_t nmbs; + /*The list of coded fragments, in coded order. + Uncoded fragments are stored in reverse order from the end of the list.*/ + ptrdiff_t *coded_fragis; /*The number of coded fragments in each plane.*/ - int ncoded_fragis[3]; - /*The list of uncoded fragments. - This just past the end of the list, which is in reverse order, and - uses the same block of allocated storage as the coded_fragis list.*/ - int *uncoded_fragis; - /*The number of uncoded fragments in each plane.*/ - int nuncoded_fragis[3]; - /*The list of coded macro blocks in the Y plane, in coded order.*/ - int *coded_mbis; - /*The number of coded macro blocks in the Y plane.*/ - int ncoded_mbis; - /*A copy of the image data used to fill the input pointers in each fragment. - If the data pointers or strides change, these input pointers must be - re-populated.*/ - th_ycbcr_buffer input; + ptrdiff_t ncoded_fragis[3]; + /*The total number of coded fragments.*/ + ptrdiff_t ntotal_coded_fragis; + /*The index of the buffers being used for each OC_FRAME_* reference frame.*/ + int ref_frame_idx[4]; + /*The actual buffers used for the previously decoded frames.*/ + th_ycbcr_buffer ref_frame_bufs[4]; + /*The storage for the reference frame buffers.*/ + unsigned char *ref_frame_data[4]; + /*The strides for each plane in the reference frames.*/ + int ref_ystride[3]; /*The number of unique border patterns.*/ int nborders; - /*The storage for the border info for all border fragments. - This data is pointed to from the appropriate fragments.*/ + /*The unique border patterns for all border fragments. + The borderi field of fragments which straddle the border indexes this + list.*/ oc_border_info borders[16]; - /*The index of the buffers being used for each OC_FRAME_* reference frame.*/ - int ref_frame_idx[3]; - /*The actual buffers used for the previously decoded frames.*/ - th_ycbcr_buffer ref_frame_bufs[3]; - /*The storage for the reference frame buffers.*/ - unsigned char *ref_frame_data; /*The frame number of the last keyframe.*/ ogg_int64_t keyframe_num; /*The frame number of the current frame.*/ @@ -343,14 +366,17 @@ struct oc_theora_state{ /*The granpos of the current frame.*/ ogg_int64_t granpos; /*The type of the current frame.*/ - int frame_type; - /*The quality indices of the current frame.*/ - int qis[3]; + unsigned char frame_type; + /*The bias to add to the frame count when computing granule positions.*/ + unsigned char granpos_bias; /*The number of quality indices used in the current frame.*/ - int nqis; - /*The dequantization tables.*/ - oc_quant_table *dequant_tables[2][3]; - oc_quant_tables dequant_table_data[2][3]; + unsigned char nqis; + /*The quality indices of the current frame.*/ + unsigned char qis[3]; + /*The dequantization tables, stored in zig-zag order, and indexed by + qi, pli, qti, and zzi.*/ + ogg_uint16_t *dequant_tables[64][3][2]; + OC_ALIGN16(oc_quant_table dequant_table_data[64][3][2]); /*Loop filter strength parameters.*/ unsigned char loop_filter_limits[64]; }; @@ -369,25 +395,20 @@ typedef void (*oc_set_chroma_mvs_func)(oc_mv _cbmvs[4],const oc_mv _lbmvs[4]); /*A map from the index in the zig zag scan to the coefficient number in a - block. - The extra 64 entries send out of bounds indexes to index 64. - This is used to safely ignore invalid zero runs when decoding - coefficients.*/ -extern const int OC_FZIG_ZAG[128]; + block.*/ +extern const unsigned char OC_FZIG_ZAG[128]; /*A map from the coefficient number in a block to its index in the zig zag scan.*/ -extern const int OC_IZIG_ZAG[64]; -/*The predictor frame to use for each macro block mode.*/ -extern const int OC_FRAME_FOR_MODE[OC_NMODES]; +extern const unsigned char OC_IZIG_ZAG[64]; /*A map from physical macro block ordering to bitstream macro block ordering within a super block.*/ -extern const int OC_MB_MAP[2][2]; -/*A list of the indices in the oc_mb.map array that can be valid for each of +extern const unsigned char OC_MB_MAP[2][2]; +/*A list of the indices in the oc_mb_map array that can be valid for each of the various chroma decimation types.*/ -extern const int OC_MB_MAP_IDXS[TH_PF_NFORMATS][12]; -/*The number of indices in the oc_mb.map array that can be valid for each of +extern const unsigned char OC_MB_MAP_IDXS[TH_PF_NFORMATS][12]; +/*The number of indices in the oc_mb_map array that can be valid for each of the various chroma decimation types.*/ -extern const int OC_MB_MAP_NIDXS[TH_PF_NFORMATS]; +extern const unsigned char OC_MB_MAP_NIDXS[TH_PF_NFORMATS]; /*A table of functions used to fill in the Cb,Cr plane motion vectors for a macro block when 4 different motion vectors are specified in the luma plane.*/ @@ -403,12 +424,7 @@ void oc_free_2d(void *_ptr); void oc_ycbcr_buffer_flip(th_ycbcr_buffer _dst, const th_ycbcr_buffer _src); -int oc_dct_token_skip(int _token,int _extra_bits); - -int oc_frag_pred_dc(const oc_fragment *_frag, - const oc_fragment_plane *_fplane,int _x,int _y,int _pred_last[3]); - -int oc_state_init(oc_theora_state *_state,const th_info *_info); +int oc_state_init(oc_theora_state *_state,const th_info *_info,int _nrefs); void oc_state_clear(oc_theora_state *_state); void oc_state_vtable_init_c(oc_theora_state *_state); void oc_state_borders_fill_rows(oc_theora_state *_state,int _refi,int _pli, @@ -418,8 +434,8 @@ void oc_state_borders_fill(oc_theora_state *_state,int _refi); void oc_state_fill_buffer_ptrs(oc_theora_state *_state,int _buf_idx, th_ycbcr_buffer _img); int oc_state_mbi_for_pos(oc_theora_state *_state,int _mbx,int _mby); -int oc_state_get_mv_offsets(oc_theora_state *_state,int *_offsets, - int _dx,int _dy,int _ystride,int _pli); +int oc_state_get_mv_offsets(const oc_theora_state *_state,int _offsets[2], + int _pli,int _dx,int _dy); int oc_state_loop_filter_init(oc_theora_state *_state,int *_bv); void oc_state_loop_filter(oc_theora_state *_state,int _frame); @@ -429,39 +445,42 @@ int oc_state_dump_frame(const oc_theora_state *_state,int _frame, #endif /*Shared accelerated functions.*/ +void oc_frag_copy(const oc_theora_state *_state,unsigned char *_dst, + const unsigned char *_src,int _ystride); void oc_frag_recon_intra(const oc_theora_state *_state, - unsigned char *_dst,int _dst_ystride,const ogg_int16_t *_residue); -void oc_frag_recon_inter(const oc_theora_state *_state, - unsigned char *_dst,int _dst_ystride, - const unsigned char *_src,int _src_ystride,const ogg_int16_t *_residue); + unsigned char *_dst,int _dst_ystride,const ogg_int16_t _residue[64]); +void oc_frag_recon_inter(const oc_theora_state *_state,unsigned char *_dst, + const unsigned char *_src,int _ystride,const ogg_int16_t _residue[64]); void oc_frag_recon_inter2(const oc_theora_state *_state, - unsigned char *_dst,int _dst_ystride, - const unsigned char *_src1,int _src1_ystride,const unsigned char *_src2, - int _src2_ystride,const ogg_int16_t *_residue); -void oc_state_frag_copy(const oc_theora_state *_state,const int *_fragis, - int _nfragis,int _dst_frame,int _src_frame,int _pli); -void oc_state_frag_recon(oc_theora_state *_state,oc_fragment *_frag, - int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, - ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]); -void oc_state_loop_filter_frag_rows(oc_theora_state *_state,int *_bv, - int _refi,int _pli,int _fragy0,int _fragy_end); + unsigned char *_dst,const unsigned char *_src1,const unsigned char *_src2, + int _ystride,const ogg_int16_t _residue[64]); +void oc_idct8x8(const oc_theora_state *_state,ogg_int16_t _y[64],int _last_zzi); +void oc_state_frag_recon(const oc_theora_state *_state,ptrdiff_t _fragi, + int _pli,ogg_int16_t _dct_coeffs[64],int _last_zzi,ogg_uint16_t _dc_quant); +void oc_state_frag_copy_list(const oc_theora_state *_state, + const ptrdiff_t *_fragis,ptrdiff_t _nfragis, + int _dst_frame,int _src_frame,int _pli); +void oc_state_loop_filter_frag_rows(const oc_theora_state *_state, + int _bv[256],int _refi,int _pli,int _fragy0,int _fragy_end); void oc_restore_fpu(const oc_theora_state *_state); /*Default pure-C implementations.*/ +void oc_frag_copy_c(unsigned char *_dst, + const unsigned char *_src,int _src_ystride); void oc_frag_recon_intra_c(unsigned char *_dst,int _dst_ystride, - const ogg_int16_t *_residue); -void oc_frag_recon_inter_c(unsigned char *_dst,int _dst_ystride, - const unsigned char *_src,int _src_ystride,const ogg_int16_t *_residue); -void oc_frag_recon_inter2_c(unsigned char *_dst,int _dst_ystride, - const unsigned char *_src1,int _src1_ystride,const unsigned char *_src2, - int _src2_ystride,const ogg_int16_t *_residue); -void oc_state_frag_copy_c(const oc_theora_state *_state,const int *_fragis, - int _nfragis,int _dst_frame,int _src_frame,int _pli); -void oc_state_frag_recon_c(oc_theora_state *_state,oc_fragment *_frag, - int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, - ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]); -void oc_state_loop_filter_frag_rows_c(oc_theora_state *_state,int *_bv, - int _refi,int _pli,int _fragy0,int _fragy_end); + const ogg_int16_t _residue[64]); +void oc_frag_recon_inter_c(unsigned char *_dst, + const unsigned char *_src,int _ystride,const ogg_int16_t _residue[64]); +void oc_frag_recon_inter2_c(unsigned char *_dst,const unsigned char *_src1, + const unsigned char *_src2,int _ystride,const ogg_int16_t _residue[64]); +void oc_idct8x8_c(ogg_int16_t _y[64],int _last_zzi); +void oc_state_frag_recon_c(const oc_theora_state *_state,ptrdiff_t _fragi, + int _pli,ogg_int16_t _dct_coeffs[64],int _last_zzi,ogg_uint16_t _dc_quant); +void oc_state_frag_copy_list_c(const oc_theora_state *_state, + const ptrdiff_t *_fragis,ptrdiff_t _nfragis, + int _dst_frame,int _src_frame,int _pli); +void oc_state_loop_filter_frag_rows_c(const oc_theora_state *_state, + int _bv[256],int _refi,int _pli,int _fragy0,int _fragy_end); void oc_restore_fpu_c(void); /*We need a way to call a few encoder functions without introducing a link-time @@ -472,16 +491,15 @@ void oc_restore_fpu_c(void); We do a similar thing for the decoder in case we ever decide to split off a common base library.*/ typedef void (*oc_state_clear_func)(theora_state *_th); -typedef int (*oc_state_control_func)(theora_state *th,int req, - void *buf,size_t buf_sz); +typedef int (*oc_state_control_func)(theora_state *th,int _req, + void *_buf,size_t _buf_sz); typedef ogg_int64_t (*oc_state_granule_frame_func)(theora_state *_th, ogg_int64_t _granulepos); typedef double (*oc_state_granule_time_func)(theora_state *_th, ogg_int64_t _granulepos); -typedef struct oc_state_dispatch_vtbl oc_state_dispatch_vtbl; -struct oc_state_dispatch_vtbl{ +struct oc_state_dispatch_vtable{ oc_state_clear_func clear; oc_state_control_func control; oc_state_granule_frame_func granule_frame; diff --git a/Engine/lib/libtheora/lib/mathops.c b/Engine/lib/libtheora/lib/mathops.c new file mode 100644 index 000000000..d3fb90919 --- /dev/null +++ b/Engine/lib/libtheora/lib/mathops.c @@ -0,0 +1,296 @@ +#include "mathops.h" +#include + +/*The fastest fallback strategy for platforms with fast multiplication appears + to be based on de Bruijn sequences~\cite{LP98}. + Tests confirmed this to be true even on an ARM11, where it is actually faster + than using the native clz instruction. + Define OC_ILOG_NODEBRUIJN to use a simpler fallback on platforms where + multiplication or table lookups are too expensive. + + @UNPUBLISHED{LP98, + author="Charles E. Leiserson and Harald Prokop", + title="Using de {Bruijn} Sequences to Index a 1 in a Computer Word", + month=Jun, + year=1998, + note="\url{http://supertech.csail.mit.edu/papers/debruijn.pdf}" + }*/ +#if !defined(OC_ILOG_NODEBRUIJN)&& \ + !defined(OC_CLZ32)||!defined(OC_CLZ64)&&LONG_MAX<9223372036854775807LL +static const unsigned char OC_DEBRUIJN_IDX32[32]={ + 0, 1,28, 2,29,14,24, 3,30,22,20,15,25,17, 4, 8, + 31,27,13,23,21,19,16, 7,26,12,18, 6,11, 5,10, 9 +}; +#endif + +int oc_ilog32(ogg_uint32_t _v){ +#if defined(OC_CLZ32) + return (OC_CLZ32_OFFS-OC_CLZ32(_v))&-!!_v; +#else +/*On a Pentium M, this branchless version tested as the fastest version without + multiplications on 1,000,000,000 random 32-bit integers, edging out a + similar version with branches, and a 256-entry LUT version.*/ +# if defined(OC_ILOG_NODEBRUIJN) + int ret; + int m; + ret=_v>0; + m=(_v>0xFFFFU)<<4; + _v>>=m; + ret|=m; + m=(_v>0xFFU)<<3; + _v>>=m; + ret|=m; + m=(_v>0xFU)<<2; + _v>>=m; + ret|=m; + m=(_v>3)<<1; + _v>>=m; + ret|=m; + ret+=_v>1; + return ret; +/*This de Bruijn sequence version is faster if you have a fast multiplier.*/ +# else + int ret; + ret=_v>0; + _v|=_v>>1; + _v|=_v>>2; + _v|=_v>>4; + _v|=_v>>8; + _v|=_v>>16; + _v=(_v>>1)+1; + ret+=OC_DEBRUIJN_IDX32[_v*0x77CB531U>>27&0x1F]; + return ret; +# endif +#endif +} + +int oc_ilog64(ogg_int64_t _v){ +#if defined(OC_CLZ64) + return (OC_CLZ64_OFFS-OC_CLZ64(_v))&-!!_v; +#else +# if defined(OC_ILOG_NODEBRUIJN) + ogg_uint32_t v; + int ret; + int m; + ret=_v>0; + m=(_v>0xFFFFFFFFU)<<5; + v=(ogg_uint32_t)(_v>>m); + ret|=m; + m=(v>0xFFFFU)<<4; + v>>=m; + ret|=m; + m=(v>0xFFU)<<3; + v>>=m; + ret|=m; + m=(v>0xFU)<<2; + v>>=m; + ret|=m; + m=(v>3)<<1; + v>>=m; + ret|=m; + ret+=v>1; + return ret; +# else +/*If we don't have a 64-bit word, split it into two 32-bit halves.*/ +# if LONG_MAX<9223372036854775807LL + ogg_uint32_t v; + int ret; + int m; + ret=_v>0; + m=(_v>0xFFFFFFFFU)<<5; + v=(ogg_uint32_t)(_v>>m); + ret|=m; + v|=v>>1; + v|=v>>2; + v|=v>>4; + v|=v>>8; + v|=v>>16; + v=(v>>1)+1; + ret+=OC_DEBRUIJN_IDX32[v*0x77CB531U>>27&0x1F]; + return ret; +/*Otherwise do it in one 64-bit operation.*/ +# else + static const unsigned char OC_DEBRUIJN_IDX64[64]={ + 0, 1, 2, 7, 3,13, 8,19, 4,25,14,28, 9,34,20,40, + 5,17,26,38,15,46,29,48,10,31,35,54,21,50,41,57, + 63, 6,12,18,24,27,33,39,16,37,45,47,30,53,49,56, + 62,11,23,32,36,44,52,55,61,22,43,51,60,42,59,58 + }; + int ret; + ret=_v>0; + _v|=_v>>1; + _v|=_v>>2; + _v|=_v>>4; + _v|=_v>>8; + _v|=_v>>16; + _v|=_v>>32; + _v=(_v>>1)+1; + ret+=OC_DEBRUIJN_IDX64[_v*0x218A392CD3D5DBF>>58&0x3F]; + return ret; +# endif +# endif +#endif +} + +/*round(2**(62+i)*atanh(2**(-(i+1)))/log(2))*/ +static const ogg_int64_t OC_ATANH_LOG2[32]={ + 0x32B803473F7AD0F4LL,0x2F2A71BD4E25E916LL,0x2E68B244BB93BA06LL, + 0x2E39FB9198CE62E4LL,0x2E2E683F68565C8FLL,0x2E2B850BE2077FC1LL, + 0x2E2ACC58FE7B78DBLL,0x2E2A9E2DE52FD5F2LL,0x2E2A92A338D53EECLL, + 0x2E2A8FC08F5E19B6LL,0x2E2A8F07E51A485ELL,0x2E2A8ED9BA8AF388LL, + 0x2E2A8ECE2FE7384ALL,0x2E2A8ECB4D3E4B1ALL,0x2E2A8ECA94940FE8LL, + 0x2E2A8ECA6669811DLL,0x2E2A8ECA5ADEDD6ALL,0x2E2A8ECA57FC347ELL, + 0x2E2A8ECA57438A43LL,0x2E2A8ECA57155FB4LL,0x2E2A8ECA5709D510LL, + 0x2E2A8ECA5706F267LL,0x2E2A8ECA570639BDLL,0x2E2A8ECA57060B92LL, + 0x2E2A8ECA57060008LL,0x2E2A8ECA5705FD25LL,0x2E2A8ECA5705FC6CLL, + 0x2E2A8ECA5705FC3ELL,0x2E2A8ECA5705FC33LL,0x2E2A8ECA5705FC30LL, + 0x2E2A8ECA5705FC2FLL,0x2E2A8ECA5705FC2FLL +}; + +/*Computes the binary exponential of _z, a log base 2 in Q57 format.*/ +ogg_int64_t oc_bexp64(ogg_int64_t _z){ + ogg_int64_t w; + ogg_int64_t z; + int ipart; + ipart=(int)(_z>>57); + if(ipart<0)return 0; + if(ipart>=63)return 0x7FFFFFFFFFFFFFFFLL; + z=_z-OC_Q57(ipart); + if(z){ + ogg_int64_t mask; + long wlo; + int i; + /*C doesn't give us 64x64->128 muls, so we use CORDIC. + This is not particularly fast, but it's not being used in time-critical + code; it is very accurate.*/ + /*z is the fractional part of the log in Q62 format. + We need 1 bit of headroom since the magnitude can get larger than 1 + during the iteration, and a sign bit.*/ + z<<=5; + /*w is the exponential in Q61 format (since it also needs headroom and can + get as large as 2.0); we could get another bit if we dropped the sign, + but we'll recover that bit later anyway. + Ideally this should start out as + \lim_{n->\infty} 2^{61}/\product_{i=1}^n \sqrt{1-2^{-2i}} + but in order to guarantee convergence we have to repeat iterations 4, + 13 (=3*4+1), and 40 (=3*13+1, etc.), so it winds up somewhat larger.*/ + w=0x26A3D0E401DD846DLL; + for(i=0;;i++){ + mask=-(z<0); + w+=(w>>i+1)+mask^mask; + z-=OC_ATANH_LOG2[i]+mask^mask; + /*Repeat iteration 4.*/ + if(i>=3)break; + z<<=1; + } + for(;;i++){ + mask=-(z<0); + w+=(w>>i+1)+mask^mask; + z-=OC_ATANH_LOG2[i]+mask^mask; + /*Repeat iteration 13.*/ + if(i>=12)break; + z<<=1; + } + for(;i<32;i++){ + mask=-(z<0); + w+=(w>>i+1)+mask^mask; + z=z-(OC_ATANH_LOG2[i]+mask^mask)<<1; + } + wlo=0; + /*Skip the remaining iterations unless we really require that much + precision. + We could have bailed out earlier for smaller iparts, but that would + require initializing w from a table, as the limit doesn't converge to + 61-bit precision until n=30.*/ + if(ipart>30){ + /*For these iterations, we just update the low bits, as the high bits + can't possibly be affected. + OC_ATANH_LOG2 has also converged (it actually did so one iteration + earlier, but that's no reason for an extra special case).*/ + for(;;i++){ + mask=-(z<0); + wlo+=(w>>i)+mask^mask; + z-=OC_ATANH_LOG2[31]+mask^mask; + /*Repeat iteration 40.*/ + if(i>=39)break; + z<<=1; + } + for(;i<61;i++){ + mask=-(z<0); + wlo+=(w>>i)+mask^mask; + z=z-(OC_ATANH_LOG2[31]+mask^mask)<<1; + } + } + w=(w<<1)+wlo; + } + else w=(ogg_int64_t)1<<62; + if(ipart<62)w=(w>>61-ipart)+1>>1; + return w; +} + +/*Computes the binary logarithm of _w, returned in Q57 format.*/ +ogg_int64_t oc_blog64(ogg_int64_t _w){ + ogg_int64_t z; + int ipart; + if(_w<=0)return -1; + ipart=OC_ILOGNZ_64(_w)-1; + if(ipart>61)_w>>=ipart-61; + else _w<<=61-ipart; + z=0; + if(_w&_w-1){ + ogg_int64_t x; + ogg_int64_t y; + ogg_int64_t u; + ogg_int64_t mask; + int i; + /*C doesn't give us 64x64->128 muls, so we use CORDIC. + This is not particularly fast, but it's not being used in time-critical + code; it is very accurate.*/ + /*z is the fractional part of the log in Q61 format.*/ + /*x and y are the cosh() and sinh(), respectively, in Q61 format. + We are computing z=2*atanh(y/x)=2*atanh((_w-1)/(_w+1)).*/ + x=_w+((ogg_int64_t)1<<61); + y=_w-((ogg_int64_t)1<<61); + for(i=0;i<4;i++){ + mask=-(y<0); + z+=(OC_ATANH_LOG2[i]>>i)+mask^mask; + u=x>>i+1; + x-=(y>>i+1)+mask^mask; + y-=u+mask^mask; + } + /*Repeat iteration 4.*/ + for(i--;i<13;i++){ + mask=-(y<0); + z+=(OC_ATANH_LOG2[i]>>i)+mask^mask; + u=x>>i+1; + x-=(y>>i+1)+mask^mask; + y-=u+mask^mask; + } + /*Repeat iteration 13.*/ + for(i--;i<32;i++){ + mask=-(y<0); + z+=(OC_ATANH_LOG2[i]>>i)+mask^mask; + u=x>>i+1; + x-=(y>>i+1)+mask^mask; + y-=u+mask^mask; + } + /*OC_ATANH_LOG2 has converged.*/ + for(;i<40;i++){ + mask=-(y<0); + z+=(OC_ATANH_LOG2[31]>>i)+mask^mask; + u=x>>i+1; + x-=(y>>i+1)+mask^mask; + y-=u+mask^mask; + } + /*Repeat iteration 40.*/ + for(i--;i<62;i++){ + mask=-(y<0); + z+=(OC_ATANH_LOG2[31]>>i)+mask^mask; + u=x>>i+1; + x-=(y>>i+1)+mask^mask; + y-=u+mask^mask; + } + z=z+8>>4; + } + return OC_Q57(ipart)+z; +} diff --git a/Engine/lib/libtheora/lib/mathops.h b/Engine/lib/libtheora/lib/mathops.h new file mode 100644 index 000000000..efbc5377b --- /dev/null +++ b/Engine/lib/libtheora/lib/mathops.h @@ -0,0 +1,141 @@ +#if !defined(_mathops_H) +# define _mathops_H (1) +# include + +# ifdef __GNUC_PREREQ +# if __GNUC_PREREQ(3,4) +# include +/*Note the casts to (int) below: this prevents OC_CLZ{32|64}_OFFS from + "upgrading" the type of an entire expression to an (unsigned) size_t.*/ +# if INT_MAX>=2147483647 +# define OC_CLZ32_OFFS ((int)sizeof(unsigned)*CHAR_BIT) +# define OC_CLZ32(_x) (__builtin_clz(_x)) +# elif LONG_MAX>=2147483647L +# define OC_CLZ32_OFFS ((int)sizeof(unsigned long)*CHAR_BIT) +# define OC_CLZ32(_x) (__builtin_clzl(_x)) +# endif +# if INT_MAX>=9223372036854775807LL +# define OC_CLZ64_OFFS ((int)sizeof(unsigned)*CHAR_BIT) +# define OC_CLZ64(_x) (__builtin_clz(_x)) +# elif LONG_MAX>=9223372036854775807LL +# define OC_CLZ64_OFFS ((int)sizeof(unsigned long)*CHAR_BIT) +# define OC_CLZ64(_x) (__builtin_clzl(_x)) +# elif LLONG_MAX>=9223372036854775807LL|| \ + __LONG_LONG_MAX__>=9223372036854775807LL +# define OC_CLZ64_OFFS ((int)sizeof(unsigned long long)*CHAR_BIT) +# define OC_CLZ64(_x) (__builtin_clzll(_x)) +# endif +# endif +# endif + + + +/** + * oc_ilog32 - Integer binary logarithm of a 32-bit value. + * @_v: A 32-bit value. + * Returns floor(log2(_v))+1, or 0 if _v==0. + * This is the number of bits that would be required to represent _v in two's + * complement notation with all of the leading zeros stripped. + * The OC_ILOG_32() or OC_ILOGNZ_32() macros may be able to use a builtin + * function instead, which should be faster. + */ +int oc_ilog32(ogg_uint32_t _v); +/** + * oc_ilog64 - Integer binary logarithm of a 64-bit value. + * @_v: A 64-bit value. + * Returns floor(log2(_v))+1, or 0 if _v==0. + * This is the number of bits that would be required to represent _v in two's + * complement notation with all of the leading zeros stripped. + * The OC_ILOG_64() or OC_ILOGNZ_64() macros may be able to use a builtin + * function instead, which should be faster. + */ +int oc_ilog64(ogg_int64_t _v); + + +# if defined(OC_CLZ32) +/** + * OC_ILOGNZ_32 - Integer binary logarithm of a non-zero 32-bit value. + * @_v: A non-zero 32-bit value. + * Returns floor(log2(_v))+1. + * This is the number of bits that would be required to represent _v in two's + * complement notation with all of the leading zeros stripped. + * If _v is zero, the return value is undefined; use OC_ILOG_32() instead. + */ +# define OC_ILOGNZ_32(_v) (OC_CLZ32_OFFS-OC_CLZ32(_v)) +/** + * OC_ILOG_32 - Integer binary logarithm of a 32-bit value. + * @_v: A 32-bit value. + * Returns floor(log2(_v))+1, or 0 if _v==0. + * This is the number of bits that would be required to represent _v in two's + * complement notation with all of the leading zeros stripped. + */ +# define OC_ILOG_32(_v) (OC_ILOGNZ_32(_v)&-!!(_v)) +# else +# define OC_ILOGNZ_32(_v) (oc_ilog32(_v)) +# define OC_ILOG_32(_v) (oc_ilog32(_v)) +# endif + +# if defined(CLZ64) +/** + * OC_ILOGNZ_64 - Integer binary logarithm of a non-zero 64-bit value. + * @_v: A non-zero 64-bit value. + * Returns floor(log2(_v))+1. + * This is the number of bits that would be required to represent _v in two's + * complement notation with all of the leading zeros stripped. + * If _v is zero, the return value is undefined; use OC_ILOG_64() instead. + */ +# define OC_ILOGNZ_64(_v) (CLZ64_OFFS-CLZ64(_v)) +/** + * OC_ILOG_64 - Integer binary logarithm of a 64-bit value. + * @_v: A 64-bit value. + * Returns floor(log2(_v))+1, or 0 if _v==0. + * This is the number of bits that would be required to represent _v in two's + * complement notation with all of the leading zeros stripped. + */ +# define OC_ILOG_64(_v) (OC_ILOGNZ_64(_v)&-!!(_v)) +# else +# define OC_ILOGNZ_64(_v) (oc_ilog64(_v)) +# define OC_ILOG_64(_v) (oc_ilog64(_v)) +# endif + +# define OC_STATIC_ILOG0(_v) (!!(_v)) +# define OC_STATIC_ILOG1(_v) (((_v)&0x2)?2:OC_STATIC_ILOG0(_v)) +# define OC_STATIC_ILOG2(_v) \ + (((_v)&0xC)?2+OC_STATIC_ILOG1((_v)>>2):OC_STATIC_ILOG1(_v)) +# define OC_STATIC_ILOG3(_v) \ + (((_v)&0xF0)?4+OC_STATIC_ILOG2((_v)>>4):OC_STATIC_ILOG2(_v)) +# define OC_STATIC_ILOG4(_v) \ + (((_v)&0xFF00)?8+OC_STATIC_ILOG3((_v)>>8):OC_STATIC_ILOG3(_v)) +# define OC_STATIC_ILOG5(_v) \ + (((_v)&0xFFFF0000)?16+OC_STATIC_ILOG4((_v)>>16):OC_STATIC_ILOG4(_v)) +# define OC_STATIC_ILOG6(_v) \ + (((_v)&0xFFFFFFFF00000000ULL)?32+OC_STATIC_ILOG5((_v)>>32):OC_STATIC_ILOG5(_v)) +/** + * OC_STATIC_ILOG_32 - The integer logarithm of an (unsigned, 32-bit) constant. + * @_v: A non-negative 32-bit constant. + * Returns floor(log2(_v))+1, or 0 if _v==0. + * This is the number of bits that would be required to represent _v in two's + * complement notation with all of the leading zeros stripped. + * This macro is suitable for evaluation at compile time, but it should not be + * used on values that can change at runtime, as it operates via exhaustive + * search. + */ +# define OC_STATIC_ILOG_32(_v) (OC_STATIC_ILOG5((ogg_uint32_t)(_v))) +/** + * OC_STATIC_ILOG_64 - The integer logarithm of an (unsigned, 64-bit) constant. + * @_v: A non-negative 64-bit constant. + * Returns floor(log2(_v))+1, or 0 if _v==0. + * This is the number of bits that would be required to represent _v in two's + * complement notation with all of the leading zeros stripped. + * This macro is suitable for evaluation at compile time, but it should not be + * used on values that can change at runtime, as it operates via exhaustive + * search. + */ +# define OC_STATIC_ILOG_64(_v) (OC_STATIC_ILOG6((ogg_int64_t)(_v))) + +#define OC_Q57(_v) ((ogg_int64_t)(_v)<<57) + +ogg_int64_t oc_bexp64(ogg_int64_t _z); +ogg_int64_t oc_blog64(ogg_int64_t _w); + +#endif diff --git a/Engine/lib/libtheora/lib/mcenc.c b/Engine/lib/libtheora/lib/mcenc.c new file mode 100644 index 000000000..797e81f4f --- /dev/null +++ b/Engine/lib/libtheora/lib/mcenc.c @@ -0,0 +1,767 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id$ + + ********************************************************************/ +#include +#include +#include +#include "encint.h" + + + +typedef struct oc_mcenc_ctx oc_mcenc_ctx; + + + +/*Temporary state used for motion estimation.*/ +struct oc_mcenc_ctx{ + /*The candidate motion vectors.*/ + int candidates[13][2]; + /*The start of the Set B candidates.*/ + int setb0; + /*The total number of candidates.*/ + int ncandidates; +}; + + + +/*The maximum Y plane SAD value for accepting the median predictor.*/ +#define OC_YSAD_THRESH1 (256) +/*The amount to right shift the minimum error by when inflating it for + computing the second maximum Y plane SAD threshold.*/ +#define OC_YSAD_THRESH2_SCALE_BITS (4) +/*The amount to add to the second maximum Y plane threshold when inflating + it.*/ +#define OC_YSAD_THRESH2_OFFSET (64) + +/*The vector offsets in the X direction for each search site in the square + pattern.*/ +static const int OC_SQUARE_DX[9]={-1,0,1,-1,0,1,-1,0,1}; +/*The vector offsets in the Y direction for each search site in the square + pattern.*/ +static const int OC_SQUARE_DY[9]={-1,-1,-1,0,0,0,1,1,1}; +/*The number of sites to search for each boundary condition in the square + pattern. + Bit flags for the boundary conditions are as follows: + 1: -16==dx + 2: dx==15(.5) + 4: -16==dy + 8: dy==15(.5)*/ +static const int OC_SQUARE_NSITES[11]={8,5,5,0,5,3,3,0,5,3,3}; +/*The list of sites to search for each boundary condition in the square + pattern.*/ +static const int OC_SQUARE_SITES[11][8]={ + /* -15.5mb_info; + /*Skip a position to store the median predictor in.*/ + ncandidates=1; + if(embs[_mbi].ncneighbors>0){ + /*Fill in the first part of set A: the vectors from adjacent blocks.*/ + for(i=0;icandidates[ncandidates][0]=embs[nmbi].analysis_mv[0][_frame][0]; + _mcenc->candidates[ncandidates][1]=embs[nmbi].analysis_mv[0][_frame][1]; + ncandidates++; + } + } + /*Add a few additional vectors to set A: the vectors used in the previous + frames and the (0,0) vector.*/ + _mcenc->candidates[ncandidates][0]=OC_CLAMPI(-31,_accum[0],31); + _mcenc->candidates[ncandidates][1]=OC_CLAMPI(-31,_accum[1],31); + ncandidates++; + _mcenc->candidates[ncandidates][0]=OC_CLAMPI(-31, + embs[_mbi].analysis_mv[1][_frame][0]+_accum[0],31); + _mcenc->candidates[ncandidates][1]=OC_CLAMPI(-31, + embs[_mbi].analysis_mv[1][_frame][1]+_accum[1],31); + ncandidates++; + _mcenc->candidates[ncandidates][0]=0; + _mcenc->candidates[ncandidates][1]=0; + ncandidates++; + /*Use the first three vectors of set A to find our best predictor: their + median.*/ + memcpy(a,_mcenc->candidates+1,sizeof(a)); + OC_SORT2I(a[0][0],a[1][0]); + OC_SORT2I(a[0][1],a[1][1]); + OC_SORT2I(a[1][0],a[2][0]); + OC_SORT2I(a[1][1],a[2][1]); + OC_SORT2I(a[0][0],a[1][0]); + OC_SORT2I(a[0][1],a[1][1]); + _mcenc->candidates[0][0]=a[1][0]; + _mcenc->candidates[0][1]=a[1][1]; + /*Fill in set B: accelerated predictors for this and adjacent macro blocks.*/ + _mcenc->setb0=ncandidates; + /*The first time through the loop use the current macro block.*/ + nmbi=_mbi; + for(i=0;;i++){ + _mcenc->candidates[ncandidates][0]=OC_CLAMPI(-31, + 2*embs[_mbi].analysis_mv[1][_frame][0] + -embs[_mbi].analysis_mv[2][_frame][0]+_accum[0],31); + _mcenc->candidates[ncandidates][1]=OC_CLAMPI(-31, + 2*embs[_mbi].analysis_mv[1][_frame][1] + -embs[_mbi].analysis_mv[2][_frame][1]+_accum[1],31); + ncandidates++; + if(i>=embs[_mbi].npneighbors)break; + nmbi=embs[_mbi].pneighbors[i]; + } + /*Truncate to full-pel positions.*/ + for(i=0;icandidates[i][0]=OC_DIV2(_mcenc->candidates[i][0]); + _mcenc->candidates[i][1]=OC_DIV2(_mcenc->candidates[i][1]); + } + _mcenc->ncandidates=ncandidates; +} + +#if 0 +static unsigned oc_sad16_halfpel(const oc_enc_ctx *_enc, + const ptrdiff_t *_frag_buf_offs,const ptrdiff_t _fragis[4], + int _mvoffset0,int _mvoffset1,const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _best_err){ + unsigned err; + int bi; + err=0; + for(bi=0;bi<4;bi++){ + ptrdiff_t frag_offs; + frag_offs=_frag_buf_offs[_fragis[bi]]; + err+=oc_enc_frag_sad2_thresh(_enc,_src+frag_offs,_ref+frag_offs+_mvoffset0, + _ref+frag_offs+_mvoffset1,_ystride,_best_err-err); + } + return err; +} +#endif + +static unsigned oc_satd16_halfpel(const oc_enc_ctx *_enc, + const ptrdiff_t *_frag_buf_offs,const ptrdiff_t _fragis[4], + int _mvoffset0,int _mvoffset1,const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _best_err){ + unsigned err; + int bi; + err=0; + for(bi=0;bi<4;bi++){ + ptrdiff_t frag_offs; + frag_offs=_frag_buf_offs[_fragis[bi]]; + err+=oc_enc_frag_satd2_thresh(_enc,_src+frag_offs,_ref+frag_offs+_mvoffset0, + _ref+frag_offs+_mvoffset1,_ystride,_best_err-err); + } + return err; +} + +static unsigned oc_mcenc_ysad_check_mbcandidate_fullpel(const oc_enc_ctx *_enc, + const ptrdiff_t *_frag_buf_offs,const ptrdiff_t _fragis[4],int _dx,int _dy, + const unsigned char *_src,const unsigned char *_ref,int _ystride, + unsigned _block_err[4]){ + unsigned err; + int mvoffset; + int bi; + mvoffset=_dx+_dy*_ystride; + err=0; + for(bi=0;bi<4;bi++){ + ptrdiff_t frag_offs; + unsigned block_err; + frag_offs=_frag_buf_offs[_fragis[bi]]; + block_err=oc_enc_frag_sad(_enc, + _src+frag_offs,_ref+frag_offs+mvoffset,_ystride); + _block_err[bi]=block_err; + err+=block_err; + } + return err; +} + +static int oc_mcenc_ysatd_check_mbcandidate_fullpel(const oc_enc_ctx *_enc, + const ptrdiff_t *_frag_buf_offs,const ptrdiff_t _fragis[4],int _dx,int _dy, + const unsigned char *_src,const unsigned char *_ref,int _ystride){ + int mvoffset; + int err; + int bi; + mvoffset=_dx+_dy*_ystride; + err=0; + for(bi=0;bi<4;bi++){ + ptrdiff_t frag_offs; + frag_offs=_frag_buf_offs[_fragis[bi]]; + err+=oc_enc_frag_satd_thresh(_enc, + _src+frag_offs,_ref+frag_offs+mvoffset,_ystride,UINT_MAX); + } + return err; +} + +static unsigned oc_mcenc_ysatd_check_bcandidate_fullpel(const oc_enc_ctx *_enc, + ptrdiff_t _frag_offs,int _dx,int _dy, + const unsigned char *_src,const unsigned char *_ref,int _ystride){ + return oc_enc_frag_satd_thresh(_enc, + _src+_frag_offs,_ref+_frag_offs+_dx+_dy*_ystride,_ystride,UINT_MAX); +} + +/*Perform a motion vector search for this macro block against a single + reference frame. + As a bonus, individual block motion vectors are computed as well, as much of + the work can be shared. + The actual motion vector is stored in the appropriate place in the + oc_mb_enc_info structure. + _mcenc: The motion compensation context. + _accum: Drop frame/golden MV accumulators. + _mbi: The macro block index. + _frame: The frame to search, either OC_FRAME_PREV or OC_FRAME_GOLD.*/ +void oc_mcenc_search_frame(oc_enc_ctx *_enc,int _accum[2],int _mbi,int _frame){ + /*Note: Traditionally this search is done using a rate-distortion objective + function of the form D+lambda*R. + However, xiphmont tested this and found it produced a small degredation, + while requiring extra computation. + This is most likely due to Theora's peculiar MV encoding scheme: MVs are + not coded relative to a predictor, and the only truly cheap way to use a + MV is in the LAST or LAST2 MB modes, which are not being considered here. + Therefore if we use the MV found here, it's only because both LAST and + LAST2 performed poorly, and therefore the MB is not likely to be uniform + or suffer from the aperture problem. + Furthermore we would like to re-use the MV found here for as many MBs as + possible, so picking a slightly sub-optimal vector to save a bit or two + may cause increased degredation in many blocks to come. + We could artificially reduce lambda to compensate, but it's faster to just + disable it entirely, and use D (the distortion) as the sole criterion.*/ + oc_mcenc_ctx mcenc; + const ptrdiff_t *frag_buf_offs; + const ptrdiff_t *fragis; + const unsigned char *src; + const unsigned char *ref; + int ystride; + oc_mb_enc_info *embs; + ogg_int32_t hit_cache[31]; + ogg_int32_t hitbit; + unsigned best_block_err[4]; + unsigned block_err[4]; + unsigned best_err; + int best_vec[2]; + int best_block_vec[4][2]; + int candx; + int candy; + int bi; + embs=_enc->mb_info; + /*Find some candidate motion vectors.*/ + oc_mcenc_find_candidates(_enc,&mcenc,_accum,_mbi,_frame); + /*Clear the cache of locations we've examined.*/ + memset(hit_cache,0,sizeof(hit_cache)); + /*Start with the median predictor.*/ + candx=mcenc.candidates[0][0]; + candy=mcenc.candidates[0][1]; + hit_cache[candy+15]|=(ogg_int32_t)1<state.frag_buf_offs; + fragis=_enc->state.mb_maps[_mbi][0]; + src=_enc->state.ref_frame_data[OC_FRAME_IO]; + ref=_enc->state.ref_frame_data[_enc->state.ref_frame_idx[_frame]]; + ystride=_enc->state.ref_ystride[0]; + /*TODO: customize error function for speed/(quality+size) tradeoff.*/ + best_err=oc_mcenc_ysad_check_mbcandidate_fullpel(_enc, + frag_buf_offs,fragis,candx,candy,src,ref,ystride,block_err); + best_vec[0]=candx; + best_vec[1]=candy; + if(_frame==OC_FRAME_PREV){ + for(bi=0;bi<4;bi++){ + best_block_err[bi]=block_err[bi]; + best_block_vec[bi][0]=candx; + best_block_vec[bi][1]=candy; + } + } + /*If this predictor fails, move on to set A.*/ + if(best_err>OC_YSAD_THRESH1){ + unsigned err; + unsigned t2; + int ncs; + int ci; + /*Compute the early termination threshold for set A.*/ + t2=embs[_mbi].error[_frame]; + ncs=OC_MINI(3,embs[_mbi].ncneighbors); + for(ci=0;ci>OC_YSAD_THRESH2_SCALE_BITS)+OC_YSAD_THRESH2_OFFSET; + /*Examine the candidates in set A.*/ + for(ci=1;cit2){ + /*Examine the candidates in set B.*/ + for(;cit2){ + int best_site; + int nsites; + int sitei; + int site; + int b; + /*Square pattern search.*/ + for(;;){ + best_site=4; + /*Compose the bit flags for boundary conditions.*/ + b=OC_DIV16(-best_vec[0]+1)|OC_DIV16(best_vec[0]+1)<<1| + OC_DIV16(-best_vec[1]+1)<<2|OC_DIV16(best_vec[1]+1)<<3; + nsites=OC_SQUARE_NSITES[b]; + for(sitei=0;sitei>=2; + for(bi=0;bi<4;bi++){ + if(best_block_err[bi]>t2){ + /*Square pattern search. + We do this in a slightly interesting manner. + We continue to check the SAD of all four blocks in the + macro block. + This gives us two things: + 1) We can continue to use the hit_cache to avoid duplicate + checks. + Otherwise we could continue to read it, but not write to it + without saving and restoring it for each block. + Note that we could still eliminate a large number of + duplicate checks by taking into account the site we came + from when choosing the site list. + We can still do that to avoid extra hit_cache queries, and + it might even be a speed win. + 2) It gives us a slightly better chance of escaping local + minima. + We would not be here if we weren't doing a fairly bad job + in finding a good vector, and checking these vectors can + save us from 100 to several thousand points off our SAD 1 + in 15 times. + TODO: Is this a good idea? + Who knows. + It needs more testing.*/ + for(;;){ + int bestx; + int besty; + int bj; + bestx=best_block_vec[bi][0]; + besty=best_block_vec[bi][1]; + /*Compose the bit flags for boundary conditions.*/ + b=OC_DIV16(-bestx+1)|OC_DIV16(bestx+1)<<1| + OC_DIV16(-besty+1)<<2|OC_DIV16(besty+1)<<3; + nsites=OC_SQUARE_NSITES[b]; + for(sitei=0;siteimb_info[_mbi].analysis_mv; + if(_enc->prevframe_dropped){ + accum_p[0]=mvs[0][OC_FRAME_PREV][0]; + accum_p[1]=mvs[0][OC_FRAME_PREV][1]; + } + else accum_p[1]=accum_p[0]=0; + accum_g[0]=mvs[2][OC_FRAME_GOLD][0]; + accum_g[1]=mvs[2][OC_FRAME_GOLD][1]; + mvs[0][OC_FRAME_PREV][0]-=mvs[2][OC_FRAME_PREV][0]; + mvs[0][OC_FRAME_PREV][1]-=mvs[2][OC_FRAME_PREV][1]; + /*Move the motion vector predictors back a frame.*/ + memmove(mvs+1,mvs,2*sizeof(*mvs)); + /*Search the last frame.*/ + oc_mcenc_search_frame(_enc,accum_p,_mbi,OC_FRAME_PREV); + mvs[2][OC_FRAME_PREV][0]=accum_p[0]; + mvs[2][OC_FRAME_PREV][1]=accum_p[1]; + /*GOLDEN MVs are different from PREV MVs in that they're each absolute + offsets from some frame in the past rather than relative offsets from the + frame before. + For predictor calculation to make sense, we need them to be in the same + form as PREV MVs.*/ + mvs[1][OC_FRAME_GOLD][0]-=mvs[2][OC_FRAME_GOLD][0]; + mvs[1][OC_FRAME_GOLD][1]-=mvs[2][OC_FRAME_GOLD][1]; + mvs[2][OC_FRAME_GOLD][0]-=accum_g[0]; + mvs[2][OC_FRAME_GOLD][1]-=accum_g[1]; + /*Search the golden frame.*/ + oc_mcenc_search_frame(_enc,accum_g,_mbi,OC_FRAME_GOLD); + /*Put GOLDEN MVs back into absolute offset form. + The newest MV is already an absolute offset.*/ + mvs[2][OC_FRAME_GOLD][0]+=accum_g[0]; + mvs[2][OC_FRAME_GOLD][1]+=accum_g[1]; + mvs[1][OC_FRAME_GOLD][0]+=mvs[2][OC_FRAME_GOLD][0]; + mvs[1][OC_FRAME_GOLD][1]+=mvs[2][OC_FRAME_GOLD][1]; +} + +#if 0 +static int oc_mcenc_ysad_halfpel_mbrefine(const oc_enc_ctx *_enc,int _mbi, + int _vec[2],int _best_err,int _frame){ + const unsigned char *src; + const unsigned char *ref; + const ptrdiff_t *frag_buf_offs; + const ptrdiff_t *fragis; + int offset_y[9]; + int ystride; + int mvoffset_base; + int best_site; + int sitei; + int err; + src=_enc->state.ref_frame_data[OC_FRAME_IO]; + ref=_enc->state.ref_frame_data[_enc->state.ref_frame_idx[_framei]]; + frag_buf_offs=_enc->state.frag_buf_offs; + fragis=_enc->state.mb_maps[_mbi][0]; + ystride=_enc->state.ref_ystride[0]; + mvoffset_base=_vec[0]+_vec[1]*ystride; + offset_y[0]=offset_y[1]=offset_y[2]=-ystride; + offset_y[3]=offset_y[5]=0; + offset_y[6]=offset_y[7]=offset_y[8]=ystride; + best_site=4; + for(sitei=0;sitei<8;sitei++){ + int site; + int xmask; + int ymask; + int dx; + int dy; + int mvoffset0; + int mvoffset1; + site=OC_SQUARE_SITES[0][sitei]; + dx=OC_SQUARE_DX[site]; + dy=OC_SQUARE_DY[site]; + /*The following code SHOULD be equivalent to + oc_state_get_mv_offsets(&_mcenc->enc.state,&mvoffset0,&mvoffset1, + (_vec[0]<<1)+dx,(_vec[1]<<1)+dy,ref_ystride,0); + However, it should also be much faster, as it involves no multiplies and + doesn't have to handle chroma vectors.*/ + xmask=OC_SIGNMASK(((_vec[0]<<1)+dx)^dx); + ymask=OC_SIGNMASK(((_vec[1]<<1)+dy)^dy); + mvoffset0=mvoffset_base+(dx&xmask)+(offset_y[site]&ymask); + mvoffset1=mvoffset_base+(dx&~xmask)+(offset_y[site]&~ymask); + err=oc_sad16_halfpel(_enc,frag_buf_offs,fragis, + mvoffset0,mvoffset1,src,ref,ystride,_best_err); + if(err<_best_err){ + _best_err=err; + best_site=site; + } + } + _vec[0]=(_vec[0]<<1)+OC_SQUARE_DX[best_site]; + _vec[1]=(_vec[1]<<1)+OC_SQUARE_DY[best_site]; + return _best_err; +} +#endif + +static unsigned oc_mcenc_ysatd_halfpel_mbrefine(const oc_enc_ctx *_enc, + int _mbi,int _vec[2],unsigned _best_err,int _frame){ + const unsigned char *src; + const unsigned char *ref; + const ptrdiff_t *frag_buf_offs; + const ptrdiff_t *fragis; + int offset_y[9]; + int ystride; + int mvoffset_base; + int best_site; + int sitei; + int err; + src=_enc->state.ref_frame_data[OC_FRAME_IO]; + ref=_enc->state.ref_frame_data[_enc->state.ref_frame_idx[_frame]]; + frag_buf_offs=_enc->state.frag_buf_offs; + fragis=_enc->state.mb_maps[_mbi][0]; + ystride=_enc->state.ref_ystride[0]; + mvoffset_base=_vec[0]+_vec[1]*ystride; + offset_y[0]=offset_y[1]=offset_y[2]=-ystride; + offset_y[3]=offset_y[5]=0; + offset_y[6]=offset_y[7]=offset_y[8]=ystride; + best_site=4; + for(sitei=0;sitei<8;sitei++){ + int site; + int xmask; + int ymask; + int dx; + int dy; + int mvoffset0; + int mvoffset1; + site=OC_SQUARE_SITES[0][sitei]; + dx=OC_SQUARE_DX[site]; + dy=OC_SQUARE_DY[site]; + /*The following code SHOULD be equivalent to + oc_state_get_mv_offsets(&_mcenc->enc.state,&mvoffset0,&mvoffset1, + (_vec[0]<<1)+dx,(_vec[1]<<1)+dy,ref_ystride,0); + However, it should also be much faster, as it involves no multiplies and + doesn't have to handle chroma vectors.*/ + xmask=OC_SIGNMASK(((_vec[0]<<1)+dx)^dx); + ymask=OC_SIGNMASK(((_vec[1]<<1)+dy)^dy); + mvoffset0=mvoffset_base+(dx&xmask)+(offset_y[site]&ymask); + mvoffset1=mvoffset_base+(dx&~xmask)+(offset_y[site]&~ymask); + err=oc_satd16_halfpel(_enc,frag_buf_offs,fragis, + mvoffset0,mvoffset1,src,ref,ystride,_best_err); + if(err<_best_err){ + _best_err=err; + best_site=site; + } + } + _vec[0]=(_vec[0]<<1)+OC_SQUARE_DX[best_site]; + _vec[1]=(_vec[1]<<1)+OC_SQUARE_DY[best_site]; + return _best_err; +} + +void oc_mcenc_refine1mv(oc_enc_ctx *_enc,int _mbi,int _frame){ + oc_mb_enc_info *embs; + int vec[2]; + embs=_enc->mb_info; + vec[0]=OC_DIV2(embs[_mbi].analysis_mv[0][_frame][0]); + vec[1]=OC_DIV2(embs[_mbi].analysis_mv[0][_frame][1]); + embs[_mbi].satd[_frame]=oc_mcenc_ysatd_halfpel_mbrefine(_enc, + _mbi,vec,embs[_mbi].satd[_frame],_frame); + embs[_mbi].analysis_mv[0][_frame][0]=(signed char)vec[0]; + embs[_mbi].analysis_mv[0][_frame][1]=(signed char)vec[1]; +} + +#if 0 +static int oc_mcenc_ysad_halfpel_brefine(const oc_enc_ctx *_enc, + int _vec[2],const unsigned char *_src,const unsigned char *_ref,int _ystride, + int _offset_y[9],unsigned _best_err){ + int mvoffset_base; + int best_site; + int sitei; + mvoffset_base=_vec[0]+_vec[1]*_ystride; + best_site=4; + for(sitei=0;sitei<8;sitei++){ + unsigned err; + int site; + int xmask; + int ymask; + int dx; + int dy; + int mvoffset0; + int mvoffset1; + site=OC_SQUARE_SITES[0][sitei]; + dx=OC_SQUARE_DX[site]; + dy=OC_SQUARE_DY[site]; + /*The following code SHOULD be equivalent to + oc_state_get_mv_offsets(&_mcenc->enc.state,&mvoffset0,&mvoffset1, + (_vec[0]<<1)+dx,(_vec[1]<<1)+dy,ref_ystride,0); + However, it should also be much faster, as it involves no multiplies and + doesn't have to handle chroma vectors.*/ + xmask=OC_SIGNMASK(((_vec[0]<<1)+dx)^dx); + ymask=OC_SIGNMASK(((_vec[1]<<1)+dy)^dy); + mvoffset0=mvoffset_base+(dx&xmask)+(_offset_y[site]&ymask); + mvoffset1=mvoffset_base+(dx&~xmask)+(_offset_y[site]&~ymask); + err=oc_enc_frag_sad2_thresh(_enc,_src, + _ref+mvoffset0,_ref+mvoffset1,ystride,_best_err); + if(err<_best_err){ + _best_err=err; + best_site=site; + } + } + _vec[0]=(_vec[0]<<1)+OC_SQUARE_DX[best_site]; + _vec[1]=(_vec[1]<<1)+OC_SQUARE_DY[best_site]; + return _best_err; +} +#endif + +static unsigned oc_mcenc_ysatd_halfpel_brefine(const oc_enc_ctx *_enc, + int _vec[2],const unsigned char *_src,const unsigned char *_ref,int _ystride, + int _offset_y[9],unsigned _best_err){ + int mvoffset_base; + int best_site; + int sitei; + mvoffset_base=_vec[0]+_vec[1]*_ystride; + best_site=4; + for(sitei=0;sitei<8;sitei++){ + unsigned err; + int site; + int xmask; + int ymask; + int dx; + int dy; + int mvoffset0; + int mvoffset1; + site=OC_SQUARE_SITES[0][sitei]; + dx=OC_SQUARE_DX[site]; + dy=OC_SQUARE_DY[site]; + /*The following code SHOULD be equivalent to + oc_state_get_mv_offsets(&_enc->state,&mvoffsets,0, + (_vec[0]<<1)+dx,(_vec[1]<<1)+dy); + However, it should also be much faster, as it involves no multiplies and + doesn't have to handle chroma vectors.*/ + xmask=OC_SIGNMASK(((_vec[0]<<1)+dx)^dx); + ymask=OC_SIGNMASK(((_vec[1]<<1)+dy)^dy); + mvoffset0=mvoffset_base+(dx&xmask)+(_offset_y[site]&ymask); + mvoffset1=mvoffset_base+(dx&~xmask)+(_offset_y[site]&~ymask); + err=oc_enc_frag_satd2_thresh(_enc,_src, + _ref+mvoffset0,_ref+mvoffset1,_ystride,_best_err); + if(err<_best_err){ + _best_err=err; + best_site=site; + } + } + _vec[0]=(_vec[0]<<1)+OC_SQUARE_DX[best_site]; + _vec[1]=(_vec[1]<<1)+OC_SQUARE_DY[best_site]; + return _best_err; +} + +void oc_mcenc_refine4mv(oc_enc_ctx *_enc,int _mbi){ + oc_mb_enc_info *embs; + const ptrdiff_t *frag_buf_offs; + const ptrdiff_t *fragis; + const unsigned char *src; + const unsigned char *ref; + int offset_y[9]; + int ystride; + int bi; + ystride=_enc->state.ref_ystride[0]; + frag_buf_offs=_enc->state.frag_buf_offs; + fragis=_enc->state.mb_maps[_mbi][0]; + src=_enc->state.ref_frame_data[OC_FRAME_IO]; + ref=_enc->state.ref_frame_data[_enc->state.ref_frame_idx[OC_FRAME_PREV]]; + offset_y[0]=offset_y[1]=offset_y[2]=-ystride; + offset_y[3]=offset_y[5]=0; + offset_y[6]=offset_y[7]=offset_y[8]=ystride; + embs=_enc->mb_info; + for(bi=0;bi<4;bi++){ + ptrdiff_t frag_offs; + int vec[2]; + frag_offs=frag_buf_offs[fragis[bi]]; + vec[0]=OC_DIV2(embs[_mbi].block_mv[bi][0]); + vec[1]=OC_DIV2(embs[_mbi].block_mv[bi][1]); + embs[_mbi].block_satd[bi]=oc_mcenc_ysatd_halfpel_brefine(_enc,vec, + src+frag_offs,ref+frag_offs,ystride,offset_y,embs[_mbi].block_satd[bi]); + embs[_mbi].ref_mv[bi][0]=(signed char)vec[0]; + embs[_mbi].ref_mv[bi][1]=(signed char)vec[1]; + } +} diff --git a/Engine/lib/libtheora/lib/modedec.h b/Engine/lib/libtheora/lib/modedec.h new file mode 100644 index 000000000..ea12c64af --- /dev/null +++ b/Engine/lib/libtheora/lib/modedec.h @@ -0,0 +1,4027 @@ +/*File generated by libtheora with OC_COLLECT_METRICS defined at compile time.*/ +#if !defined(_modedec_H) +# define _modedec_H (1) + + + +# if defined(OC_COLLECT_METRICS) +typedef struct oc_mode_metrics oc_mode_metrics; +# endif +typedef struct oc_mode_rd oc_mode_rd; + + + +/*The number of extra bits of precision at which to store rate metrics.*/ +# define OC_BIT_SCALE (6) +/*The number of extra bits of precision at which to store RMSE metrics. + This must be at least half OC_BIT_SCALE (rounded up).*/ +# define OC_RMSE_SCALE (5) +/*The number of bins to partition statistics into.*/ +# define OC_SAD_BINS (24) +/*The number of bits of precision to drop from SAD scores to assign them to a + bin.*/ +# define OC_SAD_SHIFT (9) + + + +# if defined(OC_COLLECT_METRICS) +struct oc_mode_metrics{ + double fragw; + double satd; + double rate; + double rmse; + double satd2; + double satdrate; + double rate2; + double satdrmse; + double rmse2; +}; + + +int oc_has_mode_metrics; +oc_mode_metrics OC_MODE_METRICS[64][3][2][OC_SAD_BINS]; +# endif + + + +struct oc_mode_rd{ + ogg_int16_t rate; + ogg_int16_t rmse; +}; + + +# if !defined(OC_COLLECT_METRICS) +static const +# endif +oc_mode_rd OC_MODE_RD[64][3][2][OC_SAD_BINS]={ + { + { + /*Y' qi=0 INTRA*/ + { + { 87, -66},{ 132, 1611},{ 197, 3474},{ 285, 5130}, + { 376, 6419},{ 450, 7545},{ 521, 8587},{ 600, 9587}, + { 689,10498},{ 790,11348},{ 899,12158},{ 1030,12855}, + { 1166,13459},{ 1276,14052},{ 1353,14732},{ 1444,15425}, + { 1535,16101},{ 1609,16856},{ 1697,17532},{ 1823,17995}, + { 1962,18426},{ 2085,18919},{ 2201,19503},{ 2304,20307} + }, + /*Y' qi=0 INTER*/ + { + { 32, -105},{ 40, 1268},{ 54, 2919},{ 91, 4559}, + { 118, 6244},{ 132, 7932},{ 142, 9514},{ 149,10989}, + { 155,12375},{ 161,13679},{ 168,14958},{ 176,16215}, + { 187,17431},{ 196,18623},{ 207,19790},{ 218,20941}, + { 230,22083},{ 246,23213},{ 265,24333},{ 292,25439}, + { 328,26512},{ 372,27538},{ 427,28522},{ 494,29479} + } + }, + { + /*Cb qi=0 INTRA*/ + { + { 1, 6},{ 27, 368},{ 52, 738},{ 67, 1171}, + { 80, 1642},{ 99, 2134},{ 110, 2642},{ 112, 3144}, + { 126, 3578},{ 154, 3967},{ 167, 4387},{ 172, 4839}, + { 191, 5278},{ 208, 5666},{ 220, 6036},{ 223, 6398}, + { 227, 6814},{ 253, 7157},{ 284, 7403},{ 292, 7699}, + { 314, 7983},{ 339, 8203},{ 363, 8460},{ 399, 8919} + }, + /*Cb qi=0 INTER*/ + { + { 68, -55},{ 63, 275},{ 58, 602},{ 53, 936}, + { 50, 1290},{ 54, 1691},{ 58, 2116},{ 62, 2553}, + { 67, 2992},{ 72, 3422},{ 78, 3843},{ 84, 4253}, + { 89, 4658},{ 94, 5062},{ 98, 5455},{ 100, 5848}, + { 102, 6231},{ 104, 6604},{ 104, 6982},{ 105, 7359}, + { 105, 7733},{ 104, 8104},{ 105, 8465},{ 111, 8828} + } + }, + { + /*Cr qi=0 INTRA*/ + { + { 1, 8},{ 23, 375},{ 47, 759},{ 63, 1220}, + { 71, 1693},{ 82, 2171},{ 94, 2652},{ 109, 3103}, + { 125, 3567},{ 133, 3995},{ 151, 4375},{ 168, 4819}, + { 174, 5244},{ 190, 5635},{ 215, 6005},{ 242, 6347}, + { 257, 6758},{ 280, 7068},{ 311, 7336},{ 326, 7652}, + { 346, 7968},{ 372, 8213},{ 388, 8515},{ 408, 9060} + }, + /*Cr qi=0 INTER*/ + { + { 69, 0},{ 60, 314},{ 49, 624},{ 45, 943}, + { 45, 1285},{ 49, 1691},{ 55, 2130},{ 62, 2560}, + { 71, 2973},{ 79, 3385},{ 85, 3800},{ 89, 4207}, + { 92, 4620},{ 95, 5037},{ 96, 5436},{ 97, 5839}, + { 98, 6252},{ 99, 6653},{ 99, 7038},{ 103, 7426}, + { 107, 7810},{ 108, 8178},{ 107, 8539},{ 106, 8937} + } + } + }, + { + { + /*Y' qi=1 INTRA*/ + { + { 81, -71},{ 133, 1610},{ 203, 3460},{ 296, 5083}, + { 392, 6342},{ 467, 7454},{ 541, 8486},{ 625, 9466}, + { 716,10352},{ 823,11181},{ 940,11961},{ 1074,12643}, + { 1211,13233},{ 1324,13807},{ 1408,14489},{ 1504,15167}, + { 1598,15824},{ 1679,16544},{ 1788,17161},{ 1928,17579}, + { 2070,17991},{ 2202,18456},{ 2324,19021},{ 2425,19894} + }, + /*Y' qi=1 INTER*/ + { + { 34, 4},{ 40, 1307},{ 55, 2914},{ 93, 4555}, + { 120, 6243},{ 134, 7912},{ 144, 9468},{ 152,10918}, + { 158,12275},{ 164,13569},{ 171,14846},{ 180,16098}, + { 191,17310},{ 204,18484},{ 216,19636},{ 228,20779}, + { 242,21912},{ 261,23036},{ 286,24146},{ 320,25221}, + { 363,26265},{ 418,27261},{ 485,28203},{ 551,29148} + } + }, + { + /*Cb qi=1 INTRA*/ + { + { 1, 6},{ 28, 367},{ 52, 738},{ 68, 1172}, + { 86, 1644},{ 106, 2135},{ 115, 2642},{ 119, 3141}, + { 132, 3569},{ 157, 3951},{ 172, 4366},{ 177, 4819}, + { 194, 5258},{ 211, 5638},{ 224, 6006},{ 233, 6367}, + { 236, 6784},{ 258, 7121},{ 299, 7357},{ 319, 7637}, + { 337, 7921},{ 358, 8141},{ 381, 8367},{ 401, 8768} + }, + /*Cb qi=1 INTER*/ + { + { 95, -31},{ 81, 295},{ 67, 614},{ 53, 953}, + { 48, 1305},{ 51, 1700},{ 56, 2125},{ 61, 2563}, + { 67, 3008},{ 73, 3435},{ 79, 3844},{ 85, 4251}, + { 90, 4663},{ 95, 5073},{ 98, 5458},{ 100, 5844}, + { 101, 6231},{ 102, 6606},{ 102, 6980},{ 103, 7347}, + { 104, 7726},{ 105, 8096},{ 105, 8453},{ 105, 8789} + } + }, + { + /*Cr qi=1 INTRA*/ + { + { 1, 8},{ 25, 375},{ 50, 759},{ 65, 1221}, + { 74, 1695},{ 86, 2172},{ 101, 2651},{ 117, 3101}, + { 129, 3561},{ 135, 3985},{ 153, 4368},{ 171, 4807}, + { 182, 5223},{ 202, 5608},{ 225, 5964},{ 251, 6300}, + { 271, 6697},{ 295, 6978},{ 324, 7235},{ 348, 7558}, + { 367, 7877},{ 394, 8101},{ 413, 8386},{ 409, 8945} + }, + /*Cr qi=1 INTER*/ + { + { 66, 11},{ 59, 323},{ 51, 631},{ 44, 949}, + { 44, 1292},{ 49, 1703},{ 56, 2140},{ 62, 2566}, + { 69, 2991},{ 77, 3397},{ 84, 3799},{ 89, 4211}, + { 93, 4634},{ 94, 5049},{ 95, 5444},{ 96, 5854}, + { 94, 6260},{ 95, 6640},{ 96, 7032},{ 101, 7423}, + { 104, 7790},{ 105, 8158},{ 109, 8527},{ 108, 8872} + } + } + }, + { + { + /*Y' qi=2 INTRA*/ + { + { 87, -72},{ 139, 1607},{ 213, 3426},{ 315, 4992}, + { 416, 6217},{ 495, 7315},{ 574, 8317},{ 666, 9265}, + { 763,10124},{ 875,10906},{ 1001,11654},{ 1147,12305}, + { 1289,12865},{ 1407,13424},{ 1503,14076},{ 1610,14724}, + { 1720,15342},{ 1815,16020},{ 1937,16579},{ 2084,16981}, + { 2236,17371},{ 2385,17779},{ 2536,18250},{ 2689,18931} + }, + /*Y' qi=2 INTER*/ + { + { 30, -2},{ 40, 1308},{ 57, 2921},{ 96, 4567}, + { 122, 6260},{ 136, 7902},{ 148, 9418},{ 156,10826}, + { 162,12157},{ 169,13448},{ 177,14709},{ 188,15938}, + { 200,17133},{ 213,18295},{ 228,19433},{ 245,20564}, + { 264,21685},{ 289,22790},{ 323,23876},{ 368,24916}, + { 427,25906},{ 499,26837},{ 585,27700},{ 680,28514} + } + }, + { + /*Cb qi=2 INTRA*/ + { + { 1, 6},{ 30, 367},{ 58, 738},{ 77, 1172}, + { 93, 1645},{ 111, 2137},{ 123, 2642},{ 126, 3133}, + { 136, 3553},{ 162, 3934},{ 178, 4352},{ 183, 4803}, + { 199, 5231},{ 220, 5596},{ 235, 5957},{ 245, 6314}, + { 256, 6718},{ 286, 7048},{ 320, 7285},{ 336, 7568}, + { 366, 7829},{ 387, 8045},{ 405, 8261},{ 445, 8550} + }, + /*Cb qi=2 INTER*/ + { + { 115, -61},{ 93, 277},{ 71, 609},{ 54, 963}, + { 49, 1329},{ 53, 1715},{ 58, 2138},{ 63, 2583}, + { 69, 3017},{ 75, 3442},{ 81, 3857},{ 88, 4263}, + { 93, 4667},{ 96, 5065},{ 101, 5451},{ 101, 5832}, + { 102, 6213},{ 103, 6593},{ 103, 6968},{ 104, 7336}, + { 104, 7710},{ 105, 8076},{ 106, 8440},{ 106, 8822} + } + }, + { + /*Cr qi=2 INTRA*/ + { + { 1, 8},{ 27, 375},{ 54, 759},{ 70, 1222}, + { 79, 1696},{ 89, 2173},{ 106, 2652},{ 123, 3098}, + { 135, 3553},{ 143, 3972},{ 161, 4348},{ 181, 4782}, + { 194, 5189},{ 213, 5565},{ 235, 5907},{ 266, 6229}, + { 286, 6618},{ 311, 6897},{ 339, 7152},{ 362, 7454}, + { 392, 7721},{ 416, 7946},{ 429, 8227},{ 458, 8540} + }, + /*Cr qi=2 INTER*/ + { + { 74, 20},{ 63, 330},{ 51, 635},{ 44, 942}, + { 47, 1287},{ 54, 1710},{ 59, 2147},{ 65, 2571}, + { 72, 2996},{ 79, 3413},{ 86, 3820},{ 91, 4230}, + { 93, 4642},{ 95, 5046},{ 95, 5442},{ 95, 5839}, + { 96, 6243},{ 97, 6641},{ 99, 7021},{ 101, 7396}, + { 103, 7764},{ 106, 8138},{ 109, 8507},{ 114, 8851} + } + } + }, + { + { + /*Y' qi=3 INTRA*/ + { + { 91, -67},{ 141, 1606},{ 219, 3405},{ 328, 4929}, + { 433, 6122},{ 515, 7209},{ 598, 8204},{ 693, 9145}, + { 796, 9986},{ 912,10756},{ 1045,11471},{ 1200,12079}, + { 1345,12640},{ 1471,13179},{ 1571,13809},{ 1678,14450}, + { 1798,15047},{ 1905,15701},{ 2043,16205},{ 2202,16569}, + { 2351,16971},{ 2501,17393},{ 2660,17851},{ 2825,18455} + }, + /*Y' qi=3 INTER*/ + { + { 53, -164},{ 38, 1314},{ 59, 2917},{ 99, 4563}, + { 124, 6253},{ 139, 7882},{ 150, 9375},{ 159,10749}, + { 166,12059},{ 173,13349},{ 183,14608},{ 194,15826}, + { 208,17003},{ 223,18150},{ 240,19287},{ 259,20411}, + { 284,21508},{ 317,22593},{ 359,23656},{ 414,24671}, + { 483,25634},{ 569,26519},{ 670,27332},{ 786,28072} + } + }, + { + /*Cb qi=3 INTRA*/ + { + { 1, 5},{ 31, 367},{ 58, 739},{ 78, 1173}, + { 96, 1645},{ 113, 2134},{ 125, 2638},{ 133, 3127}, + { 148, 3542},{ 171, 3915},{ 184, 4328},{ 192, 4776}, + { 209, 5197},{ 230, 5556},{ 245, 5909},{ 252, 6261}, + { 272, 6641},{ 304, 6942},{ 330, 7184},{ 342, 7477}, + { 380, 7736},{ 404, 7962},{ 428, 8151},{ 469, 8430} + }, + /*Cb qi=3 INTER*/ + { + { 86, -29},{ 72, 296},{ 58, 618},{ 46, 964}, + { 47, 1338},{ 51, 1743},{ 56, 2158},{ 63, 2594}, + { 69, 3035},{ 77, 3455},{ 84, 3859},{ 89, 4266}, + { 94, 4673},{ 98, 5074},{ 101, 5460},{ 101, 5842}, + { 101, 6217},{ 101, 6593},{ 102, 6964},{ 104, 7325}, + { 103, 7696},{ 103, 8056},{ 104, 8430},{ 103, 8792} + } + }, + { + /*Cr qi=3 INTRA*/ + { + { 1, 8},{ 27, 374},{ 56, 759},{ 74, 1221}, + { 83, 1696},{ 96, 2173},{ 113, 2650},{ 127, 3091}, + { 140, 3542},{ 151, 3960},{ 164, 4334},{ 188, 4764}, + { 208, 5144},{ 224, 5493},{ 250, 5841},{ 278, 6162}, + { 298, 6548},{ 334, 6816},{ 365, 7045},{ 388, 7343}, + { 419, 7613},{ 443, 7836},{ 455, 8105},{ 484, 8445} + }, + /*Cr qi=3 INTER*/ + { + { 76, 26},{ 65, 332},{ 53, 638},{ 45, 945}, + { 45, 1304},{ 53, 1725},{ 60, 2153},{ 68, 2584}, + { 74, 3007},{ 81, 3425},{ 87, 3844},{ 91, 4253}, + { 94, 4657},{ 95, 5061},{ 94, 5462},{ 94, 5856}, + { 95, 6250},{ 96, 6635},{ 97, 7014},{ 101, 7393}, + { 104, 7761},{ 106, 8137},{ 109, 8506},{ 111, 8823} + } + } + }, + { + { + /*Y' qi=4 INTRA*/ + { + { 80, -67},{ 143, 1603},{ 227, 3378},{ 344, 4861}, + { 454, 6026},{ 537, 7104},{ 626, 8089},{ 725, 9006}, + { 830, 9827},{ 950,10581},{ 1089,11270},{ 1257,11826}, + { 1409,12366},{ 1535,12912},{ 1640,13528},{ 1753,14173}, + { 1884,14756},{ 2007,15368},{ 2148,15852},{ 2307,16212}, + { 2464,16591},{ 2614,17019},{ 2785,17455},{ 2970,17963} + }, + /*Y' qi=4 INTER*/ + { + { 50, -145},{ 38, 1324},{ 61, 2921},{ 102, 4566}, + { 127, 6248},{ 142, 7845},{ 154, 9300},{ 163,10656}, + { 169,11965},{ 177,13246},{ 188,14495},{ 202,15702}, + { 218,16864},{ 236,18003},{ 256,19124},{ 278,20233}, + { 307,21330},{ 347,22398},{ 398,23437},{ 463,24429}, + { 546,25343},{ 649,26170},{ 767,26935},{ 888,27674} + } + }, + { + /*Cb qi=4 INTRA*/ + { + { 1, 5},{ 33, 367},{ 61, 739},{ 80, 1173}, + { 98, 1646},{ 114, 2136},{ 126, 2639},{ 137, 3124}, + { 152, 3535},{ 176, 3903},{ 194, 4307},{ 206, 4753}, + { 222, 5165},{ 242, 5508},{ 260, 5857},{ 272, 6205}, + { 294, 6559},{ 332, 6848},{ 356, 7104},{ 364, 7389}, + { 396, 7637},{ 415, 7878},{ 446, 8064},{ 506, 8294} + }, + /*Cb qi=4 INTER*/ + { + { 86, -15},{ 73, 308},{ 60, 627},{ 46, 967}, + { 47, 1343},{ 51, 1754},{ 56, 2183},{ 63, 2615}, + { 70, 3044},{ 79, 3459},{ 85, 3866},{ 90, 4276}, + { 94, 4686},{ 97, 5088},{ 100, 5467},{ 102, 5837}, + { 102, 6205},{ 101, 6569},{ 103, 6939},{ 104, 7317}, + { 105, 7690},{ 107, 8043},{ 107, 8394},{ 111, 8736} + } + }, + { + /*Cr qi=4 INTRA*/ + { + { 1, 7},{ 28, 375},{ 57, 759},{ 79, 1221}, + { 92, 1697},{ 105, 2174},{ 122, 2648},{ 135, 3085}, + { 146, 3530},{ 157, 3947},{ 171, 4316},{ 195, 4737}, + { 218, 5117},{ 239, 5445},{ 268, 5767},{ 295, 6074}, + { 315, 6460},{ 355, 6735},{ 392, 6933},{ 418, 7218}, + { 448, 7495},{ 471, 7688},{ 481, 7954},{ 504, 8313} + }, + /*Cr qi=4 INTER*/ + { + { 68, 28},{ 57, 334},{ 47, 639},{ 43, 953}, + { 48, 1314},{ 54, 1736},{ 59, 2169},{ 69, 2592}, + { 78, 3017},{ 84, 3434},{ 88, 3850},{ 92, 4260}, + { 95, 4663},{ 96, 5068},{ 95, 5455},{ 95, 5839}, + { 96, 6243},{ 97, 6626},{ 98, 7006},{ 101, 7390}, + { 104, 7755},{ 108, 8115},{ 111, 8471},{ 110, 8825} + } + } + }, + { + { + /*Y' qi=5 INTRA*/ + { + { 84, -69},{ 147, 1599},{ 237, 3350},{ 360, 4796}, + { 475, 5934},{ 562, 6992},{ 657, 7953},{ 765, 8837}, + { 874, 9641},{ 998,10384},{ 1146,11047},{ 1322,11572}, + { 1484,12076},{ 1617,12609},{ 1731,13203},{ 1856,13806}, + { 1995,14367},{ 2132,14936},{ 2289,15386},{ 2460,15721}, + { 2635,16066},{ 2802,16442},{ 2980,16805},{ 3177,17272} + }, + /*Y' qi=5 INTER*/ + { + { 38, -86},{ 37, 1349},{ 64, 2920},{ 105, 4563}, + { 129, 6236},{ 145, 7809},{ 158, 9236},{ 167,10572}, + { 174,11871},{ 182,13141},{ 195,14368},{ 212,15558}, + { 230,16706},{ 250,17828},{ 274,18944},{ 303,20041}, + { 342,21116},{ 394,22152},{ 460,23144},{ 543,24073}, + { 648,24919},{ 773,25673},{ 922,26323},{ 1084,26924} + } + }, + { + /*Cb qi=5 INTRA*/ + { + { 1, 5},{ 34, 367},{ 63, 739},{ 82, 1174}, + { 102, 1647},{ 119, 2137},{ 134, 2639},{ 145, 3121}, + { 161, 3529},{ 189, 3891},{ 207, 4290},{ 216, 4721}, + { 232, 5113},{ 258, 5455},{ 277, 5798},{ 294, 6124}, + { 322, 6427},{ 352, 6697},{ 370, 6982},{ 384, 7283}, + { 423, 7529},{ 448, 7766},{ 478, 7943},{ 527, 8151} + }, + /*Cb qi=5 INTER*/ + { + { 83, -49},{ 69, 284},{ 55, 611},{ 48, 961}, + { 49, 1355},{ 52, 1769},{ 58, 2191},{ 65, 2616}, + { 73, 3041},{ 80, 3460},{ 87, 3868},{ 92, 4276}, + { 95, 4682},{ 98, 5077},{ 100, 5459},{ 102, 5827}, + { 102, 6200},{ 102, 6568},{ 103, 6930},{ 103, 7303}, + { 104, 7672},{ 106, 8032},{ 106, 8391},{ 106, 8727} + } + }, + { + /*Cr qi=5 INTRA*/ + { + { 1, 8},{ 28, 375},{ 57, 760},{ 81, 1222}, + { 99, 1696},{ 111, 2175},{ 125, 2648},{ 140, 3079}, + { 152, 3520},{ 162, 3927},{ 179, 4294},{ 203, 4714}, + { 225, 5080},{ 254, 5389},{ 286, 5703},{ 318, 5997}, + { 342, 6364},{ 380, 6640},{ 416, 6837},{ 445, 7103}, + { 473, 7370},{ 497, 7562},{ 514, 7811},{ 549, 8148} + }, + /*Cr qi=5 INTER*/ + { + { 60, 6},{ 54, 323},{ 46, 638},{ 43, 958}, + { 45, 1329},{ 54, 1749},{ 61, 2175},{ 70, 2600}, + { 79, 3021},{ 85, 3437},{ 89, 3847},{ 93, 4254}, + { 95, 4660},{ 96, 5065},{ 95, 5456},{ 95, 5849}, + { 96, 6243},{ 96, 6621},{ 97, 6996},{ 101, 7366}, + { 104, 7722},{ 107, 8088},{ 111, 8448},{ 119, 8816} + } + } + }, + { + { + /*Y' qi=6 INTRA*/ + { + { 88, -69},{ 151, 1593},{ 251, 3294},{ 387, 4681}, + { 507, 5790},{ 601, 6837},{ 702, 7787},{ 813, 8648}, + { 927, 9427},{ 1059,10152},{ 1213,10787},{ 1399,11284}, + { 1568,11781},{ 1705,12312},{ 1823,12890},{ 1957,13482}, + { 2106,14036},{ 2249,14600},{ 2411,15042},{ 2588,15359}, + { 2772,15699},{ 2947,16062},{ 3127,16429},{ 3320,16849} + }, + /*Y' qi=6 INTER*/ + { + { 44, -80},{ 36, 1346},{ 69, 2919},{ 111, 4563}, + { 136, 6216},{ 154, 7746},{ 168, 9139},{ 178,10461}, + { 185,11747},{ 195,13007},{ 211,14229},{ 230,15408}, + { 250,16547},{ 274,17663},{ 302,18769},{ 339,19851}, + { 386,20907},{ 446,21933},{ 527,22884},{ 631,23746}, + { 760,24512},{ 914,25178},{ 1087,25758},{ 1278,26262} + } + }, + { + /*Cb qi=6 INTRA*/ + { + { 1, 4},{ 36, 367},{ 66, 739},{ 84, 1174}, + { 105, 1648},{ 126, 2139},{ 140, 2639},{ 149, 3116}, + { 164, 3523},{ 194, 3880},{ 217, 4271},{ 226, 4694}, + { 243, 5077},{ 270, 5407},{ 291, 5742},{ 310, 6061}, + { 340, 6340},{ 373, 6609},{ 394, 6890},{ 409, 7189}, + { 444, 7434},{ 469, 7652},{ 499, 7853},{ 559, 8135} + }, + /*Cb qi=6 INTER*/ + { + { 68, -46},{ 60, 291},{ 50, 623},{ 49, 971}, + { 50, 1357},{ 55, 1781},{ 61, 2211},{ 69, 2634}, + { 78, 3052},{ 86, 3466},{ 91, 3882},{ 95, 4292}, + { 98, 4691},{ 101, 5080},{ 102, 5458},{ 103, 5830}, + { 103, 6192},{ 104, 6554},{ 104, 6916},{ 106, 7278}, + { 108, 7641},{ 110, 8004},{ 112, 8371},{ 112, 8758} + } + }, + { + /*Cr qi=6 INTRA*/ + { + { 1, 8},{ 29, 375},{ 59, 760},{ 84, 1223}, + { 99, 1698},{ 112, 2176},{ 129, 2647},{ 143, 3076}, + { 156, 3510},{ 168, 3906},{ 189, 4269},{ 220, 4682}, + { 241, 5047},{ 266, 5342},{ 299, 5649},{ 331, 5954}, + { 357, 6309},{ 393, 6579},{ 431, 6765},{ 467, 6997}, + { 501, 7276},{ 520, 7488},{ 525, 7749},{ 548, 8146} + }, + /*Cr qi=6 INTER*/ + { + { 94, 31},{ 69, 335},{ 47, 641},{ 43, 967}, + { 50, 1350},{ 57, 1772},{ 65, 2197},{ 74, 2625}, + { 83, 3043},{ 90, 3454},{ 94, 3867},{ 97, 4273}, + { 98, 4671},{ 99, 5068},{ 99, 5461},{ 98, 5857}, + { 98, 6245},{ 99, 6610},{ 103, 6975},{ 105, 7345}, + { 108, 7712},{ 111, 8073},{ 113, 8415},{ 119, 8768} + } + } + }, + { + { + /*Y' qi=7 INTRA*/ + { + { 92, -70},{ 156, 1590},{ 261, 3267},{ 403, 4618}, + { 529, 5704},{ 628, 6730},{ 736, 7657},{ 856, 8491}, + { 978, 9246},{ 1118, 9943},{ 1281,10550},{ 1472,11028}, + { 1645,11507},{ 1793,12008},{ 1924,12565},{ 2067,13130}, + { 2229,13638},{ 2388,14160},{ 2558,14584},{ 2744,14886}, + { 2932,15194},{ 3116,15531},{ 3311,15858},{ 3538,16197} + }, + /*Y' qi=7 INTER*/ + { + { 43, -8},{ 36, 1351},{ 71, 2923},{ 112, 4568}, + { 138, 6201},{ 157, 7705},{ 171, 9083},{ 181,10390}, + { 189,11664},{ 202,12910},{ 220,14121},{ 241,15281}, + { 266,16401},{ 295,17507},{ 328,18608},{ 371,19677}, + { 430,20701},{ 508,21676},{ 604,22588},{ 727,23397}, + { 878,24093},{ 1055,24690},{ 1263,25151},{ 1496,25504} + } + }, + { + /*Cb qi=7 INTRA*/ + { + { 1, 5},{ 40, 367},{ 72, 740},{ 89, 1175}, + { 108, 1649},{ 129, 2140},{ 143, 2637},{ 154, 3110}, + { 169, 3507},{ 198, 3860},{ 224, 4237},{ 235, 4652}, + { 253, 5037},{ 282, 5358},{ 307, 5674},{ 329, 5986}, + { 361, 6273},{ 393, 6527},{ 419, 6777},{ 435, 7078}, + { 467, 7342},{ 495, 7554},{ 529, 7757},{ 591, 8053} + }, + /*Cb qi=7 INTER*/ + { + { 79, -33},{ 68, 299},{ 56, 627},{ 50, 978}, + { 51, 1366},{ 55, 1786},{ 61, 2213},{ 70, 2642}, + { 80, 3062},{ 87, 3474},{ 92, 3886},{ 96, 4292}, + { 99, 4684},{ 102, 5072},{ 103, 5450},{ 104, 5814}, + { 104, 6176},{ 104, 6538},{ 107, 6905},{ 110, 7270}, + { 110, 7625},{ 110, 7978},{ 111, 8340},{ 117, 8674} + } + }, + { + /*Cr qi=7 INTRA*/ + { + { 2, 7},{ 31, 375},{ 62, 760},{ 87, 1223}, + { 103, 1698},{ 115, 2175},{ 131, 2644},{ 147, 3066}, + { 161, 3494},{ 175, 3889},{ 199, 4250},{ 229, 4653}, + { 250, 5001},{ 279, 5275},{ 311, 5577},{ 343, 5889}, + { 376, 6227},{ 417, 6486},{ 457, 6689},{ 484, 6925}, + { 518, 7174},{ 544, 7393},{ 549, 7662},{ 577, 8050} + }, + /*Cr qi=7 INTER*/ + { + { 89, 22},{ 62, 332},{ 45, 641},{ 47, 976}, + { 52, 1363},{ 59, 1779},{ 67, 2203},{ 76, 2628}, + { 84, 3046},{ 90, 3460},{ 94, 3875},{ 98, 4272}, + { 99, 4666},{ 98, 5063},{ 98, 5459},{ 98, 5849}, + { 99, 6226},{ 101, 6594},{ 104, 6957},{ 109, 7324}, + { 109, 7686},{ 111, 8042},{ 115, 8379},{ 119, 8699} + } + } + }, + { + { + /*Y' qi=8 INTRA*/ + { + { 91, -69},{ 160, 1585},{ 274, 3226},{ 423, 4538}, + { 557, 5596},{ 664, 6595},{ 778, 7506},{ 905, 8319}, + { 1038, 9035},{ 1186, 9701},{ 1355,10292},{ 1554,10754}, + { 1739,11196},{ 1904,11639},{ 2047,12184},{ 2194,12763}, + { 2361,13256},{ 2529,13753},{ 2709,14155},{ 2902,14433}, + { 3100,14723},{ 3292,15026},{ 3489,15327},{ 3714,15705} + }, + /*Y' qi=8 INTER*/ + { + { 32, -157},{ 33, 1346},{ 74, 2914},{ 116, 4554}, + { 142, 6172},{ 162, 7648},{ 177, 9004},{ 186,10300}, + { 196,11570},{ 210,12808},{ 231,14001},{ 256,15150}, + { 285,16259},{ 319,17352},{ 359,18435},{ 415,19475}, + { 489,20470},{ 584,21400},{ 703,22246},{ 852,22968}, + { 1038,23556},{ 1253,24032},{ 1503,24367},{ 1778,24628} + } + }, + { + /*Cb qi=8 INTRA*/ + { + { 1, 4},{ 42, 367},{ 75, 740},{ 93, 1176}, + { 111, 1649},{ 128, 2139},{ 144, 2635},{ 157, 3103}, + { 174, 3494},{ 206, 3844},{ 233, 4207},{ 251, 4605}, + { 277, 4980},{ 304, 5284},{ 335, 5584},{ 359, 5888}, + { 393, 6152},{ 432, 6398},{ 455, 6656},{ 471, 6956}, + { 502, 7193},{ 528, 7405},{ 562, 7630},{ 603, 7922} + }, + /*Cb qi=8 INTER*/ + { + { 77, -37},{ 68, 299},{ 58, 632},{ 50, 991}, + { 50, 1382},{ 55, 1799},{ 62, 2226},{ 73, 2647}, + { 82, 3066},{ 90, 3480},{ 94, 3891},{ 96, 4296}, + { 98, 4687},{ 101, 5073},{ 103, 5456},{ 104, 5817}, + { 105, 6170},{ 106, 6523},{ 107, 6886},{ 108, 7250}, + { 109, 7600},{ 110, 7955},{ 111, 8305},{ 112, 8641} + } + }, + { + /*Cr qi=8 INTRA*/ + { + { 2, 7},{ 33, 375},{ 64, 760},{ 92, 1224}, + { 111, 1700},{ 122, 2173},{ 137, 2637},{ 156, 3055}, + { 172, 3476},{ 186, 3856},{ 211, 4211},{ 242, 4597}, + { 263, 4939},{ 292, 5214},{ 335, 5489},{ 376, 5772}, + { 406, 6099},{ 440, 6378},{ 483, 6578},{ 517, 6797}, + { 550, 7049},{ 571, 7283},{ 583, 7560},{ 618, 7967} + }, + /*Cr qi=8 INTER*/ + { + { 74, 25},{ 58, 328},{ 43, 637},{ 45, 980}, + { 51, 1371},{ 59, 1788},{ 69, 2207},{ 79, 2630}, + { 86, 3051},{ 91, 3470},{ 95, 3880},{ 97, 4280}, + { 98, 4680},{ 97, 5074},{ 96, 5456},{ 97, 5839}, + { 99, 6219},{ 101, 6583},{ 103, 6945},{ 106, 7312}, + { 110, 7671},{ 114, 8009},{ 115, 8345},{ 117, 8686} + } + } + }, + { + { + /*Y' qi=9 INTRA*/ + { + { 104, -68},{ 164, 1580},{ 288, 3173},{ 448, 4439}, + { 587, 5485},{ 702, 6465},{ 824, 7351},{ 958, 8148}, + { 1096, 8845},{ 1253, 9480},{ 1432,10047},{ 1640,10494}, + { 1835,10926},{ 2015,11350},{ 2166,11871},{ 2321,12428}, + { 2508,12876},{ 2684,13345},{ 2866,13741},{ 3069,13991}, + { 3281,14243},{ 3487,14518},{ 3689,14813},{ 3911,15175} + }, + /*Y' qi=9 INTER*/ + { + { 47, -140},{ 34, 1348},{ 77, 2915},{ 119, 4552}, + { 145, 6150},{ 166, 7600},{ 182, 8936},{ 192,10221}, + { 203,11482},{ 220,12711},{ 244,13886},{ 274,15012}, + { 308,16111},{ 349,17190},{ 401,18244},{ 470,19257}, + { 561,20209},{ 680,21069},{ 830,21822},{ 1010,22463}, + { 1227,22971},{ 1482,23328},{ 1769,23544},{ 2077,23655} + } + }, + { + /*Cb qi=9 INTRA*/ + { + { 1, 5},{ 43, 367},{ 76, 740},{ 95, 1176}, + { 114, 1649},{ 135, 2138},{ 153, 2629},{ 165, 3091}, + { 184, 3481},{ 217, 3831},{ 244, 4187},{ 260, 4572}, + { 290, 4930},{ 320, 5231},{ 351, 5521},{ 379, 5812}, + { 414, 6055},{ 452, 6307},{ 483, 6564},{ 502, 6848}, + { 525, 7115},{ 554, 7321},{ 589, 7533},{ 626, 7833} + }, + /*Cb qi=9 INTER*/ + { + { 101, -43},{ 81, 298},{ 62, 637},{ 49, 989}, + { 51, 1381},{ 56, 1806},{ 65, 2231},{ 74, 2653}, + { 84, 3071},{ 91, 3482},{ 95, 3892},{ 97, 4293}, + { 99, 4684},{ 101, 5066},{ 103, 5437},{ 103, 5793}, + { 103, 6148},{ 104, 6511},{ 105, 6867},{ 107, 7221}, + { 110, 7572},{ 111, 7926},{ 112, 8283},{ 116, 8625} + } + }, + { + /*Cr qi=9 INTRA*/ + { + { 2, 7},{ 35, 375},{ 66, 761},{ 93, 1224}, + { 112, 1700},{ 126, 2173},{ 144, 2633},{ 165, 3047}, + { 183, 3458},{ 199, 3835},{ 224, 4191},{ 257, 4558}, + { 283, 4887},{ 309, 5176},{ 351, 5446},{ 397, 5713}, + { 433, 6017},{ 469, 6283},{ 508, 6480},{ 546, 6687}, + { 579, 6945},{ 600, 7182},{ 610, 7434},{ 623, 7793} + }, + /*Cr qi=9 INTER*/ + { + { 77, 15},{ 57, 330},{ 45, 640},{ 48, 980}, + { 54, 1380},{ 61, 1802},{ 70, 2220},{ 80, 2639}, + { 87, 3057},{ 92, 3474},{ 94, 3882},{ 98, 4282}, + { 98, 4675},{ 97, 5062},{ 97, 5450},{ 98, 5829}, + { 100, 6197},{ 101, 6561},{ 104, 6927},{ 107, 7289}, + { 113, 7638},{ 117, 7978},{ 119, 8311},{ 117, 8629} + } + } + }, + { + { + /*Y' qi=10 INTRA*/ + { + { 101, -69},{ 168, 1574},{ 299, 3143},{ 465, 4386}, + { 610, 5410},{ 736, 6353},{ 866, 7207},{ 1006, 7982}, + { 1153, 8655},{ 1319, 9261},{ 1504, 9812},{ 1719,10248}, + { 1928,10653},{ 2116,11056},{ 2282,11550},{ 2458,12070}, + { 2654,12492},{ 2846,12923},{ 3043,13291},{ 3249,13537}, + { 3466,13764},{ 3682,13999},{ 3896,14268},{ 4145,14548} + }, + /*Y' qi=10 INTER*/ + { + { 48, -94},{ 34, 1355},{ 81, 2920},{ 124, 4545}, + { 151, 6113},{ 174, 7532},{ 190, 8850},{ 201,10125}, + { 214,11379},{ 235,12591},{ 264,13745},{ 299,14859}, + { 338,15948},{ 388,17008},{ 456,18029},{ 546,18988}, + { 661,19877},{ 808,20666},{ 993,21321},{ 1218,21835}, + { 1481,22203},{ 1783,22420},{ 2117,22504},{ 2469,22481} + } + }, + { + /*Cb qi=10 INTRA*/ + { + { 2, 4},{ 44, 367},{ 79, 740},{ 99, 1178}, + { 117, 1652},{ 137, 2141},{ 156, 2630},{ 170, 3089}, + { 192, 3474},{ 227, 3813},{ 259, 4157},{ 282, 4526}, + { 310, 4860},{ 342, 5140},{ 377, 5425},{ 400, 5714}, + { 436, 5952},{ 475, 6194},{ 496, 6468},{ 522, 6748}, + { 559, 6996},{ 587, 7216},{ 617, 7433},{ 673, 7678} + }, + /*Cb qi=10 INTER*/ + { + { 87, -37},{ 72, 301},{ 58, 636},{ 49, 995}, + { 51, 1394},{ 57, 1819},{ 66, 2241},{ 78, 2660}, + { 87, 3074},{ 93, 3482},{ 97, 3891},{ 99, 4294}, + { 101, 4678},{ 103, 5050},{ 105, 5414},{ 106, 5773}, + { 107, 6134},{ 108, 6485},{ 110, 6832},{ 113, 7187}, + { 113, 7547},{ 114, 7887},{ 117, 8230},{ 112, 8590} + } + }, + { + /*Cr qi=10 INTRA*/ + { + { 2, 7},{ 38, 375},{ 69, 761},{ 96, 1224}, + { 116, 1701},{ 131, 2175},{ 148, 2634},{ 168, 3041}, + { 190, 3439},{ 211, 3802},{ 238, 4151},{ 271, 4506}, + { 297, 4824},{ 331, 5103},{ 373, 5360},{ 415, 5632}, + { 459, 5928},{ 500, 6176},{ 535, 6386},{ 573, 6586}, + { 608, 6834},{ 629, 7079},{ 642, 7337},{ 686, 7680} + }, + /*Cr qi=10 INTER*/ + { + { 81, 34},{ 63, 333},{ 50, 633},{ 48, 987}, + { 53, 1397},{ 61, 1820},{ 71, 2237},{ 83, 2651}, + { 91, 3065},{ 95, 3479},{ 98, 3882},{ 100, 4279}, + { 101, 4673},{ 101, 5054},{ 100, 5429},{ 101, 5801}, + { 102, 6173},{ 104, 6541},{ 108, 6904},{ 110, 7264}, + { 114, 7609},{ 119, 7945},{ 123, 8275},{ 128, 8615} + } + } + }, + { + { + /*Y' qi=11 INTRA*/ + { + { 110, -66},{ 176, 1564},{ 316, 3087},{ 492, 4296}, + { 645, 5299},{ 781, 6217},{ 924, 7039},{ 1075, 7776}, + { 1232, 8421},{ 1410, 9005},{ 1607, 9532},{ 1834, 9929}, + { 2053,10300},{ 2249,10697},{ 2427,11184},{ 2619,11682}, + { 2826,12083},{ 3019,12508},{ 3225,12869},{ 3452,13064}, + { 3670,13280},{ 3890,13519},{ 4123,13750},{ 4367,14059} + }, + /*Y' qi=11 INTER*/ + { + { 72, -115},{ 32, 1354},{ 83, 2911},{ 126, 4534}, + { 154, 6080},{ 178, 7475},{ 194, 8779},{ 205,10047}, + { 222,11290},{ 246,12488},{ 281,13621},{ 322,14714}, + { 372,15786},{ 436,16821},{ 519,17813},{ 628,18728}, + { 770,19549},{ 950,20254},{ 1175,20800},{ 1443,21197}, + { 1752,21446},{ 2095,21555},{ 2457,21553},{ 2808,21544} + } + }, + { + /*Cb qi=11 INTRA*/ + { + { 2, 4},{ 45, 367},{ 81, 740},{ 101, 1177}, + { 121, 1650},{ 142, 2136},{ 159, 2621},{ 174, 3075}, + { 199, 3451},{ 234, 3778},{ 265, 4117},{ 297, 4473}, + { 333, 4789},{ 367, 5054},{ 402, 5319},{ 427, 5613}, + { 462, 5871},{ 503, 6107},{ 532, 6336},{ 560, 6584}, + { 601, 6842},{ 631, 7092},{ 662, 7292},{ 721, 7497} + }, + /*Cb qi=11 INTER*/ + { + { 117, -24},{ 93, 308},{ 69, 638},{ 52, 993}, + { 52, 1395},{ 58, 1822},{ 68, 2246},{ 80, 2665}, + { 89, 3082},{ 94, 3492},{ 96, 3900},{ 98, 4299}, + { 101, 4679},{ 103, 5047},{ 104, 5405},{ 106, 5763}, + { 106, 6120},{ 107, 6474},{ 109, 6823},{ 112, 7163}, + { 115, 7516},{ 117, 7868},{ 118, 8213},{ 119, 8561} + } + }, + { + /*Cr qi=11 INTRA*/ + { + { 2, 7},{ 40, 375},{ 75, 761},{ 100, 1224}, + { 119, 1700},{ 137, 2169},{ 154, 2622},{ 178, 3025}, + { 198, 3416},{ 220, 3770},{ 255, 4114},{ 294, 4459}, + { 323, 4756},{ 359, 5028},{ 399, 5292},{ 438, 5556}, + { 483, 5827},{ 518, 6073},{ 551, 6298},{ 598, 6501}, + { 634, 6754},{ 652, 6997},{ 670, 7211},{ 689, 7560} + }, + /*Cr qi=11 INTER*/ + { + { 75, 30},{ 61, 334},{ 51, 639},{ 49, 995}, + { 53, 1403},{ 62, 1821},{ 73, 2237},{ 84, 2654}, + { 91, 3070},{ 95, 3485},{ 96, 3890},{ 98, 4287}, + { 98, 4672},{ 99, 5050},{ 99, 5427},{ 100, 5798}, + { 103, 6169},{ 105, 6528},{ 107, 6881},{ 113, 7233}, + { 118, 7580},{ 121, 7916},{ 125, 8240},{ 130, 8551} + } + } + }, + { + { + /*Y' qi=12 INTRA*/ + { + { 104, -69},{ 182, 1557},{ 335, 3040},{ 521, 4205}, + { 684, 5178},{ 831, 6068},{ 986, 6854},{ 1151, 7559}, + { 1323, 8169},{ 1523, 8704},{ 1736, 9192},{ 1978, 9558}, + { 2213, 9908},{ 2421,10298},{ 2613,10757},{ 2822,11208}, + { 3042,11585},{ 3250,11991},{ 3474,12308},{ 3710,12480}, + { 3939,12687},{ 4174,12902},{ 4416,13102},{ 4672,13369} + }, + /*Y' qi=12 INTER*/ + { + { 52, -91},{ 34, 1355},{ 86, 2911},{ 129, 4518}, + { 159, 6037},{ 184, 7405},{ 200, 8694},{ 213, 9955}, + { 232,11185},{ 263,12360},{ 304,13479},{ 354,14555}, + { 415,15601},{ 495,16608},{ 601,17549},{ 738,18400}, + { 915,19136},{ 1139,19724},{ 1414,20150},{ 1731,20412}, + { 2090,20520},{ 2473,20509},{ 2851,20442},{ 3227,20328} + } + }, + { + /*Cb qi=12 INTRA*/ + { + { 1, 4},{ 46, 367},{ 85, 740},{ 109, 1178}, + { 126, 1650},{ 145, 2134},{ 165, 2617},{ 182, 3061}, + { 209, 3428},{ 245, 3749},{ 281, 4077},{ 316, 4417}, + { 354, 4718},{ 392, 4970},{ 430, 5217},{ 456, 5501}, + { 490, 5771},{ 534, 5996},{ 571, 6207},{ 600, 6458}, + { 644, 6697},{ 675, 6942},{ 707, 7151},{ 766, 7342} + }, + /*Cb qi=12 INTER*/ + { + { 84, -24},{ 73, 311},{ 60, 644},{ 52, 998}, + { 53, 1398},{ 60, 1825},{ 71, 2249},{ 83, 2665}, + { 90, 3081},{ 94, 3490},{ 97, 3893},{ 99, 4286}, + { 102, 4663},{ 104, 5032},{ 105, 5393},{ 106, 5751}, + { 107, 6102},{ 108, 6445},{ 111, 6788},{ 113, 7136}, + { 114, 7483},{ 117, 7828},{ 121, 8163},{ 122, 8496} + } + }, + { + /*Cr qi=12 INTRA*/ + { + { 3, 7},{ 41, 375},{ 78, 761},{ 106, 1225}, + { 124, 1700},{ 140, 2167},{ 163, 2616},{ 188, 3010}, + { 213, 3385},{ 240, 3718},{ 271, 4062},{ 309, 4406}, + { 345, 4691},{ 387, 4956},{ 430, 5212},{ 469, 5467}, + { 513, 5729},{ 554, 5970},{ 587, 6176},{ 633, 6395}, + { 673, 6659},{ 692, 6868},{ 712, 7061},{ 758, 7259} + }, + /*Cr qi=12 INTER*/ + { + { 73, 31},{ 59, 335},{ 48, 638},{ 50, 998}, + { 56, 1410},{ 65, 1827},{ 75, 2240},{ 85, 2657}, + { 92, 3073},{ 95, 3485},{ 97, 3888},{ 99, 4279}, + { 98, 4663},{ 99, 5042},{ 101, 5412},{ 102, 5779}, + { 105, 6142},{ 107, 6498},{ 108, 6848},{ 113, 7198}, + { 118, 7540},{ 121, 7867},{ 127, 8188},{ 132, 8508} + } + } + }, + { + { + /*Y' qi=13 INTRA*/ + { + { 109, -68},{ 187, 1551},{ 347, 3010},{ 541, 4153}, + { 709, 5107},{ 864, 5975},{ 1026, 6745},{ 1194, 7433}, + { 1375, 8021},{ 1581, 8550},{ 1803, 9026},{ 2054, 9371}, + { 2301, 9713},{ 2522,10082},{ 2728,10515},{ 2949,10956}, + { 3184,11297},{ 3408,11653},{ 3643,11946},{ 3886,12100}, + { 4124,12277},{ 4377,12459},{ 4632,12635},{ 4898,12861} + }, + /*Y' qi=13 INTER*/ + { + { 48, -78},{ 35, 1357},{ 89, 2914},{ 133, 4512}, + { 164, 6004},{ 190, 7348},{ 207, 8627},{ 222, 9881}, + { 247,11096},{ 284,12251},{ 333,13350},{ 392,14407}, + { 466,15426},{ 565,16391},{ 696,17279},{ 865,18058}, + { 1085,18689},{ 1358,19156},{ 1684,19456},{ 2050,19605}, + { 2447,19614},{ 2855,19524},{ 3243,19398},{ 3611,19201} + } + }, + { + /*Cb qi=13 INTRA*/ + { + { 2, 4},{ 47, 367},{ 86, 741},{ 108, 1179}, + { 127, 1651},{ 150, 2133},{ 173, 2611},{ 194, 3050}, + { 222, 3417},{ 262, 3733},{ 303, 4048},{ 337, 4375}, + { 378, 4657},{ 420, 4897},{ 456, 5148},{ 486, 5422}, + { 518, 5682},{ 558, 5903},{ 592, 6113},{ 623, 6372}, + { 662, 6628},{ 700, 6833},{ 751, 6989},{ 805, 7147} + }, + /*Cb qi=13 INTER*/ + { + { 94, -34},{ 78, 303},{ 60, 638},{ 51, 994}, + { 54, 1406},{ 61, 1836},{ 73, 2253},{ 84, 2668}, + { 92, 3082},{ 96, 3492},{ 99, 3894},{ 101, 4284}, + { 103, 4659},{ 105, 5023},{ 106, 5376},{ 108, 5726}, + { 109, 6070},{ 110, 6418},{ 113, 6765},{ 117, 7105}, + { 119, 7448},{ 122, 7784},{ 126, 8119},{ 131, 8463} + } + }, + { + /*Cr qi=13 INTRA*/ + { + { 3, 7},{ 43, 375},{ 80, 762},{ 110, 1226}, + { 131, 1701},{ 149, 2166},{ 172, 2610},{ 196, 2999}, + { 221, 3359},{ 254, 3679},{ 292, 4005},{ 332, 4329}, + { 369, 4612},{ 408, 4880},{ 456, 5139},{ 500, 5388}, + { 544, 5631},{ 581, 5877},{ 615, 6101},{ 660, 6316}, + { 692, 6594},{ 714, 6795},{ 736, 6997},{ 789, 7290} + }, + /*Cr qi=13 INTER*/ + { + { 73, 28},{ 61, 336},{ 46, 642},{ 50, 1003}, + { 58, 1414},{ 67, 1832},{ 79, 2245},{ 87, 2660}, + { 93, 3075},{ 97, 3484},{ 99, 3888},{ 100, 4277}, + { 100, 4651},{ 100, 5027},{ 101, 5403},{ 102, 5765}, + { 105, 6116},{ 109, 6470},{ 113, 6825},{ 119, 7163}, + { 124, 7497},{ 127, 7827},{ 131, 8137},{ 135, 8437} + } + } + }, + { + { + /*Y' qi=14 INTRA*/ + { + { 113, -68},{ 191, 1545},{ 358, 2981},{ 559, 4104}, + { 733, 5044},{ 896, 5890},{ 1066, 6636},{ 1241, 7304}, + { 1428, 7886},{ 1642, 8402},{ 1872, 8871},{ 2128, 9219}, + { 2380, 9547},{ 2609, 9908},{ 2825,10321},{ 3055,10728}, + { 3294,11076},{ 3523,11425},{ 3766,11689},{ 4013,11845}, + { 4254,12022},{ 4506,12209},{ 4759,12383},{ 5013,12637} + }, + /*Y' qi=14 INTER*/ + { + { 58, -82},{ 38, 1362},{ 93, 2914},{ 138, 4492}, + { 171, 5962},{ 198, 7289},{ 216, 8559},{ 234, 9804}, + { 263,11005},{ 306,12143},{ 363,13222},{ 434,14259}, + { 523,15255},{ 639,16188},{ 794,17021},{ 1000,17717}, + { 1262,18260},{ 1575,18645},{ 1943,18841},{ 2356,18872}, + { 2782,18802},{ 3194,18682},{ 3576,18559},{ 3923,18447} + } + }, + { + /*Cb qi=14 INTRA*/ + { + { 2, 3},{ 50, 367},{ 91, 741},{ 114, 1180}, + { 134, 1651},{ 157, 2131},{ 181, 2601},{ 208, 3028}, + { 239, 3391},{ 279, 3706},{ 322, 4000},{ 361, 4309}, + { 406, 4587},{ 445, 4822},{ 482, 5067},{ 515, 5344}, + { 546, 5612},{ 589, 5821},{ 626, 6020},{ 655, 6276}, + { 701, 6523},{ 748, 6717},{ 796, 6876},{ 815, 7151} + }, + /*Cb qi=14 INTER*/ + { + { 80, -43},{ 68, 301},{ 56, 644},{ 50, 1004}, + { 54, 1412},{ 63, 1836},{ 75, 2253},{ 87, 2670}, + { 94, 3083},{ 98, 3487},{ 101, 3885},{ 103, 4271}, + { 106, 4645},{ 107, 5004},{ 108, 5358},{ 109, 5705}, + { 112, 6047},{ 115, 6388},{ 118, 6731},{ 121, 7081}, + { 126, 7421},{ 129, 7747},{ 132, 8076},{ 137, 8419} + } + }, + { + /*Cr qi=14 INTRA*/ + { + { 3, 6},{ 45, 375},{ 85, 762},{ 116, 1226}, + { 138, 1700},{ 158, 2163},{ 180, 2602},{ 206, 2985}, + { 236, 3333},{ 270, 3639},{ 310, 3956},{ 359, 4258}, + { 397, 4524},{ 430, 4802},{ 478, 5068},{ 527, 5316}, + { 572, 5560},{ 613, 5802},{ 654, 6012},{ 699, 6216}, + { 734, 6489},{ 755, 6707},{ 775, 6898},{ 841, 7111} + }, + /*Cr qi=14 INTER*/ + { + { 78, 0},{ 59, 322},{ 46, 649},{ 51, 1016}, + { 58, 1422},{ 68, 1839},{ 81, 2253},{ 90, 2666}, + { 95, 3080},{ 98, 3486},{ 101, 3881},{ 102, 4268}, + { 102, 4644},{ 103, 5017},{ 105, 5382},{ 106, 5743}, + { 108, 6093},{ 112, 6442},{ 118, 6791},{ 124, 7130}, + { 127, 7463},{ 133, 7784},{ 138, 8085},{ 142, 8395} + } + } + }, + { + { + /*Y' qi=15 INTRA*/ + { + { 111, -66},{ 197, 1538},{ 370, 2949},{ 579, 4050}, + { 762, 4968},{ 933, 5798},{ 1112, 6520},{ 1299, 7161}, + { 1497, 7725},{ 1723, 8219},{ 1967, 8654},{ 2234, 8990}, + { 2499, 9302},{ 2740, 9637},{ 2968,10039},{ 3215,10414}, + { 3473,10709},{ 3721,11015},{ 3971,11270},{ 4228,11402}, + { 4487,11543},{ 4752,11707},{ 5011,11871},{ 5290,12099} + }, + /*Y' qi=15 INTER*/ + { + { 59, -113},{ 37, 1349},{ 95, 2904},{ 139, 4478}, + { 174, 5929},{ 201, 7244},{ 220, 8505},{ 241, 9736}, + { 275,10922},{ 327,12040},{ 395,13097},{ 477,14114}, + { 585,15071},{ 730,15947},{ 917,16714},{ 1162,17326}, + { 1468,17770},{ 1833,18029},{ 2251,18111},{ 2694,18068}, + { 3125,17968},{ 3529,17845},{ 3908,17713},{ 4260,17587} + } + }, + { + /*Cb qi=15 INTRA*/ + { + { 2, 3},{ 51, 367},{ 94, 741},{ 120, 1180}, + { 140, 1651},{ 160, 2129},{ 184, 2591},{ 213, 3010}, + { 246, 3371},{ 289, 3680},{ 335, 3969},{ 374, 4274}, + { 418, 4546},{ 460, 4783},{ 498, 5019},{ 532, 5280}, + { 565, 5553},{ 608, 5765},{ 647, 5958},{ 683, 6193}, + { 732, 6433},{ 782, 6620},{ 832, 6769},{ 848, 7027} + }, + /*Cb qi=15 INTER*/ + { + { 71, -52},{ 63, 296},{ 54, 644},{ 50, 1010}, + { 53, 1417},{ 64, 1837},{ 77, 2253},{ 88, 2666}, + { 95, 3079},{ 98, 3487},{ 100, 3882},{ 103, 4264}, + { 106, 4633},{ 108, 4991},{ 109, 5343},{ 109, 5693}, + { 112, 6038},{ 114, 6371},{ 119, 6709},{ 123, 7051}, + { 125, 7385},{ 130, 7716},{ 135, 8050},{ 140, 8374} + } + }, + { + /*Cr qi=15 INTRA*/ + { + { 2, 6},{ 47, 375},{ 87, 763},{ 119, 1225}, + { 143, 1699},{ 162, 2158},{ 185, 2595},{ 213, 2971}, + { 246, 3315},{ 279, 3618},{ 320, 3920},{ 372, 4210}, + { 409, 4480},{ 446, 4756},{ 496, 5017},{ 542, 5263}, + { 590, 5487},{ 639, 5721},{ 687, 5923},{ 724, 6132}, + { 753, 6417},{ 781, 6622},{ 805, 6806},{ 856, 6977} + }, + /*Cr qi=15 INTER*/ + { + { 71, 3},{ 61, 326},{ 52, 651},{ 50, 1017}, + { 58, 1422},{ 69, 1837},{ 82, 2251},{ 90, 2668}, + { 95, 3080},{ 98, 3484},{ 101, 3877},{ 102, 4257}, + { 102, 4632},{ 101, 5005},{ 103, 5370},{ 106, 5733}, + { 110, 6082},{ 116, 6424},{ 120, 6774},{ 124, 7106}, + { 130, 7427},{ 135, 7748},{ 141, 8052},{ 147, 8333} + } + } + }, + { + { + /*Y' qi=16 INTRA*/ + { + { 114, -63},{ 206, 1525},{ 396, 2887},{ 618, 3945}, + { 816, 4832},{ 1002, 5626},{ 1196, 6319},{ 1401, 6923}, + { 1616, 7458},{ 1857, 7928},{ 2121, 8334},{ 2405, 8645}, + { 2685, 8934},{ 2938, 9255},{ 3175, 9638},{ 3433, 9990}, + { 3707,10263},{ 3958,10577},{ 4218,10807},{ 4488,10906}, + { 4760,11028},{ 5037,11148},{ 5306,11286},{ 5625,11463} + }, + /*Y' qi=16 INTER*/ + { + { 69, -153},{ 39, 1348},{ 98, 2894},{ 144, 4448}, + { 181, 5872},{ 209, 7167},{ 228, 8422},{ 254, 9644}, + { 297,10810},{ 359,11908},{ 438,12944},{ 539,13930}, + { 672,14842},{ 850,15650},{ 1085,16318},{ 1391,16793}, + { 1769,17082},{ 2200,17198},{ 2659,17174},{ 3116,17072}, + { 3547,16948},{ 3943,16819},{ 4299,16701},{ 4611,16644} + } + }, + { + /*Cb qi=16 INTRA*/ + { + { 3, 4},{ 54, 367},{ 97, 742},{ 122, 1181}, + { 143, 1651},{ 168, 2123},{ 197, 2575},{ 226, 2985}, + { 263, 3338},{ 314, 3631},{ 367, 3903},{ 409, 4200}, + { 453, 4468},{ 491, 4703},{ 528, 4932},{ 566, 5188}, + { 601, 5459},{ 647, 5672},{ 693, 5844},{ 734, 6058}, + { 784, 6305},{ 836, 6460},{ 882, 6602},{ 905, 6891} + }, + /*Cb qi=16 INTER*/ + { + { 75, -64},{ 67, 292},{ 56, 645},{ 51, 1016}, + { 54, 1421},{ 66, 1842},{ 79, 2257},{ 89, 2670}, + { 95, 3082},{ 98, 3488},{ 101, 3879},{ 104, 4258}, + { 106, 4623},{ 108, 4974},{ 109, 5321},{ 113, 5664}, + { 116, 6001},{ 117, 6341},{ 123, 6677},{ 128, 7004}, + { 130, 7336},{ 136, 7671},{ 143, 7996},{ 148, 8310} + } + }, + { + /*Cr qi=16 INTRA*/ + { + { 4, 7},{ 50, 375},{ 90, 763},{ 124, 1225}, + { 148, 1698},{ 168, 2154},{ 195, 2582},{ 227, 2948}, + { 263, 3279},{ 302, 3575},{ 343, 3865},{ 394, 4137}, + { 439, 4402},{ 482, 4672},{ 533, 4925},{ 579, 5165}, + { 626, 5382},{ 675, 5616},{ 725, 5812},{ 769, 5991}, + { 810, 6242},{ 848, 6430},{ 868, 6615},{ 944, 6732} + }, + /*Cr qi=16 INTER*/ + { + { 78, 11},{ 62, 327},{ 49, 650},{ 50, 1025}, + { 59, 1431},{ 72, 1841},{ 83, 2253},{ 90, 2671}, + { 95, 3084},{ 98, 3487},{ 100, 3879},{ 101, 4254}, + { 102, 4625},{ 103, 4994},{ 106, 5355},{ 108, 5708}, + { 111, 6058},{ 115, 6400},{ 121, 6733},{ 128, 7058}, + { 134, 7374},{ 140, 7691},{ 146, 7993},{ 146, 8317} + } + } + }, + { + { + /*Y' qi=17 INTRA*/ + { + { 112, -59},{ 210, 1515},{ 409, 2850},{ 640, 3882}, + { 844, 4748},{ 1038, 5529},{ 1240, 6206},{ 1452, 6803}, + { 1676, 7330},{ 1925, 7792},{ 2194, 8201},{ 2483, 8512}, + { 2766, 8801},{ 3027, 9121},{ 3279, 9482},{ 3548, 9810}, + { 3825,10069},{ 4088,10345},{ 4362,10544},{ 4638,10644}, + { 4915,10744},{ 5196,10850},{ 5471,10981},{ 5802,11136} + }, + /*Y' qi=17 INTER*/ + { + { 70, -147},{ 45, 1349},{ 106, 2894},{ 155, 4425}, + { 195, 5818},{ 225, 7099},{ 247, 8348},{ 278, 9565}, + { 328,10717},{ 399,11794},{ 491,12807},{ 609,13760}, + { 766,14623},{ 984,15349},{ 1274,15902},{ 1642,16256}, + { 2082,16411},{ 2563,16409},{ 3048,16315},{ 3508,16194}, + { 3924,16064},{ 4306,15938},{ 4656,15828},{ 4966,15733} + } + }, + { + /*Cb qi=17 INTRA*/ + { + { 3, 4},{ 57, 367},{ 101, 742},{ 126, 1182}, + { 148, 1650},{ 175, 2118},{ 207, 2565},{ 241, 2966}, + { 279, 3307},{ 331, 3588},{ 389, 3845},{ 435, 4132}, + { 474, 4408},{ 517, 4641},{ 560, 4869},{ 602, 5122}, + { 638, 5389},{ 672, 5610},{ 716, 5787},{ 758, 6002}, + { 817, 6226},{ 869, 6393},{ 916, 6530},{ 950, 6799} + }, + /*Cb qi=17 INTER*/ + { + { 105, -65},{ 86, 288},{ 66, 638},{ 54, 1014}, + { 59, 1427},{ 71, 1844},{ 86, 2257},{ 95, 2668}, + { 100, 3075},{ 103, 3476},{ 106, 3867},{ 110, 4241}, + { 112, 4598},{ 114, 4948},{ 117, 5294},{ 121, 5633}, + { 123, 5968},{ 126, 6301},{ 131, 6637},{ 136, 6968}, + { 144, 7287},{ 152, 7606},{ 158, 7931},{ 162, 8262} + } + }, + { + /*Cr qi=17 INTRA*/ + { + { 4, 6},{ 55, 376},{ 97, 765},{ 128, 1226}, + { 152, 1696},{ 175, 2144},{ 204, 2568},{ 241, 2928}, + { 282, 3250},{ 323, 3530},{ 368, 3811},{ 420, 4089}, + { 463, 4347},{ 505, 4609},{ 562, 4860},{ 609, 5094}, + { 655, 5303},{ 709, 5535},{ 759, 5740},{ 803, 5913}, + { 844, 6153},{ 879, 6350},{ 905, 6527},{ 972, 6637} + }, + /*Cr qi=17 INTER*/ + { + { 88, 8},{ 68, 330},{ 51, 653},{ 54, 1028}, + { 65, 1433},{ 77, 1845},{ 89, 2257},{ 96, 2669}, + { 100, 3081},{ 102, 3481},{ 105, 3867},{ 106, 4245}, + { 108, 4613},{ 110, 4971},{ 112, 5328},{ 115, 5679}, + { 120, 6019},{ 127, 6355},{ 133, 6686},{ 140, 7007}, + { 149, 7316},{ 158, 7618},{ 166, 7924},{ 170, 8232} + } + } + }, + { + { + /*Y' qi=18 INTRA*/ + { + { 122, -58},{ 216, 1506},{ 425, 2815},{ 665, 3822}, + { 882, 4666},{ 1088, 5425},{ 1301, 6084},{ 1529, 6653}, + { 1766, 7162},{ 2026, 7611},{ 2312, 7987},{ 2612, 8278}, + { 2913, 8551},{ 3196, 8840},{ 3454, 9184},{ 3734, 9490}, + { 4030, 9725},{ 4305, 9973},{ 4585,10162},{ 4864,10251}, + { 5150,10324},{ 5443,10420},{ 5727,10536},{ 6053,10682} + }, + /*Y' qi=18 INTER*/ + { + { 66, -143},{ 47, 1351},{ 108, 2886},{ 158, 4401}, + { 200, 5775},{ 232, 7044},{ 256, 8288},{ 292, 9493}, + { 351,10625},{ 434,11679},{ 541,12665},{ 681,13578}, + { 875,14379},{ 1136,15025},{ 1483,15475},{ 1914,15709}, + { 2399,15767},{ 2907,15699},{ 3400,15579},{ 3852,15453}, + { 4259,15332},{ 4630,15221},{ 4976,15121},{ 5294,15061} + } + }, + { + /*Cb qi=18 INTRA*/ + { + { 2, 3},{ 61, 367},{ 107, 743},{ 131, 1182}, + { 155, 1648},{ 183, 2110},{ 220, 2542},{ 260, 2927}, + { 303, 3265},{ 359, 3540},{ 416, 3785},{ 462, 4063}, + { 506, 4334},{ 553, 4567},{ 595, 4797},{ 636, 5049}, + { 676, 5304},{ 717, 5516},{ 759, 5698},{ 801, 5904}, + { 861, 6133},{ 911, 6311},{ 962, 6443},{ 1021, 6645} + }, + /*Cb qi=18 INTER*/ + { + { 126, 5},{ 95, 326},{ 66, 643},{ 55, 1015}, + { 60, 1427},{ 73, 1843},{ 87, 2256},{ 96, 2667}, + { 101, 3073},{ 104, 3470},{ 108, 3853},{ 111, 4226}, + { 114, 4584},{ 117, 4928},{ 119, 5274},{ 122, 5612}, + { 126, 5942},{ 130, 6271},{ 136, 6606},{ 141, 6931}, + { 148, 7247},{ 156, 7568},{ 164, 7891},{ 173, 8211} + } + }, + { + /*Cr qi=18 INTRA*/ + { + { 4, 6},{ 59, 376},{ 104, 765},{ 133, 1226}, + { 156, 1692},{ 184, 2136},{ 218, 2548},{ 260, 2893}, + { 308, 3204},{ 348, 3481},{ 397, 3751},{ 448, 4024}, + { 490, 4281},{ 541, 4523},{ 593, 4776},{ 634, 5022}, + { 685, 5236},{ 748, 5455},{ 812, 5638},{ 856, 5818}, + { 891, 6048},{ 928, 6230},{ 961, 6405},{ 1055, 6449} + }, + /*Cr qi=18 INTER*/ + { + { 81, 34},{ 68, 342},{ 57, 652},{ 59, 1027}, + { 67, 1439},{ 80, 1848},{ 91, 2257},{ 97, 2670}, + { 100, 3076},{ 103, 3473},{ 106, 3857},{ 108, 4231}, + { 109, 4599},{ 110, 4958},{ 113, 5307},{ 119, 5650}, + { 125, 5991},{ 130, 6325},{ 138, 6651},{ 147, 6971}, + { 153, 7278},{ 162, 7578},{ 172, 7874},{ 177, 8156} + } + } + }, + { + { + /*Y' qi=19 INTRA*/ + { + { 128, -55},{ 228, 1495},{ 448, 2775},{ 699, 3758}, + { 931, 4571},{ 1154, 5296},{ 1386, 5914},{ 1636, 6450}, + { 1894, 6930},{ 2177, 7342},{ 2479, 7698},{ 2792, 7976}, + { 3099, 8235},{ 3392, 8517},{ 3658, 8853},{ 3938, 9155}, + { 4242, 9371},{ 4527, 9605},{ 4810, 9781},{ 5089, 9853}, + { 5378, 9920},{ 5674,10009},{ 5972,10110},{ 6336,10196} + }, + /*Y' qi=19 INTER*/ + { + { 69, -147},{ 49, 1353},{ 111, 2883},{ 162, 4381}, + { 205, 5737},{ 237, 6996},{ 264, 8232},{ 307, 9421}, + { 376,10534},{ 472,11567},{ 596,12525},{ 761,13395}, + { 990,14130},{ 1298,14694},{ 1695,15053},{ 2172,15195}, + { 2696,15173},{ 3213,15075},{ 3696,14948},{ 4141,14829}, + { 4541,14721},{ 4910,14609},{ 5245,14506},{ 5536,14399} + } + }, + { + /*Cb qi=19 INTRA*/ + { + { 3, 3},{ 61, 367},{ 109, 743},{ 135, 1182}, + { 161, 1646},{ 191, 2101},{ 229, 2524},{ 273, 2898}, + { 318, 3221},{ 376, 3490},{ 436, 3731},{ 487, 3994}, + { 539, 4251},{ 584, 4485},{ 621, 4721},{ 664, 4967}, + { 709, 5225},{ 752, 5431},{ 801, 5595},{ 846, 5796}, + { 912, 6011},{ 959, 6193},{ 1015, 6321},{ 1121, 6504} + }, + /*Cb qi=19 INTER*/ + { + { 126, 4},{ 97, 329},{ 69, 649},{ 56, 1017}, + { 61, 1432},{ 74, 1846},{ 88, 2255},{ 98, 2663}, + { 103, 3065},{ 106, 3460},{ 110, 3844},{ 114, 4211}, + { 117, 4564},{ 120, 4911},{ 122, 5253},{ 125, 5588}, + { 129, 5916},{ 135, 6241},{ 142, 6567},{ 149, 6885}, + { 155, 7206},{ 163, 7527},{ 174, 7843},{ 188, 8145} + } + }, + { + /*Cr qi=19 INTRA*/ + { + { 5, 6},{ 61, 376},{ 106, 765},{ 135, 1225}, + { 160, 1689},{ 192, 2126},{ 229, 2531},{ 271, 2869}, + { 321, 3168},{ 370, 3433},{ 421, 3704},{ 476, 3965}, + { 520, 4212},{ 572, 4452},{ 629, 4691},{ 671, 4939}, + { 724, 5152},{ 792, 5347},{ 858, 5510},{ 895, 5696}, + { 939, 5905},{ 991, 6056},{ 1027, 6244},{ 1127, 6333} + }, + /*Cr qi=19 INTER*/ + { + { 80, 45},{ 66, 344},{ 55, 654},{ 56, 1030}, + { 66, 1440},{ 80, 1850},{ 91, 2259},{ 98, 2668}, + { 102, 3072},{ 104, 3466},{ 107, 3845},{ 109, 4215}, + { 110, 4578},{ 112, 4933},{ 116, 5283},{ 122, 5625}, + { 129, 5963},{ 136, 6287},{ 143, 6611},{ 151, 6927}, + { 160, 7229},{ 170, 7528},{ 181, 7818},{ 191, 8092} + } + } + }, + { + { + /*Y' qi=20 INTRA*/ + { + { 129, -50},{ 238, 1481},{ 469, 2728},{ 730, 3684}, + { 974, 4473},{ 1213, 5171},{ 1463, 5763},{ 1729, 6281}, + { 2002, 6744},{ 2299, 7146},{ 2613, 7492},{ 2940, 7746}, + { 3265, 7978},{ 3571, 8228},{ 3853, 8543},{ 4156, 8815}, + { 4476, 9001},{ 4775, 9218},{ 5070, 9373},{ 5352, 9446}, + { 5649, 9510},{ 5956, 9580},{ 6268, 9660},{ 6647, 9705} + }, + /*Y' qi=20 INTER*/ + { + { 64, -93},{ 52, 1340},{ 116, 2862},{ 170, 4344}, + { 216, 5678},{ 249, 6928},{ 281, 8155},{ 333, 9326}, + { 418,10410},{ 533,11411},{ 683,12329},{ 890,13127}, + { 1183,13750},{ 1579,14162},{ 2066,14357},{ 2611,14370}, + { 3159,14284},{ 3675,14167},{ 4142,14053},{ 4568,13953}, + { 4961,13852},{ 5320,13755},{ 5649,13675},{ 5933,13610} + } + }, + { + /*Cb qi=20 INTRA*/ + { + { 3, 3},{ 62, 367},{ 112, 743},{ 140, 1183}, + { 165, 1646},{ 196, 2099},{ 235, 2517},{ 284, 2883}, + { 334, 3198},{ 393, 3460},{ 457, 3690},{ 509, 3945}, + { 560, 4198},{ 605, 4435},{ 647, 4658},{ 699, 4888}, + { 742, 5155},{ 788, 5350},{ 835, 5517},{ 880, 5730}, + { 956, 5914},{ 1007, 6060},{ 1053, 6199},{ 1158, 6358} + }, + /*Cb qi=20 INTER*/ + { + { 128, -6},{ 96, 322},{ 66, 653},{ 54, 1025}, + { 63, 1431},{ 79, 1844},{ 91, 2256},{ 99, 2665}, + { 104, 3065},{ 107, 3455},{ 111, 3831},{ 115, 4189}, + { 120, 4539},{ 123, 4885},{ 126, 5219},{ 130, 5548}, + { 135, 5876},{ 141, 6199},{ 149, 6519},{ 156, 6837}, + { 166, 7153},{ 179, 7468},{ 189, 7784},{ 194, 8102} + } + }, + { + /*Cr qi=20 INTRA*/ + { + { 4, 6},{ 63, 376},{ 109, 765},{ 139, 1225}, + { 165, 1689},{ 199, 2124},{ 239, 2523},{ 285, 2852}, + { 340, 3140},{ 388, 3398},{ 438, 3662},{ 499, 3914}, + { 547, 4155},{ 596, 4392},{ 652, 4634},{ 699, 4877}, + { 759, 5074},{ 824, 5257},{ 883, 5428},{ 936, 5589}, + { 986, 5790},{ 1030, 5960},{ 1074, 6119},{ 1172, 6191} + }, + /*Cr qi=20 INTER*/ + { + { 92, 40},{ 70, 345},{ 55, 658},{ 57, 1034}, + { 69, 1441},{ 84, 1852},{ 94, 2261},{ 98, 2669}, + { 102, 3074},{ 105, 3465},{ 107, 3841},{ 110, 4206}, + { 112, 4562},{ 116, 4915},{ 121, 5260},{ 127, 5591}, + { 134, 5920},{ 142, 6246},{ 153, 6562},{ 163, 6870}, + { 173, 7170},{ 186, 7463},{ 198, 7746},{ 199, 8030} + } + } + }, + { + { + /*Y' qi=21 INTRA*/ + { + { 130, -51},{ 244, 1476},{ 483, 2705},{ 756, 3635}, + { 1013, 4396},{ 1266, 5070},{ 1530, 5647},{ 1806, 6153}, + { 2093, 6600},{ 2411, 6976},{ 2739, 7299},{ 3079, 7534}, + { 3422, 7744},{ 3738, 7987},{ 4032, 8274},{ 4348, 8533}, + { 4675, 8721},{ 4989, 8909},{ 5291, 9051},{ 5577, 9111}, + { 5879, 9163},{ 6190, 9228},{ 6506, 9286},{ 6899, 9295} + }, + /*Y' qi=21 INTER*/ + { + { 64, -56},{ 55, 1341},{ 119, 2859},{ 174, 4324}, + { 223, 5640},{ 258, 6880},{ 295, 8096},{ 359, 9246}, + { 460,10302},{ 595,11268},{ 778,12131},{ 1032,12857}, + { 1387,13385},{ 1850,13683},{ 2399,13774},{ 2976,13729}, + { 3527,13619},{ 4034,13504},{ 4492,13401},{ 4912,13291}, + { 5298,13209},{ 5648,13137},{ 5974,13046},{ 6308,12977} + } + }, + { + /*Cb qi=21 INTRA*/ + { + { 4, 3},{ 64, 367},{ 114, 743},{ 141, 1183}, + { 166, 1645},{ 201, 2092},{ 247, 2502},{ 299, 2856}, + { 352, 3158},{ 413, 3412},{ 480, 3642},{ 536, 3893}, + { 588, 4137},{ 637, 4367},{ 678, 4598},{ 725, 4834}, + { 774, 5083},{ 827, 5269},{ 883, 5420},{ 930, 5633}, + { 999, 5829},{ 1057, 5959},{ 1113, 6082},{ 1200, 6265} + }, + /*Cb qi=21 INTER*/ + { + { 109, -8},{ 84, 321},{ 62, 654},{ 54, 1028}, + { 64, 1434},{ 80, 1847},{ 92, 2259},{ 100, 2664}, + { 105, 3060},{ 109, 3445},{ 114, 3815},{ 118, 4172}, + { 122, 4519},{ 126, 4861},{ 128, 5194},{ 133, 5520}, + { 139, 5847},{ 146, 6169},{ 155, 6487},{ 166, 6801}, + { 177, 7114},{ 189, 7423},{ 201, 7729},{ 208, 8035} + } + }, + { + /*Cr qi=21 INTRA*/ + { + { 4, 6},{ 64, 377},{ 111, 766},{ 144, 1225}, + { 174, 1683},{ 206, 2114},{ 248, 2506},{ 302, 2824}, + { 357, 3099},{ 404, 3357},{ 455, 3622},{ 519, 3867}, + { 573, 4098},{ 625, 4331},{ 683, 4571},{ 733, 4802}, + { 793, 4994},{ 863, 5173},{ 926, 5337},{ 978, 5492}, + { 1030, 5685},{ 1079, 5856},{ 1126, 6027},{ 1217, 6159} + }, + /*Cr qi=21 INTER*/ + { + { 82, 29},{ 67, 341},{ 55, 660},{ 58, 1038}, + { 71, 1443},{ 85, 1851},{ 95, 2258},{ 99, 2666}, + { 103, 3069},{ 107, 3456},{ 110, 3826},{ 112, 4188}, + { 114, 4544},{ 118, 4891},{ 124, 5231},{ 132, 5567}, + { 139, 5894},{ 148, 6210},{ 159, 6520},{ 171, 6822}, + { 185, 7111},{ 196, 7403},{ 209, 7691},{ 225, 7945} + } + } + }, + { + { + /*Y' qi=22 INTRA*/ + { + { 128, -45},{ 254, 1463},{ 507, 2662},{ 794, 3562}, + { 1070, 4292},{ 1340, 4941},{ 1622, 5492},{ 1920, 5968}, + { 2229, 6387},{ 2565, 6742},{ 2911, 7047},{ 3263, 7264}, + { 3615, 7464},{ 3944, 7689},{ 4258, 7950},{ 4591, 8183}, + { 4934, 8347},{ 5259, 8517},{ 5573, 8634},{ 5870, 8683}, + { 6186, 8723},{ 6508, 8762},{ 6831, 8801},{ 7232, 8830} + }, + /*Y' qi=22 INTER*/ + { + { 77, -48},{ 57, 1343},{ 122, 2853},{ 180, 4299}, + { 231, 5597},{ 269, 6826},{ 314, 8025},{ 393, 9150}, + { 512,10179},{ 673,11103},{ 894,11908},{ 1207,12542}, + { 1635,12956},{ 2166,13148},{ 2755,13167},{ 3345,13088}, + { 3895,12966},{ 4386,12848},{ 4832,12746},{ 5252,12647}, + { 5634,12563},{ 5978,12497},{ 6299,12412},{ 6633,12338} + } + }, + { + /*Cb qi=22 INTRA*/ + { + { 4, 3},{ 66, 367},{ 122, 744},{ 153, 1182}, + { 177, 1640},{ 213, 2080},{ 263, 2475},{ 323, 2811}, + { 382, 3103},{ 451, 3346},{ 522, 3568},{ 581, 3814}, + { 633, 4054},{ 674, 4288},{ 719, 4523},{ 768, 4756}, + { 823, 4979},{ 883, 5162},{ 937, 5325},{ 996, 5510}, + { 1070, 5687},{ 1129, 5807},{ 1193, 5929},{ 1311, 6099} + }, + /*Cb qi=22 INTER*/ + { + { 107, -5},{ 83, 322},{ 61, 653},{ 55, 1030}, + { 66, 1436},{ 81, 1845},{ 94, 2253},{ 102, 2656}, + { 107, 3050},{ 111, 3435},{ 115, 3804},{ 119, 4158}, + { 124, 4501},{ 128, 4835},{ 132, 5164},{ 138, 5490}, + { 146, 5812},{ 154, 6128},{ 163, 6442},{ 174, 6754}, + { 188, 7060},{ 205, 7361},{ 219, 7662},{ 233, 7953} + } + }, + { + /*Cr qi=22 INTRA*/ + { + { 4, 6},{ 67, 378},{ 118, 767},{ 151, 1222}, + { 182, 1675},{ 221, 2097},{ 269, 2476},{ 329, 2774}, + { 389, 3039},{ 444, 3292},{ 500, 3545},{ 560, 3788}, + { 615, 4020},{ 671, 4251},{ 734, 4484},{ 781, 4712}, + { 850, 4887},{ 925, 5060},{ 981, 5229},{ 1031, 5369}, + { 1092, 5549},{ 1148, 5715},{ 1200, 5861},{ 1291, 5943} + }, + /*Cr qi=22 INTER*/ + { + { 88, 34},{ 69, 340},{ 57, 657},{ 60, 1039}, + { 73, 1445},{ 87, 1851},{ 96, 2257},{ 100, 2662}, + { 103, 3058},{ 107, 3442},{ 111, 3812},{ 115, 4172}, + { 118, 4524},{ 123, 4864},{ 129, 5199},{ 136, 5531}, + { 145, 5855},{ 156, 6168},{ 170, 6468},{ 184, 6765}, + { 193, 7066},{ 207, 7353},{ 222, 7628},{ 230, 7900} + } + } + }, + { + { + /*Y' qi=23 INTRA*/ + { + { 126, -40},{ 257, 1458},{ 521, 2636},{ 825, 3501}, + { 1111, 4207},{ 1391, 4842},{ 1684, 5385},{ 1992, 5858}, + { 2311, 6277},{ 2653, 6626},{ 3005, 6929},{ 3366, 7134}, + { 3729, 7311},{ 4071, 7526},{ 4396, 7770},{ 4734, 7986}, + { 5086, 8131},{ 5421, 8286},{ 5735, 8404},{ 6033, 8456}, + { 6357, 8486},{ 6682, 8525},{ 7003, 8573},{ 7387, 8604} + }, + /*Y' qi=23 INTER*/ + { + { 64, -57},{ 60, 1345},{ 124, 2853},{ 185, 4284}, + { 239, 5565},{ 282, 6783},{ 336, 7967},{ 429, 9069}, + { 568,10063},{ 758,10943},{ 1028,11679},{ 1407,12216}, + { 1909,12520},{ 2502,12616},{ 3126,12573},{ 3722,12461}, + { 4258,12344},{ 4742,12236},{ 5185,12136},{ 5590,12052}, + { 5970,11980},{ 6315,11901},{ 6631,11826},{ 6954,11769} + } + }, + { + /*Cb qi=23 INTRA*/ + { + { 3, 3},{ 70, 367},{ 124, 744},{ 151, 1182}, + { 181, 1637},{ 222, 2071},{ 276, 2460},{ 343, 2785}, + { 403, 3072},{ 468, 3317},{ 542, 3534},{ 605, 3773}, + { 659, 4009},{ 703, 4243},{ 747, 4479},{ 795, 4707}, + { 852, 4923},{ 908, 5105},{ 972, 5254},{ 1043, 5423}, + { 1118, 5594},{ 1172, 5731},{ 1240, 5853},{ 1365, 6005} + }, + /*Cb qi=23 INTER*/ + { + { 109, -10},{ 87, 325},{ 63, 650},{ 57, 1031}, + { 67, 1439},{ 83, 1847},{ 96, 2253},{ 103, 2652}, + { 109, 3041},{ 114, 3421},{ 117, 3789},{ 122, 4141}, + { 128, 4480},{ 134, 4811},{ 139, 5138},{ 144, 5463}, + { 152, 5781},{ 161, 6096},{ 174, 6404},{ 185, 6714}, + { 198, 7023},{ 216, 7320},{ 233, 7621},{ 245, 7935} + } + }, + { + /*Cr qi=23 INTRA*/ + { + { 5, 6},{ 70, 379},{ 122, 768},{ 155, 1222}, + { 187, 1671},{ 231, 2088},{ 283, 2459},{ 346, 2750}, + { 411, 3009},{ 465, 3261},{ 523, 3509},{ 585, 3746}, + { 639, 3980},{ 695, 4219},{ 754, 4449},{ 803, 4671}, + { 873, 4840},{ 953, 5001},{ 1015, 5156},{ 1071, 5286}, + { 1137, 5464},{ 1191, 5629},{ 1249, 5782},{ 1359, 5885} + }, + /*Cr qi=23 INTER*/ + { + { 84, 29},{ 69, 343},{ 58, 660},{ 62, 1041}, + { 75, 1448},{ 88, 1853},{ 97, 2258},{ 102, 2659}, + { 105, 3050},{ 108, 3430},{ 113, 3799},{ 116, 4155}, + { 121, 4505},{ 126, 4845},{ 132, 5176},{ 142, 5504}, + { 153, 5826},{ 165, 6133},{ 180, 6432},{ 197, 6722}, + { 212, 7005},{ 226, 7287},{ 244, 7555},{ 258, 7828} + } + } + }, + { + { + /*Y' qi=24 INTRA*/ + { + { 125, -34},{ 268, 1444},{ 547, 2590},{ 866, 3422}, + { 1172, 4098},{ 1476, 4702},{ 1790, 5222},{ 2117, 5678}, + { 2453, 6080},{ 2811, 6418},{ 3178, 6700},{ 3552, 6895}, + { 3928, 7055},{ 4286, 7243},{ 4627, 7477},{ 4981, 7674}, + { 5344, 7802},{ 5683, 7944},{ 6009, 8043},{ 6313, 8082}, + { 6633, 8111},{ 6959, 8151},{ 7280, 8197},{ 7660, 8221} + }, + /*Y' qi=24 INTER*/ + { + { 62, -63},{ 68, 1345},{ 134, 2840},{ 199, 4245}, + { 256, 5508},{ 304, 6715},{ 371, 7880},{ 484, 8950}, + { 652, 9899},{ 892,10709},{ 1238,11334},{ 1722,11722}, + { 2326,11875},{ 2983,11864},{ 3616,11783},{ 4189,11678}, + { 4707,11570},{ 5178,11476},{ 5617,11395},{ 6017,11319}, + { 6380,11252},{ 6720,11185},{ 7044,11126},{ 7377,11118} + } + }, + { + /*Cb qi=24 INTRA*/ + { + { 4, 3},{ 75, 367},{ 132, 745},{ 159, 1182}, + { 187, 1634},{ 230, 2061},{ 289, 2439},{ 361, 2753}, + { 425, 3034},{ 492, 3278},{ 566, 3490},{ 630, 3720}, + { 686, 3956},{ 732, 4190},{ 777, 4420},{ 829, 4637}, + { 894, 4840},{ 958, 5012},{ 1023, 5155},{ 1090, 5326}, + { 1165, 5502},{ 1226, 5622},{ 1299, 5717},{ 1408, 5887} + }, + /*Cb qi=24 INTER*/ + { + { 110, 35},{ 92, 337},{ 70, 651},{ 63, 1033}, + { 74, 1440},{ 91, 1846},{ 102, 2248},{ 109, 2644}, + { 114, 3031},{ 120, 3404},{ 127, 3762},{ 133, 4109}, + { 138, 4445},{ 144, 4772},{ 151, 5094},{ 159, 5411}, + { 168, 5728},{ 180, 6037},{ 195, 6338},{ 210, 6640}, + { 227, 6944},{ 249, 7236},{ 272, 7528},{ 299, 7809} + } + }, + { + /*Cr qi=24 INTRA*/ + { + { 5, 6},{ 72, 380},{ 124, 770},{ 158, 1222}, + { 195, 1668},{ 240, 2079},{ 297, 2438},{ 367, 2715}, + { 433, 2966},{ 488, 3218},{ 549, 3467},{ 609, 3701}, + { 664, 3935},{ 728, 4165},{ 792, 4379},{ 845, 4586}, + { 917, 4744},{ 995, 4898},{ 1063, 5049},{ 1120, 5187}, + { 1190, 5359},{ 1249, 5522},{ 1304, 5672},{ 1397, 5806} + }, + /*Cr qi=24 INTER*/ + { + { 91, 56},{ 73, 353},{ 61, 664},{ 66, 1045}, + { 80, 1449},{ 95, 1851},{ 103, 2250},{ 107, 2648}, + { 111, 3038},{ 116, 3413},{ 120, 3774},{ 124, 4128}, + { 130, 4471},{ 138, 4802},{ 145, 5130},{ 156, 5453}, + { 171, 5764},{ 187, 6061},{ 204, 6355},{ 220, 6643}, + { 238, 6923},{ 254, 7204},{ 275, 7475},{ 289, 7752} + } + } + }, + { + { + /*Y' qi=25 INTRA*/ + { + { 125, -28},{ 285, 1426},{ 582, 2540},{ 917, 3351}, + { 1244, 3997},{ 1569, 4570},{ 1903, 5071},{ 2258, 5498}, + { 2626, 5866},{ 3002, 6182},{ 3382, 6448},{ 3770, 6623}, + { 4162, 6760},{ 4528, 6934},{ 4882, 7144},{ 5249, 7328}, + { 5610, 7453},{ 5958, 7578},{ 6291, 7672},{ 6597, 7708}, + { 6928, 7715},{ 7258, 7737},{ 7575, 7781},{ 7950, 7829} + }, + /*Y' qi=25 INTER*/ + { + { 64, -16},{ 72, 1348},{ 139, 2832},{ 206, 4218}, + { 268, 5465},{ 322, 6659},{ 403, 7803},{ 540, 8838}, + { 747, 9734},{ 1044,10465},{ 1473,10981},{ 2048,11249}, + { 2717,11311},{ 3397,11257},{ 4025,11161},{ 4589,11052}, + { 5099,10947},{ 5560,10859},{ 5989,10786},{ 6389,10717}, + { 6753,10652},{ 7078,10592},{ 7389,10535},{ 7697,10460} + } + }, + { + /*Cb qi=25 INTRA*/ + { + { 3, 3},{ 78, 368},{ 133, 745},{ 159, 1180}, + { 193, 1627},{ 242, 2046},{ 304, 2411},{ 381, 2714}, + { 456, 2983},{ 527, 3224},{ 598, 3437},{ 667, 3655}, + { 726, 3888},{ 776, 4117},{ 826, 4333},{ 883, 4543}, + { 954, 4727},{ 1019, 4878},{ 1095, 5014},{ 1171, 5187}, + { 1255, 5342},{ 1319, 5458},{ 1396, 5546},{ 1536, 5678} + }, + /*Cb qi=25 INTER*/ + { + { 117, 32},{ 89, 342},{ 67, 660},{ 64, 1037}, + { 77, 1441},{ 93, 1845},{ 105, 2243},{ 113, 2633}, + { 120, 3016},{ 125, 3387},{ 131, 3739},{ 137, 4080}, + { 144, 4416},{ 152, 4741},{ 160, 5057},{ 169, 5369}, + { 180, 5680},{ 193, 5990},{ 209, 6294},{ 227, 6594}, + { 249, 6888},{ 269, 7180},{ 294, 7467},{ 317, 7768} + } + }, + { + /*Cr qi=25 INTRA*/ + { + { 6, 6},{ 74, 380},{ 129, 770},{ 165, 1220}, + { 201, 1658},{ 253, 2061},{ 315, 2410},{ 388, 2676}, + { 462, 2920},{ 523, 3166},{ 584, 3404},{ 647, 3637}, + { 701, 3870},{ 769, 4086},{ 838, 4296},{ 898, 4491}, + { 980, 4627},{ 1065, 4759},{ 1126, 4920},{ 1187, 5058}, + { 1283, 5180},{ 1347, 5332},{ 1404, 5475},{ 1527, 5534} + }, + /*Cr qi=25 INTER*/ + { + { 92, 41},{ 75, 347},{ 64, 664},{ 70, 1045}, + { 85, 1448},{ 98, 1849},{ 105, 2245},{ 110, 2637}, + { 115, 3023},{ 120, 3395},{ 126, 3753},{ 131, 4102}, + { 136, 4439},{ 145, 4768},{ 156, 5094},{ 168, 5410}, + { 184, 5717},{ 203, 6010},{ 221, 6300},{ 239, 6577}, + { 262, 6847},{ 282, 7123},{ 303, 7390},{ 322, 7665} + } + } + }, + { + { + /*Y' qi=26 INTRA*/ + { + { 130, -24},{ 292, 1423},{ 594, 2525},{ 943, 3307}, + { 1289, 3921},{ 1633, 4467},{ 1991, 4943},{ 2368, 5348}, + { 2753, 5696},{ 3148, 5991},{ 3545, 6247},{ 3942, 6415}, + { 4342, 6535},{ 4726, 6690},{ 5093, 6883},{ 5466, 7047}, + { 5840, 7159},{ 6202, 7274},{ 6545, 7351},{ 6855, 7375}, + { 7186, 7384},{ 7517, 7416},{ 7840, 7447},{ 8238, 7450} + }, + /*Y' qi=26 INTER*/ + { + { 52, 16},{ 75, 1336},{ 143, 2815},{ 213, 4191}, + { 278, 5427},{ 339, 6611},{ 436, 7734},{ 600, 8732}, + { 843, 9579},{ 1195,10243},{ 1702,10660},{ 2355,10825}, + { 3070,10820},{ 3755,10743},{ 4372,10643},{ 4925,10538}, + { 5426,10440},{ 5882,10354},{ 6296,10290},{ 6686,10224}, + { 7049,10163},{ 7380,10113},{ 7672,10062},{ 7937,10021} + } + }, + { + /*Cb qi=26 INTRA*/ + { + { 4, 3},{ 79, 368},{ 138, 745},{ 167, 1180}, + { 200, 1623},{ 252, 2034},{ 322, 2389},{ 403, 2682}, + { 480, 2941},{ 558, 3176},{ 631, 3393},{ 700, 3608}, + { 766, 3825},{ 819, 4046},{ 868, 4265},{ 926, 4472}, + { 1002, 4645},{ 1070, 4800},{ 1151, 4924},{ 1242, 5063}, + { 1325, 5221},{ 1393, 5338},{ 1464, 5431},{ 1595, 5559} + }, + /*Cb qi=26 INTER*/ + { + { 98, 33},{ 83, 343},{ 65, 662},{ 65, 1037}, + { 80, 1437},{ 96, 1839},{ 107, 2238},{ 115, 2628}, + { 122, 3007},{ 128, 3373},{ 134, 3722},{ 142, 4060}, + { 149, 4390},{ 158, 4713},{ 167, 5029},{ 178, 5341}, + { 191, 5647},{ 208, 5948},{ 227, 6244},{ 247, 6539}, + { 269, 6833},{ 295, 7114},{ 328, 7388},{ 369, 7658} + } + }, + { + /*Cr qi=26 INTRA*/ + { + { 5, 6},{ 75, 380},{ 133, 769},{ 172, 1217}, + { 212, 1652},{ 266, 2048},{ 333, 2384},{ 412, 2643}, + { 490, 2880},{ 552, 3124},{ 616, 3365},{ 681, 3594}, + { 739, 3816},{ 810, 4024},{ 880, 4224},{ 945, 4405}, + { 1029, 4538},{ 1114, 4674},{ 1183, 4822},{ 1254, 4946}, + { 1346, 5063},{ 1417, 5201},{ 1478, 5345},{ 1597, 5411} + }, + /*Cr qi=26 INTER*/ + { + { 97, 29},{ 75, 342},{ 62, 667},{ 70, 1047}, + { 87, 1447},{ 100, 1846},{ 107, 2242},{ 113, 2633}, + { 118, 3016},{ 123, 3382},{ 128, 3737},{ 135, 4082}, + { 142, 4417},{ 151, 4746},{ 162, 5066},{ 176, 5377}, + { 194, 5679},{ 217, 5963},{ 239, 6244},{ 260, 6522}, + { 284, 6789},{ 309, 7052},{ 335, 7313},{ 355, 7582} + } + } + }, + { + { + /*Y' qi=27 INTRA*/ + { + { 118, -10},{ 308, 1404},{ 630, 2473},{ 997, 3227}, + { 1360, 3819},{ 1719, 4354},{ 2086, 4829},{ 2470, 5233}, + { 2863, 5576},{ 3267, 5870},{ 3677, 6117},{ 4085, 6268}, + { 4499, 6376},{ 4888, 6521},{ 5257, 6705},{ 5638, 6865}, + { 6020, 6962},{ 6394, 7056},{ 6744, 7130},{ 7051, 7158}, + { 7386, 7164},{ 7717, 7185},{ 8042, 7209},{ 8444, 7206} + }, + /*Y' qi=27 INTER*/ + { + { 54, 19},{ 77, 1333},{ 147, 2806},{ 221, 4166}, + { 290, 5390},{ 360, 6564},{ 474, 7665},{ 664, 8630}, + { 949, 9423},{ 1370,10002},{ 1958,10323},{ 2670,10414}, + { 3406,10375},{ 4086,10285},{ 4691,10182},{ 5233,10085}, + { 5724, 9994},{ 6169, 9918},{ 6582, 9863},{ 6962, 9813}, + { 7316, 9759},{ 7645, 9707},{ 7948, 9660},{ 8262, 9623} + } + }, + { + /*Cb qi=27 INTRA*/ + { + { 4, 3},{ 79, 368},{ 137, 745},{ 166, 1180}, + { 200, 1622},{ 253, 2030},{ 324, 2381},{ 407, 2671}, + { 487, 2925},{ 567, 3156},{ 640, 3372},{ 712, 3580}, + { 782, 3792},{ 833, 4015},{ 887, 4227},{ 954, 4422}, + { 1031, 4592},{ 1103, 4738},{ 1187, 4856},{ 1280, 4990}, + { 1371, 5135},{ 1442, 5244},{ 1520, 5321},{ 1684, 5398} + }, + /*Cb qi=27 INTER*/ + { + { 113, 20},{ 90, 338},{ 66, 661},{ 67, 1034}, + { 82, 1438},{ 97, 1842},{ 108, 2238},{ 115, 2624}, + { 123, 3000},{ 130, 3361},{ 138, 3708},{ 146, 4040}, + { 155, 4367},{ 164, 4688},{ 174, 4999},{ 186, 5306}, + { 203, 5609},{ 222, 5908},{ 243, 6202},{ 268, 6494}, + { 295, 6781},{ 326, 7058},{ 367, 7319},{ 420, 7551} + } + }, + { + /*Cr qi=27 INTRA*/ + { + { 5, 6},{ 75, 380},{ 133, 770},{ 173, 1217}, + { 214, 1650},{ 268, 2040},{ 337, 2375},{ 418, 2631}, + { 496, 2862},{ 558, 3104},{ 625, 3346},{ 692, 3571}, + { 753, 3786},{ 825, 3989},{ 896, 4182},{ 969, 4352}, + { 1059, 4479},{ 1144, 4614},{ 1212, 4757},{ 1284, 4871}, + { 1380, 4982},{ 1457, 5125},{ 1528, 5267},{ 1651, 5346} + }, + /*Cr qi=27 INTER*/ + { + { 92, 24},{ 74, 341},{ 61, 669},{ 71, 1049}, + { 88, 1448},{ 100, 1849},{ 107, 2243},{ 113, 2631}, + { 119, 3010},{ 125, 3373},{ 131, 3723},{ 137, 4064}, + { 146, 4396},{ 159, 4720},{ 172, 5033},{ 189, 5340}, + { 210, 5636},{ 233, 5920},{ 256, 6197},{ 282, 6465}, + { 310, 6730},{ 332, 7000},{ 359, 7259},{ 385, 7515} + } + } + }, + { + { + /*Y' qi=28 INTRA*/ + { + { 116, -8},{ 314, 1400},{ 640, 2458},{ 1013, 3197}, + { 1386, 3768},{ 1762, 4279},{ 2151, 4733},{ 2558, 5117}, + { 2970, 5442},{ 3393, 5714},{ 3820, 5935},{ 4243, 6069}, + { 4671, 6161},{ 5074, 6289},{ 5456, 6457},{ 5849, 6598}, + { 6244, 6689},{ 6632, 6777},{ 6984, 6833},{ 7294, 6855}, + { 7625, 6862},{ 7961, 6875},{ 8302, 6890},{ 8720, 6883} + }, + /*Y' qi=28 INTER*/ + { + { 54, 8},{ 81, 1333},{ 154, 2793},{ 231, 4138}, + { 304, 5352},{ 384, 6512},{ 519, 7585},{ 743, 8508}, + { 1082, 9236},{ 1587, 9717},{ 2267, 9928},{ 3034, 9944}, + { 3775, 9878},{ 4438, 9786},{ 5031, 9686},{ 5563, 9601}, + { 6042, 9523},{ 6481, 9456},{ 6890, 9405},{ 7266, 9356}, + { 7614, 9313},{ 7933, 9265},{ 8238, 9220},{ 8545, 9193} + } + }, + { + /*Cb qi=28 INTRA*/ + { + { 3, 3},{ 80, 368},{ 138, 746},{ 168, 1179}, + { 208, 1615},{ 268, 2014},{ 345, 2354},{ 432, 2637}, + { 515, 2884},{ 595, 3108},{ 669, 3323},{ 745, 3533}, + { 818, 3740},{ 876, 3953},{ 932, 4160},{ 1003, 4349}, + { 1088, 4501},{ 1154, 4648},{ 1241, 4768},{ 1349, 4889}, + { 1441, 5023},{ 1524, 5113},{ 1611, 5187},{ 1783, 5283} + }, + /*Cb qi=28 INTER*/ + { + { 117, 29},{ 91, 341},{ 65, 663},{ 68, 1038}, + { 85, 1440},{ 100, 1841},{ 110, 2234},{ 119, 2616}, + { 127, 2985},{ 135, 3342},{ 142, 3685},{ 151, 4015}, + { 162, 4337},{ 174, 4652},{ 186, 4960},{ 201, 5264}, + { 218, 5567},{ 239, 5863},{ 266, 6149},{ 295, 6434}, + { 328, 6715},{ 371, 6976},{ 409, 7239},{ 460, 7477} + } + }, + { + /*Cr qi=28 INTRA*/ + { + { 6, 7},{ 79, 381},{ 138, 771},{ 178, 1215}, + { 222, 1644},{ 285, 2026},{ 359, 2347},{ 441, 2597}, + { 521, 2827},{ 588, 3066},{ 655, 3303},{ 725, 3523}, + { 791, 3728},{ 870, 3920},{ 950, 4103},{ 1030, 4265}, + { 1121, 4388},{ 1198, 4520},{ 1266, 4659},{ 1356, 4759}, + { 1461, 4865},{ 1540, 4993},{ 1619, 5115},{ 1786, 5160} + }, + /*Cr qi=28 INTER*/ + { + { 96, 18},{ 78, 340},{ 66, 672},{ 74, 1051}, + { 90, 1450},{ 103, 1845},{ 110, 2235},{ 116, 2619}, + { 122, 2995},{ 129, 3356},{ 137, 3702},{ 146, 4038}, + { 156, 4365},{ 168, 4684},{ 182, 4995},{ 203, 5297}, + { 227, 5588},{ 253, 5866},{ 282, 6131},{ 311, 6394}, + { 339, 6664},{ 366, 6918},{ 400, 7171},{ 424, 7450} + } + } + }, + { + { + /*Y' qi=29 INTRA*/ + { + { 112, 7},{ 334, 1382},{ 681, 2410},{ 1081, 3112}, + { 1484, 3650},{ 1894, 4128},{ 2316, 4547},{ 2749, 4905}, + { 3188, 5208},{ 3634, 5458},{ 4079, 5666},{ 4517, 5791}, + { 4952, 5870},{ 5359, 5983},{ 5754, 6137},{ 6165, 6268}, + { 6568, 6351},{ 6958, 6423},{ 7320, 6471},{ 7638, 6490}, + { 7979, 6490},{ 8313, 6499},{ 8651, 6517},{ 9085, 6499} + }, + /*Y' qi=29 INTER*/ + { + { 55, 15},{ 85, 1336},{ 160, 2780},{ 242, 4104}, + { 323, 5302},{ 418, 6443},{ 586, 7480},{ 859, 8342}, + { 1278, 8982},{ 1888, 9347},{ 2658, 9457},{ 3457, 9425}, + { 4192, 9343},{ 4842, 9247},{ 5417, 9162},{ 5935, 9086}, + { 6404, 9011},{ 6841, 8952},{ 7241, 8907},{ 7609, 8867}, + { 7953, 8832},{ 8267, 8792},{ 8562, 8740},{ 8836, 8701} + } + }, + { + /*Cb qi=29 INTRA*/ + { + { 5, 3},{ 84, 368},{ 144, 746},{ 176, 1175}, + { 219, 1604},{ 285, 1991},{ 372, 2318},{ 462, 2591}, + { 546, 2833},{ 628, 3058},{ 704, 3274},{ 788, 3473}, + { 870, 3664},{ 935, 3865},{ 995, 4059},{ 1072, 4239}, + { 1167, 4388},{ 1248, 4518},{ 1334, 4634},{ 1429, 4765}, + { 1536, 4884},{ 1628, 4964},{ 1716, 5038},{ 1885, 5128} + }, + /*Cb qi=29 INTER*/ + { + { 126, 25},{ 95, 340},{ 69, 662},{ 71, 1039}, + { 88, 1440},{ 102, 1839},{ 113, 2227},{ 122, 2604}, + { 132, 2969},{ 141, 3320},{ 151, 3659},{ 161, 3985}, + { 172, 4301},{ 186, 4612},{ 200, 4917},{ 219, 5213}, + { 241, 5509},{ 265, 5800},{ 296, 6081},{ 329, 6360}, + { 369, 6633},{ 414, 6899},{ 465, 7148},{ 520, 7387} + } + }, + { + /*Cr qi=29 INTRA*/ + { + { 6, 7},{ 82, 382},{ 142, 772},{ 185, 1211}, + { 233, 1632},{ 303, 2000},{ 388, 2306},{ 475, 2550}, + { 556, 2779},{ 627, 3007},{ 707, 3237},{ 778, 3459}, + { 843, 3654},{ 927, 3834},{ 1012, 4012},{ 1101, 4152}, + { 1197, 4262},{ 1275, 4399},{ 1359, 4511},{ 1455, 4596}, + { 1562, 4708},{ 1644, 4833},{ 1719, 4954},{ 1888, 4988} + }, + /*Cr qi=29 INTER*/ + { + { 101, 28},{ 81, 343},{ 67, 673},{ 75, 1053}, + { 93, 1450},{ 106, 1844},{ 113, 2230},{ 119, 2610}, + { 127, 2980},{ 135, 3334},{ 143, 3676},{ 153, 4007}, + { 165, 4330},{ 180, 4645},{ 201, 4951},{ 224, 5243}, + { 253, 5522},{ 284, 5794},{ 314, 6060},{ 345, 6322}, + { 381, 6578},{ 419, 6828},{ 455, 7073},{ 495, 7316} + } + } + }, + { + { + /*Y' qi=30 INTRA*/ + { + { 112, 8},{ 335, 1380},{ 682, 2401},{ 1083, 3093}, + { 1489, 3619},{ 1902, 4092},{ 2332, 4511},{ 2777, 4865}, + { 3231, 5156},{ 3693, 5394},{ 4153, 5585},{ 4605, 5689}, + { 5049, 5764},{ 5468, 5871},{ 5875, 6004},{ 6295, 6120}, + { 6706, 6201},{ 7099, 6273},{ 7461, 6311},{ 7785, 6320}, + { 8128, 6322},{ 8469, 6331},{ 8806, 6342},{ 9220, 6338} + }, + /*Y' qi=30 INTER*/ + { + { 58, 8},{ 90, 1340},{ 169, 2771},{ 257, 4079}, + { 345, 5266},{ 459, 6387},{ 660, 7383},{ 990, 8180}, + { 1496, 8726},{ 2203, 8992},{ 3029, 9038},{ 3833, 8984}, + { 4549, 8900},{ 5183, 8813},{ 5745, 8735},{ 6250, 8674}, + { 6715, 8619},{ 7138, 8565},{ 7529, 8528},{ 7899, 8495}, + { 8234, 8465},{ 8550, 8429},{ 8856, 8395},{ 9160, 8374} + } + }, + { + /*Cb qi=30 INTRA*/ + { + { 7, 3},{ 88, 369},{ 149, 747},{ 185, 1175}, + { 232, 1599},{ 304, 1976},{ 392, 2293},{ 486, 2557}, + { 573, 2797},{ 656, 3027},{ 735, 3243},{ 819, 3442}, + { 903, 3629},{ 966, 3828},{ 1025, 4027},{ 1105, 4204}, + { 1201, 4343},{ 1282, 4469},{ 1379, 4575},{ 1486, 4689}, + { 1588, 4813},{ 1678, 4900},{ 1767, 4969},{ 1911, 5080} + }, + /*Cb qi=30 INTER*/ + { + { 120, 23},{ 96, 336},{ 72, 661},{ 75, 1043}, + { 91, 1441},{ 105, 1837},{ 117, 2221},{ 127, 2592}, + { 137, 2953},{ 148, 3301},{ 159, 3635},{ 170, 3959}, + { 184, 4271},{ 199, 4578},{ 216, 4879},{ 238, 5175}, + { 262, 5466},{ 294, 5750},{ 332, 6027},{ 373, 6298}, + { 421, 6559},{ 473, 6805},{ 526, 7053},{ 587, 7298} + } + }, + { + /*Cr qi=30 INTRA*/ + { + { 10, 7},{ 89, 384},{ 147, 773},{ 192, 1211}, + { 245, 1627},{ 322, 1984},{ 412, 2280},{ 501, 2520}, + { 583, 2750},{ 654, 2982},{ 736, 3207},{ 810, 3419}, + { 873, 3614},{ 957, 3794},{ 1048, 3965},{ 1139, 4102}, + { 1237, 4208},{ 1327, 4328},{ 1408, 4448},{ 1496, 4545}, + { 1604, 4652},{ 1699, 4760},{ 1780, 4877},{ 1937, 4942} + }, + /*Cr qi=30 INTER*/ + { + { 115, 26},{ 89, 342},{ 70, 672},{ 79, 1055}, + { 96, 1451},{ 108, 1841},{ 116, 2222},{ 124, 2599}, + { 132, 2965},{ 141, 3316},{ 151, 3655},{ 163, 3984}, + { 178, 4301},{ 197, 4609},{ 219, 4909},{ 247, 5195}, + { 280, 5469},{ 317, 5734},{ 351, 5991},{ 383, 6248}, + { 423, 6500},{ 467, 6744},{ 502, 6995},{ 558, 7226} + } + } + }, + { + { + /*Y' qi=31 INTRA*/ + { + { 116, 20},{ 359, 1361},{ 732, 2350},{ 1162, 3010}, + { 1597, 3507},{ 2042, 3950},{ 2503, 4339},{ 2974, 4670}, + { 3446, 4951},{ 3922, 5179},{ 4394, 5357},{ 4858, 5454}, + { 5313, 5519},{ 5734, 5626},{ 6154, 5755},{ 6585, 5859}, + { 7004, 5928},{ 7408, 5998},{ 7775, 6039},{ 8102, 6048}, + { 8442, 6051},{ 8790, 6054},{ 9136, 6057},{ 9554, 6041} + }, + /*Y' qi=31 INTER*/ + { + { 53, 12},{ 90, 1340},{ 169, 2765},{ 259, 4062}, + { 353, 5236},{ 483, 6340},{ 713, 7305},{ 1086, 8059}, + { 1651, 8548},{ 2423, 8751},{ 3288, 8754},{ 4106, 8674}, + { 4827, 8572},{ 5451, 8482},{ 6007, 8407},{ 6514, 8344}, + { 6970, 8282},{ 7397, 8225},{ 7795, 8193},{ 8159, 8161}, + { 8498, 8120},{ 8814, 8093},{ 9127, 8066},{ 9432, 8040} + } + }, + { + /*Cb qi=31 INTRA*/ + { + { 7, 3},{ 88, 369},{ 149, 746},{ 185, 1173}, + { 234, 1595},{ 308, 1967},{ 399, 2278},{ 494, 2537}, + { 583, 2774},{ 669, 2997},{ 755, 3204},{ 847, 3390}, + { 936, 3569},{ 1008, 3759},{ 1078, 3942},{ 1162, 4104}, + { 1262, 4238},{ 1352, 4364},{ 1442, 4470},{ 1557, 4567}, + { 1676, 4674},{ 1759, 4781},{ 1850, 4853},{ 2043, 4897} + }, + /*Cb qi=31 INTER*/ + { + { 121, 23},{ 96, 335},{ 72, 660},{ 74, 1043}, + { 90, 1440},{ 105, 1834},{ 116, 2217},{ 127, 2586}, + { 138, 2945},{ 148, 3293},{ 159, 3626},{ 172, 3945}, + { 185, 4256},{ 202, 4559},{ 223, 4856},{ 245, 5150}, + { 272, 5440},{ 306, 5719},{ 346, 5989},{ 391, 6253}, + { 443, 6511},{ 510, 6743},{ 583, 6965},{ 651, 7182} + } + }, + { + /*Cr qi=31 INTRA*/ + { + { 10, 7},{ 88, 384},{ 147, 773},{ 192, 1209}, + { 247, 1622},{ 326, 1974},{ 417, 2262},{ 509, 2500}, + { 596, 2726},{ 670, 2949},{ 754, 3170},{ 836, 3370}, + { 912, 3548},{ 999, 3724},{ 1093, 3888},{ 1198, 4000}, + { 1304, 4095},{ 1384, 4230},{ 1470, 4347},{ 1577, 4422}, + { 1696, 4513},{ 1798, 4620},{ 1869, 4746},{ 1991, 4798} + }, + /*Cr qi=31 INTER*/ + { + { 113, 32},{ 88, 345},{ 69, 674},{ 79, 1055}, + { 96, 1451},{ 108, 1839},{ 115, 2218},{ 123, 2592}, + { 132, 2957},{ 141, 3308},{ 151, 3643},{ 163, 3968}, + { 179, 4285},{ 200, 4590},{ 225, 4886},{ 254, 5169}, + { 291, 5436},{ 330, 5696},{ 368, 5951},{ 409, 6200}, + { 452, 6448},{ 493, 6695},{ 536, 6940},{ 571, 7204} + } + } + }, + { + { + /*Y' qi=32 INTRA*/ + { + { 123, 26},{ 370, 1356},{ 756, 2321},{ 1211, 2944}, + { 1674, 3408},{ 2148, 3826},{ 2639, 4193},{ 3138, 4504}, + { 3634, 4765},{ 4133, 4973},{ 4625, 5137},{ 5101, 5225}, + { 5567, 5274},{ 6002, 5363},{ 6437, 5482},{ 6885, 5566}, + { 7312, 5625},{ 7723, 5686},{ 8101, 5721},{ 8429, 5732}, + { 8769, 5728},{ 9120, 5726},{ 9472, 5723},{ 9918, 5700} + }, + /*Y' qi=32 INTER*/ + { + { 54, -3},{ 95, 1343},{ 179, 2750},{ 276, 4027}, + { 382, 5185},{ 543, 6256},{ 830, 7161},{ 1301, 7815}, + { 2003, 8172},{ 2883, 8266},{ 3779, 8217},{ 4578, 8127}, + { 5274, 8035},{ 5886, 7952},{ 6430, 7887},{ 6929, 7835}, + { 7380, 7779},{ 7796, 7737},{ 8190, 7705},{ 8552, 7672}, + { 8896, 7640},{ 9210, 7612},{ 9510, 7589},{ 9746, 7552} + } + }, + { + /*Cb qi=32 INTRA*/ + { + { 6, 3},{ 89, 369},{ 153, 746},{ 193, 1167}, + { 247, 1577},{ 330, 1935},{ 429, 2236},{ 528, 2494}, + { 620, 2732},{ 712, 2948},{ 801, 3146},{ 898, 3325}, + { 999, 3489},{ 1078, 3664},{ 1155, 3832},{ 1251, 3985}, + { 1360, 4115},{ 1451, 4236},{ 1549, 4338},{ 1667, 4433}, + { 1797, 4522},{ 1891, 4613},{ 1989, 4687},{ 2162, 4776} + }, + /*Cb qi=32 INTER*/ + { + { 116, -1},{ 98, 321},{ 80, 656},{ 80, 1042}, + { 96, 1438},{ 110, 1827},{ 122, 2205},{ 133, 2570}, + { 144, 2925},{ 157, 3268},{ 170, 3597},{ 185, 3911}, + { 202, 4216},{ 221, 4516},{ 244, 4809},{ 273, 5096}, + { 308, 5376},{ 350, 5644},{ 401, 5907},{ 459, 6160}, + { 520, 6401},{ 592, 6630},{ 676, 6837},{ 758, 7050} + } + }, + { + /*Cr qi=32 INTRA*/ + { + { 12, 7},{ 91, 386},{ 152, 773},{ 201, 1202}, + { 261, 1603},{ 347, 1942},{ 447, 2223},{ 540, 2460}, + { 626, 2684},{ 711, 2901},{ 801, 3115},{ 887, 3312}, + { 969, 3480},{ 1068, 3633},{ 1176, 3779},{ 1283, 3885}, + { 1392, 3969},{ 1485, 4090},{ 1573, 4206},{ 1686, 4274}, + { 1813, 4354},{ 1911, 4459},{ 2004, 4563},{ 2162, 4590} + }, + /*Cr qi=32 INTER*/ + { + { 129, 5},{ 98, 334},{ 75, 673},{ 84, 1055}, + { 101, 1448},{ 113, 1832},{ 121, 2206},{ 129, 2577}, + { 140, 2937},{ 151, 3282},{ 163, 3614},{ 179, 3932}, + { 198, 4240},{ 221, 4542},{ 252, 4830},{ 290, 5102}, + { 329, 5364},{ 373, 5618},{ 420, 5864},{ 468, 6105}, + { 513, 6351},{ 564, 6587},{ 624, 6810},{ 697, 7017} + } + } + }, + { + { + /*Y' qi=33 INTRA*/ + { + { 115, 36},{ 388, 1338},{ 791, 2289},{ 1258, 2899}, + { 1732, 3352},{ 2220, 3760},{ 2730, 4117},{ 3244, 4415}, + { 3751, 4662},{ 4261, 4858},{ 4766, 5012},{ 5249, 5094}, + { 5719, 5141},{ 6159, 5225},{ 6597, 5333},{ 7044, 5416}, + { 7474, 5472},{ 7893, 5531},{ 8268, 5570},{ 8591, 5580}, + { 8931, 5578},{ 9283, 5579},{ 9634, 5582},{10067, 5560} + }, + /*Y' qi=33 INTER*/ + { + { 65, -14},{ 102, 1345},{ 190, 2736},{ 294, 3999}, + { 411, 5146},{ 597, 6192},{ 934, 7045},{ 1488, 7622}, + { 2281, 7895},{ 3213, 7937},{ 4108, 7871},{ 4883, 7784}, + { 5556, 7709},{ 6150, 7643},{ 6685, 7585},{ 7176, 7539}, + { 7620, 7502},{ 8034, 7466},{ 8427, 7435},{ 8793, 7409}, + { 9136, 7386},{ 9446, 7364},{ 9743, 7339},{10025, 7303} + } + }, + { + /*Cb qi=33 INTRA*/ + { + { 5, 3},{ 92, 369},{ 159, 746},{ 203, 1163}, + { 263, 1564},{ 353, 1911},{ 458, 2204},{ 557, 2460}, + { 650, 2697},{ 744, 2913},{ 836, 3110},{ 934, 3292}, + { 1036, 3454},{ 1125, 3616},{ 1204, 3781},{ 1298, 3932}, + { 1410, 4058},{ 1507, 4170},{ 1606, 4265},{ 1725, 4358}, + { 1853, 4445},{ 1955, 4535},{ 2067, 4597},{ 2258, 4663} + }, + /*Cb qi=33 INTER*/ + { + { 109, 37},{ 94, 343},{ 81, 662},{ 85, 1042}, + { 102, 1436},{ 116, 1823},{ 128, 2195},{ 141, 2554}, + { 154, 2906},{ 167, 3246},{ 183, 3570},{ 202, 3881}, + { 220, 4185},{ 241, 4482},{ 268, 4772},{ 302, 5053}, + { 341, 5328},{ 388, 5592},{ 446, 5846},{ 507, 6096}, + { 581, 6328},{ 670, 6534},{ 762, 6731},{ 842, 6922} + } + }, + { + /*Cr qi=33 INTRA*/ + { + { 11, 7},{ 93, 387},{ 158, 774},{ 211, 1197}, + { 278, 1589},{ 372, 1917},{ 475, 2191},{ 569, 2429}, + { 658, 2655},{ 744, 2868},{ 835, 3083},{ 926, 3271}, + { 1010, 3430},{ 1110, 3586},{ 1224, 3724},{ 1336, 3826}, + { 1449, 3908},{ 1547, 4021},{ 1636, 4136},{ 1751, 4200}, + { 1886, 4277},{ 1977, 4384},{ 2070, 4474},{ 2232, 4510} + }, + /*Cr qi=33 INTER*/ + { + { 77, 9},{ 90, 347},{ 80, 674},{ 91, 1053}, + { 107, 1444},{ 119, 1825},{ 127, 2196},{ 137, 2563}, + { 149, 2919},{ 161, 3259},{ 176, 3588},{ 194, 3905}, + { 217, 4209},{ 246, 4504},{ 280, 4786},{ 320, 5055}, + { 364, 5316},{ 409, 5565},{ 460, 5804},{ 517, 6039}, + { 578, 6264},{ 640, 6489},{ 701, 6721},{ 772, 6948} + } + } + }, + { + { + /*Y' qi=34 INTRA*/ + { + { 124, 40},{ 401, 1333},{ 823, 2262},{ 1318, 2842}, + { 1823, 3265},{ 2339, 3650},{ 2872, 3991},{ 3405, 4274}, + { 3926, 4513},{ 4448, 4704},{ 4961, 4845},{ 5450, 4921}, + { 5925, 4971},{ 6372, 5053},{ 6813, 5160},{ 7264, 5242}, + { 7704, 5291},{ 8124, 5346},{ 8500, 5382},{ 8831, 5384}, + { 9178, 5380},{ 9525, 5387},{ 9869, 5389},{10310, 5356} + }, + /*Y' qi=34 INTER*/ + { + { 64, -17},{ 101, 1344},{ 190, 2730},{ 299, 3981}, + { 430, 5110},{ 648, 6127},{ 1036, 6933},{ 1664, 7445}, + { 2535, 7652},{ 3504, 7653},{ 4402, 7572},{ 5173, 7479}, + { 5843, 7400},{ 6441, 7334},{ 6976, 7280},{ 7464, 7231}, + { 7910, 7189},{ 8332, 7157},{ 8730, 7125},{ 9091, 7103}, + { 9422, 7086},{ 9753, 7061},{10067, 7036},{10316, 7029} + } + }, + { + /*Cb qi=34 INTRA*/ + { + { 5, 3},{ 91, 369},{ 158, 746},{ 204, 1162}, + { 266, 1561},{ 358, 1903},{ 466, 2189},{ 570, 2439}, + { 665, 2671},{ 765, 2880},{ 864, 3069},{ 970, 3238}, + { 1079, 3392},{ 1174, 3545},{ 1265, 3693},{ 1360, 3841}, + { 1471, 3968},{ 1572, 4083},{ 1675, 4181},{ 1804, 4255}, + { 1939, 4332},{ 2048, 4411},{ 2155, 4484},{ 2339, 4584} + }, + /*Cb qi=34 INTER*/ + { + { 99, 44},{ 92, 345},{ 82, 661},{ 86, 1043}, + { 101, 1436},{ 116, 1821},{ 128, 2191},{ 140, 2549}, + { 154, 2898},{ 168, 3235},{ 185, 3556},{ 203, 3865}, + { 224, 4166},{ 248, 4457},{ 278, 4741},{ 315, 5021}, + { 361, 5289},{ 416, 5546},{ 483, 5792},{ 559, 6025}, + { 651, 6237},{ 752, 6432},{ 849, 6626},{ 967, 6790} + } + }, + { + /*Cr qi=34 INTRA*/ + { + { 11, 7},{ 93, 387},{ 158, 773},{ 212, 1195}, + { 282, 1584},{ 378, 1909},{ 483, 2179},{ 578, 2414}, + { 671, 2633},{ 766, 2837},{ 866, 3038},{ 960, 3223}, + { 1049, 3376},{ 1158, 3520},{ 1285, 3644},{ 1400, 3740}, + { 1505, 3828},{ 1616, 3928},{ 1713, 4030},{ 1820, 4104}, + { 1957, 4185},{ 2063, 4280},{ 2160, 4355},{ 2320, 4341} + }, + /*Cr qi=34 INTER*/ + { + { 78, 11},{ 89, 347},{ 79, 674},{ 90, 1053}, + { 106, 1444},{ 117, 1823},{ 127, 2192},{ 137, 2558}, + { 149, 2912},{ 163, 3249},{ 178, 3574},{ 197, 3888}, + { 222, 4189},{ 252, 4481},{ 293, 4755},{ 341, 5013}, + { 386, 5268},{ 436, 5512},{ 498, 5743},{ 563, 5970}, + { 622, 6200},{ 694, 6415},{ 776, 6622},{ 871, 6818} + } + } + }, + { + { + /*Y' qi=35 INTRA*/ + { + { 116, 51},{ 433, 1312},{ 881, 2221},{ 1406, 2771}, + { 1948, 3156},{ 2511, 3501},{ 3085, 3811},{ 3654, 4066}, + { 4212, 4273},{ 4763, 4444},{ 5298, 4572},{ 5799, 4638}, + { 6285, 4678},{ 6747, 4746},{ 7203, 4838},{ 7673, 4905}, + { 8124, 4950},{ 8552, 5003},{ 8938, 5027},{ 9275, 5026}, + { 9628, 5019},{ 9981, 5024},{10331, 5030},{10795, 5000} + }, + /*Y' qi=35 INTER*/ + { + { 71, -10},{ 108, 1348},{ 203, 2710},{ 325, 3938}, + { 485, 5040},{ 766, 6000},{ 1267, 6706},{ 2048, 7089}, + { 3037, 7191},{ 4032, 7146},{ 4903, 7061},{ 5648, 6977}, + { 6301, 6912},{ 6884, 6857},{ 7413, 6812},{ 7898, 6775}, + { 8342, 6739},{ 8764, 6710},{ 9160, 6688},{ 9519, 6668}, + { 9859, 6646},{10190, 6625},{10492, 6612},{10755, 6595} + } + }, + { + /*Cb qi=35 INTRA*/ + { + { 6, 3},{ 95, 369},{ 164, 746},{ 214, 1156}, + { 287, 1542},{ 390, 1869},{ 504, 2143},{ 611, 2388}, + { 712, 2613},{ 822, 2811},{ 937, 2987},{ 1055, 3147}, + { 1174, 3285},{ 1286, 3420},{ 1386, 3560},{ 1488, 3698}, + { 1604, 3814},{ 1714, 3916},{ 1825, 4008},{ 1958, 4088}, + { 2101, 4159},{ 2224, 4226},{ 2339, 4292},{ 2538, 4383} + }, + /*Cb qi=35 INTER*/ + { + { 98, 41},{ 90, 348},{ 86, 665},{ 92, 1042}, + { 108, 1432},{ 122, 1812},{ 136, 2175},{ 151, 2528}, + { 165, 2872},{ 182, 3202},{ 202, 3516},{ 225, 3819}, + { 251, 4112},{ 281, 4398},{ 320, 4675},{ 367, 4944}, + { 421, 5204},{ 493, 5450},{ 579, 5679},{ 672, 5892}, + { 785, 6082},{ 906, 6258},{ 1026, 6432},{ 1153, 6592} + } + }, + { + /*Cr qi=35 INTRA*/ + { + { 12, 7},{ 98, 388},{ 166, 773},{ 226, 1187}, + { 306, 1563},{ 411, 1874},{ 524, 2134},{ 622, 2365}, + { 721, 2577},{ 826, 2768},{ 947, 2946},{ 1066, 3106}, + { 1163, 3250},{ 1274, 3395},{ 1417, 3508},{ 1539, 3590}, + { 1639, 3671},{ 1754, 3765},{ 1865, 3855},{ 1979, 3921}, + { 2127, 3998},{ 2249, 4085},{ 2346, 4172},{ 2473, 4210} + }, + /*Cr qi=35 INTER*/ + { + { 86, 12},{ 94, 354},{ 85, 677},{ 96, 1052}, + { 113, 1439},{ 125, 1811},{ 135, 2177},{ 147, 2537}, + { 160, 2884},{ 177, 3215},{ 195, 3535},{ 219, 3842}, + { 252, 4133},{ 292, 4413},{ 339, 4680},{ 396, 4928}, + { 455, 5169},{ 514, 5408},{ 588, 5626},{ 672, 5835}, + { 750, 6051},{ 837, 6257},{ 943, 6442},{ 1073, 6595} + } + } + }, + { + { + /*Y' qi=36 INTRA*/ + { + { 116, 52},{ 432, 1312},{ 881, 2215},{ 1407, 2759}, + { 1948, 3140},{ 2511, 3484},{ 3090, 3789},{ 3672, 4036}, + { 4243, 4236},{ 4803, 4397},{ 5346, 4517},{ 5856, 4581}, + { 6350, 4614},{ 6821, 4675},{ 7286, 4763},{ 7754, 4832}, + { 8201, 4875},{ 8631, 4922},{ 9015, 4948},{ 9351, 4945}, + { 9706, 4941},{10061, 4948},{10408, 4949},{10878, 4923} + }, + /*Y' qi=36 INTER*/ + { + { 63, -16},{ 114, 1332},{ 216, 2690},{ 343, 3914}, + { 515, 5009},{ 829, 5939},{ 1399, 6586},{ 2263, 6901}, + { 3290, 6967},{ 4272, 6920},{ 5115, 6847},{ 5839, 6779}, + { 6478, 6726},{ 7051, 6685},{ 7571, 6649},{ 8050, 6614}, + { 8495, 6587},{ 8908, 6567},{ 9298, 6550},{ 9673, 6530}, + {10005, 6512},{10324, 6499},{10640, 6483},{10936, 6487} + } + }, + { + /*Cb qi=36 INTRA*/ + { + { 6, 3},{ 98, 370},{ 170, 746},{ 225, 1150}, + { 306, 1527},{ 416, 1845},{ 534, 2116},{ 642, 2363}, + { 743, 2591},{ 851, 2794},{ 964, 2972},{ 1081, 3133}, + { 1198, 3275},{ 1311, 3410},{ 1411, 3547},{ 1519, 3680}, + { 1642, 3789},{ 1750, 3892},{ 1860, 3982},{ 1998, 4054}, + { 2141, 4129},{ 2256, 4204},{ 2372, 4278},{ 2567, 4356} + }, + /*Cb qi=36 INTER*/ + { + { 107, 30},{ 96, 346},{ 88, 667},{ 100, 1039}, + { 115, 1426},{ 128, 1804},{ 142, 2164},{ 158, 2512}, + { 176, 2851},{ 195, 3178},{ 218, 3491},{ 243, 3791}, + { 270, 4084},{ 307, 4365},{ 348, 4638},{ 397, 4908}, + { 464, 5157},{ 545, 5392},{ 635, 5620},{ 734, 5831}, + { 854, 6015},{ 993, 6170},{ 1124, 6327},{ 1234, 6502} + } + }, + { + /*Cr qi=36 INTRA*/ + { + { 12, 7},{ 102, 388},{ 172, 773},{ 239, 1182}, + { 328, 1546},{ 439, 1848},{ 554, 2106},{ 651, 2341}, + { 747, 2561},{ 850, 2757},{ 972, 2934},{ 1086, 3097}, + { 1182, 3245},{ 1302, 3382},{ 1447, 3491},{ 1572, 3567}, + { 1677, 3641},{ 1793, 3733},{ 1899, 3828},{ 2013, 3894}, + { 2163, 3967},{ 2283, 4059},{ 2387, 4142},{ 2559, 4145} + }, + /*Cr qi=36 INTER*/ + { + { 98, -10},{ 96, 347},{ 89, 676},{ 102, 1048}, + { 118, 1433},{ 130, 1804},{ 141, 2167},{ 154, 2523}, + { 171, 2866},{ 190, 3194},{ 212, 3508},{ 240, 3809}, + { 276, 4099},{ 320, 4377},{ 372, 4638},{ 428, 4887}, + { 492, 5122},{ 560, 5353},{ 638, 5572},{ 725, 5779}, + { 814, 5985},{ 902, 6192},{ 1013, 6377},{ 1155, 6527} + } + } + }, + { + { + /*Y' qi=37 INTRA*/ + { + { 109, 58},{ 445, 1302},{ 927, 2177},{ 1489, 2689}, + { 2053, 3052},{ 2632, 3387},{ 3230, 3683},{ 3830, 3922}, + { 4417, 4114},{ 4992, 4266},{ 5546, 4375},{ 6067, 4430}, + { 6571, 4459},{ 7046, 4516},{ 7513, 4599},{ 7991, 4663}, + { 8445, 4706},{ 8883, 4749},{ 9273, 4771},{ 9612, 4770}, + { 9970, 4765},{10325, 4773},{10672, 4778},{11106, 4758} + }, + /*Y' qi=37 INTER*/ + { + { 56, -14},{ 114, 1333},{ 218, 2683},{ 354, 3894}, + { 550, 4966},{ 916, 5854},{ 1569, 6437},{ 2520, 6685}, + { 3596, 6704},{ 4585, 6635},{ 5424, 6556},{ 6147, 6489}, + { 6787, 6437},{ 7358, 6395},{ 7876, 6358},{ 8361, 6325}, + { 8807, 6294},{ 9229, 6271},{ 9631, 6253},{10002, 6238}, + {10356, 6228},{10678, 6212},{10975, 6197},{11274, 6185} + } + }, + { + /*Cb qi=37 INTRA*/ + { + { 6, 3},{ 99, 370},{ 171, 746},{ 227, 1149}, + { 309, 1522},{ 421, 1836},{ 541, 2104},{ 652, 2347}, + { 757, 2572},{ 871, 2768},{ 989, 2936},{ 1111, 3087}, + { 1238, 3223},{ 1357, 3352},{ 1465, 3486},{ 1576, 3612}, + { 1709, 3705},{ 1828, 3801},{ 1937, 3895},{ 2076, 3967}, + { 2220, 4035},{ 2345, 4104},{ 2466, 4173},{ 2680, 4265} + }, + /*Cb qi=37 INTER*/ + { + { 111, 27},{ 97, 344},{ 87, 667},{ 99, 1038}, + { 115, 1425},{ 128, 1802},{ 143, 2160},{ 159, 2506}, + { 176, 2843},{ 198, 3167},{ 220, 3477},{ 247, 3774}, + { 280, 4061},{ 321, 4338},{ 368, 4608},{ 427, 4867}, + { 501, 5109},{ 595, 5332},{ 701, 5544},{ 818, 5738}, + { 956, 5905},{ 1105, 6066},{ 1248, 6217},{ 1381, 6353} + } + }, + { + /*Cr qi=37 INTRA*/ + { + { 12, 7},{ 102, 388},{ 173, 773},{ 242, 1180}, + { 331, 1541},{ 444, 1839},{ 562, 2095},{ 662, 2326}, + { 763, 2540},{ 871, 2728},{ 1003, 2892},{ 1130, 3045}, + { 1230, 3188},{ 1350, 3321},{ 1503, 3418},{ 1634, 3492}, + { 1737, 3568},{ 1856, 3653},{ 1970, 3744},{ 2091, 3802}, + { 2247, 3871},{ 2371, 3962},{ 2477, 4041},{ 2655, 4052} + }, + /*Cr qi=37 INTER*/ + { + { 89, -9},{ 97, 347},{ 88, 677},{ 102, 1048}, + { 118, 1432},{ 130, 1802},{ 141, 2163},{ 154, 2517}, + { 172, 2857},{ 192, 3181},{ 216, 3494},{ 246, 3793}, + { 286, 4074},{ 337, 4343},{ 395, 4600},{ 464, 4837}, + { 534, 5066},{ 608, 5289},{ 694, 5501},{ 788, 5704}, + { 893, 5901},{ 1010, 6088},{ 1151, 6249},{ 1331, 6374} + } + } + }, + { + { + /*Y' qi=38 INTRA*/ + { + { 107, 65},{ 476, 1286},{ 968, 2148},{ 1548, 2641}, + { 2141, 2979},{ 2757, 3289},{ 3390, 3564},{ 4020, 3784}, + { 4632, 3957},{ 5224, 4097},{ 5794, 4201},{ 6326, 4250}, + { 6828, 4274},{ 7309, 4322},{ 7790, 4401},{ 8271, 4463}, + { 8729, 4498},{ 9165, 4540},{ 9552, 4566},{ 9901, 4560}, + {10266, 4552},{10617, 4563},{10964, 4572},{11393, 4567} + }, + /*Y' qi=38 INTER*/ + { + { 57, -13},{ 118, 1332},{ 233, 2665},{ 386, 3856}, + { 620, 4899},{ 1070, 5722},{ 1849, 6211},{ 2898, 6384}, + { 3989, 6376},{ 4947, 6311},{ 5754, 6249},{ 6454, 6199}, + { 7077, 6161},{ 7640, 6132},{ 8159, 6101},{ 8639, 6076}, + { 9081, 6054},{ 9502, 6037},{ 9900, 6027},{10274, 6012}, + {10621, 5999},{10938, 5991},{11237, 5977},{11557, 5966} + } + }, + { + /*Cb qi=38 INTRA*/ + { + { 8, 3},{ 104, 370},{ 179, 744},{ 243, 1139}, + { 338, 1498},{ 458, 1801},{ 584, 2060},{ 700, 2297}, + { 812, 2514},{ 935, 2699},{ 1061, 2858},{ 1189, 3007}, + { 1321, 3141},{ 1446, 3266},{ 1563, 3388},{ 1684, 3512}, + { 1816, 3614},{ 1942, 3702},{ 2055, 3793},{ 2201, 3857}, + { 2357, 3923},{ 2477, 3994},{ 2593, 4061},{ 2768, 4178} + }, + /*Cb qi=38 INTER*/ + { + { 118, 24},{ 102, 342},{ 91, 663},{ 101, 1040}, + { 116, 1427},{ 131, 1799},{ 147, 2152},{ 168, 2491}, + { 191, 2822},{ 215, 3139},{ 244, 3441},{ 276, 3731}, + { 316, 4013},{ 363, 4286},{ 423, 4546},{ 495, 4795}, + { 584, 5028},{ 691, 5242},{ 814, 5439},{ 959, 5608}, + { 1119, 5759},{ 1277, 5906},{ 1449, 6035},{ 1655, 6144} + } + }, + { + /*Cr qi=38 INTRA*/ + { + { 12, 6},{ 106, 387},{ 182, 771},{ 261, 1168}, + { 364, 1514},{ 483, 1802},{ 603, 2053},{ 707, 2282}, + { 817, 2489},{ 933, 2670},{ 1074, 2825},{ 1210, 2967}, + { 1320, 3104},{ 1444, 3229},{ 1599, 3324},{ 1735, 3396}, + { 1846, 3464},{ 1971, 3547},{ 2086, 3646},{ 2206, 3711}, + { 2366, 3773},{ 2499, 3859},{ 2603, 3945},{ 2766, 3952} + }, + /*Cr qi=38 INTER*/ + { + { 86, -9},{ 91, 352},{ 85, 680},{ 102, 1053}, + { 119, 1435},{ 132, 1799},{ 146, 2153},{ 162, 2501}, + { 183, 2835},{ 209, 3154},{ 240, 3458},{ 278, 3751}, + { 327, 4025},{ 388, 4284},{ 455, 4532},{ 529, 4766}, + { 616, 4980},{ 711, 5188},{ 815, 5386},{ 920, 5583}, + { 1042, 5770},{ 1186, 5936},{ 1348, 6080},{ 1542, 6196} + } + } + }, + { + { + /*Y' qi=39 INTRA*/ + { + { 103, 66},{ 479, 1283},{ 998, 2125},{ 1610, 2591}, + { 2223, 2913},{ 2855, 3214},{ 3501, 3482},{ 4146, 3698}, + { 4772, 3868},{ 5376, 3999},{ 5956, 4095},{ 6496, 4140}, + { 7008, 4162},{ 7499, 4209},{ 7987, 4282},{ 8478, 4338}, + { 8947, 4374},{ 9385, 4417},{ 9783, 4437},{10143, 4433}, + {10504, 4424},{10866, 4435},{11225, 4444},{11665, 4430} + }, + /*Y' qi=39 INTER*/ + { + { 56, 2},{ 118, 1332},{ 235, 2660},{ 395, 3843}, + { 653, 4867},{ 1153, 5652},{ 2003, 6089},{ 3113, 6214}, + { 4228, 6178},{ 5189, 6102},{ 6002, 6031},{ 6707, 5976}, + { 7336, 5936},{ 7901, 5900},{ 8424, 5870},{ 8915, 5844}, + { 9361, 5822},{ 9784, 5807},{10187, 5794},{10571, 5778}, + {10931, 5763},{11264, 5751},{11582, 5742},{11916, 5730} + } + }, + { + /*Cb qi=39 INTRA*/ + { + { 8, 3},{ 104, 370},{ 179, 744},{ 244, 1138}, + { 340, 1496},{ 461, 1796},{ 588, 2053},{ 705, 2288}, + { 820, 2503},{ 945, 2684},{ 1073, 2840},{ 1210, 2981}, + { 1352, 3106},{ 1480, 3225},{ 1603, 3342},{ 1728, 3464}, + { 1865, 3559},{ 1990, 3645},{ 2106, 3734},{ 2258, 3796}, + { 2413, 3856},{ 2540, 3920},{ 2667, 3986},{ 2887, 4060} + }, + /*Cb qi=39 INTER*/ + { + { 119, 19},{ 103, 340},{ 90, 664},{ 100, 1040}, + { 115, 1426},{ 131, 1797},{ 148, 2148},{ 169, 2486}, + { 192, 2816},{ 217, 3131},{ 247, 3432},{ 282, 3721}, + { 324, 3999},{ 374, 4268},{ 435, 4526},{ 520, 4766}, + { 621, 4990},{ 738, 5194},{ 878, 5376},{ 1035, 5543}, + { 1202, 5686},{ 1374, 5819},{ 1545, 5950},{ 1729, 6064} + } + }, + { + /*Cr qi=39 INTRA*/ + { + { 12, 6},{ 106, 387},{ 182, 771},{ 262, 1167}, + { 365, 1512},{ 486, 1798},{ 608, 2047},{ 713, 2274}, + { 824, 2479},{ 945, 2655},{ 1091, 2804},{ 1231, 2941}, + { 1346, 3073},{ 1475, 3194},{ 1633, 3282},{ 1778, 3345}, + { 1891, 3414},{ 2013, 3501},{ 2138, 3584},{ 2266, 3640}, + { 2428, 3701},{ 2568, 3782},{ 2674, 3863},{ 2816, 3894} + }, + /*Cr qi=39 INTER*/ + { + { 88, -7},{ 92, 352},{ 85, 680},{ 102, 1053}, + { 119, 1434},{ 132, 1797},{ 146, 2151},{ 163, 2498}, + { 185, 2830},{ 211, 3147},{ 243, 3451},{ 285, 3735}, + { 337, 4005},{ 401, 4260},{ 477, 4499},{ 565, 4721}, + { 655, 4937},{ 749, 5148},{ 858, 5344},{ 979, 5529}, + { 1110, 5710},{ 1264, 5871},{ 1460, 5990},{ 1677, 6086} + } + } + }, + { + { + /*Y' qi=40 INTRA*/ + { + { 98, 71},{ 491, 1274},{ 1023, 2103},{ 1641, 2559}, + { 2257, 2877},{ 2898, 3171},{ 3566, 3429},{ 4233, 3629}, + { 4881, 3784},{ 5499, 3906},{ 6088, 3997},{ 6631, 4040}, + { 7145, 4060},{ 7640, 4107},{ 8128, 4178},{ 8618, 4233}, + { 9077, 4267},{ 9514, 4304},{ 9919, 4324},{10277, 4317}, + {10635, 4312},{10985, 4324},{11338, 4331},{11792, 4334} + }, + /*Y' qi=40 INTER*/ + { + { 63, -26},{ 125, 1331},{ 256, 2640},{ 439, 3801}, + { 757, 4782},{ 1391, 5474},{ 2399, 5805},{ 3582, 5870}, + { 4678, 5824},{ 5600, 5763},{ 6386, 5710},{ 7076, 5667}, + { 7693, 5637},{ 8252, 5610},{ 8775, 5586},{ 9255, 5571}, + { 9694, 5556},{10115, 5541},{10530, 5530},{10903, 5522}, + {11242, 5515},{11596, 5501},{11904, 5482},{12205, 5475} + } + }, + { + /*Cb qi=40 INTRA*/ + { + { 8, 3},{ 108, 371},{ 189, 743},{ 265, 1128}, + { 371, 1475},{ 499, 1767},{ 628, 2022},{ 746, 2256}, + { 864, 2467},{ 991, 2647},{ 1124, 2801},{ 1270, 2933}, + { 1412, 3054},{ 1547, 3165},{ 1677, 3277},{ 1804, 3393}, + { 1946, 3483},{ 2078, 3569},{ 2201, 3651},{ 2352, 3711}, + { 2513, 3766},{ 2643, 3826},{ 2775, 3880},{ 3025, 3919} + }, + /*Cb qi=40 INTER*/ + { + { 114, 35},{ 104, 349},{ 96, 667},{ 106, 1040}, + { 121, 1423},{ 138, 1789},{ 158, 2132},{ 184, 2464}, + { 212, 2787},{ 242, 3095},{ 279, 3389},{ 321, 3671}, + { 374, 3941},{ 438, 4199},{ 517, 4446},{ 617, 4673}, + { 740, 4881},{ 891, 5064},{ 1058, 5225},{ 1239, 5372}, + { 1441, 5499},{ 1638, 5610},{ 1840, 5719},{ 2076, 5814} + } + }, + { + /*Cr qi=40 INTRA*/ + { + { 14, 7},{ 114, 389},{ 193, 771},{ 283, 1156}, + { 399, 1488},{ 523, 1768},{ 643, 2018},{ 752, 2245}, + { 865, 2450},{ 984, 2626},{ 1139, 2763},{ 1290, 2887}, + { 1413, 3014},{ 1550, 3128},{ 1711, 3211},{ 1865, 3268}, + { 1981, 3334},{ 2103, 3415},{ 2237, 3486},{ 2365, 3543}, + { 2529, 3610},{ 2666, 3700},{ 2775, 3779},{ 2929, 3803} + }, + /*Cr qi=40 INTER*/ + { + { 89, -8},{ 95, 353},{ 90, 681},{ 107, 1053}, + { 124, 1430},{ 139, 1787},{ 156, 2136},{ 177, 2477}, + { 203, 2803},{ 237, 3112},{ 276, 3406},{ 329, 3683}, + { 395, 3942},{ 475, 4182},{ 567, 4407},{ 665, 4624}, + { 767, 4834},{ 879, 5032},{ 1011, 5213},{ 1169, 5375}, + { 1348, 5525},{ 1547, 5654},{ 1785, 5743},{ 2066, 5787} + } + } + }, + { + { + /*Y' qi=41 INTRA*/ + { + { 98, 71},{ 495, 1272},{ 1040, 2090},{ 1675, 2533}, + { 2302, 2842},{ 2953, 3132},{ 3631, 3381},{ 4309, 3574}, + { 4966, 3726},{ 5593, 3846},{ 6189, 3934},{ 6738, 3972}, + { 7256, 3991},{ 7754, 4036},{ 8250, 4099},{ 8747, 4150}, + { 9207, 4185},{ 9650, 4222},{10057, 4242},{10411, 4237}, + {10771, 4230},{11127, 4244},{11486, 4254},{11933, 4252} + }, + /*Y' qi=41 INTER*/ + { + { 65, -25},{ 125, 1331},{ 260, 2633},{ 457, 3782}, + { 807, 4740},{ 1499, 5397},{ 2562, 5693},{ 3766, 5743}, + { 4859, 5695},{ 5776, 5638},{ 6556, 5590},{ 7243, 5554}, + { 7859, 5529},{ 8417, 5506},{ 8935, 5486},{ 9419, 5473}, + { 9869, 5460},{10296, 5446},{10711, 5436},{11089, 5430}, + {11445, 5421},{11802, 5412},{12129, 5404},{12465, 5393} + } + }, + { + /*Cb qi=41 INTRA*/ + { + { 8, 3},{ 108, 371},{ 189, 743},{ 267, 1126}, + { 374, 1471},{ 504, 1760},{ 635, 2011},{ 758, 2241}, + { 881, 2447},{ 1013, 2621},{ 1147, 2773},{ 1293, 2906}, + { 1441, 3023},{ 1580, 3131},{ 1712, 3243},{ 1844, 3360}, + { 1985, 3451},{ 2114, 3532},{ 2240, 3613},{ 2390, 3680}, + { 2550, 3740},{ 2687, 3800},{ 2825, 3862},{ 3052, 3944} + }, + /*Cb qi=41 INTER*/ + { + { 104, 39},{ 100, 350},{ 95, 667},{ 105, 1040}, + { 121, 1422},{ 137, 1787},{ 159, 2129},{ 185, 2459}, + { 216, 2778},{ 249, 3083},{ 287, 3374},{ 335, 3653}, + { 393, 3920},{ 462, 4175},{ 549, 4414},{ 660, 4636}, + { 791, 4839},{ 952, 5014},{ 1135, 5166},{ 1337, 5297}, + { 1552, 5411},{ 1752, 5530},{ 1972, 5634},{ 2224, 5724} + } + }, + { + /*Cr qi=41 INTRA*/ + { + { 15, 7},{ 115, 389},{ 193, 770},{ 284, 1154}, + { 401, 1484},{ 528, 1761},{ 652, 2005},{ 764, 2228}, + { 882, 2427},{ 1008, 2599},{ 1167, 2734},{ 1320, 2859}, + { 1443, 2990},{ 1580, 3103},{ 1743, 3181},{ 1894, 3241}, + { 2012, 3309},{ 2141, 3385},{ 2272, 3459},{ 2398, 3519}, + { 2566, 3584},{ 2707, 3680},{ 2816, 3762},{ 2991, 3770} + }, + /*Cr qi=41 INTER*/ + { + { 92, -9},{ 98, 354},{ 90, 682},{ 107, 1052}, + { 124, 1429},{ 139, 1786},{ 156, 2132},{ 178, 2471}, + { 207, 2794},{ 241, 3100},{ 285, 3391},{ 345, 3662}, + { 417, 3915},{ 503, 4151},{ 600, 4375},{ 703, 4589}, + { 815, 4791},{ 942, 4981},{ 1088, 5155},{ 1250, 5316}, + { 1432, 5462},{ 1653, 5575},{ 1930, 5639},{ 2250, 5655} + } + } + }, + { + { + /*Y' qi=42 INTRA*/ + { + { 109, 75},{ 534, 1257},{ 1114, 2047},{ 1793, 2456}, + { 2461, 2735},{ 3157, 2994},{ 3879, 3221},{ 4595, 3396}, + { 5282, 3531},{ 5931, 3638},{ 6546, 3714},{ 7105, 3749}, + { 7633, 3766},{ 8147, 3803},{ 8652, 3865},{ 9148, 3915}, + { 9613, 3946},{10075, 3976},{10489, 3997},{10835, 3994}, + {11195, 3985},{11553, 3997},{11909, 4004},{12369, 3990} + }, + /*Y' qi=42 INTER*/ + { + { 69, -23},{ 134, 1332},{ 287, 2611},{ 521, 3730}, + { 970, 4624},{ 1827, 5176},{ 3028, 5382},{ 4262, 5389}, + { 5325, 5338},{ 6214, 5291},{ 6976, 5255},{ 7651, 5228}, + { 8260, 5206},{ 8821, 5190},{ 9343, 5177},{ 9823, 5165}, + {10273, 5152},{10709, 5143},{11121, 5136},{11502, 5129}, + {11857, 5125},{12193, 5115},{12520, 5107},{12802, 5097} + } + }, + { + /*Cb qi=42 INTRA*/ + { + { 9, 3},{ 113, 371},{ 199, 743},{ 279, 1123}, + { 390, 1462},{ 525, 1743},{ 662, 1986},{ 789, 2208}, + { 916, 2406},{ 1057, 2571},{ 1204, 2712},{ 1362, 2835}, + { 1524, 2943},{ 1676, 3040},{ 1815, 3145},{ 1959, 3249}, + { 2117, 3325},{ 2249, 3406},{ 2377, 3488},{ 2537, 3547}, + { 2706, 3597},{ 2854, 3646},{ 2999, 3705},{ 3236, 3759} + }, + /*Cb qi=42 INTER*/ + { + { 114, 44},{ 107, 353},{ 101, 670},{ 111, 1041}, + { 129, 1418},{ 148, 1775},{ 174, 2110},{ 208, 2432}, + { 244, 2746},{ 283, 3046},{ 330, 3330},{ 388, 3602}, + { 460, 3858},{ 546, 4101},{ 655, 4326},{ 793, 4530}, + { 966, 4703},{ 1165, 4851},{ 1388, 4980},{ 1630, 5088}, + { 1869, 5189},{ 2122, 5268},{ 2403, 5328},{ 2667, 5417} + } + }, + { + /*Cr qi=42 INTRA*/ + { + { 15, 7},{ 120, 390},{ 202, 771},{ 298, 1150}, + { 421, 1473},{ 553, 1743},{ 681, 1982},{ 796, 2199}, + { 923, 2388},{ 1062, 2547},{ 1225, 2678},{ 1392, 2792}, + { 1531, 2907},{ 1682, 3007},{ 1856, 3074},{ 2009, 3134}, + { 2138, 3192},{ 2274, 3257},{ 2407, 3333},{ 2536, 3393}, + { 2711, 3455},{ 2875, 3531},{ 3000, 3598},{ 3186, 3599} + }, + /*Cr qi=42 INTER*/ + { + { 87, -4},{ 95, 358},{ 97, 683},{ 113, 1052}, + { 131, 1423},{ 148, 1774},{ 170, 2116},{ 198, 2448}, + { 234, 2762},{ 276, 3062},{ 331, 3343},{ 404, 3603}, + { 494, 3844},{ 598, 4067},{ 715, 4276},{ 842, 4471}, + { 977, 4661},{ 1128, 4840},{ 1311, 4991},{ 1516, 5127}, + { 1759, 5233},{ 2050, 5300},{ 2377, 5323},{ 2710, 5304} + } + } + }, + { + { + /*Y' qi=43 INTRA*/ + { + { 99, 79},{ 557, 1244},{ 1175, 2016},{ 1882, 2408}, + { 2570, 2677},{ 3288, 2926},{ 4030, 3141},{ 4760, 3307}, + { 5458, 3435},{ 6115, 3537},{ 6743, 3608},{ 7312, 3636}, + { 7841, 3652},{ 8357, 3687},{ 8870, 3742},{ 9376, 3788}, + { 9850, 3821},{10315, 3853},{10734, 3873},{11084, 3870}, + {11442, 3862},{11800, 3874},{12160, 3879},{12618, 3876} + }, + /*Y' qi=43 INTER*/ + { + { 69, -22},{ 134, 1331},{ 294, 2601},{ 551, 3703}, + { 1056, 4563},{ 2003, 5061},{ 3276, 5215},{ 4534, 5194}, + { 5599, 5133},{ 6488, 5083},{ 7257, 5044},{ 7938, 5014}, + { 8556, 4992},{ 9124, 4975},{ 9648, 4960},{10138, 4948}, + {10594, 4939},{11039, 4926},{11462, 4919},{11847, 4912}, + {12216, 4904},{12570, 4896},{12883, 4889},{13189, 4879} + } + }, + { + /*Cb qi=43 INTRA*/ + { + { 9, 3},{ 114, 371},{ 202, 740},{ 294, 1110}, + { 417, 1440},{ 558, 1716},{ 700, 1956},{ 833, 2172}, + { 966, 2365},{ 1116, 2524},{ 1269, 2661},{ 1431, 2781}, + { 1599, 2885},{ 1756, 2980},{ 1902, 3082},{ 2051, 3185}, + { 2209, 3261},{ 2337, 3342},{ 2464, 3420},{ 2633, 3475}, + { 2809, 3525},{ 2948, 3579},{ 3094, 3633},{ 3347, 3678} + }, + /*Cb qi=43 INTER*/ + { + { 111, 44},{ 106, 353},{ 102, 670},{ 112, 1040}, + { 128, 1416},{ 148, 1771},{ 176, 2104},{ 211, 2424}, + { 250, 2734},{ 293, 3030},{ 347, 3309},{ 411, 3575}, + { 490, 3828},{ 589, 4064},{ 716, 4278},{ 869, 4472}, + { 1050, 4640},{ 1264, 4781},{ 1512, 4895},{ 1775, 4991}, + { 2042, 5069},{ 2310, 5141},{ 2593, 5207},{ 2912, 5239} + } + }, + { + /*Cr qi=43 INTRA*/ + { + { 15, 7},{ 121, 390},{ 208, 767},{ 315, 1135}, + { 449, 1449},{ 586, 1715},{ 718, 1950},{ 843, 2158}, + { 977, 2342},{ 1120, 2501},{ 1290, 2632},{ 1466, 2739}, + { 1613, 2845},{ 1763, 2945},{ 1937, 3015},{ 2093, 3070}, + { 2225, 3126},{ 2366, 3194},{ 2501, 3267},{ 2634, 3324}, + { 2815, 3385},{ 2964, 3466},{ 3087, 3538},{ 3263, 3555} + }, + /*Cr qi=43 INTER*/ + { + { 84, -4},{ 93, 358},{ 95, 683},{ 113, 1052}, + { 131, 1421},{ 148, 1770},{ 171, 2110},{ 201, 2439}, + { 240, 2750},{ 287, 3046},{ 348, 3322},{ 429, 3576}, + { 527, 3811},{ 641, 4029},{ 767, 4230},{ 904, 4422}, + { 1053, 4603},{ 1225, 4765},{ 1433, 4903},{ 1661, 5030}, + { 1928, 5121},{ 2252, 5160},{ 2604, 5164},{ 2979, 5125} + } + } + }, + { + { + /*Y' qi=44 INTRA*/ + { + { 103, 80},{ 560, 1244},{ 1183, 2009},{ 1891, 2391}, + { 2586, 2649},{ 3324, 2884},{ 4093, 3089},{ 4850, 3243}, + { 5575, 3358},{ 6252, 3452},{ 6886, 3518},{ 7459, 3546}, + { 7993, 3562},{ 8515, 3594},{ 9030, 3645},{ 9534, 3691}, + {10004, 3723},{10469, 3750},{10887, 3765},{11236, 3766}, + {11596, 3762},{11960, 3775},{12317, 3784},{12766, 3789} + }, + /*Y' qi=44 INTER*/ + { + { 77, -24},{ 145, 1332},{ 332, 2580},{ 642, 3649}, + { 1270, 4438},{ 2360, 4860},{ 3685, 4982},{ 4910, 4966}, + { 5929, 4928},{ 6785, 4900},{ 7529, 4880},{ 8198, 4863}, + { 8804, 4850},{ 9361, 4842},{ 9882, 4836},{10371, 4830}, + {10827, 4822},{11262, 4816},{11672, 4811},{12052, 4807}, + {12431, 4806},{12780, 4798},{13095, 4792},{13401, 4791} + } + }, + { + /*Cb qi=44 INTRA*/ + { + { 9, 2},{ 122, 371},{ 214, 741},{ 307, 1109}, + { 433, 1432},{ 576, 1704},{ 718, 1939},{ 855, 2152}, + { 991, 2340},{ 1141, 2497},{ 1298, 2632},{ 1463, 2749}, + { 1636, 2851},{ 1796, 2944},{ 1947, 3041},{ 2101, 3140}, + { 2260, 3219},{ 2392, 3297},{ 2527, 3366},{ 2693, 3424}, + { 2872, 3477},{ 3025, 3525},{ 3175, 3584},{ 3451, 3626} + }, + /*Cb qi=44 INTER*/ + { + { 111, 14},{ 110, 339},{ 109, 671},{ 120, 1040}, + { 139, 1410},{ 162, 1758},{ 197, 2084},{ 243, 2397}, + { 291, 2702},{ 342, 2992},{ 405, 3265},{ 484, 3521}, + { 584, 3760},{ 705, 3983},{ 855, 4185},{ 1048, 4356}, + { 1274, 4500},{ 1531, 4617},{ 1816, 4707},{ 2111, 4783}, + { 2409, 4846},{ 2720, 4901},{ 3044, 4957},{ 3391, 4985} + } + }, + { + /*Cr qi=44 INTRA*/ + { + { 17, 7},{ 128, 392},{ 219, 770},{ 329, 1135}, + { 465, 1442},{ 601, 1703},{ 734, 1935},{ 862, 2142}, + { 998, 2325},{ 1147, 2482},{ 1321, 2606},{ 1496, 2710}, + { 1649, 2813},{ 1809, 2908},{ 1984, 2977},{ 2143, 3032}, + { 2279, 3087},{ 2423, 3152},{ 2559, 3225},{ 2684, 3288}, + { 2866, 3351},{ 3025, 3426},{ 3161, 3492},{ 3372, 3500} + }, + /*Cr qi=44 INTER*/ + { + { 89, 0},{ 101, 352},{ 104, 683},{ 121, 1051}, + { 141, 1414},{ 163, 1757},{ 192, 2092},{ 231, 2415}, + { 278, 2720},{ 336, 3007},{ 412, 3273},{ 510, 3516}, + { 633, 3733},{ 769, 3936},{ 914, 4130},{ 1076, 4307}, + { 1256, 4472},{ 1469, 4617},{ 1723, 4732},{ 2012, 4822}, + { 2347, 4871},{ 2716, 4875},{ 3082, 4866},{ 3422, 4826} + } + } + }, + { + { + /*Y' qi=45 INTRA*/ + { + { 119, 78},{ 610, 1226},{ 1271, 1965},{ 2026, 2319}, + { 2768, 2550},{ 3556, 2757},{ 4369, 2938},{ 5157, 3076}, + { 5901, 3182},{ 6598, 3268},{ 7253, 3326},{ 7844, 3343}, + { 8392, 3356},{ 8922, 3386},{ 9453, 3433},{ 9973, 3474}, + {10457, 3503},{10929, 3530},{11351, 3543},{11709, 3541}, + {12068, 3537},{12434, 3547},{12805, 3555},{13268, 3563} + }, + /*Y' qi=45 INTER*/ + { + { 77, -20},{ 146, 1330},{ 342, 2566},{ 699, 3604}, + { 1439, 4332},{ 2669, 4672},{ 4075, 4727},{ 5318, 4679}, + { 6345, 4630},{ 7209, 4595},{ 7963, 4570},{ 8644, 4551}, + { 9262, 4535},{ 9831, 4525},{10370, 4515},{10872, 4506}, + {11334, 4500},{11783, 4492},{12219, 4489},{12617, 4483}, + {12995, 4477},{13350, 4472},{13674, 4466},{13968, 4468} + } + }, + { + /*Cb qi=45 INTRA*/ + { + { 9, 2},{ 122, 370},{ 219, 735},{ 324, 1096}, + { 465, 1414},{ 619, 1679},{ 771, 1905},{ 920, 2103}, + { 1070, 2276},{ 1236, 2419},{ 1410, 2539},{ 1595, 2644}, + { 1784, 2736},{ 1949, 2831},{ 2104, 2931},{ 2275, 3021}, + { 2443, 3092},{ 2586, 3166},{ 2735, 3234},{ 2904, 3288}, + { 3093, 3338},{ 3262, 3382},{ 3419, 3427},{ 3708, 3456} + }, + /*Cb qi=45 INTER*/ + { + { 103, 0},{ 109, 339},{ 109, 670},{ 119, 1039}, + { 137, 1408},{ 162, 1754},{ 199, 2076},{ 248, 2386}, + { 301, 2684},{ 360, 2967},{ 433, 3234},{ 525, 3481}, + { 640, 3713},{ 780, 3924},{ 956, 4110},{ 1176, 4266}, + { 1438, 4390},{ 1736, 4481},{ 2057, 4553},{ 2385, 4613}, + { 2718, 4656},{ 3056, 4698},{ 3416, 4733},{ 3799, 4755} + } + }, + { + /*Cr qi=45 INTRA*/ + { + { 16, 7},{ 128, 391},{ 225, 763},{ 350, 1120}, + { 500, 1420},{ 649, 1673},{ 792, 1893},{ 929, 2089}, + { 1084, 2257},{ 1250, 2401},{ 1440, 2518},{ 1633, 2614}, + { 1799, 2708},{ 1968, 2798},{ 2151, 2863},{ 2314, 2914}, + { 2453, 2968},{ 2611, 3025},{ 2759, 3095},{ 2887, 3160}, + { 3082, 3210},{ 3259, 3278},{ 3403, 3342},{ 3593, 3354} + }, + /*Cr qi=45 INTER*/ + { + { 92, 0},{ 101, 352},{ 103, 682},{ 120, 1049}, + { 140, 1412},{ 163, 1752},{ 193, 2083},{ 234, 2402}, + { 287, 2702},{ 353, 2983},{ 442, 3240},{ 557, 3471}, + { 694, 3680},{ 846, 3873},{ 1014, 4056},{ 1200, 4224}, + { 1414, 4369},{ 1664, 4495},{ 1946, 4595},{ 2278, 4654}, + { 2654, 4673},{ 3047, 4658},{ 3438, 4627},{ 3825, 4585} + } + } + }, + { + { + /*Y' qi=46 INTRA*/ + { + { 119, 78},{ 610, 1227},{ 1277, 1960},{ 2043, 2309}, + { 2805, 2529},{ 3618, 2719},{ 4452, 2887},{ 5257, 3016}, + { 6017, 3115},{ 6727, 3195},{ 7392, 3248},{ 7984, 3267}, + { 8528, 3281},{ 9059, 3310},{ 9593, 3354},{10119, 3395}, + {10599, 3425},{11064, 3450},{11493, 3464},{11850, 3466}, + {12207, 3462},{12578, 3471},{12948, 3480},{13407, 3487} + }, + /*Y' qi=46 INTER*/ + { + { 74, -14},{ 149, 1326},{ 382, 2538},{ 807, 3541}, + { 1670, 4211},{ 3000, 4499},{ 4416, 4533},{ 5628, 4490}, + { 6628, 4453},{ 7479, 4425},{ 8228, 4406},{ 8902, 4393}, + { 9521, 4380},{10090, 4371},{10623, 4364},{11124, 4356}, + {11586, 4351},{12043, 4344},{12476, 4341},{12863, 4340}, + {13244, 4337},{13610, 4329},{13936, 4324},{14246, 4329} + } + }, + { + /*Cb qi=46 INTRA*/ + { + { 11, 2},{ 132, 371},{ 234, 737},{ 340, 1094}, + { 481, 1405},{ 637, 1667},{ 791, 1891},{ 944, 2084}, + { 1099, 2253},{ 1268, 2392},{ 1444, 2507},{ 1633, 2610}, + { 1825, 2700},{ 1990, 2794},{ 2147, 2895},{ 2321, 2984}, + { 2493, 3053},{ 2640, 3126},{ 2787, 3198},{ 2954, 3253}, + { 3146, 3297},{ 3313, 3344},{ 3473, 3393},{ 3757, 3434} + }, + /*Cb qi=46 INTER*/ + { + { 97, 0},{ 109, 339},{ 108, 669},{ 120, 1035}, + { 142, 1398},{ 173, 1737},{ 221, 2052},{ 281, 2353}, + { 345, 2646},{ 415, 2924},{ 504, 3183},{ 616, 3421}, + { 749, 3643},{ 914, 3842},{ 1123, 4012},{ 1379, 4150}, + { 1685, 4250},{ 2014, 4327},{ 2366, 4382},{ 2731, 4426}, + { 3083, 4470},{ 3445, 4490},{ 3805, 4511},{ 4146, 4539} + } + }, + { + /*Cr qi=46 INTRA*/ + { + { 19, 7},{ 137, 393},{ 237, 765},{ 364, 1116}, + { 516, 1411},{ 665, 1662},{ 809, 1880},{ 951, 2072}, + { 1109, 2236},{ 1278, 2378},{ 1474, 2491},{ 1669, 2584}, + { 1835, 2678},{ 2014, 2766},{ 2203, 2828},{ 2366, 2880}, + { 2506, 2933},{ 2661, 2988},{ 2810, 3053},{ 2941, 3116}, + { 3131, 3175},{ 3310, 3243},{ 3461, 3303},{ 3656, 3321} + }, + /*Cr qi=46 INTER*/ + { + { 91, 1},{ 103, 351},{ 104, 681},{ 121, 1046}, + { 144, 1401},{ 173, 1736},{ 213, 2060},{ 265, 2373}, + { 330, 2666},{ 410, 2938},{ 517, 3185},{ 655, 3404}, + { 815, 3601},{ 989, 3784},{ 1183, 3951},{ 1400, 4104}, + { 1649, 4241},{ 1933, 4352},{ 2261, 4427},{ 2646, 4458}, + { 3057, 4446},{ 3453, 4418},{ 3820, 4385},{ 4171, 4352} + } + } + }, + { + { + /*Y' qi=47 INTRA*/ + { + { 117, 83},{ 670, 1205},{ 1408, 1904},{ 2239, 2219}, + { 3049, 2414},{ 3905, 2584},{ 4775, 2734},{ 5610, 2852}, + { 6393, 2944},{ 7121, 3017},{ 7804, 3066},{ 8407, 3081}, + { 8957, 3093},{ 9498, 3119},{10043, 3160},{10582, 3199}, + {11083, 3226},{11561, 3250},{11993, 3263},{12352, 3264}, + {12711, 3259},{13092, 3266},{13463, 3271},{13918, 3275} + }, + /*Y' qi=47 INTER*/ + { + { 74, -11},{ 148, 1325},{ 404, 2518},{ 910, 3478}, + { 1916, 4080},{ 3369, 4298},{ 4823, 4292},{ 6035, 4238}, + { 7037, 4197},{ 7894, 4168},{ 8650, 4146},{ 9337, 4129}, + { 9968, 4116},{10549, 4105},{11096, 4096},{11605, 4089}, + {12081, 4083},{12547, 4076},{12990, 4070},{13399, 4070}, + {13776, 4065},{14133, 4059},{14486, 4057},{14842, 4053} + } + }, + { + /*Cb qi=47 INTRA*/ + { + { 11, 2},{ 133, 370},{ 242, 731},{ 367, 1077}, + { 524, 1378},{ 692, 1630},{ 860, 1844},{ 1028, 2024}, + { 1203, 2178},{ 1393, 2305},{ 1582, 2413},{ 1787, 2507}, + { 1992, 2590},{ 2175, 2676},{ 2351, 2767},{ 2534, 2851}, + { 2707, 2923},{ 2862, 2994},{ 3021, 3060},{ 3193, 3111}, + { 3396, 3147},{ 3573, 3184},{ 3752, 3220},{ 4038, 3255} + }, + /*Cb qi=47 INTER*/ + { + { 101, 0},{ 107, 339},{ 108, 667},{ 120, 1033}, + { 142, 1394},{ 175, 1729},{ 227, 2040},{ 295, 2335}, + { 369, 2619},{ 452, 2888},{ 556, 3138},{ 686, 3368}, + { 850, 3574},{ 1050, 3758},{ 1299, 3910},{ 1605, 4024}, + { 1950, 4104},{ 2317, 4163},{ 2689, 4210},{ 3077, 4239}, + { 3466, 4258},{ 3840, 4278},{ 4205, 4298},{ 4515, 4340} + } + }, + { + /*Cr qi=47 INTRA*/ + { + { 19, 7},{ 138, 392},{ 248, 758},{ 396, 1094}, + { 563, 1378},{ 723, 1621},{ 881, 1829},{ 1037, 2011}, + { 1214, 2165},{ 1410, 2290},{ 1623, 2393},{ 1834, 2480}, + { 2016, 2564},{ 2203, 2647},{ 2405, 2707},{ 2569, 2757}, + { 2709, 2810},{ 2871, 2860},{ 3027, 2924},{ 3178, 2980}, + { 3375, 3034},{ 3563, 3097},{ 3724, 3151},{ 3952, 3153} + }, + /*Cr qi=47 INTER*/ + { + { 91, 1},{ 100, 351},{ 102, 681},{ 120, 1043}, + { 144, 1397},{ 175, 1729},{ 219, 2049},{ 277, 2356}, + { 353, 2640},{ 451, 2902},{ 579, 3136},{ 739, 3342}, + { 926, 3525},{ 1125, 3698},{ 1343, 3859},{ 1595, 3998}, + { 1881, 4113},{ 2208, 4205},{ 2589, 4253},{ 3014, 4250}, + { 3444, 4220},{ 3838, 4183},{ 4196, 4147},{ 4521, 4116} + } + } + }, + { + { + /*Y' qi=48 INTRA*/ + { + { 107, 87},{ 681, 1200},{ 1456, 1883},{ 2306, 2193}, + { 3122, 2386},{ 3984, 2548},{ 4862, 2693},{ 5704, 2808}, + { 6495, 2899},{ 7232, 2970},{ 7915, 3018},{ 8524, 3034}, + { 9085, 3043},{ 9635, 3068},{10192, 3108},{10735, 3145}, + {11237, 3171},{11719, 3194},{12153, 3207},{12516, 3206}, + {12888, 3202},{13266, 3210},{13637, 3218},{14101, 3219} + }, + /*Y' qi=48 INTER*/ + { + { 83, -18},{ 147, 1328},{ 398, 2519},{ 923, 3468}, + { 1979, 4047},{ 3472, 4246},{ 4936, 4232},{ 6148, 4178}, + { 7150, 4139},{ 8007, 4111},{ 8765, 4091},{ 9458, 4076}, + {10090, 4063},{10676, 4054},{11226, 4045},{11742, 4038}, + {12223, 4033},{12686, 4029},{13127, 4022},{13527, 4015}, + {13915, 4012},{14277, 4007},{14619, 4004},{14966, 4001} + } + }, + { + /*Cb qi=48 INTRA*/ + { + { 11, 2},{ 134, 369},{ 245, 730},{ 373, 1075}, + { 531, 1374},{ 698, 1625},{ 865, 1839},{ 1033, 2019}, + { 1207, 2173},{ 1397, 2300},{ 1588, 2408},{ 1795, 2501}, + { 2003, 2581},{ 2187, 2666},{ 2362, 2757},{ 2548, 2841}, + { 2719, 2912},{ 2876, 2983},{ 3034, 3047},{ 3209, 3097}, + { 3409, 3137},{ 3589, 3178},{ 3762, 3216},{ 4004, 3252} + }, + /*Cb qi=48 INTER*/ + { + { 113, 26},{ 112, 344},{ 111, 668},{ 120, 1032}, + { 141, 1392},{ 173, 1727},{ 224, 2036},{ 290, 2330}, + { 363, 2612},{ 447, 2880},{ 551, 3130},{ 685, 3358}, + { 852, 3563},{ 1061, 3742},{ 1332, 3884},{ 1654, 3993}, + { 2011, 4068},{ 2394, 4120},{ 2782, 4160},{ 3172, 4186}, + { 3557, 4209},{ 3932, 4228},{ 4306, 4237},{ 4675, 4236} + } + }, + { + /*Cr qi=48 INTRA*/ + { + { 18, 7},{ 139, 389},{ 252, 755},{ 404, 1090}, + { 573, 1372},{ 732, 1615},{ 889, 1823},{ 1045, 2005}, + { 1222, 2159},{ 1417, 2285},{ 1631, 2387},{ 1843, 2474}, + { 2027, 2558},{ 2212, 2639},{ 2413, 2697},{ 2578, 2746}, + { 2720, 2798},{ 2887, 2852},{ 3040, 2913},{ 3181, 2970}, + { 3381, 3024},{ 3581, 3081},{ 3743, 3130},{ 3948, 3133} + }, + /*Cr qi=48 INTER*/ + { + { 89, 0},{ 106, 352},{ 105, 682},{ 120, 1044}, + { 144, 1395},{ 174, 1724},{ 215, 2044},{ 270, 2350}, + { 343, 2635},{ 441, 2895},{ 571, 3129},{ 735, 3334}, + { 926, 3518},{ 1139, 3684},{ 1371, 3836},{ 1628, 3977}, + { 1933, 4089},{ 2279, 4164},{ 2672, 4204},{ 3105, 4205}, + { 3533, 4176},{ 3931, 4135},{ 4290, 4089},{ 4624, 4057} + } + } + }, + { + { + /*Y' qi=49 INTRA*/ + { + { 120, 85},{ 706, 1194},{ 1485, 1875},{ 2348, 2187}, + { 3190, 2372},{ 4076, 2521},{ 4967, 2658},{ 5819, 2771}, + { 6611, 2861},{ 7345, 2936},{ 8026, 2990},{ 8626, 3013}, + { 9182, 3030},{ 9723, 3059},{10266, 3100},{10802, 3143}, + {11293, 3179},{11768, 3206},{12201, 3221},{12556, 3225}, + {12914, 3226},{13281, 3237},{13639, 3247},{14089, 3257} + }, + /*Y' qi=49 INTER*/ + { + { 72, -11},{ 155, 1320},{ 458, 2485},{ 1090, 3386}, + { 2284, 3907},{ 3835, 4075},{ 5272, 4064},{ 6449, 4026}, + { 7426, 4003},{ 8267, 3987},{ 9017, 3976},{ 9698, 3967}, + {10328, 3962},{10913, 3959},{11452, 3954},{11961, 3950}, + {12442, 3947},{12904, 3946},{13347, 3945},{13749, 3943}, + {14123, 3941},{14490, 3941},{14826, 3939},{15153, 3937} + } + }, + { + /*Cb qi=49 INTRA*/ + { + { 11, 2},{ 145, 369},{ 262, 729},{ 393, 1070}, + { 557, 1363},{ 731, 1607},{ 907, 1811},{ 1085, 1983}, + { 1268, 2130},{ 1465, 2251},{ 1658, 2359},{ 1868, 2454}, + { 2079, 2534},{ 2264, 2621},{ 2440, 2717},{ 2625, 2802}, + { 2792, 2878},{ 2945, 2954},{ 3106, 3021},{ 3277, 3075}, + { 3466, 3119},{ 3638, 3170},{ 3824, 3213},{ 4100, 3243} + }, + /*Cb qi=49 INTER*/ + { + { 98, -6},{ 113, 343},{ 110, 669},{ 122, 1029}, + { 149, 1380},{ 192, 1706},{ 258, 2007},{ 340, 2293}, + { 426, 2569},{ 525, 2831},{ 653, 3071},{ 814, 3287}, + { 1013, 3478},{ 1262, 3637},{ 1575, 3761},{ 1936, 3851}, + { 2328, 3910},{ 2741, 3949},{ 3163, 3970},{ 3559, 3994}, + { 3936, 4025},{ 4300, 4050},{ 4655, 4060},{ 4962, 4062} + } + }, + { + /*Cr qi=49 INTRA*/ + { + { 19, 7},{ 151, 389},{ 270, 753},{ 427, 1084}, + { 602, 1360},{ 767, 1595},{ 933, 1794},{ 1098, 1968}, + { 1285, 2115},{ 1489, 2237},{ 1699, 2342},{ 1912, 2435}, + { 2101, 2519},{ 2288, 2601},{ 2486, 2663},{ 2651, 2715}, + { 2799, 2769},{ 2958, 2825},{ 3106, 2890},{ 3257, 2948}, + { 3452, 3007},{ 3634, 3075},{ 3786, 3136},{ 3959, 3164} + }, + /*Cr qi=49 INTER*/ + { + { 85, 1},{ 103, 352},{ 104, 681},{ 121, 1039}, + { 152, 1382},{ 195, 1702},{ 248, 2015},{ 316, 2316}, + { 403, 2595},{ 520, 2847},{ 676, 3068},{ 870, 3258}, + { 1091, 3429},{ 1329, 3585},{ 1597, 3725},{ 1894, 3849}, + { 2242, 3940},{ 2656, 3984},{ 3098, 3992},{ 3531, 3981}, + { 3936, 3950},{ 4304, 3915},{ 4646, 3879},{ 4915, 3861} + } + } + }, + { + { + /*Y' qi=50 INTRA*/ + { + { 122, 89},{ 798, 1170},{ 1682, 1812},{ 2613, 2096}, + { 3501, 2260},{ 4430, 2388},{ 5352, 2510},{ 6228, 2613}, + { 7043, 2698},{ 7793, 2770},{ 8486, 2823},{ 9092, 2846}, + { 9652, 2865},{10210, 2895},{10773, 2936},{11315, 2979}, + {11817, 3014},{12297, 3041},{12734, 3057},{13097, 3064}, + {13443, 3067},{13813, 3078},{14190, 3088},{14646, 3103} + }, + /*Y' qi=50 INTER*/ + { + { 73, -11},{ 154, 1318},{ 501, 2457},{ 1281, 3291}, + { 2685, 3719},{ 4356, 3810},{ 5811, 3769},{ 6988, 3726}, + { 7976, 3700},{ 8835, 3682},{ 9606, 3669},{10307, 3659}, + {10953, 3652},{11556, 3645},{12115, 3643},{12641, 3640}, + {13138, 3636},{13613, 3634},{14068, 3629},{14488, 3627}, + {14876, 3625},{15237, 3621},{15585, 3623},{15922, 3629} + } + }, + { + /*Cb qi=50 INTRA*/ + { + { 11, 2},{ 148, 368},{ 278, 724},{ 431, 1052}, + { 613, 1334},{ 806, 1567},{ 1004, 1756},{ 1203, 1915}, + { 1405, 2051},{ 1621, 2163},{ 1833, 2262},{ 2059, 2347}, + { 2280, 2424},{ 2476, 2512},{ 2670, 2598},{ 2864, 2679}, + { 3037, 2754},{ 3201, 2826},{ 3376, 2887},{ 3562, 2936}, + { 3756, 2976},{ 3932, 3022},{ 4117, 3065},{ 4385, 3094} + }, + /*Cb qi=50 INTER*/ + { + { 92, -3},{ 112, 343},{ 109, 669},{ 121, 1027}, + { 149, 1375},{ 196, 1697},{ 270, 1992},{ 366, 2267}, + { 471, 2532},{ 594, 2782},{ 747, 3011},{ 942, 3212}, + { 1189, 3384},{ 1497, 3521},{ 1875, 3613},{ 2297, 3673}, + { 2739, 3710},{ 3195, 3725},{ 3644, 3737},{ 4057, 3751}, + { 4445, 3763},{ 4841, 3769},{ 5211, 3779},{ 5568, 3769} + } + }, + { + /*Cr qi=50 INTRA*/ + { + { 19, 7},{ 155, 388},{ 290, 744},{ 474, 1060}, + { 666, 1324},{ 847, 1549},{ 1033, 1737},{ 1219, 1898}, + { 1428, 2034},{ 1653, 2147},{ 1885, 2245},{ 2115, 2329}, + { 2316, 2410},{ 2517, 2486},{ 2730, 2539},{ 2901, 2586}, + { 3042, 2638},{ 3199, 2693},{ 3366, 2755},{ 3534, 2805}, + { 3738, 2858},{ 3934, 2916},{ 4079, 2975},{ 4257, 2992} + }, + /*Cr qi=50 INTER*/ + { + { 87, 1},{ 102, 353},{ 103, 680},{ 121, 1036}, + { 153, 1377},{ 199, 1694},{ 260, 1999},{ 339, 2291}, + { 446, 2559},{ 590, 2797},{ 780, 3003},{ 1010, 3176}, + { 1267, 3331},{ 1547, 3474},{ 1874, 3594},{ 2245, 3688}, + { 2666, 3742},{ 3130, 3758},{ 3594, 3748},{ 4028, 3711}, + { 4415, 3674},{ 4771, 3641},{ 5122, 3605},{ 5482, 3569} + } + } + }, + { + { + /*Y' qi=51 INTRA*/ + { + { 115, 93},{ 819, 1164},{ 1739, 1806},{ 2695, 2101}, + { 3612, 2257},{ 4552, 2374},{ 5479, 2490},{ 6352, 2593}, + { 7158, 2683},{ 7898, 2761},{ 8580, 2823},{ 9177, 2854}, + { 9728, 2880},{10268, 2917},{10816, 2966},{11350, 3016}, + {11834, 3058},{12311, 3089},{12741, 3109},{13092, 3119}, + {13434, 3126},{13791, 3142},{14156, 3155},{14590, 3171} + }, + /*Y' qi=51 INTER*/ + { + { 58, 0},{ 171, 1307},{ 610, 2407},{ 1563, 3175}, + { 3116, 3545},{ 4789, 3624},{ 6185, 3602},{ 7320, 3583}, + { 8282, 3574},{ 9124, 3569},{ 9878, 3567},{10569, 3565}, + {11207, 3563},{11801, 3564},{12359, 3566},{12884, 3567}, + {13373, 3568},{13841, 3567},{14289, 3566},{14699, 3568}, + {15086, 3568},{15446, 3566},{15788, 3564},{16103, 3568} + } + }, + { + /*Cb qi=51 INTRA*/ + { + { 14, 3},{ 161, 369},{ 297, 722},{ 454, 1047}, + { 639, 1325},{ 833, 1554},{ 1033, 1742},{ 1236, 1897}, + { 1440, 2032},{ 1653, 2148},{ 1860, 2253},{ 2077, 2347}, + { 2288, 2432},{ 2476, 2525},{ 2661, 2621},{ 2841, 2714}, + { 3010, 2797},{ 3170, 2876},{ 3333, 2945},{ 3510, 3000}, + { 3696, 3054},{ 3865, 3114},{ 4046, 3164},{ 4317, 3200} + }, + /*Cb qi=51 INTER*/ + { + { 88, -11},{ 109, 341},{ 109, 668},{ 126, 1019}, + { 168, 1358},{ 233, 1670},{ 329, 1955},{ 451, 2219}, + { 584, 2472},{ 736, 2711},{ 931, 2923},{ 1179, 3104}, + { 1480, 3254},{ 1846, 3368},{ 2265, 3448},{ 2714, 3501}, + { 3180, 3524},{ 3638, 3529},{ 4074, 3543},{ 4485, 3560}, + { 4868, 3571},{ 5238, 3581},{ 5597, 3594},{ 5953, 3591} + } + }, + { + /*Cr qi=51 INTRA*/ + { + { 24, 7},{ 168, 388},{ 309, 742},{ 496, 1054}, + { 688, 1316},{ 873, 1538},{ 1063, 1723},{ 1252, 1882}, + { 1460, 2018},{ 1682, 2134},{ 1907, 2238},{ 2125, 2332}, + { 2317, 2422},{ 2507, 2510},{ 2705, 2575},{ 2869, 2630}, + { 3015, 2684},{ 3178, 2744},{ 3329, 2815},{ 3477, 2878}, + { 3667, 2945},{ 3848, 3016},{ 3997, 3082},{ 4174, 3121} + }, + /*Cr qi=51 INTER*/ + { + { 83, -2},{ 102, 351},{ 102, 680},{ 126, 1029}, + { 172, 1359},{ 238, 1665},{ 321, 1962},{ 422, 2246}, + { 552, 2505},{ 733, 2728},{ 970, 2912},{ 1247, 3069}, + { 1552, 3209},{ 1876, 3338},{ 2251, 3440},{ 2692, 3502}, + { 3161, 3529},{ 3637, 3525},{ 4084, 3509},{ 4487, 3479}, + { 4850, 3444},{ 5181, 3419},{ 5507, 3406},{ 5786, 3398} + } + } + }, + { + { + /*Y' qi=52 INTRA*/ + { + { 117, 93},{ 814, 1168},{ 1729, 1822},{ 2706, 2119}, + { 3655, 2262},{ 4604, 2374},{ 5528, 2490},{ 6394, 2596}, + { 7189, 2691},{ 7921, 2777},{ 8596, 2846},{ 9184, 2885}, + { 9728, 2918},{10260, 2961},{10796, 3014},{11316, 3069}, + {11793, 3115},{12267, 3150},{12692, 3172},{13037, 3185}, + {13367, 3196},{13717, 3214},{14087, 3227},{14521, 3249} + }, + /*Y' qi=52 INTER*/ + { + { 52, 0},{ 169, 1308},{ 668, 2382},{ 1735, 3112}, + { 3384, 3451},{ 5077, 3519},{ 6461, 3506},{ 7587, 3496}, + { 8545, 3494},{ 9384, 3494},{10142, 3498},{10838, 3501}, + {11475, 3503},{12078, 3508},{12640, 3511},{13162, 3513}, + {13654, 3517},{14130, 3521},{14576, 3522},{14980, 3523}, + {15369, 3523},{15737, 3522},{16071, 3521},{16382, 3516} + } + }, + { + /*Cb qi=52 INTRA*/ + { + { 14, 3},{ 163, 369},{ 299, 722},{ 457, 1044}, + { 645, 1319},{ 843, 1545},{ 1050, 1728},{ 1261, 1879}, + { 1468, 2013},{ 1678, 2132},{ 1883, 2240},{ 2093, 2338}, + { 2301, 2428},{ 2488, 2523},{ 2667, 2619},{ 2843, 2718}, + { 3010, 2805},{ 3163, 2887},{ 3323, 2963},{ 3490, 3028}, + { 3665, 3087},{ 3841, 3145},{ 4011, 3197},{ 4289, 3230} + }, + /*Cb qi=52 INTER*/ + { + { 98, -7},{ 109, 342},{ 109, 668},{ 126, 1018}, + { 170, 1355},{ 242, 1663},{ 352, 1941},{ 490, 2195}, + { 642, 2439},{ 823, 2666},{ 1052, 2868},{ 1333, 3039}, + { 1670, 3178},{ 2074, 3280},{ 2524, 3348},{ 2996, 3390}, + { 3469, 3410},{ 3923, 3420},{ 4355, 3434},{ 4771, 3451}, + { 5166, 3468},{ 5532, 3483},{ 5885, 3499},{ 6263, 3501} + } + }, + { + /*Cr qi=52 INTRA*/ + { + { 25, 7},{ 170, 388},{ 312, 741},{ 500, 1051}, + { 694, 1310},{ 883, 1529},{ 1082, 1709},{ 1280, 1864}, + { 1491, 1998},{ 1710, 2117},{ 1932, 2225},{ 2143, 2324}, + { 2328, 2418},{ 2516, 2506},{ 2708, 2578},{ 2870, 2637}, + { 3017, 2693},{ 3170, 2758},{ 3312, 2835},{ 3455, 2901}, + { 3644, 2972},{ 3827, 3049},{ 3968, 3121},{ 4115, 3166} + }, + /*Cr qi=52 INTER*/ + { + { 86, -2},{ 101, 352},{ 100, 680},{ 126, 1028}, + { 175, 1356},{ 247, 1657},{ 341, 1948},{ 458, 2224}, + { 615, 2471},{ 828, 2681},{ 1091, 2857},{ 1395, 3008}, + { 1732, 3140},{ 2095, 3257},{ 2502, 3348},{ 2968, 3402}, + { 3457, 3420},{ 3926, 3413},{ 4360, 3388},{ 4759, 3357}, + { 5128, 3329},{ 5449, 3306},{ 5741, 3295},{ 6071, 3296} + } + } + }, + { + { + /*Y' qi=53 INTRA*/ + { + { 138, 93},{ 850, 1161},{ 1773, 1810},{ 2763, 2103}, + { 3722, 2245},{ 4675, 2360},{ 5600, 2483},{ 6464, 2597}, + { 7255, 2700},{ 7982, 2792},{ 8652, 2867},{ 9237, 2913}, + { 9775, 2950},{10302, 2998},{10834, 3058},{11347, 3121}, + {11826, 3169},{12299, 3207},{12713, 3235},{13054, 3250}, + {13387, 3265},{13744, 3286},{14110, 3302},{14515, 3323} + }, + /*Y' qi=53 INTER*/ + { + { 52, 2},{ 169, 1308},{ 680, 2377},{ 1763, 3103}, + { 3410, 3450},{ 5094, 3531},{ 6469, 3526},{ 7590, 3525}, + { 8547, 3530},{ 9385, 3534},{10139, 3540},{10835, 3548}, + {11479, 3553},{12075, 3559},{12634, 3565},{13159, 3570}, + {13650, 3573},{14124, 3576},{14575, 3580},{14993, 3583}, + {15375, 3584},{15744, 3584},{16091, 3583},{16421, 3586} + } + }, + { + /*Cb qi=53 INTRA*/ + { + { 14, 3},{ 167, 367},{ 317, 717},{ 492, 1033}, + { 687, 1306},{ 887, 1531},{ 1095, 1715},{ 1309, 1866}, + { 1517, 2000},{ 1729, 2119},{ 1932, 2227},{ 2146, 2325}, + { 2358, 2414},{ 2544, 2511},{ 2724, 2611},{ 2902, 2711}, + { 3070, 2800},{ 3227, 2878},{ 3381, 2954},{ 3548, 3021}, + { 3724, 3077},{ 3888, 3140},{ 4065, 3196},{ 4359, 3225} + }, + /*Cb qi=53 INTER*/ + { + { 93, -8},{ 110, 342},{ 108, 668},{ 125, 1018}, + { 170, 1355},{ 242, 1663},{ 353, 1939},{ 494, 2192}, + { 651, 2433},{ 838, 2658},{ 1076, 2856},{ 1368, 3022}, + { 1716, 3158},{ 2123, 3260},{ 2575, 3330},{ 3042, 3373}, + { 3507, 3396},{ 3962, 3413},{ 4394, 3430},{ 4797, 3452}, + { 5169, 3476},{ 5547, 3496},{ 5914, 3510},{ 6235, 3525} + } + }, + { + /*Cr qi=53 INTRA*/ + { + { 25, 7},{ 175, 386},{ 335, 734},{ 541, 1037}, + { 737, 1296},{ 926, 1516},{ 1125, 1696},{ 1324, 1851}, + { 1540, 1984},{ 1763, 2102},{ 1989, 2210},{ 2202, 2310}, + { 2386, 2404},{ 2572, 2495},{ 2768, 2569},{ 2929, 2627}, + { 3071, 2684},{ 3231, 2749},{ 3374, 2825},{ 3514, 2894}, + { 3703, 2963},{ 3882, 3040},{ 4024, 3111},{ 4190, 3150} + }, + /*Cr qi=53 INTER*/ + { + { 87, -1},{ 99, 352},{ 100, 680},{ 125, 1027}, + { 175, 1355},{ 249, 1657},{ 343, 1946},{ 462, 2220}, + { 624, 2465},{ 844, 2671},{ 1122, 2841},{ 1435, 2989}, + { 1768, 3125},{ 2134, 3243},{ 2545, 3334},{ 3002, 3393}, + { 3490, 3412},{ 3965, 3405},{ 4401, 3384},{ 4797, 3359}, + { 5156, 3328},{ 5482, 3297},{ 5800, 3292},{ 6135, 3293} + } + } + }, + { + { + /*Y' qi=54 INTRA*/ + { + { 184, 94},{ 902, 1151},{ 1876, 1776},{ 2881, 2057}, + { 3832, 2200},{ 4785, 2315},{ 5709, 2442},{ 6570, 2562}, + { 7362, 2672},{ 8092, 2771},{ 8760, 2852},{ 9337, 2901}, + { 9874, 2943},{10402, 2995},{10928, 3059},{11443, 3126}, + {11926, 3178},{12396, 3220},{12805, 3251},{13139, 3266}, + {13466, 3280},{13822, 3304},{14184, 3322},{14585, 3342} + }, + /*Y' qi=54 INTER*/ + { + { 60, 5},{ 169, 1308},{ 683, 2375},{ 1791, 3090}, + { 3478, 3412},{ 5184, 3470},{ 6568, 3455},{ 7697, 3446}, + { 8659, 3446},{ 9503, 3447},{10266, 3450},{10971, 3454}, + {11619, 3458},{12223, 3462},{12789, 3467},{13315, 3471}, + {13811, 3475},{14291, 3479},{14743, 3479},{15148, 3481}, + {15535, 3483},{15913, 3481},{16252, 3479},{16569, 3472} + } + }, + { + /*Cb qi=54 INTRA*/ + { + { 13, 2},{ 165, 367},{ 318, 715},{ 498, 1030}, + { 698, 1301},{ 906, 1523},{ 1121, 1703},{ 1336, 1853}, + { 1549, 1984},{ 1765, 2100},{ 1974, 2207},{ 2192, 2306}, + { 2402, 2396},{ 2587, 2493},{ 2773, 2591},{ 2953, 2691}, + { 3119, 2778},{ 3277, 2858},{ 3430, 2940},{ 3603, 3004}, + { 3788, 3059},{ 3950, 3121},{ 4128, 3173},{ 4398, 3215} + }, + /*Cb qi=54 INTER*/ + { + { 100, -3},{ 109, 343},{ 107, 668},{ 125, 1018}, + { 169, 1354},{ 241, 1662},{ 353, 1938},{ 496, 2190}, + { 655, 2431},{ 843, 2655},{ 1082, 2851},{ 1381, 3015}, + { 1739, 3146},{ 2154, 3243},{ 2610, 3310},{ 3094, 3344}, + { 3581, 3358},{ 4034, 3371},{ 4457, 3384},{ 4867, 3399}, + { 5255, 3413},{ 5630, 3425},{ 6003, 3440},{ 6346, 3440} + } + }, + { + /*Cr qi=54 INTRA*/ + { + { 23, 7},{ 174, 386},{ 338, 732},{ 549, 1034}, + { 751, 1289},{ 947, 1506},{ 1150, 1685},{ 1353, 1837}, + { 1572, 1969},{ 1800, 2087},{ 2031, 2192},{ 2248, 2291}, + { 2434, 2387},{ 2622, 2477},{ 2815, 2549},{ 2976, 2607}, + { 3126, 2663},{ 3286, 2727},{ 3427, 2807},{ 3569, 2877}, + { 3761, 2941},{ 3942, 3016},{ 4084, 3093},{ 4226, 3131} + }, + /*Cr qi=54 INTER*/ + { + { 88, -2},{ 99, 351},{ 100, 680},{ 125, 1027}, + { 175, 1354},{ 248, 1656},{ 343, 1945},{ 463, 2219}, + { 626, 2463},{ 850, 2668},{ 1128, 2837},{ 1445, 2983}, + { 1791, 3111},{ 2168, 3224},{ 2597, 3309},{ 3075, 3351}, + { 3560, 3364},{ 4029, 3356},{ 4464, 3335},{ 4858, 3307}, + { 5218, 3275},{ 5547, 3256},{ 5850, 3247},{ 6171, 3214} + } + } + }, + { + { + /*Y' qi=55 INTRA*/ + { + { 178, 95},{ 968, 1137},{ 2000, 1747},{ 3013, 2027}, + { 3966, 2173},{ 4920, 2294},{ 5842, 2427},{ 6702, 2553}, + { 7489, 2668},{ 8213, 2773},{ 8875, 2858},{ 9452, 2913}, + { 9986, 2959},{10504, 3016},{11023, 3085},{11530, 3157}, + {12011, 3213},{12480, 3257},{12882, 3291},{13214, 3310}, + {13542, 3325},{13890, 3350},{14248, 3371},{14671, 3398} + }, + /*Y' qi=55 INTER*/ + { + { 59, 5},{ 170, 1307},{ 725, 2358},{ 1886, 3058}, + { 3589, 3385},{ 5284, 3459},{ 6654, 3458},{ 7771, 3461}, + { 8727, 3470},{ 9564, 3478},{10322, 3488},{11019, 3497}, + {11658, 3505},{12258, 3513},{12819, 3520},{13344, 3527}, + {13840, 3533},{14314, 3537},{14755, 3541},{15161, 3544}, + {15552, 3548},{15916, 3548},{16257, 3548},{16576, 3540} + } + }, + { + /*Cb qi=55 INTRA*/ + { + { 13, 2},{ 167, 366},{ 322, 714},{ 508, 1026}, + { 716, 1292},{ 930, 1511},{ 1148, 1690},{ 1366, 1839}, + { 1578, 1972},{ 1793, 2090},{ 2001, 2199},{ 2217, 2300}, + { 2427, 2393},{ 2609, 2495},{ 2784, 2600},{ 2961, 2704}, + { 3121, 2797},{ 3268, 2884},{ 3423, 2965},{ 3590, 3032}, + { 3764, 3096},{ 3926, 3165},{ 4101, 3223},{ 4405, 3258} + }, + /*Cb qi=55 INTER*/ + { + { 90, -4},{ 109, 344},{ 107, 668},{ 126, 1017}, + { 172, 1351},{ 249, 1657},{ 370, 1928},{ 527, 2174}, + { 702, 2407},{ 909, 2624},{ 1170, 2814},{ 1493, 2970}, + { 1869, 3097},{ 2292, 3192},{ 2752, 3258},{ 3232, 3295}, + { 3709, 3314},{ 4156, 3335},{ 4592, 3355},{ 5004, 3373}, + { 5377, 3389},{ 5737, 3411},{ 6092, 3432},{ 6473, 3423} + } + }, + { + /*Cr qi=55 INTRA*/ + { + { 23, 7},{ 175, 385},{ 342, 730},{ 561, 1028}, + { 771, 1279},{ 973, 1493},{ 1181, 1669},{ 1384, 1822}, + { 1602, 1956},{ 1830, 2076},{ 2057, 2184},{ 2270, 2288}, + { 2452, 2389},{ 2637, 2484},{ 2823, 2559},{ 2983, 2621}, + { 3129, 2682},{ 3280, 2753},{ 3417, 2833},{ 3554, 2904}, + { 3743, 2977},{ 3921, 3060},{ 4055, 3137},{ 4185, 3186} + }, + /*Cr qi=55 INTER*/ + { + { 85, 0},{ 99, 352},{ 100, 679},{ 126, 1025}, + { 178, 1351},{ 256, 1650},{ 359, 1935},{ 493, 2202}, + { 675, 2439},{ 921, 2636},{ 1220, 2799},{ 1552, 2941}, + { 1910, 3068},{ 2303, 3177},{ 2735, 3262},{ 3206, 3311}, + { 3689, 3333},{ 4152, 3327},{ 4588, 3299},{ 4978, 3272}, + { 5325, 3243},{ 5651, 3221},{ 5969, 3210},{ 6218, 3185} + } + } + }, + { + { + /*Y' qi=56 INTRA*/ + { + { 137, 104},{ 1048, 1128},{ 2147, 1760},{ 3261, 2029}, + { 4319, 2131},{ 5310, 2234},{ 6245, 2351},{ 7101, 2464}, + { 7886, 2572},{ 8610, 2675},{ 9270, 2762},{ 9840, 2818}, + {10365, 2869},{10875, 2928},{11393, 2997},{11900, 3071}, + {12371, 3128},{12834, 3172},{13233, 3208},{13562, 3228}, + {13878, 3245},{14221, 3271},{14584, 3292},{15008, 3320} + }, + /*Y' qi=56 INTER*/ + { + { 19, 21},{ 207, 1292},{ 1031, 2252},{ 2553, 2846}, + { 4463, 3085},{ 6137, 3131},{ 7441, 3151},{ 8526, 3172}, + { 9468, 3193},{10301, 3209},{11059, 3224},{11760, 3237}, + {12405, 3249},{13008, 3261},{13570, 3270},{14100, 3278}, + {14597, 3284},{15074, 3289},{15524, 3297},{15929, 3302}, + {16314, 3306},{16675, 3307},{17004, 3305},{17288, 3301} + } + }, + { + /*Cb qi=56 INTRA*/ + { + { 16, 3},{ 188, 367},{ 353, 712},{ 546, 1017}, + { 765, 1275},{ 989, 1484},{ 1221, 1653},{ 1459, 1791}, + { 1681, 1920},{ 1893, 2046},{ 2102, 2160},{ 2323, 2257}, + { 2534, 2347},{ 2720, 2447},{ 2902, 2549},{ 3075, 2654}, + { 3239, 2749},{ 3392, 2835},{ 3544, 2920},{ 3712, 2988}, + { 3882, 3052},{ 4052, 3123},{ 4227, 3181},{ 4483, 3213} + }, + /*Cb qi=56 INTER*/ + { + { 92, -1},{ 111, 343},{ 114, 665},{ 148, 1003}, + { 224, 1321},{ 345, 1609},{ 526, 1858},{ 754, 2077}, + { 1009, 2281},{ 1319, 2464},{ 1702, 2614},{ 2145, 2732}, + { 2625, 2824},{ 3123, 2890},{ 3634, 2933},{ 4137, 2954}, + { 4614, 2965},{ 5052, 2988},{ 5468, 3015},{ 5852, 3035}, + { 6213, 3060},{ 6557, 3081},{ 6906, 3094},{ 7243, 3112} + } + }, + { + /*Cr qi=56 INTRA*/ + { + { 28, 8},{ 195, 385},{ 373, 727},{ 598, 1019}, + { 816, 1263},{ 1033, 1465},{ 1260, 1630},{ 1482, 1773}, + { 1717, 1900},{ 1949, 2018},{ 2178, 2128},{ 2393, 2233}, + { 2570, 2338},{ 2749, 2435},{ 2937, 2514},{ 3097, 2577}, + { 3240, 2638},{ 3398, 2709},{ 3540, 2791},{ 3673, 2865}, + { 3869, 2938},{ 4049, 3019},{ 4179, 3095},{ 4330, 3137} + }, + /*Cr qi=56 INTER*/ + { + { 83, 0},{ 99, 353},{ 103, 676},{ 146, 1010}, + { 232, 1320},{ 355, 1601},{ 512, 1866},{ 713, 2109}, + { 988, 2312},{ 1344, 2471},{ 1750, 2602},{ 2180, 2719}, + { 2642, 2819},{ 3141, 2892},{ 3653, 2939},{ 4159, 2961}, + { 4636, 2961},{ 5072, 2945},{ 5464, 2917},{ 5813, 2895}, + { 6134, 2890},{ 6458, 2883},{ 6735, 2881},{ 6953, 2902} + } + } + }, + { + { + /*Y' qi=57 INTRA*/ + { + { 170, 106},{ 1106, 1120},{ 2246, 1740},{ 3399, 1993}, + { 4482, 2077},{ 5492, 2167},{ 6446, 2273},{ 7324, 2379}, + { 8130, 2482},{ 8866, 2578},{ 9537, 2661},{10119, 2715}, + {10646, 2762},{11161, 2820},{11694, 2886},{12214, 2957}, + {12693, 3013},{13166, 3053},{13569, 3087},{13897, 3106}, + {14224, 3122},{14568, 3148},{14931, 3167},{15390, 3192} + }, + /*Y' qi=57 INTER*/ + { + { 19, 20},{ 205, 1292},{ 1096, 2229},{ 2775, 2766}, + { 4811, 2943},{ 6512, 2964},{ 7832, 2976},{ 8940, 2990}, + { 9903, 3004},{10755, 3017},{11532, 3029},{12243, 3039}, + {12891, 3047},{13502, 3058},{14073, 3065},{14603, 3071}, + {15097, 3078},{15581, 3083},{16036, 3086},{16452, 3090}, + {16855, 3093},{17222, 3094},{17552, 3092},{17851, 3098} + } + }, + { + /*Cb qi=57 INTRA*/ + { + { 16, 3},{ 197, 365},{ 384, 704},{ 603, 1001}, + { 837, 1252},{ 1077, 1455},{ 1326, 1618},{ 1581, 1748}, + { 1819, 1871},{ 2042, 1993},{ 2264, 2104},{ 2500, 2196}, + { 2722, 2280},{ 2916, 2375},{ 3103, 2473},{ 3290, 2575}, + { 3456, 2667},{ 3612, 2748},{ 3775, 2829},{ 3958, 2896}, + { 4145, 2947},{ 4307, 3012},{ 4476, 3070},{ 4733, 3110} + }, + /*Cb qi=57 INTER*/ + { + { 94, -1},{ 111, 344},{ 112, 665},{ 147, 1002}, + { 227, 1319},{ 353, 1604},{ 543, 1849},{ 785, 2062}, + { 1066, 2257},{ 1408, 2430},{ 1827, 2568},{ 2320, 2670}, + { 2848, 2743},{ 3386, 2791},{ 3934, 2812},{ 4453, 2820}, + { 4929, 2830},{ 5368, 2842},{ 5787, 2856},{ 6190, 2875}, + { 6554, 2896},{ 6895, 2913},{ 7229, 2927},{ 7572, 2932} + } + }, + { + /*Cr qi=57 INTRA*/ + { + { 28, 8},{ 207, 383},{ 413, 716},{ 661, 999}, + { 889, 1237},{ 1123, 1433},{ 1365, 1592},{ 1603, 1731}, + { 1853, 1852},{ 2103, 1965},{ 2345, 2072},{ 2571, 2173}, + { 2763, 2271},{ 2949, 2364},{ 3146, 2438},{ 3315, 2497}, + { 3459, 2552},{ 3618, 2616},{ 3767, 2697},{ 3906, 2773}, + { 4099, 2841},{ 4281, 2916},{ 4429, 2987},{ 4569, 3030} + }, + /*Cr qi=57 INTER*/ + { + { 85, 0},{ 99, 352},{ 102, 675},{ 147, 1008}, + { 235, 1317},{ 363, 1597},{ 529, 1858},{ 748, 2094}, + { 1050, 2287},{ 1439, 2436},{ 1877, 2557},{ 2352, 2660}, + { 2869, 2740},{ 3413, 2791},{ 3962, 2815},{ 4485, 2819}, + { 4955, 2816},{ 5382, 2800},{ 5769, 2772},{ 6107, 2748}, + { 6443, 2740},{ 6754, 2739},{ 7029, 2737},{ 7284, 2745} + } + } + }, + { + { + /*Y' qi=58 INTRA*/ + { + { 164, 109},{ 1198, 1111},{ 2396, 1737},{ 3606, 1978}, + { 4727, 2048},{ 5749, 2138},{ 6708, 2243},{ 7584, 2347}, + { 8388, 2449},{ 9122, 2549},{ 9784, 2635},{10354, 2691}, + {10876, 2740},{11385, 2800},{11912, 2869},{12429, 2941}, + {12902, 2997},{13375, 3040},{13779, 3075},{14103, 3096}, + {14435, 3112},{14783, 3140},{15141, 3160},{15599, 3186} + }, + /*Y' qi=58 INTER*/ + { + { 14, 23},{ 210, 1290},{ 1277, 2178},{ 3118, 2677}, + { 5207, 2834},{ 6902, 2857},{ 8218, 2878},{ 9323, 2900}, + {10285, 2919},{11132, 2934},{11899, 2949},{12599, 2961}, + {13235, 2971},{13835, 2982},{14394, 2991},{14917, 2997}, + {15412, 3005},{15882, 3009},{16325, 3013},{16735, 3016}, + {17131, 3018},{17501, 3021},{17824, 3021},{18125, 3016} + } + }, + { + /*Cb qi=58 INTRA*/ + { + { 17, 3},{ 200, 365},{ 389, 703},{ 613, 996}, + { 853, 1243},{ 1095, 1445},{ 1349, 1604},{ 1613, 1731}, + { 1853, 1853},{ 2074, 1978},{ 2292, 2091},{ 2526, 2184}, + { 2750, 2266},{ 2945, 2360},{ 3134, 2458},{ 3320, 2561}, + { 3482, 2654},{ 3641, 2737},{ 3804, 2818},{ 3985, 2881}, + { 4168, 2935},{ 4331, 3003},{ 4499, 3060},{ 4751, 3100} + }, + /*Cb qi=58 INTER*/ + { + { 94, -1},{ 112, 345},{ 112, 665},{ 152, 998}, + { 247, 1307},{ 406, 1580},{ 644, 1810},{ 938, 2007}, + { 1271, 2189},{ 1668, 2348},{ 2151, 2470},{ 2691, 2558}, + { 3249, 2619},{ 3798, 2659},{ 4334, 2682},{ 4849, 2692}, + { 5314, 2700},{ 5747, 2721},{ 6167, 2742},{ 6547, 2765}, + { 6902, 2790},{ 7251, 2804},{ 7583, 2819},{ 7924, 2833} + } + }, + { + /*Cr qi=58 INTRA*/ + { + { 29, 8},{ 210, 382},{ 419, 714},{ 671, 993}, + { 903, 1229},{ 1141, 1422},{ 1390, 1578},{ 1635, 1713}, + { 1889, 1833},{ 2140, 1946},{ 2379, 2055},{ 2604, 2157}, + { 2794, 2256},{ 2977, 2349},{ 3174, 2422},{ 3339, 2482}, + { 3483, 2537},{ 3643, 2604},{ 3790, 2684},{ 3927, 2757}, + { 4112, 2826},{ 4294, 2900},{ 4451, 2975},{ 4600, 3011} + }, + /*Cr qi=58 INTER*/ + { + { 86, 0},{ 99, 352},{ 103, 675},{ 151, 1004}, + { 256, 1306},{ 417, 1573},{ 628, 1819},{ 901, 2040}, + { 1262, 2217},{ 1705, 2353},{ 2191, 2466},{ 2713, 2556}, + { 3268, 2622},{ 3831, 2664},{ 4374, 2682},{ 4881, 2686}, + { 5339, 2685},{ 5747, 2668},{ 6123, 2646},{ 6465, 2630}, + { 6783, 2618},{ 7082, 2623},{ 7366, 2632},{ 7673, 2654} + } + } + }, + { + { + /*Y' qi=59 INTRA*/ + { + { 142, 112},{ 1259, 1100},{ 2552, 1711},{ 3815, 1933}, + { 4955, 1987},{ 5983, 2068},{ 6949, 2165},{ 7832, 2263}, + { 8645, 2359},{ 9392, 2454},{10066, 2536},{10643, 2589}, + {11174, 2636},{11696, 2693},{12230, 2758},{12752, 2826}, + {13239, 2883},{13721, 2926},{14139, 2959},{14479, 2978}, + {14811, 2993},{15166, 3020},{15532, 3039},{16000, 3062} + }, + /*Y' qi=59 INTER*/ + { + { 8, 25},{ 211, 1289},{ 1394, 2144},{ 3421, 2580}, + { 5611, 2689},{ 7316, 2701},{ 8643, 2717},{ 9762, 2734}, + {10735, 2750},{11587, 2763},{12353, 2775},{13056, 2785}, + {13693, 2793},{14288, 2805},{14843, 2814},{15361, 2821}, + {15857, 2827},{16328, 2831},{16763, 2834},{17171, 2838}, + {17568, 2840},{17941, 2842},{18285, 2843},{18586, 2839} + } + }, + { + /*Cb qi=59 INTRA*/ + { + { 17, 3},{ 224, 363},{ 441, 696},{ 689, 982}, + { 945, 1222},{ 1204, 1416},{ 1474, 1571},{ 1751, 1695}, + { 2001, 1816},{ 2228, 1941},{ 2453, 2055},{ 2693, 2147}, + { 2924, 2227},{ 3125, 2321},{ 3321, 2416},{ 3510, 2520}, + { 3676, 2616},{ 3839, 2699},{ 4008, 2778},{ 4193, 2842}, + { 4371, 2898},{ 4535, 2965},{ 4710, 3023},{ 4921, 3068} + }, + /*Cb qi=59 INTER*/ + { + { 95, -5},{ 111, 343},{ 112, 664},{ 157, 995}, + { 258, 1302},{ 429, 1569},{ 691, 1790},{ 1017, 1977}, + { 1387, 2148},{ 1832, 2294},{ 2368, 2401},{ 2961, 2472}, + { 3553, 2518},{ 4133, 2545},{ 4688, 2557},{ 5198, 2563}, + { 5663, 2574},{ 6100, 2590},{ 6511, 2608},{ 6898, 2621}, + { 7274, 2634},{ 7631, 2655},{ 7984, 2669},{ 8361, 2669} + } + }, + { + /*Cr qi=59 INTRA*/ + { + { 31, 8},{ 240, 379},{ 480, 706},{ 748, 978}, + { 993, 1208},{ 1250, 1394},{ 1519, 1543},{ 1779, 1674}, + { 2047, 1792},{ 2307, 1904},{ 2552, 2013},{ 2780, 2116}, + { 2973, 2216},{ 3165, 2309},{ 3362, 2383},{ 3528, 2444}, + { 3677, 2499},{ 3841, 2566},{ 3995, 2646},{ 4139, 2720}, + { 4324, 2793},{ 4504, 2867},{ 4658, 2939},{ 4806, 2975} + }, + /*Cr qi=59 INTER*/ + { + { 89, -3},{ 98, 352},{ 103, 674},{ 156, 1002}, + { 268, 1300},{ 441, 1562},{ 673, 1801},{ 980, 2010}, + { 1385, 2175},{ 1868, 2301},{ 2401, 2402},{ 2984, 2474}, + { 3591, 2520},{ 4179, 2545},{ 4729, 2555},{ 5232, 2553}, + { 5679, 2545},{ 6081, 2530},{ 6447, 2510},{ 6791, 2496}, + { 7101, 2487},{ 7393, 2489},{ 7684, 2499},{ 7950, 2501} + } + } + }, + { + { + /*Y' qi=60 INTRA*/ + { + { 92, 116},{ 1361, 1085},{ 2746, 1686},{ 4050, 1895}, + { 5209, 1939},{ 6244, 2012},{ 7213, 2103},{ 8105, 2197}, + { 8928, 2290},{ 9685, 2381},{10371, 2460},{10952, 2511}, + {11487, 2556},{12026, 2611},{12574, 2674},{13102, 2739}, + {13597, 2793},{14092, 2831},{14523, 2862},{14862, 2881}, + {15198, 2897},{15568, 2923},{15949, 2941},{16416, 2964} + }, + /*Y' qi=60 INTER*/ + { + { 4, 30},{ 215, 1287},{ 1547, 2104},{ 3729, 2491}, + { 5973, 2568},{ 7672, 2577},{ 9001, 2591},{10123, 2606}, + {11094, 2620},{11943, 2632},{12709, 2643},{13409, 2652}, + {14044, 2660},{14641, 2669},{15193, 2677},{15709, 2684}, + {16201, 2689},{16675, 2693},{17118, 2696},{17522, 2701}, + {17920, 2704},{18293, 2706},{18620, 2702},{18923, 2700} + } + }, + { + /*Cb qi=60 INTRA*/ + { + { 18, 3},{ 227, 362},{ 447, 694},{ 708, 974}, + { 981, 1207},{ 1252, 1397},{ 1532, 1547},{ 1822, 1663}, + { 2082, 1780},{ 2316, 1903},{ 2548, 2013},{ 2794, 2101}, + { 3029, 2178},{ 3242, 2266},{ 3445, 2360},{ 3638, 2459}, + { 3816, 2547},{ 3980, 2628},{ 4146, 2708},{ 4344, 2766}, + { 4546, 2812},{ 4725, 2872},{ 4880, 2930},{ 5054, 2966} + }, + /*Cb qi=60 INTER*/ + { + { 97, -4},{ 112, 343},{ 114, 664},{ 162, 993}, + { 273, 1294},{ 472, 1553},{ 774, 1762},{ 1138, 1939}, + { 1543, 2102},{ 2034, 2236},{ 2620, 2329},{ 3244, 2389}, + { 3860, 2423},{ 4443, 2440},{ 4997, 2449},{ 5502, 2455}, + { 5962, 2458},{ 6413, 2466},{ 6836, 2485},{ 7217, 2506}, + { 7592, 2518},{ 7957, 2533},{ 8291, 2543},{ 8574, 2545} + } + }, + { + /*Cr qi=60 INTRA*/ + { + { 32, 8},{ 243, 379},{ 488, 702},{ 771, 968}, + { 1030, 1192},{ 1300, 1373},{ 1581, 1517},{ 1854, 1643}, + { 2127, 1757},{ 2393, 1864},{ 2645, 1968},{ 2879, 2068}, + { 3078, 2166},{ 3277, 2256},{ 3484, 2325},{ 3660, 2381}, + { 3808, 2433},{ 3970, 2496},{ 4138, 2571},{ 4288, 2643}, + { 4475, 2710},{ 4655, 2778},{ 4810, 2843},{ 4959, 2879} + }, + /*Cr qi=60 INTER*/ + { + { 86, -2},{ 99, 352},{ 103, 673},{ 160, 998}, + { 284, 1292},{ 484, 1546},{ 753, 1774},{ 1100, 1973}, + { 1546, 2129},{ 2072, 2246},{ 2652, 2334},{ 3279, 2392}, + { 3911, 2425},{ 4504, 2440},{ 5044, 2443},{ 5536, 2440}, + { 5979, 2430},{ 6381, 2413},{ 6735, 2397},{ 7062, 2382}, + { 7383, 2376},{ 7680, 2375},{ 7962, 2373},{ 8203, 2379} + } + } + }, + { + { + /*Y' qi=61 INTRA*/ + { + { 54, 121},{ 1477, 1069},{ 3061, 1638},{ 4465, 1808}, + { 5649, 1827},{ 6710, 1884},{ 7716, 1958},{ 8648, 2037}, + { 9514, 2116},{10311, 2192},{11033, 2261},{11641, 2305}, + {12202, 2342},{12771, 2387},{13356, 2440},{13924, 2493}, + {14444, 2541},{14951, 2576},{15409, 2600},{15779, 2615}, + {16131, 2626},{16521, 2648},{16921, 2663},{17409, 2694} + }, + /*Y' qi=61 INTER*/ + { + { -1, 32},{ 216, 1286},{ 1806, 2036},{ 4279, 2327}, + { 6629, 2352},{ 8347, 2352},{ 9707, 2357},{10860, 2364}, + {11857, 2372},{12726, 2377},{13508, 2382},{14225, 2387}, + {14877, 2392},{15484, 2398},{16048, 2401},{16581, 2405}, + {17092, 2409},{17573, 2409},{18016, 2410},{18427, 2413}, + {18829, 2415},{19221, 2415},{19578, 2415},{19980, 2413} + } + }, + { + /*Cb qi=61 INTRA*/ + { + { 19, 3},{ 231, 362},{ 456, 693},{ 733, 965}, + { 1032, 1188},{ 1330, 1369},{ 1637, 1508},{ 1956, 1612}, + { 2241, 1718},{ 2496, 1832},{ 2750, 1932},{ 3019, 2007}, + { 3274, 2074},{ 3505, 2154},{ 3725, 2236},{ 3943, 2323}, + { 4138, 2403},{ 4323, 2476},{ 4505, 2543},{ 4706, 2592}, + { 4909, 2630},{ 5109, 2675},{ 5292, 2724},{ 5495, 2768} + }, + /*Cb qi=61 INTER*/ + { + { 91, -2},{ 111, 344},{ 114, 663},{ 166, 989}, + { 291, 1285},{ 522, 1534},{ 875, 1729},{ 1302, 1889}, + { 1786, 2031},{ 2368, 2141},{ 3042, 2207},{ 3734, 2243}, + { 4388, 2259},{ 4982, 2264},{ 5533, 2265},{ 6043, 2262}, + { 6524, 2264},{ 6982, 2274},{ 7422, 2283},{ 7831, 2295}, + { 8198, 2308},{ 8593, 2319},{ 8965, 2329},{ 9258, 2340} + } + }, + { + /*Cr qi=61 INTRA*/ + { + { 33, 9},{ 245, 378},{ 497, 699},{ 801, 958}, + { 1087, 1171},{ 1384, 1342},{ 1692, 1474},{ 1992, 1589}, + { 2290, 1692},{ 2576, 1789},{ 2852, 1884},{ 3109, 1973}, + { 3324, 2061},{ 3544, 2142},{ 3763, 2199},{ 3945, 2244}, + { 4103, 2292},{ 4283, 2349},{ 4469, 2413},{ 4635, 2476}, + { 4836, 2534},{ 5038, 2592},{ 5210, 2649},{ 5358, 2682} + }, + /*Cr qi=61 INTER*/ + { + { 82, 0},{ 97, 353},{ 104, 672},{ 165, 995}, + { 303, 1284},{ 532, 1529},{ 852, 1742},{ 1273, 1921}, + { 1798, 2057},{ 2409, 2154},{ 3090, 2212},{ 3794, 2240}, + { 4460, 2251},{ 5057, 2249},{ 5596, 2249},{ 6085, 2245}, + { 6519, 2234},{ 6908, 2220},{ 7269, 2203},{ 7618, 2196}, + { 7949, 2198},{ 8269, 2195},{ 8554, 2196},{ 8928, 2217} + } + } + }, + { + { + /*Y' qi=62 INTRA*/ + { + { 29, 124},{ 1527, 1067},{ 3221, 1618},{ 4703, 1751}, + { 5909, 1744},{ 7001, 1779},{ 8057, 1829},{ 9049, 1885}, + { 9968, 1943},{10813, 1999},{11572, 2050},{12206, 2082}, + {12801, 2107},{13402, 2140},{14020, 2180},{14625, 2223}, + {15179, 2260},{15718, 2288},{16196, 2305},{16581, 2313}, + {16963, 2324},{17382, 2341},{17800, 2351},{18318, 2376} + }, + /*Y' qi=62 INTER*/ + { + { -8, 36},{ 218, 1284},{ 2073, 1965},{ 4814, 2159}, + { 7237, 2138},{ 8979, 2124},{10378, 2115},{11570, 2109}, + {12601, 2106},{13503, 2103},{14320, 2103},{15064, 2103}, + {15746, 2103},{16384, 2104},{16975, 2105},{17534, 2105}, + {18062, 2106},{18564, 2107},{19035, 2106},{19471, 2107}, + {19890, 2107},{20288, 2107},{20651, 2107},{21012, 2108} + } + }, + { + /*Cb qi=62 INTRA*/ + { + { 21, 3},{ 283, 360},{ 565, 683},{ 907, 938}, + { 1269, 1143},{ 1611, 1311},{ 1949, 1441},{ 2290, 1535}, + { 2596, 1632},{ 2877, 1738},{ 3162, 1828},{ 3458, 1893}, + { 3745, 1948},{ 4011, 2016},{ 4253, 2089},{ 4506, 2164}, + { 4734, 2233},{ 4943, 2294},{ 5162, 2353},{ 5381, 2393}, + { 5593, 2420},{ 5807, 2454},{ 6003, 2496},{ 6210, 2543} + }, + /*Cb qi=62 INTER*/ + { + { 91, -1},{ 110, 344},{ 113, 663},{ 169, 987}, + { 306, 1279},{ 562, 1519},{ 961, 1701},{ 1450, 1845}, + { 2013, 1967},{ 2686, 2053},{ 3437, 2095},{ 4171, 2109}, + { 4841, 2109},{ 5441, 2105},{ 6002, 2097},{ 6542, 2089}, + { 7028, 2087},{ 7491, 2088},{ 7949, 2090},{ 8377, 2089}, + { 8789, 2095},{ 9195, 2103},{ 9569, 2104},{ 9937, 2102} + } + }, + { + /*Cr qi=62 INTRA*/ + { + { 38, 8},{ 308, 374},{ 619, 685},{ 984, 925}, + { 1326, 1126},{ 1662, 1285},{ 1999, 1407},{ 2328, 1512}, + { 2659, 1604},{ 2976, 1691},{ 3285, 1774},{ 3570, 1853}, + { 3815, 1931},{ 4068, 1998},{ 4304, 2044},{ 4491, 2082}, + { 4666, 2124},{ 4870, 2174},{ 5078, 2231},{ 5262, 2285}, + { 5480, 2335},{ 5703, 2378},{ 5905, 2423},{ 6075, 2454} + }, + /*Cr qi=62 INTER*/ + { + { 79, 1},{ 95, 353},{ 102, 671},{ 169, 992}, + { 318, 1277},{ 569, 1515},{ 936, 1716},{ 1428, 1876}, + { 2034, 1993},{ 2738, 2067},{ 3511, 2095},{ 4268, 2094}, + { 4943, 2087},{ 5543, 2079},{ 6074, 2074},{ 6552, 2069}, + { 6985, 2057},{ 7366, 2043},{ 7728, 2030},{ 8086, 2021}, + { 8423, 2017},{ 8752, 2016},{ 9057, 2014},{ 9376, 2008} + } + } + }, + { + { + /*Y' qi=63 INTRA*/ + { + { -59, 134},{ 1734, 1036},{ 3743, 1521},{ 5309, 1618}, + { 6520, 1597},{ 7664, 1609},{ 8809, 1630},{ 9894, 1657}, + {10907, 1687},{11838, 1717},{12673, 1744},{13379, 1758}, + {14038, 1767},{14698, 1784},{15379, 1806},{16062, 1831}, + {16694, 1852},{17300, 1867},{17827, 1878},{18250, 1881}, + {18702, 1884},{19199, 1892},{19665, 1896},{20273, 1908} + }, + /*Y' qi=63 INTER*/ + { + { -7, 33},{ 209, 1285},{ 2309, 1904},{ 5274, 2025}, + { 7801, 1966},{ 9637, 1924},{11126, 1892},{12403, 1868}, + {13515, 1849},{14491, 1834},{15380, 1822},{16197, 1814}, + {16944, 1806},{17645, 1799},{18303, 1794},{18916, 1789}, + {19494, 1785},{20056, 1782},{20568, 1779},{21047, 1776}, + {21508, 1775},{21925, 1772},{22327, 1770},{22678, 1771} + } + }, + { + /*Cb qi=63 INTRA*/ + { + { 20, 3},{ 294, 357},{ 608, 673},{ 1047, 908}, + { 1501, 1090},{ 1898, 1240},{ 2275, 1353},{ 2654, 1427}, + { 3014, 1502},{ 3366, 1579},{ 3726, 1637},{ 4084, 1674}, + { 4425, 1703},{ 4752, 1743},{ 5058, 1791},{ 5377, 1838}, + { 5676, 1877},{ 5946, 1912},{ 6213, 1945},{ 6458, 1969}, + { 6704, 1982},{ 6969, 1997},{ 7210, 2017},{ 7439, 2037} + }, + /*Cb qi=63 INTER*/ + { + { 86, 1},{ 108, 345},{ 111, 663},{ 168, 985}, + { 307, 1276},{ 577, 1513},{ 1007, 1688},{ 1550, 1819}, + { 2189, 1921},{ 2938, 1981},{ 3744, 2002},{ 4512, 2002}, + { 5199, 1996},{ 5824, 1986},{ 6419, 1971},{ 6978, 1954}, + { 7507, 1940},{ 8015, 1932},{ 8502, 1928},{ 8978, 1920}, + { 9410, 1915},{ 9842, 1910},{10262, 1901},{10634, 1896} + } + }, + { + /*Cr qi=63 INTRA*/ + { + { 38, 7},{ 324, 367},{ 677, 670},{ 1136, 892}, + { 1562, 1070},{ 1951, 1209},{ 2326, 1313},{ 2694, 1399}, + { 3074, 1471},{ 3460, 1531},{ 3850, 1575},{ 4214, 1622}, + { 4522, 1679},{ 4819, 1723},{ 5089, 1749},{ 5315, 1769}, + { 5530, 1792},{ 5756, 1825},{ 6006, 1860},{ 6244, 1889}, + { 6514, 1924},{ 6792, 1946},{ 7026, 1962},{ 7191, 1971} + }, + /*Cr qi=63 INTER*/ + { + { 80, 2},{ 95, 354},{ 101, 671},{ 167, 990}, + { 321, 1274},{ 585, 1509},{ 984, 1702},{ 1534, 1849}, + { 2217, 1947},{ 3005, 1995},{ 3839, 1999},{ 4619, 1986}, + { 5310, 1973},{ 5933, 1961},{ 6486, 1952},{ 6988, 1942}, + { 7435, 1927},{ 7817, 1911},{ 8198, 1900},{ 8552, 1895}, + { 8881, 1890},{ 9253, 1883},{ 9598, 1876},{ 9923, 1859} + } + } + } +}; + +#endif diff --git a/Engine/lib/libtheora/lib/ocintrin.h b/Engine/lib/libtheora/lib/ocintrin.h new file mode 100644 index 000000000..d49ebb215 --- /dev/null +++ b/Engine/lib/libtheora/lib/ocintrin.h @@ -0,0 +1,128 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: ocintrin.h 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ + +/*Some common macros for potential platform-specific optimization.*/ +#include +#if !defined(_ocintrin_H) +# define _ocintrin_H (1) + +/*Some specific platforms may have optimized intrinsic or inline assembly + versions of these functions which can substantially improve performance. + We define macros for them to allow easy incorporation of these non-ANSI + features.*/ + +/*Note that we do not provide a macro for abs(), because it is provided as a + library function, which we assume is translated into an intrinsic to avoid + the function call overhead and then implemented in the smartest way for the + target platform. + With modern gcc (4.x), this is true: it uses cmov instructions if the + architecture supports it and branchless bit-twiddling if it does not (the + speed difference between the two approaches is not measurable). + Interestingly, the bit-twiddling method was patented in 2000 (US 6,073,150) + by Sun Microsystems, despite prior art dating back to at least 1996: + http://web.archive.org/web/19961201174141/www.x86.org/ftp/articles/pentopt/PENTOPT.TXT + On gcc 3.x, however, our assumption is not true, as abs() is translated to a + conditional jump, which is horrible on deeply piplined architectures (e.g., + all consumer architectures for the past decade or more). + Also be warned that -C*abs(x) where C is a constant is mis-optimized as + abs(C*x) on every gcc release before 4.2.3. + See bug http://gcc.gnu.org/bugzilla/show_bug.cgi?id=34130 */ + +/*Modern gcc (4.x) can compile the naive versions of min and max with cmov if + given an appropriate architecture, but the branchless bit-twiddling versions + are just as fast, and do not require any special target architecture. + Earlier gcc versions (3.x) compiled both code to the same assembly + instructions, because of the way they represented ((_b)>(_a)) internally.*/ +#define OC_MAXI(_a,_b) ((_a)-((_a)-(_b)&-((_b)>(_a)))) +#define OC_MINI(_a,_b) ((_a)+((_b)-(_a)&-((_b)<(_a)))) +/*Clamps an integer into the given range. + If _a>_c, then the lower bound _a is respected over the upper bound _c (this + behavior is required to meet our documented API behavior). + _a: The lower bound. + _b: The value to clamp. + _c: The upper boud.*/ +#define OC_CLAMPI(_a,_b,_c) (OC_MAXI(_a,OC_MINI(_b,_c))) +#define OC_CLAMP255(_x) ((unsigned char)((((_x)<0)-1)&((_x)|-((_x)>255)))) +/*This has a chance of compiling branchless, and is just as fast as the + bit-twiddling method, which is slightly less portable, since it relies on a + sign-extended rightshift, which is not guaranteed by ANSI (but present on + every relevant platform).*/ +#define OC_SIGNI(_a) (((_a)>0)-((_a)<0)) +/*Slightly more portable than relying on a sign-extended right-shift (which is + not guaranteed by ANSI), and just as fast, since gcc (3.x and 4.x both) + compile it into the right-shift anyway.*/ +#define OC_SIGNMASK(_a) (-((_a)<0)) +/*Divides an integer by a power of two, truncating towards 0. + _dividend: The integer to divide. + _shift: The non-negative power of two to divide by. + _rmask: (1<<_shift)-1*/ +#define OC_DIV_POW2(_dividend,_shift,_rmask)\ + ((_dividend)+(OC_SIGNMASK(_dividend)&(_rmask))>>(_shift)) +/*Divides _x by 65536, truncating towards 0.*/ +#define OC_DIV2_16(_x) OC_DIV_POW2(_x,16,0xFFFF) +/*Divides _x by 2, truncating towards 0.*/ +#define OC_DIV2(_x) OC_DIV_POW2(_x,1,0x1) +/*Divides _x by 8, truncating towards 0.*/ +#define OC_DIV8(_x) OC_DIV_POW2(_x,3,0x7) +/*Divides _x by 16, truncating towards 0.*/ +#define OC_DIV16(_x) OC_DIV_POW2(_x,4,0xF) +/*Right shifts _dividend by _shift, adding _rval, and subtracting one for + negative dividends first. + When _rval is (1<<_shift-1), this is equivalent to division with rounding + ties away from zero.*/ +#define OC_DIV_ROUND_POW2(_dividend,_shift,_rval)\ + ((_dividend)+OC_SIGNMASK(_dividend)+(_rval)>>(_shift)) +/*Divides a _x by 2, rounding towards even numbers.*/ +#define OC_DIV2_RE(_x) ((_x)+((_x)>>1&1)>>1) +/*Divides a _x by (1<<(_shift)), rounding towards even numbers.*/ +#define OC_DIV_POW2_RE(_x,_shift) \ + ((_x)+((_x)>>(_shift)&1)+((1<<(_shift))-1>>1)>>(_shift)) +/*Swaps two integers _a and _b if _a>_b.*/ +#define OC_SORT2I(_a,_b) \ + do{ \ + int t__; \ + t__=((_a)^(_b))&-((_b)<(_a)); \ + (_a)^=t__; \ + (_b)^=t__; \ + } \ + while(0) + +/*Accesses one of four (signed) bytes given an index. + This can be used to avoid small lookup tables.*/ +#define OC_BYTE_TABLE32(_a,_b,_c,_d,_i) \ + ((signed char) \ + (((_a)&0xFF|((_b)&0xFF)<<8|((_c)&0xFF)<<16|((_d)&0xFF)<<24)>>(_i)*8)) +/*Accesses one of eight (unsigned) nibbles given an index. + This can be used to avoid small lookup tables.*/ +#define OC_UNIBBLE_TABLE32(_a,_b,_c,_d,_e,_f,_g,_h,_i) \ + ((((_a)&0xF|((_b)&0xF)<<4|((_c)&0xF)<<8|((_d)&0xF)<<12| \ + ((_e)&0xF)<<16|((_f)&0xF)<<20|((_g)&0xF)<<24|((_h)&0xF)<<28)>>(_i)*4)&0xF) + + + +/*All of these macros should expect floats as arguments.*/ +#define OC_MAXF(_a,_b) ((_a)<(_b)?(_b):(_a)) +#define OC_MINF(_a,_b) ((_a)>(_b)?(_b):(_a)) +#define OC_CLAMPF(_a,_b,_c) (OC_MINF(_a,OC_MAXF(_b,_c))) +#define OC_FABSF(_f) ((float)fabs(_f)) +#define OC_SQRTF(_f) ((float)sqrt(_f)) +#define OC_POWF(_b,_e) ((float)pow(_b,_e)) +#define OC_LOGF(_f) ((float)log(_f)) +#define OC_IFLOORF(_f) ((int)floor(_f)) +#define OC_ICEILF(_f) ((int)ceil(_f)) + +#endif diff --git a/Engine/lib/libtheora/lib/quant.c b/Engine/lib/libtheora/lib/quant.c new file mode 100644 index 000000000..8359f5abe --- /dev/null +++ b/Engine/lib/libtheora/lib/quant.c @@ -0,0 +1,119 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: quant.c 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ + +#include +#include +#include +#include "quant.h" +#include "decint.h" + +static const unsigned OC_DC_QUANT_MIN[2]={4<<2,8<<2}; +static const unsigned OC_AC_QUANT_MIN[2]={2<<2,4<<2}; + +/*Initializes the dequantization tables from a set of quantizer info. + Currently the dequantizer (and elsewhere enquantizer) tables are expected to + be initialized as pointing to the storage reserved for them in the + oc_theora_state (resp. oc_enc_ctx) structure. + If some tables are duplicates of others, the pointers will be adjusted to + point to a single copy of the tables, but the storage for them will not be + freed. + If you're concerned about the memory footprint, the obvious thing to do is + to move the storage out of its fixed place in the structures and allocate + it on demand. + However, a much, much better option is to only store the quantization + matrices being used for the current frame, and to recalculate these as the + qi values change between frames (this is what VP3 did).*/ +void oc_dequant_tables_init(ogg_uint16_t *_dequant[64][3][2], + int _pp_dc_scale[64],const th_quant_info *_qinfo){ + /*Coding mode: intra or inter.*/ + int qti; + /*Y', C_b, C_r*/ + int pli; + for(qti=0;qti<2;qti++)for(pli=0;pli<3;pli++){ + /*Quality index.*/ + int qi; + /*Range iterator.*/ + int qri; + for(qi=0,qri=0;qri<=_qinfo->qi_ranges[qti][pli].nranges;qri++){ + th_quant_base base; + ogg_uint32_t q; + int qi_start; + int qi_end; + memcpy(base,_qinfo->qi_ranges[qti][pli].base_matrices[qri], + sizeof(base)); + qi_start=qi; + if(qri==_qinfo->qi_ranges[qti][pli].nranges)qi_end=qi+1; + else qi_end=qi+_qinfo->qi_ranges[qti][pli].sizes[qri]; + /*Iterate over quality indicies in this range.*/ + for(;;){ + ogg_uint32_t qfac; + int zzi; + int ci; + /*In the original VP3.2 code, the rounding offset and the size of the + dead zone around 0 were controlled by a "sharpness" parameter. + The size of our dead zone is now controlled by the per-coefficient + quality thresholds returned by our HVS module. + We round down from a more accurate value when the quality of the + reconstruction does not fall below our threshold and it saves bits. + Hence, all of that VP3.2 code is gone from here, and the remaining + floating point code has been implemented as equivalent integer code + with exact precision.*/ + qfac=(ogg_uint32_t)_qinfo->dc_scale[qi]*base[0]; + /*For postprocessing, not dequantization.*/ + if(_pp_dc_scale!=NULL)_pp_dc_scale[qi]=(int)(qfac/160); + /*Scale DC the coefficient from the proper table.*/ + q=(qfac/100)<<2; + q=OC_CLAMPI(OC_DC_QUANT_MIN[qti],q,OC_QUANT_MAX); + _dequant[qi][pli][qti][0]=(ogg_uint16_t)q; + /*Now scale AC coefficients from the proper table.*/ + for(zzi=1;zzi<64;zzi++){ + q=((ogg_uint32_t)_qinfo->ac_scale[qi]*base[OC_FZIG_ZAG[zzi]]/100)<<2; + q=OC_CLAMPI(OC_AC_QUANT_MIN[qti],q,OC_QUANT_MAX); + _dequant[qi][pli][qti][zzi]=(ogg_uint16_t)q; + } + /*If this is a duplicate of a previous matrix, use that instead. + This simple check helps us improve cache coherency later.*/ + { + int dupe; + int qtj; + int plj; + dupe=0; + for(qtj=0;qtj<=qti;qtj++){ + for(plj=0;plj<(qtj=qi_end)break; + /*Interpolate the next base matrix.*/ + for(ci=0;ci<64;ci++){ + base[ci]=(unsigned char)( + (2*((qi_end-qi)*_qinfo->qi_ranges[qti][pli].base_matrices[qri][ci]+ + (qi-qi_start)*_qinfo->qi_ranges[qti][pli].base_matrices[qri+1][ci]) + +_qinfo->qi_ranges[qti][pli].sizes[qri])/ + (2*_qinfo->qi_ranges[qti][pli].sizes[qri])); + } + } + } + } +} diff --git a/Engine/lib/libtheora/lib/dec/quant.h b/Engine/lib/libtheora/lib/quant.h similarity index 82% rename from Engine/lib/libtheora/lib/dec/quant.h rename to Engine/lib/libtheora/lib/quant.h index c3cefd1d8..49ce13a65 100644 --- a/Engine/lib/libtheora/lib/dec/quant.h +++ b/Engine/lib/libtheora/lib/quant.h @@ -5,13 +5,13 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: quant.h 15400 2008-10-15 12:10:58Z tterribe $ + last mod: $Id: quant.h 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ @@ -21,14 +21,13 @@ # include "ocintrin.h" typedef ogg_uint16_t oc_quant_table[64]; -typedef oc_quant_table oc_quant_tables[64]; /*Maximum scaled quantizer value.*/ #define OC_QUANT_MAX (1024<<2) -void oc_dequant_tables_init(oc_quant_table *_dequant[2][3], +void oc_dequant_tables_init(ogg_uint16_t *_dequant[64][3][2], int _pp_dc_scale[64],const th_quant_info *_qinfo); #endif diff --git a/Engine/lib/libtheora/lib/rate.c b/Engine/lib/libtheora/lib/rate.c new file mode 100644 index 000000000..4f43bb2e5 --- /dev/null +++ b/Engine/lib/libtheora/lib/rate.c @@ -0,0 +1,1137 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: rate.c 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ +#include +#include +#include "encint.h" + +/*A rough lookup table for tan(x), 0<=x>24; + if(i>=17)i=16; + t0=OC_ROUGH_TAN_LOOKUP[i]; + t1=OC_ROUGH_TAN_LOOKUP[i+1]; + d=_alpha*36-(i<<24); + return (int)(((ogg_int64_t)t0<<32)+(t1-t0<<8)*(ogg_int64_t)d>>32); +} + +/*Re-initialize the Bessel filter coefficients with the specified delay. + This does not alter the x/y state, but changes the reaction time of the + filter. + Altering the time constant of a reactive filter without alterning internal + state is something that has to be done carefuly, but our design operates at + high enough delays and with small enough time constant changes to make it + safe.*/ +static void oc_iir_filter_reinit(oc_iir_filter *_f,int _delay){ + int alpha; + ogg_int64_t one48; + ogg_int64_t warp; + ogg_int64_t k1; + ogg_int64_t k2; + ogg_int64_t d; + ogg_int64_t a; + ogg_int64_t ik2; + ogg_int64_t b1; + ogg_int64_t b2; + /*This borrows some code from an unreleased version of Postfish. + See the recipe at http://unicorn.us.com/alex/2polefilters.html for details + on deriving the filter coefficients.*/ + /*alpha is Q24*/ + alpha=(1<<24)/_delay; + one48=(ogg_int64_t)1<<48; + /*warp is 7.12*/ + warp=OC_MAXI(oc_warp_alpha(alpha),1); + /*k1 is 9.12*/ + k1=3*warp; + /*k2 is 16.24.*/ + k2=k1*warp; + /*d is 16.15.*/ + d=((1<<12)+k1<<12)+k2+256>>9; + /*a is 0.32, since d is larger than both 1.0 and k2.*/ + a=(k2<<23)/d; + /*ik2 is 25.24.*/ + ik2=one48/k2; + /*b1 is Q56; in practice, the integer ranges between -2 and 2.*/ + b1=2*a*(ik2-(1<<24)); + /*b2 is Q56; in practice, the integer ranges between -2 and 2.*/ + b2=(one48<<8)-(4*a<<24)-b1; + /*All of the filter parameters are Q24.*/ + _f->c[0]=(ogg_int32_t)(b1+((ogg_int64_t)1<<31)>>32); + _f->c[1]=(ogg_int32_t)(b2+((ogg_int64_t)1<<31)>>32); + _f->g=(ogg_int32_t)(a+128>>8); +} + +/*Initialize a 2nd order low-pass Bessel filter with the corresponding delay + and initial value. + _value is Q24.*/ +static void oc_iir_filter_init(oc_iir_filter *_f,int _delay,ogg_int32_t _value){ + oc_iir_filter_reinit(_f,_delay); + _f->y[1]=_f->y[0]=_f->x[1]=_f->x[0]=_value; +} + +static ogg_int64_t oc_iir_filter_update(oc_iir_filter *_f,ogg_int32_t _x){ + ogg_int64_t c0; + ogg_int64_t c1; + ogg_int64_t g; + ogg_int64_t x0; + ogg_int64_t x1; + ogg_int64_t y0; + ogg_int64_t y1; + ogg_int64_t ya; + c0=_f->c[0]; + c1=_f->c[1]; + g=_f->g; + x0=_f->x[0]; + x1=_f->x[1]; + y0=_f->y[0]; + y1=_f->y[1]; + ya=(_x+x0*2+x1)*g+y0*c0+y1*c1+(1<<23)>>24; + _f->x[1]=(ogg_int32_t)x0; + _f->x[0]=_x; + _f->y[1]=(ogg_int32_t)y0; + _f->y[0]=(ogg_int32_t)ya; + return ya; +} + + + +/*Search for the quantizer that matches the target most closely. + We don't assume a linear ordering, but when there are ties we pick the + quantizer closest to the old one.*/ +static int oc_enc_find_qi_for_target(oc_enc_ctx *_enc,int _qti,int _qi_old, + int _qi_min,ogg_int64_t _log_qtarget){ + ogg_int64_t best_qdiff; + int best_qi; + int qi; + best_qi=_qi_min; + best_qdiff=_enc->log_qavg[_qti][best_qi]-_log_qtarget; + best_qdiff=best_qdiff+OC_SIGNMASK(best_qdiff)^OC_SIGNMASK(best_qdiff); + for(qi=_qi_min+1;qi<64;qi++){ + ogg_int64_t qdiff; + qdiff=_enc->log_qavg[_qti][qi]-_log_qtarget; + qdiff=qdiff+OC_SIGNMASK(qdiff)^OC_SIGNMASK(qdiff); + if(qdiffstate.qis[0]; + /*If rate control is active, use the lambda for the _target_ quantizer. + This allows us to scale to rates slightly lower than we'd normally be able + to reach, and give the rate control a semblance of "fractional qi" + precision. + TODO: Add API for changing QI, and allow extra precision.*/ + if(_enc->state.info.target_bitrate>0)lq=_enc->rc.log_qtarget; + else lq=_enc->log_qavg[_qti][qi]; + /*The resulting lambda value is less than 0x500000.*/ + _enc->lambda=(int)oc_bexp64(2*lq-0x4780BD468D6B62BLL); + /*Select additional quantizers. + The R-D optimal block AC quantizer statistics suggest that the distribution + is roughly Gaussian-like with a slight positive skew. + K-means clustering on log_qavg to select 3 quantizers produces cluster + centers of {log_qavg-0.6,log_qavg,log_qavg+0.7}. + Experiments confirm these are relatively good choices. + + Although we do greedy R-D optimization of the qii flags to avoid switching + too frequently, this becomes ineffective at low rates, either because we + do a poor job of predicting the actual R-D cost, or the greedy + optimization is not sufficient. + Therefore adaptive quantization is disabled above an (experimentally + suggested) threshold of log_qavg=7.00 (e.g., below INTRA qi=12 or + INTER qi=20 with current matrices). + This may need to be revised if the R-D cost estimation or qii flag + optimization strategies change.*/ + nqis=1; + if(lq<(OC_Q57(56)>>3)&&!_enc->vp3_compatible){ + qi1=oc_enc_find_qi_for_target(_enc,_qti,OC_MAXI(qi-1,0),0, + lq+(OC_Q57(7)+5)/10); + if(qi1!=qi)_enc->state.qis[nqis++]=qi1; + qi1=oc_enc_find_qi_for_target(_enc,_qti,OC_MINI(qi+1,63),0, + lq-(OC_Q57(6)+5)/10); + if(qi1!=qi&&qi1!=_enc->state.qis[nqis-1])_enc->state.qis[nqis++]=qi1; + } + _enc->state.nqis=nqis; +} + +/*Binary exponential of _log_scale with 24-bit fractional precision and + saturation. + _log_scale: A binary logarithm in Q24 format. + Return: The binary exponential in Q24 format, saturated to 2**47-1 if + _log_scale was too large.*/ +static ogg_int64_t oc_bexp_q24(ogg_int32_t _log_scale){ + if(_log_scale<(ogg_int32_t)23<<24){ + ogg_int64_t ret; + ret=oc_bexp64(((ogg_int64_t)_log_scale<<33)+OC_Q57(24)); + return ret<0x7FFFFFFFFFFFLL?ret:0x7FFFFFFFFFFFLL; + } + return 0x7FFFFFFFFFFFLL; +} + +/*Convenience function converts Q57 value to a clamped 32-bit Q24 value + _in: input in Q57 format. + Return: same number in Q24 */ +static ogg_int32_t oc_q57_to_q24(ogg_int64_t _in){ + ogg_int64_t ret; + ret=_in+((ogg_int64_t)1<<32)>>33; + /*0x80000000 is automatically converted to unsigned on 32-bit systems. + -0x7FFFFFFF-1 is needed to avoid "promoting" the whole expression to + unsigned.*/ + return (ogg_int32_t)OC_CLAMPI(-0x7FFFFFFF-1,ret,0x7FFFFFFF); +} + +/*Binary exponential of _log_scale with 24-bit fractional precision and + saturation. + _log_scale: A binary logarithm in Q57 format. + Return: The binary exponential in Q24 format, saturated to 2**31-1 if + _log_scale was too large.*/ +static ogg_int32_t oc_bexp64_q24(ogg_int64_t _log_scale){ + if(_log_scalerc.bits_per_frame=(_enc->state.info.target_bitrate* + (ogg_int64_t)_enc->state.info.fps_denominator)/ + _enc->state.info.fps_numerator; + /*Insane framerates or frame sizes mean insane bitrates. + Let's not get carried away.*/ + if(_enc->rc.bits_per_frame>0x400000000000LL){ + _enc->rc.bits_per_frame=(ogg_int64_t)0x400000000000LL; + } + else if(_enc->rc.bits_per_frame<32)_enc->rc.bits_per_frame=32; + _enc->rc.buf_delay=OC_MAXI(_enc->rc.buf_delay,12); + _enc->rc.max=_enc->rc.bits_per_frame*_enc->rc.buf_delay; + /*Start with a buffer fullness of 50% plus 25% of the amount we plan to spend + on a single keyframe interval. + We can require fully half the bits in an interval for a keyframe, so this + initial level gives us maximum flexibility for over/under-shooting in + subsequent frames.*/ + _enc->rc.target=(_enc->rc.max+1>>1)+(_enc->rc.bits_per_frame+2>>2)* + OC_MINI(_enc->keyframe_frequency_force,_enc->rc.buf_delay); + _enc->rc.fullness=_enc->rc.target; + /*Pick exponents and initial scales for quantizer selection.*/ + npixels=_enc->state.info.frame_width* + (ogg_int64_t)_enc->state.info.frame_height; + _enc->rc.log_npixels=oc_blog64(npixels); + ibpp=npixels/_enc->rc.bits_per_frame; + if(ibpp<1){ + _enc->rc.exp[0]=59; + _enc->rc.log_scale[0]=oc_blog64(1997)-OC_Q57(8); + } + else if(ibpp<2){ + _enc->rc.exp[0]=55; + _enc->rc.log_scale[0]=oc_blog64(1604)-OC_Q57(8); + } + else{ + _enc->rc.exp[0]=48; + _enc->rc.log_scale[0]=oc_blog64(834)-OC_Q57(8); + } + if(ibpp<4){ + _enc->rc.exp[1]=100; + _enc->rc.log_scale[1]=oc_blog64(2249)-OC_Q57(8); + } + else if(ibpp<8){ + _enc->rc.exp[1]=95; + _enc->rc.log_scale[1]=oc_blog64(1751)-OC_Q57(8); + } + else{ + _enc->rc.exp[1]=73; + _enc->rc.log_scale[1]=oc_blog64(1260)-OC_Q57(8); + } + _enc->rc.prev_drop_count=0; + _enc->rc.log_drop_scale=OC_Q57(0); + /*Set up second order followers, initialized according to corresponding + time constants.*/ + oc_iir_filter_init(&_enc->rc.scalefilter[0],4, + oc_q57_to_q24(_enc->rc.log_scale[0])); + inter_delay=(_enc->rc.twopass? + OC_MAXI(_enc->keyframe_frequency_force,12):_enc->rc.buf_delay)>>1; + _enc->rc.inter_count=0; + /*We clamp the actual inter_delay to a minimum of 10 to work within the range + of values where later incrementing the delay works as designed. + 10 is not an exact choice, but rather a good working trade-off.*/ + _enc->rc.inter_delay=10; + _enc->rc.inter_delay_target=inter_delay; + oc_iir_filter_init(&_enc->rc.scalefilter[1],_enc->rc.inter_delay, + oc_q57_to_q24(_enc->rc.log_scale[1])); + oc_iir_filter_init(&_enc->rc.vfrfilter,4, + oc_bexp64_q24(_enc->rc.log_drop_scale)); +} + +void oc_rc_state_init(oc_rc_state *_rc,oc_enc_ctx *_enc){ + _rc->twopass=0; + _rc->twopass_buffer_bytes=0; + _rc->twopass_force_kf=0; + _rc->frame_metrics=NULL; + _rc->rate_bias=0; + if(_enc->state.info.target_bitrate>0){ + /*The buffer size is set equal to the keyframe interval, clamped to the + range [12,256] frames. + The 12 frame minimum gives us some chance to distribute bit estimation + errors. + The 256 frame maximum means we'll require 8-10 seconds of pre-buffering + at 24-30 fps, which is not unreasonable.*/ + _rc->buf_delay=_enc->keyframe_frequency_force>256? + 256:_enc->keyframe_frequency_force; + /*By default, enforce all buffer constraints.*/ + _rc->drop_frames=1; + _rc->cap_overflow=1; + _rc->cap_underflow=0; + oc_enc_rc_reset(_enc); + } +} + +void oc_rc_state_clear(oc_rc_state *_rc){ + _ogg_free(_rc->frame_metrics); +} + +void oc_enc_rc_resize(oc_enc_ctx *_enc){ + /*If encoding has not yet begun, reset the buffer state.*/ + if(_enc->state.curframe_num<0)oc_enc_rc_reset(_enc); + else{ + int idt; + /*Otherwise, update the bounds on the buffer, but not the current + fullness.*/ + _enc->rc.bits_per_frame=(_enc->state.info.target_bitrate* + (ogg_int64_t)_enc->state.info.fps_denominator)/ + _enc->state.info.fps_numerator; + /*Insane framerates or frame sizes mean insane bitrates. + Let's not get carried away.*/ + if(_enc->rc.bits_per_frame>0x400000000000LL){ + _enc->rc.bits_per_frame=(ogg_int64_t)0x400000000000LL; + } + else if(_enc->rc.bits_per_frame<32)_enc->rc.bits_per_frame=32; + _enc->rc.buf_delay=OC_MAXI(_enc->rc.buf_delay,12); + _enc->rc.max=_enc->rc.bits_per_frame*_enc->rc.buf_delay; + _enc->rc.target=(_enc->rc.max+1>>1)+(_enc->rc.bits_per_frame+2>>2)* + OC_MINI(_enc->keyframe_frequency_force,_enc->rc.buf_delay); + /*Update the INTER-frame scale filter delay. + We jump to it immediately if we've already seen enough frames; otherwise + it is simply set as the new target.*/ + _enc->rc.inter_delay_target=idt=OC_MAXI(_enc->rc.buf_delay>>1,10); + if(idtrc.inter_delay,_enc->rc.inter_count)){ + oc_iir_filter_init(&_enc->rc.scalefilter[1],idt, + _enc->rc.scalefilter[1].y[0]); + _enc->rc.inter_delay=idt; + } + } + /*If we're in pass-2 mode, make sure the frame metrics array is big enough + to hold frame statistics for the full buffer.*/ + if(_enc->rc.twopass==2){ + int cfm; + int buf_delay; + int reset_window; + buf_delay=_enc->rc.buf_delay; + reset_window=_enc->rc.frame_metrics==NULL&&(_enc->rc.frames_total[0]==0|| + buf_delay<_enc->rc.frames_total[0]+_enc->rc.frames_total[1] + +_enc->rc.frames_total[2]); + cfm=_enc->rc.cframe_metrics; + /*Only try to resize the frame metrics buffer if a) it's too small and + b) we were using a finite buffer, or are about to start.*/ + if(cfmrc.frame_metrics!=NULL||reset_window)){ + oc_frame_metrics *fm; + int nfm; + int fmh; + fm=(oc_frame_metrics *)_ogg_realloc(_enc->rc.frame_metrics, + buf_delay*sizeof(*_enc->rc.frame_metrics)); + if(fm==NULL){ + /*We failed to allocate a finite buffer.*/ + /*If we don't have a valid 2-pass header yet, just return; we'll reset + the buffer size when we read the header.*/ + if(_enc->rc.frames_total[0]==0)return; + /*Otherwise revert to the largest finite buffer previously set, or to + whole-file buffering if we were still using that.*/ + _enc->rc.buf_delay=_enc->rc.frame_metrics!=NULL? + cfm:_enc->rc.frames_total[0]+_enc->rc.frames_total[1] + +_enc->rc.frames_total[2]; + oc_enc_rc_resize(_enc); + return; + } + _enc->rc.frame_metrics=fm; + _enc->rc.cframe_metrics=buf_delay; + /*Re-organize the circular buffer.*/ + fmh=_enc->rc.frame_metrics_head; + nfm=_enc->rc.nframe_metrics; + if(fmh+nfm>cfm){ + int shift; + shift=OC_MINI(fmh+nfm-cfm,buf_delay-cfm); + memcpy(fm+cfm,fm,OC_MINI(fmh+nfm-cfm,buf_delay-cfm)*sizeof(*fm)); + if(fmh+nfm>buf_delay)memmove(fm,fm+shift,fmh+nfm-buf_delay); + } + } + /*We were using whole-file buffering; now we're not.*/ + if(reset_window){ + _enc->rc.nframes[0]=_enc->rc.nframes[1]=_enc->rc.nframes[2]=0; + _enc->rc.scale_sum[0]=_enc->rc.scale_sum[1]=0; + _enc->rc.scale_window_end=_enc->rc.scale_window0= + _enc->state.curframe_num+_enc->prev_dup_count+1; + if(_enc->rc.twopass_buffer_bytes){ + int qti; + /*We already read the metrics for the first frame in the window.*/ + *(_enc->rc.frame_metrics)=*&_enc->rc.cur_metrics; + _enc->rc.nframe_metrics++; + qti=_enc->rc.cur_metrics.frame_type; + _enc->rc.nframes[qti]++; + _enc->rc.nframes[2]+=_enc->rc.cur_metrics.dup_count; + _enc->rc.scale_sum[qti]+=oc_bexp_q24(_enc->rc.cur_metrics.log_scale); + _enc->rc.scale_window_end+=_enc->rc.cur_metrics.dup_count+1; + if(_enc->rc.scale_window_end-_enc->rc.scale_window0rc.twopass_buffer_bytes=0; + } + } + } + /*Otherwise, we could shrink the size of the current window, if necessary, + but leaving it like it is lets us adapt to the new buffer size more + gracefully.*/ + } +} + +/*Scale the number of frames by the number of expected drops/duplicates.*/ +static int oc_rc_scale_drop(oc_rc_state *_rc,int _nframes){ + if(_rc->prev_drop_count>0||_rc->log_drop_scale>OC_Q57(0)){ + ogg_int64_t dup_scale; + dup_scale=oc_bexp64((_rc->log_drop_scale + +oc_blog64(_rc->prev_drop_count+1)>>1)+OC_Q57(8)); + if(dup_scale<_nframes<<8){ + int dup_scalei; + dup_scalei=(int)dup_scale; + if(dup_scalei>0)_nframes=((_nframes<<8)+dup_scalei-1)/dup_scalei; + } + else _nframes=!!_nframes; + } + return _nframes; +} + +int oc_enc_select_qi(oc_enc_ctx *_enc,int _qti,int _clamp){ + ogg_int64_t rate_total; + ogg_int64_t rate_bias; + int nframes[2]; + int buf_delay; + int buf_pad; + ogg_int64_t log_qtarget; + ogg_int64_t log_scale0; + ogg_int64_t log_cur_scale; + ogg_int64_t log_qexp; + int exp0; + int old_qi; + int qi; + /*Figure out how to re-distribute bits so that we hit our fullness target + before the last keyframe in our current buffer window (after the current + frame), or the end of the buffer window, whichever comes first.*/ + log_cur_scale=(ogg_int64_t)_enc->rc.scalefilter[_qti].y[0]<<33; + buf_pad=0; + switch(_enc->rc.twopass){ + default:{ + ogg_uint32_t next_key_frame; + /*Single pass mode: assume only forced keyframes and attempt to estimate + the drop count for VFR content.*/ + next_key_frame=_qti?_enc->keyframe_frequency_force + -(_enc->state.curframe_num-_enc->state.keyframe_num):0; + nframes[0]=(_enc->rc.buf_delay-OC_MINI(next_key_frame,_enc->rc.buf_delay) + +_enc->keyframe_frequency_force-1)/_enc->keyframe_frequency_force; + if(nframes[0]+_qti>1){ + nframes[0]--; + buf_delay=next_key_frame+nframes[0]*_enc->keyframe_frequency_force; + } + else buf_delay=_enc->rc.buf_delay; + nframes[1]=buf_delay-nframes[0]; + /*Downgrade the delta frame rate to correspond to the recent drop count + history.*/ + nframes[1]=oc_rc_scale_drop(&_enc->rc,nframes[1]); + }break; + case 1:{ + /*Pass 1 mode: use a fixed qi value.*/ + qi=_enc->state.qis[0]; + _enc->rc.log_qtarget=_enc->log_qavg[_qti][qi]; + return qi; + }break; + case 2:{ + ogg_int64_t scale_sum[2]; + int qti; + /*Pass 2 mode: we know exactly how much of each frame type there is in + the current buffer window, and have estimates for the scales.*/ + nframes[0]=_enc->rc.nframes[0]; + nframes[1]=_enc->rc.nframes[1]; + scale_sum[0]=_enc->rc.scale_sum[0]; + scale_sum[1]=_enc->rc.scale_sum[1]; + /*The window size can be slightly larger than the buffer window for VFR + content; clamp it down, if appropriate (the excess will all be dup + frames).*/ + buf_delay=OC_MINI(_enc->rc.scale_window_end-_enc->rc.scale_window0, + _enc->rc.buf_delay); + /*If we're approaching the end of the file, add some slack to keep us + from slamming into a rail. + Our rate accuracy goes down, but it keeps the result sensible. + We position the target where the first forced keyframe beyond the end + of the file would be (for consistency with 1-pass mode).*/ + buf_pad=OC_MINI(_enc->rc.buf_delay,_enc->state.keyframe_num + +_enc->keyframe_frequency_force-_enc->rc.scale_window0); + if(buf_delayrc.frame_metrics!=NULL){ + int fmi; + int fm_tail; + fm_tail=_enc->rc.frame_metrics_head+_enc->rc.nframe_metrics; + if(fm_tail>=_enc->rc.cframe_metrics)fm_tail-=_enc->rc.cframe_metrics; + for(fmi=fm_tail;;){ + oc_frame_metrics *m; + fmi--; + if(fmi<0)fmi+=_enc->rc.cframe_metrics; + /*Stop before we remove the first frame.*/ + if(fmi==_enc->rc.frame_metrics_head)break; + m=_enc->rc.frame_metrics+fmi; + /*If we find a keyframe, remove it and everything past it.*/ + if(m->frame_type==OC_INTRA_FRAME){ + do{ + qti=m->frame_type; + nframes[qti]--; + scale_sum[qti]-=oc_bexp_q24(m->log_scale); + buf_delay-=m->dup_count+1; + fmi++; + if(fmi>=_enc->rc.cframe_metrics)fmi=0; + m=_enc->rc.frame_metrics+fmi; + } + while(fmi!=fm_tail); + /*And stop scanning backwards.*/ + break; + } + } + } + } + /*If we're not using the same frame type as in pass 1 (because someone + changed the keyframe interval), remove that scale estimate. + We'll add in a replacement for the correct frame type below.*/ + qti=_enc->rc.cur_metrics.frame_type; + if(qti!=_qti){ + nframes[qti]--; + scale_sum[qti]-=oc_bexp_q24(_enc->rc.cur_metrics.log_scale); + } + /*Compute log_scale estimates for each frame type from the pass-1 scales + we measured in the current window.*/ + for(qti=0;qti<2;qti++){ + _enc->rc.log_scale[qti]=nframes[qti]>0? + oc_blog64(scale_sum[qti])-oc_blog64(nframes[qti])-OC_Q57(24): + -_enc->rc.log_npixels; + } + /*If we're not using the same frame type as in pass 1, add a scale + estimate for the corresponding frame using the current low-pass + filter value. + This is mostly to ensure we have a valid estimate even when pass 1 had + no frames of this type in the buffer window. + TODO: We could also plan ahead and figure out how many keyframes we'll + be forced to add in the current buffer window.*/ + qti=_enc->rc.cur_metrics.frame_type; + if(qti!=_qti){ + ogg_int64_t scale; + scale=_enc->rc.log_scale[_qti]rc.log_scale[_qti]+OC_Q57(24)):0x7FFFFFFFFFFFLL; + scale*=nframes[_qti]; + nframes[_qti]++; + scale+=oc_bexp_q24(log_cur_scale>>33); + _enc->rc.log_scale[_qti]=oc_blog64(scale) + -oc_blog64(nframes[qti])-OC_Q57(24); + } + else log_cur_scale=(ogg_int64_t)_enc->rc.cur_metrics.log_scale<<33; + /*Add the padding from above. + This basically reverts to 1-pass estimations in the last keyframe + interval.*/ + if(buf_pad>0){ + ogg_int64_t scale; + int nextra_frames; + /*Extend the buffer.*/ + buf_delay+=buf_pad; + /*Add virtual delta frames according to the estimated drop count.*/ + nextra_frames=oc_rc_scale_drop(&_enc->rc,buf_pad); + /*And blend in the low-pass filtered scale according to how many frames + we added.*/ + scale= + oc_bexp64(_enc->rc.log_scale[1]+OC_Q57(24))*(ogg_int64_t)nframes[1] + +oc_bexp_q24(_enc->rc.scalefilter[1].y[0])*(ogg_int64_t)nextra_frames; + nframes[1]+=nextra_frames; + _enc->rc.log_scale[1]=oc_blog64(scale)-oc_blog64(nframes[1])-OC_Q57(24); + } + }break; + } + /*If we've been missing our target, add a penalty term.*/ + rate_bias=(_enc->rc.rate_bias/(_enc->state.curframe_num+1000))* + (buf_delay-buf_pad); + /*rate_total is the total bits available over the next buf_delay frames.*/ + rate_total=_enc->rc.fullness-_enc->rc.target+rate_bias + +buf_delay*_enc->rc.bits_per_frame; + log_scale0=_enc->rc.log_scale[_qti]+_enc->rc.log_npixels; + /*If there aren't enough bits to achieve our desired fullness level, use the + minimum quality permitted.*/ + if(rate_total<=buf_delay)log_qtarget=OC_QUANT_MAX_LOG; + else{ + static const ogg_int64_t LOG_KEY_RATIO=0x0137222BB70747BALL; + ogg_int64_t log_scale1; + ogg_int64_t rlo; + ogg_int64_t rhi; + log_scale1=_enc->rc.log_scale[1-_qti]+_enc->rc.log_npixels; + rlo=0; + rhi=(rate_total+nframes[_qti]-1)/nframes[_qti]; + while(rlo>1; + log_rpow=oc_blog64(curr)-log_scale0; + log_rpow=(log_rpow+(_enc->rc.exp[_qti]>>1))/_enc->rc.exp[_qti]; + if(_qti)log_rpow+=LOG_KEY_RATIO>>6; + else log_rpow-=LOG_KEY_RATIO>>6; + log_rpow*=_enc->rc.exp[1-_qti]; + rscale=nframes[1-_qti]*oc_bexp64(log_scale1+log_rpow); + rdiff=nframes[_qti]*curr+rscale-rate_total; + if(rdiff<0)rlo=curr+1; + else if(rdiff>0)rhi=curr-1; + else break; + } + log_qtarget=OC_Q57(2)-((oc_blog64(rlo)-log_scale0+(_enc->rc.exp[_qti]>>1))/ + _enc->rc.exp[_qti]<<6); + log_qtarget=OC_MINI(log_qtarget,OC_QUANT_MAX_LOG); + } + /*The above allocation looks only at the total rate we'll accumulate in the + next buf_delay frames. + However, we could overflow the buffer on the very next frame, so check for + that here, if we're not using a soft target.*/ + exp0=_enc->rc.exp[_qti]; + if(_enc->rc.cap_overflow){ + ogg_int64_t margin; + ogg_int64_t soft_limit; + ogg_int64_t log_soft_limit; + /*Allow 3% of the buffer for prediction error. + This should be plenty, and we don't mind if we go a bit over; we only + want to keep these bits from being completely wasted.*/ + margin=_enc->rc.max+31>>5; + /*We want to use at least this many bits next frame.*/ + soft_limit=_enc->rc.fullness+_enc->rc.bits_per_frame-(_enc->rc.max-margin); + log_soft_limit=oc_blog64(soft_limit); + /*If we're predicting we won't use that many...*/ + log_qexp=(log_qtarget-OC_Q57(2)>>6)*exp0; + if(log_scale0-log_qexp>32)* + ((OC_MINI(margin,soft_limit)<<32)/margin); + log_qtarget=((log_qexp+(exp0>>1))/exp0<<6)+OC_Q57(2); + } + } + /*If this was not one of the initial frames, limit the change in quality.*/ + old_qi=_enc->state.qis[0]; + if(_clamp){ + ogg_int64_t log_qmin; + ogg_int64_t log_qmax; + /*Clamp the target quantizer to within [0.8*Q,1.2*Q], where Q is the + current quantizer. + TODO: With user-specified quant matrices, we need to enlarge these limits + if they don't actually let us change qi values.*/ + log_qmin=_enc->log_qavg[_qti][old_qi]-0x00A4D3C25E68DC58LL; + log_qmax=_enc->log_qavg[_qti][old_qi]+0x00A4D3C25E68DC58LL; + log_qtarget=OC_CLAMPI(log_qmin,log_qtarget,log_qmax); + } + /*The above allocation looks only at the total rate we'll accumulate in the + next buf_delay frames. + However, we could bust the budget on the very next frame, so check for that + here, if we're not using a soft target.*/ + /* Disabled when our minimum qi > 0; if we saturate log_qtarget to + to the maximum possible size when we have a minimum qi, the + resulting lambda will interact very strangely with SKIP. The + resulting artifacts look like waterfalls. */ + if(_enc->state.info.quality==0){ + ogg_int64_t log_hard_limit; + /*Compute the maximum number of bits we can use in the next frame. + Allow 50% of the rate for a single frame for prediction error. + This may not be enough for keyframes or sudden changes in complexity.*/ + log_hard_limit=oc_blog64(_enc->rc.fullness+(_enc->rc.bits_per_frame>>1)); + /*If we're predicting we'll use more than this...*/ + log_qexp=(log_qtarget-OC_Q57(2)>>6)*exp0; + if(log_scale0-log_qexp>log_hard_limit){ + /*Force the target to hit our limit exactly.*/ + log_qexp=log_scale0-log_hard_limit; + log_qtarget=((log_qexp+(exp0>>1))/exp0<<6)+OC_Q57(2); + /*If that target is unreasonable, oh well; we'll have to drop.*/ + log_qtarget=OC_MINI(log_qtarget,OC_QUANT_MAX_LOG); + } + } + /*Compute a final estimate of the number of bits we plan to use.*/ + log_qexp=(log_qtarget-OC_Q57(2)>>6)*_enc->rc.exp[_qti]; + _enc->rc.rate_bias+=oc_bexp64(log_cur_scale+_enc->rc.log_npixels-log_qexp); + qi=oc_enc_find_qi_for_target(_enc,_qti,old_qi, + _enc->state.info.quality,log_qtarget); + /*Save the quantizer target for lambda calculations.*/ + _enc->rc.log_qtarget=log_qtarget; + return qi; +} + +int oc_enc_update_rc_state(oc_enc_ctx *_enc, + long _bits,int _qti,int _qi,int _trial,int _droppable){ + ogg_int64_t buf_delta; + ogg_int64_t log_scale; + int dropped; + dropped=0; + /* Drop frames also disabled for now in the case of infinite-buffer + two-pass mode */ + if(!_enc->rc.drop_frames||_enc->rc.twopass&&_enc->rc.frame_metrics==NULL){ + _droppable=0; + } + buf_delta=_enc->rc.bits_per_frame*(1+_enc->dup_count); + if(_bits<=0){ + /*We didn't code any blocks in this frame.*/ + log_scale=OC_Q57(-64); + _bits=0; + } + else{ + ogg_int64_t log_bits; + ogg_int64_t log_qexp; + /*Compute the estimated scale factor for this frame type.*/ + log_bits=oc_blog64(_bits); + log_qexp=_enc->rc.log_qtarget-OC_Q57(2); + log_qexp=(log_qexp>>6)*(_enc->rc.exp[_qti]); + log_scale=OC_MINI(log_bits-_enc->rc.log_npixels+log_qexp,OC_Q57(16)); + } + /*Special two-pass processing.*/ + switch(_enc->rc.twopass){ + case 1:{ + /*Pass 1 mode: save the metrics for this frame.*/ + _enc->rc.cur_metrics.log_scale=oc_q57_to_q24(log_scale); + _enc->rc.cur_metrics.dup_count=_enc->dup_count; + _enc->rc.cur_metrics.frame_type=_enc->state.frame_type; + _enc->rc.twopass_buffer_bytes=0; + }break; + case 2:{ + /*Pass 2 mode:*/ + if(!_trial){ + ogg_int64_t next_frame_num; + int qti; + /*Move the current metrics back one frame.*/ + *&_enc->rc.prev_metrics=*&_enc->rc.cur_metrics; + next_frame_num=_enc->state.curframe_num+_enc->dup_count+1; + /*Back out the last frame's statistics from the sliding window.*/ + qti=_enc->rc.prev_metrics.frame_type; + _enc->rc.frames_left[qti]--; + _enc->rc.frames_left[2]-=_enc->rc.prev_metrics.dup_count; + _enc->rc.nframes[qti]--; + _enc->rc.nframes[2]-=_enc->rc.prev_metrics.dup_count; + _enc->rc.scale_sum[qti]-=oc_bexp_q24(_enc->rc.prev_metrics.log_scale); + _enc->rc.scale_window0=(int)next_frame_num; + /*Free the corresponding entry in the circular buffer.*/ + if(_enc->rc.frame_metrics!=NULL){ + _enc->rc.nframe_metrics--; + _enc->rc.frame_metrics_head++; + if(_enc->rc.frame_metrics_head>=_enc->rc.cframe_metrics){ + _enc->rc.frame_metrics_head=0; + } + } + /*Mark us ready for the next 2-pass packet.*/ + _enc->rc.twopass_buffer_bytes=0; + /*Update state, so the user doesn't have to keep calling 2pass_in after + they've fed in all the data when we're using a finite buffer.*/ + _enc->prev_dup_count=_enc->dup_count; + oc_enc_rc_2pass_in(_enc,NULL,0); + } + }break; + } + /*Common to all passes:*/ + if(_bits>0){ + if(_trial){ + oc_iir_filter *f; + /*Use the estimated scale factor directly if this was a trial.*/ + f=_enc->rc.scalefilter+_qti; + f->y[1]=f->y[0]=f->x[1]=f->x[0]=oc_q57_to_q24(log_scale); + _enc->rc.log_scale[_qti]=log_scale; + } + else{ + /*Lengthen the time constant for the INTER filter as we collect more + frame statistics, until we reach our target.*/ + if(_enc->rc.inter_delay<_enc->rc.inter_delay_target&& + _enc->rc.inter_count>=_enc->rc.inter_delay&&_qti==OC_INTER_FRAME){ + oc_iir_filter_reinit(&_enc->rc.scalefilter[1],++_enc->rc.inter_delay); + } + /*Otherwise update the low-pass scale filter for this frame type, + regardless of whether or not we dropped this frame.*/ + _enc->rc.log_scale[_qti]=oc_iir_filter_update( + _enc->rc.scalefilter+_qti,oc_q57_to_q24(log_scale))<<33; + /*If this frame busts our budget, it must be dropped.*/ + if(_droppable&&_enc->rc.fullness+buf_delta<_bits){ + _enc->rc.prev_drop_count+=1+_enc->dup_count; + _bits=0; + dropped=1; + } + else{ + ogg_uint32_t drop_count; + /*Update a low-pass filter to estimate the "real" frame rate taking + drops and duplicates into account. + This is only done if the frame is coded, as it needs the final + count of dropped frames.*/ + drop_count=_enc->rc.prev_drop_count+1; + if(drop_count>0x7F)drop_count=0x7FFFFFFF; + else drop_count<<=24; + _enc->rc.log_drop_scale=oc_blog64(oc_iir_filter_update( + &_enc->rc.vfrfilter,drop_count))-OC_Q57(24); + /*Initialize the drop count for this frame to the user-requested dup + count. + It will be increased if we drop more frames.*/ + _enc->rc.prev_drop_count=_enc->dup_count; + } + } + /*Increment the INTER frame count, for filter adaptation purposes.*/ + if(_enc->rc.inter_countrc.inter_count+=_qti; + } + /*Increase the drop count.*/ + else _enc->rc.prev_drop_count+=1+_enc->dup_count; + /*And update the buffer fullness level.*/ + if(!_trial){ + _enc->rc.fullness+=buf_delta-_bits; + /*If we're too quick filling the buffer and overflow is capped, + that rate is lost forever.*/ + if(_enc->rc.cap_overflow&&_enc->rc.fullness>_enc->rc.max){ + _enc->rc.fullness=_enc->rc.max; + } + /*If we're too quick draining the buffer and underflow is capped, + don't try to make up that rate later.*/ + if(_enc->rc.cap_underflow&&_enc->rc.fullness<0){ + _enc->rc.fullness=0; + } + /*Adjust the bias for the real bits we've used.*/ + _enc->rc.rate_bias-=_bits; + } + return dropped; +} + +#define OC_RC_2PASS_VERSION (1) +#define OC_RC_2PASS_HDR_SZ (38) +#define OC_RC_2PASS_PACKET_SZ (8) + +static void oc_rc_buffer_val(oc_rc_state *_rc,ogg_int64_t _val,int _bytes){ + while(_bytes-->0){ + _rc->twopass_buffer[_rc->twopass_buffer_bytes++]=(unsigned char)(_val&0xFF); + _val>>=8; + } +} + +int oc_enc_rc_2pass_out(oc_enc_ctx *_enc,unsigned char **_buf){ + if(_enc->rc.twopass_buffer_bytes==0){ + if(_enc->rc.twopass==0){ + int qi; + /*Pick first-pass qi for scale calculations.*/ + qi=oc_enc_select_qi(_enc,0,0); + _enc->state.nqis=1; + _enc->state.qis[0]=qi; + _enc->rc.twopass=1; + _enc->rc.frames_total[0]=_enc->rc.frames_total[1]= + _enc->rc.frames_total[2]=0; + _enc->rc.scale_sum[0]=_enc->rc.scale_sum[1]=0; + /*Fill in dummy summary values.*/ + oc_rc_buffer_val(&_enc->rc,0x5032544F,4); + oc_rc_buffer_val(&_enc->rc,OC_RC_2PASS_VERSION,4); + oc_rc_buffer_val(&_enc->rc,0,OC_RC_2PASS_HDR_SZ-8); + } + else{ + int qti; + qti=_enc->rc.cur_metrics.frame_type; + _enc->rc.scale_sum[qti]+=oc_bexp_q24(_enc->rc.cur_metrics.log_scale); + _enc->rc.frames_total[qti]++; + _enc->rc.frames_total[2]+=_enc->rc.cur_metrics.dup_count; + oc_rc_buffer_val(&_enc->rc, + _enc->rc.cur_metrics.dup_count|_enc->rc.cur_metrics.frame_type<<31,4); + oc_rc_buffer_val(&_enc->rc,_enc->rc.cur_metrics.log_scale,4); + } + } + else if(_enc->packet_state==OC_PACKET_DONE&& + _enc->rc.twopass_buffer_bytes!=OC_RC_2PASS_HDR_SZ){ + _enc->rc.twopass_buffer_bytes=0; + oc_rc_buffer_val(&_enc->rc,0x5032544F,4); + oc_rc_buffer_val(&_enc->rc,OC_RC_2PASS_VERSION,4); + oc_rc_buffer_val(&_enc->rc,_enc->rc.frames_total[0],4); + oc_rc_buffer_val(&_enc->rc,_enc->rc.frames_total[1],4); + oc_rc_buffer_val(&_enc->rc,_enc->rc.frames_total[2],4); + oc_rc_buffer_val(&_enc->rc,_enc->rc.exp[0],1); + oc_rc_buffer_val(&_enc->rc,_enc->rc.exp[1],1); + oc_rc_buffer_val(&_enc->rc,_enc->rc.scale_sum[0],8); + oc_rc_buffer_val(&_enc->rc,_enc->rc.scale_sum[1],8); + } + else{ + /*The data for this frame has already been retrieved.*/ + *_buf=NULL; + return 0; + } + *_buf=_enc->rc.twopass_buffer; + return _enc->rc.twopass_buffer_bytes; +} + +static size_t oc_rc_buffer_fill(oc_rc_state *_rc, + unsigned char *_buf,size_t _bytes,size_t _consumed,size_t _goal){ + while(_rc->twopass_buffer_fill<_goal&&_consumed<_bytes){ + _rc->twopass_buffer[_rc->twopass_buffer_fill++]=_buf[_consumed++]; + } + return _consumed; +} + +static ogg_int64_t oc_rc_unbuffer_val(oc_rc_state *_rc,int _bytes){ + ogg_int64_t ret; + int shift; + ret=0; + shift=0; + while(_bytes-->0){ + ret|=((ogg_int64_t)_rc->twopass_buffer[_rc->twopass_buffer_bytes++])<rc.twopass==0){ + _enc->rc.twopass=2; + _enc->rc.twopass_buffer_fill=0; + _enc->rc.frames_total[0]=0; + _enc->rc.nframe_metrics=0; + _enc->rc.cframe_metrics=0; + _enc->rc.frame_metrics_head=0; + _enc->rc.scale_window0=0; + _enc->rc.scale_window_end=0; + } + /*If we haven't got a valid summary header yet, try to parse one.*/ + if(_enc->rc.frames_total[0]==0){ + if(!_buf){ + int frames_needed; + /*If we're using a whole-file buffer, we just need the first frame. + Otherwise, we may need as many as one per buffer slot.*/ + frames_needed=_enc->rc.frame_metrics==NULL?1:_enc->rc.buf_delay; + return OC_RC_2PASS_HDR_SZ+frames_needed*OC_RC_2PASS_PACKET_SZ + -_enc->rc.twopass_buffer_fill; + } + consumed=oc_rc_buffer_fill(&_enc->rc, + _buf,_bytes,consumed,OC_RC_2PASS_HDR_SZ); + if(_enc->rc.twopass_buffer_fill>=OC_RC_2PASS_HDR_SZ){ + ogg_int64_t scale_sum[2]; + int exp[2]; + int buf_delay; + /*Read the summary header data.*/ + /*Check the magic value and version number.*/ + if(oc_rc_unbuffer_val(&_enc->rc,4)!=0x5032544F|| + oc_rc_unbuffer_val(&_enc->rc,4)!=OC_RC_2PASS_VERSION){ + _enc->rc.twopass_buffer_bytes=0; + return TH_ENOTFORMAT; + } + _enc->rc.frames_total[0]=(ogg_uint32_t)oc_rc_unbuffer_val(&_enc->rc,4); + _enc->rc.frames_total[1]=(ogg_uint32_t)oc_rc_unbuffer_val(&_enc->rc,4); + _enc->rc.frames_total[2]=(ogg_uint32_t)oc_rc_unbuffer_val(&_enc->rc,4); + exp[0]=(int)oc_rc_unbuffer_val(&_enc->rc,1); + exp[1]=(int)oc_rc_unbuffer_val(&_enc->rc,1); + scale_sum[0]=oc_rc_unbuffer_val(&_enc->rc,8); + scale_sum[1]=oc_rc_unbuffer_val(&_enc->rc,8); + /*Make sure the file claims to have at least one frame. + Otherwise we probably got the placeholder data from an aborted pass 1. + Also make sure the total frame count doesn't overflow an integer.*/ + buf_delay=_enc->rc.frames_total[0]+_enc->rc.frames_total[1] + +_enc->rc.frames_total[2]; + if(_enc->rc.frames_total[0]==0||buf_delay<0|| + (ogg_uint32_t)buf_delay<_enc->rc.frames_total[0]|| + (ogg_uint32_t)buf_delay<_enc->rc.frames_total[1]){ + _enc->rc.frames_total[0]=0; + _enc->rc.twopass_buffer_bytes=0; + return TH_EBADHEADER; + } + /*Got a valid header; set up pass 2.*/ + _enc->rc.frames_left[0]=_enc->rc.frames_total[0]; + _enc->rc.frames_left[1]=_enc->rc.frames_total[1]; + _enc->rc.frames_left[2]=_enc->rc.frames_total[2]; + /*If the user hasn't specified a buffer size, use the whole file.*/ + if(_enc->rc.frame_metrics==NULL){ + _enc->rc.buf_delay=buf_delay; + _enc->rc.nframes[0]=_enc->rc.frames_total[0]; + _enc->rc.nframes[1]=_enc->rc.frames_total[1]; + _enc->rc.nframes[2]=_enc->rc.frames_total[2]; + _enc->rc.scale_sum[0]=scale_sum[0]; + _enc->rc.scale_sum[1]=scale_sum[1]; + _enc->rc.scale_window_end=buf_delay; + oc_enc_rc_reset(_enc); + } + _enc->rc.exp[0]=exp[0]; + _enc->rc.exp[1]=exp[1]; + /*Clear the header data from the buffer to make room for packet data.*/ + _enc->rc.twopass_buffer_fill=0; + _enc->rc.twopass_buffer_bytes=0; + } + } + if(_enc->rc.frames_total[0]!=0){ + ogg_int64_t curframe_num; + int nframes_total; + curframe_num=_enc->state.curframe_num; + if(curframe_num>=0){ + /*We just encoded a frame; make sure things matched.*/ + if(_enc->rc.prev_metrics.dup_count!=_enc->prev_dup_count){ + _enc->rc.twopass_buffer_bytes=0; + return TH_EINVAL; + } + } + curframe_num+=_enc->prev_dup_count+1; + nframes_total=_enc->rc.frames_total[0]+_enc->rc.frames_total[1] + +_enc->rc.frames_total[2]; + if(curframe_num>=nframes_total){ + /*We don't want any more data after the last frame, and we don't want to + allow any more frames to be encoded.*/ + _enc->rc.twopass_buffer_bytes=0; + } + else if(_enc->rc.twopass_buffer_bytes==0){ + if(_enc->rc.frame_metrics==NULL){ + /*We're using a whole-file buffer:*/ + if(!_buf)return OC_RC_2PASS_PACKET_SZ-_enc->rc.twopass_buffer_fill; + consumed=oc_rc_buffer_fill(&_enc->rc, + _buf,_bytes,consumed,OC_RC_2PASS_PACKET_SZ); + if(_enc->rc.twopass_buffer_fill>=OC_RC_2PASS_PACKET_SZ){ + ogg_uint32_t dup_count; + ogg_int32_t log_scale; + int qti; + int arg; + /*Read the metrics for the next frame.*/ + dup_count=oc_rc_unbuffer_val(&_enc->rc,4); + log_scale=oc_rc_unbuffer_val(&_enc->rc,4); + _enc->rc.cur_metrics.log_scale=log_scale; + qti=(dup_count&0x80000000)>>31; + _enc->rc.cur_metrics.dup_count=dup_count&0x7FFFFFFF; + _enc->rc.cur_metrics.frame_type=qti; + _enc->rc.twopass_force_kf=qti==OC_INTRA_FRAME; + /*"Helpfully" set the dup count back to what it was in pass 1.*/ + arg=_enc->rc.cur_metrics.dup_count; + th_encode_ctl(_enc,TH_ENCCTL_SET_DUP_COUNT,&arg,sizeof(arg)); + /*Clear the buffer for the next frame.*/ + _enc->rc.twopass_buffer_fill=0; + } + } + else{ + int frames_needed; + /*We're using a finite buffer:*/ + frames_needed=OC_CLAMPI(0,_enc->rc.buf_delay + -(_enc->rc.scale_window_end-_enc->rc.scale_window0), + _enc->rc.frames_left[0]+_enc->rc.frames_left[1] + -_enc->rc.nframes[0]-_enc->rc.nframes[1]); + while(frames_needed>0){ + if(!_buf){ + return OC_RC_2PASS_PACKET_SZ*frames_needed + -_enc->rc.twopass_buffer_fill; + } + consumed=oc_rc_buffer_fill(&_enc->rc, + _buf,_bytes,consumed,OC_RC_2PASS_PACKET_SZ); + if(_enc->rc.twopass_buffer_fill>=OC_RC_2PASS_PACKET_SZ){ + oc_frame_metrics *m; + int fmi; + ogg_uint32_t dup_count; + ogg_int32_t log_scale; + int qti; + /*Read the metrics for the next frame.*/ + dup_count=oc_rc_unbuffer_val(&_enc->rc,4); + log_scale=oc_rc_unbuffer_val(&_enc->rc,4); + /*Add the to the circular buffer.*/ + fmi=_enc->rc.frame_metrics_head+_enc->rc.nframe_metrics++; + if(fmi>=_enc->rc.cframe_metrics)fmi-=_enc->rc.cframe_metrics; + m=_enc->rc.frame_metrics+fmi; + m->log_scale=log_scale; + qti=(dup_count&0x80000000)>>31; + m->dup_count=dup_count&0x7FFFFFFF; + m->frame_type=qti; + /*And accumulate the statistics over the window.*/ + _enc->rc.nframes[qti]++; + _enc->rc.nframes[2]+=m->dup_count; + _enc->rc.scale_sum[qti]+=oc_bexp_q24(m->log_scale); + _enc->rc.scale_window_end+=m->dup_count+1; + /*Compute an upper bound on the number of remaining packets needed + for the current window.*/ + frames_needed=OC_CLAMPI(0,_enc->rc.buf_delay + -(_enc->rc.scale_window_end-_enc->rc.scale_window0), + _enc->rc.frames_left[0]+_enc->rc.frames_left[1] + -_enc->rc.nframes[0]-_enc->rc.nframes[1]); + /*Clear the buffer for the next frame.*/ + _enc->rc.twopass_buffer_fill=0; + _enc->rc.twopass_buffer_bytes=0; + } + /*Go back for more data.*/ + else break; + } + /*If we've got all the frames we need, fill in the current metrics. + We're ready to go.*/ + if(frames_needed<=0){ + int arg; + *&_enc->rc.cur_metrics= + *(_enc->rc.frame_metrics+_enc->rc.frame_metrics_head); + _enc->rc.twopass_force_kf= + _enc->rc.cur_metrics.frame_type==OC_INTRA_FRAME; + /*"Helpfully" set the dup count back to what it was in pass 1.*/ + arg=_enc->rc.cur_metrics.dup_count; + th_encode_ctl(_enc,TH_ENCCTL_SET_DUP_COUNT,&arg,sizeof(arg)); + /*Mark us ready for the next frame.*/ + _enc->rc.twopass_buffer_bytes=1; + } + } + } + } + return (int)consumed; +} diff --git a/Engine/lib/libtheora/lib/dec/state.c b/Engine/lib/libtheora/lib/state.c similarity index 55% rename from Engine/lib/libtheora/lib/dec/state.c rename to Engine/lib/libtheora/lib/state.c index 387f2d0b1..42ed33a9a 100644 --- a/Engine/lib/libtheora/lib/dec/state.c +++ b/Engine/lib/libtheora/lib/state.c @@ -5,21 +5,20 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: state.c 15469 2008-10-30 12:49:42Z tterribe $ + last mod: $Id: state.c 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ #include #include -#include "../internal.h" -#include "idct.h" -#if defined(USE_ASM) +#include "internal.h" +#if defined(OC_X86_ASM) #if defined(_MSC_VER) # include "x86_vc/x86int.h" #else @@ -31,35 +30,30 @@ # include "png.h" #endif -void oc_restore_fpu(const oc_theora_state *_state){ - _state->opt_vtable.restore_fpu(); -} - -void oc_restore_fpu_c(void){} - /*Returns the fragment index of the top-left block in a macro block. - This can be used to test whether or not the whole macro block is coded. - _sb: The super block. - _quadi: The quadrant number. + This can be used to test whether or not the whole macro block is valid. + _sb_map: The super block map. + _quadi: The quadrant number. Return: The index of the fragment of the upper left block in the macro block, or -1 if the block lies outside the coded frame.*/ -static int oc_sb_quad_top_left_frag(const oc_sb *_sb,int _quadi){ +static ptrdiff_t oc_sb_quad_top_left_frag(oc_sb_map_quad _sb_map[4],int _quadi){ /*It so happens that under the Hilbert curve ordering described below, the upper-left block in each macro block is at index 0, except in macro block 3, where it is at index 2.*/ - return _sb->map[_quadi][_quadi&_quadi<<1]; + return _sb_map[_quadi][_quadi&_quadi<<1]; } /*Fills in the mapping from block positions to fragment numbers for a single color plane. - This function also fills in the "valid" flag of each quadrant in a super - block. - _sbs: The array of super blocks for the color plane. - _frag0: The index of the first fragment in the plane. - _hfrags: The number of horizontal fragments in a coded frame. - _vfrags: The number of vertical fragments in a coded frame.*/ -static void oc_sb_create_plane_mapping(oc_sb _sbs[],int _frag0,int _hfrags, - int _vfrags){ + This function also fills in the "valid" flag of each quadrant in the super + block flags. + _sb_maps: The array of super block maps for the color plane. + _sb_flags: The array of super block flags for the color plane. + _frag0: The index of the first fragment in the plane. + _hfrags: The number of horizontal fragments in a coded frame. + _vfrags: The number of vertical fragments in a coded frame.*/ +static void oc_sb_create_plane_mapping(oc_sb_map _sb_maps[], + oc_sb_flags _sb_flags[],ptrdiff_t _frag0,int _hfrags,int _vfrags){ /*Contains the (macro_block,block) indices for a 4x4 grid of fragments. The pattern is a 4x4 Hilbert space-filling curve. @@ -74,10 +68,10 @@ static void oc_sb_create_plane_mapping(oc_sb _sbs[],int _frag0,int _hfrags, {{1,0},{1,3},{2,0},{2,3}}, {{1,1},{1,2},{2,1},{2,2}} }; - oc_sb *sb; - int yfrag; - int y; - sb=_sbs; + ptrdiff_t yfrag; + unsigned sbi; + int y; + sbi=0; yfrag=_frag0; for(y=0;;y+=4){ int imax; @@ -87,30 +81,31 @@ static void oc_sb_create_plane_mapping(oc_sb _sbs[],int _frag0,int _hfrags, imax=_vfrags-y; if(imax>4)imax=4; else if(imax<=0)break; - for(x=0;;x+=4,sb++){ - int xfrag; - int jmax; - int quadi; - int i; + for(x=0;;x+=4,sbi++){ + ptrdiff_t xfrag; + int jmax; + int quadi; + int i; /*Figure out how many rows of blocks in this super block lie within the image.*/ jmax=_hfrags-x; if(jmax>4)jmax=4; else if(jmax<=0)break; /*By default, set all fragment indices to -1.*/ - memset(sb->map[0],0xFF,sizeof(sb->map)); + memset(_sb_maps[sbi][0],0xFF,sizeof(_sb_maps[sbi])); /*Fill in the fragment map for this super block.*/ xfrag=yfrag+x; for(i=0;imap[SB_MAP[i][j][0]][SB_MAP[i][j][1]]=xfrag+j; + _sb_maps[sbi][SB_MAP[i][j][0]][SB_MAP[i][j][1]]=xfrag+j; } xfrag+=_hfrags; } /*Mark which quadrants of this super block lie within the image.*/ for(quadi=0;quadi<4;quadi++){ - sb->quad_valid|=(oc_sb_quad_top_left_frag(sb,quadi)>=0)<=0)<=_fplane->nvfrags)break; - for(j=0;j<2;j++){ - if(_x+j>=_fplane->nhfrags)break; - _mb->map[0][i<<1|j]=(_y+i)*_fplane->nhfrags+_x+j; - } + int j; + for(i=0;i<2;i++)for(j=0;j<2;j++){ + _mb_map[0][i<<1|j]=(_yfrag0+i)*(ptrdiff_t)_fplane->nhfrags+_xfrag0+j; } } /*Fills in the chroma plane fragment maps for a macro block. - This version is for use with chroma decimated in the X and Y directions. - _mb: The macro block to fill. + This version is for use with chroma decimated in the X and Y directions + (4:2:0). + _mb_map: The macro block map to fill. _fplanes: The descriptions of the fragment planes. - _x: The X location of the upper-left hand fragment in the Y plane. - _y: The Y location of the upper-left hand fragment in the Y plane.*/ -static void oc_mb_fill_cmapping00(oc_mb *_mb, - const oc_fragment_plane _fplanes[3],int _x,int _y){ - int fragi; - _x>>=1; - _y>>=1; - fragi=_y*_fplanes[1].nhfrags+_x; - _mb->map[1][0]=fragi+_fplanes[1].froffset; - _mb->map[2][0]=fragi+_fplanes[2].froffset; + _xfrag0: The X location of the upper-left hand fragment in the luma plane. + _yfrag0: The Y location of the upper-left hand fragment in the luma plane.*/ +static void oc_mb_fill_cmapping00(oc_mb_map_plane _mb_map[3], + const oc_fragment_plane _fplanes[3],int _xfrag0,int _yfrag0){ + ptrdiff_t fragi; + _xfrag0>>=1; + _yfrag0>>=1; + fragi=_yfrag0*(ptrdiff_t)_fplanes[1].nhfrags+_xfrag0; + _mb_map[1][0]=fragi+_fplanes[1].froffset; + _mb_map[2][0]=fragi+_fplanes[2].froffset; } /*Fills in the chroma plane fragment maps for a macro block. This version is for use with chroma decimated in the Y direction. - _mb: The macro block to fill. + _mb_map: The macro block map to fill. _fplanes: The descriptions of the fragment planes. - _x: The X location of the upper-left hand fragment in the Y plane. - _y: The Y location of the upper-left hand fragment in the Y plane.*/ -static void oc_mb_fill_cmapping01(oc_mb *_mb, - const oc_fragment_plane _fplanes[3],int _x,int _y){ - int fragi; - int j; - _y>>=1; - fragi=_y*_fplanes[1].nhfrags+_x; + _xfrag0: The X location of the upper-left hand fragment in the luma plane. + _yfrag0: The Y location of the upper-left hand fragment in the luma plane.*/ +static void oc_mb_fill_cmapping01(oc_mb_map_plane _mb_map[3], + const oc_fragment_plane _fplanes[3],int _xfrag0,int _yfrag0){ + ptrdiff_t fragi; + int j; + _yfrag0>>=1; + fragi=_yfrag0*(ptrdiff_t)_fplanes[1].nhfrags+_xfrag0; for(j=0;j<2;j++){ - if(_x+j>=_fplanes[1].nhfrags)break; - _mb->map[1][j]=fragi+_fplanes[1].froffset; - _mb->map[2][j]=fragi+_fplanes[2].froffset; + _mb_map[1][j]=fragi+_fplanes[1].froffset; + _mb_map[2][j]=fragi+_fplanes[2].froffset; fragi++; } } /*Fills in the chroma plane fragment maps for a macro block. - This version is for use with chroma decimated in the X direction. - _mb: The macro block to fill. + This version is for use with chroma decimated in the X direction (4:2:2). + _mb_map: The macro block map to fill. _fplanes: The descriptions of the fragment planes. - _x: The X location of the upper-left hand fragment in the Y plane. - _y: The Y location of the upper-left hand fragment in the Y plane.*/ -static void oc_mb_fill_cmapping10(oc_mb *_mb, - const oc_fragment_plane _fplanes[3],int _x,int _y){ - int fragi; - int i; - _x>>=1; - fragi=_y*_fplanes[1].nhfrags+_x; + _xfrag0: The X location of the upper-left hand fragment in the luma plane. + _yfrag0: The Y location of the upper-left hand fragment in the luma plane.*/ +static void oc_mb_fill_cmapping10(oc_mb_map_plane _mb_map[3], + const oc_fragment_plane _fplanes[3],int _xfrag0,int _yfrag0){ + ptrdiff_t fragi; + int i; + _xfrag0>>=1; + fragi=_yfrag0*(ptrdiff_t)_fplanes[1].nhfrags+_xfrag0; for(i=0;i<2;i++){ - if(_y+i>=_fplanes[1].nvfrags)break; - _mb->map[1][i<<1]=fragi+_fplanes[1].froffset; - _mb->map[2][i<<1]=fragi+_fplanes[2].froffset; + _mb_map[1][i<<1]=fragi+_fplanes[1].froffset; + _mb_map[2][i<<1]=fragi+_fplanes[2].froffset; fragi+=_fplanes[1].nhfrags; } } /*Fills in the chroma plane fragment maps for a macro block. - This version is for use with no chroma decimation. - This uses the already filled-in Y plane values. - _mb: The macro block to fill. + This version is for use with no chroma decimation (4:4:4). + This uses the already filled-in luma plane values. + _mb_map: The macro block map to fill. _fplanes: The descriptions of the fragment planes.*/ -static void oc_mb_fill_cmapping11(oc_mb *_mb, +static void oc_mb_fill_cmapping11(oc_mb_map_plane _mb_map[3], const oc_fragment_plane _fplanes[3]){ int k; for(k=0;k<4;k++){ - if(_mb->map[0][k]>=0){ - _mb->map[1][k]=_mb->map[0][k]+_fplanes[1].froffset; - _mb->map[2][k]=_mb->map[0][k]+_fplanes[2].froffset; - } + _mb_map[1][k]=_mb_map[0][k]+_fplanes[1].froffset; + _mb_map[2][k]=_mb_map[0][k]+_fplanes[2].froffset; } } /*The function type used to fill in the chroma plane fragment maps for a macro block. - _mb: The macro block to fill. + _mb_map: The macro block map to fill. _fplanes: The descriptions of the fragment planes. - _x: The X location of the upper-left hand fragment in the Y plane. - _y: The Y location of the upper-left hand fragment in the Y plane.*/ -typedef void (*oc_mb_fill_cmapping_func)(oc_mb *_mb, + _xfrag0: The X location of the upper-left hand fragment in the luma plane. + _yfrag0: The Y location of the upper-left hand fragment in the luma plane.*/ +typedef void (*oc_mb_fill_cmapping_func)(oc_mb_map_plane _mb_map[3], const oc_fragment_plane _fplanes[3],int _xfrag0,int _yfrag0); /*A table of functions used to fill in the chroma plane fragment maps for a @@ -228,44 +216,43 @@ static const oc_mb_fill_cmapping_func OC_MB_FILL_CMAPPING_TABLE[4]={ /*Fills in the mapping from macro blocks to their corresponding fragment numbers in each plane. - _mbs: The array of macro blocks. - _fplanes: The descriptions of the fragment planes. - _ctype: The chroma decimation type.*/ -static void oc_mb_create_mapping(oc_mb _mbs[], - const oc_fragment_plane _fplanes[3],int _ctype){ + _mb_maps: The list of macro block maps. + _mb_modes: The list of macro block modes; macro blocks completely outside + the coded region are marked invalid. + _fplanes: The descriptions of the fragment planes. + _pixel_fmt: The chroma decimation type.*/ +static void oc_mb_create_mapping(oc_mb_map _mb_maps[], + signed char _mb_modes[],const oc_fragment_plane _fplanes[3],int _pixel_fmt){ oc_mb_fill_cmapping_func mb_fill_cmapping; - oc_mb *mb0; + unsigned sbi; int y; - mb0=_mbs; - mb_fill_cmapping=OC_MB_FILL_CMAPPING_TABLE[_ctype]; - /*Loop through the Y plane super blocks.*/ - for(y=0;y<_fplanes[0].nvfrags;y+=4){ + mb_fill_cmapping=OC_MB_FILL_CMAPPING_TABLE[_pixel_fmt]; + /*Loop through the luma plane super blocks.*/ + for(sbi=y=0;y<_fplanes[0].nvfrags;y+=4){ int x; - for(x=0;x<_fplanes[0].nhfrags;x+=4,mb0+=4){ + for(x=0;x<_fplanes[0].nhfrags;x+=4,sbi++){ int ymb; /*Loop through the macro blocks in each super block in display order.*/ for(ymb=0;ymb<2;ymb++){ int xmb; for(xmb=0;xmb<2;xmb++){ - oc_mb *mb; - int mbx; - int mby; - mb=mb0+OC_MB_MAP[ymb][xmb]; + unsigned mbi; + int mbx; + int mby; + mbi=sbi<<2|OC_MB_MAP[ymb][xmb]; mbx=x|xmb<<1; mby=y|ymb<<1; - mb->x=mbx<<3; - mb->y=mby<<3; - /*Initialize fragment indexes to -1.*/ - memset(mb->map,0xFF,sizeof(mb->map)); + /*Initialize fragment indices to -1.*/ + memset(_mb_maps[mbi],0xFF,sizeof(_mb_maps[mbi])); /*Make sure this macro block is within the encoded region.*/ if(mbx>=_fplanes[0].nhfrags||mby>=_fplanes[0].nvfrags){ - mb->mode=OC_MODE_INVALID; + _mb_modes[mbi]=OC_MODE_INVALID; continue; } - /*Fill in the fragment indices for the Y plane.*/ - oc_mb_fill_ymapping(mb,_fplanes,mbx,mby); + /*Fill in the fragment indices for the luma plane.*/ + oc_mb_fill_ymapping(_mb_maps[mbi],_fplanes,mbx,mby); /*Fill in the fragment indices for the chroma planes.*/ - (*mb_fill_cmapping)(mb,_fplanes,mbx,mby); + (*mb_fill_cmapping)(_mb_maps[mbi],_fplanes,mbx,mby); } } } @@ -276,18 +263,14 @@ static void oc_mb_create_mapping(oc_mb _mbs[], region of the frame. _state: The Theora state containing the fragments to be marked.*/ static void oc_state_border_init(oc_theora_state *_state){ - typedef struct{ - int x0; - int y0; - int xf; - int yf; - }oc_crop_rect; oc_fragment *frag; oc_fragment *yfrag_end; oc_fragment *xfrag_end; oc_fragment_plane *fplane; - oc_crop_rect *crop; - oc_crop_rect crop_rects[3]; + int crop_x0; + int crop_y0; + int crop_xf; + int crop_yf; int pli; int y; int x; @@ -301,20 +284,19 @@ static void oc_state_border_init(oc_theora_state *_state){ yfrag_end=frag=_state->frags; for(pli=0;pli<3;pli++){ fplane=_state->fplanes+pli; - crop=crop_rects+pli; /*Set up the cropping rectangle for this plane.*/ - crop->x0=_state->info.pic_x; - crop->xf=_state->info.pic_x+_state->info.pic_width; - crop->y0=_state->info.pic_y; - crop->yf=_state->info.pic_y+_state->info.pic_height; + crop_x0=_state->info.pic_x; + crop_xf=_state->info.pic_x+_state->info.pic_width; + crop_y0=_state->info.pic_y; + crop_yf=_state->info.pic_y+_state->info.pic_height; if(pli>0){ if(!(_state->info.pixel_fmt&1)){ - crop->x0=crop->x0>>1; - crop->xf=crop->xf+1>>1; + crop_x0=crop_x0>>1; + crop_xf=crop_xf+1>>1; } if(!(_state->info.pixel_fmt&2)){ - crop->y0=crop->y0>>1; - crop->yf=crop->yf+1>>1; + crop_y0=crop_y0>>1; + crop_yf=crop_yf+1>>1; } } y=0; @@ -327,13 +309,13 @@ static void oc_state_border_init(oc_theora_state *_state){ This guarantees that if we count a fragment as straddling the border below, at least one pixel in the fragment will be inside the displayable region.*/ - if(x+8<=crop->x0||crop->xf<=x||y+8<=crop->y0||crop->yf<=y|| - crop->x0>=crop->xf||crop->y0>=crop->yf){ + if(x+8<=crop_x0||crop_xf<=x||y+8<=crop_y0||crop_yf<=y|| + crop_x0>=crop_xf||crop_y0>=crop_yf){ frag->invalid=1; } /*Otherwise, check to see if it straddles the border.*/ - else if(xx0&&crop->x0xf&&crop->xfy0&&crop->y0yf&&crop->yf=crop->x0&&x+jxf&&y+i>=crop->y0&&y+iyf){ + if(x+j>=crop_x0&&x+j=crop_y0&&y+i=_state->nborders){ _state->nborders++; @@ -357,34 +339,35 @@ static void oc_state_border_init(oc_theora_state *_state){ _state->borders[i].npixels=npixels; } else if(_state->borders[i].mask!=mask)continue; - frag->border=_state->borders+i; + frag->borderi=i; break; } } + else frag->borderi=-1; } } } } -static void oc_state_frarray_init(oc_theora_state *_state){ - int yhfrags; - int yvfrags; - int chfrags; - int cvfrags; - int yfrags; - int cfrags; - int nfrags; - int yhsbs; - int yvsbs; - int chsbs; - int cvsbs; - int ysbs; - int csbs; - int nsbs; - int nmbs; - int hdec; - int vdec; - int pli; +static int oc_state_frarray_init(oc_theora_state *_state){ + int yhfrags; + int yvfrags; + int chfrags; + int cvfrags; + ptrdiff_t yfrags; + ptrdiff_t cfrags; + ptrdiff_t nfrags; + unsigned yhsbs; + unsigned yvsbs; + unsigned chsbs; + unsigned cvsbs; + unsigned ysbs; + unsigned csbs; + unsigned nsbs; + size_t nmbs; + int hdec; + int vdec; + int pli; /*Figure out the number of fragments in each plane.*/ /*These parameters have already been validated to be multiples of 16.*/ yhfrags=_state->info.frame_width>>3; @@ -393,8 +376,8 @@ static void oc_state_frarray_init(oc_theora_state *_state){ vdec=!(_state->info.pixel_fmt&2); chfrags=yhfrags+hdec>>hdec; cvfrags=yvfrags+vdec>>vdec; - yfrags=yhfrags*yvfrags; - cfrags=chfrags*cvfrags; + yfrags=yhfrags*(ptrdiff_t)yvfrags; + cfrags=chfrags*(ptrdiff_t)cvfrags; nfrags=yfrags+2*cfrags; /*Figure out the number of super blocks in each plane.*/ yhsbs=yhfrags+3>>2; @@ -404,7 +387,20 @@ static void oc_state_frarray_init(oc_theora_state *_state){ ysbs=yhsbs*yvsbs; csbs=chsbs*cvsbs; nsbs=ysbs+2*csbs; - nmbs=ysbs<<2; + nmbs=(size_t)ysbs<<2; + /*Check for overflow. + We support the ridiculous upper limits of the specification (1048560 by + 1048560, or 3 TB frames) if the target architecture has 64-bit pointers, + but for those with 32-bit pointers (or smaller!) we have to check. + If the caller wants to prevent denial-of-service by imposing a more + reasonable upper limit on the size of attempted allocations, they must do + so themselves; we have no platform independent way to determine how much + system memory there is nor an application-independent way to decide what a + "reasonable" allocation is.*/ + if(yfrags/yhfrags!=yvfrags||2*cfrags>2!=ysbs){ + return TH_EIMPL; + } /*Initialize the fragment array.*/ _state->fplanes[0].nhfrags=yhfrags; _state->fplanes[0].nvfrags=yvfrags; @@ -425,34 +421,45 @@ static void oc_state_frarray_init(oc_theora_state *_state){ _state->fplanes[2].sboffset=ysbs+csbs; _state->fplanes[1].nsbs=_state->fplanes[2].nsbs=csbs; _state->nfrags=nfrags; - _state->frags=_ogg_calloc(nfrags,sizeof(oc_fragment)); + _state->frags=_ogg_calloc(nfrags,sizeof(*_state->frags)); + _state->frag_mvs=_ogg_malloc(nfrags*sizeof(*_state->frag_mvs)); _state->nsbs=nsbs; - _state->sbs=_ogg_calloc(nsbs,sizeof(oc_sb)); + _state->sb_maps=_ogg_malloc(nsbs*sizeof(*_state->sb_maps)); + _state->sb_flags=_ogg_calloc(nsbs,sizeof(*_state->sb_flags)); _state->nhmbs=yhsbs<<1; _state->nvmbs=yvsbs<<1; _state->nmbs=nmbs; - _state->mbs=_ogg_calloc(nmbs,sizeof(oc_mb)); - _state->coded_fragis=_ogg_malloc(nfrags*sizeof(_state->coded_fragis[0])); - _state->uncoded_fragis=_state->coded_fragis+nfrags; - _state->coded_mbis=_ogg_malloc(nmbs*sizeof(_state->coded_mbis[0])); + _state->mb_maps=_ogg_calloc(nmbs,sizeof(*_state->mb_maps)); + _state->mb_modes=_ogg_calloc(nmbs,sizeof(*_state->mb_modes)); + _state->coded_fragis=_ogg_malloc(nfrags*sizeof(*_state->coded_fragis)); + if(_state->frags==NULL||_state->frag_mvs==NULL||_state->sb_maps==NULL|| + _state->sb_flags==NULL||_state->mb_maps==NULL||_state->mb_modes==NULL|| + _state->coded_fragis==NULL){ + return TH_EFAULT; + } /*Create the mapping from super blocks to fragments.*/ for(pli=0;pli<3;pli++){ oc_fragment_plane *fplane; fplane=_state->fplanes+pli; - oc_sb_create_plane_mapping(_state->sbs+fplane->sboffset, - fplane->froffset,fplane->nhfrags,fplane->nvfrags); + oc_sb_create_plane_mapping(_state->sb_maps+fplane->sboffset, + _state->sb_flags+fplane->sboffset,fplane->froffset, + fplane->nhfrags,fplane->nvfrags); } /*Create the mapping from macro blocks to fragments.*/ - oc_mb_create_mapping(_state->mbs,_state->fplanes,_state->info.pixel_fmt); - /*Initialize the invalid and border fields of each fragment.*/ + oc_mb_create_mapping(_state->mb_maps,_state->mb_modes, + _state->fplanes,_state->info.pixel_fmt); + /*Initialize the invalid and borderi fields of each fragment.*/ oc_state_border_init(_state); + return 0; } static void oc_state_frarray_clear(oc_theora_state *_state){ - _ogg_free(_state->coded_mbis); _ogg_free(_state->coded_fragis); - _ogg_free(_state->mbs); - _ogg_free(_state->sbs); + _ogg_free(_state->mb_modes); + _ogg_free(_state->mb_maps); + _ogg_free(_state->sb_flags); + _ogg_free(_state->sb_maps); + _ogg_free(_state->frag_mvs); _ogg_free(_state->frags); } @@ -462,84 +469,144 @@ static void oc_state_frarray_clear(oc_theora_state *_state){ unrestricted motion vectors without special casing the boundary. If chroma is decimated in either direction, the padding is reduced by a factor of 2 on the appropriate sides. - _enc: The encoding context to store the buffers in.*/ -static void oc_state_ref_bufs_init(oc_theora_state *_state){ - th_info *info; + _nrefs: The number of reference buffers to init; must be 3 or 4.*/ +static int oc_state_ref_bufs_init(oc_theora_state *_state,int _nrefs){ + th_info *info; unsigned char *ref_frame_data; + size_t ref_frame_data_sz; + size_t ref_frame_sz; size_t yplane_sz; size_t cplane_sz; int yhstride; - int yvstride; + int yheight; int chstride; - int cvstride; - int yoffset; - int coffset; + int cheight; + ptrdiff_t yoffset; + ptrdiff_t coffset; + ptrdiff_t *frag_buf_offs; + ptrdiff_t fragi; + int hdec; + int vdec; int rfi; + int pli; + if(_nrefs<3||_nrefs>4)return TH_EINVAL; info=&_state->info; /*Compute the image buffer parameters for each plane.*/ + hdec=!(info->pixel_fmt&1); + vdec=!(info->pixel_fmt&2); yhstride=info->frame_width+2*OC_UMV_PADDING; - yvstride=info->frame_height+2*OC_UMV_PADDING; - chstride=yhstride>>!(info->pixel_fmt&1); - cvstride=yvstride>>!(info->pixel_fmt&2); - yplane_sz=(size_t)yhstride*yvstride; - cplane_sz=(size_t)chstride*cvstride; - yoffset=OC_UMV_PADDING+OC_UMV_PADDING*yhstride; - coffset=(OC_UMV_PADDING>>!(info->pixel_fmt&1))+ - (OC_UMV_PADDING>>!(info->pixel_fmt&2))*chstride; - _state->ref_frame_data=ref_frame_data=_ogg_malloc(3*(yplane_sz+2*cplane_sz)); + yheight=info->frame_height+2*OC_UMV_PADDING; + chstride=yhstride>>hdec; + cheight=yheight>>vdec; + yplane_sz=yhstride*(size_t)yheight; + cplane_sz=chstride*(size_t)cheight; + yoffset=OC_UMV_PADDING+OC_UMV_PADDING*(ptrdiff_t)yhstride; + coffset=(OC_UMV_PADDING>>hdec)+(OC_UMV_PADDING>>vdec)*(ptrdiff_t)chstride; + ref_frame_sz=yplane_sz+2*cplane_sz; + ref_frame_data_sz=_nrefs*ref_frame_sz; + /*Check for overflow. + The same caveats apply as for oc_state_frarray_init().*/ + if(yplane_sz/yhstride!=yheight||2*cplane_szfrag_buf_offs= + _ogg_malloc(_state->nfrags*sizeof(*frag_buf_offs)); + if(ref_frame_data==NULL||frag_buf_offs==NULL){ + _ogg_free(frag_buf_offs); + _ogg_free(ref_frame_data); + return TH_EFAULT; + } /*Set up the width, height and stride for the image buffers.*/ _state->ref_frame_bufs[0][0].width=info->frame_width; _state->ref_frame_bufs[0][0].height=info->frame_height; _state->ref_frame_bufs[0][0].stride=yhstride; _state->ref_frame_bufs[0][1].width=_state->ref_frame_bufs[0][2].width= - info->frame_width>>!(info->pixel_fmt&1); + info->frame_width>>hdec; _state->ref_frame_bufs[0][1].height=_state->ref_frame_bufs[0][2].height= - info->frame_height>>!(info->pixel_fmt&2); + info->frame_height>>vdec; _state->ref_frame_bufs[0][1].stride=_state->ref_frame_bufs[0][2].stride= chstride; - memcpy(_state->ref_frame_bufs[1],_state->ref_frame_bufs[0], - sizeof(_state->ref_frame_bufs[0])); - memcpy(_state->ref_frame_bufs[2],_state->ref_frame_bufs[0], - sizeof(_state->ref_frame_bufs[0])); + for(rfi=1;rfi<_nrefs;rfi++){ + memcpy(_state->ref_frame_bufs[rfi],_state->ref_frame_bufs[0], + sizeof(_state->ref_frame_bufs[0])); + } /*Set up the data pointers for the image buffers.*/ - for(rfi=0;rfi<3;rfi++){ + for(rfi=0;rfi<_nrefs;rfi++){ + _state->ref_frame_data[rfi]=ref_frame_data; _state->ref_frame_bufs[rfi][0].data=ref_frame_data+yoffset; ref_frame_data+=yplane_sz; _state->ref_frame_bufs[rfi][1].data=ref_frame_data+coffset; ref_frame_data+=cplane_sz; _state->ref_frame_bufs[rfi][2].data=ref_frame_data+coffset; ref_frame_data+=cplane_sz; - /*Flip the buffer upside down.*/ + /*Flip the buffer upside down. + This allows us to decode Theora's bottom-up frames in their natural + order, yet return a top-down buffer with a positive stride to the user.*/ oc_ycbcr_buffer_flip(_state->ref_frame_bufs[rfi], _state->ref_frame_bufs[rfi]); - /*Initialize the fragment pointers into this buffer.*/ - oc_state_fill_buffer_ptrs(_state,rfi,_state->ref_frame_bufs[rfi]); } - /*Initialize the reference frame indexes.*/ + _state->ref_ystride[0]=-yhstride; + _state->ref_ystride[1]=_state->ref_ystride[2]=-chstride; + /*Initialize the fragment buffer offsets.*/ + ref_frame_data=_state->ref_frame_data[0]; + fragi=0; + for(pli=0;pli<3;pli++){ + th_img_plane *iplane; + oc_fragment_plane *fplane; + unsigned char *vpix; + ptrdiff_t stride; + ptrdiff_t vfragi_end; + int nhfrags; + iplane=_state->ref_frame_bufs[0]+pli; + fplane=_state->fplanes+pli; + vpix=iplane->data; + vfragi_end=fplane->froffset+fplane->nfrags; + nhfrags=fplane->nhfrags; + stride=iplane->stride; + while(fragiref_frame_idx[OC_FRAME_GOLD]= _state->ref_frame_idx[OC_FRAME_PREV]= _state->ref_frame_idx[OC_FRAME_SELF]=-1; + _state->ref_frame_idx[OC_FRAME_IO]=_nrefs>3?3:-1; + return 0; } static void oc_state_ref_bufs_clear(oc_theora_state *_state){ - _ogg_free(_state->ref_frame_data); + _ogg_free(_state->frag_buf_offs); + _ogg_free(_state->ref_frame_data[0]); } void oc_state_vtable_init_c(oc_theora_state *_state){ + _state->opt_vtable.frag_copy=oc_frag_copy_c; _state->opt_vtable.frag_recon_intra=oc_frag_recon_intra_c; _state->opt_vtable.frag_recon_inter=oc_frag_recon_inter_c; _state->opt_vtable.frag_recon_inter2=oc_frag_recon_inter2_c; - _state->opt_vtable.state_frag_copy=oc_state_frag_copy_c; + _state->opt_vtable.idct8x8=oc_idct8x8_c; _state->opt_vtable.state_frag_recon=oc_state_frag_recon_c; + _state->opt_vtable.state_frag_copy_list=oc_state_frag_copy_list_c; _state->opt_vtable.state_loop_filter_frag_rows= oc_state_loop_filter_frag_rows_c; _state->opt_vtable.restore_fpu=oc_restore_fpu_c; + _state->opt_data.dct_fzig_zag=OC_FZIG_ZAG; } /*Initialize the accelerated function pointers.*/ void oc_state_vtable_init(oc_theora_state *_state){ -#if defined(USE_ASM) +#if defined(OC_X86_ASM) oc_state_vtable_init_x86(_state); #else oc_state_vtable_init_c(_state); @@ -547,8 +614,8 @@ void oc_state_vtable_init(oc_theora_state *_state){ } -int oc_state_init(oc_theora_state *_state,const th_info *_info){ - int old_granpos; +int oc_state_init(oc_theora_state *_state,const th_info *_info,int _nrefs){ + int ret; /*First validate the parameters.*/ if(_info==NULL)return TH_EFAULT; /*The width and height of the encoded frame must be multiples of 16. @@ -561,11 +628,16 @@ int oc_state_init(oc_theora_state *_state,const th_info *_info){ The displayable frame must fit inside the encoded frame. The color space must be one known by the encoder.*/ if((_info->frame_width&0xF)||(_info->frame_height&0xF)|| - _info->frame_width>=0x100000||_info->frame_height>=0x100000|| + _info->frame_width<=0||_info->frame_width>=0x100000|| + _info->frame_height<=0||_info->frame_height>=0x100000|| _info->pic_x+_info->pic_width>_info->frame_width|| _info->pic_y+_info->pic_height>_info->frame_height|| - _info->pic_x>255|| - _info->frame_height-_info->pic_height-_info->pic_y>255|| + _info->pic_x>255||_info->frame_height-_info->pic_height-_info->pic_y>255|| + /*Note: the following <0 comparisons may generate spurious warnings on + platforms where enums are unsigned. + We could cast them to unsigned and just use the following >= comparison, + but there are a number of compilers which will mis-optimize this. + It's better to live with the spurious warnings.*/ _info->colorspace<0||_info->colorspace>=TH_CS_NSPACES|| _info->pixel_fmt<0||_info->pixel_fmt>=TH_PF_NFORMATS){ return TH_EINVAL; @@ -577,22 +649,24 @@ int oc_state_init(oc_theora_state *_state,const th_info *_info){ _state->info.pic_y=_info->frame_height-_info->pic_height-_info->pic_y; _state->frame_type=OC_UNKWN_FRAME; oc_state_vtable_init(_state); - oc_state_frarray_init(_state); - oc_state_ref_bufs_init(_state); + ret=oc_state_frarray_init(_state); + if(ret>=0)ret=oc_state_ref_bufs_init(_state,_nrefs); + if(ret<0){ + oc_state_frarray_clear(_state); + return ret; + } /*If the keyframe_granule_shift is out of range, use the maximum allowable value.*/ if(_info->keyframe_granule_shift<0||_info->keyframe_granule_shift>31){ _state->info.keyframe_granule_shift=31; } - _state->keyframe_num=1; - _state->curframe_num=0; + _state->keyframe_num=0; + _state->curframe_num=-1; /*3.2.0 streams mark the frame index instead of the frame count. This was changed with stream version 3.2.1 to conform to other Ogg codecs. - We subtract an extra one from the frame number for old streams.*/ - old_granpos=!TH_VERSION_CHECK(_info,3,2,1); - _state->curframe_num-=old_granpos; - _state->keyframe_num-=old_granpos; + We add an extra bias when computing granule positions for new streams.*/ + _state->granpos_bias=TH_VERSION_CHECK(_info,3,2,1); return 0; } @@ -612,22 +686,24 @@ void oc_state_clear(oc_theora_state *_state){ _yend: The Y coordinate of the row to stop padding at.*/ void oc_state_borders_fill_rows(oc_theora_state *_state,int _refi,int _pli, int _y0,int _yend){ - th_img_plane *iplane; - unsigned char *apix; - unsigned char *bpix; - unsigned char *epix; - int hpadding; + th_img_plane *iplane; + unsigned char *apix; + unsigned char *bpix; + unsigned char *epix; + int stride; + int hpadding; hpadding=OC_UMV_PADDING>>(_pli!=0&&!(_state->info.pixel_fmt&1)); iplane=_state->ref_frame_bufs[_refi]+_pli; - apix=iplane->data+_y0*iplane->stride; + stride=iplane->stride; + apix=iplane->data+_y0*(ptrdiff_t)stride; bpix=apix+iplane->width-1; - epix=iplane->data+_yend*iplane->stride; - /*Note the use of != instead of <, which allows ystride to be negative.*/ + epix=iplane->data+_yend*(ptrdiff_t)stride; + /*Note the use of != instead of <, which allows the stride to be negative.*/ while(apix!=epix){ memset(apix-hpadding,apix[0],hpadding); memset(bpix+1,bpix[0],hpadding); - apix+=iplane->stride; - bpix+=iplane->stride; + apix+=stride; + bpix+=stride; } } @@ -638,25 +714,27 @@ void oc_state_borders_fill_rows(oc_theora_state *_state,int _refi,int _pli, _refi: The index of the reference buffer to pad. _pli: The color plane.*/ void oc_state_borders_fill_caps(oc_theora_state *_state,int _refi,int _pli){ - th_img_plane *iplane; - unsigned char *apix; - unsigned char *bpix; - unsigned char *epix; - int hpadding; - int vpadding; - int fullw; + th_img_plane *iplane; + unsigned char *apix; + unsigned char *bpix; + unsigned char *epix; + int stride; + int hpadding; + int vpadding; + int fullw; hpadding=OC_UMV_PADDING>>(_pli!=0&&!(_state->info.pixel_fmt&1)); vpadding=OC_UMV_PADDING>>(_pli!=0&&!(_state->info.pixel_fmt&2)); iplane=_state->ref_frame_bufs[_refi]+_pli; + stride=iplane->stride; fullw=iplane->width+(hpadding<<1); apix=iplane->data-hpadding; - bpix=iplane->data+(iplane->height-1)*iplane->stride-hpadding; - epix=apix-iplane->stride*vpadding; + bpix=iplane->data+(iplane->height-1)*(ptrdiff_t)stride-hpadding; + epix=apix-stride*(ptrdiff_t)vpadding; while(apix!=epix){ - memcpy(apix-iplane->stride,apix,fullw); - memcpy(bpix+iplane->stride,bpix,fullw); - apix-=iplane->stride; - bpix+=iplane->stride; + memcpy(apix-stride,apix,fullw); + memcpy(bpix+stride,bpix,fullw); + apix-=stride; + bpix+=stride; } } @@ -673,73 +751,18 @@ void oc_state_borders_fill(oc_theora_state *_state,int _refi){ } } -/*Sets the buffer pointer in each fragment to point to the portion of the - image buffer which it corresponds to. - _state: The Theora state to fill. - _buf_idx: The index of the buffer pointer to fill. - The first three correspond to our reconstructed frame buffers, - while the last corresponds to the input image. - _img: The image buffer to fill the fragments with.*/ -void oc_state_fill_buffer_ptrs(oc_theora_state *_state,int _buf_idx, - th_ycbcr_buffer _img){ - int pli; - /*Special handling for the input image to give us the opportunity to skip - some updates. - The other buffers do not change throughout the encoding process.*/ - if(_buf_idx==OC_FRAME_IO){ - if(memcmp(_state->input,_img,sizeof(th_ycbcr_buffer))==0)return; - memcpy(_state->input,_img,sizeof(th_ycbcr_buffer)); - } - for(pli=0;pli<3;pli++){ - th_img_plane *iplane; - oc_fragment_plane *fplane; - oc_fragment *frag; - oc_fragment *vfrag_end; - unsigned char *vpix; - iplane=&_img[pli]; - fplane=&_state->fplanes[pli]; - vpix=iplane->data; - frag=_state->frags+fplane->froffset; - vfrag_end=frag+fplane->nfrags; - while(fragnhfrags;fragbuffer[_buf_idx]=hpix; - hpix+=8; - } - vpix+=iplane->stride<<3; - } - } -} - -/*Returns the macro block index of the macro block in the given position. - _state: The Theora state the macro block is contained in. - _mbx: The X coordinate of the macro block (in macro blocks, not pixels). - _mby: The Y coordinate of the macro block (in macro blocks, not pixels). - Return: The index of the macro block in the given position.*/ -int oc_state_mbi_for_pos(oc_theora_state *_state,int _mbx,int _mby){ - return ((_mbx&~1)<<1)+(_mby&~1)*_state->nhmbs+OC_MB_MAP[_mby&1][_mbx&1]; -} - /*Determines the offsets in an image buffer to use for motion compensation. _state: The Theora state the offsets are to be computed with. _offsets: Returns the offset for the buffer(s). _offsets[0] is always set. _offsets[1] is set if the motion vector has non-zero fractional components. + _pli: The color plane index. _dx: The X component of the motion vector. _dy: The Y component of the motion vector. - _ystride: The Y stride in the buffer the motion vector points into. - _pli: The color plane index. Return: The number of offsets returned: 1 or 2.*/ -int oc_state_get_mv_offsets(oc_theora_state *_state,int _offsets[2], - int _dx,int _dy,int _ystride,int _pli){ - int xprec; - int yprec; - int xfrac; - int yfrac; +int oc_state_get_mv_offsets(const oc_theora_state *_state,int _offsets[2], + int _pli,int _dx,int _dy){ /*Here is a brief description of how Theora handles motion vectors: Motion vector components are specified to half-pixel accuracy in undecimated directions of each plane, and quarter-pixel accuracy in @@ -754,131 +777,142 @@ int oc_state_get_mv_offsets(oc_theora_state *_state,int _offsets[2], non-zero fractional parts. The second offset is computed by dividing (not shifting) by the appropriate amount, always truncating _away_ from zero.*/ +#if 0 + /*This version of the code doesn't use any tables, but is slower.*/ + int ystride; + int xprec; + int yprec; + int xfrac; + int yfrac; + int offs; + ystride=_state->ref_ystride[_pli]; /*These two variables decide whether we are in half- or quarter-pixel precision in each component.*/ - xprec=1+(!(_state->info.pixel_fmt&1)&&_pli); - yprec=1+(!(_state->info.pixel_fmt&2)&&_pli); - /*These two variables are either 0 if all the fractional bits are 0 or 1 if - any of them are non-zero.*/ - xfrac=!!(_dx&(1<>xprec)+(_dy>>yprec)*_ystride; + xprec=1+(_pli!=0&&!(_state->info.pixel_fmt&1)); + yprec=1+(_pli!=0&&!(_state->info.pixel_fmt&2)); + /*These two variables are either 0 if all the fractional bits are zero or -1 + if any of them are non-zero.*/ + xfrac=OC_SIGNMASK(-(_dx&(xprec|1))); + yfrac=OC_SIGNMASK(-(_dy&(yprec|1))); + offs=(_dx>>xprec)+(_dy>>yprec)*ystride; if(xfrac||yfrac){ - /*This branchless code is equivalent to: - if(_dx<0)_offests[0]=-(-_dx>>xprec); - else _offsets[0]=(_dx>>xprec); - if(_dy<0)_offsets[0]-=(-_dy>>yprec)*_ystride; - else _offsets[0]+=(_dy>>yprec)*_ystride; - _offsets[1]=_offsets[0]; - if(xfrac){ - if(_dx<0)_offsets[1]++; - else _offsets[1]--; - } - if(yfrac){ - if(_dy<0)_offsets[1]+=_ystride; - else _offsets[1]-=_ystride; - }*/ - _offsets[1]=_offsets[0]; - _offsets[_dx>=0]+=xfrac; - _offsets[_dy>=0]+=_ystride&-yfrac; + int xmask; + int ymask; + xmask=OC_SIGNMASK(_dx); + ymask=OC_SIGNMASK(_dy); + yfrac&=ystride; + _offsets[0]=offs-(xfrac&xmask)+(yfrac&ymask); + _offsets[1]=offs-(xfrac&~xmask)+(yfrac&~ymask); return 2; } - else return 1; + else{ + _offsets[0]=offs; + return 1; + } +#else + /*Using tables simplifies the code, and there's enough arithmetic to hide the + latencies of the memory references.*/ + static const signed char OC_MVMAP[2][64]={ + { + -15,-15,-14,-14,-13,-13,-12,-12,-11,-11,-10,-10, -9, -9, -8, + -8, -7, -7, -6, -6, -5, -5, -4, -4, -3, -3, -2, -2, -1, -1, 0, + 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, + 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13, 14, 14, 15, 15 + }, + { + -7, -7, -7, -7, -6, -6, -6, -6, -5, -5, -5, -5, -4, -4, -4, + -4, -3, -3, -3, -3, -2, -2, -2, -2, -1, -1, -1, -1, 0, 0, 0, + 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, + 4, 4, 4, 4, 5, 5, 5, 5, 6, 6, 6, 6, 7, 7, 7, 7 + } + }; + static const signed char OC_MVMAP2[2][64]={ + { + -1, 0,-1, 0,-1, 0,-1, 0,-1, 0,-1, 0,-1, 0,-1, + 0,-1, 0,-1, 0,-1, 0,-1, 0,-1, 0,-1, 0,-1, 0,-1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, + 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1, 0, 1 + }, + { + -1,-1,-1, 0,-1,-1,-1, 0,-1,-1,-1, 0,-1,-1,-1, + 0,-1,-1,-1, 0,-1,-1,-1, 0,-1,-1,-1, 0,-1,-1,-1, + 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, + 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1, 0, 1, 1, 1 + } + }; + int ystride; + int qpx; + int qpy; + int mx; + int my; + int mx2; + int my2; + int offs; + ystride=_state->ref_ystride[_pli]; + qpy=_pli!=0&&!(_state->info.pixel_fmt&2); + my=OC_MVMAP[qpy][_dy+31]; + my2=OC_MVMAP2[qpy][_dy+31]; + qpx=_pli!=0&&!(_state->info.pixel_fmt&1); + mx=OC_MVMAP[qpx][_dx+31]; + mx2=OC_MVMAP2[qpx][_dx+31]; + offs=my*ystride+mx; + if(mx2||my2){ + _offsets[1]=offs+my2*ystride+mx2; + _offsets[0]=offs; + return 2; + } + _offsets[0]=offs; + return 1; +#endif } -void oc_state_frag_recon(oc_theora_state *_state,oc_fragment *_frag, - int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, - ogg_uint16_t _dc_iquant,const ogg_uint16_t _ac_iquant[64]){ - _state->opt_vtable.state_frag_recon(_state,_frag,_pli,_dct_coeffs, - _last_zzi,_ncoefs,_dc_iquant,_ac_iquant); +void oc_state_frag_recon(const oc_theora_state *_state,ptrdiff_t _fragi, + int _pli,ogg_int16_t _dct_coeffs[64],int _last_zzi,ogg_uint16_t _dc_quant){ + _state->opt_vtable.state_frag_recon(_state,_fragi,_pli,_dct_coeffs, + _last_zzi,_dc_quant); } -void oc_state_frag_recon_c(oc_theora_state *_state,oc_fragment *_frag, - int _pli,ogg_int16_t _dct_coeffs[128],int _last_zzi,int _ncoefs, - ogg_uint16_t _dc_iquant, const ogg_uint16_t _ac_iquant[64]){ - ogg_int16_t dct_buf[64]; - ogg_int16_t res_buf[64]; - int dst_framei; - int dst_ystride; - int zzi; - int ci; - /*_last_zzi is subtly different from an actual count of the number of - coefficients we decoded for this block. - It contains the value of zzi BEFORE the final token in the block was - decoded. - In most cases this is an EOB token (the continuation of an EOB run from a - previous block counts), and so this is the same as the coefficient count. - However, in the case that the last token was NOT an EOB token, but filled - the block up with exactly 64 coefficients, _last_zzi will be less than 64. - Provided the last token was not a pure zero run, the minimum value it can - be is 46, and so that doesn't affect any of the cases in this routine. - However, if the last token WAS a pure zero run of length 63, then _last_zzi - will be 1 while the number of coefficients decoded is 64. - Thus, we will trigger the following special case, where the real - coefficient count would not. - Note also that a zero run of length 64 will give _last_zzi a value of 0, - but we still process the DC coefficient, which might have a non-zero value - due to DC prediction. - Although convoluted, this is arguably the correct behavior: it allows us to - dequantize fewer coefficients and use a smaller transform when the block - ends with a long zero run instead of a normal EOB token. - It could be smarter... multiple separate zero runs at the end of a block - will fool it, but an encoder that generates these really deserves what it - gets. - Needless to say we inherited this approach from VP3.*/ +void oc_state_frag_recon_c(const oc_theora_state *_state,ptrdiff_t _fragi, + int _pli,ogg_int16_t _dct_coeffs[64],int _last_zzi,ogg_uint16_t _dc_quant){ + unsigned char *dst; + ptrdiff_t frag_buf_off; + int ystride; + int mb_mode; + /*Apply the inverse transform.*/ /*Special case only having a DC component.*/ if(_last_zzi<2){ ogg_int16_t p; - /*Why is the iquant product rounded in this case and no others? - Who knows.*/ - p=(ogg_int16_t)((ogg_int32_t)_frag->dc*_dc_iquant+15>>5); + int ci; + /*We round this dequant product (and not any of the others) because there's + no iDCT rounding.*/ + p=(ogg_int16_t)(_dct_coeffs[0]*(ogg_int32_t)_dc_quant+15>>5); /*LOOP VECTORIZES.*/ - for(ci=0;ci<64;ci++)res_buf[ci]=p; + for(ci=0;ci<64;ci++)_dct_coeffs[ci]=p; } else{ - /*First, dequantize the coefficients.*/ - dct_buf[0]=(ogg_int16_t)((ogg_int32_t)_frag->dc*_dc_iquant); - for(zzi=1;zzi<_ncoefs;zzi++){ - int ci; - ci=OC_FZIG_ZAG[zzi]; - dct_buf[ci]=(ogg_int16_t)((ogg_int32_t)_dct_coeffs[zzi]*_ac_iquant[ci]); - } - /*Then, fill in the remainder of the coefficients with 0's, and perform - the iDCT.*/ - if(_last_zzi<10){ - for(;zzi<10;zzi++)dct_buf[OC_FZIG_ZAG[zzi]]=0; - oc_idct8x8_10_c(res_buf,dct_buf); - } - else{ - for(;zzi<64;zzi++)dct_buf[OC_FZIG_ZAG[zzi]]=0; - oc_idct8x8_c(res_buf,dct_buf); - } + /*First, dequantize the DC coefficient.*/ + _dct_coeffs[0]=(ogg_int16_t)(_dct_coeffs[0]*(int)_dc_quant); + oc_idct8x8(_state,_dct_coeffs,_last_zzi); } /*Fill in the target buffer.*/ - dst_framei=_state->ref_frame_idx[OC_FRAME_SELF]; - dst_ystride=_state->ref_frame_bufs[dst_framei][_pli].stride; - /*For now ystride values in all ref frames assumed to be equal.*/ - if(_frag->mbmode==OC_MODE_INTRA){ - oc_frag_recon_intra(_state,_frag->buffer[dst_framei],dst_ystride,res_buf); - } + frag_buf_off=_state->frag_buf_offs[_fragi]; + mb_mode=_state->frags[_fragi].mb_mode; + ystride=_state->ref_ystride[_pli]; + dst=_state->ref_frame_data[_state->ref_frame_idx[OC_FRAME_SELF]]+frag_buf_off; + if(mb_mode==OC_MODE_INTRA)oc_frag_recon_intra(_state,dst,ystride,_dct_coeffs); else{ - int ref_framei; - int ref_ystride; - int mvoffsets[2]; - ref_framei=_state->ref_frame_idx[OC_FRAME_FOR_MODE[_frag->mbmode]]; - ref_ystride=_state->ref_frame_bufs[ref_framei][_pli].stride; - if(oc_state_get_mv_offsets(_state,mvoffsets,_frag->mv[0],_frag->mv[1], - ref_ystride,_pli)>1){ - oc_frag_recon_inter2(_state,_frag->buffer[dst_framei],dst_ystride, - _frag->buffer[ref_framei]+mvoffsets[0],ref_ystride, - _frag->buffer[ref_framei]+mvoffsets[1],ref_ystride,res_buf); - } - else{ - oc_frag_recon_inter(_state,_frag->buffer[dst_framei],dst_ystride, - _frag->buffer[ref_framei]+mvoffsets[0],ref_ystride,res_buf); + const unsigned char *ref; + int mvoffsets[2]; + ref= + _state->ref_frame_data[_state->ref_frame_idx[OC_FRAME_FOR_MODE(mb_mode)]] + +frag_buf_off; + if(oc_state_get_mv_offsets(_state,mvoffsets,_pli, + _state->frag_mvs[_fragi][0],_state->frag_mvs[_fragi][1])>1){ + oc_frag_recon_inter2(_state, + dst,ref+mvoffsets[0],ref+mvoffsets[1],ystride,_dct_coeffs); } + else oc_frag_recon_inter(_state,dst,ref+mvoffsets[0],ystride,_dct_coeffs); } - oc_restore_fpu(_state); } /*Copies the fragments specified by the lists of fragment indices from one @@ -888,38 +922,30 @@ void oc_state_frag_recon_c(oc_theora_state *_state,oc_fragment *_frag, _dst_frame: The reference frame to copy to. _src_frame: The reference frame to copy from. _pli: The color plane the fragments lie in.*/ -void oc_state_frag_copy(const oc_theora_state *_state,const int *_fragis, - int _nfragis,int _dst_frame,int _src_frame,int _pli){ - _state->opt_vtable.state_frag_copy(_state,_fragis,_nfragis,_dst_frame, +void oc_state_frag_copy_list(const oc_theora_state *_state, + const ptrdiff_t *_fragis,ptrdiff_t _nfragis, + int _dst_frame,int _src_frame,int _pli){ + _state->opt_vtable.state_frag_copy_list(_state,_fragis,_nfragis,_dst_frame, _src_frame,_pli); } -void oc_state_frag_copy_c(const oc_theora_state *_state,const int *_fragis, - int _nfragis,int _dst_frame,int _src_frame,int _pli){ - const int *fragi; - const int *fragi_end; - int dst_framei; - int dst_ystride; - int src_framei; - int src_ystride; - dst_framei=_state->ref_frame_idx[_dst_frame]; - src_framei=_state->ref_frame_idx[_src_frame]; - dst_ystride=_state->ref_frame_bufs[dst_framei][_pli].stride; - src_ystride=_state->ref_frame_bufs[src_framei][_pli].stride; - fragi_end=_fragis+_nfragis; - for(fragi=_fragis;fragifrags+*fragi; - dst=frag->buffer[dst_framei]; - src=frag->buffer[src_framei]; - for(j=0;j<8;j++){ - memcpy(dst,src,sizeof(dst[0])*8); - dst+=dst_ystride; - src+=src_ystride; - } +void oc_state_frag_copy_list_c(const oc_theora_state *_state, + const ptrdiff_t *_fragis,ptrdiff_t _nfragis, + int _dst_frame,int _src_frame,int _pli){ + const ptrdiff_t *frag_buf_offs; + const unsigned char *src_frame_data; + unsigned char *dst_frame_data; + ptrdiff_t fragii; + int ystride; + dst_frame_data=_state->ref_frame_data[_state->ref_frame_idx[_dst_frame]]; + src_frame_data=_state->ref_frame_data[_state->ref_frame_idx[_src_frame]]; + ystride=_state->ref_ystride[_pli]; + frag_buf_offs=_state->frag_buf_offs; + for(fragii=0;fragii<_nfragis;fragii++){ + ptrdiff_t frag_buf_off; + frag_buf_off=frag_buf_offs[_fragis[fragii]]; + oc_frag_copy(_state,dst_frame_data+frag_buf_off, + src_frame_data+frag_buf_off,ystride); } } @@ -940,25 +966,24 @@ static void loop_filter_h(unsigned char *_pix,int _ystride,int *_bv){ } static void loop_filter_v(unsigned char *_pix,int _ystride,int *_bv){ - int y; + int x; _pix-=_ystride*2; - for(y=0;y<8;y++){ + for(x=0;x<8;x++){ int f; - f=_pix[0]-_pix[_ystride*3]+3*(_pix[_ystride*2]-_pix[_ystride]); + f=_pix[x]-_pix[_ystride*3+x]+3*(_pix[_ystride*2+x]-_pix[_ystride+x]); /*The _bv array is used to compute the function f=OC_CLAMPI(OC_MINI(-_2flimit-f,0),f,OC_MAXI(_2flimit-f,0)); where _2flimit=_state->loop_filter_limits[_state->qis[0]]<<1;*/ f=*(_bv+(f+4>>3)); - _pix[_ystride]=OC_CLAMP255(_pix[_ystride]+f); - _pix[_ystride*2]=OC_CLAMP255(_pix[_ystride*2]-f); - _pix++; + _pix[_ystride+x]=OC_CLAMP255(_pix[_ystride+x]+f); + _pix[_ystride*2+x]=OC_CLAMP255(_pix[_ystride*2+x]-f); } } /*Initialize the bounding values array used by the loop filter. _bv: Storage for the array. Return: 0 on success, or a non-zero value if no filtering need be applied.*/ -int oc_state_loop_filter_init(oc_theora_state *_state,int *_bv){ +int oc_state_loop_filter_init(oc_theora_state *_state,int _bv[256]){ int flimit; int i; flimit=_state->loop_filter_limits[_state->qis[0]]; @@ -981,56 +1006,61 @@ int oc_state_loop_filter_init(oc_theora_state *_state,int *_bv){ _pli: The color plane to filter. _fragy0: The Y coordinate of the first fragment row to filter. _fragy_end: The Y coordinate of the fragment row to stop filtering at.*/ -void oc_state_loop_filter_frag_rows(oc_theora_state *_state,int *_bv, +void oc_state_loop_filter_frag_rows(const oc_theora_state *_state,int _bv[256], int _refi,int _pli,int _fragy0,int _fragy_end){ _state->opt_vtable.state_loop_filter_frag_rows(_state,_bv,_refi,_pli, _fragy0,_fragy_end); } -void oc_state_loop_filter_frag_rows_c(oc_theora_state *_state,int *_bv, +void oc_state_loop_filter_frag_rows_c(const oc_theora_state *_state,int *_bv, int _refi,int _pli,int _fragy0,int _fragy_end){ - th_img_plane *iplane; - oc_fragment_plane *fplane; - oc_fragment *frag_top; - oc_fragment *frag0; - oc_fragment *frag; - oc_fragment *frag_end; - oc_fragment *frag0_end; - oc_fragment *frag_bot; + const oc_fragment_plane *fplane; + const oc_fragment *frags; + const ptrdiff_t *frag_buf_offs; + unsigned char *ref_frame_data; + ptrdiff_t fragi_top; + ptrdiff_t fragi_bot; + ptrdiff_t fragi0; + ptrdiff_t fragi0_end; + int ystride; + int nhfrags; _bv+=127; - iplane=_state->ref_frame_bufs[_refi]+_pli; fplane=_state->fplanes+_pli; + nhfrags=fplane->nhfrags; + fragi_top=fplane->froffset; + fragi_bot=fragi_top+fplane->nfrags; + fragi0=fragi_top+_fragy0*(ptrdiff_t)nhfrags; + fragi0_end=fragi0+(_fragy_end-_fragy0)*(ptrdiff_t)nhfrags; + ystride=_state->ref_ystride[_pli]; + frags=_state->frags; + frag_buf_offs=_state->frag_buf_offs; + ref_frame_data=_state->ref_frame_data[_refi]; /*The following loops are constructed somewhat non-intuitively on purpose. The main idea is: if a block boundary has at least one coded fragment on it, the filter is applied to it. However, the order that the filters are applied in matters, and VP3 chose the somewhat strange ordering used below.*/ - frag_top=_state->frags+fplane->froffset; - frag0=frag_top+_fragy0*fplane->nhfrags; - frag0_end=frag0+(_fragy_end-_fragy0)*fplane->nhfrags; - frag_bot=_state->frags+fplane->froffset+fplane->nfrags; - while(frag0nhfrags; - while(fragcoded){ - if(frag>frag0){ - loop_filter_h(frag->buffer[_refi],iplane->stride,_bv); + while(fragi0fragi0)loop_filter_h(ref,ystride,_bv); + if(fragi0>fragi_top)loop_filter_v(ref,ystride,_bv); + if(fragi+1frag_top){ - loop_filter_v(frag->buffer[_refi],iplane->stride,_bv); - } - if(frag+1coded){ - loop_filter_h(frag->buffer[_refi]+8,iplane->stride,_bv); - } - if(frag+fplane->nhfragsnhfrags)->coded){ - loop_filter_v((frag+fplane->nhfrags)->buffer[_refi], - iplane->stride,_bv); + if(fragi+nhfragsnhfrags; + fragi0+=nhfrags; } } @@ -1066,7 +1096,11 @@ int oc_state_dump_frame(const oc_theora_state *_state,int _frame, sprintf(fname,"%08i%s.png",(int)(iframe+pframe),_suf); fp=fopen(fname,"wb"); if(fp==NULL)return TH_EFAULT; - image=(png_bytep *)oc_malloc_2d(height,6*width,sizeof(image[0][0])); + image=(png_bytep *)oc_malloc_2d(height,6*width,sizeof(**image)); + if(image==NULL){ + fclose(fp); + return TH_EFAULT; + } png=png_create_write_struct(PNG_LIBPNG_VER_STRING,NULL,NULL,NULL); if(png==NULL){ oc_free_2d(image); @@ -1149,6 +1183,7 @@ int oc_state_dump_frame(const oc_theora_state *_state,int _frame, png_set_cHRM_fixed(png,info,31271,32902, 64000,33000,29000,60000,15000,6000); }break; + default:break; } png_set_pHYs(png,info,_state->info.aspect_numerator, _state->info.aspect_denominator,0); diff --git a/Engine/lib/libtheora/lib/tokenize.c b/Engine/lib/libtheora/lib/tokenize.c new file mode 100644 index 000000000..60574c359 --- /dev/null +++ b/Engine/lib/libtheora/lib/tokenize.c @@ -0,0 +1,1072 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: tokenize.c 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ +#include +#include +#include "encint.h" + + + +static int oc_make_eob_token(int _run_count){ + if(_run_count<4)return OC_DCT_EOB1_TOKEN+_run_count-1; + else{ + int cat; + cat=OC_ILOGNZ_32(_run_count)-3; + cat=OC_MINI(cat,3); + return OC_DCT_REPEAT_RUN0_TOKEN+cat; + } +} + +static int oc_make_eob_token_full(int _run_count,int *_eb){ + if(_run_count<4){ + *_eb=0; + return OC_DCT_EOB1_TOKEN+_run_count-1; + } + else{ + int cat; + cat=OC_ILOGNZ_32(_run_count)-3; + cat=OC_MINI(cat,3); + *_eb=_run_count-OC_BYTE_TABLE32(4,8,16,0,cat); + return OC_DCT_REPEAT_RUN0_TOKEN+cat; + } +} + +/*Returns the number of blocks ended by an EOB token.*/ +static int oc_decode_eob_token(int _token,int _eb){ + return (0x20820C41U>>_token*5&0x1F)+_eb; +} + +/*TODO: This is now only used during DCT tokenization, and never for runs; it + should be simplified.*/ +static int oc_make_dct_token_full(int _zzi,int _zzj,int _val,int *_eb){ + int neg; + int zero_run; + int token; + int eb; + neg=_val<0; + _val=abs(_val); + zero_run=_zzj-_zzi; + if(zero_run>0){ + int adj; + /*Implement a minor restriction on stack 1 so that we know during DC fixups + that extending a dctrun token from stack 1 will never overflow.*/ + adj=_zzi!=1; + if(_val<2&&zero_run<17+adj){ + if(zero_run<6){ + token=OC_DCT_RUN_CAT1A+zero_run-1; + eb=neg; + } + else if(zero_run<10){ + token=OC_DCT_RUN_CAT1B; + eb=zero_run-6+(neg<<2); + } + else{ + token=OC_DCT_RUN_CAT1C; + eb=zero_run-10+(neg<<3); + } + } + else if(_val<4&&zero_run<3+adj){ + if(zero_run<2){ + token=OC_DCT_RUN_CAT2A; + eb=_val-2+(neg<<1); + } + else{ + token=OC_DCT_RUN_CAT2B; + eb=zero_run-2+(_val-2<<1)+(neg<<2); + } + } + else{ + if(zero_run<9)token=OC_DCT_SHORT_ZRL_TOKEN; + else token=OC_DCT_ZRL_TOKEN; + eb=zero_run-1; + } + } + else if(_val<3){ + token=OC_ONE_TOKEN+(_val-1<<1)+neg; + eb=0; + } + else if(_val<7){ + token=OC_DCT_VAL_CAT2+_val-3; + eb=neg; + } + else if(_val<9){ + token=OC_DCT_VAL_CAT3; + eb=_val-7+(neg<<1); + } + else if(_val<13){ + token=OC_DCT_VAL_CAT4; + eb=_val-9+(neg<<2); + } + else if(_val<21){ + token=OC_DCT_VAL_CAT5; + eb=_val-13+(neg<<3); + } + else if(_val<37){ + token=OC_DCT_VAL_CAT6; + eb=_val-21+(neg<<4); + } + else if(_val<69){ + token=OC_DCT_VAL_CAT7; + eb=_val-37+(neg<<5); + } + else{ + token=OC_DCT_VAL_CAT8; + eb=_val-69+(neg<<9); + } + *_eb=eb; + return token; +} + +/*Token logging to allow a few fragments of efficient rollback. + Late SKIP analysis is tied up in the tokenization process, so we need to be + able to undo a fragment's tokens on a whim.*/ + +static const unsigned char OC_ZZI_HUFF_OFFSET[64]={ + 0,16,16,16,16,16,32,32, + 32,32,32,32,32,32,32,48, + 48,48,48,48,48,48,48,48, + 48,48,48,48,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64 +}; + +static int oc_token_bits(oc_enc_ctx *_enc,int _huffi,int _zzi,int _token){ + return _enc->huff_codes[_huffi+OC_ZZI_HUFF_OFFSET[_zzi]][_token].nbits + +OC_DCT_TOKEN_EXTRA_BITS[_token]; +} + +static void oc_enc_tokenlog_checkpoint(oc_enc_ctx *_enc, + oc_token_checkpoint *_cp,int _pli,int _zzi){ + _cp->pli=_pli; + _cp->zzi=_zzi; + _cp->eob_run=_enc->eob_run[_pli][_zzi]; + _cp->ndct_tokens=_enc->ndct_tokens[_pli][_zzi]; +} + +void oc_enc_tokenlog_rollback(oc_enc_ctx *_enc, + const oc_token_checkpoint *_stack,int _n){ + int i; + for(i=_n;i-->0;){ + int pli; + int zzi; + pli=_stack[i].pli; + zzi=_stack[i].zzi; + _enc->eob_run[pli][zzi]=_stack[i].eob_run; + _enc->ndct_tokens[pli][zzi]=_stack[i].ndct_tokens; + } +} + +static void oc_enc_token_log(oc_enc_ctx *_enc, + int _pli,int _zzi,int _token,int _eb){ + ptrdiff_t ti; + ti=_enc->ndct_tokens[_pli][_zzi]++; + _enc->dct_tokens[_pli][_zzi][ti]=(unsigned char)_token; + _enc->extra_bits[_pli][_zzi][ti]=(ogg_uint16_t)_eb; +} + +static void oc_enc_eob_log(oc_enc_ctx *_enc, + int _pli,int _zzi,int _run_count){ + int token; + int eb; + token=oc_make_eob_token_full(_run_count,&eb); + oc_enc_token_log(_enc,_pli,_zzi,token,eb); +} + + +void oc_enc_tokenize_start(oc_enc_ctx *_enc){ + memset(_enc->ndct_tokens,0,sizeof(_enc->ndct_tokens)); + memset(_enc->eob_run,0,sizeof(_enc->eob_run)); + memset(_enc->dct_token_offs,0,sizeof(_enc->dct_token_offs)); + memset(_enc->dc_pred_last,0,sizeof(_enc->dc_pred_last)); +} + +typedef struct oc_quant_token oc_quant_token; + +/*A single node in the Viterbi trellis. + We maintain up to 2 of these per coefficient: + - A token to code if the value is zero (EOB, zero run, or combo token). + - A token to code if the value is not zero (DCT value token).*/ +struct oc_quant_token{ + unsigned char next; + signed char token; + ogg_int16_t eb; + ogg_uint32_t cost; + int bits; + int qc; +}; + +/*Tokenizes the AC coefficients, possibly adjusting the quantization, and then + dequantizes and de-zig-zags the result. + The DC coefficient is not preserved; it should be restored by the caller.*/ +int oc_enc_tokenize_ac(oc_enc_ctx *_enc,int _pli,ptrdiff_t _fragi, + ogg_int16_t *_qdct,const ogg_uint16_t *_dequant,const ogg_int16_t *_dct, + int _zzi,oc_token_checkpoint **_stack,int _acmin){ + oc_token_checkpoint *stack; + ogg_int64_t zflags; + ogg_int64_t nzflags; + ogg_int64_t best_flags; + ogg_uint32_t d2_accum[64]; + oc_quant_token tokens[64][2]; + ogg_uint16_t *eob_run; + const unsigned char *dct_fzig_zag; + ogg_uint32_t cost; + int bits; + int eob; + int token; + int eb; + int next; + int huffi; + int zzi; + int ti; + int zzj; + int qc; + huffi=_enc->huff_idxs[_enc->state.frame_type][1][_pli+1>>1]; + eob_run=_enc->eob_run[_pli]; + memset(tokens[0],0,sizeof(tokens[0])); + best_flags=nzflags=0; + zflags=1; + d2_accum[0]=0; + zzj=64; + for(zzi=OC_MINI(_zzi,63);zzi>0;zzi--){ + ogg_int32_t lambda; + ogg_uint32_t best_cost; + int best_bits=best_bits; + int best_next=best_next; + int best_token=best_token; + int best_eb=best_eb; + int best_qc=best_qc; + int flush_bits; + ogg_uint32_t d2; + int dq; + int e; + int c; + int s; + int tj; + lambda=_enc->lambda; + qc=_qdct[zzi]; + s=-(qc<0); + qc=qc+s^s; + c=_dct[OC_FZIG_ZAG[zzi]]; + if(qc<=1){ + ogg_uint32_t sum_d2; + int nzeros; + int dc_reserve; + /*The hard case: try a zero run.*/ + if(!qc){ + /*Skip runs that are already quantized to zeros. + If we considered each zero coefficient in turn, we might + theoretically find a better way to partition long zero runs (e.g., + a run of > 17 zeros followed by a 1 might be better coded as a short + zero run followed by a combo token, rather than the longer zero + token followed by a 1 value token), but zeros are so common that + this becomes very computationally expensive (quadratic instead of + linear in the number of coefficients), for a marginal gain.*/ + while(zzi>1&&!_qdct[zzi-1])zzi--; + /*The distortion of coefficients originally quantized to zero is + treated as zero (since we'll never quantize them to anything else).*/ + d2=0; + } + else{ + c=c+s^s; + d2=c*(ogg_int32_t)c; + } + eob=eob_run[zzi]; + nzeros=zzj-zzi; + zzj&=63; + sum_d2=d2+d2_accum[zzj]; + d2_accum[zzi]=sum_d2; + flush_bits=eob>0?oc_token_bits(_enc,huffi,zzi,oc_make_eob_token(eob)):0; + /*We reserve 1 spot for combo run tokens that start in the 1st AC stack + to ensure they can be extended to include the DC coefficient if + necessary; this greatly simplifies stack-rewriting later on.*/ + dc_reserve=zzi+62>>6; + best_cost=0xFFFFFFFF; + for(;;){ + if(nzflags>>zzj&1){ + int cat; + int val; + int val_s; + int zzk; + int tk; + next=tokens[zzj][1].next; + tk=next&1; + zzk=next>>1; + /*Try a pure zero run to this point.*/ + cat=nzeros+55>>6; + token=OC_DCT_SHORT_ZRL_TOKEN+cat; + bits=flush_bits+oc_token_bits(_enc,huffi,zzi,token); + d2=sum_d2-d2_accum[zzj]; + cost=d2+lambda*bits+tokens[zzj][1].cost; + if(cost<=best_cost){ + best_next=(zzj<<1)+1; + best_token=token; + best_eb=nzeros-1; + best_cost=cost; + best_bits=bits+tokens[zzj][1].bits; + best_qc=0; + } + if(nzeros<16+dc_reserve){ + val=_qdct[zzj]; + val_s=-(val<0); + val=val+val_s^val_s; + if(val<=2){ + /*Try a +/- 1 combo token.*/ + if(nzeros<6){ + token=OC_DCT_RUN_CAT1A+nzeros-1; + eb=-val_s; + } + else{ + cat=nzeros+54>>6; + token=OC_DCT_RUN_CAT1B+cat; + eb=(-val_s<>1; + token=OC_DCT_RUN_CAT2A+cat; + bits=flush_bits+oc_token_bits(_enc,huffi,zzi,token); + val=2+((val+val_s^val_s)>2); + e=(_dct[OC_FZIG_ZAG[zzj]]+val_s^val_s)-_dequant[zzj]*val; + d2=e*(ogg_int32_t)e+sum_d2-d2_accum[zzj]; + cost=d2+lambda*bits+tokens[zzk][tk].cost; + if(cost<=best_cost){ + best_cost=cost; + best_bits=bits+tokens[zzk][tk].bits; + best_next=next; + best_token=token; + best_eb=(-val_s<<1+cat)+(val-2<>1); + best_qc=val+val_s^val_s; + } + } + } + /*zzj can't be coded as a zero, so stop trying to extend the run.*/ + if(!(zflags>>zzj&1))break; + } + /*We could try to consider _all_ potentially non-zero coefficients, but + if we already found a bunch of them not worth coding, it's fairly + unlikely they would now be worth coding from this position; skipping + them saves a lot of work.*/ + zzj=(tokens[zzj][0].next>>1)-(tokens[zzj][0].qc!=0)&63; + if(zzj==0){ + /*We made it all the way to the end of the block; try an EOB token.*/ + if(eob<4095){ + bits=oc_token_bits(_enc,huffi,zzi,oc_make_eob_token(eob+1)) + -flush_bits; + } + else bits=oc_token_bits(_enc,huffi,zzi,OC_DCT_EOB1_TOKEN); + cost=sum_d2+bits*lambda; + /*If the best route so far is still a pure zero run to the end of the + block, force coding it as an EOB. + Even if it's not optimal for this block, it has a good chance of + getting combined with an EOB token from subsequent blocks, saving + bits overall.*/ + if(cost<=best_cost||best_token<=OC_DCT_ZRL_TOKEN&&zzi+best_eb==63){ + best_next=0; + /*This token is just a marker; in reality we may not emit any + tokens, but update eob_run[] instead.*/ + best_token=OC_DCT_EOB1_TOKEN; + best_eb=0; + best_cost=cost; + best_bits=bits; + best_qc=0; + } + break; + } + nzeros=zzj-zzi; + } + tokens[zzi][0].next=(unsigned char)best_next; + tokens[zzi][0].token=(signed char)best_token; + tokens[zzi][0].eb=(ogg_int16_t)best_eb; + tokens[zzi][0].cost=best_cost; + tokens[zzi][0].bits=best_bits; + tokens[zzi][0].qc=best_qc; + zflags|=(ogg_int64_t)1<>zzj&1; + next=(zzj<<1)+tj; + tokens[zzi][1].next=(unsigned char)next; + tokens[zzi][1].token=(signed char)token; + tokens[zzi][1].eb=0; + tokens[zzi][1].cost=d2+lambda*bits+tokens[zzj][tj].cost; + tokens[zzi][1].bits=bits+tokens[zzj][tj].bits; + tokens[zzi][1].qc=1+s^s; + nzflags|=(ogg_int64_t)1<0?oc_token_bits(_enc,huffi,zzi,oc_make_eob_token(eob)):0; + if(qc<=2){ + e=2*dq-c; + d2=e*(ogg_int32_t)e; + best_token=OC_TWO_TOKEN-s; + best_bits=flush_bits+oc_token_bits(_enc,huffi,zzi,best_token); + best_cost=d2+lambda*best_bits; + e-=dq; + d2=e*(ogg_int32_t)e; + token=OC_ONE_TOKEN-s; + bits=flush_bits+oc_token_bits(_enc,huffi,zzi,token); + cost=d2+lambda*bits; + if(cost<=best_cost){ + best_token=token; + best_bits=bits; + best_cost=cost; + qc--; + } + best_eb=0; + } + else if(qc<=3){ + e=3*dq-c; + d2=e*(ogg_int32_t)e; + best_token=OC_DCT_VAL_CAT2; + best_eb=-s; + best_bits=flush_bits+oc_token_bits(_enc,huffi,zzi,best_token); + best_cost=d2+lambda*best_bits; + e-=dq; + d2=e*(ogg_int32_t)e; + token=OC_TWO_TOKEN-s; + bits=flush_bits+oc_token_bits(_enc,huffi,zzi,token); + cost=d2+lambda*bits; + if(cost<=best_cost){ + best_token=token; + best_eb=0; + best_bits=bits; + best_cost=cost; + qc--; + } + } + else if(qc<=6){ + e=qc*dq-c; + d2=e*(ogg_int32_t)e; + best_token=OC_DCT_VAL_CAT2+qc-3; + best_eb=-s; + best_bits=flush_bits+oc_token_bits(_enc,huffi,zzi,best_token); + best_cost=d2+lambda*best_bits; + e-=dq; + d2=e*(ogg_int32_t)e; + token=best_token-1; + bits=flush_bits+oc_token_bits(_enc,huffi,zzi,token); + cost=d2+lambda*bits; + if(cost<=best_cost){ + best_token=token; + best_bits=bits; + best_cost=cost; + qc--; + } + } + else if(qc<=8){ + e=qc*dq-c; + d2=e*(ogg_int32_t)e; + best_token=OC_DCT_VAL_CAT3; + best_eb=(-s<<1)+qc-7; + best_bits=flush_bits+oc_token_bits(_enc,huffi,zzi,best_token); + best_cost=d2+lambda*best_bits; + e=6*dq-c; + d2=e*(ogg_int32_t)e; + token=OC_DCT_VAL_CAT2+3; + bits=flush_bits+oc_token_bits(_enc,huffi,zzi,token); + cost=d2+lambda*bits; + if(cost<=best_cost){ + best_token=token; + best_eb=-s; + best_bits=bits; + best_cost=cost; + qc=6; + } + } + else if(qc<=12){ + e=qc*dq-c; + d2=e*(ogg_int32_t)e; + best_token=OC_DCT_VAL_CAT4; + best_eb=(-s<<2)+qc-9; + best_bits=flush_bits+oc_token_bits(_enc,huffi,zzi,best_token); + best_cost=d2+lambda*best_bits; + e=8*dq-c; + d2=e*(ogg_int32_t)e; + token=best_token-1; + bits=flush_bits+oc_token_bits(_enc,huffi,zzi,token); + cost=d2+lambda*bits; + if(cost<=best_cost){ + best_token=token; + best_eb=(-s<<1)+1; + best_bits=bits; + best_cost=cost; + qc=8; + } + } + else if(qc<=20){ + e=qc*dq-c; + d2=e*(ogg_int32_t)e; + best_token=OC_DCT_VAL_CAT5; + best_eb=(-s<<3)+qc-13; + best_bits=flush_bits+oc_token_bits(_enc,huffi,zzi,best_token); + best_cost=d2+lambda*best_bits; + e=12*dq-c; + d2=e*(ogg_int32_t)e; + token=best_token-1; + bits=flush_bits+oc_token_bits(_enc,huffi,zzi,token); + cost=d2+lambda*bits; + if(cost<=best_cost){ + best_token=token; + best_eb=(-s<<2)+3; + best_bits=bits; + best_cost=cost; + qc=12; + } + } + else if(qc<=36){ + e=qc*dq-c; + d2=e*(ogg_int32_t)e; + best_token=OC_DCT_VAL_CAT6; + best_eb=(-s<<4)+qc-21; + best_bits=flush_bits+oc_token_bits(_enc,huffi,zzi,best_token); + best_cost=d2+lambda*best_bits; + e=20*dq-c; + d2=e*(ogg_int32_t)e; + token=best_token-1; + bits=flush_bits+oc_token_bits(_enc,huffi,zzi,token); + cost=d2+lambda*bits; + if(cost<=best_cost){ + best_token=token; + best_eb=(-s<<3)+7; + best_bits=bits; + best_cost=cost; + qc=20; + } + } + else if(qc<=68){ + e=qc*dq-c; + d2=e*(ogg_int32_t)e; + best_token=OC_DCT_VAL_CAT7; + best_eb=(-s<<5)+qc-37; + best_bits=flush_bits+oc_token_bits(_enc,huffi,zzi,best_token); + best_cost=d2+lambda*best_bits; + e=36*dq-c; + d2=e*(ogg_int32_t)e; + token=best_token-1; + bits=flush_bits+oc_token_bits(_enc,huffi,zzi,token); + cost=d2+lambda*bits; + if(cost>zzj&1; + next=(zzj<<1)+tj; + tokens[zzi][1].next=(unsigned char)next; + tokens[zzi][1].token=(signed char)best_token; + tokens[zzi][1].eb=best_eb; + tokens[zzi][1].cost=best_cost+tokens[zzj][tj].cost; + tokens[zzi][1].bits=best_bits+tokens[zzj][tj].bits; + tokens[zzi][1].qc=qc+s^s; + nzflags|=(ogg_int64_t)1<state.opt_data.dct_fzig_zag; + zzi=1; + ti=best_flags>>1&1; + bits=tokens[zzi][ti].bits; + do{ + oc_enc_tokenlog_checkpoint(_enc,stack++,_pli,zzi); + eob=eob_run[zzi]; + if(tokens[zzi][ti].token=4095){ + oc_enc_eob_log(_enc,_pli,zzi,eob); + eob=0; + } + eob_run[zzi]=eob; + /*We don't include the actual EOB cost for this block in the return value. + It will be paid for by the fragment that terminates the EOB run.*/ + bits-=tokens[zzi][ti].bits; + zzi=_zzi; + break; + } + /*Emit pending EOB run if any.*/ + if(eob>0){ + oc_enc_eob_log(_enc,_pli,zzi,eob); + eob_run[zzi]=0; + } + oc_enc_token_log(_enc,_pli,zzi,tokens[zzi][ti].token,tokens[zzi][ti].eb); + next=tokens[zzi][ti].next; + qc=tokens[zzi][ti].qc; + zzj=(next>>1)-1&63; + /*TODO: It may be worth saving the dequantized coefficient in the trellis + above; we had to compute it to measure the error anyway.*/ + _qdct[dct_fzig_zag[zzj]]=(ogg_int16_t)(qc*(int)_dequant[zzj]); + zzi=next>>1; + ti=next&1; + } + while(zzi); + *_stack=stack; + return bits; +} + +void oc_enc_pred_dc_frag_rows(oc_enc_ctx *_enc, + int _pli,int _fragy0,int _frag_yend){ + const oc_fragment_plane *fplane; + const oc_fragment *frags; + ogg_int16_t *frag_dc; + ptrdiff_t fragi; + int *pred_last; + int nhfrags; + int fragx; + int fragy; + fplane=_enc->state.fplanes+_pli; + frags=_enc->state.frags; + frag_dc=_enc->frag_dc; + pred_last=_enc->dc_pred_last[_pli]; + nhfrags=fplane->nhfrags; + fragi=fplane->froffset+_fragy0*nhfrags; + for(fragy=_fragy0;fragy<_frag_yend;fragy++){ + if(fragy==0){ + /*For the first row, all of the cases reduce to just using the previous + predictor for the same reference frame.*/ + for(fragx=0;fragx=nhfrags)ur_ref=-1; + else{ + ur_ref=u_frags[fragi+1].coded? + OC_FRAME_FOR_MODE(u_frags[fragi+1].mb_mode):-1; + } + if(frags[fragi].coded){ + int pred; + int ref; + ref=OC_FRAME_FOR_MODE(frags[fragi].mb_mode); + /*We break out a separate case based on which of our neighbors use + the same reference frames. + This is somewhat faster than trying to make a generic case which + handles all of them, since it reduces lots of poorly predicted + jumps to one switch statement, and also lets a number of the + multiplications be optimized out by strength reduction.*/ + switch((l_ref==ref)|(ul_ref==ref)<<1| + (u_ref==ref)<<2|(ur_ref==ref)<<3){ + default:pred=pred_last[ref];break; + case 1: + case 3:pred=frags[fragi-1].dc;break; + case 2:pred=u_frags[fragi-1].dc;break; + case 4: + case 6: + case 12:pred=u_frags[fragi].dc;break; + case 5:pred=(frags[fragi-1].dc+u_frags[fragi].dc)/2;break; + case 8:pred=u_frags[fragi+1].dc;break; + case 9: + case 11: + case 13:{ + pred=(75*frags[fragi-1].dc+53*u_frags[fragi+1].dc)/128; + }break; + case 10:pred=(u_frags[fragi-1].dc+u_frags[fragi+1].dc)/2;break; + case 14:{ + pred=(3*(u_frags[fragi-1].dc+u_frags[fragi+1].dc) + +10*u_frags[fragi].dc)/16; + }break; + case 7: + case 15:{ + int p0; + int p1; + int p2; + p0=frags[fragi-1].dc; + p1=u_frags[fragi-1].dc; + p2=u_frags[fragi].dc; + pred=(29*(p0+p2)-26*p1)/32; + if(abs(pred-p2)>128)pred=p2; + else if(abs(pred-p0)>128)pred=p0; + else if(abs(pred-p1)>128)pred=p1; + }break; + } + frag_dc[fragi]=(ogg_int16_t)(frags[fragi].dc-pred); + pred_last[ref]=frags[fragi].dc; + l_ref=ref; + } + else l_ref=-1; + ul_ref=u_ref; + u_ref=ur_ref; + } + } + } +} + +void oc_enc_tokenize_dc_frag_list(oc_enc_ctx *_enc,int _pli, + const ptrdiff_t *_coded_fragis,ptrdiff_t _ncoded_fragis, + int _prev_ndct_tokens1,int _prev_eob_run1){ + const ogg_int16_t *frag_dc; + ptrdiff_t fragii; + unsigned char *dct_tokens0; + unsigned char *dct_tokens1; + ogg_uint16_t *extra_bits0; + ogg_uint16_t *extra_bits1; + ptrdiff_t ti0; + ptrdiff_t ti1r; + ptrdiff_t ti1w; + int eob_run0; + int eob_run1; + int neobs1; + int token; + int eb; + int token1=token1; + int eb1=eb1; + /*Return immediately if there are no coded fragments; otherwise we'd flush + any trailing EOB run into the AC 1 list and never read it back out.*/ + if(_ncoded_fragis<=0)return; + frag_dc=_enc->frag_dc; + dct_tokens0=_enc->dct_tokens[_pli][0]; + dct_tokens1=_enc->dct_tokens[_pli][1]; + extra_bits0=_enc->extra_bits[_pli][0]; + extra_bits1=_enc->extra_bits[_pli][1]; + ti0=_enc->ndct_tokens[_pli][0]; + ti1w=ti1r=_prev_ndct_tokens1; + eob_run0=_enc->eob_run[_pli][0]; + /*Flush any trailing EOB run for the 1st AC coefficient. + This is needed to allow us to track tokens to the end of the list.*/ + eob_run1=_enc->eob_run[_pli][1]; + if(eob_run1>0)oc_enc_eob_log(_enc,_pli,1,eob_run1); + /*If there was an active EOB run at the start of the 1st AC stack, read it + in and decode it.*/ + if(_prev_eob_run1>0){ + token1=dct_tokens1[ti1r]; + eb1=extra_bits1[ti1r]; + ti1r++; + eob_run1=oc_decode_eob_token(token1,eb1); + /*Consume the portion of the run that came before these fragments.*/ + neobs1=eob_run1-_prev_eob_run1; + } + else eob_run1=neobs1=0; + for(fragii=0;fragii<_ncoded_fragis;fragii++){ + int val; + /*All tokens in the 1st AC coefficient stack are regenerated as the DC + coefficients are produced. + This can be done in-place; stack 1 cannot get larger.*/ + if(!neobs1){ + /*There's no active EOB run in stack 1; read the next token.*/ + token1=dct_tokens1[ti1r]; + eb1=extra_bits1[ti1r]; + ti1r++; + if(token10){ + token=oc_make_eob_token_full(eob_run0,&eb); + dct_tokens0[ti0]=(unsigned char)token; + extra_bits0[ti0]=(ogg_uint16_t)eb; + ti0++; + eob_run0=0; + } + token=oc_make_dct_token_full(0,0,val,&eb); + dct_tokens0[ti0]=(unsigned char)token; + extra_bits0[ti0]=(ogg_uint16_t)eb; + ti0++; + } + else{ + /*Zero DC value; that means the entry in stack 1 might need to be coded + from stack 0. + This requires a stack 1 fixup.*/ + if(neobs1>0){ + /*We're in the middle of an active EOB run in stack 1. + Move it to stack 0.*/ + if(++eob_run0>=4095){ + token=oc_make_eob_token_full(eob_run0,&eb); + dct_tokens0[ti0]=(unsigned char)token; + extra_bits0[ti0]=(ogg_uint16_t)eb; + ti0++; + eob_run0=0; + } + eob_run1--; + } + else{ + /*No active EOB run in stack 1, so we can't extend one in stack 0. + Flush it if we've got it.*/ + if(eob_run0>0){ + token=oc_make_eob_token_full(eob_run0,&eb); + dct_tokens0[ti0]=(unsigned char)token; + extra_bits0[ti0]=(ogg_uint16_t)eb; + ti0++; + eob_run0=0; + } + /*Stack 1 token is one of: a pure zero run token, a single + coefficient token, or a zero run/coefficient combo token. + A zero run token is expanded and moved to token stack 0, and the + stack 1 entry dropped. + A single coefficient value may be transformed into combo token that + is moved to stack 0, or if it cannot be combined, it is left alone + and a single length-1 zero run is emitted in stack 0. + A combo token is extended and moved to stack 0. + During AC coding, we restrict the run lengths on combo tokens for + stack 1 to guarantee we can extend them.*/ + switch(token1){ + case OC_DCT_SHORT_ZRL_TOKEN:{ + if(eb1<7){ + dct_tokens0[ti0]=OC_DCT_SHORT_ZRL_TOKEN; + extra_bits0[ti0]=(ogg_uint16_t)(eb1+1); + ti0++; + /*Don't write the AC coefficient back out.*/ + continue; + } + /*Fall through.*/ + } + case OC_DCT_ZRL_TOKEN:{ + dct_tokens0[ti0]=OC_DCT_ZRL_TOKEN; + extra_bits0[ti0]=(ogg_uint16_t)(eb1+1); + ti0++; + /*Don't write the AC coefficient back out.*/ + }continue; + case OC_ONE_TOKEN: + case OC_MINUS_ONE_TOKEN:{ + dct_tokens0[ti0]=OC_DCT_RUN_CAT1A; + extra_bits0[ti0]=(ogg_uint16_t)(token1-OC_ONE_TOKEN); + ti0++; + /*Don't write the AC coefficient back out.*/ + }continue; + case OC_TWO_TOKEN: + case OC_MINUS_TWO_TOKEN:{ + dct_tokens0[ti0]=OC_DCT_RUN_CAT2A; + extra_bits0[ti0]=(ogg_uint16_t)(token1-OC_TWO_TOKEN<<1); + ti0++; + /*Don't write the AC coefficient back out.*/ + }continue; + case OC_DCT_VAL_CAT2:{ + dct_tokens0[ti0]=OC_DCT_RUN_CAT2A; + extra_bits0[ti0]=(ogg_uint16_t)((eb1<<1)+1); + ti0++; + /*Don't write the AC coefficient back out.*/ + }continue; + case OC_DCT_RUN_CAT1A: + case OC_DCT_RUN_CAT1A+1: + case OC_DCT_RUN_CAT1A+2: + case OC_DCT_RUN_CAT1A+3:{ + dct_tokens0[ti0]=(unsigned char)(token1+1); + extra_bits0[ti0]=(ogg_uint16_t)eb1; + ti0++; + /*Don't write the AC coefficient back out.*/ + }continue; + case OC_DCT_RUN_CAT1A+4:{ + dct_tokens0[ti0]=OC_DCT_RUN_CAT1B; + extra_bits0[ti0]=(ogg_uint16_t)(eb1<<2); + ti0++; + /*Don't write the AC coefficient back out.*/ + }continue; + case OC_DCT_RUN_CAT1B:{ + if((eb1&3)<3){ + dct_tokens0[ti0]=OC_DCT_RUN_CAT1B; + extra_bits0[ti0]=(ogg_uint16_t)(eb1+1); + ti0++; + /*Don't write the AC coefficient back out.*/ + continue; + } + eb1=((eb1&4)<<1)-1; + /*Fall through.*/ + } + case OC_DCT_RUN_CAT1C:{ + dct_tokens0[ti0]=OC_DCT_RUN_CAT1C; + extra_bits0[ti0]=(ogg_uint16_t)(eb1+1); + ti0++; + /*Don't write the AC coefficient back out.*/ + }continue; + case OC_DCT_RUN_CAT2A:{ + eb1=(eb1<<1)-1; + /*Fall through.*/ + } + case OC_DCT_RUN_CAT2B:{ + dct_tokens0[ti0]=OC_DCT_RUN_CAT2B; + extra_bits0[ti0]=(ogg_uint16_t)(eb1+1); + ti0++; + /*Don't write the AC coefficient back out.*/ + }continue; + } + /*We can't merge tokens, write a short zero run and keep going.*/ + dct_tokens0[ti0]=OC_DCT_SHORT_ZRL_TOKEN; + extra_bits0[ti0]=0; + ti0++; + } + } + if(!neobs1){ + /*Flush any (inactive) EOB run.*/ + if(eob_run1>0){ + token=oc_make_eob_token_full(eob_run1,&eb); + dct_tokens1[ti1w]=(unsigned char)token; + extra_bits1[ti1w]=(ogg_uint16_t)eb; + ti1w++; + eob_run1=0; + } + /*There's no active EOB run, so log the current token.*/ + dct_tokens1[ti1w]=(unsigned char)token1; + extra_bits1[ti1w]=(ogg_uint16_t)eb1; + ti1w++; + } + else{ + /*Otherwise consume one EOB from the current run.*/ + neobs1--; + /*If we have more than 4095 EOBs outstanding in stack1, flush the run.*/ + if(eob_run1-neobs1>=4095){ + token=oc_make_eob_token_full(4095,&eb); + dct_tokens1[ti1w]=(unsigned char)token; + extra_bits1[ti1w]=(ogg_uint16_t)eb; + ti1w++; + eob_run1-=4095; + } + } + } + /*Save the current state.*/ + _enc->ndct_tokens[_pli][0]=ti0; + _enc->ndct_tokens[_pli][1]=ti1w; + _enc->eob_run[_pli][0]=eob_run0; + _enc->eob_run[_pli][1]=eob_run1; +} + +/*Final EOB run welding.*/ +void oc_enc_tokenize_finish(oc_enc_ctx *_enc){ + int pli; + int zzi; + /*Emit final EOB runs.*/ + for(pli=0;pli<3;pli++)for(zzi=0;zzi<64;zzi++){ + int eob_run; + eob_run=_enc->eob_run[pli][zzi]; + if(eob_run>0)oc_enc_eob_log(_enc,pli,zzi,eob_run); + } + /*Merge the final EOB run of one token list with the start of the next, if + possible.*/ + for(zzi=0;zzi<64;zzi++)for(pli=0;pli<3;pli++){ + int old_tok1; + int old_tok2; + int old_eb1; + int old_eb2; + int new_tok; + int new_eb; + int zzj; + int plj; + ptrdiff_t ti=ti; + int run_count; + /*Make sure this coefficient has tokens at all.*/ + if(_enc->ndct_tokens[pli][zzi]<=0)continue; + /*Ensure the first token is an EOB run.*/ + old_tok2=_enc->dct_tokens[pli][zzi][0]; + if(old_tok2>=OC_NDCT_EOB_TOKEN_MAX)continue; + /*Search for a previous coefficient that has any tokens at all.*/ + old_tok1=OC_NDCT_EOB_TOKEN_MAX; + for(zzj=zzi,plj=pli;zzj>=0;zzj--){ + while(plj-->0){ + ti=_enc->ndct_tokens[plj][zzj]-1; + if(ti>=_enc->dct_token_offs[plj][zzj]){ + old_tok1=_enc->dct_tokens[plj][zzj][ti]; + break; + } + } + if(plj>=0)break; + plj=3; + } + /*Ensure its last token was an EOB run.*/ + if(old_tok1>=OC_NDCT_EOB_TOKEN_MAX)continue; + /*Pull off the associated extra bits, if any, and decode the runs.*/ + old_eb1=_enc->extra_bits[plj][zzj][ti]; + old_eb2=_enc->extra_bits[pli][zzi][0]; + run_count=oc_decode_eob_token(old_tok1,old_eb1) + +oc_decode_eob_token(old_tok2,old_eb2); + /*We can't possibly combine these into one run. + It might be possible to split them more optimally, but we'll just leave + them as-is.*/ + if(run_count>=4096)continue; + /*We CAN combine them into one run.*/ + new_tok=oc_make_eob_token_full(run_count,&new_eb); + _enc->dct_tokens[plj][zzj][ti]=(unsigned char)new_tok; + _enc->extra_bits[plj][zzj][ti]=(ogg_uint16_t)new_eb; + _enc->dct_token_offs[pli][zzi]++; + } +} diff --git a/Engine/lib/libtheora/lib/x86/mmxencfrag.c b/Engine/lib/libtheora/lib/x86/mmxencfrag.c new file mode 100644 index 000000000..c79ff01fc --- /dev/null +++ b/Engine/lib/libtheora/lib/x86/mmxencfrag.c @@ -0,0 +1,900 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dsp_mmx.c 14579 2008-03-12 06:42:40Z xiphmont $ + + ********************************************************************/ +#include +#include "x86enc.h" + +#if defined(OC_X86_ASM) + +unsigned oc_enc_frag_sad_mmxext(const unsigned char *_src, + const unsigned char *_ref,int _ystride){ + ptrdiff_t ystride3; + ptrdiff_t ret; + __asm__ __volatile__( + /*Load the first 4 rows of each block.*/ + "movq (%[src]),%%mm0\n\t" + "movq (%[ref]),%%mm1\n\t" + "movq (%[src],%[ystride]),%%mm2\n\t" + "movq (%[ref],%[ystride]),%%mm3\n\t" + "lea (%[ystride],%[ystride],2),%[ystride3]\n\t" + "movq (%[src],%[ystride],2),%%mm4\n\t" + "movq (%[ref],%[ystride],2),%%mm5\n\t" + "movq (%[src],%[ystride3]),%%mm6\n\t" + "movq (%[ref],%[ystride3]),%%mm7\n\t" + /*Compute their SADs and add them in %%mm0*/ + "psadbw %%mm1,%%mm0\n\t" + "psadbw %%mm3,%%mm2\n\t" + "lea (%[src],%[ystride],4),%[src]\n\t" + "paddw %%mm2,%%mm0\n\t" + "lea (%[ref],%[ystride],4),%[ref]\n\t" + /*Load the next 3 rows as registers become available.*/ + "movq (%[src]),%%mm2\n\t" + "movq (%[ref]),%%mm3\n\t" + "psadbw %%mm5,%%mm4\n\t" + "psadbw %%mm7,%%mm6\n\t" + "paddw %%mm4,%%mm0\n\t" + "movq (%[ref],%[ystride]),%%mm5\n\t" + "movq (%[src],%[ystride]),%%mm4\n\t" + "paddw %%mm6,%%mm0\n\t" + "movq (%[ref],%[ystride],2),%%mm7\n\t" + "movq (%[src],%[ystride],2),%%mm6\n\t" + /*Start adding their SADs to %%mm0*/ + "psadbw %%mm3,%%mm2\n\t" + "psadbw %%mm5,%%mm4\n\t" + "paddw %%mm2,%%mm0\n\t" + "psadbw %%mm7,%%mm6\n\t" + /*Load last row as registers become available.*/ + "movq (%[src],%[ystride3]),%%mm2\n\t" + "movq (%[ref],%[ystride3]),%%mm3\n\t" + /*And finish adding up their SADs.*/ + "paddw %%mm4,%%mm0\n\t" + "psadbw %%mm3,%%mm2\n\t" + "paddw %%mm6,%%mm0\n\t" + "paddw %%mm2,%%mm0\n\t" + "movd %%mm0,%[ret]\n\t" + :[ret]"=a"(ret),[src]"+%r"(_src),[ref]"+r"(_ref),[ystride3]"=&r"(ystride3) + :[ystride]"r"((ptrdiff_t)_ystride) + ); + return (unsigned)ret; +} + +unsigned oc_enc_frag_sad_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _thresh){ + /*Early termination is for suckers.*/ + return oc_enc_frag_sad_mmxext(_src,_ref,_ystride); +} + +/*Assumes the first two rows of %[ref1] and %[ref2] are in %%mm0...%%mm3, the + first two rows of %[src] are in %%mm4,%%mm5, and {1}x8 is in %%mm7. + We pre-load the next two rows of data as registers become available.*/ +#define OC_SAD2_LOOP \ + "#OC_SAD2_LOOP\n\t" \ + /*We want to compute (%%mm0+%%mm1>>1) on unsigned bytes without overflow, but \ + pavgb computes (%%mm0+%%mm1+1>>1). \ + The latter is exactly 1 too large when the low bit of two corresponding \ + bytes is only set in one of them. \ + Therefore we pxor the operands, pand to mask out the low bits, and psubb to \ + correct the output of pavgb.*/ \ + "movq %%mm0,%%mm6\n\t" \ + "lea (%[ref1],%[ystride],2),%[ref1]\n\t" \ + "pxor %%mm1,%%mm0\n\t" \ + "pavgb %%mm1,%%mm6\n\t" \ + "lea (%[ref2],%[ystride],2),%[ref2]\n\t" \ + "movq %%mm2,%%mm1\n\t" \ + "pand %%mm7,%%mm0\n\t" \ + "pavgb %%mm3,%%mm2\n\t" \ + "pxor %%mm3,%%mm1\n\t" \ + "movq (%[ref2],%[ystride]),%%mm3\n\t" \ + "psubb %%mm0,%%mm6\n\t" \ + "movq (%[ref1]),%%mm0\n\t" \ + "pand %%mm7,%%mm1\n\t" \ + "psadbw %%mm6,%%mm4\n\t" \ + "movd %[ret],%%mm6\n\t" \ + "psubb %%mm1,%%mm2\n\t" \ + "movq (%[ref2]),%%mm1\n\t" \ + "lea (%[src],%[ystride],2),%[src]\n\t" \ + "psadbw %%mm2,%%mm5\n\t" \ + "movq (%[ref1],%[ystride]),%%mm2\n\t" \ + "paddw %%mm4,%%mm5\n\t" \ + "movq (%[src]),%%mm4\n\t" \ + "paddw %%mm5,%%mm6\n\t" \ + "movq (%[src],%[ystride]),%%mm5\n\t" \ + "movd %%mm6,%[ret]\n\t" \ + +/*Same as above, but does not pre-load the next two rows.*/ +#define OC_SAD2_TAIL \ + "#OC_SAD2_TAIL\n\t" \ + "movq %%mm0,%%mm6\n\t" \ + "pavgb %%mm1,%%mm0\n\t" \ + "pxor %%mm1,%%mm6\n\t" \ + "movq %%mm2,%%mm1\n\t" \ + "pand %%mm7,%%mm6\n\t" \ + "pavgb %%mm3,%%mm2\n\t" \ + "pxor %%mm3,%%mm1\n\t" \ + "psubb %%mm6,%%mm0\n\t" \ + "pand %%mm7,%%mm1\n\t" \ + "psadbw %%mm0,%%mm4\n\t" \ + "psubb %%mm1,%%mm2\n\t" \ + "movd %[ret],%%mm6\n\t" \ + "psadbw %%mm2,%%mm5\n\t" \ + "paddw %%mm4,%%mm5\n\t" \ + "paddw %%mm5,%%mm6\n\t" \ + "movd %%mm6,%[ret]\n\t" \ + +unsigned oc_enc_frag_sad2_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride, + unsigned _thresh){ + ptrdiff_t ret; + __asm__ __volatile__( + "movq (%[ref1]),%%mm0\n\t" + "movq (%[ref2]),%%mm1\n\t" + "movq (%[ref1],%[ystride]),%%mm2\n\t" + "movq (%[ref2],%[ystride]),%%mm3\n\t" + "xor %[ret],%[ret]\n\t" + "movq (%[src]),%%mm4\n\t" + "pxor %%mm7,%%mm7\n\t" + "pcmpeqb %%mm6,%%mm6\n\t" + "movq (%[src],%[ystride]),%%mm5\n\t" + "psubb %%mm6,%%mm7\n\t" + OC_SAD2_LOOP + OC_SAD2_LOOP + OC_SAD2_LOOP + OC_SAD2_TAIL + :[ret]"=&a"(ret),[src]"+r"(_src),[ref1]"+%r"(_ref1),[ref2]"+r"(_ref2) + :[ystride]"r"((ptrdiff_t)_ystride) + ); + return (unsigned)ret; +} + +/*Load an 8x4 array of pixel values from %[src] and %[ref] and compute their + 16-bit difference in %%mm0...%%mm7.*/ +#define OC_LOAD_SUB_8x4(_off) \ + "#OC_LOAD_SUB_8x4\n\t" \ + "movd "_off"(%[src]),%%mm0\n\t" \ + "movd "_off"(%[ref]),%%mm4\n\t" \ + "movd "_off"(%[src],%[src_ystride]),%%mm1\n\t" \ + "lea (%[src],%[src_ystride],2),%[src]\n\t" \ + "movd "_off"(%[ref],%[ref_ystride]),%%mm5\n\t" \ + "lea (%[ref],%[ref_ystride],2),%[ref]\n\t" \ + "movd "_off"(%[src]),%%mm2\n\t" \ + "movd "_off"(%[ref]),%%mm7\n\t" \ + "movd "_off"(%[src],%[src_ystride]),%%mm3\n\t" \ + "movd "_off"(%[ref],%[ref_ystride]),%%mm6\n\t" \ + "punpcklbw %%mm4,%%mm0\n\t" \ + "lea (%[src],%[src_ystride],2),%[src]\n\t" \ + "punpcklbw %%mm4,%%mm4\n\t" \ + "lea (%[ref],%[ref_ystride],2),%[ref]\n\t" \ + "psubw %%mm4,%%mm0\n\t" \ + "movd "_off"(%[src]),%%mm4\n\t" \ + "movq %%mm0,"_off"*2(%[buf])\n\t" \ + "movd "_off"(%[ref]),%%mm0\n\t" \ + "punpcklbw %%mm5,%%mm1\n\t" \ + "punpcklbw %%mm5,%%mm5\n\t" \ + "psubw %%mm5,%%mm1\n\t" \ + "movd "_off"(%[src],%[src_ystride]),%%mm5\n\t" \ + "punpcklbw %%mm7,%%mm2\n\t" \ + "punpcklbw %%mm7,%%mm7\n\t" \ + "psubw %%mm7,%%mm2\n\t" \ + "movd "_off"(%[ref],%[ref_ystride]),%%mm7\n\t" \ + "punpcklbw %%mm6,%%mm3\n\t" \ + "lea (%[src],%[src_ystride],2),%[src]\n\t" \ + "punpcklbw %%mm6,%%mm6\n\t" \ + "psubw %%mm6,%%mm3\n\t" \ + "movd "_off"(%[src]),%%mm6\n\t" \ + "punpcklbw %%mm0,%%mm4\n\t" \ + "lea (%[ref],%[ref_ystride],2),%[ref]\n\t" \ + "punpcklbw %%mm0,%%mm0\n\t" \ + "lea (%[src],%[src_ystride],2),%[src]\n\t" \ + "psubw %%mm0,%%mm4\n\t" \ + "movd "_off"(%[ref]),%%mm0\n\t" \ + "punpcklbw %%mm7,%%mm5\n\t" \ + "neg %[src_ystride]\n\t" \ + "punpcklbw %%mm7,%%mm7\n\t" \ + "psubw %%mm7,%%mm5\n\t" \ + "movd "_off"(%[src],%[src_ystride]),%%mm7\n\t" \ + "punpcklbw %%mm0,%%mm6\n\t" \ + "lea (%[ref],%[ref_ystride],2),%[ref]\n\t" \ + "punpcklbw %%mm0,%%mm0\n\t" \ + "neg %[ref_ystride]\n\t" \ + "psubw %%mm0,%%mm6\n\t" \ + "movd "_off"(%[ref],%[ref_ystride]),%%mm0\n\t" \ + "lea (%[src],%[src_ystride],8),%[src]\n\t" \ + "punpcklbw %%mm0,%%mm7\n\t" \ + "neg %[src_ystride]\n\t" \ + "punpcklbw %%mm0,%%mm0\n\t" \ + "lea (%[ref],%[ref_ystride],8),%[ref]\n\t" \ + "psubw %%mm0,%%mm7\n\t" \ + "neg %[ref_ystride]\n\t" \ + "movq "_off"*2(%[buf]),%%mm0\n\t" \ + +/*Load an 8x4 array of pixel values from %[src] into %%mm0...%%mm7.*/ +#define OC_LOAD_8x4(_off) \ + "#OC_LOAD_8x4\n\t" \ + "movd "_off"(%[src]),%%mm0\n\t" \ + "movd "_off"(%[src],%[ystride]),%%mm1\n\t" \ + "movd "_off"(%[src],%[ystride],2),%%mm2\n\t" \ + "pxor %%mm7,%%mm7\n\t" \ + "movd "_off"(%[src],%[ystride3]),%%mm3\n\t" \ + "punpcklbw %%mm7,%%mm0\n\t" \ + "movd "_off"(%[src4]),%%mm4\n\t" \ + "punpcklbw %%mm7,%%mm1\n\t" \ + "movd "_off"(%[src4],%[ystride]),%%mm5\n\t" \ + "punpcklbw %%mm7,%%mm2\n\t" \ + "movd "_off"(%[src4],%[ystride],2),%%mm6\n\t" \ + "punpcklbw %%mm7,%%mm3\n\t" \ + "movd "_off"(%[src4],%[ystride3]),%%mm7\n\t" \ + "punpcklbw %%mm4,%%mm4\n\t" \ + "punpcklbw %%mm5,%%mm5\n\t" \ + "psrlw $8,%%mm4\n\t" \ + "psrlw $8,%%mm5\n\t" \ + "punpcklbw %%mm6,%%mm6\n\t" \ + "punpcklbw %%mm7,%%mm7\n\t" \ + "psrlw $8,%%mm6\n\t" \ + "psrlw $8,%%mm7\n\t" \ + +/*Performs the first two stages of an 8-point 1-D Hadamard transform. + The transform is performed in place, except that outputs 0-3 are swapped with + outputs 4-7. + Outputs 2, 3, 6 and 7 from the second stage are negated (which allows us to + perform this stage in place with no temporary registers).*/ +#define OC_HADAMARD_AB_8x4 \ + "#OC_HADAMARD_AB_8x4\n\t" \ + /*Stage A: \ + Outputs 0-3 are swapped with 4-7 here.*/ \ + "paddw %%mm1,%%mm5\n\t" \ + "paddw %%mm2,%%mm6\n\t" \ + "paddw %%mm1,%%mm1\n\t" \ + "paddw %%mm2,%%mm2\n\t" \ + "psubw %%mm5,%%mm1\n\t" \ + "psubw %%mm6,%%mm2\n\t" \ + "paddw %%mm3,%%mm7\n\t" \ + "paddw %%mm0,%%mm4\n\t" \ + "paddw %%mm3,%%mm3\n\t" \ + "paddw %%mm0,%%mm0\n\t" \ + "psubw %%mm7,%%mm3\n\t" \ + "psubw %%mm4,%%mm0\n\t" \ + /*Stage B:*/ \ + "paddw %%mm2,%%mm0\n\t" \ + "paddw %%mm3,%%mm1\n\t" \ + "paddw %%mm6,%%mm4\n\t" \ + "paddw %%mm7,%%mm5\n\t" \ + "paddw %%mm2,%%mm2\n\t" \ + "paddw %%mm3,%%mm3\n\t" \ + "paddw %%mm6,%%mm6\n\t" \ + "paddw %%mm7,%%mm7\n\t" \ + "psubw %%mm0,%%mm2\n\t" \ + "psubw %%mm1,%%mm3\n\t" \ + "psubw %%mm4,%%mm6\n\t" \ + "psubw %%mm5,%%mm7\n\t" \ + +/*Performs the last stage of an 8-point 1-D Hadamard transform in place. + Ouputs 1, 3, 5, and 7 are negated (which allows us to perform this stage in + place with no temporary registers).*/ +#define OC_HADAMARD_C_8x4 \ + "#OC_HADAMARD_C_8x4\n\t" \ + /*Stage C:*/ \ + "paddw %%mm1,%%mm0\n\t" \ + "paddw %%mm3,%%mm2\n\t" \ + "paddw %%mm5,%%mm4\n\t" \ + "paddw %%mm7,%%mm6\n\t" \ + "paddw %%mm1,%%mm1\n\t" \ + "paddw %%mm3,%%mm3\n\t" \ + "paddw %%mm5,%%mm5\n\t" \ + "paddw %%mm7,%%mm7\n\t" \ + "psubw %%mm0,%%mm1\n\t" \ + "psubw %%mm2,%%mm3\n\t" \ + "psubw %%mm4,%%mm5\n\t" \ + "psubw %%mm6,%%mm7\n\t" \ + +/*Performs an 8-point 1-D Hadamard transform. + The transform is performed in place, except that outputs 0-3 are swapped with + outputs 4-7. + Outputs 1, 2, 5 and 6 are negated (which allows us to perform the transform + in place with no temporary registers).*/ +#define OC_HADAMARD_8x4 \ + OC_HADAMARD_AB_8x4 \ + OC_HADAMARD_C_8x4 \ + +/*Performs the first part of the final stage of the Hadamard transform and + summing of absolute values. + At the end of this part, %%mm1 will contain the DC coefficient of the + transform.*/ +#define OC_HADAMARD_C_ABS_ACCUM_A_8x4(_r6,_r7) \ + /*We use the fact that \ + (abs(a+b)+abs(a-b))/2=max(abs(a),abs(b)) \ + to merge the final butterfly with the abs and the first stage of \ + accumulation. \ + Thus we can avoid using pabsw, which is not available until SSSE3. \ + Emulating pabsw takes 3 instructions, so the straightforward MMXEXT \ + implementation would be (3+3)*8+7=55 instructions (+4 for spilling \ + registers). \ + Even with pabsw, it would be (3+1)*8+7=39 instructions (with no spills). \ + This implementation is only 26 (+4 for spilling registers).*/ \ + "#OC_HADAMARD_C_ABS_ACCUM_A_8x4\n\t" \ + "movq %%mm7,"_r7"(%[buf])\n\t" \ + "movq %%mm6,"_r6"(%[buf])\n\t" \ + /*mm7={0x7FFF}x4 \ + mm0=max(abs(mm0),abs(mm1))-0x7FFF*/ \ + "pcmpeqb %%mm7,%%mm7\n\t" \ + "movq %%mm0,%%mm6\n\t" \ + "psrlw $1,%%mm7\n\t" \ + "paddw %%mm1,%%mm6\n\t" \ + "pmaxsw %%mm1,%%mm0\n\t" \ + "paddsw %%mm7,%%mm6\n\t" \ + "psubw %%mm6,%%mm0\n\t" \ + /*mm2=max(abs(mm2),abs(mm3))-0x7FFF \ + mm4=max(abs(mm4),abs(mm5))-0x7FFF*/ \ + "movq %%mm2,%%mm6\n\t" \ + "movq %%mm4,%%mm1\n\t" \ + "pmaxsw %%mm3,%%mm2\n\t" \ + "pmaxsw %%mm5,%%mm4\n\t" \ + "paddw %%mm3,%%mm6\n\t" \ + "paddw %%mm5,%%mm1\n\t" \ + "movq "_r7"(%[buf]),%%mm3\n\t" \ + +/*Performs the second part of the final stage of the Hadamard transform and + summing of absolute values.*/ +#define OC_HADAMARD_C_ABS_ACCUM_B_8x4(_r6,_r7) \ + "#OC_HADAMARD_C_ABS_ACCUM_B_8x4\n\t" \ + "paddsw %%mm7,%%mm6\n\t" \ + "movq "_r6"(%[buf]),%%mm5\n\t" \ + "paddsw %%mm7,%%mm1\n\t" \ + "psubw %%mm6,%%mm2\n\t" \ + "psubw %%mm1,%%mm4\n\t" \ + /*mm7={1}x4 (needed for the horizontal add that follows) \ + mm0+=mm2+mm4+max(abs(mm3),abs(mm5))-0x7FFF*/ \ + "movq %%mm3,%%mm6\n\t" \ + "pmaxsw %%mm5,%%mm3\n\t" \ + "paddw %%mm2,%%mm0\n\t" \ + "paddw %%mm5,%%mm6\n\t" \ + "paddw %%mm4,%%mm0\n\t" \ + "paddsw %%mm7,%%mm6\n\t" \ + "paddw %%mm3,%%mm0\n\t" \ + "psrlw $14,%%mm7\n\t" \ + "psubw %%mm6,%%mm0\n\t" \ + +/*Performs the last stage of an 8-point 1-D Hadamard transform, takes the + absolute value of each component, and accumulates everything into mm0. + This is the only portion of SATD which requires MMXEXT (we could use plain + MMX, but it takes 4 instructions and an extra register to work around the + lack of a pmaxsw, which is a pretty serious penalty).*/ +#define OC_HADAMARD_C_ABS_ACCUM_8x4(_r6,_r7) \ + OC_HADAMARD_C_ABS_ACCUM_A_8x4(_r6,_r7) \ + OC_HADAMARD_C_ABS_ACCUM_B_8x4(_r6,_r7) \ + +/*Performs an 8-point 1-D Hadamard transform, takes the absolute value of each + component, and accumulates everything into mm0. + Note that mm0 will have an extra 4 added to each column, and that after + removing this value, the remainder will be half the conventional value.*/ +#define OC_HADAMARD_ABS_ACCUM_8x4(_r6,_r7) \ + OC_HADAMARD_AB_8x4 \ + OC_HADAMARD_C_ABS_ACCUM_8x4(_r6,_r7) + +/*Performs two 4x4 transposes (mostly) in place. + On input, {mm0,mm1,mm2,mm3} contains rows {e,f,g,h}, and {mm4,mm5,mm6,mm7} + contains rows {a,b,c,d}. + On output, {0x40,0x50,0x60,0x70}+_off(%[buf]) contains {e,f,g,h}^T, and + {mm4,mm5,mm6,mm7} contains the transposed rows {a,b,c,d}^T.*/ +#define OC_TRANSPOSE_4x4x2(_off) \ + "#OC_TRANSPOSE_4x4x2\n\t" \ + /*First 4x4 transpose:*/ \ + "movq %%mm5,0x10+"_off"(%[buf])\n\t" \ + /*mm0 = e3 e2 e1 e0 \ + mm1 = f3 f2 f1 f0 \ + mm2 = g3 g2 g1 g0 \ + mm3 = h3 h2 h1 h0*/ \ + "movq %%mm2,%%mm5\n\t" \ + "punpcklwd %%mm3,%%mm2\n\t" \ + "punpckhwd %%mm3,%%mm5\n\t" \ + "movq %%mm0,%%mm3\n\t" \ + "punpcklwd %%mm1,%%mm0\n\t" \ + "punpckhwd %%mm1,%%mm3\n\t" \ + /*mm0 = f1 e1 f0 e0 \ + mm3 = f3 e3 f2 e2 \ + mm2 = h1 g1 h0 g0 \ + mm5 = h3 g3 h2 g2*/ \ + "movq %%mm0,%%mm1\n\t" \ + "punpckldq %%mm2,%%mm0\n\t" \ + "punpckhdq %%mm2,%%mm1\n\t" \ + "movq %%mm3,%%mm2\n\t" \ + "punpckhdq %%mm5,%%mm3\n\t" \ + "movq %%mm0,0x40+"_off"(%[buf])\n\t" \ + "punpckldq %%mm5,%%mm2\n\t" \ + /*mm0 = h0 g0 f0 e0 \ + mm1 = h1 g1 f1 e1 \ + mm2 = h2 g2 f2 e2 \ + mm3 = h3 g3 f3 e3*/ \ + "movq 0x10+"_off"(%[buf]),%%mm5\n\t" \ + /*Second 4x4 transpose:*/ \ + /*mm4 = a3 a2 a1 a0 \ + mm5 = b3 b2 b1 b0 \ + mm6 = c3 c2 c1 c0 \ + mm7 = d3 d2 d1 d0*/ \ + "movq %%mm6,%%mm0\n\t" \ + "punpcklwd %%mm7,%%mm6\n\t" \ + "movq %%mm1,0x50+"_off"(%[buf])\n\t" \ + "punpckhwd %%mm7,%%mm0\n\t" \ + "movq %%mm4,%%mm7\n\t" \ + "punpcklwd %%mm5,%%mm4\n\t" \ + "movq %%mm2,0x60+"_off"(%[buf])\n\t" \ + "punpckhwd %%mm5,%%mm7\n\t" \ + /*mm4 = b1 a1 b0 a0 \ + mm7 = b3 a3 b2 a2 \ + mm6 = d1 c1 d0 c0 \ + mm0 = d3 c3 d2 c2*/ \ + "movq %%mm4,%%mm5\n\t" \ + "punpckldq %%mm6,%%mm4\n\t" \ + "movq %%mm3,0x70+"_off"(%[buf])\n\t" \ + "punpckhdq %%mm6,%%mm5\n\t" \ + "movq %%mm7,%%mm6\n\t" \ + "punpckhdq %%mm0,%%mm7\n\t" \ + "punpckldq %%mm0,%%mm6\n\t" \ + /*mm4 = d0 c0 b0 a0 \ + mm5 = d1 c1 b1 a1 \ + mm6 = d2 c2 b2 a2 \ + mm7 = d3 c3 b3 a3*/ \ + +static unsigned oc_int_frag_satd_thresh_mmxext(const unsigned char *_src, + int _src_ystride,const unsigned char *_ref,int _ref_ystride,unsigned _thresh){ + OC_ALIGN8(ogg_int16_t buf[64]); + ogg_int16_t *bufp; + unsigned ret; + unsigned ret2; + bufp=buf; + __asm__ __volatile__( + OC_LOAD_SUB_8x4("0x00") + OC_HADAMARD_8x4 + OC_TRANSPOSE_4x4x2("0x00") + /*Finish swapping out this 8x4 block to make room for the next one. + mm0...mm3 have been swapped out already.*/ + "movq %%mm4,0x00(%[buf])\n\t" + "movq %%mm5,0x10(%[buf])\n\t" + "movq %%mm6,0x20(%[buf])\n\t" + "movq %%mm7,0x30(%[buf])\n\t" + OC_LOAD_SUB_8x4("0x04") + OC_HADAMARD_8x4 + OC_TRANSPOSE_4x4x2("0x08") + /*Here the first 4x4 block of output from the last transpose is the second + 4x4 block of input for the next transform. + We have cleverly arranged that it already be in the appropriate place, so + we only have to do half the loads.*/ + "movq 0x10(%[buf]),%%mm1\n\t" + "movq 0x20(%[buf]),%%mm2\n\t" + "movq 0x30(%[buf]),%%mm3\n\t" + "movq 0x00(%[buf]),%%mm0\n\t" + OC_HADAMARD_ABS_ACCUM_8x4("0x28","0x38") + /*Up to this point, everything fit in 16 bits (8 input + 1 for the + difference + 2*3 for the two 8-point 1-D Hadamards - 1 for the abs - 1 + for the factor of two we dropped + 3 for the vertical accumulation). + Now we finally have to promote things to dwords. + We break this part out of OC_HADAMARD_ABS_ACCUM_8x4 to hide the long + latency of pmaddwd by starting the next series of loads now.*/ + "mov %[thresh],%[ret2]\n\t" + "pmaddwd %%mm7,%%mm0\n\t" + "movq 0x50(%[buf]),%%mm1\n\t" + "movq 0x58(%[buf]),%%mm5\n\t" + "movq %%mm0,%%mm4\n\t" + "movq 0x60(%[buf]),%%mm2\n\t" + "punpckhdq %%mm0,%%mm0\n\t" + "movq 0x68(%[buf]),%%mm6\n\t" + "paddd %%mm0,%%mm4\n\t" + "movq 0x70(%[buf]),%%mm3\n\t" + "movd %%mm4,%[ret]\n\t" + "movq 0x78(%[buf]),%%mm7\n\t" + /*The sums produced by OC_HADAMARD_ABS_ACCUM_8x4 each have an extra 4 + added to them, and a factor of two removed; correct the final sum here.*/ + "lea -32(%[ret],%[ret]),%[ret]\n\t" + "movq 0x40(%[buf]),%%mm0\n\t" + "cmp %[ret2],%[ret]\n\t" + "movq 0x48(%[buf]),%%mm4\n\t" + "jae 1f\n\t" + OC_HADAMARD_ABS_ACCUM_8x4("0x68","0x78") + "pmaddwd %%mm7,%%mm0\n\t" + /*There isn't much to stick in here to hide the latency this time, but the + alternative to pmaddwd is movq->punpcklwd->punpckhwd->paddd, whose + latency is even worse.*/ + "sub $32,%[ret]\n\t" + "movq %%mm0,%%mm4\n\t" + "punpckhdq %%mm0,%%mm0\n\t" + "paddd %%mm0,%%mm4\n\t" + "movd %%mm4,%[ret2]\n\t" + "lea (%[ret],%[ret2],2),%[ret]\n\t" + ".p2align 4,,15\n\t" + "1:\n\t" + /*Although it looks like we're using 7 registers here, gcc can alias %[ret] + and %[ret2] with some of the inputs, since for once we don't write to + them until after we're done using everything but %[buf] (which is also + listed as an output to ensure gcc _doesn't_ alias them against it).*/ + /*Note that _src_ystride and _ref_ystride must be given non-overlapping + constraints, otherewise if gcc can prove they're equal it will allocate + them to the same register (which is bad); _src and _ref face a similar + problem, though those are never actually the same.*/ + :[ret]"=a"(ret),[ret2]"=r"(ret2),[buf]"+r"(bufp) + :[src]"r"(_src),[src_ystride]"c"((ptrdiff_t)_src_ystride), + [ref]"r"(_ref),[ref_ystride]"d"((ptrdiff_t)_ref_ystride), + [thresh]"m"(_thresh) + /*We have to use neg, so we actually clobber the condition codes for once + (not to mention cmp, sub, and add).*/ + :"cc" + ); + return ret; +} + +unsigned oc_enc_frag_satd_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _thresh){ + return oc_int_frag_satd_thresh_mmxext(_src,_ystride,_ref,_ystride,_thresh); +} + +/*Our internal implementation of frag_copy2 takes an extra stride parameter so + we can share code with oc_enc_frag_satd2_thresh_mmxext().*/ +static void oc_int_frag_copy2_mmxext(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src1,const unsigned char *_src2,int _src_ystride){ + __asm__ __volatile__( + /*Load the first 3 rows.*/ + "movq (%[src1]),%%mm0\n\t" + "movq (%[src2]),%%mm1\n\t" + "movq (%[src1],%[src_ystride]),%%mm2\n\t" + "lea (%[src1],%[src_ystride],2),%[src1]\n\t" + "movq (%[src2],%[src_ystride]),%%mm3\n\t" + "lea (%[src2],%[src_ystride],2),%[src2]\n\t" + "pxor %%mm7,%%mm7\n\t" + "movq (%[src1]),%%mm4\n\t" + "pcmpeqb %%mm6,%%mm6\n\t" + "movq (%[src2]),%%mm5\n\t" + /*mm7={1}x8.*/ + "psubb %%mm6,%%mm7\n\t" + /*Start averaging %%mm0 and %%mm1 into %%mm6.*/ + "movq %%mm0,%%mm6\n\t" + "pxor %%mm1,%%mm0\n\t" + "pavgb %%mm1,%%mm6\n\t" + /*%%mm1 is free, start averaging %%mm3 into %%mm2 using %%mm1.*/ + "movq %%mm2,%%mm1\n\t" + "pand %%mm7,%%mm0\n\t" + "pavgb %%mm3,%%mm2\n\t" + "pxor %%mm3,%%mm1\n\t" + /*%%mm3 is free.*/ + "psubb %%mm0,%%mm6\n\t" + /*%%mm0 is free, start loading the next row.*/ + "movq (%[src1],%[src_ystride]),%%mm0\n\t" + /*Start averaging %%mm5 and %%mm4 using %%mm3.*/ + "movq %%mm4,%%mm3\n\t" + /*%%mm6 (row 0) is done; write it out.*/ + "movq %%mm6,(%[dst])\n\t" + "pand %%mm7,%%mm1\n\t" + "pavgb %%mm5,%%mm4\n\t" + "psubb %%mm1,%%mm2\n\t" + /*%%mm1 is free, continue loading the next row.*/ + "movq (%[src2],%[src_ystride]),%%mm1\n\t" + "pxor %%mm5,%%mm3\n\t" + "lea (%[src1],%[src_ystride],2),%[src1]\n\t" + /*%%mm2 (row 1) is done; write it out.*/ + "movq %%mm2,(%[dst],%[dst_ystride])\n\t" + "pand %%mm7,%%mm3\n\t" + /*Start loading the next row.*/ + "movq (%[src1]),%%mm2\n\t" + "lea (%[dst],%[dst_ystride],2),%[dst]\n\t" + "psubb %%mm3,%%mm4\n\t" + "lea (%[src2],%[src_ystride],2),%[src2]\n\t" + /*%%mm4 (row 2) is done; write it out.*/ + "movq %%mm4,(%[dst])\n\t" + /*Continue loading the next row.*/ + "movq (%[src2]),%%mm3\n\t" + /*Start averaging %%mm0 and %%mm1 into %%mm6.*/ + "movq %%mm0,%%mm6\n\t" + "pxor %%mm1,%%mm0\n\t" + /*Start loading the next row.*/ + "movq (%[src1],%[src_ystride]),%%mm4\n\t" + "pavgb %%mm1,%%mm6\n\t" + /*%%mm1 is free; start averaging %%mm3 into %%mm2 using %%mm1.*/ + "movq %%mm2,%%mm1\n\t" + "pand %%mm7,%%mm0\n\t" + /*Continue loading the next row.*/ + "movq (%[src2],%[src_ystride]),%%mm5\n\t" + "pavgb %%mm3,%%mm2\n\t" + "lea (%[src1],%[src_ystride],2),%[src1]\n\t" + "pxor %%mm3,%%mm1\n\t" + /*%%mm3 is free.*/ + "psubb %%mm0,%%mm6\n\t" + /*%%mm0 is free, start loading the next row.*/ + "movq (%[src1]),%%mm0\n\t" + /*Start averaging %%mm5 into %%mm4 using %%mm3.*/ + "movq %%mm4,%%mm3\n\t" + /*%%mm6 (row 3) is done; write it out.*/ + "movq %%mm6,(%[dst],%[dst_ystride])\n\t" + "pand %%mm7,%%mm1\n\t" + "lea (%[src2],%[src_ystride],2),%[src2]\n\t" + "pavgb %%mm5,%%mm4\n\t" + "lea (%[dst],%[dst_ystride],2),%[dst]\n\t" + "psubb %%mm1,%%mm2\n\t" + /*%%mm1 is free; continue loading the next row.*/ + "movq (%[src2]),%%mm1\n\t" + "pxor %%mm5,%%mm3\n\t" + /*%%mm2 (row 4) is done; write it out.*/ + "movq %%mm2,(%[dst])\n\t" + "pand %%mm7,%%mm3\n\t" + /*Start loading the next row.*/ + "movq (%[src1],%[src_ystride]),%%mm2\n\t" + "psubb %%mm3,%%mm4\n\t" + /*Start averaging %%mm0 and %%mm1 into %%mm6.*/ + "movq %%mm0,%%mm6\n\t" + /*Continue loading the next row.*/ + "movq (%[src2],%[src_ystride]),%%mm3\n\t" + /*%%mm4 (row 5) is done; write it out.*/ + "movq %%mm4,(%[dst],%[dst_ystride])\n\t" + "pxor %%mm1,%%mm0\n\t" + "pavgb %%mm1,%%mm6\n\t" + /*%%mm4 is free; start averaging %%mm3 into %%mm2 using %%mm4.*/ + "movq %%mm2,%%mm4\n\t" + "pand %%mm7,%%mm0\n\t" + "pavgb %%mm3,%%mm2\n\t" + "pxor %%mm3,%%mm4\n\t" + "lea (%[dst],%[dst_ystride],2),%[dst]\n\t" + "psubb %%mm0,%%mm6\n\t" + "pand %%mm7,%%mm4\n\t" + /*%%mm6 (row 6) is done, write it out.*/ + "movq %%mm6,(%[dst])\n\t" + "psubb %%mm4,%%mm2\n\t" + /*%%mm2 (row 7) is done, write it out.*/ + "movq %%mm2,(%[dst],%[dst_ystride])\n\t" + :[dst]"+r"(_dst),[src1]"+%r"(_src1),[src2]"+r"(_src2) + :[dst_ystride]"r"((ptrdiff_t)_dst_ystride), + [src_ystride]"r"((ptrdiff_t)_src_ystride) + :"memory" + ); +} + +unsigned oc_enc_frag_satd2_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride, + unsigned _thresh){ + OC_ALIGN8(unsigned char ref[64]); + oc_int_frag_copy2_mmxext(ref,8,_ref1,_ref2,_ystride); + return oc_int_frag_satd_thresh_mmxext(_src,_ystride,ref,8,_thresh); +} + +unsigned oc_enc_frag_intra_satd_mmxext(const unsigned char *_src, + int _ystride){ + OC_ALIGN8(ogg_int16_t buf[64]); + ogg_int16_t *bufp; + unsigned ret; + unsigned ret2; + bufp=buf; + __asm__ __volatile__( + OC_LOAD_8x4("0x00") + OC_HADAMARD_8x4 + OC_TRANSPOSE_4x4x2("0x00") + /*Finish swapping out this 8x4 block to make room for the next one. + mm0...mm3 have been swapped out already.*/ + "movq %%mm4,0x00(%[buf])\n\t" + "movq %%mm5,0x10(%[buf])\n\t" + "movq %%mm6,0x20(%[buf])\n\t" + "movq %%mm7,0x30(%[buf])\n\t" + OC_LOAD_8x4("0x04") + OC_HADAMARD_8x4 + OC_TRANSPOSE_4x4x2("0x08") + /*Here the first 4x4 block of output from the last transpose is the second + 4x4 block of input for the next transform. + We have cleverly arranged that it already be in the appropriate place, so + we only have to do half the loads.*/ + "movq 0x10(%[buf]),%%mm1\n\t" + "movq 0x20(%[buf]),%%mm2\n\t" + "movq 0x30(%[buf]),%%mm3\n\t" + "movq 0x00(%[buf]),%%mm0\n\t" + /*We split out the stages here so we can save the DC coefficient in the + middle.*/ + OC_HADAMARD_AB_8x4 + OC_HADAMARD_C_ABS_ACCUM_A_8x4("0x28","0x38") + "movd %%mm1,%[ret]\n\t" + OC_HADAMARD_C_ABS_ACCUM_B_8x4("0x28","0x38") + /*Up to this point, everything fit in 16 bits (8 input + 1 for the + difference + 2*3 for the two 8-point 1-D Hadamards - 1 for the abs - 1 + for the factor of two we dropped + 3 for the vertical accumulation). + Now we finally have to promote things to dwords. + We break this part out of OC_HADAMARD_ABS_ACCUM_8x4 to hide the long + latency of pmaddwd by starting the next series of loads now.*/ + "pmaddwd %%mm7,%%mm0\n\t" + "movq 0x50(%[buf]),%%mm1\n\t" + "movq 0x58(%[buf]),%%mm5\n\t" + "movq 0x60(%[buf]),%%mm2\n\t" + "movq %%mm0,%%mm4\n\t" + "movq 0x68(%[buf]),%%mm6\n\t" + "punpckhdq %%mm0,%%mm0\n\t" + "movq 0x70(%[buf]),%%mm3\n\t" + "paddd %%mm0,%%mm4\n\t" + "movq 0x78(%[buf]),%%mm7\n\t" + "movd %%mm4,%[ret2]\n\t" + "movq 0x40(%[buf]),%%mm0\n\t" + "movq 0x48(%[buf]),%%mm4\n\t" + OC_HADAMARD_ABS_ACCUM_8x4("0x68","0x78") + "pmaddwd %%mm7,%%mm0\n\t" + /*We assume that the DC coefficient is always positive (which is true, + because the input to the INTRA transform was not a difference).*/ + "movzx %w[ret],%[ret]\n\t" + "add %[ret2],%[ret2]\n\t" + "sub %[ret],%[ret2]\n\t" + "movq %%mm0,%%mm4\n\t" + "punpckhdq %%mm0,%%mm0\n\t" + "paddd %%mm0,%%mm4\n\t" + "movd %%mm4,%[ret]\n\t" + "lea -64(%[ret2],%[ret],2),%[ret]\n\t" + /*Although it looks like we're using 7 registers here, gcc can alias %[ret] + and %[ret2] with some of the inputs, since for once we don't write to + them until after we're done using everything but %[buf] (which is also + listed as an output to ensure gcc _doesn't_ alias them against it).*/ + :[ret]"=a"(ret),[ret2]"=r"(ret2),[buf]"+r"(bufp) + :[src]"r"(_src),[src4]"r"(_src+4*_ystride), + [ystride]"r"((ptrdiff_t)_ystride),[ystride3]"r"((ptrdiff_t)3*_ystride) + /*We have to use sub, so we actually clobber the condition codes for once + (not to mention add).*/ + :"cc" + ); + return ret; +} + +void oc_enc_frag_sub_mmx(ogg_int16_t _residue[64], + const unsigned char *_src,const unsigned char *_ref,int _ystride){ + int i; + __asm__ __volatile__("pxor %%mm7,%%mm7\n\t"::); + for(i=4;i-->0;){ + __asm__ __volatile__( + /*mm0=[src]*/ + "movq (%[src]),%%mm0\n\t" + /*mm1=[ref]*/ + "movq (%[ref]),%%mm1\n\t" + /*mm4=[src+ystride]*/ + "movq (%[src],%[ystride]),%%mm4\n\t" + /*mm5=[ref+ystride]*/ + "movq (%[ref],%[ystride]),%%mm5\n\t" + /*Compute [src]-[ref].*/ + "movq %%mm0,%%mm2\n\t" + "punpcklbw %%mm7,%%mm0\n\t" + "movq %%mm1,%%mm3\n\t" + "punpckhbw %%mm7,%%mm2\n\t" + "punpcklbw %%mm7,%%mm1\n\t" + "punpckhbw %%mm7,%%mm3\n\t" + "psubw %%mm1,%%mm0\n\t" + "psubw %%mm3,%%mm2\n\t" + /*Compute [src+ystride]-[ref+ystride].*/ + "movq %%mm4,%%mm1\n\t" + "punpcklbw %%mm7,%%mm4\n\t" + "movq %%mm5,%%mm3\n\t" + "punpckhbw %%mm7,%%mm1\n\t" + "lea (%[src],%[ystride],2),%[src]\n\t" + "punpcklbw %%mm7,%%mm5\n\t" + "lea (%[ref],%[ystride],2),%[ref]\n\t" + "punpckhbw %%mm7,%%mm3\n\t" + "psubw %%mm5,%%mm4\n\t" + "psubw %%mm3,%%mm1\n\t" + /*Write the answer out.*/ + "movq %%mm0,0x00(%[residue])\n\t" + "movq %%mm2,0x08(%[residue])\n\t" + "movq %%mm4,0x10(%[residue])\n\t" + "movq %%mm1,0x18(%[residue])\n\t" + "lea 0x20(%[residue]),%[residue]\n\t" + :[residue]"+r"(_residue),[src]"+r"(_src),[ref]"+r"(_ref) + :[ystride]"r"((ptrdiff_t)_ystride) + :"memory" + ); + } +} + +void oc_enc_frag_sub_128_mmx(ogg_int16_t _residue[64], + const unsigned char *_src,int _ystride){ + ptrdiff_t ystride3; + __asm__ __volatile__( + /*mm0=[src]*/ + "movq (%[src]),%%mm0\n\t" + /*mm1=[src+ystride]*/ + "movq (%[src],%[ystride]),%%mm1\n\t" + /*mm6={-1}x4*/ + "pcmpeqw %%mm6,%%mm6\n\t" + /*mm2=[src+2*ystride]*/ + "movq (%[src],%[ystride],2),%%mm2\n\t" + /*[ystride3]=3*[ystride]*/ + "lea (%[ystride],%[ystride],2),%[ystride3]\n\t" + /*mm6={1}x4*/ + "psllw $15,%%mm6\n\t" + /*mm3=[src+3*ystride]*/ + "movq (%[src],%[ystride3]),%%mm3\n\t" + /*mm6={128}x4*/ + "psrlw $8,%%mm6\n\t" + /*mm7=0*/ + "pxor %%mm7,%%mm7\n\t" + /*[src]=[src]+4*[ystride]*/ + "lea (%[src],%[ystride],4),%[src]\n\t" + /*Compute [src]-128 and [src+ystride]-128*/ + "movq %%mm0,%%mm4\n\t" + "punpcklbw %%mm7,%%mm0\n\t" + "movq %%mm1,%%mm5\n\t" + "punpckhbw %%mm7,%%mm4\n\t" + "psubw %%mm6,%%mm0\n\t" + "punpcklbw %%mm7,%%mm1\n\t" + "psubw %%mm6,%%mm4\n\t" + "punpckhbw %%mm7,%%mm5\n\t" + "psubw %%mm6,%%mm1\n\t" + "psubw %%mm6,%%mm5\n\t" + /*Write the answer out.*/ + "movq %%mm0,0x00(%[residue])\n\t" + "movq %%mm4,0x08(%[residue])\n\t" + "movq %%mm1,0x10(%[residue])\n\t" + "movq %%mm5,0x18(%[residue])\n\t" + /*mm0=[src+4*ystride]*/ + "movq (%[src]),%%mm0\n\t" + /*mm1=[src+5*ystride]*/ + "movq (%[src],%[ystride]),%%mm1\n\t" + /*Compute [src+2*ystride]-128 and [src+3*ystride]-128*/ + "movq %%mm2,%%mm4\n\t" + "punpcklbw %%mm7,%%mm2\n\t" + "movq %%mm3,%%mm5\n\t" + "punpckhbw %%mm7,%%mm4\n\t" + "psubw %%mm6,%%mm2\n\t" + "punpcklbw %%mm7,%%mm3\n\t" + "psubw %%mm6,%%mm4\n\t" + "punpckhbw %%mm7,%%mm5\n\t" + "psubw %%mm6,%%mm3\n\t" + "psubw %%mm6,%%mm5\n\t" + /*Write the answer out.*/ + "movq %%mm2,0x20(%[residue])\n\t" + "movq %%mm4,0x28(%[residue])\n\t" + "movq %%mm3,0x30(%[residue])\n\t" + "movq %%mm5,0x38(%[residue])\n\t" + /*mm2=[src+6*ystride]*/ + "movq (%[src],%[ystride],2),%%mm2\n\t" + /*mm3=[src+7*ystride]*/ + "movq (%[src],%[ystride3]),%%mm3\n\t" + /*Compute [src+4*ystride]-128 and [src+5*ystride]-128*/ + "movq %%mm0,%%mm4\n\t" + "punpcklbw %%mm7,%%mm0\n\t" + "movq %%mm1,%%mm5\n\t" + "punpckhbw %%mm7,%%mm4\n\t" + "psubw %%mm6,%%mm0\n\t" + "punpcklbw %%mm7,%%mm1\n\t" + "psubw %%mm6,%%mm4\n\t" + "punpckhbw %%mm7,%%mm5\n\t" + "psubw %%mm6,%%mm1\n\t" + "psubw %%mm6,%%mm5\n\t" + /*Write the answer out.*/ + "movq %%mm0,0x40(%[residue])\n\t" + "movq %%mm4,0x48(%[residue])\n\t" + "movq %%mm1,0x50(%[residue])\n\t" + "movq %%mm5,0x58(%[residue])\n\t" + /*Compute [src+6*ystride]-128 and [src+7*ystride]-128*/ + "movq %%mm2,%%mm4\n\t" + "punpcklbw %%mm7,%%mm2\n\t" + "movq %%mm3,%%mm5\n\t" + "punpckhbw %%mm7,%%mm4\n\t" + "psubw %%mm6,%%mm2\n\t" + "punpcklbw %%mm7,%%mm3\n\t" + "psubw %%mm6,%%mm4\n\t" + "punpckhbw %%mm7,%%mm5\n\t" + "psubw %%mm6,%%mm3\n\t" + "psubw %%mm6,%%mm5\n\t" + /*Write the answer out.*/ + "movq %%mm2,0x60(%[residue])\n\t" + "movq %%mm4,0x68(%[residue])\n\t" + "movq %%mm3,0x70(%[residue])\n\t" + "movq %%mm5,0x78(%[residue])\n\t" + :[src]"+r"(_src),[ystride3]"=&r"(ystride3) + :[residue]"r"(_residue),[ystride]"r"((ptrdiff_t)_ystride) + :"memory" + ); +} + +void oc_enc_frag_copy2_mmxext(unsigned char *_dst, + const unsigned char *_src1,const unsigned char *_src2,int _ystride){ + oc_int_frag_copy2_mmxext(_dst,_ystride,_src1,_src2,_ystride); +} + +#endif diff --git a/Engine/lib/libtheora/lib/x86/mmxfdct.c b/Engine/lib/libtheora/lib/x86/mmxfdct.c new file mode 100644 index 000000000..211875255 --- /dev/null +++ b/Engine/lib/libtheora/lib/x86/mmxfdct.c @@ -0,0 +1,665 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 1999-2006 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ********************************************************************/ +/*MMX fDCT implementation for x86_32*/ +/*$Id: fdct_ses2.c 14579 2008-03-12 06:42:40Z xiphmont $*/ +#include "x86enc.h" + +#if defined(OC_X86_ASM) + +# define OC_FDCT_STAGE1_8x4 \ + "#OC_FDCT_STAGE1_8x4\n\t" \ + /*Stage 1:*/ \ + /*mm0=t7'=t0-t7*/ \ + "psubw %%mm7,%%mm0\n\t" \ + "paddw %%mm7,%%mm7\n\t" \ + /*mm1=t6'=t1-t6*/ \ + "psubw %%mm6,%%mm1\n\t" \ + "paddw %%mm6,%%mm6\n\t" \ + /*mm2=t5'=t2-t5*/ \ + "psubw %%mm5,%%mm2\n\t" \ + "paddw %%mm5,%%mm5\n\t" \ + /*mm3=t4'=t3-t4*/ \ + "psubw %%mm4,%%mm3\n\t" \ + "paddw %%mm4,%%mm4\n\t" \ + /*mm7=t0'=t0+t7*/ \ + "paddw %%mm0,%%mm7\n\t" \ + /*mm6=t1'=t1+t6*/ \ + "paddw %%mm1,%%mm6\n\t" \ + /*mm5=t2'=t2+t5*/ \ + "paddw %%mm2,%%mm5\n\t" \ + /*mm4=t3'=t3+t4*/ \ + "paddw %%mm3,%%mm4\n\t" \ + +# define OC_FDCT8x4(_r0,_r1,_r2,_r3,_r4,_r5,_r6,_r7) \ + "#OC_FDCT8x4\n\t" \ + /*Stage 2:*/ \ + /*mm7=t3''=t0'-t3'*/ \ + "psubw %%mm4,%%mm7\n\t" \ + "paddw %%mm4,%%mm4\n\t" \ + /*mm6=t2''=t1'-t2'*/ \ + "psubw %%mm5,%%mm6\n\t" \ + "movq %%mm7,"_r6"(%[y])\n\t" \ + "paddw %%mm5,%%mm5\n\t" \ + /*mm1=t5''=t6'-t5'*/ \ + "psubw %%mm2,%%mm1\n\t" \ + "movq %%mm6,"_r2"(%[y])\n\t" \ + /*mm4=t0''=t0'+t3'*/ \ + "paddw %%mm7,%%mm4\n\t" \ + "paddw %%mm2,%%mm2\n\t" \ + /*mm5=t1''=t1'+t2'*/ \ + "movq %%mm4,"_r0"(%[y])\n\t" \ + "paddw %%mm6,%%mm5\n\t" \ + /*mm2=t6''=t6'+t5'*/ \ + "paddw %%mm1,%%mm2\n\t" \ + "movq %%mm5,"_r4"(%[y])\n\t" \ + /*mm0=t7', mm1=t5'', mm2=t6'', mm3=t4'.*/ \ + /*mm4, mm5, mm6, mm7 are free.*/ \ + /*Stage 3:*/ \ + /*mm6={2}x4, mm7={27146,0xB500>>1}x2*/ \ + "mov $0x5A806A0A,%[a]\n\t" \ + "pcmpeqb %%mm6,%%mm6\n\t" \ + "movd %[a],%%mm7\n\t" \ + "psrlw $15,%%mm6\n\t" \ + "punpckldq %%mm7,%%mm7\n\t" \ + "paddw %%mm6,%%mm6\n\t" \ + /*mm0=0, m2={-1}x4 \ + mm5:mm4=t5''*27146+0xB500*/ \ + "movq %%mm1,%%mm4\n\t" \ + "movq %%mm1,%%mm5\n\t" \ + "punpcklwd %%mm6,%%mm4\n\t" \ + "movq %%mm2,"_r3"(%[y])\n\t" \ + "pmaddwd %%mm7,%%mm4\n\t" \ + "movq %%mm0,"_r7"(%[y])\n\t" \ + "punpckhwd %%mm6,%%mm5\n\t" \ + "pxor %%mm0,%%mm0\n\t" \ + "pmaddwd %%mm7,%%mm5\n\t" \ + "pcmpeqb %%mm2,%%mm2\n\t" \ + /*mm2=t6'', mm1=t5''+(t5''!=0) \ + mm4=(t5''*27146+0xB500>>16)*/ \ + "pcmpeqw %%mm1,%%mm0\n\t" \ + "psrad $16,%%mm4\n\t" \ + "psubw %%mm2,%%mm0\n\t" \ + "movq "_r3"(%[y]),%%mm2\n\t" \ + "psrad $16,%%mm5\n\t" \ + "paddw %%mm0,%%mm1\n\t" \ + "packssdw %%mm5,%%mm4\n\t" \ + /*mm4=s=(t5''*27146+0xB500>>16)+t5''+(t5''!=0)>>1*/ \ + "paddw %%mm1,%%mm4\n\t" \ + "movq "_r7"(%[y]),%%mm0\n\t" \ + "psraw $1,%%mm4\n\t" \ + "movq %%mm3,%%mm1\n\t" \ + /*mm3=t4''=t4'+s*/ \ + "paddw %%mm4,%%mm3\n\t" \ + /*mm1=t5'''=t4'-s*/ \ + "psubw %%mm4,%%mm1\n\t" \ + /*mm1=0, mm3={-1}x4 \ + mm5:mm4=t6''*27146+0xB500*/ \ + "movq %%mm2,%%mm4\n\t" \ + "movq %%mm2,%%mm5\n\t" \ + "punpcklwd %%mm6,%%mm4\n\t" \ + "movq %%mm1,"_r5"(%[y])\n\t" \ + "pmaddwd %%mm7,%%mm4\n\t" \ + "movq %%mm3,"_r1"(%[y])\n\t" \ + "punpckhwd %%mm6,%%mm5\n\t" \ + "pxor %%mm1,%%mm1\n\t" \ + "pmaddwd %%mm7,%%mm5\n\t" \ + "pcmpeqb %%mm3,%%mm3\n\t" \ + /*mm2=t6''+(t6''!=0), mm4=(t6''*27146+0xB500>>16)*/ \ + "psrad $16,%%mm4\n\t" \ + "pcmpeqw %%mm2,%%mm1\n\t" \ + "psrad $16,%%mm5\n\t" \ + "psubw %%mm3,%%mm1\n\t" \ + "packssdw %%mm5,%%mm4\n\t" \ + "paddw %%mm1,%%mm2\n\t" \ + /*mm1=t1'' \ + mm4=s=(t6''*27146+0xB500>>16)+t6''+(t6''!=0)>>1*/ \ + "paddw %%mm2,%%mm4\n\t" \ + "movq "_r4"(%[y]),%%mm1\n\t" \ + "psraw $1,%%mm4\n\t" \ + "movq %%mm0,%%mm2\n\t" \ + /*mm7={54491-0x7FFF,0x7FFF}x2 \ + mm0=t7''=t7'+s*/ \ + "paddw %%mm4,%%mm0\n\t" \ + /*mm2=t6'''=t7'-s*/ \ + "psubw %%mm4,%%mm2\n\t" \ + /*Stage 4:*/ \ + /*mm0=0, mm2=t0'' \ + mm5:mm4=t1''*27146+0xB500*/ \ + "movq %%mm1,%%mm4\n\t" \ + "movq %%mm1,%%mm5\n\t" \ + "punpcklwd %%mm6,%%mm4\n\t" \ + "movq %%mm2,"_r3"(%[y])\n\t" \ + "pmaddwd %%mm7,%%mm4\n\t" \ + "movq "_r0"(%[y]),%%mm2\n\t" \ + "punpckhwd %%mm6,%%mm5\n\t" \ + "movq %%mm0,"_r7"(%[y])\n\t" \ + "pmaddwd %%mm7,%%mm5\n\t" \ + "pxor %%mm0,%%mm0\n\t" \ + /*mm7={27146,0x4000>>1}x2 \ + mm0=s=(t1''*27146+0xB500>>16)+t1''+(t1''!=0)*/ \ + "psrad $16,%%mm4\n\t" \ + "mov $0x20006A0A,%[a]\n\t" \ + "pcmpeqw %%mm1,%%mm0\n\t" \ + "movd %[a],%%mm7\n\t" \ + "psrad $16,%%mm5\n\t" \ + "psubw %%mm3,%%mm0\n\t" \ + "packssdw %%mm5,%%mm4\n\t" \ + "paddw %%mm1,%%mm0\n\t" \ + "punpckldq %%mm7,%%mm7\n\t" \ + "paddw %%mm4,%%mm0\n\t" \ + /*mm6={0x00000E3D}x2 \ + mm1=-(t0''==0), mm5:mm4=t0''*27146+0x4000*/ \ + "movq %%mm2,%%mm4\n\t" \ + "movq %%mm2,%%mm5\n\t" \ + "punpcklwd %%mm6,%%mm4\n\t" \ + "mov $0x0E3D,%[a]\n\t" \ + "pmaddwd %%mm7,%%mm4\n\t" \ + "punpckhwd %%mm6,%%mm5\n\t" \ + "movd %[a],%%mm6\n\t" \ + "pmaddwd %%mm7,%%mm5\n\t" \ + "pxor %%mm1,%%mm1\n\t" \ + "punpckldq %%mm6,%%mm6\n\t" \ + "pcmpeqw %%mm2,%%mm1\n\t" \ + /*mm4=r=(t0''*27146+0x4000>>16)+t0''+(t0''!=0)*/ \ + "psrad $16,%%mm4\n\t" \ + "psubw %%mm3,%%mm1\n\t" \ + "psrad $16,%%mm5\n\t" \ + "paddw %%mm1,%%mm2\n\t" \ + "packssdw %%mm5,%%mm4\n\t" \ + "movq "_r5"(%[y]),%%mm1\n\t" \ + "paddw %%mm2,%%mm4\n\t" \ + /*mm2=t6'', mm0=_y[0]=u=r+s>>1 \ + The naive implementation could cause overflow, so we use \ + u=(r&s)+((r^s)>>1).*/ \ + "movq "_r3"(%[y]),%%mm2\n\t" \ + "movq %%mm0,%%mm7\n\t" \ + "pxor %%mm4,%%mm0\n\t" \ + "pand %%mm4,%%mm7\n\t" \ + "psraw $1,%%mm0\n\t" \ + "mov $0x7FFF54DC,%[a]\n\t" \ + "paddw %%mm7,%%mm0\n\t" \ + "movd %[a],%%mm7\n\t" \ + /*mm7={54491-0x7FFF,0x7FFF}x2 \ + mm4=_y[4]=v=r-u*/ \ + "psubw %%mm0,%%mm4\n\t" \ + "punpckldq %%mm7,%%mm7\n\t" \ + "movq %%mm4,"_r4"(%[y])\n\t" \ + /*mm0=0, mm7={36410}x4 \ + mm1=(t5'''!=0), mm5:mm4=54491*t5'''+0x0E3D*/ \ + "movq %%mm1,%%mm4\n\t" \ + "movq %%mm1,%%mm5\n\t" \ + "punpcklwd %%mm1,%%mm4\n\t" \ + "mov $0x8E3A8E3A,%[a]\n\t" \ + "pmaddwd %%mm7,%%mm4\n\t" \ + "movq %%mm0,"_r0"(%[y])\n\t" \ + "punpckhwd %%mm1,%%mm5\n\t" \ + "pxor %%mm0,%%mm0\n\t" \ + "pmaddwd %%mm7,%%mm5\n\t" \ + "pcmpeqw %%mm0,%%mm1\n\t" \ + "movd %[a],%%mm7\n\t" \ + "psubw %%mm3,%%mm1\n\t" \ + "punpckldq %%mm7,%%mm7\n\t" \ + "paddd %%mm6,%%mm4\n\t" \ + "paddd %%mm6,%%mm5\n\t" \ + /*mm0=0 \ + mm3:mm1=36410*t6'''+((t5'''!=0)<<16)*/ \ + "movq %%mm2,%%mm6\n\t" \ + "movq %%mm2,%%mm3\n\t" \ + "pmulhw %%mm7,%%mm6\n\t" \ + "paddw %%mm2,%%mm1\n\t" \ + "pmullw %%mm7,%%mm3\n\t" \ + "pxor %%mm0,%%mm0\n\t" \ + "paddw %%mm1,%%mm6\n\t" \ + "movq %%mm3,%%mm1\n\t" \ + "punpckhwd %%mm6,%%mm3\n\t" \ + "punpcklwd %%mm6,%%mm1\n\t" \ + /*mm3={-1}x4, mm6={1}x4 \ + mm4=_y[5]=u=(54491*t5'''+36410*t6'''+0x0E3D>>16)+(t5'''!=0)*/ \ + "paddd %%mm3,%%mm5\n\t" \ + "paddd %%mm1,%%mm4\n\t" \ + "psrad $16,%%mm5\n\t" \ + "pxor %%mm6,%%mm6\n\t" \ + "psrad $16,%%mm4\n\t" \ + "pcmpeqb %%mm3,%%mm3\n\t" \ + "packssdw %%mm5,%%mm4\n\t" \ + "psubw %%mm3,%%mm6\n\t" \ + /*mm1=t7'', mm7={26568,0x3400}x2 \ + mm2=s=t6'''-(36410*u>>16)*/ \ + "movq %%mm4,%%mm1\n\t" \ + "mov $0x340067C8,%[a]\n\t" \ + "pmulhw %%mm7,%%mm4\n\t" \ + "movd %[a],%%mm7\n\t" \ + "movq %%mm1,"_r5"(%[y])\n\t" \ + "punpckldq %%mm7,%%mm7\n\t" \ + "paddw %%mm1,%%mm4\n\t" \ + "movq "_r7"(%[y]),%%mm1\n\t" \ + "psubw %%mm4,%%mm2\n\t" \ + /*mm6={0x00007B1B}x2 \ + mm0=(s!=0), mm5:mm4=s*26568+0x3400*/ \ + "movq %%mm2,%%mm4\n\t" \ + "movq %%mm2,%%mm5\n\t" \ + "punpcklwd %%mm6,%%mm4\n\t" \ + "pcmpeqw %%mm2,%%mm0\n\t" \ + "pmaddwd %%mm7,%%mm4\n\t" \ + "mov $0x7B1B,%[a]\n\t" \ + "punpckhwd %%mm6,%%mm5\n\t" \ + "movd %[a],%%mm6\n\t" \ + "pmaddwd %%mm7,%%mm5\n\t" \ + "psubw %%mm3,%%mm0\n\t" \ + "punpckldq %%mm6,%%mm6\n\t" \ + /*mm7={64277-0x7FFF,0x7FFF}x2 \ + mm2=_y[3]=v=(s*26568+0x3400>>17)+s+(s!=0)*/ \ + "psrad $17,%%mm4\n\t" \ + "paddw %%mm0,%%mm2\n\t" \ + "psrad $17,%%mm5\n\t" \ + "mov $0x7FFF7B16,%[a]\n\t" \ + "packssdw %%mm5,%%mm4\n\t" \ + "movd %[a],%%mm7\n\t" \ + "paddw %%mm4,%%mm2\n\t" \ + "punpckldq %%mm7,%%mm7\n\t" \ + /*mm0=0, mm7={12785}x4 \ + mm1=(t7''!=0), mm2=t4'', mm5:mm4=64277*t7''+0x7B1B*/ \ + "movq %%mm1,%%mm4\n\t" \ + "movq %%mm1,%%mm5\n\t" \ + "movq %%mm2,"_r3"(%[y])\n\t" \ + "punpcklwd %%mm1,%%mm4\n\t" \ + "movq "_r1"(%[y]),%%mm2\n\t" \ + "pmaddwd %%mm7,%%mm4\n\t" \ + "mov $0x31F131F1,%[a]\n\t" \ + "punpckhwd %%mm1,%%mm5\n\t" \ + "pxor %%mm0,%%mm0\n\t" \ + "pmaddwd %%mm7,%%mm5\n\t" \ + "pcmpeqw %%mm0,%%mm1\n\t" \ + "movd %[a],%%mm7\n\t" \ + "psubw %%mm3,%%mm1\n\t" \ + "punpckldq %%mm7,%%mm7\n\t" \ + "paddd %%mm6,%%mm4\n\t" \ + "paddd %%mm6,%%mm5\n\t" \ + /*mm3:mm1=12785*t4'''+((t7''!=0)<<16)*/ \ + "movq %%mm2,%%mm6\n\t" \ + "movq %%mm2,%%mm3\n\t" \ + "pmulhw %%mm7,%%mm6\n\t" \ + "pmullw %%mm7,%%mm3\n\t" \ + "paddw %%mm1,%%mm6\n\t" \ + "movq %%mm3,%%mm1\n\t" \ + "punpckhwd %%mm6,%%mm3\n\t" \ + "punpcklwd %%mm6,%%mm1\n\t" \ + /*mm3={-1}x4, mm6={1}x4 \ + mm4=_y[1]=u=(12785*t4'''+64277*t7''+0x7B1B>>16)+(t7''!=0)*/ \ + "paddd %%mm3,%%mm5\n\t" \ + "paddd %%mm1,%%mm4\n\t" \ + "psrad $16,%%mm5\n\t" \ + "pxor %%mm6,%%mm6\n\t" \ + "psrad $16,%%mm4\n\t" \ + "pcmpeqb %%mm3,%%mm3\n\t" \ + "packssdw %%mm5,%%mm4\n\t" \ + "psubw %%mm3,%%mm6\n\t" \ + /*mm1=t3'', mm7={20539,0x3000}x2 \ + mm4=s=(12785*u>>16)-t4''*/ \ + "movq %%mm4,"_r1"(%[y])\n\t" \ + "pmulhw %%mm7,%%mm4\n\t" \ + "mov $0x3000503B,%[a]\n\t" \ + "movq "_r6"(%[y]),%%mm1\n\t" \ + "movd %[a],%%mm7\n\t" \ + "psubw %%mm2,%%mm4\n\t" \ + "punpckldq %%mm7,%%mm7\n\t" \ + /*mm6={0x00006CB7}x2 \ + mm0=(s!=0), mm5:mm4=s*20539+0x3000*/ \ + "movq %%mm4,%%mm5\n\t" \ + "movq %%mm4,%%mm2\n\t" \ + "punpcklwd %%mm6,%%mm4\n\t" \ + "pcmpeqw %%mm2,%%mm0\n\t" \ + "pmaddwd %%mm7,%%mm4\n\t" \ + "mov $0x6CB7,%[a]\n\t" \ + "punpckhwd %%mm6,%%mm5\n\t" \ + "movd %[a],%%mm6\n\t" \ + "pmaddwd %%mm7,%%mm5\n\t" \ + "psubw %%mm3,%%mm0\n\t" \ + "punpckldq %%mm6,%%mm6\n\t" \ + /*mm7={60547-0x7FFF,0x7FFF}x2 \ + mm2=_y[7]=v=(s*20539+0x3000>>20)+s+(s!=0)*/ \ + "psrad $20,%%mm4\n\t" \ + "paddw %%mm0,%%mm2\n\t" \ + "psrad $20,%%mm5\n\t" \ + "mov $0x7FFF6C84,%[a]\n\t" \ + "packssdw %%mm5,%%mm4\n\t" \ + "movd %[a],%%mm7\n\t" \ + "paddw %%mm4,%%mm2\n\t" \ + "punpckldq %%mm7,%%mm7\n\t" \ + /*mm0=0, mm7={25080}x4 \ + mm2=t2'', mm5:mm4=60547*t3''+0x6CB7*/ \ + "movq %%mm1,%%mm4\n\t" \ + "movq %%mm1,%%mm5\n\t" \ + "movq %%mm2,"_r7"(%[y])\n\t" \ + "punpcklwd %%mm1,%%mm4\n\t" \ + "movq "_r2"(%[y]),%%mm2\n\t" \ + "pmaddwd %%mm7,%%mm4\n\t" \ + "mov $0x61F861F8,%[a]\n\t" \ + "punpckhwd %%mm1,%%mm5\n\t" \ + "pxor %%mm0,%%mm0\n\t" \ + "pmaddwd %%mm7,%%mm5\n\t" \ + "movd %[a],%%mm7\n\t" \ + "pcmpeqw %%mm0,%%mm1\n\t" \ + "psubw %%mm3,%%mm1\n\t" \ + "punpckldq %%mm7,%%mm7\n\t" \ + "paddd %%mm6,%%mm4\n\t" \ + "paddd %%mm6,%%mm5\n\t" \ + /*mm3:mm1=25080*t2''+((t3''!=0)<<16)*/ \ + "movq %%mm2,%%mm6\n\t" \ + "movq %%mm2,%%mm3\n\t" \ + "pmulhw %%mm7,%%mm6\n\t" \ + "pmullw %%mm7,%%mm3\n\t" \ + "paddw %%mm1,%%mm6\n\t" \ + "movq %%mm3,%%mm1\n\t" \ + "punpckhwd %%mm6,%%mm3\n\t" \ + "punpcklwd %%mm6,%%mm1\n\t" \ + /*mm1={-1}x4 \ + mm4=u=(25080*t2''+60547*t3''+0x6CB7>>16)+(t3''!=0)*/ \ + "paddd %%mm3,%%mm5\n\t" \ + "paddd %%mm1,%%mm4\n\t" \ + "psrad $16,%%mm5\n\t" \ + "mov $0x28005460,%[a]\n\t" \ + "psrad $16,%%mm4\n\t" \ + "pcmpeqb %%mm1,%%mm1\n\t" \ + "packssdw %%mm5,%%mm4\n\t" \ + /*mm5={1}x4, mm6=_y[2]=u, mm7={21600,0x2800}x2 \ + mm4=s=(25080*u>>16)-t2''*/ \ + "movq %%mm4,%%mm6\n\t" \ + "pmulhw %%mm7,%%mm4\n\t" \ + "pxor %%mm5,%%mm5\n\t" \ + "movd %[a],%%mm7\n\t" \ + "psubw %%mm1,%%mm5\n\t" \ + "punpckldq %%mm7,%%mm7\n\t" \ + "psubw %%mm2,%%mm4\n\t" \ + /*mm2=s+(s!=0) \ + mm4:mm3=s*21600+0x2800*/ \ + "movq %%mm4,%%mm3\n\t" \ + "movq %%mm4,%%mm2\n\t" \ + "punpckhwd %%mm5,%%mm4\n\t" \ + "pcmpeqw %%mm2,%%mm0\n\t" \ + "pmaddwd %%mm7,%%mm4\n\t" \ + "psubw %%mm1,%%mm0\n\t" \ + "punpcklwd %%mm5,%%mm3\n\t" \ + "paddw %%mm0,%%mm2\n\t" \ + "pmaddwd %%mm7,%%mm3\n\t" \ + /*mm0=_y[4], mm1=_y[7], mm4=_y[0], mm5=_y[5] \ + mm3=_y[6]=v=(s*21600+0x2800>>18)+s+(s!=0)*/ \ + "movq "_r4"(%[y]),%%mm0\n\t" \ + "psrad $18,%%mm4\n\t" \ + "movq "_r5"(%[y]),%%mm5\n\t" \ + "psrad $18,%%mm3\n\t" \ + "movq "_r7"(%[y]),%%mm1\n\t" \ + "packssdw %%mm4,%%mm3\n\t" \ + "movq "_r0"(%[y]),%%mm4\n\t" \ + "paddw %%mm2,%%mm3\n\t" \ + +/*On input, mm4=_y[0], mm6=_y[2], mm0=_y[4], mm5=_y[5], mm3=_y[6], mm1=_y[7]. + On output, {_y[4],mm1,mm2,mm3} contains the transpose of _y[4...7] and + {mm4,mm5,mm6,mm7} contains the transpose of _y[0...3].*/ +# define OC_TRANSPOSE8x4(_r0,_r1,_r2,_r3,_r4,_r5,_r6,_r7) \ + "#OC_TRANSPOSE8x4\n\t" \ + /*First 4x4 transpose:*/ \ + /*mm0 = e3 e2 e1 e0 \ + mm5 = f3 f2 f1 f0 \ + mm3 = g3 g2 g1 g0 \ + mm1 = h3 h2 h1 h0*/ \ + "movq %%mm0,%%mm2\n\t" \ + "punpcklwd %%mm5,%%mm0\n\t" \ + "punpckhwd %%mm5,%%mm2\n\t" \ + "movq %%mm3,%%mm5\n\t" \ + "punpcklwd %%mm1,%%mm3\n\t" \ + "punpckhwd %%mm1,%%mm5\n\t" \ + /*mm0 = f1 e1 f0 e0 \ + mm2 = f3 e3 f2 e2 \ + mm3 = h1 g1 h0 g0 \ + mm5 = h3 g3 h2 g2*/ \ + "movq %%mm0,%%mm1\n\t" \ + "punpckldq %%mm3,%%mm0\n\t" \ + "movq %%mm0,"_r4"(%[y])\n\t" \ + "punpckhdq %%mm3,%%mm1\n\t" \ + "movq "_r1"(%[y]),%%mm0\n\t" \ + "movq %%mm2,%%mm3\n\t" \ + "punpckldq %%mm5,%%mm2\n\t" \ + "punpckhdq %%mm5,%%mm3\n\t" \ + "movq "_r3"(%[y]),%%mm5\n\t" \ + /*_y[4] = h0 g0 f0 e0 \ + mm1 = h1 g1 f1 e1 \ + mm2 = h2 g2 f2 e2 \ + mm3 = h3 g3 f3 e3*/ \ + /*Second 4x4 transpose:*/ \ + /*mm4 = a3 a2 a1 a0 \ + mm0 = b3 b2 b1 b0 \ + mm6 = c3 c2 c1 c0 \ + mm5 = d3 d2 d1 d0*/ \ + "movq %%mm4,%%mm7\n\t" \ + "punpcklwd %%mm0,%%mm4\n\t" \ + "punpckhwd %%mm0,%%mm7\n\t" \ + "movq %%mm6,%%mm0\n\t" \ + "punpcklwd %%mm5,%%mm6\n\t" \ + "punpckhwd %%mm5,%%mm0\n\t" \ + /*mm4 = b1 a1 b0 a0 \ + mm7 = b3 a3 b2 a2 \ + mm6 = d1 c1 d0 c0 \ + mm0 = d3 c3 d2 c2*/ \ + "movq %%mm4,%%mm5\n\t" \ + "punpckldq %%mm6,%%mm4\n\t" \ + "punpckhdq %%mm6,%%mm5\n\t" \ + "movq %%mm7,%%mm6\n\t" \ + "punpckhdq %%mm0,%%mm7\n\t" \ + "punpckldq %%mm0,%%mm6\n\t" \ + /*mm4 = d0 c0 b0 a0 \ + mm5 = d1 c1 b1 a1 \ + mm6 = d2 c2 b2 a2 \ + mm7 = d3 c3 b3 a3*/ \ + +/*MMX implementation of the fDCT.*/ +void oc_enc_fdct8x8_mmx(ogg_int16_t _y[64],const ogg_int16_t _x[64]){ + ptrdiff_t a; + __asm__ __volatile__( + /*Add two extra bits of working precision to improve accuracy; any more and + we could overflow.*/ + /*We also add biases to correct for some systematic error that remains in + the full fDCT->iDCT round trip.*/ + "movq 0x00(%[x]),%%mm0\n\t" + "movq 0x10(%[x]),%%mm1\n\t" + "movq 0x20(%[x]),%%mm2\n\t" + "movq 0x30(%[x]),%%mm3\n\t" + "pcmpeqb %%mm4,%%mm4\n\t" + "pxor %%mm7,%%mm7\n\t" + "movq %%mm0,%%mm5\n\t" + "psllw $2,%%mm0\n\t" + "pcmpeqw %%mm7,%%mm5\n\t" + "movq 0x70(%[x]),%%mm7\n\t" + "psllw $2,%%mm1\n\t" + "psubw %%mm4,%%mm5\n\t" + "psllw $2,%%mm2\n\t" + "mov $1,%[a]\n\t" + "pslld $16,%%mm5\n\t" + "movd %[a],%%mm6\n\t" + "psllq $16,%%mm5\n\t" + "mov $0x10001,%[a]\n\t" + "psllw $2,%%mm3\n\t" + "movd %[a],%%mm4\n\t" + "punpckhwd %%mm6,%%mm5\n\t" + "psubw %%mm6,%%mm1\n\t" + "movq 0x60(%[x]),%%mm6\n\t" + "paddw %%mm5,%%mm0\n\t" + "movq 0x50(%[x]),%%mm5\n\t" + "paddw %%mm4,%%mm0\n\t" + "movq 0x40(%[x]),%%mm4\n\t" + /*We inline stage1 of the transform here so we can get better instruction + scheduling with the shifts.*/ + /*mm0=t7'=t0-t7*/ + "psllw $2,%%mm7\n\t" + "psubw %%mm7,%%mm0\n\t" + "psllw $2,%%mm6\n\t" + "paddw %%mm7,%%mm7\n\t" + /*mm1=t6'=t1-t6*/ + "psllw $2,%%mm5\n\t" + "psubw %%mm6,%%mm1\n\t" + "psllw $2,%%mm4\n\t" + "paddw %%mm6,%%mm6\n\t" + /*mm2=t5'=t2-t5*/ + "psubw %%mm5,%%mm2\n\t" + "paddw %%mm5,%%mm5\n\t" + /*mm3=t4'=t3-t4*/ + "psubw %%mm4,%%mm3\n\t" + "paddw %%mm4,%%mm4\n\t" + /*mm7=t0'=t0+t7*/ + "paddw %%mm0,%%mm7\n\t" + /*mm6=t1'=t1+t6*/ + "paddw %%mm1,%%mm6\n\t" + /*mm5=t2'=t2+t5*/ + "paddw %%mm2,%%mm5\n\t" + /*mm4=t3'=t3+t4*/ + "paddw %%mm3,%%mm4\n\t" + OC_FDCT8x4("0x00","0x10","0x20","0x30","0x40","0x50","0x60","0x70") + OC_TRANSPOSE8x4("0x00","0x10","0x20","0x30","0x40","0x50","0x60","0x70") + /*Swap out this 8x4 block for the next one.*/ + "movq 0x08(%[x]),%%mm0\n\t" + "movq %%mm7,0x30(%[y])\n\t" + "movq 0x78(%[x]),%%mm7\n\t" + "movq %%mm1,0x50(%[y])\n\t" + "movq 0x18(%[x]),%%mm1\n\t" + "movq %%mm6,0x20(%[y])\n\t" + "movq 0x68(%[x]),%%mm6\n\t" + "movq %%mm2,0x60(%[y])\n\t" + "movq 0x28(%[x]),%%mm2\n\t" + "movq %%mm5,0x10(%[y])\n\t" + "movq 0x58(%[x]),%%mm5\n\t" + "movq %%mm3,0x70(%[y])\n\t" + "movq 0x38(%[x]),%%mm3\n\t" + /*And increase its working precision, too.*/ + "psllw $2,%%mm0\n\t" + "movq %%mm4,0x00(%[y])\n\t" + "psllw $2,%%mm7\n\t" + "movq 0x48(%[x]),%%mm4\n\t" + /*We inline stage1 of the transform here so we can get better instruction + scheduling with the shifts.*/ + /*mm0=t7'=t0-t7*/ + "psubw %%mm7,%%mm0\n\t" + "psllw $2,%%mm1\n\t" + "paddw %%mm7,%%mm7\n\t" + "psllw $2,%%mm6\n\t" + /*mm1=t6'=t1-t6*/ + "psubw %%mm6,%%mm1\n\t" + "psllw $2,%%mm2\n\t" + "paddw %%mm6,%%mm6\n\t" + "psllw $2,%%mm5\n\t" + /*mm2=t5'=t2-t5*/ + "psubw %%mm5,%%mm2\n\t" + "psllw $2,%%mm3\n\t" + "paddw %%mm5,%%mm5\n\t" + "psllw $2,%%mm4\n\t" + /*mm3=t4'=t3-t4*/ + "psubw %%mm4,%%mm3\n\t" + "paddw %%mm4,%%mm4\n\t" + /*mm7=t0'=t0+t7*/ + "paddw %%mm0,%%mm7\n\t" + /*mm6=t1'=t1+t6*/ + "paddw %%mm1,%%mm6\n\t" + /*mm5=t2'=t2+t5*/ + "paddw %%mm2,%%mm5\n\t" + /*mm4=t3'=t3+t4*/ + "paddw %%mm3,%%mm4\n\t" + OC_FDCT8x4("0x08","0x18","0x28","0x38","0x48","0x58","0x68","0x78") + OC_TRANSPOSE8x4("0x08","0x18","0x28","0x38","0x48","0x58","0x68","0x78") + /*Here the first 4x4 block of output from the last transpose is the second + 4x4 block of input for the next transform. + We have cleverly arranged that it already be in the appropriate place, + so we only have to do half the stores and loads.*/ + "movq 0x00(%[y]),%%mm0\n\t" + "movq %%mm1,0x58(%[y])\n\t" + "movq 0x10(%[y]),%%mm1\n\t" + "movq %%mm2,0x68(%[y])\n\t" + "movq 0x20(%[y]),%%mm2\n\t" + "movq %%mm3,0x78(%[y])\n\t" + "movq 0x30(%[y]),%%mm3\n\t" + OC_FDCT_STAGE1_8x4 + OC_FDCT8x4("0x00","0x10","0x20","0x30","0x08","0x18","0x28","0x38") + OC_TRANSPOSE8x4("0x00","0x10","0x20","0x30","0x08","0x18","0x28","0x38") + /*mm0={-2}x4*/ + "pcmpeqw %%mm0,%%mm0\n\t" + "paddw %%mm0,%%mm0\n\t" + /*Round the results.*/ + "psubw %%mm0,%%mm1\n\t" + "psubw %%mm0,%%mm2\n\t" + "psraw $2,%%mm1\n\t" + "psubw %%mm0,%%mm3\n\t" + "movq %%mm1,0x18(%[y])\n\t" + "psraw $2,%%mm2\n\t" + "psubw %%mm0,%%mm4\n\t" + "movq 0x08(%[y]),%%mm1\n\t" + "psraw $2,%%mm3\n\t" + "psubw %%mm0,%%mm5\n\t" + "psraw $2,%%mm4\n\t" + "psubw %%mm0,%%mm6\n\t" + "psraw $2,%%mm5\n\t" + "psubw %%mm0,%%mm7\n\t" + "psraw $2,%%mm6\n\t" + "psubw %%mm0,%%mm1\n\t" + "psraw $2,%%mm7\n\t" + "movq 0x40(%[y]),%%mm0\n\t" + "psraw $2,%%mm1\n\t" + "movq %%mm7,0x30(%[y])\n\t" + "movq 0x78(%[y]),%%mm7\n\t" + "movq %%mm1,0x08(%[y])\n\t" + "movq 0x50(%[y]),%%mm1\n\t" + "movq %%mm6,0x20(%[y])\n\t" + "movq 0x68(%[y]),%%mm6\n\t" + "movq %%mm2,0x28(%[y])\n\t" + "movq 0x60(%[y]),%%mm2\n\t" + "movq %%mm5,0x10(%[y])\n\t" + "movq 0x58(%[y]),%%mm5\n\t" + "movq %%mm3,0x38(%[y])\n\t" + "movq 0x70(%[y]),%%mm3\n\t" + "movq %%mm4,0x00(%[y])\n\t" + "movq 0x48(%[y]),%%mm4\n\t" + OC_FDCT_STAGE1_8x4 + OC_FDCT8x4("0x40","0x50","0x60","0x70","0x48","0x58","0x68","0x78") + OC_TRANSPOSE8x4("0x40","0x50","0x60","0x70","0x48","0x58","0x68","0x78") + /*mm0={-2}x4*/ + "pcmpeqw %%mm0,%%mm0\n\t" + "paddw %%mm0,%%mm0\n\t" + /*Round the results.*/ + "psubw %%mm0,%%mm1\n\t" + "psubw %%mm0,%%mm2\n\t" + "psraw $2,%%mm1\n\t" + "psubw %%mm0,%%mm3\n\t" + "movq %%mm1,0x58(%[y])\n\t" + "psraw $2,%%mm2\n\t" + "psubw %%mm0,%%mm4\n\t" + "movq 0x48(%[y]),%%mm1\n\t" + "psraw $2,%%mm3\n\t" + "psubw %%mm0,%%mm5\n\t" + "movq %%mm2,0x68(%[y])\n\t" + "psraw $2,%%mm4\n\t" + "psubw %%mm0,%%mm6\n\t" + "movq %%mm3,0x78(%[y])\n\t" + "psraw $2,%%mm5\n\t" + "psubw %%mm0,%%mm7\n\t" + "movq %%mm4,0x40(%[y])\n\t" + "psraw $2,%%mm6\n\t" + "psubw %%mm0,%%mm1\n\t" + "movq %%mm5,0x50(%[y])\n\t" + "psraw $2,%%mm7\n\t" + "movq %%mm6,0x60(%[y])\n\t" + "psraw $2,%%mm1\n\t" + "movq %%mm7,0x70(%[y])\n\t" + "movq %%mm1,0x48(%[y])\n\t" + :[a]"=&r"(a) + :[y]"r"(_y),[x]"r"(_x) + :"memory" + ); +} + +#endif diff --git a/Engine/lib/libtheora/lib/dec/x86/mmxfrag.c b/Engine/lib/libtheora/lib/x86/mmxfrag.c similarity index 83% rename from Engine/lib/libtheora/lib/dec/x86/mmxfrag.c rename to Engine/lib/libtheora/lib/x86/mmxfrag.c index b4f8167a6..2c732939c 100644 --- a/Engine/lib/libtheora/lib/dec/x86/mmxfrag.c +++ b/Engine/lib/libtheora/lib/x86/mmxfrag.c @@ -5,13 +5,13 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2003 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: mmxfrag.c 15400 2008-10-15 12:10:58Z tterribe $ + last mod: $Id: mmxfrag.c 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ @@ -20,12 +20,20 @@ Additional optimization by Nils Pipenbrinck. Note: Loops are unrolled for best performance. The iteration each instruction belongs to is marked in the comments as #i.*/ -#include "x86int.h" #include +#include "x86int.h" +#include "mmxfrag.h" -#if defined(USE_ASM) +#if defined(OC_X86_ASM) -void oc_frag_recon_intra_mmx(unsigned char *_dst,int _dst_ystride, +/*Copies an 8x8 block of pixels from _src to _dst, assuming _ystride bytes + between rows.*/ +void oc_frag_copy_mmx(unsigned char *_dst, + const unsigned char *_src,int _ystride){ + OC_FRAG_COPY_MMX(_dst,_src,_ystride); +} + +void oc_frag_recon_intra_mmx(unsigned char *_dst,int _ystride, const ogg_int16_t *_residue){ __asm__ __volatile__( /*Set mm0 to 0xFFFFFFFFFFFFFFFF.*/ @@ -67,9 +75,9 @@ void oc_frag_recon_intra_mmx(unsigned char *_dst,int _dst_ystride, /*#0 Write row.*/ "movq %%mm1,(%[dst])\n\t" /*#1 Write row.*/ - "movq %%mm3,(%[dst],%[dst_ystride])\n\t" + "movq %%mm3,(%[dst],%[ystride])\n\t" /*#2 Write row.*/ - "movq %%mm5,(%[dst],%[dst_ystride],2)\n\t" + "movq %%mm5,(%[dst],%[ystride],2)\n\t" /*#3 Load low residue.*/ "movq 6*8(%[residue]),%%mm1\n\t" /*#3 Load high residue.*/ @@ -101,11 +109,11 @@ void oc_frag_recon_intra_mmx(unsigned char *_dst,int _dst_ystride, /*#5 Pack to byte.*/ "packuswb %%mm6,%%mm5\n\t" /*#3 Write row.*/ - "movq %%mm1,(%[dst],%[dst_ystride3])\n\t" + "movq %%mm1,(%[dst],%[ystride3])\n\t" /*#4 Write row.*/ "movq %%mm3,(%[dst4])\n\t" /*#5 Write row.*/ - "movq %%mm5,(%[dst4],%[dst_ystride])\n\t" + "movq %%mm5,(%[dst4],%[ystride])\n\t" /*#6 Load low residue.*/ "movq 12*8(%[residue]),%%mm1\n\t" /*#6 Load high residue.*/ @@ -127,21 +135,21 @@ void oc_frag_recon_intra_mmx(unsigned char *_dst,int _dst_ystride, /*#7 Pack to byte.*/ "packuswb %%mm4,%%mm3\n\t" /*#6 Write row.*/ - "movq %%mm1,(%[dst4],%[dst_ystride],2)\n\t" + "movq %%mm1,(%[dst4],%[ystride],2)\n\t" /*#7 Write row.*/ - "movq %%mm3,(%[dst4],%[dst_ystride3])\n\t" + "movq %%mm3,(%[dst4],%[ystride3])\n\t" : :[residue]"r"(_residue), [dst]"r"(_dst), - [dst4]"r"(_dst+(_dst_ystride<<2)), - [dst_ystride]"r"((ptrdiff_t)_dst_ystride), - [dst_ystride3]"r"((ptrdiff_t)_dst_ystride*3) + [dst4]"r"(_dst+(_ystride<<2)), + [ystride]"r"((ptrdiff_t)_ystride), + [ystride3]"r"((ptrdiff_t)_ystride*3) :"memory" ); } -void oc_frag_recon_inter_mmx(unsigned char *_dst,int _dst_ystride, - const unsigned char *_src,int _src_ystride,const ogg_int16_t *_residue){ +void oc_frag_recon_inter_mmx(unsigned char *_dst,const unsigned char *_src, + int _ystride,const ogg_int16_t *_residue){ int i; /*Zero mm0.*/ __asm__ __volatile__("pxor %%mm0,%%mm0\n\t"::); @@ -150,7 +158,7 @@ void oc_frag_recon_inter_mmx(unsigned char *_dst,int _dst_ystride, /*#0 Load source.*/ "movq (%[src]),%%mm3\n\t" /*#1 Load source.*/ - "movq (%[src],%[src_ystride]),%%mm7\n\t" + "movq (%[src],%[ystride]),%%mm7\n\t" /*#0 Get copy of src.*/ "movq %%mm3,%%mm4\n\t" /*#0 Expand high source.*/ @@ -178,29 +186,23 @@ void oc_frag_recon_inter_mmx(unsigned char *_dst,int _dst_ystride, /*#1 Pack final row pixels.*/ "packuswb %%mm2,%%mm7\n\t" /*Advance src.*/ - "lea (%[src],%[src_ystride],2),%[src]\n\t" + "lea (%[src],%[ystride],2),%[src]\n\t" /*#0 Write row.*/ "movq %%mm3,(%[dst])\n\t" /*#1 Write row.*/ - "movq %%mm7,(%[dst],%[dst_ystride])\n\t" + "movq %%mm7,(%[dst],%[ystride])\n\t" /*Advance dst.*/ - "lea (%[dst],%[dst_ystride],2),%[dst]\n\t" + "lea (%[dst],%[ystride],2),%[dst]\n\t" :[residue]"+r"(_residue),[dst]"+r"(_dst),[src]"+r"(_src) - :[dst_ystride]"r"((ptrdiff_t)_dst_ystride), - [src_ystride]"r"((ptrdiff_t)_src_ystride) + :[ystride]"r"((ptrdiff_t)_ystride) :"memory" ); } } -void oc_frag_recon_inter2_mmx(unsigned char *_dst,int _dst_ystride, - const unsigned char *_src1,int _src1_ystride,const unsigned char *_src2, - int _src2_ystride,const ogg_int16_t *_residue){ +void oc_frag_recon_inter2_mmx(unsigned char *_dst,const unsigned char *_src1, + const unsigned char *_src2,int _ystride,const ogg_int16_t *_residue){ int i; - /*NOTE: This assumes that - _dst_ystride==_src1_ystride&&_dst_ystride==_src2_ystride. - This is currently always the case, but a slower fallback version will need - to be written if it ever is not.*/ /*Zero mm7.*/ __asm__ __volatile__("pxor %%mm7,%%mm7\n\t"::); for(i=4;i-->0;){ @@ -278,8 +280,8 @@ void oc_frag_recon_inter2_mmx(unsigned char *_dst,int _dst_ystride, /*Advance dest ptr.*/ "lea (%[dst],%[ystride],2),%[dst]\n\t" :[dst]"+r"(_dst),[residue]"+r"(_residue), - [src1]"+r"(_src1),[src2]"+r"(_src2) - :[ystride]"r"((ptrdiff_t)_dst_ystride) + [src1]"+%r"(_src1),[src2]"+r"(_src2) + :[ystride]"r"((ptrdiff_t)_ystride) :"memory" ); } diff --git a/Engine/lib/libtheora/lib/x86/mmxfrag.h b/Engine/lib/libtheora/lib/x86/mmxfrag.h new file mode 100644 index 000000000..a39842762 --- /dev/null +++ b/Engine/lib/libtheora/lib/x86/mmxfrag.h @@ -0,0 +1,64 @@ +#if !defined(_x86_mmxfrag_H) +# define _x86_mmxfrag_H (1) +# include +# include "x86int.h" + +#if defined(OC_X86_ASM) + +/*Copies an 8x8 block of pixels from _src to _dst, assuming _ystride bytes + between rows.*/ +#define OC_FRAG_COPY_MMX(_dst,_src,_ystride) \ + do{ \ + const unsigned char *src; \ + unsigned char *dst; \ + ptrdiff_t ystride3; \ + src=(_src); \ + dst=(_dst); \ + __asm__ __volatile__( \ + /*src+0*ystride*/ \ + "movq (%[src]),%%mm0\n\t" \ + /*src+1*ystride*/ \ + "movq (%[src],%[ystride]),%%mm1\n\t" \ + /*ystride3=ystride*3*/ \ + "lea (%[ystride],%[ystride],2),%[ystride3]\n\t" \ + /*src+2*ystride*/ \ + "movq (%[src],%[ystride],2),%%mm2\n\t" \ + /*src+3*ystride*/ \ + "movq (%[src],%[ystride3]),%%mm3\n\t" \ + /*dst+0*ystride*/ \ + "movq %%mm0,(%[dst])\n\t" \ + /*dst+1*ystride*/ \ + "movq %%mm1,(%[dst],%[ystride])\n\t" \ + /*Pointer to next 4.*/ \ + "lea (%[src],%[ystride],4),%[src]\n\t" \ + /*dst+2*ystride*/ \ + "movq %%mm2,(%[dst],%[ystride],2)\n\t" \ + /*dst+3*ystride*/ \ + "movq %%mm3,(%[dst],%[ystride3])\n\t" \ + /*Pointer to next 4.*/ \ + "lea (%[dst],%[ystride],4),%[dst]\n\t" \ + /*src+0*ystride*/ \ + "movq (%[src]),%%mm0\n\t" \ + /*src+1*ystride*/ \ + "movq (%[src],%[ystride]),%%mm1\n\t" \ + /*src+2*ystride*/ \ + "movq (%[src],%[ystride],2),%%mm2\n\t" \ + /*src+3*ystride*/ \ + "movq (%[src],%[ystride3]),%%mm3\n\t" \ + /*dst+0*ystride*/ \ + "movq %%mm0,(%[dst])\n\t" \ + /*dst+1*ystride*/ \ + "movq %%mm1,(%[dst],%[ystride])\n\t" \ + /*dst+2*ystride*/ \ + "movq %%mm2,(%[dst],%[ystride],2)\n\t" \ + /*dst+3*ystride*/ \ + "movq %%mm3,(%[dst],%[ystride3])\n\t" \ + :[dst]"+r"(dst),[src]"+r"(src),[ystride3]"=&r"(ystride3) \ + :[ystride]"r"((ptrdiff_t)(_ystride)) \ + :"memory" \ + ); \ + } \ + while(0) + +# endif +#endif diff --git a/Engine/lib/libtheora/lib/dec/x86/mmxidct.c b/Engine/lib/libtheora/lib/x86/mmxidct.c similarity index 86% rename from Engine/lib/libtheora/lib/dec/x86/mmxidct.c rename to Engine/lib/libtheora/lib/x86/mmxidct.c index 5dbbe201a..76424e636 100644 --- a/Engine/lib/libtheora/lib/dec/x86/mmxidct.c +++ b/Engine/lib/libtheora/lib/x86/mmxidct.c @@ -5,25 +5,22 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: mmxidct.c 15400 2008-10-15 12:10:58Z tterribe $ + last mod: $Id: mmxidct.c 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ /*MMX acceleration of Theora's iDCT. Originally written by Rudolf Marek, based on code from On2's VP3.*/ -#include -#include "../dct.h" -#include "../idct.h" - #include "x86int.h" +#include "../dct.h" -#if defined(USE_ASM) +#if defined(OC_X86_ASM) /*These are offsets into the table of constants below.*/ /*7 rows of cosines, in order: pi/16 * (1 ... 7).*/ @@ -194,7 +191,7 @@ static const ogg_uint16_t __attribute__((aligned(8),used)) J(7) = h3 g3 f3 e3 I(0) I(1) I(2) I(3) is the transpose of r0 I(1) r2 r3. - J(4) J(5) J(6) J(7) is the transpose of r4 r5 r6 r7. + J(4) J(5) J(6) J(7) is the transpose of r4 r5 r6 r7. Since r1 is free at entry, we calculate the Js first.*/ /*19 cycles.*/ @@ -313,9 +310,9 @@ static const ogg_uint16_t __attribute__((aligned(8),used)) #define OC_C(_i) OC_MID(OC_COSINE_OFFSET,_i-1) #define OC_8 OC_MID(OC_EIGHT_OFFSET,0) -void oc_idct8x8_mmx(ogg_int16_t _y[64]){ - /*This routine accepts an 8x8 matrix, but in transposed form. - Every 4x4 submatrix is transposed.*/ +static void oc_idct8x8_slow(ogg_int16_t _y[64]){ + /*This routine accepts an 8x8 matrix, but in partially transposed form. + Every 4x4 block is transposed.*/ __asm__ __volatile__( #define OC_I(_k) OC_M2STR((_k*16))"(%[y])" #define OC_J(_k) OC_M2STR(((_k-4)*16)+8)"(%[y])" @@ -339,7 +336,6 @@ void oc_idct8x8_mmx(ogg_int16_t _y[64]){ OC_COLUMN_IDCT #undef OC_I #undef OC_J - "emms\n\t" : :[y]"r"(_y),[c]"r"(OC_IDCT_CONSTS) ); @@ -507,7 +503,7 @@ void oc_idct8x8_mmx(ogg_int16_t _y[64]){ "movq %%mm0,"OC_I(0)"\n\t" \ "#end OC_COLUMN_IDCT_10\n\t" \ -void oc_idct8x8_10_mmx(ogg_int16_t _y[64]){ +static void oc_idct8x8_10(ogg_int16_t _y[64]){ __asm__ __volatile__( #define OC_I(_k) OC_M2STR((_k*16))"(%[y])" #define OC_J(_k) OC_M2STR(((_k-4)*16)+8)"(%[y])" @@ -527,9 +523,42 @@ void oc_idct8x8_10_mmx(ogg_int16_t _y[64]){ OC_COLUMN_IDCT_10 #undef OC_I #undef OC_J - "emms\n\t" : :[y]"r"(_y),[c]"r"(OC_IDCT_CONSTS) ); } + +/*Performs an inverse 8x8 Type-II DCT transform. + The input is assumed to be scaled by a factor of 4 relative to orthonormal + version of the transform.*/ +void oc_idct8x8_mmx(ogg_int16_t _y[64],int _last_zzi){ + /*_last_zzi is subtly different from an actual count of the number of + coefficients we decoded for this block. + It contains the value of zzi BEFORE the final token in the block was + decoded. + In most cases this is an EOB token (the continuation of an EOB run from a + previous block counts), and so this is the same as the coefficient count. + However, in the case that the last token was NOT an EOB token, but filled + the block up with exactly 64 coefficients, _last_zzi will be less than 64. + Provided the last token was not a pure zero run, the minimum value it can + be is 46, and so that doesn't affect any of the cases in this routine. + However, if the last token WAS a pure zero run of length 63, then _last_zzi + will be 1 while the number of coefficients decoded is 64. + Thus, we will trigger the following special case, where the real + coefficient count would not. + Note also that a zero run of length 64 will give _last_zzi a value of 0, + but we still process the DC coefficient, which might have a non-zero value + due to DC prediction. + Although convoluted, this is arguably the correct behavior: it allows us to + use a smaller transform when the block ends with a long zero run instead + of a normal EOB token. + It could be smarter... multiple separate zero runs at the end of a block + will fool it, but an encoder that generates these really deserves what it + gets. + Needless to say we inherited this approach from VP3.*/ + /*Then perform the iDCT.*/ + if(_last_zzi<10)oc_idct8x8_10(_y); + else oc_idct8x8_slow(_y); +} + #endif diff --git a/Engine/lib/libtheora/lib/x86/mmxloop.h b/Engine/lib/libtheora/lib/x86/mmxloop.h new file mode 100644 index 000000000..2e870c795 --- /dev/null +++ b/Engine/lib/libtheora/lib/x86/mmxloop.h @@ -0,0 +1,215 @@ +#if !defined(_x86_mmxloop_H) +# define _x86_mmxloop_H (1) +# include +# include "x86int.h" + +#if defined(OC_X86_ASM) + +/*On entry, mm0={a0,...,a7}, mm1={b0,...,b7}, mm2={c0,...,c7}, mm3={d0,...d7}. + On exit, mm1={b0+lflim(R_0,L),...,b7+lflim(R_7,L)} and + mm2={c0-lflim(R_0,L),...,c7-lflim(R_7,L)}; mm0 and mm3 are clobbered.*/ +#define OC_LOOP_FILTER8_MMX \ + "#OC_LOOP_FILTER8_MMX\n\t" \ + /*mm7=0*/ \ + "pxor %%mm7,%%mm7\n\t" \ + /*mm6:mm0={a0,...,a7}*/ \ + "movq %%mm0,%%mm6\n\t" \ + "punpcklbw %%mm7,%%mm0\n\t" \ + "punpckhbw %%mm7,%%mm6\n\t" \ + /*mm3:mm5={d0,...,d7}*/ \ + "movq %%mm3,%%mm5\n\t" \ + "punpcklbw %%mm7,%%mm3\n\t" \ + "punpckhbw %%mm7,%%mm5\n\t" \ + /*mm6:mm0={a0-d0,...,a7-d7}*/ \ + "psubw %%mm3,%%mm0\n\t" \ + "psubw %%mm5,%%mm6\n\t" \ + /*mm3:mm1={b0,...,b7}*/ \ + "movq %%mm1,%%mm3\n\t" \ + "punpcklbw %%mm7,%%mm1\n\t" \ + "movq %%mm2,%%mm4\n\t" \ + "punpckhbw %%mm7,%%mm3\n\t" \ + /*mm5:mm4={c0,...,c7}*/ \ + "movq %%mm2,%%mm5\n\t" \ + "punpcklbw %%mm7,%%mm4\n\t" \ + "punpckhbw %%mm7,%%mm5\n\t" \ + /*mm7={3}x4 \ + mm5:mm4={c0-b0,...,c7-b7}*/ \ + "pcmpeqw %%mm7,%%mm7\n\t" \ + "psubw %%mm1,%%mm4\n\t" \ + "psrlw $14,%%mm7\n\t" \ + "psubw %%mm3,%%mm5\n\t" \ + /*Scale by 3.*/ \ + "pmullw %%mm7,%%mm4\n\t" \ + "pmullw %%mm7,%%mm5\n\t" \ + /*mm7={4}x4 \ + mm5:mm4=f={a0-d0+3*(c0-b0),...,a7-d7+3*(c7-b7)}*/ \ + "psrlw $1,%%mm7\n\t" \ + "paddw %%mm0,%%mm4\n\t" \ + "psllw $2,%%mm7\n\t" \ + "movq (%[ll]),%%mm0\n\t" \ + "paddw %%mm6,%%mm5\n\t" \ + /*R_i has the range [-127,128], so we compute -R_i instead. \ + mm4=-R_i=-(f+4>>3)=0xFF^(f-4>>3)*/ \ + "psubw %%mm7,%%mm4\n\t" \ + "psubw %%mm7,%%mm5\n\t" \ + "psraw $3,%%mm4\n\t" \ + "psraw $3,%%mm5\n\t" \ + "pcmpeqb %%mm7,%%mm7\n\t" \ + "packsswb %%mm5,%%mm4\n\t" \ + "pxor %%mm6,%%mm6\n\t" \ + "pxor %%mm7,%%mm4\n\t" \ + "packuswb %%mm3,%%mm1\n\t" \ + /*Now compute lflim of -mm4 cf. Section 7.10 of the sepc.*/ \ + /*There's no unsigned byte+signed byte with unsigned saturation op code, so \ + we have to split things by sign (the other option is to work in 16 bits, \ + but working in 8 bits gives much better parallelism). \ + We compute abs(R_i), but save a mask of which terms were negative in mm6. \ + Then we compute mm4=abs(lflim(R_i,L))=min(abs(R_i),max(2*L-abs(R_i),0)). \ + Finally, we split mm4 into positive and negative pieces using the mask in \ + mm6, and add and subtract them as appropriate.*/ \ + /*mm4=abs(-R_i)*/ \ + /*mm7=255-2*L*/ \ + "pcmpgtb %%mm4,%%mm6\n\t" \ + "psubb %%mm0,%%mm7\n\t" \ + "pxor %%mm6,%%mm4\n\t" \ + "psubb %%mm0,%%mm7\n\t" \ + "psubb %%mm6,%%mm4\n\t" \ + /*mm7=255-max(2*L-abs(R_i),0)*/ \ + "paddusb %%mm4,%%mm7\n\t" \ + /*mm4=min(abs(R_i),max(2*L-abs(R_i),0))*/ \ + "paddusb %%mm7,%%mm4\n\t" \ + "psubusb %%mm7,%%mm4\n\t" \ + /*Now split mm4 by the original sign of -R_i.*/ \ + "movq %%mm4,%%mm5\n\t" \ + "pand %%mm6,%%mm4\n\t" \ + "pandn %%mm5,%%mm6\n\t" \ + /*mm1={b0+lflim(R_0,L),...,b7+lflim(R_7,L)}*/ \ + /*mm2={c0-lflim(R_0,L),...,c7-lflim(R_7,L)}*/ \ + "paddusb %%mm4,%%mm1\n\t" \ + "psubusb %%mm4,%%mm2\n\t" \ + "psubusb %%mm6,%%mm1\n\t" \ + "paddusb %%mm6,%%mm2\n\t" \ + +#define OC_LOOP_FILTER_V_MMX(_pix,_ystride,_ll) \ + do{ \ + ptrdiff_t ystride3__; \ + __asm__ __volatile__( \ + /*mm0={a0,...,a7}*/ \ + "movq (%[pix]),%%mm0\n\t" \ + /*ystride3=_ystride*3*/ \ + "lea (%[ystride],%[ystride],2),%[ystride3]\n\t" \ + /*mm3={d0,...,d7}*/ \ + "movq (%[pix],%[ystride3]),%%mm3\n\t" \ + /*mm1={b0,...,b7}*/ \ + "movq (%[pix],%[ystride]),%%mm1\n\t" \ + /*mm2={c0,...,c7}*/ \ + "movq (%[pix],%[ystride],2),%%mm2\n\t" \ + OC_LOOP_FILTER8_MMX \ + /*Write it back out.*/ \ + "movq %%mm1,(%[pix],%[ystride])\n\t" \ + "movq %%mm2,(%[pix],%[ystride],2)\n\t" \ + :[ystride3]"=&r"(ystride3__) \ + :[pix]"r"(_pix-_ystride*2),[ystride]"r"((ptrdiff_t)(_ystride)), \ + [ll]"r"(_ll) \ + :"memory" \ + ); \ + } \ + while(0) + +#define OC_LOOP_FILTER_H_MMX(_pix,_ystride,_ll) \ + do{ \ + unsigned char *pix__; \ + ptrdiff_t ystride3__; \ + ptrdiff_t d__; \ + pix__=(_pix)-2; \ + __asm__ __volatile__( \ + /*x x x x d0 c0 b0 a0*/ \ + "movd (%[pix]),%%mm0\n\t" \ + /*x x x x d1 c1 b1 a1*/ \ + "movd (%[pix],%[ystride]),%%mm1\n\t" \ + /*ystride3=_ystride*3*/ \ + "lea (%[ystride],%[ystride],2),%[ystride3]\n\t" \ + /*x x x x d2 c2 b2 a2*/ \ + "movd (%[pix],%[ystride],2),%%mm2\n\t" \ + /*x x x x d3 c3 b3 a3*/ \ + "lea (%[pix],%[ystride],4),%[d]\n\t" \ + "movd (%[pix],%[ystride3]),%%mm3\n\t" \ + /*x x x x d4 c4 b4 a4*/ \ + "movd (%[d]),%%mm4\n\t" \ + /*x x x x d5 c5 b5 a5*/ \ + "movd (%[d],%[ystride]),%%mm5\n\t" \ + /*x x x x d6 c6 b6 a6*/ \ + "movd (%[d],%[ystride],2),%%mm6\n\t" \ + /*x x x x d7 c7 b7 a7*/ \ + "movd (%[d],%[ystride3]),%%mm7\n\t" \ + /*mm0=d1 d0 c1 c0 b1 b0 a1 a0*/ \ + "punpcklbw %%mm1,%%mm0\n\t" \ + /*mm2=d3 d2 c3 c2 b3 b2 a3 a2*/ \ + "punpcklbw %%mm3,%%mm2\n\t" \ + /*mm3=d1 d0 c1 c0 b1 b0 a1 a0*/ \ + "movq %%mm0,%%mm3\n\t" \ + /*mm0=b3 b2 b1 b0 a3 a2 a1 a0*/ \ + "punpcklwd %%mm2,%%mm0\n\t" \ + /*mm3=d3 d2 d1 d0 c3 c2 c1 c0*/ \ + "punpckhwd %%mm2,%%mm3\n\t" \ + /*mm1=b3 b2 b1 b0 a3 a2 a1 a0*/ \ + "movq %%mm0,%%mm1\n\t" \ + /*mm4=d5 d4 c5 c4 b5 b4 a5 a4*/ \ + "punpcklbw %%mm5,%%mm4\n\t" \ + /*mm6=d7 d6 c7 c6 b7 b6 a7 a6*/ \ + "punpcklbw %%mm7,%%mm6\n\t" \ + /*mm5=d5 d4 c5 c4 b5 b4 a5 a4*/ \ + "movq %%mm4,%%mm5\n\t" \ + /*mm4=b7 b6 b5 b4 a7 a6 a5 a4*/ \ + "punpcklwd %%mm6,%%mm4\n\t" \ + /*mm5=d7 d6 d5 d4 c7 c6 c5 c4*/ \ + "punpckhwd %%mm6,%%mm5\n\t" \ + /*mm2=d3 d2 d1 d0 c3 c2 c1 c0*/ \ + "movq %%mm3,%%mm2\n\t" \ + /*mm0=a7 a6 a5 a4 a3 a2 a1 a0*/ \ + "punpckldq %%mm4,%%mm0\n\t" \ + /*mm1=b7 b6 b5 b4 b3 b2 b1 b0*/ \ + "punpckhdq %%mm4,%%mm1\n\t" \ + /*mm2=c7 c6 c5 c4 c3 c2 c1 c0*/ \ + "punpckldq %%mm5,%%mm2\n\t" \ + /*mm3=d7 d6 d5 d4 d3 d2 d1 d0*/ \ + "punpckhdq %%mm5,%%mm3\n\t" \ + OC_LOOP_FILTER8_MMX \ + /*mm2={b0+R_0'',...,b7+R_7''}*/ \ + "movq %%mm1,%%mm0\n\t" \ + /*mm1={b0+R_0'',c0-R_0'',...,b3+R_3'',c3-R_3''}*/ \ + "punpcklbw %%mm2,%%mm1\n\t" \ + /*mm2={b4+R_4'',c4-R_4'',...,b7+R_7'',c7-R_7''}*/ \ + "punpckhbw %%mm2,%%mm0\n\t" \ + /*[d]=c1 b1 c0 b0*/ \ + "movd %%mm1,%[d]\n\t" \ + "movw %w[d],1(%[pix])\n\t" \ + "psrlq $32,%%mm1\n\t" \ + "shr $16,%[d]\n\t" \ + "movw %w[d],1(%[pix],%[ystride])\n\t" \ + /*[d]=c3 b3 c2 b2*/ \ + "movd %%mm1,%[d]\n\t" \ + "movw %w[d],1(%[pix],%[ystride],2)\n\t" \ + "shr $16,%[d]\n\t" \ + "movw %w[d],1(%[pix],%[ystride3])\n\t" \ + "lea (%[pix],%[ystride],4),%[pix]\n\t" \ + /*[d]=c5 b5 c4 b4*/ \ + "movd %%mm0,%[d]\n\t" \ + "movw %w[d],1(%[pix])\n\t" \ + "psrlq $32,%%mm0\n\t" \ + "shr $16,%[d]\n\t" \ + "movw %w[d],1(%[pix],%[ystride])\n\t" \ + /*[d]=c7 b7 c6 b6*/ \ + "movd %%mm0,%[d]\n\t" \ + "movw %w[d],1(%[pix],%[ystride],2)\n\t" \ + "shr $16,%[d]\n\t" \ + "movw %w[d],1(%[pix],%[ystride3])\n\t" \ + :[pix]"+r"(pix__),[ystride3]"=&r"(ystride3__),[d]"=&r"(d__) \ + :[ystride]"r"((ptrdiff_t)(_ystride)),[ll]"r"(_ll) \ + :"memory" \ + ); \ + } \ + while(0) + +# endif +#endif diff --git a/Engine/lib/libtheora/lib/x86/mmxstate.c b/Engine/lib/libtheora/lib/x86/mmxstate.c new file mode 100644 index 000000000..808b0a789 --- /dev/null +++ b/Engine/lib/libtheora/lib/x86/mmxstate.c @@ -0,0 +1,188 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: mmxstate.c 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ + +/*MMX acceleration of complete fragment reconstruction algorithm. + Originally written by Rudolf Marek.*/ +#include +#include "x86int.h" +#include "mmxfrag.h" +#include "mmxloop.h" + +#if defined(OC_X86_ASM) + +void oc_state_frag_recon_mmx(const oc_theora_state *_state,ptrdiff_t _fragi, + int _pli,ogg_int16_t _dct_coeffs[64],int _last_zzi,ogg_uint16_t _dc_quant){ + unsigned char *dst; + ptrdiff_t frag_buf_off; + int ystride; + int mb_mode; + /*Apply the inverse transform.*/ + /*Special case only having a DC component.*/ + if(_last_zzi<2){ + /*Note that this value must be unsigned, to keep the __asm__ block from + sign-extending it when it puts it in a register.*/ + ogg_uint16_t p; + /*We round this dequant product (and not any of the others) because there's + no iDCT rounding.*/ + p=(ogg_int16_t)(_dct_coeffs[0]*(ogg_int32_t)_dc_quant+15>>5); + /*Fill _dct_coeffs with p.*/ + __asm__ __volatile__( + /*mm0=0000 0000 0000 AAAA*/ + "movd %[p],%%mm0\n\t" + /*mm0=0000 0000 AAAA AAAA*/ + "punpcklwd %%mm0,%%mm0\n\t" + /*mm0=AAAA AAAA AAAA AAAA*/ + "punpckldq %%mm0,%%mm0\n\t" + "movq %%mm0,(%[y])\n\t" + "movq %%mm0,8(%[y])\n\t" + "movq %%mm0,16(%[y])\n\t" + "movq %%mm0,24(%[y])\n\t" + "movq %%mm0,32(%[y])\n\t" + "movq %%mm0,40(%[y])\n\t" + "movq %%mm0,48(%[y])\n\t" + "movq %%mm0,56(%[y])\n\t" + "movq %%mm0,64(%[y])\n\t" + "movq %%mm0,72(%[y])\n\t" + "movq %%mm0,80(%[y])\n\t" + "movq %%mm0,88(%[y])\n\t" + "movq %%mm0,96(%[y])\n\t" + "movq %%mm0,104(%[y])\n\t" + "movq %%mm0,112(%[y])\n\t" + "movq %%mm0,120(%[y])\n\t" + : + :[y]"r"(_dct_coeffs),[p]"r"((unsigned)p) + :"memory" + ); + } + else{ + /*Dequantize the DC coefficient.*/ + _dct_coeffs[0]=(ogg_int16_t)(_dct_coeffs[0]*(int)_dc_quant); + oc_idct8x8_mmx(_dct_coeffs,_last_zzi); + } + /*Fill in the target buffer.*/ + frag_buf_off=_state->frag_buf_offs[_fragi]; + mb_mode=_state->frags[_fragi].mb_mode; + ystride=_state->ref_ystride[_pli]; + dst=_state->ref_frame_data[_state->ref_frame_idx[OC_FRAME_SELF]]+frag_buf_off; + if(mb_mode==OC_MODE_INTRA)oc_frag_recon_intra_mmx(dst,ystride,_dct_coeffs); + else{ + const unsigned char *ref; + int mvoffsets[2]; + ref= + _state->ref_frame_data[_state->ref_frame_idx[OC_FRAME_FOR_MODE(mb_mode)]] + +frag_buf_off; + if(oc_state_get_mv_offsets(_state,mvoffsets,_pli, + _state->frag_mvs[_fragi][0],_state->frag_mvs[_fragi][1])>1){ + oc_frag_recon_inter2_mmx(dst,ref+mvoffsets[0],ref+mvoffsets[1],ystride, + _dct_coeffs); + } + else oc_frag_recon_inter_mmx(dst,ref+mvoffsets[0],ystride,_dct_coeffs); + } +} + +/*We copy these entire function to inline the actual MMX routines so that we + use only a single indirect call.*/ + +/*Copies the fragments specified by the lists of fragment indices from one + frame to another. + _fragis: A pointer to a list of fragment indices. + _nfragis: The number of fragment indices to copy. + _dst_frame: The reference frame to copy to. + _src_frame: The reference frame to copy from. + _pli: The color plane the fragments lie in.*/ +void oc_state_frag_copy_list_mmx(const oc_theora_state *_state, + const ptrdiff_t *_fragis,ptrdiff_t _nfragis, + int _dst_frame,int _src_frame,int _pli){ + const ptrdiff_t *frag_buf_offs; + const unsigned char *src_frame_data; + unsigned char *dst_frame_data; + ptrdiff_t fragii; + int ystride; + dst_frame_data=_state->ref_frame_data[_state->ref_frame_idx[_dst_frame]]; + src_frame_data=_state->ref_frame_data[_state->ref_frame_idx[_src_frame]]; + ystride=_state->ref_ystride[_pli]; + frag_buf_offs=_state->frag_buf_offs; + for(fragii=0;fragii<_nfragis;fragii++){ + ptrdiff_t frag_buf_off; + frag_buf_off=frag_buf_offs[_fragis[fragii]]; + OC_FRAG_COPY_MMX(dst_frame_data+frag_buf_off, + src_frame_data+frag_buf_off,ystride); + } +} + +/*Apply the loop filter to a given set of fragment rows in the given plane. + The filter may be run on the bottom edge, affecting pixels in the next row of + fragments, so this row also needs to be available. + _bv: The bounding values array. + _refi: The index of the frame buffer to filter. + _pli: The color plane to filter. + _fragy0: The Y coordinate of the first fragment row to filter. + _fragy_end: The Y coordinate of the fragment row to stop filtering at.*/ +void oc_state_loop_filter_frag_rows_mmx(const oc_theora_state *_state, + int _bv[256],int _refi,int _pli,int _fragy0,int _fragy_end){ + OC_ALIGN8(unsigned char ll[8]); + const oc_fragment_plane *fplane; + const oc_fragment *frags; + const ptrdiff_t *frag_buf_offs; + unsigned char *ref_frame_data; + ptrdiff_t fragi_top; + ptrdiff_t fragi_bot; + ptrdiff_t fragi0; + ptrdiff_t fragi0_end; + int ystride; + int nhfrags; + memset(ll,_state->loop_filter_limits[_state->qis[0]],sizeof(ll)); + fplane=_state->fplanes+_pli; + nhfrags=fplane->nhfrags; + fragi_top=fplane->froffset; + fragi_bot=fragi_top+fplane->nfrags; + fragi0=fragi_top+_fragy0*(ptrdiff_t)nhfrags; + fragi0_end=fragi0+(_fragy_end-_fragy0)*(ptrdiff_t)nhfrags; + ystride=_state->ref_ystride[_pli]; + frags=_state->frags; + frag_buf_offs=_state->frag_buf_offs; + ref_frame_data=_state->ref_frame_data[_refi]; + /*The following loops are constructed somewhat non-intuitively on purpose. + The main idea is: if a block boundary has at least one coded fragment on + it, the filter is applied to it. + However, the order that the filters are applied in matters, and VP3 chose + the somewhat strange ordering used below.*/ + while(fragi0fragi0)OC_LOOP_FILTER_H_MMX(ref,ystride,ll); + if(fragi0>fragi_top)OC_LOOP_FILTER_V_MMX(ref,ystride,ll); + if(fragi+1 +#include "x86enc.h" + +#if defined(OC_X86_64_ASM) + +# define OC_FDCT8x8 \ + /*Note: xmm15={0}x8 and xmm14={-1}x8.*/ \ + "#OC_FDCT8x8\n\t" \ + /*Stage 1:*/ \ + "movdqa %%xmm0,%%xmm11\n\t" \ + "movdqa %%xmm1,%%xmm10\n\t" \ + "movdqa %%xmm2,%%xmm9\n\t" \ + "movdqa %%xmm3,%%xmm8\n\t" \ + /*xmm11=t7'=t0-t7*/ \ + "psubw %%xmm7,%%xmm11\n\t" \ + /*xmm10=t6'=t1-t6*/ \ + "psubw %%xmm6,%%xmm10\n\t" \ + /*xmm9=t5'=t2-t5*/ \ + "psubw %%xmm5,%%xmm9\n\t" \ + /*xmm8=t4'=t3-t4*/ \ + "psubw %%xmm4,%%xmm8\n\t" \ + /*xmm0=t0'=t0+t7*/ \ + "paddw %%xmm7,%%xmm0\n\t" \ + /*xmm1=t1'=t1+t6*/ \ + "paddw %%xmm6,%%xmm1\n\t" \ + /*xmm5=t2'=t2+t5*/ \ + "paddw %%xmm2,%%xmm5\n\t" \ + /*xmm4=t3'=t3+t4*/ \ + "paddw %%xmm3,%%xmm4\n\t" \ + /*xmm2,3,6,7 are now free.*/ \ + /*Stage 2:*/ \ + "movdqa %%xmm0,%%xmm3\n\t" \ + "mov $0x5A806A0A,%[a]\n\t" \ + "movdqa %%xmm1,%%xmm2\n\t" \ + "movd %[a],%%xmm13\n\t" \ + "movdqa %%xmm10,%%xmm6\n\t" \ + "pshufd $00,%%xmm13,%%xmm13\n\t" \ + /*xmm2=t2''=t1'-t2'*/ \ + "psubw %%xmm5,%%xmm2\n\t" \ + "pxor %%xmm12,%%xmm12\n\t" \ + /*xmm3=t3''=t0'-t3'*/ \ + "psubw %%xmm4,%%xmm3\n\t" \ + "psubw %%xmm14,%%xmm12\n\t" \ + /*xmm10=t5''=t6'-t5'*/ \ + "psubw %%xmm9,%%xmm10\n\t" \ + "paddw %%xmm12,%%xmm12\n\t" \ + /*xmm4=t0''=t0'+t3'*/ \ + "paddw %%xmm0,%%xmm4\n\t" \ + /*xmm1=t1''=t1'+t2'*/ \ + "paddw %%xmm5,%%xmm1\n\t" \ + /*xmm6=t6''=t6'+t5'*/ \ + "paddw %%xmm9,%%xmm6\n\t" \ + /*xmm0,xmm5,xmm9 are now free.*/ \ + /*Stage 3:*/ \ + /*xmm10:xmm5=t5''*27146+0xB500 \ + xmm0=t5''*/ \ + "movdqa %%xmm10,%%xmm5\n\t" \ + "movdqa %%xmm10,%%xmm0\n\t" \ + "punpckhwd %%xmm12,%%xmm10\n\t" \ + "pmaddwd %%xmm13,%%xmm10\n\t" \ + "punpcklwd %%xmm12,%%xmm5\n\t" \ + "pmaddwd %%xmm13,%%xmm5\n\t" \ + /*xmm5=(t5''*27146+0xB500>>16)+t5''*/ \ + "psrad $16,%%xmm10\n\t" \ + "psrad $16,%%xmm5\n\t" \ + "packssdw %%xmm10,%%xmm5\n\t" \ + "paddw %%xmm0,%%xmm5\n\t" \ + /*xmm0=s=(t5''*27146+0xB500>>16)+t5''+(t5''!=0)>>1*/ \ + "pcmpeqw %%xmm15,%%xmm0\n\t" \ + "psubw %%xmm14,%%xmm0\n\t" \ + "paddw %%xmm5,%%xmm0\n\t" \ + "movdqa %%xmm8,%%xmm5\n\t" \ + "psraw $1,%%xmm0\n\t" \ + /*xmm5=t5'''=t4'-s*/ \ + "psubw %%xmm0,%%xmm5\n\t" \ + /*xmm8=t4''=t4'+s*/ \ + "paddw %%xmm0,%%xmm8\n\t" \ + /*xmm0,xmm7,xmm9,xmm10 are free.*/ \ + /*xmm7:xmm9=t6''*27146+0xB500*/ \ + "movdqa %%xmm6,%%xmm7\n\t" \ + "movdqa %%xmm6,%%xmm9\n\t" \ + "punpckhwd %%xmm12,%%xmm7\n\t" \ + "pmaddwd %%xmm13,%%xmm7\n\t" \ + "punpcklwd %%xmm12,%%xmm9\n\t" \ + "pmaddwd %%xmm13,%%xmm9\n\t" \ + /*xmm9=(t6''*27146+0xB500>>16)+t6''*/ \ + "psrad $16,%%xmm7\n\t" \ + "psrad $16,%%xmm9\n\t" \ + "packssdw %%xmm7,%%xmm9\n\t" \ + "paddw %%xmm6,%%xmm9\n\t" \ + /*xmm9=s=(t6''*27146+0xB500>>16)+t6''+(t6''!=0)>>1*/ \ + "pcmpeqw %%xmm15,%%xmm6\n\t" \ + "psubw %%xmm14,%%xmm6\n\t" \ + "paddw %%xmm6,%%xmm9\n\t" \ + "movdqa %%xmm11,%%xmm7\n\t" \ + "psraw $1,%%xmm9\n\t" \ + /*xmm7=t6'''=t7'-s*/ \ + "psubw %%xmm9,%%xmm7\n\t" \ + /*xmm9=t7''=t7'+s*/ \ + "paddw %%xmm11,%%xmm9\n\t" \ + /*xmm0,xmm6,xmm10,xmm11 are free.*/ \ + /*Stage 4:*/ \ + /*xmm10:xmm0=t1''*27146+0xB500*/ \ + "movdqa %%xmm1,%%xmm0\n\t" \ + "movdqa %%xmm1,%%xmm10\n\t" \ + "punpcklwd %%xmm12,%%xmm0\n\t" \ + "pmaddwd %%xmm13,%%xmm0\n\t" \ + "punpckhwd %%xmm12,%%xmm10\n\t" \ + "pmaddwd %%xmm13,%%xmm10\n\t" \ + /*xmm0=(t1''*27146+0xB500>>16)+t1''*/ \ + "psrad $16,%%xmm0\n\t" \ + "psrad $16,%%xmm10\n\t" \ + "mov $0x20006A0A,%[a]\n\t" \ + "packssdw %%xmm10,%%xmm0\n\t" \ + "movd %[a],%%xmm13\n\t" \ + "paddw %%xmm1,%%xmm0\n\t" \ + /*xmm0=s=(t1''*27146+0xB500>>16)+t1''+(t1''!=0)*/ \ + "pcmpeqw %%xmm15,%%xmm1\n\t" \ + "pshufd $00,%%xmm13,%%xmm13\n\t" \ + "psubw %%xmm14,%%xmm1\n\t" \ + "paddw %%xmm1,%%xmm0\n\t" \ + /*xmm10:xmm4=t0''*27146+0x4000*/ \ + "movdqa %%xmm4,%%xmm1\n\t" \ + "movdqa %%xmm4,%%xmm10\n\t" \ + "punpcklwd %%xmm12,%%xmm4\n\t" \ + "pmaddwd %%xmm13,%%xmm4\n\t" \ + "punpckhwd %%xmm12,%%xmm10\n\t" \ + "pmaddwd %%xmm13,%%xmm10\n\t" \ + /*xmm4=(t0''*27146+0x4000>>16)+t0''*/ \ + "psrad $16,%%xmm4\n\t" \ + "psrad $16,%%xmm10\n\t" \ + "mov $0x6CB7,%[a]\n\t" \ + "packssdw %%xmm10,%%xmm4\n\t" \ + "movd %[a],%%xmm12\n\t" \ + "paddw %%xmm1,%%xmm4\n\t" \ + /*xmm4=r=(t0''*27146+0x4000>>16)+t0''+(t0''!=0)*/ \ + "pcmpeqw %%xmm15,%%xmm1\n\t" \ + "pshufd $00,%%xmm12,%%xmm12\n\t" \ + "psubw %%xmm14,%%xmm1\n\t" \ + "mov $0x7FFF6C84,%[a]\n\t" \ + "paddw %%xmm1,%%xmm4\n\t" \ + /*xmm0=_y[0]=u=r+s>>1 \ + The naive implementation could cause overflow, so we use \ + u=(r&s)+((r^s)>>1).*/ \ + "movdqa %%xmm0,%%xmm6\n\t" \ + "pxor %%xmm4,%%xmm0\n\t" \ + "pand %%xmm4,%%xmm6\n\t" \ + "psraw $1,%%xmm0\n\t" \ + "movd %[a],%%xmm13\n\t" \ + "paddw %%xmm6,%%xmm0\n\t" \ + /*xmm4=_y[4]=v=r-u*/ \ + "pshufd $00,%%xmm13,%%xmm13\n\t" \ + "psubw %%xmm0,%%xmm4\n\t" \ + /*xmm1,xmm6,xmm10,xmm11 are free.*/ \ + /*xmm6:xmm10=60547*t3''+0x6CB7*/ \ + "movdqa %%xmm3,%%xmm10\n\t" \ + "movdqa %%xmm3,%%xmm6\n\t" \ + "punpcklwd %%xmm3,%%xmm10\n\t" \ + "pmaddwd %%xmm13,%%xmm10\n\t" \ + "mov $0x61F861F8,%[a]\n\t" \ + "punpckhwd %%xmm3,%%xmm6\n\t" \ + "pmaddwd %%xmm13,%%xmm6\n\t" \ + "movd %[a],%%xmm13\n\t" \ + "paddd %%xmm12,%%xmm10\n\t" \ + "pshufd $00,%%xmm13,%%xmm13\n\t" \ + "paddd %%xmm12,%%xmm6\n\t" \ + /*xmm1:xmm2=25080*t2'' \ + xmm12=t2''*/ \ + "movdqa %%xmm2,%%xmm11\n\t" \ + "movdqa %%xmm2,%%xmm12\n\t" \ + "pmullw %%xmm13,%%xmm2\n\t" \ + "pmulhw %%xmm13,%%xmm11\n\t" \ + "movdqa %%xmm2,%%xmm1\n\t" \ + "punpcklwd %%xmm11,%%xmm2\n\t" \ + "punpckhwd %%xmm11,%%xmm1\n\t" \ + /*xmm10=u=(25080*t2''+60547*t3''+0x6CB7>>16)+(t3''!=0)*/ \ + "paddd %%xmm2,%%xmm10\n\t" \ + "paddd %%xmm1,%%xmm6\n\t" \ + "psrad $16,%%xmm10\n\t" \ + "pcmpeqw %%xmm15,%%xmm3\n\t" \ + "psrad $16,%%xmm6\n\t" \ + "psubw %%xmm14,%%xmm3\n\t" \ + "packssdw %%xmm6,%%xmm10\n\t" \ + "paddw %%xmm3,%%xmm10\n\t" \ + /*xmm2=_y[2]=u \ + xmm10=s=(25080*u>>16)-t2''*/ \ + "movdqa %%xmm10,%%xmm2\n\t" \ + "pmulhw %%xmm13,%%xmm10\n\t" \ + "psubw %%xmm12,%%xmm10\n\t" \ + /*xmm1:xmm6=s*21600+0x2800*/ \ + "pxor %%xmm12,%%xmm12\n\t" \ + "psubw %%xmm14,%%xmm12\n\t" \ + "mov $0x28005460,%[a]\n\t" \ + "movd %[a],%%xmm13\n\t" \ + "pshufd $00,%%xmm13,%%xmm13\n\t" \ + "movdqa %%xmm10,%%xmm6\n\t" \ + "movdqa %%xmm10,%%xmm1\n\t" \ + "punpcklwd %%xmm12,%%xmm6\n\t" \ + "pmaddwd %%xmm13,%%xmm6\n\t" \ + "mov $0x0E3D,%[a]\n\t" \ + "punpckhwd %%xmm12,%%xmm1\n\t" \ + "pmaddwd %%xmm13,%%xmm1\n\t" \ + /*xmm6=(s*21600+0x2800>>18)+s*/ \ + "psrad $18,%%xmm6\n\t" \ + "psrad $18,%%xmm1\n\t" \ + "movd %[a],%%xmm12\n\t" \ + "packssdw %%xmm1,%%xmm6\n\t" \ + "pshufd $00,%%xmm12,%%xmm12\n\t" \ + "paddw %%xmm10,%%xmm6\n\t" \ + /*xmm6=_y[6]=v=(s*21600+0x2800>>18)+s+(s!=0)*/ \ + "mov $0x7FFF54DC,%[a]\n\t" \ + "pcmpeqw %%xmm15,%%xmm10\n\t" \ + "movd %[a],%%xmm13\n\t" \ + "psubw %%xmm14,%%xmm10\n\t" \ + "pshufd $00,%%xmm13,%%xmm13\n\t" \ + "paddw %%xmm10,%%xmm6\n\t " \ + /*xmm1,xmm3,xmm10,xmm11 are free.*/ \ + /*xmm11:xmm10=54491*t5'''+0x0E3D*/ \ + "movdqa %%xmm5,%%xmm10\n\t" \ + "movdqa %%xmm5,%%xmm11\n\t" \ + "punpcklwd %%xmm5,%%xmm10\n\t" \ + "pmaddwd %%xmm13,%%xmm10\n\t" \ + "mov $0x8E3A8E3A,%[a]\n\t" \ + "punpckhwd %%xmm5,%%xmm11\n\t" \ + "pmaddwd %%xmm13,%%xmm11\n\t" \ + "movd %[a],%%xmm13\n\t" \ + "paddd %%xmm12,%%xmm10\n\t" \ + "pshufd $00,%%xmm13,%%xmm13\n\t" \ + "paddd %%xmm12,%%xmm11\n\t" \ + /*xmm7:xmm12=36410*t6''' \ + xmm1=t6'''*/ \ + "movdqa %%xmm7,%%xmm3\n\t" \ + "movdqa %%xmm7,%%xmm1\n\t" \ + "pmulhw %%xmm13,%%xmm3\n\t" \ + "pmullw %%xmm13,%%xmm7\n\t" \ + "paddw %%xmm1,%%xmm3\n\t" \ + "movdqa %%xmm7,%%xmm12\n\t" \ + "punpckhwd %%xmm3,%%xmm7\n\t" \ + "punpcklwd %%xmm3,%%xmm12\n\t" \ + /*xmm10=u=(54491*t5'''+36410*t6'''+0x0E3D>>16)+(t5'''!=0)*/ \ + "paddd %%xmm12,%%xmm10\n\t" \ + "paddd %%xmm7,%%xmm11\n\t" \ + "psrad $16,%%xmm10\n\t" \ + "pcmpeqw %%xmm15,%%xmm5\n\t" \ + "psrad $16,%%xmm11\n\t" \ + "psubw %%xmm14,%%xmm5\n\t" \ + "packssdw %%xmm11,%%xmm10\n\t" \ + "pxor %%xmm12,%%xmm12\n\t" \ + "paddw %%xmm5,%%xmm10\n\t" \ + /*xmm5=_y[5]=u \ + xmm1=s=t6'''-(36410*u>>16)*/ \ + "psubw %%xmm14,%%xmm12\n\t" \ + "movdqa %%xmm10,%%xmm5\n\t" \ + "mov $0x340067C8,%[a]\n\t" \ + "pmulhw %%xmm13,%%xmm10\n\t" \ + "movd %[a],%%xmm13\n\t" \ + "paddw %%xmm5,%%xmm10\n\t" \ + "pshufd $00,%%xmm13,%%xmm13\n\t" \ + "psubw %%xmm10,%%xmm1\n\t" \ + /*xmm11:xmm3=s*26568+0x3400*/ \ + "movdqa %%xmm1,%%xmm3\n\t" \ + "movdqa %%xmm1,%%xmm11\n\t" \ + "punpcklwd %%xmm12,%%xmm3\n\t" \ + "pmaddwd %%xmm13,%%xmm3\n\t" \ + "mov $0x7B1B,%[a]\n\t" \ + "punpckhwd %%xmm12,%%xmm11\n\t" \ + "pmaddwd %%xmm13,%%xmm11\n\t" \ + /*xmm3=(s*26568+0x3400>>17)+s*/ \ + "psrad $17,%%xmm3\n\t" \ + "psrad $17,%%xmm11\n\t" \ + "movd %[a],%%xmm12\n\t" \ + "packssdw %%xmm11,%%xmm3\n\t" \ + "pshufd $00,%%xmm12,%%xmm12\n\t" \ + "paddw %%xmm1,%%xmm3\n\t" \ + /*xmm3=_y[3]=v=(s*26568+0x3400>>17)+s+(s!=0)*/ \ + "mov $0x7FFF7B16,%[a]\n\t" \ + "pcmpeqw %%xmm15,%%xmm1\n\t" \ + "movd %[a],%%xmm13\n\t" \ + "psubw %%xmm14,%%xmm1\n\t" \ + "pshufd $00,%%xmm13,%%xmm13\n\t" \ + "paddw %%xmm1,%%xmm3\n\t " \ + /*xmm1,xmm7,xmm10,xmm11 are free.*/ \ + /*xmm11:xmm10=64277*t7''+0x7B1B*/ \ + "movdqa %%xmm9,%%xmm10\n\t" \ + "movdqa %%xmm9,%%xmm11\n\t" \ + "punpcklwd %%xmm9,%%xmm10\n\t" \ + "pmaddwd %%xmm13,%%xmm10\n\t" \ + "mov $0x31F131F1,%[a]\n\t" \ + "punpckhwd %%xmm9,%%xmm11\n\t" \ + "pmaddwd %%xmm13,%%xmm11\n\t" \ + "movd %[a],%%xmm13\n\t" \ + "paddd %%xmm12,%%xmm10\n\t" \ + "pshufd $00,%%xmm13,%%xmm13\n\t" \ + "paddd %%xmm12,%%xmm11\n\t" \ + /*xmm12:xmm7=12785*t4''*/ \ + "movdqa %%xmm8,%%xmm7\n\t" \ + "movdqa %%xmm8,%%xmm1\n\t" \ + "pmullw %%xmm13,%%xmm7\n\t" \ + "pmulhw %%xmm13,%%xmm1\n\t" \ + "movdqa %%xmm7,%%xmm12\n\t" \ + "punpcklwd %%xmm1,%%xmm7\n\t" \ + "punpckhwd %%xmm1,%%xmm12\n\t" \ + /*xmm10=u=(12785*t4''+64277*t7''+0x7B1B>>16)+(t7''!=0)*/ \ + "paddd %%xmm7,%%xmm10\n\t" \ + "paddd %%xmm12,%%xmm11\n\t" \ + "psrad $16,%%xmm10\n\t" \ + "pcmpeqw %%xmm15,%%xmm9\n\t" \ + "psrad $16,%%xmm11\n\t" \ + "psubw %%xmm14,%%xmm9\n\t" \ + "packssdw %%xmm11,%%xmm10\n\t" \ + "pxor %%xmm12,%%xmm12\n\t" \ + "paddw %%xmm9,%%xmm10\n\t" \ + /*xmm1=_y[1]=u \ + xmm10=s=(12785*u>>16)-t4''*/ \ + "psubw %%xmm14,%%xmm12\n\t" \ + "movdqa %%xmm10,%%xmm1\n\t" \ + "mov $0x3000503B,%[a]\n\t" \ + "pmulhw %%xmm13,%%xmm10\n\t" \ + "movd %[a],%%xmm13\n\t" \ + "psubw %%xmm8,%%xmm10\n\t" \ + "pshufd $00,%%xmm13,%%xmm13\n\t" \ + /*xmm8:xmm7=s*20539+0x3000*/ \ + "movdqa %%xmm10,%%xmm7\n\t" \ + "movdqa %%xmm10,%%xmm8\n\t" \ + "punpcklwd %%xmm12,%%xmm7\n\t" \ + "pmaddwd %%xmm13,%%xmm7\n\t" \ + "punpckhwd %%xmm12,%%xmm8\n\t" \ + "pmaddwd %%xmm13,%%xmm8\n\t" \ + /*xmm7=(s*20539+0x3000>>20)+s*/ \ + "psrad $20,%%xmm7\n\t" \ + "psrad $20,%%xmm8\n\t" \ + "packssdw %%xmm8,%%xmm7\n\t" \ + "paddw %%xmm10,%%xmm7\n\t" \ + /*xmm7=_y[7]=v=(s*20539+0x3000>>20)+s+(s!=0)*/ \ + "pcmpeqw %%xmm15,%%xmm10\n\t" \ + "psubw %%xmm14,%%xmm10\n\t" \ + "paddw %%xmm10,%%xmm7\n\t " \ + +# define OC_TRANSPOSE8x8 \ + "#OC_TRANSPOSE8x8\n\t" \ + "movdqa %%xmm4,%%xmm8\n\t" \ + /*xmm4 = f3 e3 f2 e2 f1 e1 f0 e0*/ \ + "punpcklwd %%xmm5,%%xmm4\n\t" \ + /*xmm8 = f7 e7 f6 e6 f5 e5 f4 e4*/ \ + "punpckhwd %%xmm5,%%xmm8\n\t" \ + /*xmm5 is free.*/ \ + "movdqa %%xmm0,%%xmm5\n\t" \ + /*xmm0 = b3 a3 b2 a2 b1 a1 b0 a0*/ \ + "punpcklwd %%xmm1,%%xmm0\n\t" \ + /*xmm5 = b7 a7 b6 a6 b5 a5 b4 a4*/ \ + "punpckhwd %%xmm1,%%xmm5\n\t" \ + /*xmm1 is free.*/ \ + "movdqa %%xmm6,%%xmm1\n\t" \ + /*xmm6 = h3 g3 h2 g2 h1 g1 h0 g0*/ \ + "punpcklwd %%xmm7,%%xmm6\n\t" \ + /*xmm1 = h7 g7 h6 g6 h5 g5 h4 g4*/ \ + "punpckhwd %%xmm7,%%xmm1\n\t" \ + /*xmm7 is free.*/ \ + "movdqa %%xmm2,%%xmm7\n\t" \ + /*xmm7 = d3 c3 d2 c2 d1 c1 d0 c0*/ \ + "punpcklwd %%xmm3,%%xmm7\n\t" \ + /*xmm2 = d7 c7 d6 c6 d5 c5 d4 c4*/ \ + "punpckhwd %%xmm3,%%xmm2\n\t" \ + /*xmm3 is free.*/ \ + "movdqa %%xmm0,%%xmm3\n\t" \ + /*xmm0 = d1 c1 b1 a1 d0 c0 b0 a0*/ \ + "punpckldq %%xmm7,%%xmm0\n\t" \ + /*xmm3 = d3 c3 b3 a3 d2 c2 b2 a2*/ \ + "punpckhdq %%xmm7,%%xmm3\n\t" \ + /*xmm7 is free.*/ \ + "movdqa %%xmm5,%%xmm7\n\t" \ + /*xmm5 = d5 c5 b5 a5 d4 c4 b4 a4*/ \ + "punpckldq %%xmm2,%%xmm5\n\t" \ + /*xmm7 = d7 c7 b7 a7 d6 c6 b6 a6*/ \ + "punpckhdq %%xmm2,%%xmm7\n\t" \ + /*xmm2 is free.*/ \ + "movdqa %%xmm4,%%xmm2\n\t" \ + /*xmm2 = h1 g1 f1 e1 h0 g0 f0 e0*/ \ + "punpckldq %%xmm6,%%xmm2\n\t" \ + /*xmm4 = h3 g3 f3 e3 h2 g2 f2 e2*/ \ + "punpckhdq %%xmm6,%%xmm4\n\t" \ + /*xmm6 is free.*/ \ + "movdqa %%xmm8,%%xmm6\n\t" \ + /*xmm6 = h5 g5 f5 e5 h4 g4 f4 e4*/ \ + "punpckldq %%xmm1,%%xmm6\n\t" \ + /*xmm8 = h7 g7 f7 e7 h6 g6 f6 e6*/ \ + "punpckhdq %%xmm1,%%xmm8\n\t" \ + /*xmm1 is free.*/ \ + "movdqa %%xmm0,%%xmm1\n\t" \ + /*xmm0 = h0 g0 f0 e0 d0 c0 b0 a0*/ \ + "punpcklqdq %%xmm2,%%xmm0\n\t" \ + /*xmm1 = h1 g1 f1 e1 d1 c1 b1 a1*/ \ + "punpckhqdq %%xmm2,%%xmm1\n\t" \ + /*xmm2 is free.*/ \ + "movdqa %%xmm3,%%xmm2\n\t" \ + /*xmm2 = h2 g2 f2 e2 d2 c2 b2 a2*/ \ + "punpcklqdq %%xmm4,%%xmm2\n\t" \ + /*xmm3 = h3 g3 f3 e3 d3 c3 b3 a3*/ \ + "punpckhqdq %%xmm4,%%xmm3\n\t" \ + /*xmm4 is free.*/ \ + "movdqa %%xmm5,%%xmm4\n\t" \ + /*xmm4 = h4 g4 f4 e4 d4 c4 b4 a4*/ \ + "punpcklqdq %%xmm6,%%xmm4\n\t" \ + /*xmm5 = h5 g5 f5 e5 d5 c5 b5 a5*/ \ + "punpckhqdq %%xmm6,%%xmm5\n\t" \ + /*xmm6 is free.*/ \ + "movdqa %%xmm7,%%xmm6\n\t" \ + /*xmm6 = h6 g6 f6 e6 d6 c6 b6 a6*/ \ + "punpcklqdq %%xmm8,%%xmm6\n\t" \ + /*xmm7 = h7 g7 f7 e7 d7 c7 b7 a7*/ \ + "punpckhqdq %%xmm8,%%xmm7\n\t" \ + /*xmm8 is free.*/ \ + +/*SSE2 implementation of the fDCT for x86-64 only. + Because of the 8 extra XMM registers on x86-64, this version can operate + without any temporary stack access at all.*/ +void oc_enc_fdct8x8_x86_64sse2(ogg_int16_t _y[64],const ogg_int16_t _x[64]){ + ptrdiff_t a; + __asm__ __volatile__( + /*Load the input.*/ + "movdqa 0x00(%[x]),%%xmm0\n\t" + "movdqa 0x10(%[x]),%%xmm1\n\t" + "movdqa 0x20(%[x]),%%xmm2\n\t" + "movdqa 0x30(%[x]),%%xmm3\n\t" + "movdqa 0x40(%[x]),%%xmm4\n\t" + "movdqa 0x50(%[x]),%%xmm5\n\t" + "movdqa 0x60(%[x]),%%xmm6\n\t" + "movdqa 0x70(%[x]),%%xmm7\n\t" + /*Add two extra bits of working precision to improve accuracy; any more and + we could overflow.*/ + /*We also add a few biases to correct for some systematic error that + remains in the full fDCT->iDCT round trip.*/ + /*xmm15={0}x8*/ + "pxor %%xmm15,%%xmm15\n\t" + /*xmm14={-1}x8*/ + "pcmpeqb %%xmm14,%%xmm14\n\t" + "psllw $2,%%xmm0\n\t" + /*xmm8=xmm0*/ + "movdqa %%xmm0,%%xmm8\n\t" + "psllw $2,%%xmm1\n\t" + /*xmm8={_x[7...0]==0}*/ + "pcmpeqw %%xmm15,%%xmm8\n\t" + "psllw $2,%%xmm2\n\t" + /*xmm8={_x[7...0]!=0}*/ + "psubw %%xmm14,%%xmm8\n\t" + "psllw $2,%%xmm3\n\t" + /*%[a]=1*/ + "mov $1,%[a]\n\t" + /*xmm8={_x[6]!=0,0,_x[4]!=0,0,_x[2]!=0,0,_x[0]!=0,0}*/ + "pslld $16,%%xmm8\n\t" + "psllw $2,%%xmm4\n\t" + /*xmm9={0,0,0,0,0,0,0,1}*/ + "movd %[a],%%xmm9\n\t" + /*xmm8={0,0,_x[2]!=0,0,_x[0]!=0,0}*/ + "pshufhw $0x00,%%xmm8,%%xmm8\n\t" + "psllw $2,%%xmm5\n\t" + /*%[a]={1}x2*/ + "mov $0x10001,%[a]\n\t" + /*xmm8={0,0,0,0,0,0,0,_x[0]!=0}*/ + "pshuflw $0x01,%%xmm8,%%xmm8\n\t" + "psllw $2,%%xmm6\n\t" + /*xmm10={0,0,0,0,0,0,1,1}*/ + "movd %[a],%%xmm10\n\t" + /*xmm0=_x[7...0]+{0,0,0,0,0,0,0,_x[0]!=0}*/ + "paddw %%xmm8,%%xmm0\n\t" + "psllw $2,%%xmm7\n\t" + /*xmm0=_x[7...0]+{0,0,0,0,0,0,1,(_x[0]!=0)+1}*/ + "paddw %%xmm10,%%xmm0\n\t" + /*xmm1=_x[15...8]-{0,0,0,0,0,0,0,1}*/ + "psubw %%xmm9,%%xmm1\n\t" + /*Transform columns.*/ + OC_FDCT8x8 + /*Transform rows.*/ + OC_TRANSPOSE8x8 + OC_FDCT8x8 + /*TODO: zig-zag ordering?*/ + OC_TRANSPOSE8x8 + /*xmm14={-2,-2,-2,-2,-2,-2,-2,-2}*/ + "paddw %%xmm14,%%xmm14\n\t" + "psubw %%xmm14,%%xmm0\n\t" + "psubw %%xmm14,%%xmm1\n\t" + "psraw $2,%%xmm0\n\t" + "psubw %%xmm14,%%xmm2\n\t" + "psraw $2,%%xmm1\n\t" + "psubw %%xmm14,%%xmm3\n\t" + "psraw $2,%%xmm2\n\t" + "psubw %%xmm14,%%xmm4\n\t" + "psraw $2,%%xmm3\n\t" + "psubw %%xmm14,%%xmm5\n\t" + "psraw $2,%%xmm4\n\t" + "psubw %%xmm14,%%xmm6\n\t" + "psraw $2,%%xmm5\n\t" + "psubw %%xmm14,%%xmm7\n\t" + "psraw $2,%%xmm6\n\t" + "psraw $2,%%xmm7\n\t" + /*Store the result.*/ + "movdqa %%xmm0,0x00(%[y])\n\t" + "movdqa %%xmm1,0x10(%[y])\n\t" + "movdqa %%xmm2,0x20(%[y])\n\t" + "movdqa %%xmm3,0x30(%[y])\n\t" + "movdqa %%xmm4,0x40(%[y])\n\t" + "movdqa %%xmm5,0x50(%[y])\n\t" + "movdqa %%xmm6,0x60(%[y])\n\t" + "movdqa %%xmm7,0x70(%[y])\n\t" + :[a]"=&r"(a) + :[y]"r"(_y),[x]"r"(_x) + :"memory" + ); +} +#endif diff --git a/Engine/lib/libtheora/lib/x86/x86enc.c b/Engine/lib/libtheora/lib/x86/x86enc.c new file mode 100644 index 000000000..43b7be3ea --- /dev/null +++ b/Engine/lib/libtheora/lib/x86/x86enc.c @@ -0,0 +1,49 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: x86state.c 15675 2009-02-06 09:43:27Z tterribe $ + + ********************************************************************/ +#include "x86enc.h" + +#if defined(OC_X86_ASM) + +#include "../cpu.c" + +void oc_enc_vtable_init_x86(oc_enc_ctx *_enc){ + ogg_uint32_t cpu_flags; + cpu_flags=oc_cpu_flags_get(); + oc_enc_vtable_init_c(_enc); + if(cpu_flags&OC_CPU_X86_MMX){ + _enc->opt_vtable.frag_sub=oc_enc_frag_sub_mmx; + _enc->opt_vtable.frag_sub_128=oc_enc_frag_sub_128_mmx; + _enc->opt_vtable.frag_recon_intra=oc_frag_recon_intra_mmx; + _enc->opt_vtable.frag_recon_inter=oc_frag_recon_inter_mmx; + _enc->opt_vtable.fdct8x8=oc_enc_fdct8x8_mmx; + } + if(cpu_flags&OC_CPU_X86_MMXEXT){ + _enc->opt_vtable.frag_sad=oc_enc_frag_sad_mmxext; + _enc->opt_vtable.frag_sad_thresh=oc_enc_frag_sad_thresh_mmxext; + _enc->opt_vtable.frag_sad2_thresh=oc_enc_frag_sad2_thresh_mmxext; + _enc->opt_vtable.frag_satd_thresh=oc_enc_frag_satd_thresh_mmxext; + _enc->opt_vtable.frag_satd2_thresh=oc_enc_frag_satd2_thresh_mmxext; + _enc->opt_vtable.frag_intra_satd=oc_enc_frag_intra_satd_mmxext; + _enc->opt_vtable.frag_copy2=oc_enc_frag_copy2_mmxext; + } + if(cpu_flags&OC_CPU_X86_SSE2){ +# if defined(OC_X86_64_ASM) + /*_enc->opt_vtable.fdct8x8=oc_enc_fdct8x8_x86_64sse2;*/ +# endif + } +} +#endif diff --git a/Engine/lib/libtheora/lib/x86/x86enc.h b/Engine/lib/libtheora/lib/x86/x86enc.h new file mode 100644 index 000000000..06c3908bc --- /dev/null +++ b/Engine/lib/libtheora/lib/x86/x86enc.h @@ -0,0 +1,47 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: x86int.h 15675 2009-02-06 09:43:27Z tterribe $ + + ********************************************************************/ + +#if !defined(_x86_x86enc_H) +# define _x86_x86enc_H (1) +# include "../encint.h" +# include "x86int.h" + +void oc_enc_vtable_init_x86(oc_enc_ctx *_enc); + +unsigned oc_enc_frag_sad_mmxext(const unsigned char *_src, + const unsigned char *_ref,int _ystride); +unsigned oc_enc_frag_sad_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _thresh); +unsigned oc_enc_frag_sad2_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride, + unsigned _thresh); +unsigned oc_enc_frag_satd_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _thresh); +unsigned oc_enc_frag_satd2_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride, + unsigned _thresh); +unsigned oc_enc_frag_intra_satd_mmxext(const unsigned char *_src,int _ystride); +void oc_enc_frag_sub_mmx(ogg_int16_t _diff[64], + const unsigned char *_x,const unsigned char *_y,int _stride); +void oc_enc_frag_sub_128_mmx(ogg_int16_t _diff[64], + const unsigned char *_x,int _stride); +void oc_enc_frag_copy2_mmxext(unsigned char *_dst, + const unsigned char *_src1,const unsigned char *_src2,int _ystride); +void oc_enc_fdct8x8_mmx(ogg_int16_t _y[64],const ogg_int16_t _x[64]); +void oc_enc_fdct8x8_x86_64sse2(ogg_int16_t _y[64],const ogg_int16_t _x[64]); + +#endif diff --git a/Engine/lib/libtheora/lib/x86/x86int.h b/Engine/lib/libtheora/lib/x86/x86int.h new file mode 100644 index 000000000..ede724f5a --- /dev/null +++ b/Engine/lib/libtheora/lib/x86/x86int.h @@ -0,0 +1,42 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: x86int.h 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ + +#if !defined(_x86_x86int_H) +# define _x86_x86int_H (1) +# include "../internal.h" + +void oc_state_vtable_init_x86(oc_theora_state *_state); + +void oc_frag_copy_mmx(unsigned char *_dst, + const unsigned char *_src,int _ystride); +void oc_frag_recon_intra_mmx(unsigned char *_dst,int _ystride, + const ogg_int16_t *_residue); +void oc_frag_recon_inter_mmx(unsigned char *_dst, + const unsigned char *_src,int _ystride,const ogg_int16_t *_residue); +void oc_frag_recon_inter2_mmx(unsigned char *_dst,const unsigned char *_src1, + const unsigned char *_src2,int _ystride,const ogg_int16_t *_residue); +void oc_idct8x8_mmx(ogg_int16_t _y[64],int _last_zzi); +void oc_state_frag_recon_mmx(const oc_theora_state *_state,ptrdiff_t _fragi, + int _pli,ogg_int16_t _dct_coeffs[64],int _last_zzi,ogg_uint16_t _dc_quant); +void oc_state_frag_copy_list_mmx(const oc_theora_state *_state, + const ptrdiff_t *_fragis,ptrdiff_t _nfragis, + int _dst_frame,int _src_frame,int _pli); +void oc_state_loop_filter_frag_rows_mmx(const oc_theora_state *_state, + int _bv[256],int _refi,int _pli,int _fragy0,int _fragy_end); +void oc_restore_fpu_mmx(void); + +#endif diff --git a/Engine/lib/libtheora/lib/dec/x86/x86state.c b/Engine/lib/libtheora/lib/x86/x86state.c similarity index 58% rename from Engine/lib/libtheora/lib/dec/x86/x86state.c rename to Engine/lib/libtheora/lib/x86/x86state.c index 28a559ba4..a786bec28 100644 --- a/Engine/lib/libtheora/lib/dec/x86/x86state.c +++ b/Engine/lib/libtheora/lib/x86/x86state.c @@ -5,33 +5,57 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2007 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: x86state.c 15427 2008-10-21 02:36:19Z xiphmont $ + last mod: $Id: x86state.c 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ #include "x86int.h" -#if defined(USE_ASM) +#if defined(OC_X86_ASM) -#include "../../cpu.c" +#include "../cpu.c" + +/*This table has been modified from OC_FZIG_ZAG by baking a 4x4 transpose into + each quadrant of the destination.*/ +static const unsigned char OC_FZIG_ZAG_MMX[128]={ + 0, 8, 1, 2, 9,16,24,17, + 10, 3,32,11,18,25, 4,12, + 5,26,19,40,33,34,41,48, + 27, 6,13,20,28,21,14, 7, + 56,49,42,35,43,50,57,36, + 15,22,29,30,23,44,37,58, + 51,59,38,45,52,31,60,53, + 46,39,47,54,61,62,55,63, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, +}; void oc_state_vtable_init_x86(oc_theora_state *_state){ _state->cpu_flags=oc_cpu_flags_get(); if(_state->cpu_flags&OC_CPU_X86_MMX){ + _state->opt_vtable.frag_copy=oc_frag_copy_mmx; _state->opt_vtable.frag_recon_intra=oc_frag_recon_intra_mmx; _state->opt_vtable.frag_recon_inter=oc_frag_recon_inter_mmx; _state->opt_vtable.frag_recon_inter2=oc_frag_recon_inter2_mmx; - _state->opt_vtable.state_frag_copy=oc_state_frag_copy_mmx; + _state->opt_vtable.idct8x8=oc_idct8x8_mmx; _state->opt_vtable.state_frag_recon=oc_state_frag_recon_mmx; + _state->opt_vtable.state_frag_copy_list=oc_state_frag_copy_list_mmx; _state->opt_vtable.state_loop_filter_frag_rows= oc_state_loop_filter_frag_rows_mmx; _state->opt_vtable.restore_fpu=oc_restore_fpu_mmx; + _state->opt_data.dct_fzig_zag=OC_FZIG_ZAG_MMX; } else oc_state_vtable_init_c(_state); } diff --git a/Engine/lib/libtheora/lib/x86_vc/mmxencfrag.c b/Engine/lib/libtheora/lib/x86_vc/mmxencfrag.c new file mode 100644 index 000000000..94f1d0651 --- /dev/null +++ b/Engine/lib/libtheora/lib/x86_vc/mmxencfrag.c @@ -0,0 +1,969 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: dsp_mmx.c 14579 2008-03-12 06:42:40Z xiphmont $ + + ********************************************************************/ +#include +#include "x86enc.h" + +#if defined(OC_X86_ASM) + +unsigned oc_enc_frag_sad_mmxext(const unsigned char *_src, + const unsigned char *_ref,int _ystride){ + ptrdiff_t ret; + __asm{ +#define SRC esi +#define REF edx +#define YSTRIDE ecx +#define YSTRIDE3 edi + mov YSTRIDE,_ystride + mov SRC,_src + mov REF,_ref + /*Load the first 4 rows of each block.*/ + movq mm0,[SRC] + movq mm1,[REF] + movq mm2,[SRC][YSTRIDE] + movq mm3,[REF][YSTRIDE] + lea YSTRIDE3,[YSTRIDE+YSTRIDE*2] + movq mm4,[SRC+YSTRIDE*2] + movq mm5,[REF+YSTRIDE*2] + movq mm6,[SRC+YSTRIDE3] + movq mm7,[REF+YSTRIDE3] + /*Compute their SADs and add them in mm0*/ + psadbw mm0,mm1 + psadbw mm2,mm3 + lea SRC,[SRC+YSTRIDE*4] + paddw mm0,mm2 + lea REF,[REF+YSTRIDE*4] + /*Load the next 3 rows as registers become available.*/ + movq mm2,[SRC] + movq mm3,[REF] + psadbw mm4,mm5 + psadbw mm6,mm7 + paddw mm0,mm4 + movq mm5,[REF+YSTRIDE] + movq mm4,[SRC+YSTRIDE] + paddw mm0,mm6 + movq mm7,[REF+YSTRIDE*2] + movq mm6,[SRC+YSTRIDE*2] + /*Start adding their SADs to mm0*/ + psadbw mm2,mm3 + psadbw mm4,mm5 + paddw mm0,mm2 + psadbw mm6,mm7 + /*Load last row as registers become available.*/ + movq mm2,[SRC+YSTRIDE3] + movq mm3,[REF+YSTRIDE3] + /*And finish adding up their SADs.*/ + paddw mm0,mm4 + psadbw mm2,mm3 + paddw mm0,mm6 + paddw mm0,mm2 + movd [ret],mm0 +#undef SRC +#undef REF +#undef YSTRIDE +#undef YSTRIDE3 + } + return (unsigned)ret; +} + +unsigned oc_enc_frag_sad_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _thresh){ + /*Early termination is for suckers.*/ + return oc_enc_frag_sad_mmxext(_src,_ref,_ystride); +} + +#define OC_SAD2_LOOP __asm{ \ + /*We want to compute (mm0+mm1>>1) on unsigned bytes without overflow, but \ + pavgb computes (mm0+mm1+1>>1). \ + The latter is exactly 1 too large when the low bit of two corresponding \ + bytes is only set in one of them. \ + Therefore we pxor the operands, pand to mask out the low bits, and psubb to \ + correct the output of pavgb.*/ \ + __asm movq mm6,mm0 \ + __asm lea REF1,[REF1+YSTRIDE*2] \ + __asm pxor mm0,mm1 \ + __asm pavgb mm6,mm1 \ + __asm lea REF2,[REF2+YSTRIDE*2] \ + __asm movq mm1,mm2 \ + __asm pand mm0,mm7 \ + __asm pavgb mm2,mm3 \ + __asm pxor mm1,mm3 \ + __asm movq mm3,[REF2+YSTRIDE] \ + __asm psubb mm6,mm0 \ + __asm movq mm0,[REF1] \ + __asm pand mm1,mm7 \ + __asm psadbw mm4,mm6 \ + __asm movd mm6,RET \ + __asm psubb mm2,mm1 \ + __asm movq mm1,[REF2] \ + __asm lea SRC,[SRC+YSTRIDE*2] \ + __asm psadbw mm5,mm2 \ + __asm movq mm2,[REF1+YSTRIDE] \ + __asm paddw mm5,mm4 \ + __asm movq mm4,[SRC] \ + __asm paddw mm6,mm5 \ + __asm movq mm5,[SRC+YSTRIDE] \ + __asm movd RET,mm6 \ +} + +/*Same as above, but does not pre-load the next two rows.*/ +#define OC_SAD2_TAIL __asm{ \ + __asm movq mm6,mm0 \ + __asm pavgb mm0,mm1 \ + __asm pxor mm6,mm1 \ + __asm movq mm1,mm2 \ + __asm pand mm6,mm7 \ + __asm pavgb mm2,mm3 \ + __asm pxor mm1,mm3 \ + __asm psubb mm0,mm6 \ + __asm pand mm1,mm7 \ + __asm psadbw mm4,mm0 \ + __asm psubb mm2,mm1 \ + __asm movd mm6,RET \ + __asm psadbw mm5,mm2 \ + __asm paddw mm5,mm4 \ + __asm paddw mm6,mm5 \ + __asm movd RET,mm6 \ +} + +unsigned oc_enc_frag_sad2_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride, + unsigned _thresh){ + ptrdiff_t ret; + __asm{ +#define REF1 ecx +#define REF2 edi +#define YSTRIDE esi +#define SRC edx +#define RET eax + mov YSTRIDE,_ystride + mov SRC,_src + mov REF1,_ref1 + mov REF2,_ref2 + movq mm0,[REF1] + movq mm1,[REF2] + movq mm2,[REF1+YSTRIDE] + movq mm3,[REF2+YSTRIDE] + xor RET,RET + movq mm4,[SRC] + pxor mm7,mm7 + pcmpeqb mm6,mm6 + movq mm5,[SRC+YSTRIDE] + psubb mm7,mm6 + OC_SAD2_LOOP + OC_SAD2_LOOP + OC_SAD2_LOOP + OC_SAD2_TAIL + mov [ret],RET +#undef REF1 +#undef REF2 +#undef YSTRIDE +#undef SRC +#undef RET + } + return (unsigned)ret; +} + +/*Load an 8x4 array of pixel values from %[src] and %[ref] and compute their + 16-bit difference in mm0...mm7.*/ +#define OC_LOAD_SUB_8x4(_off) __asm{ \ + __asm movd mm0,[_off+SRC] \ + __asm movd mm4,[_off+REF] \ + __asm movd mm1,[_off+SRC+SRC_YSTRIDE] \ + __asm lea SRC,[SRC+SRC_YSTRIDE*2] \ + __asm movd mm5,[_off+REF+REF_YSTRIDE] \ + __asm lea REF,[REF+REF_YSTRIDE*2] \ + __asm movd mm2,[_off+SRC] \ + __asm movd mm7,[_off+REF] \ + __asm movd mm3,[_off+SRC+SRC_YSTRIDE] \ + __asm movd mm6,[_off+REF+REF_YSTRIDE] \ + __asm punpcklbw mm0,mm4 \ + __asm lea SRC,[SRC+SRC_YSTRIDE*2] \ + __asm punpcklbw mm4,mm4 \ + __asm lea REF,[REF+REF_YSTRIDE*2] \ + __asm psubw mm0,mm4 \ + __asm movd mm4,[_off+SRC] \ + __asm movq [_off*2+BUF],mm0 \ + __asm movd mm0,[_off+REF] \ + __asm punpcklbw mm1,mm5 \ + __asm punpcklbw mm5,mm5 \ + __asm psubw mm1,mm5 \ + __asm movd mm5,[_off+SRC+SRC_YSTRIDE] \ + __asm punpcklbw mm2,mm7 \ + __asm punpcklbw mm7,mm7 \ + __asm psubw mm2,mm7 \ + __asm movd mm7,[_off+REF+REF_YSTRIDE] \ + __asm punpcklbw mm3,mm6 \ + __asm lea SRC,[SRC+SRC_YSTRIDE*2] \ + __asm punpcklbw mm6,mm6 \ + __asm psubw mm3,mm6 \ + __asm movd mm6,[_off+SRC] \ + __asm punpcklbw mm4,mm0 \ + __asm lea REF,[REF+REF_YSTRIDE*2] \ + __asm punpcklbw mm0,mm0 \ + __asm lea SRC,[SRC+SRC_YSTRIDE*2] \ + __asm psubw mm4,mm0 \ + __asm movd mm0,[_off+REF] \ + __asm punpcklbw mm5,mm7 \ + __asm neg SRC_YSTRIDE \ + __asm punpcklbw mm7,mm7 \ + __asm psubw mm5,mm7 \ + __asm movd mm7,[_off+SRC+SRC_YSTRIDE] \ + __asm punpcklbw mm6,mm0 \ + __asm lea REF,[REF+REF_YSTRIDE*2] \ + __asm punpcklbw mm0,mm0 \ + __asm neg REF_YSTRIDE \ + __asm psubw mm6,mm0 \ + __asm movd mm0,[_off+REF+REF_YSTRIDE] \ + __asm lea SRC,[SRC+SRC_YSTRIDE*8] \ + __asm punpcklbw mm7,mm0 \ + __asm neg SRC_YSTRIDE \ + __asm punpcklbw mm0,mm0 \ + __asm lea REF,[REF+REF_YSTRIDE*8] \ + __asm psubw mm7,mm0 \ + __asm neg REF_YSTRIDE \ + __asm movq mm0,[_off*2+BUF] \ +} + +/*Load an 8x4 array of pixel values from %[src] into %%mm0...%%mm7.*/ +#define OC_LOAD_8x4(_off) __asm{ \ + __asm movd mm0,[_off+SRC] \ + __asm movd mm1,[_off+SRC+YSTRIDE] \ + __asm movd mm2,[_off+SRC+YSTRIDE*2] \ + __asm pxor mm7,mm7 \ + __asm movd mm3,[_off+SRC+YSTRIDE3] \ + __asm punpcklbw mm0,mm7 \ + __asm movd mm4,[_off+SRC4] \ + __asm punpcklbw mm1,mm7 \ + __asm movd mm5,[_off+SRC4+YSTRIDE] \ + __asm punpcklbw mm2,mm7 \ + __asm movd mm6,[_off+SRC4+YSTRIDE*2] \ + __asm punpcklbw mm3,mm7 \ + __asm movd mm7,[_off+SRC4+YSTRIDE3] \ + __asm punpcklbw mm4,mm4 \ + __asm punpcklbw mm5,mm5 \ + __asm psrlw mm4,8 \ + __asm psrlw mm5,8 \ + __asm punpcklbw mm6,mm6 \ + __asm punpcklbw mm7,mm7 \ + __asm psrlw mm6,8 \ + __asm psrlw mm7,8 \ +} + +/*Performs the first two stages of an 8-point 1-D Hadamard transform. + The transform is performed in place, except that outputs 0-3 are swapped with + outputs 4-7. + Outputs 2, 3, 6 and 7 from the second stage are negated (which allows us to + perform this stage in place with no temporary registers).*/ +#define OC_HADAMARD_AB_8x4 __asm{ \ + /*Stage A: \ + Outputs 0-3 are swapped with 4-7 here.*/ \ + __asm paddw mm5,mm1 \ + __asm paddw mm6,mm2 \ + __asm paddw mm1,mm1 \ + __asm paddw mm2,mm2 \ + __asm psubw mm1,mm5 \ + __asm psubw mm2,mm6 \ + __asm paddw mm7,mm3 \ + __asm paddw mm4,mm0 \ + __asm paddw mm3,mm3 \ + __asm paddw mm0,mm0 \ + __asm psubw mm3,mm7 \ + __asm psubw mm0,mm4 \ + /*Stage B:*/ \ + __asm paddw mm0,mm2 \ + __asm paddw mm1,mm3 \ + __asm paddw mm4,mm6 \ + __asm paddw mm5,mm7 \ + __asm paddw mm2,mm2 \ + __asm paddw mm3,mm3 \ + __asm paddw mm6,mm6 \ + __asm paddw mm7,mm7 \ + __asm psubw mm2,mm0 \ + __asm psubw mm3,mm1 \ + __asm psubw mm6,mm4 \ + __asm psubw mm7,mm5 \ +} + +/*Performs the last stage of an 8-point 1-D Hadamard transform in place. + Ouputs 1, 3, 5, and 7 are negated (which allows us to perform this stage in + place with no temporary registers).*/ +#define OC_HADAMARD_C_8x4 __asm{ \ + /*Stage C:*/ \ + __asm paddw mm0,mm1 \ + __asm paddw mm2,mm3 \ + __asm paddw mm4,mm5 \ + __asm paddw mm6,mm7 \ + __asm paddw mm1,mm1 \ + __asm paddw mm3,mm3 \ + __asm paddw mm5,mm5 \ + __asm paddw mm7,mm7 \ + __asm psubw mm1,mm0 \ + __asm psubw mm3,mm2 \ + __asm psubw mm5,mm4 \ + __asm psubw mm7,mm6 \ +} + +/*Performs an 8-point 1-D Hadamard transform. + The transform is performed in place, except that outputs 0-3 are swapped with + outputs 4-7. + Outputs 1, 2, 5 and 6 are negated (which allows us to perform the transform + in place with no temporary registers).*/ +#define OC_HADAMARD_8x4 __asm{ \ + OC_HADAMARD_AB_8x4 \ + OC_HADAMARD_C_8x4 \ +} + +/*Performs the first part of the final stage of the Hadamard transform and + summing of absolute values. + At the end of this part, mm1 will contain the DC coefficient of the + transform.*/ +#define OC_HADAMARD_C_ABS_ACCUM_A_8x4(_r6,_r7) __asm{ \ + /*We use the fact that \ + (abs(a+b)+abs(a-b))/2=max(abs(a),abs(b)) \ + to merge the final butterfly with the abs and the first stage of \ + accumulation. \ + Thus we can avoid using pabsw, which is not available until SSSE3. \ + Emulating pabsw takes 3 instructions, so the straightforward MMXEXT \ + implementation would be (3+3)*8+7=55 instructions (+4 for spilling \ + registers). \ + Even with pabsw, it would be (3+1)*8+7=39 instructions (with no spills). \ + This implementation is only 26 (+4 for spilling registers).*/ \ + __asm movq [_r7+BUF],mm7 \ + __asm movq [_r6+BUF],mm6 \ + /*mm7={0x7FFF}x4 \ + mm0=max(abs(mm0),abs(mm1))-0x7FFF*/ \ + __asm pcmpeqb mm7,mm7 \ + __asm movq mm6,mm0 \ + __asm psrlw mm7,1 \ + __asm paddw mm6,mm1 \ + __asm pmaxsw mm0,mm1 \ + __asm paddsw mm6,mm7 \ + __asm psubw mm0,mm6 \ + /*mm2=max(abs(mm2),abs(mm3))-0x7FFF \ + mm4=max(abs(mm4),abs(mm5))-0x7FFF*/ \ + __asm movq mm6,mm2 \ + __asm movq mm1,mm4 \ + __asm pmaxsw mm2,mm3 \ + __asm pmaxsw mm4,mm5 \ + __asm paddw mm6,mm3 \ + __asm paddw mm1,mm5 \ + __asm movq mm3,[_r7+BUF] \ +} + +/*Performs the second part of the final stage of the Hadamard transform and + summing of absolute values.*/ +#define OC_HADAMARD_C_ABS_ACCUM_B_8x4(_r6,_r7) __asm{ \ + __asm paddsw mm6,mm7 \ + __asm movq mm5,[_r6+BUF] \ + __asm paddsw mm1,mm7 \ + __asm psubw mm2,mm6 \ + __asm psubw mm4,mm1 \ + /*mm7={1}x4 (needed for the horizontal add that follows) \ + mm0+=mm2+mm4+max(abs(mm3),abs(mm5))-0x7FFF*/ \ + __asm movq mm6,mm3 \ + __asm pmaxsw mm3,mm5 \ + __asm paddw mm0,mm2 \ + __asm paddw mm6,mm5 \ + __asm paddw mm0,mm4 \ + __asm paddsw mm6,mm7 \ + __asm paddw mm0,mm3 \ + __asm psrlw mm7,14 \ + __asm psubw mm0,mm6 \ +} + +/*Performs the last stage of an 8-point 1-D Hadamard transform, takes the + absolute value of each component, and accumulates everything into mm0. + This is the only portion of SATD which requires MMXEXT (we could use plain + MMX, but it takes 4 instructions and an extra register to work around the + lack of a pmaxsw, which is a pretty serious penalty).*/ +#define OC_HADAMARD_C_ABS_ACCUM_8x4(_r6,_r7) __asm{ \ + OC_HADAMARD_C_ABS_ACCUM_A_8x4(_r6,_r7) \ + OC_HADAMARD_C_ABS_ACCUM_B_8x4(_r6,_r7) \ +} + +/*Performs an 8-point 1-D Hadamard transform, takes the absolute value of each + component, and accumulates everything into mm0. + Note that mm0 will have an extra 4 added to each column, and that after + removing this value, the remainder will be half the conventional value.*/ +#define OC_HADAMARD_ABS_ACCUM_8x4(_r6,_r7) __asm{ \ + OC_HADAMARD_AB_8x4 \ + OC_HADAMARD_C_ABS_ACCUM_8x4(_r6,_r7) \ +} + +/*Performs two 4x4 transposes (mostly) in place. + On input, {mm0,mm1,mm2,mm3} contains rows {e,f,g,h}, and {mm4,mm5,mm6,mm7} + contains rows {a,b,c,d}. + On output, {0x40,0x50,0x60,0x70}+_off+BUF contains {e,f,g,h}^T, and + {mm4,mm5,mm6,mm7} contains the transposed rows {a,b,c,d}^T.*/ +#define OC_TRANSPOSE_4x4x2(_off) __asm{ \ + /*First 4x4 transpose:*/ \ + __asm movq [0x10+_off+BUF],mm5 \ + /*mm0 = e3 e2 e1 e0 \ + mm1 = f3 f2 f1 f0 \ + mm2 = g3 g2 g1 g0 \ + mm3 = h3 h2 h1 h0*/ \ + __asm movq mm5,mm2 \ + __asm punpcklwd mm2,mm3 \ + __asm punpckhwd mm5,mm3 \ + __asm movq mm3,mm0 \ + __asm punpcklwd mm0,mm1 \ + __asm punpckhwd mm3,mm1 \ + /*mm0 = f1 e1 f0 e0 \ + mm3 = f3 e3 f2 e2 \ + mm2 = h1 g1 h0 g0 \ + mm5 = h3 g3 h2 g2*/ \ + __asm movq mm1,mm0 \ + __asm punpckldq mm0,mm2 \ + __asm punpckhdq mm1,mm2 \ + __asm movq mm2,mm3 \ + __asm punpckhdq mm3,mm5 \ + __asm movq [0x40+_off+BUF],mm0 \ + __asm punpckldq mm2,mm5 \ + /*mm0 = h0 g0 f0 e0 \ + mm1 = h1 g1 f1 e1 \ + mm2 = h2 g2 f2 e2 \ + mm3 = h3 g3 f3 e3*/ \ + __asm movq mm5,[0x10+_off+BUF] \ + /*Second 4x4 transpose:*/ \ + /*mm4 = a3 a2 a1 a0 \ + mm5 = b3 b2 b1 b0 \ + mm6 = c3 c2 c1 c0 \ + mm7 = d3 d2 d1 d0*/ \ + __asm movq mm0,mm6 \ + __asm punpcklwd mm6,mm7 \ + __asm movq [0x50+_off+BUF],mm1 \ + __asm punpckhwd mm0,mm7 \ + __asm movq mm7,mm4 \ + __asm punpcklwd mm4,mm5 \ + __asm movq [0x60+_off+BUF],mm2 \ + __asm punpckhwd mm7,mm5 \ + /*mm4 = b1 a1 b0 a0 \ + mm7 = b3 a3 b2 a2 \ + mm6 = d1 c1 d0 c0 \ + mm0 = d3 c3 d2 c2*/ \ + __asm movq mm5,mm4 \ + __asm punpckldq mm4,mm6 \ + __asm movq [0x70+_off+BUF],mm3 \ + __asm punpckhdq mm5,mm6 \ + __asm movq mm6,mm7 \ + __asm punpckhdq mm7,mm0 \ + __asm punpckldq mm6,mm0 \ + /*mm4 = d0 c0 b0 a0 \ + mm5 = d1 c1 b1 a1 \ + mm6 = d2 c2 b2 a2 \ + mm7 = d3 c3 b3 a3*/ \ +} + +static unsigned oc_int_frag_satd_thresh_mmxext(const unsigned char *_src, + int _src_ystride,const unsigned char *_ref,int _ref_ystride,unsigned _thresh){ + OC_ALIGN8(ogg_int16_t buf[64]); + ogg_int16_t *bufp; + unsigned ret1; + unsigned ret2; + bufp=buf; + __asm{ +#define SRC esi +#define REF eax +#define SRC_YSTRIDE ecx +#define REF_YSTRIDE edx +#define BUF edi +#define RET eax +#define RET2 edx + mov SRC,_src + mov SRC_YSTRIDE,_src_ystride + mov REF,_ref + mov REF_YSTRIDE,_ref_ystride + mov BUF,bufp + OC_LOAD_SUB_8x4(0x00) + OC_HADAMARD_8x4 + OC_TRANSPOSE_4x4x2(0x00) + /*Finish swapping out this 8x4 block to make room for the next one. + mm0...mm3 have been swapped out already.*/ + movq [0x00+BUF],mm4 + movq [0x10+BUF],mm5 + movq [0x20+BUF],mm6 + movq [0x30+BUF],mm7 + OC_LOAD_SUB_8x4(0x04) + OC_HADAMARD_8x4 + OC_TRANSPOSE_4x4x2(0x08) + /*Here the first 4x4 block of output from the last transpose is the second + 4x4 block of input for the next transform. + We have cleverly arranged that it already be in the appropriate place, so + we only have to do half the loads.*/ + movq mm1,[0x10+BUF] + movq mm2,[0x20+BUF] + movq mm3,[0x30+BUF] + movq mm0,[0x00+BUF] + OC_HADAMARD_ABS_ACCUM_8x4(0x28,0x38) + /*Up to this point, everything fit in 16 bits (8 input + 1 for the + difference + 2*3 for the two 8-point 1-D Hadamards - 1 for the abs - 1 + for the factor of two we dropped + 3 for the vertical accumulation). + Now we finally have to promote things to dwords. + We break this part out of OC_HADAMARD_ABS_ACCUM_8x4 to hide the long + latency of pmaddwd by starting the next series of loads now.*/ + mov RET2,_thresh + pmaddwd mm0,mm7 + movq mm1,[0x50+BUF] + movq mm5,[0x58+BUF] + movq mm4,mm0 + movq mm2,[0x60+BUF] + punpckhdq mm0,mm0 + movq mm6,[0x68+BUF] + paddd mm4,mm0 + movq mm3,[0x70+BUF] + movd RET,mm4 + movq mm7,[0x78+BUF] + /*The sums produced by OC_HADAMARD_ABS_ACCUM_8x4 each have an extra 4 + added to them, and a factor of two removed; correct the final sum here.*/ + lea RET,[RET+RET-32] + movq mm0,[0x40+BUF] + cmp RET,RET2 + movq mm4,[0x48+BUF] + jae at_end + OC_HADAMARD_ABS_ACCUM_8x4(0x68,0x78) + pmaddwd mm0,mm7 + /*There isn't much to stick in here to hide the latency this time, but the + alternative to pmaddwd is movq->punpcklwd->punpckhwd->paddd, whose + latency is even worse.*/ + sub RET,32 + movq mm4,mm0 + punpckhdq mm0,mm0 + paddd mm4,mm0 + movd RET2,mm4 + lea RET,[RET+RET2*2] + align 16 +at_end: + mov ret1,RET +#undef SRC +#undef REF +#undef SRC_YSTRIDE +#undef REF_YSTRIDE +#undef BUF +#undef RET +#undef RET2 + } + return ret1; +} + +unsigned oc_enc_frag_satd_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _thresh){ + return oc_int_frag_satd_thresh_mmxext(_src,_ystride,_ref,_ystride,_thresh); +} + + +/*Our internal implementation of frag_copy2 takes an extra stride parameter so + we can share code with oc_enc_frag_satd2_thresh_mmxext().*/ +static void oc_int_frag_copy2_mmxext(unsigned char *_dst,int _dst_ystride, + const unsigned char *_src1,const unsigned char *_src2,int _src_ystride){ + __asm{ + /*Load the first 3 rows.*/ +#define DST_YSTRIDE edi +#define SRC_YSTRIDE esi +#define DST eax +#define SRC1 edx +#define SRC2 ecx + mov DST_YSTRIDE,_dst_ystride + mov SRC_YSTRIDE,_src_ystride + mov DST,_dst + mov SRC1,_src1 + mov SRC2,_src2 + movq mm0,[SRC1] + movq mm1,[SRC2] + movq mm2,[SRC1+SRC_YSTRIDE] + lea SRC1,[SRC1+SRC_YSTRIDE*2] + movq mm3,[SRC2+SRC_YSTRIDE] + lea SRC2,[SRC2+SRC_YSTRIDE*2] + pxor mm7,mm7 + movq mm4,[SRC1] + pcmpeqb mm6,mm6 + movq mm5,[SRC2] + /*mm7={1}x8.*/ + psubb mm7,mm6 + /*Start averaging mm0 and mm1 into mm6.*/ + movq mm6,mm0 + pxor mm0,mm1 + pavgb mm6,mm1 + /*mm1 is free, start averaging mm3 into mm2 using mm1.*/ + movq mm1,mm2 + pand mm0,mm7 + pavgb mm2,mm3 + pxor mm1,mm3 + /*mm3 is free.*/ + psubb mm6,mm0 + /*mm0 is free, start loading the next row.*/ + movq mm0,[SRC1+SRC_YSTRIDE] + /*Start averaging mm5 and mm4 using mm3.*/ + movq mm3,mm4 + /*mm6 [row 0] is done; write it out.*/ + movq [DST],mm6 + pand mm1,mm7 + pavgb mm4,mm5 + psubb mm2,mm1 + /*mm1 is free, continue loading the next row.*/ + movq mm1,[SRC2+SRC_YSTRIDE] + pxor mm3,mm5 + lea SRC1,[SRC1+SRC_YSTRIDE*2] + /*mm2 [row 1] is done; write it out.*/ + movq [DST+DST_YSTRIDE],mm2 + pand mm3,mm7 + /*Start loading the next row.*/ + movq mm2,[SRC1] + lea DST,[DST+DST_YSTRIDE*2] + psubb mm4,mm3 + lea SRC2,[SRC2+SRC_YSTRIDE*2] + /*mm4 [row 2] is done; write it out.*/ + movq [DST],mm4 + /*Continue loading the next row.*/ + movq mm3,[SRC2] + /*Start averaging mm0 and mm1 into mm6.*/ + movq mm6,mm0 + pxor mm0,mm1 + /*Start loading the next row.*/ + movq mm4,[SRC1+SRC_YSTRIDE] + pavgb mm6,mm1 + /*mm1 is free; start averaging mm3 into mm2 using mm1.*/ + movq mm1,mm2 + pand mm0,mm7 + /*Continue loading the next row.*/ + movq mm5,[SRC2+SRC_YSTRIDE] + pavgb mm2,mm3 + lea SRC1,[SRC1+SRC_YSTRIDE*2] + pxor mm1,mm3 + /*mm3 is free.*/ + psubb mm6,mm0 + /*mm0 is free, start loading the next row.*/ + movq mm0,[SRC1] + /*Start averaging mm5 into mm4 using mm3.*/ + movq mm3,mm4 + /*mm6 [row 3] is done; write it out.*/ + movq [DST+DST_YSTRIDE],mm6 + pand mm1,mm7 + lea SRC2,[SRC2+SRC_YSTRIDE*2] + pavgb mm4,mm5 + lea DST,[DST+DST_YSTRIDE*2] + psubb mm2,mm1 + /*mm1 is free; continue loading the next row.*/ + movq mm1,[SRC2] + pxor mm3,mm5 + /*mm2 [row 4] is done; write it out.*/ + movq [DST],mm2 + pand mm3,mm7 + /*Start loading the next row.*/ + movq mm2,[SRC1+SRC_YSTRIDE] + psubb mm4,mm3 + /*Start averaging mm0 and mm1 into mm6.*/ + movq mm6,mm0 + /*Continue loading the next row.*/ + movq mm3,[SRC2+SRC_YSTRIDE] + /*mm4 [row 5] is done; write it out.*/ + movq [DST+DST_YSTRIDE],mm4 + pxor mm0,mm1 + pavgb mm6,mm1 + /*mm4 is free; start averaging mm3 into mm2 using mm4.*/ + movq mm4,mm2 + pand mm0,mm7 + pavgb mm2,mm3 + pxor mm4,mm3 + lea DST,[DST+DST_YSTRIDE*2] + psubb mm6,mm0 + pand mm4,mm7 + /*mm6 [row 6] is done, write it out.*/ + movq [DST],mm6 + psubb mm2,mm4 + /*mm2 [row 7] is done, write it out.*/ + movq [DST+DST_YSTRIDE],mm2 +#undef SRC1 +#undef SRC2 +#undef SRC_YSTRIDE +#undef DST_YSTRIDE +#undef DST + } +} + +unsigned oc_enc_frag_satd2_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride, + unsigned _thresh){ + OC_ALIGN8(unsigned char ref[64]); + oc_int_frag_copy2_mmxext(ref,8,_ref1,_ref2,_ystride); + return oc_int_frag_satd_thresh_mmxext(_src,_ystride,ref,8,_thresh); +} + +unsigned oc_enc_frag_intra_satd_mmxext(const unsigned char *_src, + int _ystride){ + OC_ALIGN8(ogg_int16_t buf[64]); + ogg_int16_t *bufp; + unsigned ret1; + unsigned ret2; + bufp=buf; + __asm{ +#define SRC eax +#define SRC4 esi +#define BUF edi +#define RET eax +#define RET_WORD ax +#define RET2 ecx +#define YSTRIDE edx +#define YSTRIDE3 ecx + mov SRC,_src + mov BUF,bufp + mov YSTRIDE,_ystride + /* src4 = src+4*ystride */ + lea SRC4,[SRC+YSTRIDE*4] + /* ystride3 = 3*ystride */ + lea YSTRIDE3,[YSTRIDE+YSTRIDE*2] + OC_LOAD_8x4(0x00) + OC_HADAMARD_8x4 + OC_TRANSPOSE_4x4x2(0x00) + /*Finish swapping out this 8x4 block to make room for the next one. + mm0...mm3 have been swapped out already.*/ + movq [0x00+BUF],mm4 + movq [0x10+BUF],mm5 + movq [0x20+BUF],mm6 + movq [0x30+BUF],mm7 + OC_LOAD_8x4(0x04) + OC_HADAMARD_8x4 + OC_TRANSPOSE_4x4x2(0x08) + /*Here the first 4x4 block of output from the last transpose is the second + 4x4 block of input for the next transform. + We have cleverly arranged that it already be in the appropriate place, so + we only have to do half the loads.*/ + movq mm1,[0x10+BUF] + movq mm2,[0x20+BUF] + movq mm3,[0x30+BUF] + movq mm0,[0x00+BUF] + /*We split out the stages here so we can save the DC coefficient in the + middle.*/ + OC_HADAMARD_AB_8x4 + OC_HADAMARD_C_ABS_ACCUM_A_8x4(0x28,0x38) + movd RET,mm1 + OC_HADAMARD_C_ABS_ACCUM_B_8x4(0x28,0x38) + /*Up to this point, everything fit in 16 bits (8 input + 1 for the + difference + 2*3 for the two 8-point 1-D Hadamards - 1 for the abs - 1 + for the factor of two we dropped + 3 for the vertical accumulation). + Now we finally have to promote things to dwords. + We break this part out of OC_HADAMARD_ABS_ACCUM_8x4 to hide the long + latency of pmaddwd by starting the next series of loads now.*/ + pmaddwd mm0,mm7 + movq mm1,[0x50+BUF] + movq mm5,[0x58+BUF] + movq mm2,[0x60+BUF] + movq mm4,mm0 + movq mm6,[0x68+BUF] + punpckhdq mm0,mm0 + movq mm3,[0x70+BUF] + paddd mm4,mm0 + movq mm7,[0x78+BUF] + movd RET2,mm4 + movq mm0,[0x40+BUF] + movq mm4,[0x48+BUF] + OC_HADAMARD_ABS_ACCUM_8x4(0x68,0x78) + pmaddwd mm0,mm7 + /*We assume that the DC coefficient is always positive (which is true, + because the input to the INTRA transform was not a difference).*/ + movzx RET,RET_WORD + add RET2,RET2 + sub RET2,RET + movq mm4,mm0 + punpckhdq mm0,mm0 + paddd mm4,mm0 + movd RET,mm4 + lea RET,[-64+RET2+RET*2] + mov [ret1],RET +#undef SRC +#undef SRC4 +#undef BUF +#undef RET +#undef RET_WORD +#undef RET2 +#undef YSTRIDE +#undef YSTRIDE3 + } + return ret1; +} + +void oc_enc_frag_sub_mmx(ogg_int16_t _residue[64], + const unsigned char *_src, const unsigned char *_ref,int _ystride){ + int i; + __asm pxor mm7,mm7 + for(i=4;i-->0;){ + __asm{ +#define SRC edx +#define YSTRIDE esi +#define RESIDUE eax +#define REF ecx + mov YSTRIDE,_ystride + mov RESIDUE,_residue + mov SRC,_src + mov REF,_ref + /*mm0=[src]*/ + movq mm0,[SRC] + /*mm1=[ref]*/ + movq mm1,[REF] + /*mm4=[src+ystride]*/ + movq mm4,[SRC+YSTRIDE] + /*mm5=[ref+ystride]*/ + movq mm5,[REF+YSTRIDE] + /*Compute [src]-[ref].*/ + movq mm2,mm0 + punpcklbw mm0,mm7 + movq mm3,mm1 + punpckhbw mm2,mm7 + punpcklbw mm1,mm7 + punpckhbw mm3,mm7 + psubw mm0,mm1 + psubw mm2,mm3 + /*Compute [src+ystride]-[ref+ystride].*/ + movq mm1,mm4 + punpcklbw mm4,mm7 + movq mm3,mm5 + punpckhbw mm1,mm7 + lea SRC,[SRC+YSTRIDE*2] + punpcklbw mm5,mm7 + lea REF,[REF+YSTRIDE*2] + punpckhbw mm3,mm7 + psubw mm4,mm5 + psubw mm1,mm3 + /*Write the answer out.*/ + movq [RESIDUE+0x00],mm0 + movq [RESIDUE+0x08],mm2 + movq [RESIDUE+0x10],mm4 + movq [RESIDUE+0x18],mm1 + lea RESIDUE,[RESIDUE+0x20] + mov _residue,RESIDUE + mov _src,SRC + mov _ref,REF +#undef SRC +#undef YSTRIDE +#undef RESIDUE +#undef REF + } + } +} + +void oc_enc_frag_sub_128_mmx(ogg_int16_t _residue[64], + const unsigned char *_src,int _ystride){ + __asm{ +#define YSTRIDE edx +#define YSTRIDE3 edi +#define RESIDUE ecx +#define SRC eax + mov YSTRIDE,_ystride + mov RESIDUE,_residue + mov SRC,_src + /*mm0=[src]*/ + movq mm0,[SRC] + /*mm1=[src+ystride]*/ + movq mm1,[SRC+YSTRIDE] + /*mm6={-1}x4*/ + pcmpeqw mm6,mm6 + /*mm2=[src+2*ystride]*/ + movq mm2,[SRC+YSTRIDE*2] + /*[ystride3]=3*[ystride]*/ + lea YSTRIDE3,[YSTRIDE+YSTRIDE*2] + /*mm6={1}x4*/ + psllw mm6,15 + /*mm3=[src+3*ystride]*/ + movq mm3,[SRC+YSTRIDE3] + /*mm6={128}x4*/ + psrlw mm6,8 + /*mm7=0*/ + pxor mm7,mm7 + /*[src]=[src]+4*[ystride]*/ + lea SRC,[SRC+YSTRIDE*4] + /*Compute [src]-128 and [src+ystride]-128*/ + movq mm4,mm0 + punpcklbw mm0,mm7 + movq mm5,mm1 + punpckhbw mm4,mm7 + psubw mm0,mm6 + punpcklbw mm1,mm7 + psubw mm4,mm6 + punpckhbw mm5,mm7 + psubw mm1,mm6 + psubw mm5,mm6 + /*Write the answer out.*/ + movq [RESIDUE+0x00],mm0 + movq [RESIDUE+0x08],mm4 + movq [RESIDUE+0x10],mm1 + movq [RESIDUE+0x18],mm5 + /*mm0=[src+4*ystride]*/ + movq mm0,[SRC] + /*mm1=[src+5*ystride]*/ + movq mm1,[SRC+YSTRIDE] + /*Compute [src+2*ystride]-128 and [src+3*ystride]-128*/ + movq mm4,mm2 + punpcklbw mm2,mm7 + movq mm5,mm3 + punpckhbw mm4,mm7 + psubw mm2,mm6 + punpcklbw mm3,mm7 + psubw mm4,mm6 + punpckhbw mm5,mm7 + psubw mm3,mm6 + psubw mm5,mm6 + /*Write the answer out.*/ + movq [RESIDUE+0x20],mm2 + movq [RESIDUE+0x28],mm4 + movq [RESIDUE+0x30],mm3 + movq [RESIDUE+0x38],mm5 + /*Compute [src+6*ystride]-128 and [src+7*ystride]-128*/ + movq mm2,[SRC+YSTRIDE*2] + movq mm3,[SRC+YSTRIDE3] + movq mm4,mm0 + punpcklbw mm0,mm7 + movq mm5,mm1 + punpckhbw mm4,mm7 + psubw mm0,mm6 + punpcklbw mm1,mm7 + psubw mm4,mm6 + punpckhbw mm5,mm7 + psubw mm1,mm6 + psubw mm5,mm6 + /*Write the answer out.*/ + movq [RESIDUE+0x40],mm0 + movq [RESIDUE+0x48],mm4 + movq [RESIDUE+0x50],mm1 + movq [RESIDUE+0x58],mm5 + /*Compute [src+6*ystride]-128 and [src+7*ystride]-128*/ + movq mm4,mm2 + punpcklbw mm2,mm7 + movq mm5,mm3 + punpckhbw mm4,mm7 + psubw mm2,mm6 + punpcklbw mm3,mm7 + psubw mm4,mm6 + punpckhbw mm5,mm7 + psubw mm3,mm6 + psubw mm5,mm6 + /*Write the answer out.*/ + movq [RESIDUE+0x60],mm2 + movq [RESIDUE+0x68],mm4 + movq [RESIDUE+0x70],mm3 + movq [RESIDUE+0x78],mm5 +#undef YSTRIDE +#undef YSTRIDE3 +#undef RESIDUE +#undef SRC + } +} + +void oc_enc_frag_copy2_mmxext(unsigned char *_dst, + const unsigned char *_src1,const unsigned char *_src2,int _ystride){ + oc_int_frag_copy2_mmxext(_dst,_ystride,_src1,_src2,_ystride); +} + +#endif diff --git a/Engine/lib/libtheora/lib/x86_vc/mmxfdct.c b/Engine/lib/libtheora/lib/x86_vc/mmxfdct.c new file mode 100644 index 000000000..d908ce241 --- /dev/null +++ b/Engine/lib/libtheora/lib/x86_vc/mmxfdct.c @@ -0,0 +1,670 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 1999-2006 * + * by the Xiph.Org Foundation http://www.xiph.org/ * + * * + ********************************************************************/ + /*MMX fDCT implementation for x86_32*/ +/*$Id: fdct_ses2.c 14579 2008-03-12 06:42:40Z xiphmont $*/ +#include "x86enc.h" + +#if defined(OC_X86_ASM) + +#define OC_FDCT_STAGE1_8x4 __asm{ \ + /*Stage 1:*/ \ + /*mm0=t7'=t0-t7*/ \ + __asm psubw mm0,mm7 \ + __asm paddw mm7,mm7 \ + /*mm1=t6'=t1-t6*/ \ + __asm psubw mm1, mm6 \ + __asm paddw mm6,mm6 \ + /*mm2=t5'=t2-t5*/ \ + __asm psubw mm2,mm5 \ + __asm paddw mm5,mm5 \ + /*mm3=t4'=t3-t4*/ \ + __asm psubw mm3,mm4 \ + __asm paddw mm4,mm4 \ + /*mm7=t0'=t0+t7*/ \ + __asm paddw mm7,mm0 \ + /*mm6=t1'=t1+t6*/ \ + __asm paddw mm6,mm1 \ + /*mm5=t2'=t2+t5*/ \ + __asm paddw mm5,mm2 \ + /*mm4=t3'=t3+t4*/ \ + __asm paddw mm4,mm3\ +} + +#define OC_FDCT8x4(_r0,_r1,_r2,_r3,_r4,_r5,_r6,_r7) __asm{ \ + /*Stage 2:*/ \ + /*mm7=t3''=t0'-t3'*/ \ + __asm psubw mm7,mm4 \ + __asm paddw mm4,mm4 \ + /*mm6=t2''=t1'-t2'*/ \ + __asm psubw mm6,mm5 \ + __asm movq [Y+_r6],mm7 \ + __asm paddw mm5,mm5 \ + /*mm1=t5''=t6'-t5'*/ \ + __asm psubw mm1,mm2 \ + __asm movq [Y+_r2],mm6 \ + /*mm4=t0''=t0'+t3'*/ \ + __asm paddw mm4,mm7 \ + __asm paddw mm2,mm2 \ + /*mm5=t1''=t1'+t2'*/ \ + __asm movq [Y+_r0],mm4 \ + __asm paddw mm5,mm6 \ + /*mm2=t6''=t6'+t5'*/ \ + __asm paddw mm2,mm1 \ + __asm movq [Y+_r4],mm5 \ + /*mm0=t7', mm1=t5'', mm2=t6'', mm3=t4'.*/ \ + /*mm4, mm5, mm6, mm7 are free.*/ \ + /*Stage 3:*/ \ + /*mm6={2}x4, mm7={27146,0xB500>>1}x2*/ \ + __asm mov A,0x5A806A0A \ + __asm pcmpeqb mm6,mm6 \ + __asm movd mm7,A \ + __asm psrlw mm6,15 \ + __asm punpckldq mm7,mm7 \ + __asm paddw mm6,mm6 \ + /*mm0=0, m2={-1}x4 \ + mm5:mm4=t5''*27146+0xB500*/ \ + __asm movq mm4,mm1 \ + __asm movq mm5,mm1 \ + __asm punpcklwd mm4,mm6 \ + __asm movq [Y+_r3],mm2 \ + __asm pmaddwd mm4,mm7 \ + __asm movq [Y+_r7],mm0 \ + __asm punpckhwd mm5,mm6 \ + __asm pxor mm0,mm0 \ + __asm pmaddwd mm5,mm7 \ + __asm pcmpeqb mm2,mm2 \ + /*mm2=t6'', mm1=t5''+(t5''!=0) \ + mm4=(t5''*27146+0xB500>>16)*/ \ + __asm pcmpeqw mm0,mm1 \ + __asm psrad mm4,16 \ + __asm psubw mm0,mm2 \ + __asm movq mm2, [Y+_r3] \ + __asm psrad mm5,16 \ + __asm paddw mm1,mm0 \ + __asm packssdw mm4,mm5 \ + /*mm4=s=(t5''*27146+0xB500>>16)+t5''+(t5''!=0)>>1*/ \ + __asm paddw mm4,mm1 \ + __asm movq mm0, [Y+_r7] \ + __asm psraw mm4,1 \ + __asm movq mm1,mm3 \ + /*mm3=t4''=t4'+s*/ \ + __asm paddw mm3,mm4 \ + /*mm1=t5'''=t4'-s*/ \ + __asm psubw mm1,mm4 \ + /*mm1=0, mm3={-1}x4 \ + mm5:mm4=t6''*27146+0xB500*/ \ + __asm movq mm4,mm2 \ + __asm movq mm5,mm2 \ + __asm punpcklwd mm4,mm6 \ + __asm movq [Y+_r5],mm1 \ + __asm pmaddwd mm4,mm7 \ + __asm movq [Y+_r1],mm3 \ + __asm punpckhwd mm5,mm6 \ + __asm pxor mm1,mm1 \ + __asm pmaddwd mm5,mm7 \ + __asm pcmpeqb mm3,mm3 \ + /*mm2=t6''+(t6''!=0), mm4=(t6''*27146+0xB500>>16)*/ \ + __asm psrad mm4,16 \ + __asm pcmpeqw mm1,mm2 \ + __asm psrad mm5,16 \ + __asm psubw mm1,mm3 \ + __asm packssdw mm4,mm5 \ + __asm paddw mm2,mm1 \ + /*mm1=t1'' \ + mm4=s=(t6''*27146+0xB500>>16)+t6''+(t6''!=0)>>1*/ \ + __asm paddw mm4,mm2 \ + __asm movq mm1,[Y+_r4] \ + __asm psraw mm4,1 \ + __asm movq mm2,mm0 \ + /*mm7={54491-0x7FFF,0x7FFF}x2 \ + mm0=t7''=t7'+s*/ \ + __asm paddw mm0,mm4 \ + /*mm2=t6'''=t7'-s*/ \ + __asm psubw mm2,mm4 \ + /*Stage 4:*/ \ + /*mm0=0, mm2=t0'' \ + mm5:mm4=t1''*27146+0xB500*/ \ + __asm movq mm4,mm1 \ + __asm movq mm5,mm1 \ + __asm punpcklwd mm4,mm6 \ + __asm movq [Y+_r3],mm2 \ + __asm pmaddwd mm4,mm7 \ + __asm movq mm2,[Y+_r0] \ + __asm punpckhwd mm5,mm6 \ + __asm movq [Y+_r7],mm0 \ + __asm pmaddwd mm5,mm7 \ + __asm pxor mm0,mm0 \ + /*mm7={27146,0x4000>>1}x2 \ + mm0=s=(t1''*27146+0xB500>>16)+t1''+(t1''!=0)*/ \ + __asm psrad mm4,16 \ + __asm mov A,0x20006A0A \ + __asm pcmpeqw mm0,mm1 \ + __asm movd mm7,A \ + __asm psrad mm5,16 \ + __asm psubw mm0,mm3 \ + __asm packssdw mm4,mm5 \ + __asm paddw mm0,mm1 \ + __asm punpckldq mm7,mm7 \ + __asm paddw mm0,mm4 \ + /*mm6={0x00000E3D}x2 \ + mm1=-(t0''==0), mm5:mm4=t0''*27146+0x4000*/ \ + __asm movq mm4,mm2 \ + __asm movq mm5,mm2 \ + __asm punpcklwd mm4,mm6 \ + __asm mov A,0x0E3D \ + __asm pmaddwd mm4,mm7 \ + __asm punpckhwd mm5,mm6 \ + __asm movd mm6,A \ + __asm pmaddwd mm5,mm7 \ + __asm pxor mm1,mm1 \ + __asm punpckldq mm6,mm6 \ + __asm pcmpeqw mm1,mm2 \ + /*mm4=r=(t0''*27146+0x4000>>16)+t0''+(t0''!=0)*/ \ + __asm psrad mm4,16 \ + __asm psubw mm1,mm3 \ + __asm psrad mm5,16 \ + __asm paddw mm2,mm1 \ + __asm packssdw mm4,mm5 \ + __asm movq mm1,[Y+_r5] \ + __asm paddw mm4,mm2 \ + /*mm2=t6'', mm0=_y[0]=u=r+s>>1 \ + The naive implementation could cause overflow, so we use \ + u=(r&s)+((r^s)>>1).*/ \ + __asm movq mm2,[Y+_r3] \ + __asm movq mm7,mm0 \ + __asm pxor mm0,mm4 \ + __asm pand mm7,mm4 \ + __asm psraw mm0,1 \ + __asm mov A,0x7FFF54DC \ + __asm paddw mm0,mm7 \ + __asm movd mm7,A \ + /*mm7={54491-0x7FFF,0x7FFF}x2 \ + mm4=_y[4]=v=r-u*/ \ + __asm psubw mm4,mm0 \ + __asm punpckldq mm7,mm7 \ + __asm movq [Y+_r4],mm4 \ + /*mm0=0, mm7={36410}x4 \ + mm1=(t5'''!=0), mm5:mm4=54491*t5'''+0x0E3D*/ \ + __asm movq mm4,mm1 \ + __asm movq mm5,mm1 \ + __asm punpcklwd mm4,mm1 \ + __asm mov A,0x8E3A8E3A \ + __asm pmaddwd mm4,mm7 \ + __asm movq [Y+_r0],mm0 \ + __asm punpckhwd mm5,mm1 \ + __asm pxor mm0,mm0 \ + __asm pmaddwd mm5,mm7 \ + __asm pcmpeqw mm1,mm0 \ + __asm movd mm7,A \ + __asm psubw mm1,mm3 \ + __asm punpckldq mm7,mm7 \ + __asm paddd mm4,mm6 \ + __asm paddd mm5,mm6 \ + /*mm0=0 \ + mm3:mm1=36410*t6'''+((t5'''!=0)<<16)*/ \ + __asm movq mm6,mm2 \ + __asm movq mm3,mm2 \ + __asm pmulhw mm6,mm7 \ + __asm paddw mm1,mm2 \ + __asm pmullw mm3,mm7 \ + __asm pxor mm0,mm0 \ + __asm paddw mm6,mm1 \ + __asm movq mm1,mm3 \ + __asm punpckhwd mm3,mm6 \ + __asm punpcklwd mm1,mm6 \ + /*mm3={-1}x4, mm6={1}x4 \ + mm4=_y[5]=u=(54491*t5'''+36410*t6'''+0x0E3D>>16)+(t5'''!=0)*/ \ + __asm paddd mm5,mm3 \ + __asm paddd mm4,mm1 \ + __asm psrad mm5,16 \ + __asm pxor mm6,mm6 \ + __asm psrad mm4,16 \ + __asm pcmpeqb mm3,mm3 \ + __asm packssdw mm4,mm5 \ + __asm psubw mm6,mm3 \ + /*mm1=t7'', mm7={26568,0x3400}x2 \ + mm2=s=t6'''-(36410*u>>16)*/ \ + __asm movq mm1,mm4 \ + __asm mov A,0x340067C8 \ + __asm pmulhw mm4,mm7 \ + __asm movd mm7,A \ + __asm movq [Y+_r5],mm1 \ + __asm punpckldq mm7,mm7 \ + __asm paddw mm4,mm1 \ + __asm movq mm1,[Y+_r7] \ + __asm psubw mm2,mm4 \ + /*mm6={0x00007B1B}x2 \ + mm0=(s!=0), mm5:mm4=s*26568+0x3400*/ \ + __asm movq mm4,mm2 \ + __asm movq mm5,mm2 \ + __asm punpcklwd mm4,mm6 \ + __asm pcmpeqw mm0,mm2 \ + __asm pmaddwd mm4,mm7 \ + __asm mov A,0x7B1B \ + __asm punpckhwd mm5,mm6 \ + __asm movd mm6,A \ + __asm pmaddwd mm5,mm7 \ + __asm psubw mm0,mm3 \ + __asm punpckldq mm6,mm6 \ + /*mm7={64277-0x7FFF,0x7FFF}x2 \ + mm2=_y[3]=v=(s*26568+0x3400>>17)+s+(s!=0)*/ \ + __asm psrad mm4,17 \ + __asm paddw mm2,mm0 \ + __asm psrad mm5,17 \ + __asm mov A,0x7FFF7B16 \ + __asm packssdw mm4,mm5 \ + __asm movd mm7,A \ + __asm paddw mm2,mm4 \ + __asm punpckldq mm7,mm7 \ + /*mm0=0, mm7={12785}x4 \ + mm1=(t7''!=0), mm2=t4'', mm5:mm4=64277*t7''+0x7B1B*/ \ + __asm movq mm4,mm1 \ + __asm movq mm5,mm1 \ + __asm movq [Y+_r3],mm2 \ + __asm punpcklwd mm4,mm1 \ + __asm movq mm2,[Y+_r1] \ + __asm pmaddwd mm4,mm7 \ + __asm mov A,0x31F131F1 \ + __asm punpckhwd mm5,mm1 \ + __asm pxor mm0,mm0 \ + __asm pmaddwd mm5,mm7 \ + __asm pcmpeqw mm1,mm0 \ + __asm movd mm7,A \ + __asm psubw mm1,mm3 \ + __asm punpckldq mm7,mm7 \ + __asm paddd mm4,mm6 \ + __asm paddd mm5,mm6 \ + /*mm3:mm1=12785*t4'''+((t7''!=0)<<16)*/ \ + __asm movq mm6,mm2 \ + __asm movq mm3,mm2 \ + __asm pmulhw mm6,mm7 \ + __asm pmullw mm3,mm7 \ + __asm paddw mm6,mm1 \ + __asm movq mm1,mm3 \ + __asm punpckhwd mm3,mm6 \ + __asm punpcklwd mm1,mm6 \ + /*mm3={-1}x4, mm6={1}x4 \ + mm4=_y[1]=u=(12785*t4'''+64277*t7''+0x7B1B>>16)+(t7''!=0)*/ \ + __asm paddd mm5,mm3 \ + __asm paddd mm4,mm1 \ + __asm psrad mm5,16 \ + __asm pxor mm6,mm6 \ + __asm psrad mm4,16 \ + __asm pcmpeqb mm3,mm3 \ + __asm packssdw mm4,mm5 \ + __asm psubw mm6,mm3 \ + /*mm1=t3'', mm7={20539,0x3000}x2 \ + mm4=s=(12785*u>>16)-t4''*/ \ + __asm movq [Y+_r1],mm4 \ + __asm pmulhw mm4,mm7 \ + __asm mov A,0x3000503B \ + __asm movq mm1,[Y+_r6] \ + __asm movd mm7,A \ + __asm psubw mm4,mm2 \ + __asm punpckldq mm7,mm7 \ + /*mm6={0x00006CB7}x2 \ + mm0=(s!=0), mm5:mm4=s*20539+0x3000*/ \ + __asm movq mm5,mm4 \ + __asm movq mm2,mm4 \ + __asm punpcklwd mm4,mm6 \ + __asm pcmpeqw mm0,mm2 \ + __asm pmaddwd mm4,mm7 \ + __asm mov A,0x6CB7 \ + __asm punpckhwd mm5,mm6 \ + __asm movd mm6,A \ + __asm pmaddwd mm5,mm7 \ + __asm psubw mm0,mm3 \ + __asm punpckldq mm6,mm6 \ + /*mm7={60547-0x7FFF,0x7FFF}x2 \ + mm2=_y[7]=v=(s*20539+0x3000>>20)+s+(s!=0)*/ \ + __asm psrad mm4,20 \ + __asm paddw mm2,mm0 \ + __asm psrad mm5,20 \ + __asm mov A,0x7FFF6C84 \ + __asm packssdw mm4,mm5 \ + __asm movd mm7,A \ + __asm paddw mm2,mm4 \ + __asm punpckldq mm7,mm7 \ + /*mm0=0, mm7={25080}x4 \ + mm2=t2'', mm5:mm4=60547*t3''+0x6CB7*/ \ + __asm movq mm4,mm1 \ + __asm movq mm5,mm1 \ + __asm movq [Y+_r7],mm2 \ + __asm punpcklwd mm4,mm1 \ + __asm movq mm2,[Y+_r2] \ + __asm pmaddwd mm4,mm7 \ + __asm mov A,0x61F861F8 \ + __asm punpckhwd mm5,mm1 \ + __asm pxor mm0,mm0 \ + __asm pmaddwd mm5,mm7 \ + __asm movd mm7,A \ + __asm pcmpeqw mm1,mm0 \ + __asm psubw mm1,mm3 \ + __asm punpckldq mm7,mm7 \ + __asm paddd mm4,mm6 \ + __asm paddd mm5,mm6 \ + /*mm3:mm1=25080*t2''+((t3''!=0)<<16)*/ \ + __asm movq mm6,mm2 \ + __asm movq mm3,mm2 \ + __asm pmulhw mm6,mm7 \ + __asm pmullw mm3,mm7 \ + __asm paddw mm6,mm1 \ + __asm movq mm1,mm3 \ + __asm punpckhwd mm3,mm6 \ + __asm punpcklwd mm1,mm6 \ + /*mm1={-1}x4 \ + mm4=u=(25080*t2''+60547*t3''+0x6CB7>>16)+(t3''!=0)*/ \ + __asm paddd mm5,mm3 \ + __asm paddd mm4,mm1 \ + __asm psrad mm5,16 \ + __asm mov A,0x28005460 \ + __asm psrad mm4,16 \ + __asm pcmpeqb mm1,mm1 \ + __asm packssdw mm4,mm5 \ + /*mm5={1}x4, mm6=_y[2]=u, mm7={21600,0x2800}x2 \ + mm4=s=(25080*u>>16)-t2''*/ \ + __asm movq mm6,mm4 \ + __asm pmulhw mm4,mm7 \ + __asm pxor mm5,mm5 \ + __asm movd mm7,A \ + __asm psubw mm5,mm1 \ + __asm punpckldq mm7,mm7 \ + __asm psubw mm4,mm2 \ + /*mm2=s+(s!=0) \ + mm4:mm3=s*21600+0x2800*/ \ + __asm movq mm3,mm4 \ + __asm movq mm2,mm4 \ + __asm punpckhwd mm4,mm5 \ + __asm pcmpeqw mm0,mm2 \ + __asm pmaddwd mm4,mm7 \ + __asm psubw mm0,mm1 \ + __asm punpcklwd mm3,mm5 \ + __asm paddw mm2,mm0 \ + __asm pmaddwd mm3,mm7 \ + /*mm0=_y[4], mm1=_y[7], mm4=_y[0], mm5=_y[5] \ + mm3=_y[6]=v=(s*21600+0x2800>>18)+s+(s!=0)*/ \ + __asm movq mm0,[Y+_r4] \ + __asm psrad mm4,18 \ + __asm movq mm5,[Y+_r5] \ + __asm psrad mm3,18 \ + __asm movq mm1,[Y+_r7] \ + __asm packssdw mm3,mm4 \ + __asm movq mm4,[Y+_r0] \ + __asm paddw mm3,mm2 \ +} + +/*On input, mm4=_y[0], mm6=_y[2], mm0=_y[4], mm5=_y[5], mm3=_y[6], mm1=_y[7]. + On output, {_y[4],mm1,mm2,mm3} contains the transpose of _y[4...7] and + {mm4,mm5,mm6,mm7} contains the transpose of _y[0...3].*/ +#define OC_TRANSPOSE8x4(_r0,_r1,_r2,_r3,_r4,_r5,_r6,_r7) __asm{ \ + /*First 4x4 transpose:*/ \ + /*mm0 = e3 e2 e1 e0 \ + mm5 = f3 f2 f1 f0 \ + mm3 = g3 g2 g1 g0 \ + mm1 = h3 h2 h1 h0*/ \ + __asm movq mm2,mm0 \ + __asm punpcklwd mm0,mm5 \ + __asm punpckhwd mm2,mm5 \ + __asm movq mm5,mm3 \ + __asm punpcklwd mm3,mm1 \ + __asm punpckhwd mm5,mm1 \ + /*mm0 = f1 e1 f0 e0 \ + mm2 = f3 e3 f2 e2 \ + mm3 = h1 g1 h0 g0 \ + mm5 = h3 g3 h2 g2*/ \ + __asm movq mm1,mm0 \ + __asm punpckldq mm0,mm3 \ + __asm movq [Y+_r4],mm0 \ + __asm punpckhdq mm1,mm3 \ + __asm movq mm0,[Y+_r1] \ + __asm movq mm3,mm2 \ + __asm punpckldq mm2,mm5 \ + __asm punpckhdq mm3,mm5 \ + __asm movq mm5,[Y+_r3] \ + /*_y[4] = h0 g0 f0 e0 \ + mm1 = h1 g1 f1 e1 \ + mm2 = h2 g2 f2 e2 \ + mm3 = h3 g3 f3 e3*/ \ + /*Second 4x4 transpose:*/ \ + /*mm4 = a3 a2 a1 a0 \ + mm0 = b3 b2 b1 b0 \ + mm6 = c3 c2 c1 c0 \ + mm5 = d3 d2 d1 d0*/ \ + __asm movq mm7,mm4 \ + __asm punpcklwd mm4,mm0 \ + __asm punpckhwd mm7,mm0 \ + __asm movq mm0,mm6 \ + __asm punpcklwd mm6,mm5 \ + __asm punpckhwd mm0,mm5 \ + /*mm4 = b1 a1 b0 a0 \ + mm7 = b3 a3 b2 a2 \ + mm6 = d1 c1 d0 c0 \ + mm0 = d3 c3 d2 c2*/ \ + __asm movq mm5,mm4 \ + __asm punpckldq mm4,mm6 \ + __asm punpckhdq mm5,mm6 \ + __asm movq mm6,mm7 \ + __asm punpckhdq mm7,mm0 \ + __asm punpckldq mm6,mm0 \ + /*mm4 = d0 c0 b0 a0 \ + mm5 = d1 c1 b1 a1 \ + mm6 = d2 c2 b2 a2 \ + mm7 = d3 c3 b3 a3*/ \ +} + +/*MMX implementation of the fDCT.*/ +void oc_enc_fdct8x8_mmx(ogg_int16_t _y[64],const ogg_int16_t _x[64]){ + ptrdiff_t a; + __asm{ +#define Y eax +#define A ecx +#define X edx + /*Add two extra bits of working precision to improve accuracy; any more and + we could overflow.*/ + /*We also add biases to correct for some systematic error that remains in + the full fDCT->iDCT round trip.*/ + mov X, _x + mov Y, _y + movq mm0,[0x00+X] + movq mm1,[0x10+X] + movq mm2,[0x20+X] + movq mm3,[0x30+X] + pcmpeqb mm4,mm4 + pxor mm7,mm7 + movq mm5,mm0 + psllw mm0,2 + pcmpeqw mm5,mm7 + movq mm7,[0x70+X] + psllw mm1,2 + psubw mm5,mm4 + psllw mm2,2 + mov A,1 + pslld mm5,16 + movd mm6,A + psllq mm5,16 + mov A,0x10001 + psllw mm3,2 + movd mm4,A + punpckhwd mm5,mm6 + psubw mm1,mm6 + movq mm6,[0x60+X] + paddw mm0,mm5 + movq mm5,[0x50+X] + paddw mm0,mm4 + movq mm4,[0x40+X] + /*We inline stage1 of the transform here so we can get better instruction + scheduling with the shifts.*/ + /*mm0=t7'=t0-t7*/ + psllw mm7,2 + psubw mm0,mm7 + psllw mm6,2 + paddw mm7,mm7 + /*mm1=t6'=t1-t6*/ + psllw mm5,2 + psubw mm1,mm6 + psllw mm4,2 + paddw mm6,mm6 + /*mm2=t5'=t2-t5*/ + psubw mm2,mm5 + paddw mm5,mm5 + /*mm3=t4'=t3-t4*/ + psubw mm3,mm4 + paddw mm4,mm4 + /*mm7=t0'=t0+t7*/ + paddw mm7,mm0 + /*mm6=t1'=t1+t6*/ + paddw mm6,mm1 + /*mm5=t2'=t2+t5*/ + paddw mm5,mm2 + /*mm4=t3'=t3+t4*/ + paddw mm4,mm3 + OC_FDCT8x4(0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70) + OC_TRANSPOSE8x4(0x00,0x10,0x20,0x30,0x40,0x50,0x60,0x70) + /*Swap out this 8x4 block for the next one.*/ + movq mm0,[0x08+X] + movq [0x30+Y],mm7 + movq mm7,[0x78+X] + movq [0x50+Y],mm1 + movq mm1,[0x18+X] + movq [0x20+Y],mm6 + movq mm6,[0x68+X] + movq [0x60+Y],mm2 + movq mm2,[0x28+X] + movq [0x10+Y],mm5 + movq mm5,[0x58+X] + movq [0x70+Y],mm3 + movq mm3,[0x38+X] + /*And increase its working precision, too.*/ + psllw mm0,2 + movq [0x00+Y],mm4 + psllw mm7,2 + movq mm4,[0x48+X] + /*We inline stage1 of the transform here so we can get better instruction + scheduling with the shifts.*/ + /*mm0=t7'=t0-t7*/ + psubw mm0,mm7 + psllw mm1,2 + paddw mm7,mm7 + psllw mm6,2 + /*mm1=t6'=t1-t6*/ + psubw mm1,mm6 + psllw mm2,2 + paddw mm6,mm6 + psllw mm5,2 + /*mm2=t5'=t2-t5*/ + psubw mm2,mm5 + psllw mm3,2 + paddw mm5,mm5 + psllw mm4,2 + /*mm3=t4'=t3-t4*/ + psubw mm3,mm4 + paddw mm4,mm4 + /*mm7=t0'=t0+t7*/ + paddw mm7,mm0 + /*mm6=t1'=t1+t6*/ + paddw mm6,mm1 + /*mm5=t2'=t2+t5*/ + paddw mm5,mm2 + /*mm4=t3'=t3+t4*/ + paddw mm4,mm3 + OC_FDCT8x4(0x08,0x18,0x28,0x38,0x48,0x58,0x68,0x78) + OC_TRANSPOSE8x4(0x08,0x18,0x28,0x38,0x48,0x58,0x68,0x78) + /*Here the first 4x4 block of output from the last transpose is the second + 4x4 block of input for the next transform. + We have cleverly arranged that it already be in the appropriate place, + so we only have to do half the stores and loads.*/ + movq mm0,[0x00+Y] + movq [0x58+Y],mm1 + movq mm1,[0x10+Y] + movq [0x68+Y],mm2 + movq mm2,[0x20+Y] + movq [0x78+Y],mm3 + movq mm3,[0x30+Y] + OC_FDCT_STAGE1_8x4 + OC_FDCT8x4(0x00,0x10,0x20,0x30,0x08,0x18,0x28,0x38) + OC_TRANSPOSE8x4(0x00,0x10,0x20,0x30,0x08,0x18,0x28,0x38) + /*mm0={-2}x4*/ + pcmpeqw mm0,mm0 + paddw mm0,mm0 + /*Round the results.*/ + psubw mm1,mm0 + psubw mm2,mm0 + psraw mm1,2 + psubw mm3,mm0 + movq [0x18+Y],mm1 + psraw mm2,2 + psubw mm4,mm0 + movq mm1,[0x08+Y] + psraw mm3,2 + psubw mm5,mm0 + psraw mm4,2 + psubw mm6,mm0 + psraw mm5,2 + psubw mm7,mm0 + psraw mm6,2 + psubw mm1,mm0 + psraw mm7,2 + movq mm0,[0x40+Y] + psraw mm1,2 + movq [0x30+Y],mm7 + movq mm7,[0x78+Y] + movq [0x08+Y],mm1 + movq mm1,[0x50+Y] + movq [0x20+Y],mm6 + movq mm6,[0x68+Y] + movq [0x28+Y],mm2 + movq mm2,[0x60+Y] + movq [0x10+Y],mm5 + movq mm5,[0x58+Y] + movq [0x38+Y],mm3 + movq mm3,[0x70+Y] + movq [0x00+Y],mm4 + movq mm4,[0x48+Y] + OC_FDCT_STAGE1_8x4 + OC_FDCT8x4(0x40,0x50,0x60,0x70,0x48,0x58,0x68,0x78) + OC_TRANSPOSE8x4(0x40,0x50,0x60,0x70,0x48,0x58,0x68,0x78) + /*mm0={-2}x4*/ + pcmpeqw mm0,mm0 + paddw mm0,mm0 + /*Round the results.*/ + psubw mm1,mm0 + psubw mm2,mm0 + psraw mm1,2 + psubw mm3,mm0 + movq [0x58+Y],mm1 + psraw mm2,2 + psubw mm4,mm0 + movq mm1,[0x48+Y] + psraw mm3,2 + psubw mm5,mm0 + movq [0x68+Y],mm2 + psraw mm4,2 + psubw mm6,mm0 + movq [0x78+Y],mm3 + psraw mm5,2 + psubw mm7,mm0 + movq [0x40+Y],mm4 + psraw mm6,2 + psubw mm1,mm0 + movq [0x50+Y],mm5 + psraw mm7,2 + movq [0x60+Y],mm6 + psraw mm1,2 + movq [0x70+Y],mm7 + movq [0x48+Y],mm1 +#undef Y +#undef A +#undef X + } +} + +#endif diff --git a/Engine/lib/libtheora/lib/x86_vc/mmxfrag.c b/Engine/lib/libtheora/lib/x86_vc/mmxfrag.c new file mode 100644 index 000000000..4eb2084dc --- /dev/null +++ b/Engine/lib/libtheora/lib/x86_vc/mmxfrag.c @@ -0,0 +1,337 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: mmxfrag.c 16578 2009-09-25 19:50:48Z cristianadam $ + + ********************************************************************/ + +/*MMX acceleration of fragment reconstruction for motion compensation. + Originally written by Rudolf Marek. + Additional optimization by Nils Pipenbrinck. + Note: Loops are unrolled for best performance. + The iteration each instruction belongs to is marked in the comments as #i.*/ +#include +#include "x86int.h" +#include "mmxfrag.h" + +#if defined(OC_X86_ASM) + +/*Copies an 8x8 block of pixels from _src to _dst, assuming _ystride bytes + between rows.*/ +void oc_frag_copy_mmx(unsigned char *_dst, + const unsigned char *_src,int _ystride){ +#define SRC edx +#define DST eax +#define YSTRIDE ecx +#define YSTRIDE3 esi + OC_FRAG_COPY_MMX(_dst,_src,_ystride); +#undef SRC +#undef DST +#undef YSTRIDE +#undef YSTRIDE3 +} + +void oc_frag_recon_intra_mmx(unsigned char *_dst,int _ystride, + const ogg_int16_t *_residue){ + __asm{ +#define DST edx +#define DST4 esi +#define YSTRIDE eax +#define YSTRIDE3 edi +#define RESIDUE ecx + mov DST,_dst + mov YSTRIDE,_ystride + mov RESIDUE,_residue + lea DST4,[DST+YSTRIDE*4] + lea YSTRIDE3,[YSTRIDE+YSTRIDE*2] + /*Set mm0 to 0xFFFFFFFFFFFFFFFF.*/ + pcmpeqw mm0,mm0 + /*#0 Load low residue.*/ + movq mm1,[0*8+RESIDUE] + /*#0 Load high residue.*/ + movq mm2,[1*8+RESIDUE] + /*Set mm0 to 0x8000800080008000.*/ + psllw mm0,15 + /*#1 Load low residue.*/ + movq mm3,[2*8+RESIDUE] + /*#1 Load high residue.*/ + movq mm4,[3*8+RESIDUE] + /*Set mm0 to 0x0080008000800080.*/ + psrlw mm0,8 + /*#2 Load low residue.*/ + movq mm5,[4*8+RESIDUE] + /*#2 Load high residue.*/ + movq mm6,[5*8+RESIDUE] + /*#0 Bias low residue.*/ + paddsw mm1,mm0 + /*#0 Bias high residue.*/ + paddsw mm2,mm0 + /*#0 Pack to byte.*/ + packuswb mm1,mm2 + /*#1 Bias low residue.*/ + paddsw mm3,mm0 + /*#1 Bias high residue.*/ + paddsw mm4,mm0 + /*#1 Pack to byte.*/ + packuswb mm3,mm4 + /*#2 Bias low residue.*/ + paddsw mm5,mm0 + /*#2 Bias high residue.*/ + paddsw mm6,mm0 + /*#2 Pack to byte.*/ + packuswb mm5,mm6 + /*#0 Write row.*/ + movq [DST],mm1 + /*#1 Write row.*/ + movq [DST+YSTRIDE],mm3 + /*#2 Write row.*/ + movq [DST+YSTRIDE*2],mm5 + /*#3 Load low residue.*/ + movq mm1,[6*8+RESIDUE] + /*#3 Load high residue.*/ + movq mm2,[7*8+RESIDUE] + /*#4 Load high residue.*/ + movq mm3,[8*8+RESIDUE] + /*#4 Load high residue.*/ + movq mm4,[9*8+RESIDUE] + /*#5 Load high residue.*/ + movq mm5,[10*8+RESIDUE] + /*#5 Load high residue.*/ + movq mm6,[11*8+RESIDUE] + /*#3 Bias low residue.*/ + paddsw mm1,mm0 + /*#3 Bias high residue.*/ + paddsw mm2,mm0 + /*#3 Pack to byte.*/ + packuswb mm1,mm2 + /*#4 Bias low residue.*/ + paddsw mm3,mm0 + /*#4 Bias high residue.*/ + paddsw mm4,mm0 + /*#4 Pack to byte.*/ + packuswb mm3,mm4 + /*#5 Bias low residue.*/ + paddsw mm5,mm0 + /*#5 Bias high residue.*/ + paddsw mm6,mm0 + /*#5 Pack to byte.*/ + packuswb mm5,mm6 + /*#3 Write row.*/ + movq [DST+YSTRIDE3],mm1 + /*#4 Write row.*/ + movq [DST4],mm3 + /*#5 Write row.*/ + movq [DST4+YSTRIDE],mm5 + /*#6 Load low residue.*/ + movq mm1,[12*8+RESIDUE] + /*#6 Load high residue.*/ + movq mm2,[13*8+RESIDUE] + /*#7 Load low residue.*/ + movq mm3,[14*8+RESIDUE] + /*#7 Load high residue.*/ + movq mm4,[15*8+RESIDUE] + /*#6 Bias low residue.*/ + paddsw mm1,mm0 + /*#6 Bias high residue.*/ + paddsw mm2,mm0 + /*#6 Pack to byte.*/ + packuswb mm1,mm2 + /*#7 Bias low residue.*/ + paddsw mm3,mm0 + /*#7 Bias high residue.*/ + paddsw mm4,mm0 + /*#7 Pack to byte.*/ + packuswb mm3,mm4 + /*#6 Write row.*/ + movq [DST4+YSTRIDE*2],mm1 + /*#7 Write row.*/ + movq [DST4+YSTRIDE3],mm3 +#undef DST +#undef DST4 +#undef YSTRIDE +#undef YSTRIDE3 +#undef RESIDUE + } +} + +void oc_frag_recon_inter_mmx(unsigned char *_dst,const unsigned char *_src, + int _ystride,const ogg_int16_t *_residue){ + int i; + /*Zero mm0.*/ + __asm pxor mm0,mm0; + for(i=4;i-->0;){ + __asm{ +#define DST edx +#define SRC ecx +#define YSTRIDE edi +#define RESIDUE eax + mov DST,_dst + mov SRC,_src + mov YSTRIDE,_ystride + mov RESIDUE,_residue + /*#0 Load source.*/ + movq mm3,[SRC] + /*#1 Load source.*/ + movq mm7,[SRC+YSTRIDE] + /*#0 Get copy of src.*/ + movq mm4,mm3 + /*#0 Expand high source.*/ + punpckhbw mm4,mm0 + /*#0 Expand low source.*/ + punpcklbw mm3,mm0 + /*#0 Add residue high.*/ + paddsw mm4,[8+RESIDUE] + /*#1 Get copy of src.*/ + movq mm2,mm7 + /*#0 Add residue low.*/ + paddsw mm3,[RESIDUE] + /*#1 Expand high source.*/ + punpckhbw mm2,mm0 + /*#0 Pack final row pixels.*/ + packuswb mm3,mm4 + /*#1 Expand low source.*/ + punpcklbw mm7,mm0 + /*#1 Add residue low.*/ + paddsw mm7,[16+RESIDUE] + /*#1 Add residue high.*/ + paddsw mm2,[24+RESIDUE] + /*Advance residue.*/ + lea RESIDUE,[32+RESIDUE] + /*#1 Pack final row pixels.*/ + packuswb mm7,mm2 + /*Advance src.*/ + lea SRC,[SRC+YSTRIDE*2] + /*#0 Write row.*/ + movq [DST],mm3 + /*#1 Write row.*/ + movq [DST+YSTRIDE],mm7 + /*Advance dst.*/ + lea DST,[DST+YSTRIDE*2] + mov _residue,RESIDUE + mov _dst,DST + mov _src,SRC +#undef DST +#undef SRC +#undef YSTRIDE +#undef RESIDUE + } + } +} + +void oc_frag_recon_inter2_mmx(unsigned char *_dst,const unsigned char *_src1, + const unsigned char *_src2,int _ystride,const ogg_int16_t *_residue){ + int i; + /*Zero mm7.*/ + __asm pxor mm7,mm7; + for(i=4;i-->0;){ + __asm{ +#define SRC1 ecx +#define SRC2 edi +#define YSTRIDE esi +#define RESIDUE edx +#define DST eax + mov YSTRIDE,_ystride + mov DST,_dst + mov RESIDUE,_residue + mov SRC1,_src1 + mov SRC2,_src2 + /*#0 Load src1.*/ + movq mm0,[SRC1] + /*#0 Load src2.*/ + movq mm2,[SRC2] + /*#0 Copy src1.*/ + movq mm1,mm0 + /*#0 Copy src2.*/ + movq mm3,mm2 + /*#1 Load src1.*/ + movq mm4,[SRC1+YSTRIDE] + /*#0 Unpack lower src1.*/ + punpcklbw mm0,mm7 + /*#1 Load src2.*/ + movq mm5,[SRC2+YSTRIDE] + /*#0 Unpack higher src1.*/ + punpckhbw mm1,mm7 + /*#0 Unpack lower src2.*/ + punpcklbw mm2,mm7 + /*#0 Unpack higher src2.*/ + punpckhbw mm3,mm7 + /*Advance src1 ptr.*/ + lea SRC1,[SRC1+YSTRIDE*2] + /*Advance src2 ptr.*/ + lea SRC2,[SRC2+YSTRIDE*2] + /*#0 Lower src1+src2.*/ + paddsw mm0,mm2 + /*#0 Higher src1+src2.*/ + paddsw mm1,mm3 + /*#1 Copy src1.*/ + movq mm2,mm4 + /*#0 Build lo average.*/ + psraw mm0,1 + /*#1 Copy src2.*/ + movq mm3,mm5 + /*#1 Unpack lower src1.*/ + punpcklbw mm4,mm7 + /*#0 Build hi average.*/ + psraw mm1,1 + /*#1 Unpack higher src1.*/ + punpckhbw mm2,mm7 + /*#0 low+=residue.*/ + paddsw mm0,[RESIDUE] + /*#1 Unpack lower src2.*/ + punpcklbw mm5,mm7 + /*#0 high+=residue.*/ + paddsw mm1,[8+RESIDUE] + /*#1 Unpack higher src2.*/ + punpckhbw mm3,mm7 + /*#1 Lower src1+src2.*/ + paddsw mm5,mm4 + /*#0 Pack and saturate.*/ + packuswb mm0,mm1 + /*#1 Higher src1+src2.*/ + paddsw mm3,mm2 + /*#0 Write row.*/ + movq [DST],mm0 + /*#1 Build lo average.*/ + psraw mm5,1 + /*#1 Build hi average.*/ + psraw mm3,1 + /*#1 low+=residue.*/ + paddsw mm5,[16+RESIDUE] + /*#1 high+=residue.*/ + paddsw mm3,[24+RESIDUE] + /*#1 Pack and saturate.*/ + packuswb mm5,mm3 + /*#1 Write row ptr.*/ + movq [DST+YSTRIDE],mm5 + /*Advance residue ptr.*/ + add RESIDUE,32 + /*Advance dest ptr.*/ + lea DST,[DST+YSTRIDE*2] + mov _dst,DST + mov _residue,RESIDUE + mov _src1,SRC1 + mov _src2,SRC2 +#undef SRC1 +#undef SRC2 +#undef YSTRIDE +#undef RESIDUE +#undef DST + } + } +} + +void oc_restore_fpu_mmx(void){ + __asm emms; +} + +#endif diff --git a/Engine/lib/libtheora/lib/x86_vc/mmxfrag.h b/Engine/lib/libtheora/lib/x86_vc/mmxfrag.h new file mode 100644 index 000000000..45ee93e77 --- /dev/null +++ b/Engine/lib/libtheora/lib/x86_vc/mmxfrag.h @@ -0,0 +1,61 @@ +#if !defined(_x86_vc_mmxfrag_H) +# define _x86_vc_mmxfrag_H (1) +# include +# include "x86int.h" + +#if defined(OC_X86_ASM) + +/*Copies an 8x8 block of pixels from _src to _dst, assuming _ystride bytes + between rows.*/ +#define OC_FRAG_COPY_MMX(_dst,_src,_ystride) \ + do{ \ + const unsigned char *src; \ + unsigned char *dst; \ + src=(_src); \ + dst=(_dst); \ + __asm mov SRC,src \ + __asm mov DST,dst \ + __asm mov YSTRIDE,_ystride \ + /*src+0*ystride*/ \ + __asm movq mm0,[SRC] \ + /*src+1*ystride*/ \ + __asm movq mm1,[SRC+YSTRIDE] \ + /*ystride3=ystride*3*/ \ + __asm lea YSTRIDE3,[YSTRIDE+YSTRIDE*2] \ + /*src+2*ystride*/ \ + __asm movq mm2,[SRC+YSTRIDE*2] \ + /*src+3*ystride*/ \ + __asm movq mm3,[SRC+YSTRIDE3] \ + /*dst+0*ystride*/ \ + __asm movq [DST],mm0 \ + /*dst+1*ystride*/ \ + __asm movq [DST+YSTRIDE],mm1 \ + /*Pointer to next 4.*/ \ + __asm lea SRC,[SRC+YSTRIDE*4] \ + /*dst+2*ystride*/ \ + __asm movq [DST+YSTRIDE*2],mm2 \ + /*dst+3*ystride*/ \ + __asm movq [DST+YSTRIDE3],mm3 \ + /*Pointer to next 4.*/ \ + __asm lea DST,[DST+YSTRIDE*4] \ + /*src+0*ystride*/ \ + __asm movq mm0,[SRC] \ + /*src+1*ystride*/ \ + __asm movq mm1,[SRC+YSTRIDE] \ + /*src+2*ystride*/ \ + __asm movq mm2,[SRC+YSTRIDE*2] \ + /*src+3*ystride*/ \ + __asm movq mm3,[SRC+YSTRIDE3] \ + /*dst+0*ystride*/ \ + __asm movq [DST],mm0 \ + /*dst+1*ystride*/ \ + __asm movq [DST+YSTRIDE],mm1 \ + /*dst+2*ystride*/ \ + __asm movq [DST+YSTRIDE*2],mm2 \ + /*dst+3*ystride*/ \ + __asm movq [DST+YSTRIDE3],mm3 \ + } \ + while(0) + +# endif +#endif diff --git a/Engine/lib/libtheora/lib/x86_vc/mmxidct.c b/Engine/lib/libtheora/lib/x86_vc/mmxidct.c new file mode 100644 index 000000000..8f5ff6803 --- /dev/null +++ b/Engine/lib/libtheora/lib/x86_vc/mmxidct.c @@ -0,0 +1,562 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: mmxidct.c 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ + +/*MMX acceleration of Theora's iDCT. + Originally written by Rudolf Marek, based on code from On2's VP3.*/ +#include "x86int.h" +#include "../dct.h" + +#if defined(OC_X86_ASM) + +/*These are offsets into the table of constants below.*/ +/*7 rows of cosines, in order: pi/16 * (1 ... 7).*/ +#define OC_COSINE_OFFSET (0) +/*A row of 8's.*/ +#define OC_EIGHT_OFFSET (56) + + + +/*A table of constants used by the MMX routines.*/ +static const __declspec(align(16))ogg_uint16_t + OC_IDCT_CONSTS[(7+1)*4]={ + (ogg_uint16_t)OC_C1S7,(ogg_uint16_t)OC_C1S7, + (ogg_uint16_t)OC_C1S7,(ogg_uint16_t)OC_C1S7, + (ogg_uint16_t)OC_C2S6,(ogg_uint16_t)OC_C2S6, + (ogg_uint16_t)OC_C2S6,(ogg_uint16_t)OC_C2S6, + (ogg_uint16_t)OC_C3S5,(ogg_uint16_t)OC_C3S5, + (ogg_uint16_t)OC_C3S5,(ogg_uint16_t)OC_C3S5, + (ogg_uint16_t)OC_C4S4,(ogg_uint16_t)OC_C4S4, + (ogg_uint16_t)OC_C4S4,(ogg_uint16_t)OC_C4S4, + (ogg_uint16_t)OC_C5S3,(ogg_uint16_t)OC_C5S3, + (ogg_uint16_t)OC_C5S3,(ogg_uint16_t)OC_C5S3, + (ogg_uint16_t)OC_C6S2,(ogg_uint16_t)OC_C6S2, + (ogg_uint16_t)OC_C6S2,(ogg_uint16_t)OC_C6S2, + (ogg_uint16_t)OC_C7S1,(ogg_uint16_t)OC_C7S1, + (ogg_uint16_t)OC_C7S1,(ogg_uint16_t)OC_C7S1, + 8, 8, 8, 8 +}; + +/*38 cycles*/ +#define OC_IDCT_BEGIN __asm{ \ + __asm movq mm2,OC_I(3) \ + __asm movq mm6,OC_C(3) \ + __asm movq mm4,mm2 \ + __asm movq mm7,OC_J(5) \ + __asm pmulhw mm4,mm6 \ + __asm movq mm1,OC_C(5) \ + __asm pmulhw mm6,mm7 \ + __asm movq mm5,mm1 \ + __asm pmulhw mm1,mm2 \ + __asm movq mm3,OC_I(1) \ + __asm pmulhw mm5,mm7 \ + __asm movq mm0,OC_C(1) \ + __asm paddw mm4,mm2 \ + __asm paddw mm6,mm7 \ + __asm paddw mm2,mm1 \ + __asm movq mm1,OC_J(7) \ + __asm paddw mm7,mm5 \ + __asm movq mm5,mm0 \ + __asm pmulhw mm0,mm3 \ + __asm paddw mm4,mm7 \ + __asm pmulhw mm5,mm1 \ + __asm movq mm7,OC_C(7) \ + __asm psubw mm6,mm2 \ + __asm paddw mm0,mm3 \ + __asm pmulhw mm3,mm7 \ + __asm movq mm2,OC_I(2) \ + __asm pmulhw mm7,mm1 \ + __asm paddw mm5,mm1 \ + __asm movq mm1,mm2 \ + __asm pmulhw mm2,OC_C(2) \ + __asm psubw mm3,mm5 \ + __asm movq mm5,OC_J(6) \ + __asm paddw mm0,mm7 \ + __asm movq mm7,mm5 \ + __asm psubw mm0,mm4 \ + __asm pmulhw mm5,OC_C(2) \ + __asm paddw mm2,mm1 \ + __asm pmulhw mm1,OC_C(6) \ + __asm paddw mm4,mm4 \ + __asm paddw mm4,mm0 \ + __asm psubw mm3,mm6 \ + __asm paddw mm5,mm7 \ + __asm paddw mm6,mm6 \ + __asm pmulhw mm7,OC_C(6) \ + __asm paddw mm6,mm3 \ + __asm movq OC_I(1),mm4 \ + __asm psubw mm1,mm5 \ + __asm movq mm4,OC_C(4) \ + __asm movq mm5,mm3 \ + __asm pmulhw mm3,mm4 \ + __asm paddw mm7,mm2 \ + __asm movq OC_I(2),mm6 \ + __asm movq mm2,mm0 \ + __asm movq mm6,OC_I(0) \ + __asm pmulhw mm0,mm4 \ + __asm paddw mm5,mm3 \ + __asm movq mm3,OC_J(4) \ + __asm psubw mm5,mm1 \ + __asm paddw mm2,mm0 \ + __asm psubw mm6,mm3 \ + __asm movq mm0,mm6 \ + __asm pmulhw mm6,mm4 \ + __asm paddw mm3,mm3 \ + __asm paddw mm1,mm1 \ + __asm paddw mm3,mm0 \ + __asm paddw mm1,mm5 \ + __asm pmulhw mm4,mm3 \ + __asm paddw mm6,mm0 \ + __asm psubw mm6,mm2 \ + __asm paddw mm2,mm2 \ + __asm movq mm0,OC_I(1) \ + __asm paddw mm2,mm6 \ + __asm paddw mm4,mm3 \ + __asm psubw mm2,mm1 \ +} + +/*38+8=46 cycles.*/ +#define OC_ROW_IDCT __asm{ \ + OC_IDCT_BEGIN \ + /*r3=D'*/ \ + __asm movq mm3,OC_I(2) \ + /*r4=E'=E-G*/ \ + __asm psubw mm4,mm7 \ + /*r1=H'+H'*/ \ + __asm paddw mm1,mm1 \ + /*r7=G+G*/ \ + __asm paddw mm7,mm7 \ + /*r1=R1=A''+H'*/ \ + __asm paddw mm1,mm2 \ + /*r7=G'=E+G*/ \ + __asm paddw mm7,mm4 \ + /*r4=R4=E'-D'*/ \ + __asm psubw mm4,mm3 \ + __asm paddw mm3,mm3 \ + /*r6=R6=F'-B''*/ \ + __asm psubw mm6,mm5 \ + __asm paddw mm5,mm5 \ + /*r3=R3=E'+D'*/ \ + __asm paddw mm3,mm4 \ + /*r5=R5=F'+B''*/ \ + __asm paddw mm5,mm6 \ + /*r7=R7=G'-C'*/ \ + __asm psubw mm7,mm0 \ + __asm paddw mm0,mm0 \ + /*Save R1.*/ \ + __asm movq OC_I(1),mm1 \ + /*r0=R0=G.+C.*/ \ + __asm paddw mm0,mm7 \ +} + +/*The following macro does two 4x4 transposes in place. + At entry, we assume: + r0 = a3 a2 a1 a0 + I(1) = b3 b2 b1 b0 + r2 = c3 c2 c1 c0 + r3 = d3 d2 d1 d0 + + r4 = e3 e2 e1 e0 + r5 = f3 f2 f1 f0 + r6 = g3 g2 g1 g0 + r7 = h3 h2 h1 h0 + + At exit, we have: + I(0) = d0 c0 b0 a0 + I(1) = d1 c1 b1 a1 + I(2) = d2 c2 b2 a2 + I(3) = d3 c3 b3 a3 + + J(4) = h0 g0 f0 e0 + J(5) = h1 g1 f1 e1 + J(6) = h2 g2 f2 e2 + J(7) = h3 g3 f3 e3 + + I(0) I(1) I(2) I(3) is the transpose of r0 I(1) r2 r3. + J(4) J(5) J(6) J(7) is the transpose of r4 r5 r6 r7. + + Since r1 is free at entry, we calculate the Js first.*/ +/*19 cycles.*/ +#define OC_TRANSPOSE __asm{ \ + __asm movq mm1,mm4 \ + __asm punpcklwd mm4,mm5 \ + __asm movq OC_I(0),mm0 \ + __asm punpckhwd mm1,mm5 \ + __asm movq mm0,mm6 \ + __asm punpcklwd mm6,mm7 \ + __asm movq mm5,mm4 \ + __asm punpckldq mm4,mm6 \ + __asm punpckhdq mm5,mm6 \ + __asm movq mm6,mm1 \ + __asm movq OC_J(4),mm4 \ + __asm punpckhwd mm0,mm7 \ + __asm movq OC_J(5),mm5 \ + __asm punpckhdq mm6,mm0 \ + __asm movq mm4,OC_I(0) \ + __asm punpckldq mm1,mm0 \ + __asm movq mm5,OC_I(1) \ + __asm movq mm0,mm4 \ + __asm movq OC_J(7),mm6 \ + __asm punpcklwd mm0,mm5 \ + __asm movq OC_J(6),mm1 \ + __asm punpckhwd mm4,mm5 \ + __asm movq mm5,mm2 \ + __asm punpcklwd mm2,mm3 \ + __asm movq mm1,mm0 \ + __asm punpckldq mm0,mm2 \ + __asm punpckhdq mm1,mm2 \ + __asm movq mm2,mm4 \ + __asm movq OC_I(0),mm0 \ + __asm punpckhwd mm5,mm3 \ + __asm movq OC_I(1),mm1 \ + __asm punpckhdq mm4,mm5 \ + __asm punpckldq mm2,mm5 \ + __asm movq OC_I(3),mm4 \ + __asm movq OC_I(2),mm2 \ +} + +/*38+19=57 cycles.*/ +#define OC_COLUMN_IDCT __asm{ \ + OC_IDCT_BEGIN \ + __asm paddw mm2,OC_8 \ + /*r1=H'+H'*/ \ + __asm paddw mm1,mm1 \ + /*r1=R1=A''+H'*/ \ + __asm paddw mm1,mm2 \ + /*r2=NR2*/ \ + __asm psraw mm2,4 \ + /*r4=E'=E-G*/ \ + __asm psubw mm4,mm7 \ + /*r1=NR1*/ \ + __asm psraw mm1,4 \ + /*r3=D'*/ \ + __asm movq mm3,OC_I(2) \ + /*r7=G+G*/ \ + __asm paddw mm7,mm7 \ + /*Store NR2 at I(2).*/ \ + __asm movq OC_I(2),mm2 \ + /*r7=G'=E+G*/ \ + __asm paddw mm7,mm4 \ + /*Store NR1 at I(1).*/ \ + __asm movq OC_I(1),mm1 \ + /*r4=R4=E'-D'*/ \ + __asm psubw mm4,mm3 \ + __asm paddw mm4,OC_8 \ + /*r3=D'+D'*/ \ + __asm paddw mm3,mm3 \ + /*r3=R3=E'+D'*/ \ + __asm paddw mm3,mm4 \ + /*r4=NR4*/ \ + __asm psraw mm4,4 \ + /*r6=R6=F'-B''*/ \ + __asm psubw mm6,mm5 \ + /*r3=NR3*/ \ + __asm psraw mm3,4 \ + __asm paddw mm6,OC_8 \ + /*r5=B''+B''*/ \ + __asm paddw mm5,mm5 \ + /*r5=R5=F'+B''*/ \ + __asm paddw mm5,mm6 \ + /*r6=NR6*/ \ + __asm psraw mm6,4 \ + /*Store NR4 at J(4).*/ \ + __asm movq OC_J(4),mm4 \ + /*r5=NR5*/ \ + __asm psraw mm5,4 \ + /*Store NR3 at I(3).*/ \ + __asm movq OC_I(3),mm3 \ + /*r7=R7=G'-C'*/ \ + __asm psubw mm7,mm0 \ + __asm paddw mm7,OC_8 \ + /*r0=C'+C'*/ \ + __asm paddw mm0,mm0 \ + /*r0=R0=G'+C'*/ \ + __asm paddw mm0,mm7 \ + /*r7=NR7*/ \ + __asm psraw mm7,4 \ + /*Store NR6 at J(6).*/ \ + __asm movq OC_J(6),mm6 \ + /*r0=NR0*/ \ + __asm psraw mm0,4 \ + /*Store NR5 at J(5).*/ \ + __asm movq OC_J(5),mm5 \ + /*Store NR7 at J(7).*/ \ + __asm movq OC_J(7),mm7 \ + /*Store NR0 at I(0).*/ \ + __asm movq OC_I(0),mm0 \ +} + +#define OC_MID(_m,_i) [CONSTS+_m+(_i)*8] +#define OC_C(_i) OC_MID(OC_COSINE_OFFSET,_i-1) +#define OC_8 OC_MID(OC_EIGHT_OFFSET,0) + +static void oc_idct8x8_slow(ogg_int16_t _y[64]){ + /*This routine accepts an 8x8 matrix, but in partially transposed form. + Every 4x4 block is transposed.*/ + __asm{ +#define CONSTS eax +#define Y edx + mov CONSTS,offset OC_IDCT_CONSTS + mov Y,_y +#define OC_I(_k) [Y+_k*16] +#define OC_J(_k) [Y+(_k-4)*16+8] + OC_ROW_IDCT + OC_TRANSPOSE +#undef OC_I +#undef OC_J +#define OC_I(_k) [Y+(_k*16)+64] +#define OC_J(_k) [Y+(_k-4)*16+72] + OC_ROW_IDCT + OC_TRANSPOSE +#undef OC_I +#undef OC_J +#define OC_I(_k) [Y+_k*16] +#define OC_J(_k) OC_I(_k) + OC_COLUMN_IDCT +#undef OC_I +#undef OC_J +#define OC_I(_k) [Y+_k*16+8] +#define OC_J(_k) OC_I(_k) + OC_COLUMN_IDCT +#undef OC_I +#undef OC_J +#undef CONSTS +#undef Y + } +} + +/*25 cycles.*/ +#define OC_IDCT_BEGIN_10 __asm{ \ + __asm movq mm2,OC_I(3) \ + __asm nop \ + __asm movq mm6,OC_C(3) \ + __asm movq mm4,mm2 \ + __asm movq mm1,OC_C(5) \ + __asm pmulhw mm4,mm6 \ + __asm movq mm3,OC_I(1) \ + __asm pmulhw mm1,mm2 \ + __asm movq mm0,OC_C(1) \ + __asm paddw mm4,mm2 \ + __asm pxor mm6,mm6 \ + __asm paddw mm2,mm1 \ + __asm movq mm5,OC_I(2) \ + __asm pmulhw mm0,mm3 \ + __asm movq mm1,mm5 \ + __asm paddw mm0,mm3 \ + __asm pmulhw mm3,OC_C(7) \ + __asm psubw mm6,mm2 \ + __asm pmulhw mm5,OC_C(2) \ + __asm psubw mm0,mm4 \ + __asm movq mm7,OC_I(2) \ + __asm paddw mm4,mm4 \ + __asm paddw mm7,mm5 \ + __asm paddw mm4,mm0 \ + __asm pmulhw mm1,OC_C(6) \ + __asm psubw mm3,mm6 \ + __asm movq OC_I(1),mm4 \ + __asm paddw mm6,mm6 \ + __asm movq mm4,OC_C(4) \ + __asm paddw mm6,mm3 \ + __asm movq mm5,mm3 \ + __asm pmulhw mm3,mm4 \ + __asm movq OC_I(2),mm6 \ + __asm movq mm2,mm0 \ + __asm movq mm6,OC_I(0) \ + __asm pmulhw mm0,mm4 \ + __asm paddw mm5,mm3 \ + __asm paddw mm2,mm0 \ + __asm psubw mm5,mm1 \ + __asm pmulhw mm6,mm4 \ + __asm paddw mm6,OC_I(0) \ + __asm paddw mm1,mm1 \ + __asm movq mm4,mm6 \ + __asm paddw mm1,mm5 \ + __asm psubw mm6,mm2 \ + __asm paddw mm2,mm2 \ + __asm movq mm0,OC_I(1) \ + __asm paddw mm2,mm6 \ + __asm psubw mm2,mm1 \ + __asm nop \ +} + +/*25+8=33 cycles.*/ +#define OC_ROW_IDCT_10 __asm{ \ + OC_IDCT_BEGIN_10 \ + /*r3=D'*/ \ + __asm movq mm3,OC_I(2) \ + /*r4=E'=E-G*/ \ + __asm psubw mm4,mm7 \ + /*r1=H'+H'*/ \ + __asm paddw mm1,mm1 \ + /*r7=G+G*/ \ + __asm paddw mm7,mm7 \ + /*r1=R1=A''+H'*/ \ + __asm paddw mm1,mm2 \ + /*r7=G'=E+G*/ \ + __asm paddw mm7,mm4 \ + /*r4=R4=E'-D'*/ \ + __asm psubw mm4,mm3 \ + __asm paddw mm3,mm3 \ + /*r6=R6=F'-B''*/ \ + __asm psubw mm6,mm5 \ + __asm paddw mm5,mm5 \ + /*r3=R3=E'+D'*/ \ + __asm paddw mm3,mm4 \ + /*r5=R5=F'+B''*/ \ + __asm paddw mm5,mm6 \ + /*r7=R7=G'-C'*/ \ + __asm psubw mm7,mm0 \ + __asm paddw mm0,mm0 \ + /*Save R1.*/ \ + __asm movq OC_I(1),mm1 \ + /*r0=R0=G'+C'*/ \ + __asm paddw mm0,mm7 \ +} + +/*25+19=44 cycles'*/ +#define OC_COLUMN_IDCT_10 __asm{ \ + OC_IDCT_BEGIN_10 \ + __asm paddw mm2,OC_8 \ + /*r1=H'+H'*/ \ + __asm paddw mm1,mm1 \ + /*r1=R1=A''+H'*/ \ + __asm paddw mm1,mm2 \ + /*r2=NR2*/ \ + __asm psraw mm2,4 \ + /*r4=E'=E-G*/ \ + __asm psubw mm4,mm7 \ + /*r1=NR1*/ \ + __asm psraw mm1,4 \ + /*r3=D'*/ \ + __asm movq mm3,OC_I(2) \ + /*r7=G+G*/ \ + __asm paddw mm7,mm7 \ + /*Store NR2 at I(2).*/ \ + __asm movq OC_I(2),mm2 \ + /*r7=G'=E+G*/ \ + __asm paddw mm7,mm4 \ + /*Store NR1 at I(1).*/ \ + __asm movq OC_I(1),mm1 \ + /*r4=R4=E'-D'*/ \ + __asm psubw mm4,mm3 \ + __asm paddw mm4,OC_8 \ + /*r3=D'+D'*/ \ + __asm paddw mm3,mm3 \ + /*r3=R3=E'+D'*/ \ + __asm paddw mm3,mm4 \ + /*r4=NR4*/ \ + __asm psraw mm4,4 \ + /*r6=R6=F'-B''*/ \ + __asm psubw mm6,mm5 \ + /*r3=NR3*/ \ + __asm psraw mm3,4 \ + __asm paddw mm6,OC_8 \ + /*r5=B''+B''*/ \ + __asm paddw mm5,mm5 \ + /*r5=R5=F'+B''*/ \ + __asm paddw mm5,mm6 \ + /*r6=NR6*/ \ + __asm psraw mm6,4 \ + /*Store NR4 at J(4).*/ \ + __asm movq OC_J(4),mm4 \ + /*r5=NR5*/ \ + __asm psraw mm5,4 \ + /*Store NR3 at I(3).*/ \ + __asm movq OC_I(3),mm3 \ + /*r7=R7=G'-C'*/ \ + __asm psubw mm7,mm0 \ + __asm paddw mm7,OC_8 \ + /*r0=C'+C'*/ \ + __asm paddw mm0,mm0 \ + /*r0=R0=G'+C'*/ \ + __asm paddw mm0,mm7 \ + /*r7=NR7*/ \ + __asm psraw mm7,4 \ + /*Store NR6 at J(6).*/ \ + __asm movq OC_J(6),mm6 \ + /*r0=NR0*/ \ + __asm psraw mm0,4 \ + /*Store NR5 at J(5).*/ \ + __asm movq OC_J(5),mm5 \ + /*Store NR7 at J(7).*/ \ + __asm movq OC_J(7),mm7 \ + /*Store NR0 at I(0).*/ \ + __asm movq OC_I(0),mm0 \ +} + +static void oc_idct8x8_10(ogg_int16_t _y[64]){ + __asm{ +#define CONSTS eax +#define Y edx + mov CONSTS,offset OC_IDCT_CONSTS + mov Y,_y +#define OC_I(_k) [Y+_k*16] +#define OC_J(_k) [Y+(_k-4)*16+8] + /*Done with dequant, descramble, and partial transpose. + Now do the iDCT itself.*/ + OC_ROW_IDCT_10 + OC_TRANSPOSE +#undef OC_I +#undef OC_J +#define OC_I(_k) [Y+_k*16] +#define OC_J(_k) OC_I(_k) + OC_COLUMN_IDCT_10 +#undef OC_I +#undef OC_J +#define OC_I(_k) [Y+_k*16+8] +#define OC_J(_k) OC_I(_k) + OC_COLUMN_IDCT_10 +#undef OC_I +#undef OC_J +#undef CONSTS +#undef Y + } +} + +/*Performs an inverse 8x8 Type-II DCT transform. + The input is assumed to be scaled by a factor of 4 relative to orthonormal + version of the transform.*/ +void oc_idct8x8_mmx(ogg_int16_t _y[64],int _last_zzi){ + /*_last_zzi is subtly different from an actual count of the number of + coefficients we decoded for this block. + It contains the value of zzi BEFORE the final token in the block was + decoded. + In most cases this is an EOB token (the continuation of an EOB run from a + previous block counts), and so this is the same as the coefficient count. + However, in the case that the last token was NOT an EOB token, but filled + the block up with exactly 64 coefficients, _last_zzi will be less than 64. + Provided the last token was not a pure zero run, the minimum value it can + be is 46, and so that doesn't affect any of the cases in this routine. + However, if the last token WAS a pure zero run of length 63, then _last_zzi + will be 1 while the number of coefficients decoded is 64. + Thus, we will trigger the following special case, where the real + coefficient count would not. + Note also that a zero run of length 64 will give _last_zzi a value of 0, + but we still process the DC coefficient, which might have a non-zero value + due to DC prediction. + Although convoluted, this is arguably the correct behavior: it allows us to + use a smaller transform when the block ends with a long zero run instead + of a normal EOB token. + It could be smarter... multiple separate zero runs at the end of a block + will fool it, but an encoder that generates these really deserves what it + gets. + Needless to say we inherited this approach from VP3.*/ + /*Perform the iDCT.*/ + if(_last_zzi<10)oc_idct8x8_10(_y); + else oc_idct8x8_slow(_y); +} + +#endif diff --git a/Engine/lib/libtheora/lib/x86_vc/mmxloop.h b/Engine/lib/libtheora/lib/x86_vc/mmxloop.h new file mode 100644 index 000000000..2561fca2a --- /dev/null +++ b/Engine/lib/libtheora/lib/x86_vc/mmxloop.h @@ -0,0 +1,219 @@ +#if !defined(_x86_vc_mmxloop_H) +# define _x86_vc_mmxloop_H (1) +# include +# include "x86int.h" + +#if defined(OC_X86_ASM) + +/*On entry, mm0={a0,...,a7}, mm1={b0,...,b7}, mm2={c0,...,c7}, mm3={d0,...d7}. + On exit, mm1={b0+lflim(R_0,L),...,b7+lflim(R_7,L)} and + mm2={c0-lflim(R_0,L),...,c7-lflim(R_7,L)}; mm0 and mm3 are clobbered.*/ +#define OC_LOOP_FILTER8_MMX __asm{ \ + /*mm7=0*/ \ + __asm pxor mm7,mm7 \ + /*mm6:mm0={a0,...,a7}*/ \ + __asm movq mm6,mm0 \ + __asm punpcklbw mm0,mm7 \ + __asm punpckhbw mm6,mm7 \ + /*mm3:mm5={d0,...,d7}*/ \ + __asm movq mm5,mm3 \ + __asm punpcklbw mm3,mm7 \ + __asm punpckhbw mm5,mm7 \ + /*mm6:mm0={a0-d0,...,a7-d7}*/ \ + __asm psubw mm0,mm3 \ + __asm psubw mm6,mm5 \ + /*mm3:mm1={b0,...,b7}*/ \ + __asm movq mm3,mm1 \ + __asm punpcklbw mm1,mm7 \ + __asm movq mm4,mm2 \ + __asm punpckhbw mm3,mm7 \ + /*mm5:mm4={c0,...,c7}*/ \ + __asm movq mm5,mm2 \ + __asm punpcklbw mm4,mm7 \ + __asm punpckhbw mm5,mm7 \ + /*mm7={3}x4 \ + mm5:mm4={c0-b0,...,c7-b7}*/ \ + __asm pcmpeqw mm7,mm7 \ + __asm psubw mm4,mm1 \ + __asm psrlw mm7,14 \ + __asm psubw mm5,mm3 \ + /*Scale by 3.*/ \ + __asm pmullw mm4,mm7 \ + __asm pmullw mm5,mm7 \ + /*mm7={4}x4 \ + mm5:mm4=f={a0-d0+3*(c0-b0),...,a7-d7+3*(c7-b7)}*/ \ + __asm psrlw mm7,1 \ + __asm paddw mm4,mm0 \ + __asm psllw mm7,2 \ + __asm movq mm0,[LL] \ + __asm paddw mm5,mm6 \ + /*R_i has the range [-127,128], so we compute -R_i instead. \ + mm4=-R_i=-(f+4>>3)=0xFF^(f-4>>3)*/ \ + __asm psubw mm4,mm7 \ + __asm psubw mm5,mm7 \ + __asm psraw mm4,3 \ + __asm psraw mm5,3 \ + __asm pcmpeqb mm7,mm7 \ + __asm packsswb mm4,mm5 \ + __asm pxor mm6,mm6 \ + __asm pxor mm4,mm7 \ + __asm packuswb mm1,mm3 \ + /*Now compute lflim of -mm4 cf. Section 7.10 of the sepc.*/ \ + /*There's no unsigned byte+signed byte with unsigned saturation op code, so \ + we have to split things by sign (the other option is to work in 16 bits, \ + but working in 8 bits gives much better parallelism). \ + We compute abs(R_i), but save a mask of which terms were negative in mm6. \ + Then we compute mm4=abs(lflim(R_i,L))=min(abs(R_i),max(2*L-abs(R_i),0)). \ + Finally, we split mm4 into positive and negative pieces using the mask in \ + mm6, and add and subtract them as appropriate.*/ \ + /*mm4=abs(-R_i)*/ \ + /*mm7=255-2*L*/ \ + __asm pcmpgtb mm6,mm4 \ + __asm psubb mm7,mm0 \ + __asm pxor mm4,mm6 \ + __asm psubb mm7,mm0 \ + __asm psubb mm4,mm6 \ + /*mm7=255-max(2*L-abs(R_i),0)*/ \ + __asm paddusb mm7,mm4 \ + /*mm4=min(abs(R_i),max(2*L-abs(R_i),0))*/ \ + __asm paddusb mm4,mm7 \ + __asm psubusb mm4,mm7 \ + /*Now split mm4 by the original sign of -R_i.*/ \ + __asm movq mm5,mm4 \ + __asm pand mm4,mm6 \ + __asm pandn mm6,mm5 \ + /*mm1={b0+lflim(R_0,L),...,b7+lflim(R_7,L)}*/ \ + /*mm2={c0-lflim(R_0,L),...,c7-lflim(R_7,L)}*/ \ + __asm paddusb mm1,mm4 \ + __asm psubusb mm2,mm4 \ + __asm psubusb mm1,mm6 \ + __asm paddusb mm2,mm6 \ +} + +#define OC_LOOP_FILTER_V_MMX(_pix,_ystride,_ll) \ + do{ \ + /*Used local variable pix__ in order to fix compilation errors like: \ + "error C2425: 'SHL' : non-constant expression in 'second operand'".*/ \ + unsigned char *pix__; \ + unsigned char *ll__; \ + ll__=(_ll); \ + pix__=(_pix); \ + __asm mov YSTRIDE,_ystride \ + __asm mov LL,ll__ \ + __asm mov PIX,pix__ \ + __asm sub PIX,YSTRIDE \ + __asm sub PIX,YSTRIDE \ + /*mm0={a0,...,a7}*/ \ + __asm movq mm0,[PIX] \ + /*ystride3=_ystride*3*/ \ + __asm lea YSTRIDE3,[YSTRIDE+YSTRIDE*2] \ + /*mm3={d0,...,d7}*/ \ + __asm movq mm3,[PIX+YSTRIDE3] \ + /*mm1={b0,...,b7}*/ \ + __asm movq mm1,[PIX+YSTRIDE] \ + /*mm2={c0,...,c7}*/ \ + __asm movq mm2,[PIX+YSTRIDE*2] \ + OC_LOOP_FILTER8_MMX \ + /*Write it back out.*/ \ + __asm movq [PIX+YSTRIDE],mm1 \ + __asm movq [PIX+YSTRIDE*2],mm2 \ + } \ + while(0) + +#define OC_LOOP_FILTER_H_MMX(_pix,_ystride,_ll) \ + do{ \ + /*Used local variable ll__ in order to fix compilation errors like: \ + "error C2443: operand size conflict".*/ \ + unsigned char *ll__; \ + unsigned char *pix__; \ + ll__=(_ll); \ + pix__=(_pix)-2; \ + __asm mov PIX,pix__ \ + __asm mov YSTRIDE,_ystride \ + __asm mov LL,ll__ \ + /*x x x x d0 c0 b0 a0*/ \ + __asm movd mm0,[PIX] \ + /*x x x x d1 c1 b1 a1*/ \ + __asm movd mm1,[PIX+YSTRIDE] \ + /*ystride3=_ystride*3*/ \ + __asm lea YSTRIDE3,[YSTRIDE+YSTRIDE*2] \ + /*x x x x d2 c2 b2 a2*/ \ + __asm movd mm2,[PIX+YSTRIDE*2] \ + /*x x x x d3 c3 b3 a3*/ \ + __asm lea D,[PIX+YSTRIDE*4] \ + __asm movd mm3,[PIX+YSTRIDE3] \ + /*x x x x d4 c4 b4 a4*/ \ + __asm movd mm4,[D] \ + /*x x x x d5 c5 b5 a5*/ \ + __asm movd mm5,[D+YSTRIDE] \ + /*x x x x d6 c6 b6 a6*/ \ + __asm movd mm6,[D+YSTRIDE*2] \ + /*x x x x d7 c7 b7 a7*/ \ + __asm movd mm7,[D+YSTRIDE3] \ + /*mm0=d1 d0 c1 c0 b1 b0 a1 a0*/ \ + __asm punpcklbw mm0,mm1 \ + /*mm2=d3 d2 c3 c2 b3 b2 a3 a2*/ \ + __asm punpcklbw mm2,mm3 \ + /*mm3=d1 d0 c1 c0 b1 b0 a1 a0*/ \ + __asm movq mm3,mm0 \ + /*mm0=b3 b2 b1 b0 a3 a2 a1 a0*/ \ + __asm punpcklwd mm0,mm2 \ + /*mm3=d3 d2 d1 d0 c3 c2 c1 c0*/ \ + __asm punpckhwd mm3,mm2 \ + /*mm1=b3 b2 b1 b0 a3 a2 a1 a0*/ \ + __asm movq mm1,mm0 \ + /*mm4=d5 d4 c5 c4 b5 b4 a5 a4*/ \ + __asm punpcklbw mm4,mm5 \ + /*mm6=d7 d6 c7 c6 b7 b6 a7 a6*/ \ + __asm punpcklbw mm6,mm7 \ + /*mm5=d5 d4 c5 c4 b5 b4 a5 a4*/ \ + __asm movq mm5,mm4 \ + /*mm4=b7 b6 b5 b4 a7 a6 a5 a4*/ \ + __asm punpcklwd mm4,mm6 \ + /*mm5=d7 d6 d5 d4 c7 c6 c5 c4*/ \ + __asm punpckhwd mm5,mm6 \ + /*mm2=d3 d2 d1 d0 c3 c2 c1 c0*/ \ + __asm movq mm2,mm3 \ + /*mm0=a7 a6 a5 a4 a3 a2 a1 a0*/ \ + __asm punpckldq mm0,mm4 \ + /*mm1=b7 b6 b5 b4 b3 b2 b1 b0*/ \ + __asm punpckhdq mm1,mm4 \ + /*mm2=c7 c6 c5 c4 c3 c2 c1 c0*/ \ + __asm punpckldq mm2,mm5 \ + /*mm3=d7 d6 d5 d4 d3 d2 d1 d0*/ \ + __asm punpckhdq mm3,mm5 \ + OC_LOOP_FILTER8_MMX \ + /*mm2={b0+R_0'',...,b7+R_7''}*/ \ + __asm movq mm0,mm1 \ + /*mm1={b0+R_0'',c0-R_0'',...,b3+R_3'',c3-R_3''}*/ \ + __asm punpcklbw mm1,mm2 \ + /*mm2={b4+R_4'',c4-R_4'',...,b7+R_7'',c7-R_7''}*/ \ + __asm punpckhbw mm0,mm2 \ + /*[d]=c1 b1 c0 b0*/ \ + __asm movd D,mm1 \ + __asm mov [PIX+1],D_WORD \ + __asm psrlq mm1,32 \ + __asm shr D,16 \ + __asm mov [PIX+YSTRIDE+1],D_WORD \ + /*[d]=c3 b3 c2 b2*/ \ + __asm movd D,mm1 \ + __asm mov [PIX+YSTRIDE*2+1],D_WORD \ + __asm shr D,16 \ + __asm mov [PIX+YSTRIDE3+1],D_WORD \ + __asm lea PIX,[PIX+YSTRIDE*4] \ + /*[d]=c5 b5 c4 b4*/ \ + __asm movd D,mm0 \ + __asm mov [PIX+1],D_WORD \ + __asm psrlq mm0,32 \ + __asm shr D,16 \ + __asm mov [PIX+YSTRIDE+1],D_WORD \ + /*[d]=c7 b7 c6 b6*/ \ + __asm movd D,mm0 \ + __asm mov [PIX+YSTRIDE*2+1],D_WORD \ + __asm shr D,16 \ + __asm mov [PIX+YSTRIDE3+1],D_WORD \ + } \ + while(0) + +# endif +#endif diff --git a/Engine/lib/libtheora/lib/x86_vc/mmxstate.c b/Engine/lib/libtheora/lib/x86_vc/mmxstate.c new file mode 100644 index 000000000..73bd1981c --- /dev/null +++ b/Engine/lib/libtheora/lib/x86_vc/mmxstate.c @@ -0,0 +1,211 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: mmxstate.c 16584 2009-09-26 19:35:55Z tterribe $ + + ********************************************************************/ + +/*MMX acceleration of complete fragment reconstruction algorithm. + Originally written by Rudolf Marek.*/ +#include +#include "x86int.h" +#include "mmxfrag.h" +#include "mmxloop.h" + +#if defined(OC_X86_ASM) + +void oc_state_frag_recon_mmx(const oc_theora_state *_state,ptrdiff_t _fragi, + int _pli,ogg_int16_t _dct_coeffs[64],int _last_zzi,ogg_uint16_t _dc_quant){ + unsigned char *dst; + ptrdiff_t frag_buf_off; + int ystride; + int mb_mode; + /*Apply the inverse transform.*/ + /*Special case only having a DC component.*/ + if(_last_zzi<2){ + /*Note that this value must be unsigned, to keep the __asm__ block from + sign-extending it when it puts it in a register.*/ + ogg_uint16_t p; + /*We round this dequant product (and not any of the others) because there's + no iDCT rounding.*/ + p=(ogg_int16_t)(_dct_coeffs[0]*(ogg_int32_t)_dc_quant+15>>5); + /*Fill _dct_coeffs with p.*/ + __asm{ +#define Y eax +#define P ecx + mov Y,_dct_coeffs + movzx P,p + /*mm0=0000 0000 0000 AAAA*/ + movd mm0,P + /*mm0=0000 0000 AAAA AAAA*/ + punpcklwd mm0,mm0 + /*mm0=AAAA AAAA AAAA AAAA*/ + punpckldq mm0,mm0 + movq [Y],mm0 + movq [8+Y],mm0 + movq [16+Y],mm0 + movq [24+Y],mm0 + movq [32+Y],mm0 + movq [40+Y],mm0 + movq [48+Y],mm0 + movq [56+Y],mm0 + movq [64+Y],mm0 + movq [72+Y],mm0 + movq [80+Y],mm0 + movq [88+Y],mm0 + movq [96+Y],mm0 + movq [104+Y],mm0 + movq [112+Y],mm0 + movq [120+Y],mm0 +#undef Y +#undef P + } + } + else{ + /*Dequantize the DC coefficient.*/ + _dct_coeffs[0]=(ogg_int16_t)(_dct_coeffs[0]*(int)_dc_quant); + oc_idct8x8_mmx(_dct_coeffs,_last_zzi); + } + /*Fill in the target buffer.*/ + frag_buf_off=_state->frag_buf_offs[_fragi]; + mb_mode=_state->frags[_fragi].mb_mode; + ystride=_state->ref_ystride[_pli]; + dst=_state->ref_frame_data[_state->ref_frame_idx[OC_FRAME_SELF]]+frag_buf_off; + if(mb_mode==OC_MODE_INTRA)oc_frag_recon_intra_mmx(dst,ystride,_dct_coeffs); + else{ + const unsigned char *ref; + int mvoffsets[2]; + ref= + _state->ref_frame_data[_state->ref_frame_idx[OC_FRAME_FOR_MODE(mb_mode)]] + +frag_buf_off; + if(oc_state_get_mv_offsets(_state,mvoffsets,_pli, + _state->frag_mvs[_fragi][0],_state->frag_mvs[_fragi][1])>1){ + oc_frag_recon_inter2_mmx(dst,ref+mvoffsets[0],ref+mvoffsets[1],ystride, + _dct_coeffs); + } + else oc_frag_recon_inter_mmx(dst,ref+mvoffsets[0],ystride,_dct_coeffs); + } +} + +/*We copy these entire function to inline the actual MMX routines so that we + use only a single indirect call.*/ + +/*Copies the fragments specified by the lists of fragment indices from one + frame to another. + _fragis: A pointer to a list of fragment indices. + _nfragis: The number of fragment indices to copy. + _dst_frame: The reference frame to copy to. + _src_frame: The reference frame to copy from. + _pli: The color plane the fragments lie in.*/ +void oc_state_frag_copy_list_mmx(const oc_theora_state *_state, + const ptrdiff_t *_fragis,ptrdiff_t _nfragis, + int _dst_frame,int _src_frame,int _pli){ + const ptrdiff_t *frag_buf_offs; + const unsigned char *src_frame_data; + unsigned char *dst_frame_data; + ptrdiff_t fragii; + int ystride; + dst_frame_data=_state->ref_frame_data[_state->ref_frame_idx[_dst_frame]]; + src_frame_data=_state->ref_frame_data[_state->ref_frame_idx[_src_frame]]; + ystride=_state->ref_ystride[_pli]; + frag_buf_offs=_state->frag_buf_offs; + for(fragii=0;fragii<_nfragis;fragii++){ + ptrdiff_t frag_buf_off; + frag_buf_off=frag_buf_offs[_fragis[fragii]]; +#define SRC edx +#define DST eax +#define YSTRIDE ecx +#define YSTRIDE3 edi + OC_FRAG_COPY_MMX(dst_frame_data+frag_buf_off, + src_frame_data+frag_buf_off,ystride); +#undef SRC +#undef DST +#undef YSTRIDE +#undef YSTRIDE3 + } +} + +/*Apply the loop filter to a given set of fragment rows in the given plane. + The filter may be run on the bottom edge, affecting pixels in the next row of + fragments, so this row also needs to be available. + _bv: The bounding values array. + _refi: The index of the frame buffer to filter. + _pli: The color plane to filter. + _fragy0: The Y coordinate of the first fragment row to filter. + _fragy_end: The Y coordinate of the fragment row to stop filtering at.*/ +void oc_state_loop_filter_frag_rows_mmx(const oc_theora_state *_state, + int _bv[256],int _refi,int _pli,int _fragy0,int _fragy_end){ + OC_ALIGN8(unsigned char ll[8]); + const oc_fragment_plane *fplane; + const oc_fragment *frags; + const ptrdiff_t *frag_buf_offs; + unsigned char *ref_frame_data; + ptrdiff_t fragi_top; + ptrdiff_t fragi_bot; + ptrdiff_t fragi0; + ptrdiff_t fragi0_end; + int ystride; + int nhfrags; + memset(ll,_state->loop_filter_limits[_state->qis[0]],sizeof(ll)); + fplane=_state->fplanes+_pli; + nhfrags=fplane->nhfrags; + fragi_top=fplane->froffset; + fragi_bot=fragi_top+fplane->nfrags; + fragi0=fragi_top+_fragy0*(ptrdiff_t)nhfrags; + fragi0_end=fragi0+(_fragy_end-_fragy0)*(ptrdiff_t)nhfrags; + ystride=_state->ref_ystride[_pli]; + frags=_state->frags; + frag_buf_offs=_state->frag_buf_offs; + ref_frame_data=_state->ref_frame_data[_refi]; + /*The following loops are constructed somewhat non-intuitively on purpose. + The main idea is: if a block boundary has at least one coded fragment on + it, the filter is applied to it. + However, the order that the filters are applied in matters, and VP3 chose + the somewhat strange ordering used below.*/ + while(fragi0fragi0)OC_LOOP_FILTER_H_MMX(ref,ystride,ll); + if(fragi0>fragi_top)OC_LOOP_FILTER_V_MMX(ref,ystride,ll); + if(fragi+1opt_vtable.frag_sub=oc_enc_frag_sub_mmx; + _enc->opt_vtable.frag_sub_128=oc_enc_frag_sub_128_mmx; + _enc->opt_vtable.frag_recon_intra=oc_frag_recon_intra_mmx; + _enc->opt_vtable.frag_recon_inter=oc_frag_recon_inter_mmx; + _enc->opt_vtable.fdct8x8=oc_enc_fdct8x8_mmx; + } + if(cpu_flags&OC_CPU_X86_MMXEXT){ + _enc->opt_vtable.frag_sad=oc_enc_frag_sad_mmxext; + _enc->opt_vtable.frag_sad_thresh=oc_enc_frag_sad_thresh_mmxext; + _enc->opt_vtable.frag_sad2_thresh=oc_enc_frag_sad2_thresh_mmxext; + _enc->opt_vtable.frag_satd_thresh=oc_enc_frag_satd_thresh_mmxext; + _enc->opt_vtable.frag_satd2_thresh=oc_enc_frag_satd2_thresh_mmxext; + _enc->opt_vtable.frag_intra_satd=oc_enc_frag_intra_satd_mmxext; + _enc->opt_vtable.frag_copy2=oc_enc_frag_copy2_mmxext; + } + if(cpu_flags&OC_CPU_X86_SSE2){ +# if defined(OC_X86_64_ASM) + _enc->opt_vtable.fdct8x8=oc_enc_fdct8x8_x86_64sse2; +# endif + } +} +#endif diff --git a/Engine/lib/libtheora/lib/x86_vc/x86enc.h b/Engine/lib/libtheora/lib/x86_vc/x86enc.h new file mode 100644 index 000000000..581484641 --- /dev/null +++ b/Engine/lib/libtheora/lib/x86_vc/x86enc.h @@ -0,0 +1,47 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: x86int.h 15675 2009-02-06 09:43:27Z tterribe $ + + ********************************************************************/ + +#if !defined(_x86_vc_x86enc_H) +# define _x86_vc_x86enc_H (1) +# include "../encint.h" +# include "x86int.h" + +void oc_enc_vtable_init_x86(oc_enc_ctx *_enc); + +unsigned oc_enc_frag_sad_mmxext(const unsigned char *_src, + const unsigned char *_ref,int _ystride); +unsigned oc_enc_frag_sad_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _thresh); +unsigned oc_enc_frag_sad2_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride, + unsigned _thresh); +unsigned oc_enc_frag_satd_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref,int _ystride,unsigned _thresh); +unsigned oc_enc_frag_satd2_thresh_mmxext(const unsigned char *_src, + const unsigned char *_ref1,const unsigned char *_ref2,int _ystride, + unsigned _thresh); +unsigned oc_enc_frag_intra_satd_mmxext(const unsigned char *_src,int _ystride); +void oc_enc_frag_sub_mmx(ogg_int16_t _diff[64], + const unsigned char *_x,const unsigned char *_y,int _stride); +void oc_enc_frag_sub_128_mmx(ogg_int16_t _diff[64], + const unsigned char *_x,int _stride); +void oc_enc_frag_copy2_mmxext(unsigned char *_dst, + const unsigned char *_src1,const unsigned char *_src2,int _ystride); +void oc_enc_fdct8x8_mmx(ogg_int16_t _y[64],const ogg_int16_t _x[64]); +void oc_enc_fdct8x8_x86_64sse2(ogg_int16_t _y[64],const ogg_int16_t _x[64]); + +#endif diff --git a/Engine/lib/libtheora/lib/x86_vc/x86int.h b/Engine/lib/libtheora/lib/x86_vc/x86int.h new file mode 100644 index 000000000..4cca48531 --- /dev/null +++ b/Engine/lib/libtheora/lib/x86_vc/x86int.h @@ -0,0 +1,42 @@ +/******************************************************************** + * * + * THIS FILE IS PART OF THE OggTheora 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 Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * + * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * + * * + ******************************************************************** + + function: + last mod: $Id: x86int.h 16503 2009-08-22 18:14:02Z giles $ + + ********************************************************************/ + +#if !defined(_x86_vc_x86int_H) +# define _x86_vc_x86int_H (1) +# include "../internal.h" + +void oc_state_vtable_init_x86(oc_theora_state *_state); + +void oc_frag_copy_mmx(unsigned char *_dst, + const unsigned char *_src,int _ystride); +void oc_frag_recon_intra_mmx(unsigned char *_dst,int _ystride, + const ogg_int16_t *_residue); +void oc_frag_recon_inter_mmx(unsigned char *_dst, + const unsigned char *_src,int _ystride,const ogg_int16_t *_residue); +void oc_frag_recon_inter2_mmx(unsigned char *_dst,const unsigned char *_src1, + const unsigned char *_src2,int _ystride,const ogg_int16_t *_residue); +void oc_idct8x8_mmx(ogg_int16_t _y[64],int _last_zzi); +void oc_state_frag_recon_mmx(const oc_theora_state *_state,ptrdiff_t _fragi, + int _pli,ogg_int16_t _dct_coeffs[64],int _last_zzi,ogg_uint16_t _dc_quant); +void oc_state_frag_copy_list_mmx(const oc_theora_state *_state, + const ptrdiff_t *_fragis,ptrdiff_t _nfragis, + int _dst_frame,int _src_frame,int _pli); +void oc_state_loop_filter_frag_rows_mmx(const oc_theora_state *_state, + int _bv[256],int _refi,int _pli,int _fragy0,int _fragy_end); +void oc_restore_fpu_mmx(void); + +#endif diff --git a/Engine/lib/libtheora/lib/dec/x86_vc/x86state.c b/Engine/lib/libtheora/lib/x86_vc/x86state.c similarity index 53% rename from Engine/lib/libtheora/lib/dec/x86_vc/x86state.c rename to Engine/lib/libtheora/lib/x86_vc/x86state.c index 735390823..a786bec28 100644 --- a/Engine/lib/libtheora/lib/dec/x86_vc/x86state.c +++ b/Engine/lib/libtheora/lib/x86_vc/x86state.c @@ -5,37 +5,58 @@ * GOVERNED BY A BSD-STYLE SOURCE LICENSE INCLUDED WITH THIS SOURCE * * IN 'COPYING'. PLEASE READ THESE TERMS BEFORE DISTRIBUTING. * * * - * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2008 * + * THE Theora SOURCE CODE IS COPYRIGHT (C) 2002-2009 * * by the Xiph.Org Foundation and contributors http://www.xiph.org/ * * * ******************************************************************** function: - last mod: $Id: x86state.c 15427 2008-10-21 02:36:19Z xiphmont $ + last mod: $Id: x86state.c 16503 2009-08-22 18:14:02Z giles $ ********************************************************************/ -#if defined(USE_ASM) - #include "x86int.h" -#include "../../cpu.c" + +#if defined(OC_X86_ASM) + +#include "../cpu.c" + +/*This table has been modified from OC_FZIG_ZAG by baking a 4x4 transpose into + each quadrant of the destination.*/ +static const unsigned char OC_FZIG_ZAG_MMX[128]={ + 0, 8, 1, 2, 9,16,24,17, + 10, 3,32,11,18,25, 4,12, + 5,26,19,40,33,34,41,48, + 27, 6,13,20,28,21,14, 7, + 56,49,42,35,43,50,57,36, + 15,22,29,30,23,44,37,58, + 51,59,38,45,52,31,60,53, + 46,39,47,54,61,62,55,63, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, + 64,64,64,64,64,64,64,64, +}; void oc_state_vtable_init_x86(oc_theora_state *_state){ _state->cpu_flags=oc_cpu_flags_get(); - - /* fill with defaults */ - oc_state_vtable_init_c(_state); - - /* patch MMX functions */ if(_state->cpu_flags&OC_CPU_X86_MMX){ + _state->opt_vtable.frag_copy=oc_frag_copy_mmx; _state->opt_vtable.frag_recon_intra=oc_frag_recon_intra_mmx; _state->opt_vtable.frag_recon_inter=oc_frag_recon_inter_mmx; _state->opt_vtable.frag_recon_inter2=oc_frag_recon_inter2_mmx; - _state->opt_vtable.restore_fpu=oc_restore_fpu_mmx; - _state->opt_vtable.state_frag_copy=oc_state_frag_copy_mmx; + _state->opt_vtable.idct8x8=oc_idct8x8_mmx; _state->opt_vtable.state_frag_recon=oc_state_frag_recon_mmx; - _state->opt_vtable.state_loop_filter_frag_rows=oc_state_loop_filter_frag_rows_mmx; + _state->opt_vtable.state_frag_copy_list=oc_state_frag_copy_list_mmx; + _state->opt_vtable.state_loop_filter_frag_rows= + oc_state_loop_filter_frag_rows_mmx; + _state->opt_vtable.restore_fpu=oc_restore_fpu_mmx; + _state->opt_data.dct_fzig_zag=OC_FZIG_ZAG_MMX; } + else oc_state_vtable_init_c(_state); } - #endif From 1bbc3d9e8a309dfdb996d9a5a37673f93cf5c1a7 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 6 Jul 2014 12:21:11 +0200 Subject: [PATCH 181/317] Fix lib Opcode to compile on WIN64. --- Engine/lib/opcode/Ice/IceFPU.h | 4 ++-- Engine/lib/opcode/Ice/IceMemoryMacros.h | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/Engine/lib/opcode/Ice/IceFPU.h b/Engine/lib/opcode/Ice/IceFPU.h index a8dd69cc7..9cf2f2452 100644 --- a/Engine/lib/opcode/Ice/IceFPU.h +++ b/Engine/lib/opcode/Ice/IceFPU.h @@ -45,7 +45,7 @@ //! Fast square root for floating-point values. inline_ float FastSqrt(float square) { -#if defined( PLATFORM_WINDOWS ) +#if defined( PLATFORM_WINDOWS ) && !defined ( _WIN64 ) float retval; __asm { @@ -188,7 +188,7 @@ return x*x < epsilon; } -#ifdef PLATFORM_WINDOWS +#if defined( PLATFORM_WINDOWS ) && !defined ( _WIN64 ) #define FCOMI_ST0 _asm _emit 0xdb _asm _emit 0xf0 #define FCOMIP_ST0 _asm _emit 0xdf _asm _emit 0xf0 #define FCMOVB_ST0 _asm _emit 0xda _asm _emit 0xc0 diff --git a/Engine/lib/opcode/Ice/IceMemoryMacros.h b/Engine/lib/opcode/Ice/IceMemoryMacros.h index f0f27d5ed..f4d511189 100644 --- a/Engine/lib/opcode/Ice/IceMemoryMacros.h +++ b/Engine/lib/opcode/Ice/IceMemoryMacros.h @@ -47,7 +47,7 @@ //! \warning writes nb*4 bytes ! inline_ void StoreDwords(udword* dest, udword nb, udword value) { -#ifdef _WIN32 +#if defined( _WIN32 ) && !defined( _WIN64 ) // The asm code below **SHOULD** be equivalent to one of those C versions // or the other if your compiled is good: (checked on VC++ 6.0) // From a93f179138ff4b9977a872a2e58a48d38ffbddae Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 6 Jul 2014 12:24:48 +0200 Subject: [PATCH 182/317] Fix lib ConvexDecomp to compile on WIN64. --- Engine/lib/convexDecomp/NvThreadConfig.cpp | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/Engine/lib/convexDecomp/NvThreadConfig.cpp b/Engine/lib/convexDecomp/NvThreadConfig.cpp index af6f2bdda..5756bafa8 100644 --- a/Engine/lib/convexDecomp/NvThreadConfig.cpp +++ b/Engine/lib/convexDecomp/NvThreadConfig.cpp @@ -133,6 +133,8 @@ void tc_spinloop() asm ( "pause" ); #elif defined( _XBOX ) // Pause would do nothing on the Xbox. Threads are not scheduled. + #elif defined( _WIN64 ) + YieldProcessor( ); #else __asm { pause }; #endif @@ -144,7 +146,7 @@ void tc_interlockedExchange(void *dest, const int64_t exchange) // not working assert(false); //__sync_lock_test_and_set((int64_t*)dest, exchange); - #elif defined( _XBOX ) +#elif defined( _XBOX ) || defined( _WIN64 ) InterlockedExchange((volatile LONG *)dest, exchange); #else __asm @@ -172,7 +174,7 @@ NxI32 tc_interlockedCompareExchange(void *dest, NxI32 exchange, NxI32 compare) return 0; //return __sync_val_compare_and_swap((uintptr_t*)dest, exchange, compare); //return __sync_bool_compare_and_swap((uintptr_t*)dest, exchange, compare); - #elif defined( _XBOX ) + #elif defined( _XBOX ) || defined( _WIN64 ) return InterlockedCompareExchange((volatile LONG *)dest, exchange, compare); #else char _ret; @@ -202,7 +204,7 @@ NxI32 tc_interlockedCompareExchange(void *dest, const NxI32 exchange1, const NxI //uint64_t exchange = ((uint64_t)exchange1 << 32) | (uint64_t)exchange2; //uint64_t compare = ((uint64_t)compare1 << 32) | (uint64_t)compare2; //return __sync_bool_compare_and_swap((int64_t*)dest, exchange, compare); - #elif defined( _XBOX ) + #elif defined( _XBOX ) || defined( _WIN64 ) assert(false); return 0; #else From 8ed0f508ca5f3876502bb65ae24b2d898ae534cf Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 14 Sep 2014 21:39:56 +0200 Subject: [PATCH 183/317] Minimal changes to compile T3D on WIN64. --- Engine/source/platform/platformCPUCount.cpp | 6 +- Engine/source/platform/platformFont.h | 4 +- Engine/source/platform/profiler.cpp | 4 +- Engine/source/platform/types.visualc.h | 2 +- .../nativeDialogs/fileDialog.cpp | 2 +- Engine/source/platformWin32/winFont.cpp | 4 +- Engine/source/platformWin32/winMath_ASM.cpp | 17 +++ Engine/source/platformWin32/winMemory.cpp | 8 +- .../platformWin32/winPlatformCPUCount.cpp | 119 ++++++++++++++++++ Engine/source/platformWin32/winUser.cpp | 3 + .../windowManager/win32/win32Window.cpp | 4 +- .../windowManager/win32/win32WindowMgr.cpp | 2 +- .../windowManager/win32/winDispatch.cpp | 4 +- 13 files changed, 161 insertions(+), 18 deletions(-) create mode 100644 Engine/source/platformWin32/winPlatformCPUCount.cpp diff --git a/Engine/source/platform/platformCPUCount.cpp b/Engine/source/platform/platformCPUCount.cpp index 7db6500e3..026cdbabf 100644 --- a/Engine/source/platform/platformCPUCount.cpp +++ b/Engine/source/platform/platformCPUCount.cpp @@ -24,6 +24,8 @@ #include "platform/platform.h" #include "platform/platformCPUCount.h" +#if defined(TORQUE_OS_LINUX) || defined(TORQUE_OS_OSX) || defined(TORQUE_OS_XENON) || defined(TORQUE_OS_PS3) + // Consoles don't need this #if defined(TORQUE_OS_XENON) || defined(TORQUE_OS_PS3) namespace CPUInfo @@ -661,4 +663,6 @@ next: } } // namespace CPUInfo -#endif \ No newline at end of file +#endif + +#endif diff --git a/Engine/source/platform/platformFont.h b/Engine/source/platform/platformFont.h index a0deffc82..63a47d95a 100644 --- a/Engine/source/platform/platformFont.h +++ b/Engine/source/platform/platformFont.h @@ -88,10 +88,10 @@ public: /// This is just for createPlatformFont to call. /// /// @todo Rethink this so we don't have a private public. - virtual bool create(const char *name, U32 size, U32 charset = TGE_ANSI_CHARSET) = 0; + virtual bool create( const char *name, dsize_t size, U32 charset = TGE_ANSI_CHARSET ) = 0; static void enumeratePlatformFonts( Vector& fonts, UTF16* fontFamily = NULL ); }; -extern PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset = TGE_ANSI_CHARSET); +extern PlatformFont *createPlatformFont(const char *name, dsize_t size, U32 charset = TGE_ANSI_CHARSET); #endif // _PLATFORMFONT_H_ diff --git a/Engine/source/platform/profiler.cpp b/Engine/source/platform/profiler.cpp index 8aaf483ef..8284ffb34 100644 --- a/Engine/source/platform/profiler.cpp +++ b/Engine/source/platform/profiler.cpp @@ -476,8 +476,8 @@ void Profiler::hashPop(ProfilerRootData *expected) else { Con::warnf("Warning: the Torque profiler thread may now run on any cpu."); - DWORD procMask; - DWORD sysMask; + DWORD_PTR procMask; + DWORD_PTR sysMask; GetProcessAffinityMask( GetCurrentProcess(), &procMask, &sysMask); SetThreadAffinityMask( GetCurrentThread(), procMask); } diff --git a/Engine/source/platform/types.visualc.h b/Engine/source/platform/types.visualc.h index 2104565bb..e383c1ea7 100644 --- a/Engine/source/platform/types.visualc.h +++ b/Engine/source/platform/types.visualc.h @@ -59,7 +59,7 @@ typedef unsigned _int64 U64; # define TORQUE_OS_STRING "Xbox" # define TORQUE_OS_XBOX # include "platform/types.win.h" -#elif defined( _WIN32 ) +#elif defined( _WIN32 ) && !defined ( _WIN64 ) # define TORQUE_OS_STRING "Win32" # define TORQUE_OS_WIN # define TORQUE_OS_WIN32 diff --git a/Engine/source/platformWin32/nativeDialogs/fileDialog.cpp b/Engine/source/platformWin32/nativeDialogs/fileDialog.cpp index 5dce1a87f..5db038332 100644 --- a/Engine/source/platformWin32/nativeDialogs/fileDialog.cpp +++ b/Engine/source/platformWin32/nativeDialogs/fileDialog.cpp @@ -113,7 +113,7 @@ static UINT_PTR CALLBACK FolderHookProc(HWND hdlg, UINT uMsg, WPARAM wParam, LPA SendMessage(hParent, CDM_HIDECONTROL, cmb1, 0); SendMessage(hParent, CDM_HIDECONTROL, stc2, 0); - LONG oldProc = SetWindowLong(hParent, GWL_WNDPROC, (LONG)OKBtnFolderHackProc); + LONG oldProc = SetWindowLong(hParent, GWLP_WNDPROC, (LONG)OKBtnFolderHackProc); SetProp(hParent, dT("OldWndProc"), (HANDLE)oldProc); SetProp(hParent, dT("OFN"), (HANDLE)lpofn); } diff --git a/Engine/source/platformWin32/winFont.cpp b/Engine/source/platformWin32/winFont.cpp index 1a625d2bc..ff179be5f 100644 --- a/Engine/source/platformWin32/winFont.cpp +++ b/Engine/source/platformWin32/winFont.cpp @@ -116,7 +116,7 @@ void PlatformFont::enumeratePlatformFonts( Vector& fonts, UTF1 EnumFontFamilies( fontHDC, fontFamily, (FONTENUMPROC)EnumFamCallBack, (LPARAM)&fonts ); } -PlatformFont *createPlatformFont(const char *name, U32 size, U32 charset /* = TGE_ANSI_CHARSET */) +PlatformFont *createPlatformFont(const char *name, dsize_t size, U32 charset /* = TGE_ANSI_CHARSET */) { PlatformFont *retFont = new WinFont; @@ -139,7 +139,7 @@ WinFont::~WinFont() } } -bool WinFont::create(const char *name, U32 size, U32 charset /* = TGE_ANSI_CHARSET */) +bool WinFont::create(const char *name, dsize_t size, U32 charset /* = TGE_ANSI_CHARSET */) { if(name == NULL || size < 1) return false; diff --git a/Engine/source/platformWin32/winMath_ASM.cpp b/Engine/source/platformWin32/winMath_ASM.cpp index 0bc373494..53150afa0 100644 --- a/Engine/source/platformWin32/winMath_ASM.cpp +++ b/Engine/source/platformWin32/winMath_ASM.cpp @@ -92,6 +92,23 @@ void Platform::setMathControlStateKnown() } } +#else + +U32 Platform::getMathControlState( ) +{ + // @todo x64 See http://msdn.microsoft.com/en-us/library/c9676k6h.aspx + return 0; +} + +void Platform::setMathControlState( U32 state ) +{ + // @todo x64 See http://msdn.microsoft.com/en-us/library/c9676k6h.aspx +} + +void Platform::setMathControlStateKnown( ) +{ + // @todo x64 See http://msdn.microsoft.com/en-us/library/c9676k6h.aspx +} #endif diff --git a/Engine/source/platformWin32/winMemory.cpp b/Engine/source/platformWin32/winMemory.cpp index 8cb593f48..106871c2a 100644 --- a/Engine/source/platformWin32/winMemory.cpp +++ b/Engine/source/platformWin32/winMemory.cpp @@ -23,26 +23,26 @@ #include "platformWin32/platformWin32.h" #include -void* dMemcpy(void *dst, const void *src, unsigned size) +void* dMemcpy(void *dst, const void *src, dsize_t size) { return memcpy(dst,src,size); } //-------------------------------------- -void* dMemmove(void *dst, const void *src, unsigned size) +void* dMemmove(void *dst, const void *src, dsize_t size) { return memmove(dst,src,size); } //-------------------------------------- -void* dMemset(void *dst, S32 c, unsigned size) +void* dMemset(void *dst, S32 c, dsize_t size) { return memset(dst,c,size); } //-------------------------------------- -S32 dMemcmp(const void *ptr1, const void *ptr2, unsigned len) +S32 dMemcmp(const void *ptr1, const void *ptr2, dsize_t len) { return memcmp(ptr1, ptr2, len); } diff --git a/Engine/source/platformWin32/winPlatformCPUCount.cpp b/Engine/source/platformWin32/winPlatformCPUCount.cpp new file mode 100644 index 000000000..e4a5d54d6 --- /dev/null +++ b/Engine/source/platformWin32/winPlatformCPUCount.cpp @@ -0,0 +1,119 @@ +// Original code is: +// Copyright (c) 2005 Intel Corporation +// All Rights Reserved +// +// CPUCount.cpp : Detects three forms of hardware multi-threading support across IA-32 platform +// The three forms of HW multithreading are: Multi-processor, Multi-core, and +// HyperThreading Technology. +// This application enumerates all the logical processors enabled by OS and BIOS, +// determine the HW topology of these enabled logical processors in the system +// using information provided by CPUID instruction. +// A multi-processing system can support any combination of the three forms of HW +// multi-threading support. The relevant topology can be identified using a +// three level decomposition of the "initial APIC ID" into +// Package_id, core_id, and SMT_id. Such decomposition provides a three-level map of +// the topology of hardware resources and +// allow multi-threaded software to manage shared hardware resources in +// the platform to reduce resource contention + +// Multicore detection algorithm for processor and cache topology requires +// all leaf functions of CPUID instructions be available. System administrator +// must ensure BIOS settings is not configured to restrict CPUID functionalities. +//------------------------------------------------------------------------------------------------- + +#include "platform/platform.h" + +#if defined( TORQUE_OS_WIN ) + +#include "platform/platformCPUCount.h" +#include +#include +#include +#include + +namespace CPUInfo { + + // based on http://msdn.microsoft.com/en-us/library/ms683194.aspx + + // Helper function to count set bits in the processor mask. + DWORD CountSetBits( ULONG_PTR bitMask ) + { + DWORD LSHIFT = sizeof( ULONG_PTR ) * 8 - 1; + DWORD bitSetCount = 0; + ULONG_PTR bitTest = (ULONG_PTR)1 << LSHIFT; + DWORD i; + + for( i = 0; i <= LSHIFT; ++i ) + { + bitSetCount += ((bitMask & bitTest) ? 1 : 0); + bitTest /= 2; + } + + return bitSetCount; + } + + EConfig CPUCount( U32& TotAvailLogical, U32& TotAvailCore, U32& PhysicalNum ) + { + EConfig StatusFlag = CONFIG_UserConfigIssue; + TotAvailLogical = 0; + TotAvailCore = 0; + PhysicalNum = 0; + + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION buffer = NULL; + DWORD returnLength = 0; + + // get buffer length + DWORD rc = GetLogicalProcessorInformation( buffer, &returnLength ); + buffer = (PSYSTEM_LOGICAL_PROCESSOR_INFORMATION)malloc( returnLength ); + + rc = GetLogicalProcessorInformation( buffer, &returnLength ); + + if( FALSE == rc ) + { + free( buffer ); + return StatusFlag; + } + + PSYSTEM_LOGICAL_PROCESSOR_INFORMATION ptr = buffer; + + DWORD byteOffset = 0; + while( byteOffset + sizeof( SYSTEM_LOGICAL_PROCESSOR_INFORMATION ) <= returnLength ) + { + switch( ptr->Relationship ) + { + + case RelationProcessorCore: + TotAvailCore++; + + // A hyperthreaded core supplies more than one logical processor. + TotAvailLogical += CountSetBits( ptr->ProcessorMask ); + break; + + case RelationProcessorPackage: + // Logical processors share a physical package. + PhysicalNum++; + break; + + default: + break; + } + byteOffset += sizeof( SYSTEM_LOGICAL_PROCESSOR_INFORMATION ); + ptr++; + } + + free( buffer ); + + StatusFlag = CONFIG_SingleCoreAndHTNotCapable; + + if( TotAvailCore == 1 && TotAvailLogical > TotAvailCore ) + StatusFlag = CONFIG_SingleCoreHTEnabled; + else if( TotAvailCore > 1 && TotAvailLogical == TotAvailCore ) + StatusFlag = CONFIG_MultiCoreAndHTNotCapable; + else if( TotAvailCore > 1 && TotAvailLogical > TotAvailCore ) + StatusFlag = CONFIG_MultiCoreAndHTEnabled; + + return StatusFlag; + } + +} // namespace CPUInfo +#endif diff --git a/Engine/source/platformWin32/winUser.cpp b/Engine/source/platformWin32/winUser.cpp index 666b0858b..c82a273a3 100644 --- a/Engine/source/platformWin32/winUser.cpp +++ b/Engine/source/platformWin32/winUser.cpp @@ -25,7 +25,10 @@ #include "core/stringTable.h" #include "core/strings/unicode.h" +#ifndef TORQUE_OS_WIN64 typedef long SHANDLE_PTR; +#endif + #include #include #include diff --git a/Engine/source/windowManager/win32/win32Window.cpp b/Engine/source/windowManager/win32/win32Window.cpp index 3b95a223a..c8e02f706 100644 --- a/Engine/source/windowManager/win32/win32Window.cpp +++ b/Engine/source/windowManager/win32/win32Window.cpp @@ -661,7 +661,7 @@ void Win32Window::_unregisterWindowClass() LRESULT PASCAL Win32Window::WindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { // CodeReview [tom, 4/30/2007] The two casts here seem somewhat silly and redundant ? - Win32Window* window = (Win32Window*)((PlatformWindow*)GetWindowLong(hWnd, GWL_USERDATA)); + Win32Window* window = (Win32Window*)((PlatformWindow*)GetWindowLong(hWnd, GWLP_USERDATA)); const WindowId devId = window ? window->getWindowId() : 0; if (window && window->getOffscreenRender()) @@ -712,7 +712,7 @@ LRESULT PASCAL Win32Window::WindowProc( HWND hWnd, UINT message, WPARAM wParam, case WM_CREATE: // CodeReview [tom, 4/30/2007] Why don't we just cast this to a LONG // instead of having a ton of essentially pointless casts ? - SetWindowLong(hWnd, GWL_USERDATA, + SetWindowLong(hWnd, GWLP_USERDATA, (LONG)((PlatformWindow*)((CREATESTRUCT*)lParam)->lpCreateParams)); break; diff --git a/Engine/source/windowManager/win32/win32WindowMgr.cpp b/Engine/source/windowManager/win32/win32WindowMgr.cpp index d9e0cc22d..448cb20e8 100644 --- a/Engine/source/windowManager/win32/win32WindowMgr.cpp +++ b/Engine/source/windowManager/win32/win32WindowMgr.cpp @@ -358,7 +358,7 @@ void Win32WindowManager::_process() // [tom, 4/30/2007] I think this should work, but leaving the above commented // out just in case this is actually fubared with multiple windows. - Win32Window* window = (Win32Window*)(GetWindowLong(msg.hwnd, GWL_USERDATA)); + Win32Window* window = (Win32Window*)(GetWindowLong(msg.hwnd, GWLP_USERDATA)); if(window) translated = window->translateMessage(msg); diff --git a/Engine/source/windowManager/win32/winDispatch.cpp b/Engine/source/windowManager/win32/winDispatch.cpp index 7038d0a29..3ac7f12a2 100644 --- a/Engine/source/windowManager/win32/winDispatch.cpp +++ b/Engine/source/windowManager/win32/winDispatch.cpp @@ -176,7 +176,7 @@ static bool _dispatch(HWND hWnd,UINT message,WPARAM wParam,WPARAM lParam) static S32 mouseNCState = -1; // -1 denotes unchanged, // 0 denotes changed but was hidden // 1 denotes changed but was visible - Win32Window* window = hWnd?(Win32Window*)GetWindowLong(hWnd, GWL_USERDATA): 0; + Win32Window* window = hWnd?(Win32Window*)GetWindowLong(hWnd, GWLP_USERDATA): 0; const WindowId devId = window ? window->getWindowId() : 0; // State tracking for focus/lose focus cursor management @@ -560,7 +560,7 @@ private: static WinMessageQueue _MessageQueue; -void RemoveMessages(HWND hWnd,UINT msgBegin,WPARAM msgEnd ) +void RemoveMessages(HWND hWnd,UINT msgBegin,UINT msgEnd ) { _MessageQueue.remove( hWnd, msgBegin, msgEnd ); } From 6bfb7d8186431185b0b9bd482adaddb5fd21449c Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 14 Sep 2014 21:42:08 +0200 Subject: [PATCH 184/317] CMake changes for WIN64 --- Tools/CMake/basics.cmake | 13 ++++++++++++- Tools/CMake/torque3d.cmake | 6 +++++- 2 files changed, 17 insertions(+), 2 deletions(-) diff --git a/Tools/CMake/basics.cmake b/Tools/CMake/basics.cmake index 41ae6b8ad..7761ca9fc 100644 --- a/Tools/CMake/basics.cmake +++ b/Tools/CMake/basics.cmake @@ -22,6 +22,12 @@ project("Torque3DEngine") +if( CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 8 ) + set( TORQUE_CPU_X64 ON ) +elseif( CMAKE_CXX_SIZEOF_DATA_PTR EQUAL 4 ) + set( TORQUE_CPU_X32 ON ) +endif() + if(NOT TORQUE_TEMPLATE) set(TORQUE_TEMPLATE "Full" CACHE STRING "the template to use") endif() @@ -340,7 +346,12 @@ if(WIN32) set(TORQUE_CXX_FLAGS_LIBS "/W0" CACHE TYPE STRING) mark_as_advanced(TORQUE_CXX_FLAGS_LIBS) - set(TORQUE_CXX_FLAGS_COMMON "-DUNICODE -D_UNICODE /MP /O2 /Ob2 /Oi /Ot /Oy /GT /Zi /W4 /nologo /GF /EHsc /GS- /Gy- /Qpar- /arch:SSE2 /fp:fast /fp:except- /GR /Zc:wchar_t- /D_CRT_SECURE_NO_WARNINGS" CACHE TYPE STRING) + set(TORQUE_CXX_FLAGS_COMMON_DEFAULT "-DUNICODE -D_UNICODE /MP /O2 /Ob2 /Oi /Ot /Oy /GT /Zi /W4 /nologo /GF /EHsc /GS- /Gy- /Qpar- /fp:fast /fp:except- /GR /Zc:wchar_t- /D_CRT_SECURE_NO_WARNINGS" ) + if( TORQUE_CPU_X32 ) + set(TORQUE_CXX_FLAGS_COMMON_DEFAULT ${TORQUE_CXX_FLAGS_COMMON_DEFAULT}" /arch:SSE2") + endif() + set(TORQUE_CXX_FLAGS_COMMON ${TORQUE_CXX_FLAGS_COMMON_DEFAULT} CACHE TYPE STRING) + mark_as_advanced(TORQUE_CXX_FLAGS_COMMON) set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${TORQUE_CXX_FLAGS_COMMON}") diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 4c6869610..9b92171e4 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -144,7 +144,11 @@ if(WIN32) # warning C4244: 'initializing' : conversion from 'XXX' to 'XXX', possible loss of data set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -wd4244") - link_directories($ENV{DXSDK_DIR}/Lib/x86) + if( TORQUE_CPU_X64 ) + link_directories($ENV{DXSDK_DIR}/Lib/x64) + else() + link_directories($ENV{DXSDK_DIR}/Lib/x86) + endif() endif() ############################################################################### From fcf7bee64af8dd7b07696bd0bcb99db8dc56b451 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 14 Sep 2014 20:44:07 +0200 Subject: [PATCH 185/317] Fix x64 problems for WIN64. --- Engine/source/console/compiler.cpp | 31 +++++++++++++++++++ Engine/source/console/compiler.h | 5 +-- Engine/source/console/console.h | 2 +- Engine/source/console/consoleInternal.cpp | 4 +-- Engine/source/console/simDictionary.cpp | 2 +- Engine/source/core/stringTable.cpp | 4 +-- Engine/source/platform/platformIntrinsics.h | 2 +- Engine/source/platform/typetraits.h | 6 ++-- Engine/source/ts/tsShape.cpp | 4 ++- .../windowManager/win32/win32Window.cpp | 6 ++-- .../windowManager/win32/win32WindowMgr.cpp | 6 ++-- .../windowManager/win32/winDispatch.cpp | 2 +- 12 files changed, 52 insertions(+), 22 deletions(-) diff --git a/Engine/source/console/compiler.cpp b/Engine/source/console/compiler.cpp index ba8a0c314..21a79704e 100644 --- a/Engine/source/console/compiler.cpp +++ b/Engine/source/console/compiler.cpp @@ -70,11 +70,42 @@ namespace Compiler //------------------------------------------------------------ +#ifdef TORQUE_CPU_X64 + // Fixed unsafe conversion from pointer to U32. @todo x64 revise + U32 u32toSTEId = 0; + typedef Map< U32, StringTableEntry > U32toSteMap; + U32toSteMap u32toSTEMap; + + StringTableEntry U32toSTE( U32 u ) + { + // @todo x64 Added thread-safe convertion. + const U32toSteMap::Iterator result = u32toSTEMap.find( u ); + AssertFatal( result != u32toSTEMap.end( ), + "Don't converted U32 to STE. See evalSTEtoU32()." ); + return result->value; + } + + U32 evalSTEtoU32( StringTableEntry ste, U32 ) + { + // @todo x64 Added thread-safe convertion. + u32toSTEMap.insert( u32toSTEId++, ste ); + return (u32toSTEId - 1); // pointer to inserted + } + +#else + + StringTableEntry U32toSTE(U32 u) + { + return *((StringTableEntry *) &u); + } + U32 evalSTEtoU32(StringTableEntry ste, U32) { return *((U32 *) &ste); } +#endif + U32 compileSTEtoU32(StringTableEntry ste, U32 ip) { if(ste) diff --git a/Engine/source/console/compiler.h b/Engine/source/console/compiler.h index 66e3966b9..8e497a630 100644 --- a/Engine/source/console/compiler.h +++ b/Engine/source/console/compiler.h @@ -218,10 +218,7 @@ namespace Compiler //------------------------------------------------------------ - inline StringTableEntry U32toSTE(U32 u) - { - return *((StringTableEntry *) &u); - } + StringTableEntry U32toSTE(U32 u); extern U32 (*STEtoU32)(StringTableEntry ste, U32 ip); diff --git a/Engine/source/console/console.h b/Engine/source/console/console.h index 3c5718c48..544ebc801 100644 --- a/Engine/source/console/console.h +++ b/Engine/source/console/console.h @@ -696,7 +696,7 @@ namespace Con extern void expandEscape(char *dest, const char *src); extern bool collapseEscape(char *buf); -extern S32 HashPointer(StringTableEntry ptr); +extern U32 HashPointer(StringTableEntry ptr); /// Extended information about a console function. diff --git a/Engine/source/console/consoleInternal.cpp b/Engine/source/console/consoleInternal.cpp index 250326460..2e91da6d7 100644 --- a/Engine/source/console/consoleInternal.cpp +++ b/Engine/source/console/consoleInternal.cpp @@ -262,9 +262,9 @@ void Dictionary::deleteVariables(const char *varString) } } -S32 HashPointer(StringTableEntry ptr) +U32 HashPointer(StringTableEntry ptr) { - return (S32)(((dsize_t)ptr) >> 2); + return (U32)(((dsize_t)ptr) >> 2); } Dictionary::Entry *Dictionary::lookup(StringTableEntry name) diff --git a/Engine/source/console/simDictionary.cpp b/Engine/source/console/simDictionary.cpp index 5065d0a08..dac603517 100644 --- a/Engine/source/console/simDictionary.cpp +++ b/Engine/source/console/simDictionary.cpp @@ -25,7 +25,7 @@ //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- -extern S32 HashPointer(StringTableEntry e); +extern U32 HashPointer(StringTableEntry e); SimNameDictionary::SimNameDictionary() { diff --git a/Engine/source/core/stringTable.cpp b/Engine/source/core/stringTable.cpp index e94ca50e7..2340fd5af 100644 --- a/Engine/source/core/stringTable.cpp +++ b/Engine/source/core/stringTable.cpp @@ -59,7 +59,7 @@ U32 _StringTable::hashString(const char* str) char c; while((c = *str++) != 0) { ret <<= 1; - ret ^= sgHashTable[static_cast(c)]; + ret ^= sgHashTable[static_cast(c)]; } return ret; } @@ -73,7 +73,7 @@ U32 _StringTable::hashStringn(const char* str, S32 len) char c; while((c = *str++) != 0 && len--) { ret <<= 1; - ret ^= sgHashTable[static_cast(c)]; + ret ^= sgHashTable[static_cast(c)]; } return ret; } diff --git a/Engine/source/platform/platformIntrinsics.h b/Engine/source/platform/platformIntrinsics.h index fd2e11916..b63a987be 100644 --- a/Engine/source/platform/platformIntrinsics.h +++ b/Engine/source/platform/platformIntrinsics.h @@ -40,7 +40,7 @@ template< typename T > inline bool dCompareAndSwap( T* volatile& refPtr, T* oldPtr, T* newPtr ) { - return dCompareAndSwap( *reinterpret_cast< volatile U32* >( &refPtr ), ( U32 ) oldPtr, ( U32 ) newPtr ); + return dCompareAndSwap( *reinterpret_cast< volatile size_t* >( &refPtr ), ( size_t ) oldPtr, ( size_t ) newPtr ); } // Test-And-Set diff --git a/Engine/source/platform/typetraits.h b/Engine/source/platform/typetraits.h index a686ab782..4183a3f8f 100644 --- a/Engine/source/platform/typetraits.h +++ b/Engine/source/platform/typetraits.h @@ -222,11 +222,11 @@ struct _TypeTraits< T* > typedef _DestructPtr Destruct; template< typename A > - static bool isTaggedPtr( A* ptr ) { return ( U32( ptr ) & 0x1 ); } //TODO: 64bits + static bool isTaggedPtr( A* ptr ) { return ( size_t( ptr ) & 0x1 ); } //TODO: 64bits template< typename A > - static A* getTaggedPtr( A* ptr ) { return ( A* ) ( U32( ptr ) | 0x1 ); } //TODO: 64bits + static A* getTaggedPtr( A* ptr ) { return ( A* ) ( size_t( ptr ) | 0x1 ); } //TODO: 64bits template< typename A > - static A* getUntaggedPtr( A* ptr ) { return ( A* ) ( U32( ptr ) & 0xFFFFFFFE ); } //TODO: 64bit + static A* getUntaggedPtr( A* ptr ) { return ( A* ) ( size_t( ptr ) & (~0x1) ); } //TODO: 64bit }; template< typename T > diff --git a/Engine/source/ts/tsShape.cpp b/Engine/source/ts/tsShape.cpp index d01a871c5..7b160b670 100644 --- a/Engine/source/ts/tsShape.cpp +++ b/Engine/source/ts/tsShape.cpp @@ -1170,7 +1170,10 @@ void TSShape::assembleShape() skip = true; TSMesh * mesh = TSMesh::assembleMesh(meshType,skip); if (ptr32) + { ptr32[i] = skip ? 0 : (S32)mesh; + meshes.push_back(skip ? 0 : mesh); + } // fill in location of verts, tverts, and normals for detail levels if (mesh && meshType!=TSMesh::DecalMeshType) @@ -1198,7 +1201,6 @@ void TSShape::assembleShape() } } } - meshes.set(ptr32,numMeshes); tsalloc.checkGuard(); diff --git a/Engine/source/windowManager/win32/win32Window.cpp b/Engine/source/windowManager/win32/win32Window.cpp index c8e02f706..0bb7677df 100644 --- a/Engine/source/windowManager/win32/win32Window.cpp +++ b/Engine/source/windowManager/win32/win32Window.cpp @@ -661,7 +661,7 @@ void Win32Window::_unregisterWindowClass() LRESULT PASCAL Win32Window::WindowProc( HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam ) { // CodeReview [tom, 4/30/2007] The two casts here seem somewhat silly and redundant ? - Win32Window* window = (Win32Window*)((PlatformWindow*)GetWindowLong(hWnd, GWLP_USERDATA)); + Win32Window* window = (Win32Window*)((PlatformWindow*)GetWindowLongPtr(hWnd, GWLP_USERDATA)); const WindowId devId = window ? window->getWindowId() : 0; if (window && window->getOffscreenRender()) @@ -712,8 +712,8 @@ LRESULT PASCAL Win32Window::WindowProc( HWND hWnd, UINT message, WPARAM wParam, case WM_CREATE: // CodeReview [tom, 4/30/2007] Why don't we just cast this to a LONG // instead of having a ton of essentially pointless casts ? - SetWindowLong(hWnd, GWLP_USERDATA, - (LONG)((PlatformWindow*)((CREATESTRUCT*)lParam)->lpCreateParams)); + SetWindowLongPtr(hWnd, GWLP_USERDATA, + (LONG_PTR)((PlatformWindow*)((CREATESTRUCT*)lParam)->lpCreateParams)); break; case WM_SETFOCUS: diff --git a/Engine/source/windowManager/win32/win32WindowMgr.cpp b/Engine/source/windowManager/win32/win32WindowMgr.cpp index 448cb20e8..ef04c7fe1 100644 --- a/Engine/source/windowManager/win32/win32WindowMgr.cpp +++ b/Engine/source/windowManager/win32/win32WindowMgr.cpp @@ -136,7 +136,7 @@ void Win32WindowManager::buildMonitorsList() mMonitors.clear(); // Enumerate all monitors - EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (U32)(void*)&mMonitors); + EnumDisplayMonitors(NULL, NULL, MonitorEnumProc, (size_t)(void*)&mMonitors); } S32 Win32WindowManager::findFirstMatchingMonitor(const char* name) @@ -252,7 +252,7 @@ PlatformWindow *Win32WindowManager::createWindow(GFXDevice *device, const GFXVid w32w->setVideoMode(mode); // Associate our window struct with the HWND. - SetWindowLongPtrW(w32w->mWindowHandle, GWLP_USERDATA, (LONG)w32w); + SetWindowLongPtr(w32w->mWindowHandle, GWLP_USERDATA, (LONG_PTR)w32w); // Do some error checking. AssertFatal(w32w->mWindowHandle != NULL, "Win32WindowManager::createWindow - Could not create window!"); @@ -358,7 +358,7 @@ void Win32WindowManager::_process() // [tom, 4/30/2007] I think this should work, but leaving the above commented // out just in case this is actually fubared with multiple windows. - Win32Window* window = (Win32Window*)(GetWindowLong(msg.hwnd, GWLP_USERDATA)); + Win32Window* window = (Win32Window*)(GetWindowLongPtr(msg.hwnd, GWLP_USERDATA)); if(window) translated = window->translateMessage(msg); diff --git a/Engine/source/windowManager/win32/winDispatch.cpp b/Engine/source/windowManager/win32/winDispatch.cpp index 3ac7f12a2..0d26def4d 100644 --- a/Engine/source/windowManager/win32/winDispatch.cpp +++ b/Engine/source/windowManager/win32/winDispatch.cpp @@ -176,7 +176,7 @@ static bool _dispatch(HWND hWnd,UINT message,WPARAM wParam,WPARAM lParam) static S32 mouseNCState = -1; // -1 denotes unchanged, // 0 denotes changed but was hidden // 1 denotes changed but was visible - Win32Window* window = hWnd?(Win32Window*)GetWindowLong(hWnd, GWLP_USERDATA): 0; + Win32Window* window = hWnd?(Win32Window*)GetWindowLongPtr(hWnd, GWLP_USERDATA): 0; const WindowId devId = window ? window->getWindowId() : 0; // State tracking for focus/lose focus cursor management From fd3d44c755570fe0a0214da4bf8bf91f8d9d1409 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Mon, 15 Sep 2014 07:30:51 +1000 Subject: [PATCH 186/317] Removed annoying warning. --- Engine/source/T3D/player.cpp | 4 ---- 1 file changed, 4 deletions(-) diff --git a/Engine/source/T3D/player.cpp b/Engine/source/T3D/player.cpp index 593d48c7b..5a30fb77b 100644 --- a/Engine/source/T3D/player.cpp +++ b/Engine/source/T3D/player.cpp @@ -487,10 +487,6 @@ bool PlayerData::preload(bool server, String &errorStr) dp->death = false; if (dp->sequence != -1) getGroundInfo(si,thread,dp); - - // No real reason to spam the console about a missing jet animation - if (dStricmp(sp->name, "jet") != 0) - AssertWarn(dp->sequence != -1, avar("PlayerData::preload - Unable to find named animation sequence '%s'!", sp->name)); } for (S32 b = 0; b < mShape->sequences.size(); b++) { From 439dc9c56da941ae051e3e2a9e8441fb90389303 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Mon, 15 Sep 2014 10:01:13 +1000 Subject: [PATCH 187/317] Fixed ShapeBase animation networking. We essentially just need to remember to Update ShapeBase::Thread::position every time we advance the thread in the shape. With a little bit of supporting code these changes will be passed to the client's shape. --- Engine/source/T3D/shapeBase.cpp | 21 ++++++++------------- Engine/source/T3D/shapeBase.h | 4 +--- 2 files changed, 9 insertions(+), 16 deletions(-) diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index 56ec0e4d5..f963b171e 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -2154,13 +2154,13 @@ bool ShapeBase::setThreadSequence(U32 slot, S32 seq, bool reset) if (reset) { st.state = Thread::Play; st.atEnd = false; - st.timescale = 1.f; - st.position = 0.f; + st.timescale = 1.f; + st.position = 0.f; } if (mShapeInstance) { if (!st.thread) st.thread = mShapeInstance->addThread(); - mShapeInstance->setSequence(st.thread,seq,0); + mShapeInstance->setSequence(st.thread,seq,st.position); updateThread(st); } return true; @@ -2175,17 +2175,11 @@ void ShapeBase::updateThread(Thread& st) case Thread::Stop: { mShapeInstance->setTimeScale( st.thread, 1.f ); - mShapeInstance->setPos( st.thread, ( st.timescale > 0.f ) ? 0.0f : 1.0f ); + mShapeInstance->setPos( st.thread, ( st.timescale > 0.f ) ? 1.0f : 0.0f ); } // Drop through to pause state case Thread::Pause: { - if ( st.position != -1.f ) - { - mShapeInstance->setTimeScale( st.thread, 1.f ); - mShapeInstance->setPos( st.thread, st.position ); - } - mShapeInstance->setTimeScale( st.thread, 0.f ); } break; @@ -2339,6 +2333,7 @@ void ShapeBase::advanceThreads(F32 dt) if(st.thread) { mShapeInstance->advanceTime(dt,st.thread); + st.position = mShapeInstance->getPos(st.thread); } } } @@ -3039,9 +3034,9 @@ void ShapeBase::unpackUpdate(NetConnection *con, BitStream *stream) if (stream->readFlag()) { Thread& st = mScriptThread[i]; U32 seq = stream->readInt(ThreadSequenceBits); - st.state = stream->readInt(2); - stream->read( &st.timescale ); - stream->read( &st.position ); + st.state = Thread::State(stream->readInt(2)); + stream->read( &st.timescale ); + stream->read( &st.position ); st.atEnd = stream->readFlag(); if (st.sequence != seq && st.state != Thread::Destroy) setThreadSequence(i,seq,false); diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index 22e25810d..de22114a9 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -729,9 +729,7 @@ protected: Play, Stop, Pause, Destroy }; TSThread* thread; ///< Pointer to 3space data. - U32 state; ///< State of the thread - /// - /// @see Thread::State + State state; ///< State of the thread S32 sequence; ///< The animation sequence which is running in this thread. F32 timescale; ///< Timescale bool atEnd; ///< Are we at the end of this thread? From db089ab45f1524eccdbf6d7afe77cca1b7218ed6 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Mon, 15 Sep 2014 19:10:23 -0500 Subject: [PATCH 188/317] consistency --- Engine/source/T3D/shapeBase.cpp | 2 +- Engine/source/T3D/shapeBase.h | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/source/T3D/shapeBase.cpp b/Engine/source/T3D/shapeBase.cpp index 4a4156965..494b93a75 100644 --- a/Engine/source/T3D/shapeBase.cpp +++ b/Engine/source/T3D/shapeBase.cpp @@ -114,7 +114,7 @@ IMPLEMENT_CALLBACK( ShapeBaseData, onTrigger, void, ( ShapeBase* obj, S32 index, "@param index Index of the trigger that changed\n" "@param state New state of the trigger\n" ); -IMPLEMENT_CALLBACK(ShapeBaseData, onEndSequence, void, (ShapeBase* obj, S32 slot, const String &name), (obj, slot, name), +IMPLEMENT_CALLBACK(ShapeBaseData, onEndSequence, void, (ShapeBase* obj, S32 slot, const char* name), (obj, slot, name), "@brief Called when a thread playing a non-cyclic sequence reaches the end of the " "sequence.\n\n" "@param obj The ShapeBase object\n" diff --git a/Engine/source/T3D/shapeBase.h b/Engine/source/T3D/shapeBase.h index 51a450437..9ba46de3d 100644 --- a/Engine/source/T3D/shapeBase.h +++ b/Engine/source/T3D/shapeBase.h @@ -648,7 +648,7 @@ public: DECLARE_CALLBACK( void, onCollision, ( ShapeBase* obj, SceneObject* collObj, VectorF vec, F32 len ) ); DECLARE_CALLBACK( void, onDamage, ( ShapeBase* obj, F32 delta ) ); DECLARE_CALLBACK( void, onTrigger, ( ShapeBase* obj, S32 index, bool state ) ); - DECLARE_CALLBACK( void, onEndSequence, (ShapeBase* obj, S32 slot, const String &name)); + DECLARE_CALLBACK(void, onEndSequence, (ShapeBase* obj, S32 slot, const char* name)); DECLARE_CALLBACK( void, onForceUncloak, ( ShapeBase* obj, const char* reason ) ); /// @} }; From cb9cfea1c4aa5ae44664960ef58e7d0880cd591d Mon Sep 17 00:00:00 2001 From: Lukas Joergensen Date: Thu, 31 Jul 2014 00:25:52 +0200 Subject: [PATCH 189/317] Ribbon class implementation. This class is based on Tim Newell from MaxGaming Technologies code, it's a Ribbon class which is easy to use from other classes. --- Engine/source/T3D/fx/ribbon.cpp | 701 ++++++++++++++++++++++++++++++++ Engine/source/T3D/fx/ribbon.h | 127 ++++++ 2 files changed, 828 insertions(+) create mode 100644 Engine/source/T3D/fx/ribbon.cpp create mode 100644 Engine/source/T3D/fx/ribbon.h diff --git a/Engine/source/T3D/fx/ribbon.cpp b/Engine/source/T3D/fx/ribbon.cpp new file mode 100644 index 000000000..d035c3491 --- /dev/null +++ b/Engine/source/T3D/fx/ribbon.cpp @@ -0,0 +1,701 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "console/consoleTypes.h" +#include "console/typeValidators.h" +#include "core/stream/bitStream.h" +#include "T3D/shapeBase.h" +#include "ts/tsShapeInstance.h" +#include "T3D/fx/ribbon.h" +#include "math/mathUtils.h" +#include "math/mathIO.h" +#include "sim/netConnection.h" +#include "gfx/primBuilder.h" +#include "gfx/gfxDrawUtil.h" +#include "materials/sceneData.h" +#include "materials/matInstance.h" +#include "gui/3d/guiTSControl.h" +#include "materials/materialManager.h" +#include "materials/processedShaderMaterial.h" +#include "gfx/gfxTransformSaver.h" + + +IMPLEMENT_CO_DATABLOCK_V1(RibbonData); +IMPLEMENT_CO_NETOBJECT_V1(Ribbon); + + +//-------------------------------------------------------------------------- +// +RibbonData::RibbonData() +{ + for (U8 i = 0; i < RIBBON_NUM_FIELDS; i++) { + mSizes[i] = 0.0f; + mColours[i].set(0.0f, 0.0f, 0.0f, 1.0f); + mTimes[i] = -1.0f; + } + + mRibbonLength = 0; + mUseFadeOut = false; + mFadeAwayStep = 0.032f; + segmentsPerUpdate = 1; + mMatName = StringTable->insert(""); + mTileScale = 1.0f; + mFixedTexcoords = false; + mSegmentSkipAmount = 0; + mTexcoordsRelativeToDistance = false; +} + +//-------------------------------------------------------------------------- + +void RibbonData::initPersistFields() +{ + Parent::initPersistFields(); + + addField("size", TypeF32, Offset(mSizes, RibbonData), RIBBON_NUM_FIELDS, + "The size of the ribbon at the specified keyframe."); + addField("color", TypeColorF, Offset(mColours, RibbonData), RIBBON_NUM_FIELDS, + "The colour of the ribbon at the specified keyframe."); + addField("position", TypeF32, Offset(mTimes, RibbonData), RIBBON_NUM_FIELDS, + "The position of the keyframe along the lifetime of the ribbon."); + addField("RibbonLength", TypeS32, Offset(mRibbonLength, RibbonData), + "The amount of segments the Ribbon can maximally have in length."); + addField("UseFadeOut", TypeBool, Offset(mUseFadeOut, RibbonData), + "If true, the ribbon will fade away after deletion."); + addField("RibbonMaterial", TypeString, Offset(mMatName, RibbonData), + "The material the ribbon uses for rendering."); + addField("fadeAwayStep", TypeF32, Offset(mFadeAwayStep, RibbonData), + "How much to fade the ribbon with each update, after deletion."); + addField("segmentsPerUpdate", TypeS32, Offset(segmentsPerUpdate, RibbonData), + "How many segments to add each update."); + addField("tileScale", TypeF32, Offset(mTileScale, RibbonData), + "How much to scale each 'tile' with, where 1 means the material is stretched" + "across the whole ribbon. (If TexcoordsRelativeToDistance is true, this is in meters.)"); + addField("fixedTexcoords", TypeBool, Offset(mFixedTexcoords, RibbonData), + "If true, this prevents 'floating' texture coordinates."); + addField("skipAmount", TypeS32, Offset(mSegmentSkipAmount, RibbonData), + "The amount of segments to skip each update."); + addField("TexcoordsRelativeToDistance", TypeBool, Offset(mTexcoordsRelativeToDistance, RibbonData), + "If true, texture coordinates are scaled relative to distance, this prevents" + "'stretched' textures."); +} + + +//-------------------------------------------------------------------------- +bool RibbonData::onAdd() +{ + if(!Parent::onAdd()) + return false; + + return true; +} + + +bool RibbonData::preload(bool server, String &errorBuffer) +{ + if (Parent::preload(server, errorBuffer) == false) + return false; + + return true; +} + +//-------------------------------------------------------------------------- +void RibbonData::packData(BitStream* stream) +{ + Parent::packData(stream); + + for (U8 i = 0; i < RIBBON_NUM_FIELDS; i++) { + stream->write(mSizes[i]); + stream->write(mColours[i]); + stream->write(mTimes[i]); + } + + stream->write(mRibbonLength); + stream->writeString(mMatName); + stream->writeFlag(mUseFadeOut); + stream->write(mFadeAwayStep); + stream->write(segmentsPerUpdate); + stream->write(mTileScale); + stream->writeFlag(mFixedTexcoords); + stream->writeFlag(mTexcoordsRelativeToDistance); +} + +void RibbonData::unpackData(BitStream* stream) +{ + Parent::unpackData(stream); + + for (U8 i = 0; i < RIBBON_NUM_FIELDS; i++) { + stream->read(&mSizes[i]); + stream->read(&mColours[i]); + stream->read(&mTimes[i]); + } + + stream->read(&mRibbonLength); + mMatName = StringTable->insert(stream->readSTString()); + mUseFadeOut = stream->readFlag(); + stream->read(&mFadeAwayStep); + stream->read(&segmentsPerUpdate); + stream->read(&mTileScale); + mFixedTexcoords = stream->readFlag(); + mTexcoordsRelativeToDistance = stream->readFlag(); +} + + +//-------------------------------------------------------------------------- +//-------------------------------------- +// +Ribbon::Ribbon() +{ + mTypeMask |= StaticObjectType; + + VECTOR_SET_ASSOCIATION(mSegmentPoints); + mSegmentPoints.clear(); + + mRibbonMat = NULL; + + mUpdateBuffers = true; + mDeleteOnEnd = false; + mUseFadeOut = false; + mFadeAwayStep = 1.0f; + mFadeOut = 1.0f; + + mNetFlags.clear(Ghostable); + mNetFlags.set(IsGhost); + + mRadiusSC = NULL; + mRibbonProjSC = NULL; + + mSegmentOffset = 0; + mSegmentIdx = 0; + + mTravelledDistance = 0; +} + +Ribbon::~Ribbon() +{ + //Make sure we cleanup + SAFE_DELETE(mRibbonMat); +} + +//-------------------------------------------------------------------------- +void Ribbon::initPersistFields() +{ + Parent::initPersistFields(); +} + +bool Ribbon::onAdd() +{ + if(!Parent::onAdd()) + return false; + + // add to client side mission cleanup + SimGroup *cleanup = dynamic_cast( Sim::findObject( "ClientMissionCleanup") ); + if( cleanup != NULL ) + { + cleanup->addObject( this ); + } + else + { + AssertFatal( false, "Error, could not find ClientMissionCleanup group" ); + return false; + } + + if (!isServerObject()) { + + if(GFX->getPixelShaderVersion() >= 1.1 && dStrlen(mDataBlock->mMatName) > 0 ) + { + mRibbonMat = MATMGR->createMatInstance( mDataBlock->mMatName ); + GFXStateBlockDesc desc; + desc.setZReadWrite( true, false ); + desc.cullDefined = true; + desc.cullMode = GFXCullNone; + desc.setBlend(true, GFXBlendSrcAlpha, GFXBlendInvSrcAlpha); + + desc.samplersDefined = true; + + GFXSamplerStateDesc sDesc(GFXSamplerStateDesc::getClampLinear()); + sDesc.addressModeV = GFXAddressWrap; + + desc.samplers[0] = sDesc; + + mRibbonMat->addStateBlockDesc( desc ); + mRibbonMat->init(MATMGR->getDefaultFeatures(), getGFXVertexFormat()); + + mRadiusSC = mRibbonMat->getMaterialParameterHandle( "$radius" ); + mRibbonProjSC = mRibbonMat->getMaterialParameterHandle( "$ribbonProj" ); + + } else { + Con::warnf( "Invalid Material name: %s: for Ribbon", mDataBlock->mMatName ); +#ifdef TORQUE_DEBUG + Con::warnf( "- This could be caused by having the shader data datablocks in server-only code." ); +#endif + mRibbonMat = NULL; + } + } + + mObjBox.minExtents.set( 1.0f, 1.0f, 1.0f ); + mObjBox.maxExtents.set( 2.0f, 2.0f, 2.0f ); + // Reset the World Box. + resetWorldBox(); + // Set the Render Transform. + setRenderTransform(mObjToWorld); + + addToScene(); + + return true; +} + + +void Ribbon::onRemove() +{ + + removeFromScene(); + SAFE_DELETE(mRibbonMat); + + Parent::onRemove(); +} + + +bool Ribbon::onNewDataBlock(GameBaseData* dptr, bool reload) +{ + mDataBlock = dynamic_cast(dptr); + if (!mDataBlock || !Parent::onNewDataBlock(dptr, reload)) + return false; + + return true; +} + +void Ribbon::processTick(const Move* move) +{ + Parent::processTick(move); + + if (mDeleteOnEnd) { + + if (mUseFadeOut) { + + if (mFadeOut <= 0.0f) { + mFadeOut = 0.0f; + //delete this class + mDeleteOnEnd = false; + safeDeleteObject(); + return; + } + mFadeOut -= mFadeAwayStep; + if (mFadeOut < 0.0f) { + mFadeOut = 0.0f; + } + + mUpdateBuffers = true; + + } else { + //if (mSegmentPoints.size() == 0) { + //delete this class + mDeleteOnEnd = false; + safeDeleteObject(); + return; + //} + //mSegmentPoints.pop_back(); + } + + + } +} + +void Ribbon::advanceTime(F32 dt) +{ + Parent::advanceTime(dt); +} + +void Ribbon::interpolateTick(F32 delta) +{ + Parent::interpolateTick(delta); +} + +void Ribbon::addSegmentPoint(Point3F &point, MatrixF &mat) { + + //update our position + setRenderTransform(mat); + MatrixF xform(true); + xform.setColumn(3, point); + setTransform(xform); + + if(mSegmentIdx < mDataBlock->mSegmentSkipAmount) + { + mSegmentIdx++; + return; + } + + mSegmentIdx = 0; + + U32 segmentsToDelete = checkRibbonDistance(mDataBlock->segmentsPerUpdate); + + for (U32 i = 0; i < segmentsToDelete; i++) { + U32 last = mSegmentPoints.size() - 1; + if (last < 0) + break; + mTravelledDistance += last ? (mSegmentPoints[last] - mSegmentPoints[last-1]).len() : 0; + mSegmentPoints.pop_back(); + mUpdateBuffers = true; + mSegmentOffset++; + } + + //If there is no other points, just add a new one. + if (mSegmentPoints.size() == 0) { + + mSegmentPoints.push_front(point); + mUpdateBuffers = true; + return; + } + + Point3F startPoint = mSegmentPoints[0]; + + //add X points based on how many segments Per Update from last point to current point + for (U32 i = 0; i < mDataBlock->segmentsPerUpdate; i++) { + + F32 interp = (F32(i+1) / (F32)mDataBlock->segmentsPerUpdate); + //(end - start) * percentage) + start + Point3F derivedPoint = ((point - startPoint) * interp) + startPoint; + + mSegmentPoints.push_front(derivedPoint); + mUpdateBuffers = true; + } + + if (mSegmentPoints.size() > 1) { + + Point3F pointA = mSegmentPoints[mSegmentPoints.size()-1]; + Point3F pointB = mSegmentPoints[0]; + + Point3F diffSize = pointA - pointB; + + if (diffSize.x == 0.0f) + diffSize.x = 1.0f; + + if (diffSize.y == 0.0f) + diffSize.y = 1.0f; + + if (diffSize.z == 0.0f) + diffSize.z = 1.0f; + + Box3F objBox; + objBox.minExtents.set( diffSize * -1 ); + objBox.maxExtents.set( diffSize ); + + if (objBox.minExtents.x > objBox.maxExtents.x) { + F32 tmp = objBox.minExtents.x; + objBox.minExtents.x = objBox.maxExtents.x; + objBox.maxExtents.x = tmp; + } + if (objBox.minExtents.y > objBox.maxExtents.y) { + F32 tmp = objBox.minExtents.y; + objBox.minExtents.y = objBox.maxExtents.y; + objBox.maxExtents.y = tmp; + } + if (objBox.minExtents.z > objBox.maxExtents.z) { + F32 tmp = objBox.minExtents.z; + objBox.minExtents.z = objBox.maxExtents.z; + objBox.maxExtents.z = tmp; + } + + + + if (objBox.isValidBox()) { + mObjBox = objBox; + // Reset the World Box. + resetWorldBox(); + } + } + +} + +void Ribbon::deleteOnEnd() { + + mDeleteOnEnd = true; + mUseFadeOut = mDataBlock->mUseFadeOut; + mFadeAwayStep = mDataBlock->mFadeAwayStep; + +} + +U32 Ribbon::checkRibbonDistance(S32 segments) { + + S32 len = mSegmentPoints.size(); + S32 difference = (mDataBlock->mRibbonLength/(mDataBlock->mSegmentSkipAmount+1)) - len; + + if (difference < 0) + return mAbs(difference); + + return 0; //do not delete any points +} + +void Ribbon::setShaderParams() { + + F32 numSegments = (F32)mSegmentPoints.size(); + F32 length = (F32)mDataBlock->mRibbonLength; + Point3F radius(numSegments / length, numSegments, length); + MaterialParameters* matParams = mRibbonMat->getMaterialParameters(); + matParams->setSafe( mRadiusSC, radius ); +} + +//-------------------------------------------------------------------------- +//U32 Ribbon::packUpdate(NetConnection* con, U32 mask, BitStream* stream) +//{ +// U32 retMask = Parent::packUpdate(con, mask, stream); +// return retMask; +//} +// +//void Ribbon::unpackUpdate(NetConnection* con, BitStream* stream) +//{ +// Parent::unpackUpdate(con, stream); +//} + +//-------------------------------------------------------------------------- +void Ribbon::prepRenderImage(SceneRenderState *state) +{ + if (mFadeOut == 0.0f) + return; + + if(!mRibbonMat) + return; + + if (mDeleteOnEnd == true && mUseFadeOut == false) { + return; + } + + // We only render during the normal diffuse render pass. + if( !state->isDiffusePass() ) + return; + + U32 segments = mSegmentPoints.size(); + if (segments < 2) + return; + + MeshRenderInst *ri = state->getRenderPass()->allocInst(); + ri->type = RenderPassManager::RIT_Translucent; + ri->translucentSort = true; + ri->sortDistSq = ( mSegmentPoints[0] - state->getCameraPosition() ).lenSquared(); + + RenderPassManager *renderPass = state->getRenderPass(); + MatrixF *proj = renderPass->allocUniqueXform(MatrixF( true )); + proj->mul(GFX->getProjectionMatrix()); + proj->mul(GFX->getWorldMatrix()); + ri->objectToWorld = &MatrixF::Identity; + ri->worldToCamera = &MatrixF::Identity; + ri->projection = proj; + ri->matInst = mRibbonMat; + + // Set up our vertex buffer and primitive buffer + if(mUpdateBuffers) + createBuffers(state, verts, primBuffer, segments); + + ri->vertBuff = &verts; + ri->primBuff = &primBuffer; + ri->visibility = 1.0f; + + ri->prim = renderPass->allocPrim(); + ri->prim->type = GFXTriangleList; + ri->prim->minIndex = 0; + ri->prim->startIndex = 0; + ri->prim->numPrimitives = (segments-1) * 2; + ri->prim->startVertex = 0; + ri->prim->numVertices = segments * 2; + + if (mRibbonMat) { + ri->defaultKey = mRibbonMat->getStateHint(); + } else { + ri->defaultKey = 1; + } + ri->defaultKey2 = (U32)ri->vertBuff; // Not 64bit safe! + + state->getRenderPass()->addInst(ri); +} + +void Ribbon::createBuffers(SceneRenderState *state, GFXVertexBufferHandle &verts, GFXPrimitiveBufferHandle &pb, U32 segments) { + PROFILE_SCOPE( Ribbon_createBuffers ); + Point3F cameraPos = state->getCameraPosition(); + U32 count = 0; + U32 indexCount = 0; + verts.set(GFX, (segments*2), GFXBufferTypeDynamic); + + // create index buffer based on that size + U32 indexListSize = (segments-1) * 6; + pb.set( GFX, indexListSize, 0, GFXBufferTypeDynamic ); + U16 *indices = NULL; + + verts.lock(); + pb.lock( &indices ); + F32 totalLength = 0; + if(mDataBlock->mTexcoordsRelativeToDistance) + { + for (U32 i = 0; i < segments; i++) + if (i != 0) + totalLength += (mSegmentPoints[i] - mSegmentPoints[i-1]).len(); + } + + U8 fixedAppend = 0; + F32 curLength = 0; + for (U32 i = 0; i < segments; i++) { + + F32 interpol = ((F32)i / (F32)(segments-1)); + Point3F leftvert = mSegmentPoints[i]; + Point3F rightvert = mSegmentPoints[i]; + F32 tRadius = mDataBlock->mSizes[0]; + ColorF tColor = mDataBlock->mColours[0]; + + for (U8 j = 0; j < RIBBON_NUM_FIELDS-1; j++) { + + F32 curPosition = mDataBlock->mTimes[j]; + F32 curRadius = mDataBlock->mSizes[j]; + ColorF curColor = mDataBlock->mColours[j]; + F32 nextPosition = mDataBlock->mTimes[j+1]; + F32 nextRadius = mDataBlock->mSizes[j+1]; + ColorF nextColor = mDataBlock->mColours[j+1]; + + if ( curPosition < 0 + || curPosition > interpol ) + break; + F32 positionDiff = (interpol - curPosition) / (nextPosition - curPosition); + + tRadius = curRadius + (nextRadius - curRadius) * positionDiff; + tColor.interpolate(curColor, nextColor, positionDiff); + } + + Point3F diff; + F32 length; + if (i == 0) { + diff = mSegmentPoints[i+1] - mSegmentPoints[i]; + length = 0; + } else if (i == segments-1) { + diff = mSegmentPoints[i] - mSegmentPoints[i-1]; + length = diff.len(); + } else { + diff = mSegmentPoints[i+1] - mSegmentPoints[i-1]; + length = (mSegmentPoints[i] - mSegmentPoints[i-1]).len(); + } + + //left point + Point3F eyeMinPos = cameraPos - leftvert; + Point3F perpendicular = mCross(diff, eyeMinPos); + perpendicular.normalize(); + perpendicular = perpendicular * tRadius * -1.0f; + perpendicular += mSegmentPoints[i]; + + verts[count].point.set(perpendicular); + ColorF color = tColor; + + if (mDataBlock->mUseFadeOut) + color.alpha *= mFadeOut; + + F32 texCoords; + if(mDataBlock->mFixedTexcoords && !mDataBlock->mTexcoordsRelativeToDistance) + { + U32 fixedIdx = (i+mDataBlock->mRibbonLength-mSegmentOffset)%mDataBlock->mRibbonLength; + if(fixedIdx == 0 && i > 0) + fixedAppend++; + F32 fixedInterpol = (F32)fixedIdx / (F32)(mDataBlock->mRibbonLength); + fixedInterpol += fixedAppend; + texCoords = (1.0f - fixedInterpol)*mDataBlock->mTileScale; + } + else if(mDataBlock->mTexcoordsRelativeToDistance) + texCoords = (mTravelledDistance + (totalLength - (curLength + length)))*mDataBlock->mTileScale; + else + texCoords = (1.0f - interpol)*mDataBlock->mTileScale; + + verts[count].color = color; + verts[count].texCoord[1] = Point2F(interpol, 0); + verts[count].texCoord[0] = Point2F(0.0f, texCoords); + verts[count].normal.set(diff); + + //Triangle strip style indexing, so grab last 2 + if (count > 1) { + indices[indexCount] = count-2; + indexCount++; + indices[indexCount] = count; + indexCount++; + indices[indexCount] = count-1; + indexCount++; + } + count++; + + eyeMinPos = cameraPos - rightvert; + perpendicular = mCross(diff, eyeMinPos); + perpendicular.normalize(); + perpendicular = perpendicular * tRadius; + perpendicular += mSegmentPoints[i]; + + verts[count].point.set(perpendicular); + color = tColor; + + if (mDataBlock->mUseFadeOut) + color.alpha *= mFadeOut; + + verts[count].color = color; + verts[count].texCoord[1] = Point2F(interpol, 1); + verts[count].texCoord[0] = Point2F(1.0f, texCoords); + verts[count].normal.set(diff); + + //Triangle strip style indexing, so grab last 2 + if (count > 1) { + indices[indexCount] = count-2; + indexCount++; + indices[indexCount] = count-1; + indexCount++; + indices[indexCount] = count; + indexCount++; + } + count++; + curLength += length; + } + + Point3F pointA = verts[count-1].point; + Point3F pointB = verts[0].point; + + verts.unlock(); + pb.unlock(); + + Point3F diffSize = pointA - pointB; + + Box3F objBox; + objBox.minExtents.set( diffSize * -1 ); + objBox.maxExtents.set( diffSize ); + + if (objBox.minExtents.x > objBox.maxExtents.x) { + F32 tmp = objBox.minExtents.x; + objBox.minExtents.x = objBox.maxExtents.x; + objBox.maxExtents.x = tmp; + } + if (objBox.minExtents.y > objBox.maxExtents.y) { + F32 tmp = objBox.minExtents.y; + objBox.minExtents.y = objBox.maxExtents.y; + objBox.maxExtents.y = tmp; + } + if (objBox.minExtents.z > objBox.maxExtents.z) { + F32 tmp = objBox.minExtents.z; + objBox.minExtents.z = objBox.maxExtents.z; + objBox.maxExtents.z = tmp; + } + + if (objBox.isValidBox()) { + mObjBox = objBox; + // Reset the World Box. + resetWorldBox(); + } + + mUpdateBuffers = false; +} \ No newline at end of file diff --git a/Engine/source/T3D/fx/ribbon.h b/Engine/source/T3D/fx/ribbon.h new file mode 100644 index 000000000..ce115d461 --- /dev/null +++ b/Engine/source/T3D/fx/ribbon.h @@ -0,0 +1,127 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RIBBON_H_ +#define _RIBBON_H_ + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif + +#ifndef _GFXPRIMITIVEBUFFER_H_ +#include "gfx/gfxPrimitiveBuffer.h" +#endif + +#ifndef _GFXVERTEXBUFFER_H_ +#include "gfx/gfxVertexBuffer.h" +#endif + +#include "materials/materialParameters.h" +#include "math/util/matrixSet.h" + +#define RIBBON_NUM_FIELDS 4 + +//-------------------------------------------------------------------------- +class RibbonData : public GameBaseData +{ + typedef GameBaseData Parent; + +protected: + bool onAdd(); + +public: + + U32 mRibbonLength; ///< The amount of segments that will make up the ribbon. + F32 mSizes[RIBBON_NUM_FIELDS]; ///< The radius for each keyframe. + ColorF mColours[RIBBON_NUM_FIELDS]; ///< The colour of the ribbon for each keyframe. + F32 mTimes[RIBBON_NUM_FIELDS]; ///< The relative time for each keyframe. + StringTableEntry mMatName; ///< The material for the ribbon. + bool mUseFadeOut; ///< If true, the ribbon will fade away after deletion. + F32 mFadeAwayStep; ///< How quickly the ribbons is faded away after deletion. + S32 segmentsPerUpdate; ///< Amount of segments to add each update. + F32 mTileScale; ///< A scalar to scale the texcoord. + bool mFixedTexcoords; ///< If true, texcoords will stay the same over the lifetime for each segment. + bool mTexcoordsRelativeToDistance; ///< If true, texcoords will not be stretched if the distance between 2 segments are long. + S32 mSegmentSkipAmount; ///< The amount of segments to skip each time segments are added. + + RibbonData(); + + void packData(BitStream*); + void unpackData(BitStream*); + bool preload(bool server, String &errorBuffer); + + static void initPersistFields(); + DECLARE_CONOBJECT(RibbonData); +}; + +//-------------------------------------------------------------------------- +class Ribbon : public GameBase +{ + typedef GameBase Parent; + RibbonData* mDataBlock; + Vector mSegmentPoints; ///< The points in space where the ribbon has spawned segments. + BaseMatInstance *mRibbonMat; + MaterialParameterHandle* mRadiusSC; + MaterialParameterHandle* mRibbonProjSC; + GFXPrimitiveBufferHandle primBuffer; + GFXVertexBufferHandle verts; + bool mUpdateBuffers; ///< If true, the vertex buffers need to be updated. + bool mDeleteOnEnd; ///< If true, the ribbon should delete itself as soon as the last segment is deleted + bool mUseFadeOut; ///< If true, the ribbon will fade away upon deletion + F32 mFadeAwayStep; ///< How quickly the ribbons is faded away after deletion. + F32 mFadeOut; + U32 mSegmentOffset; + U32 mSegmentIdx; + F32 mTravelledDistance; ///< How far the ribbon has travelled in it's lifetime. + +protected: + + bool onAdd(); + void processTick(const Move*); + void advanceTime(F32); + void interpolateTick(F32 delta); + + // Rendering + void prepRenderImage(SceneRenderState *state); + + ///Checks to see if ribbon is too long + U32 checkRibbonDistance(S32 segments); + void setShaderParams(); + /// Construct the vertex and primitive buffers + void createBuffers(SceneRenderState *state, GFXVertexBufferHandle &verts, GFXPrimitiveBufferHandle &pb, U32 segments); + +public: + Ribbon(); + ~Ribbon(); + + DECLARE_CONOBJECT(Ribbon); + static void initPersistFields(); + bool onNewDataBlock(GameBaseData*,bool); + void addSegmentPoint(Point3F &point, MatrixF &mat); ///< Used to add another segment to the ribbon. + void clearSegments() { mSegmentPoints.clear(); } ///< Delete all segments. + void deleteOnEnd(); ///< Delete the ribbon when all segments have been deleted. + void onRemove(); + +}; + +#endif // _H_RIBBON + From a8a141e73c435d93a23fdd0f0d1aa86aa6f8ca90 Mon Sep 17 00:00:00 2001 From: Lukas Joergensen Date: Thu, 31 Jul 2014 00:31:02 +0200 Subject: [PATCH 190/317] RibbonNode class. Simple RibbonNode class for an implementation similar to that of ParticleEmitterNodes. Datablock currently has no static fields. --- Engine/source/T3D/fx/ribbonNode.cpp | 324 ++++++++++++++++++++++++++++ Engine/source/T3D/fx/ribbonNode.h | 108 ++++++++++ 2 files changed, 432 insertions(+) create mode 100644 Engine/source/T3D/fx/ribbonNode.cpp create mode 100644 Engine/source/T3D/fx/ribbonNode.h diff --git a/Engine/source/T3D/fx/ribbonNode.cpp b/Engine/source/T3D/fx/ribbonNode.cpp new file mode 100644 index 000000000..0343fc5b3 --- /dev/null +++ b/Engine/source/T3D/fx/ribbonNode.cpp @@ -0,0 +1,324 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#include "ribbonNode.h" +#include "console/consoleTypes.h" +#include "core/stream/bitStream.h" +#include "T3D/fx/ribbon.h" +#include "math/mathIO.h" +#include "sim/netConnection.h" +#include "console/engineAPI.h" + +IMPLEMENT_CO_DATABLOCK_V1(RibbonNodeData); +IMPLEMENT_CO_NETOBJECT_V1(RibbonNode); + +ConsoleDocClass( RibbonNodeData, + "@brief Contains additional data to be associated with a RibbonNode." + "@ingroup FX\n" + ); + +ConsoleDocClass( RibbonNode, "" + ); + + +//----------------------------------------------------------------------------- +// RibbonNodeData +//----------------------------------------------------------------------------- +RibbonNodeData::RibbonNodeData() +{ +} + +RibbonNodeData::~RibbonNodeData() +{ + +} + +//----------------------------------------------------------------------------- +// initPersistFields +//----------------------------------------------------------------------------- +void RibbonNodeData::initPersistFields() +{ + Parent::initPersistFields(); +} + + +//----------------------------------------------------------------------------- +// RibbonNode +//----------------------------------------------------------------------------- +RibbonNode::RibbonNode() +{ + // Todo: ScopeAlways? + mNetFlags.set(Ghostable); + mTypeMask |= EnvironmentObjectType; + + mActive = true; + + mDataBlock = NULL; + mRibbonDatablock = NULL; + mRibbonDatablockId = 0; + mRibbon = NULL; +} + +//----------------------------------------------------------------------------- +// Destructor +//----------------------------------------------------------------------------- +RibbonNode::~RibbonNode() +{ + // +} + +//----------------------------------------------------------------------------- +// initPersistFields +//----------------------------------------------------------------------------- +void RibbonNode::initPersistFields() +{ + addField( "active", TYPEID< bool >(), Offset(mActive,RibbonNode), + "Controls whether ribbon is emitted from this node." ); + addField( "ribbon", TYPEID< RibbonData >(), Offset(mRibbonDatablock, RibbonNode), + "Datablock to use for the ribbon." ); + + Parent::initPersistFields(); +} + +//----------------------------------------------------------------------------- +// onAdd +//----------------------------------------------------------------------------- +bool RibbonNode::onAdd() +{ + if( !Parent::onAdd() ) + return false; + + if( !mRibbonDatablock && mRibbonDatablockId != 0 ) + { + if( Sim::findObject(mRibbonDatablockId, mRibbonDatablock) == false ) + Con::errorf(ConsoleLogEntry::General, "RibbonNode::onAdd: Invalid packet, bad datablockId(mRibbonDatablock): %d", mRibbonDatablockId); + } + + if( isClientObject() ) + { + setRibbonDatablock( mRibbonDatablock ); + } + else + { + setMaskBits( StateMask | EmitterDBMask ); + } + + mObjBox.minExtents.set(-0.5, -0.5, -0.5); + mObjBox.maxExtents.set( 0.5, 0.5, 0.5); + resetWorldBox(); + addToScene(); + + return true; +} + +//----------------------------------------------------------------------------- +// onRemove +//----------------------------------------------------------------------------- +void RibbonNode::onRemove() +{ + removeFromScene(); + if( isClientObject() ) + { + if( mRibbon ) + { + mRibbon->deleteOnEnd(); + mRibbon = NULL; + } + } + + Parent::onRemove(); +} + +//----------------------------------------------------------------------------- +// onNewDataBlock +//----------------------------------------------------------------------------- +bool RibbonNode::onNewDataBlock( GameBaseData *dptr, bool reload ) +{ + mDataBlock = dynamic_cast( dptr ); + if ( !mDataBlock || !Parent::onNewDataBlock( dptr, reload ) ) + return false; + + // Todo: Uncomment if this is a "leaf" class + scriptOnNewDataBlock(); + return true; +} + +//----------------------------------------------------------------------------- +void RibbonNode::inspectPostApply() +{ + Parent::inspectPostApply(); + setMaskBits(StateMask | EmitterDBMask); +} + +//----------------------------------------------------------------------------- +// processTick +//----------------------------------------------------------------------------- +void RibbonNode::processTick(const Move* move) +{ + Parent::processTick(move); + + if ( isMounted() ) + { + MatrixF mat; + mMount.object->getMountTransform( mMount.node, mMount.xfm, &mat ); + setTransform( mat ); + } +} + +//----------------------------------------------------------------------------- +// advanceTime +//----------------------------------------------------------------------------- +void RibbonNode::advanceTime(F32 dt) +{ + Parent::advanceTime(dt); + + if(!mActive || mRibbon.isNull() || !mDataBlock) + return; + + MatrixF trans(getTransform()); + Point3F pos = getPosition(); + mRibbon->addSegmentPoint(pos, trans); +} + +//----------------------------------------------------------------------------- +// packUpdate +//----------------------------------------------------------------------------- +U32 RibbonNode::packUpdate(NetConnection* con, U32 mask, BitStream* stream) +{ + U32 retMask = Parent::packUpdate(con, mask, stream); + + if ( stream->writeFlag( mask & InitialUpdateMask ) ) + { + mathWrite(*stream, getTransform()); + mathWrite(*stream, getScale()); + } + + if ( stream->writeFlag( mask & EmitterDBMask ) ) + { + if( stream->writeFlag(mRibbonDatablock != NULL) ) + { + stream->writeRangedU32(mRibbonDatablock->getId(), DataBlockObjectIdFirst, + DataBlockObjectIdLast); + } + } + + if ( stream->writeFlag( mask & StateMask ) ) + { + stream->writeFlag( mActive ); + } + + return retMask; +} + +//----------------------------------------------------------------------------- +// unpackUpdate +//----------------------------------------------------------------------------- +void RibbonNode::unpackUpdate(NetConnection* con, BitStream* stream) +{ + Parent::unpackUpdate(con, stream); + + if ( stream->readFlag() ) + { + MatrixF temp; + Point3F tempScale; + mathRead(*stream, &temp); + mathRead(*stream, &tempScale); + + setScale(tempScale); + setTransform(temp); + } + + if ( stream->readFlag() ) + { + mRibbonDatablockId = stream->readFlag() ? + stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast) : 0; + + RibbonData *emitterDB = NULL; + Sim::findObject( mRibbonDatablockId, emitterDB ); + if ( isProperlyAdded() ) + setRibbonDatablock( emitterDB ); + } + + if ( stream->readFlag() ) + { + mActive = stream->readFlag(); + } +} + +//----------------------------------------------------------------------------- +// setRibbonDatablock +//----------------------------------------------------------------------------- +void RibbonNode::setRibbonDatablock(RibbonData* data) +{ + if ( isServerObject() ) + { + setMaskBits( EmitterDBMask ); + } + else + { + Ribbon* pRibbon = NULL; + if ( data ) + { + // Create emitter with new datablock + pRibbon = new Ribbon; + pRibbon->onNewDataBlock( data, false ); + if( pRibbon->registerObject() == false ) + { + Con::warnf(ConsoleLogEntry::General, "Could not register base ribbon of class: %s", data->getName() ? data->getName() : data->getIdString() ); + delete pRibbon; + return; + } + } + + // Replace emitter + if ( mRibbon ) + mRibbon->deleteOnEnd(); + + mRibbon = pRibbon; + } + + mRibbonDatablock = data; +} + +DefineEngineMethod(RibbonNode, setRibbonDatablock, void, (RibbonData* ribbonDatablock), (0), + "Assigns the datablock for this ribbon node.\n" + "@param ribbonDatablock RibbonData datablock to assign\n" + "@tsexample\n" + "// Assign a new emitter datablock\n" + "%emitter.setRibbonDatablock( %ribbonDatablock );\n" + "@endtsexample\n" ) +{ + if ( !ribbonDatablock ) + { + Con::errorf("RibbonData datablock could not be found when calling setRibbonDataBlock in ribbonNode."); + return; + } + + object->setRibbonDatablock(ribbonDatablock); +} + +DefineEngineMethod(RibbonNode, setActive, void, (bool active),, + "Turns the ribbon on or off.\n" + "@param active New ribbon state\n" ) +{ + object->setActive( active ); +} diff --git a/Engine/source/T3D/fx/ribbonNode.h b/Engine/source/T3D/fx/ribbonNode.h new file mode 100644 index 000000000..62b1158e4 --- /dev/null +++ b/Engine/source/T3D/fx/ribbonNode.h @@ -0,0 +1,108 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +#ifndef _RIBBON_NODE_H_ +#define _RIBBON_NODE_H_ + +#ifndef _GAMEBASE_H_ +#include "T3D/gameBase/gameBase.h" +#endif + +class RibbonData; +class Ribbon; + +//***************************************************************************** +// ParticleEmitterNodeData +//***************************************************************************** +class RibbonNodeData : public GameBaseData +{ + typedef GameBaseData Parent; + + //-------------------------------------- Console set variables +public: + F32 timeMultiple; + + //-------------------------------------- load set variables + +public: + RibbonNodeData(); + ~RibbonNodeData(); + + DECLARE_CONOBJECT(RibbonNodeData); + static void initPersistFields(); +}; + + +//***************************************************************************** +// ParticleEmitterNode +//***************************************************************************** +class RibbonNode : public GameBase +{ + typedef GameBase Parent; + + enum MaskBits + { + StateMask = Parent::NextFreeMask << 0, + EmitterDBMask = Parent::NextFreeMask << 1, + NextFreeMask = Parent::NextFreeMask << 2, + }; + + RibbonNodeData* mDataBlock; + +protected: + bool onAdd(); + void onRemove(); + bool onNewDataBlock( GameBaseData *dptr, bool reload ); + void inspectPostApply(); + + RibbonData* mRibbonDatablock; + S32 mRibbonDatablockId; + + SimObjectPtr mRibbon; + + bool mActive; + +public: + RibbonNode(); + ~RibbonNode(); + + Ribbon *getRibbonEmitter() {return mRibbon;} + + // Time/Move Management + + void processTick(const Move* move); + void advanceTime(F32 dt); + + DECLARE_CONOBJECT(RibbonNode); + static void initPersistFields(); + + U32 packUpdate (NetConnection *conn, U32 mask, BitStream* stream); + void unpackUpdate(NetConnection *conn, BitStream* stream); + + inline bool getActive( void ) { return mActive; }; + inline void setActive( bool active ) { mActive = active; setMaskBits( StateMask ); }; + + void setRibbonDatablock(RibbonData* data); +}; + +#endif // _RIBBON_NODE_H_ + From 94d32b5faea1a9b3d79167dfe8c4317893faab9f Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 21 Sep 2014 22:50:47 +1000 Subject: [PATCH 191/317] Make mSubscribers case-insensitive. --- Engine/source/util/messaging/eventManager.h | 3 +++ 1 file changed, 3 insertions(+) diff --git a/Engine/source/util/messaging/eventManager.h b/Engine/source/util/messaging/eventManager.h index e5ec82032..888e8357c 100644 --- a/Engine/source/util/messaging/eventManager.h +++ b/Engine/source/util/messaging/eventManager.h @@ -58,6 +58,9 @@ class EventManagerListener : public Dispatcher::IMessageListener SimpleHashTable< Vector > mSubscribers; public: + // Ensure that the subscriber map doesn't use case-sensitive string comparisons. + EventManagerListener(): mSubscribers(64, false) {} + /// Called by the EventManager queue when an event is triggered. Calls all listeners subscribed to the triggered event. virtual bool onMessageReceived( StringTableEntry queue, const char* event, const char* data ); virtual bool onMessageObjectReceived( StringTableEntry queue, Message *msg ) { return true; }; From 5e59a08ed9ebcfa319c9618d1128bf9427fd5d5e Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Mon, 22 Sep 2014 13:06:41 +1000 Subject: [PATCH 192/317] Bumped minimum CMake version. 2.8.12 is the lowest known working version number for all platforms. 2.8.7 is known not to work on Ubuntu 12.10. --- CMakeLists.txt | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 77057ec73..6ea0659d3 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -1,4 +1,4 @@ -cmake_minimum_required (VERSION 2.8.3) +cmake_minimum_required (VERSION 2.8.12) set(TORQUE_APP_NAME "" CACHE STRING "the app name") @@ -8,4 +8,4 @@ endif() project(${TORQUE_APP_NAME}) -add_subdirectory(Tools/CMake) \ No newline at end of file +add_subdirectory(Tools/CMake) From 65f6a4fe0a5238aa66d4341c5b6fd6fcf5241ef9 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Mon, 22 Sep 2014 22:11:03 +1000 Subject: [PATCH 193/317] Link against librt. --- Tools/CMake/torque3d.cmake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 4c6869610..7f4ead92d 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -506,7 +506,7 @@ endif() if(UNIX) # copy pasted from T3D build system, some might not be needed - set(TORQUE_EXTERNAL_LIBS "dl Xxf86vm Xext X11 Xft stdc++ pthread GL" CACHE STRING "external libs to link against") + set(TORQUE_EXTERNAL_LIBS "rt dl Xxf86vm Xext X11 Xft stdc++ pthread GL" CACHE STRING "external libs to link against") mark_as_advanced(TORQUE_EXTERNAL_LIBS) string(REPLACE " " ";" TORQUE_EXTERNAL_LIBS_LIST ${TORQUE_EXTERNAL_LIBS}) From f2a3a1e2a67064e6f4369f6540db51e780c080ba Mon Sep 17 00:00:00 2001 From: Lukas Joergensen Date: Tue, 23 Sep 2014 12:56:48 +0200 Subject: [PATCH 194/317] Enable RGB format for DDS files getting copied to bitmap. --- Engine/source/gfx/D3D9/gfxD3D9TextureObject.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/source/gfx/D3D9/gfxD3D9TextureObject.cpp b/Engine/source/gfx/D3D9/gfxD3D9TextureObject.cpp index ff9c69096..bdf0ef449 100644 --- a/Engine/source/gfx/D3D9/gfxD3D9TextureObject.cpp +++ b/Engine/source/gfx/D3D9/gfxD3D9TextureObject.cpp @@ -205,8 +205,8 @@ bool GFXD3D9TextureObject::copyToBmp(GBitmap* bmp) // check format limitations // at the moment we only support RGBA for the source (other 4 byte formats should // be easy to add though) - AssertFatal(mFormat == GFXFormatR8G8B8A8, "copyToBmp: invalid format"); - if (mFormat != GFXFormatR8G8B8A8) + AssertFatal(mFormat == GFXFormatR8G8B8A8 || mFormat == GFXFormatR8G8B8, "copyToBmp: invalid format"); + if (mFormat != GFXFormatR8G8B8A8 && mFormat != GFXFormatR8G8B8) return false; PROFILE_START(GFXD3D9TextureObject_copyToBmp); From 431d8a9b66dc29e3df91f999b7dcc500004af707 Mon Sep 17 00:00:00 2001 From: LukasPJ Date: Tue, 23 Sep 2014 07:49:27 +0200 Subject: [PATCH 195/317] Terrain baseTex support multiple formats DDS, PNG, JPG or NONE --- Engine/source/terrain/terrData.cpp | 5 +++-- Engine/source/terrain/terrData.h | 22 ++++++++++++++++++++++ Engine/source/terrain/terrRender.cpp | 25 +++++++++++++++++++------ 3 files changed, 44 insertions(+), 8 deletions(-) diff --git a/Engine/source/terrain/terrData.cpp b/Engine/source/terrain/terrData.cpp index 9db97f28a..1dd89d7b3 100644 --- a/Engine/source/terrain/terrData.cpp +++ b/Engine/source/terrain/terrData.cpp @@ -186,6 +186,7 @@ TerrainBlock::TerrainBlock() mCell( NULL ), mCRC( 0 ), mBaseTexSize( 1024 ), + mBaseTexFormat( TerrainBlock::JPG ), mBaseMaterial( NULL ), mDefaultMatInst( NULL ), mBaseTexScaleConst( NULL ), @@ -961,7 +962,7 @@ String TerrainBlock::_getBaseTexCacheFileName() const { Torque::Path basePath( mTerrFileName ); basePath.setFileName( basePath.getFileName() + "_basetex" ); - basePath.setExtension( "dds" ); + basePath.setExtension( formatToExtension(mBaseTexFormat) ); return basePath.getFullPath(); } @@ -1200,7 +1201,7 @@ void TerrainBlock::unpackUpdate(NetConnection* con, BitStream *stream) { mBaseTexSize = baseTexSize; if ( isProperlyAdded() ) - _updateBaseTexture( false ); + _updateBaseTexture( NONE ); } U32 lightMapSize; diff --git a/Engine/source/terrain/terrData.h b/Engine/source/terrain/terrData.h index 967bfa0d1..b3433dfb1 100644 --- a/Engine/source/terrain/terrData.h +++ b/Engine/source/terrain/terrData.h @@ -74,6 +74,26 @@ protected: NextFreeMask = Parent::NextFreeMask << 6, }; + enum BaseTexFormat + { + NONE, DDS, PNG, JPG + }; + + static const char* formatToExtension(BaseTexFormat format) + { + switch (format) + { + case DDS: + return "dds"; + case PNG: + return "png"; + case JPG: + return "jpg"; + default: + return ""; + } + }; + Box3F mBounds; /// @@ -132,6 +152,8 @@ protected: /// The desired size for the base texture. U32 mBaseTexSize; + BaseTexFormat mBaseTexFormat; + /// TerrCell *mCell; diff --git a/Engine/source/terrain/terrRender.cpp b/Engine/source/terrain/terrRender.cpp index 4b03ac812..61b844380 100644 --- a/Engine/source/terrain/terrRender.cpp +++ b/Engine/source/terrain/terrRender.cpp @@ -178,7 +178,7 @@ bool TerrainBlock::_initBaseShader() return true; } -void TerrainBlock::_updateBaseTexture( bool writeToCache ) +void TerrainBlock::_updateBaseTexture(bool writeToCache) { if ( !mBaseShader && !_initBaseShader() ) return; @@ -290,7 +290,14 @@ void TerrainBlock::_updateBaseTexture( bool writeToCache ) GFX->endScene(); /// Do we cache this sucker? - if ( writeToCache ) + if (mBaseTexFormat == NONE || !writeToCache) + { + // We didn't cache the result, so set the base texture + // to the render target we updated. This should be good + // for realtime painting cases. + mBaseTex = blendTex; + } + else if (mBaseTexFormat == DDS) { String cachePath = _getBaseTexCacheFileName(); @@ -327,10 +334,16 @@ void TerrainBlock::_updateBaseTexture( bool writeToCache ) } else { - // We didn't cache the result, so set the base texture - // to the render target we updated. This should be good - // for realtime painting cases. - mBaseTex = blendTex; + FileStream stream; + if (!stream.open(_getBaseTexCacheFileName(), Torque::FS::File::Write)) + { + mBaseTex = blendTex; + return; + } + + GBitmap bitmap(blendTex->getWidth(), blendTex->getHeight(), false, GFXFormatR8G8B8); + blendTex->copyToBmp(&bitmap); + bitmap.writeBitmap(formatToExtension(mBaseTexFormat), stream); } } From ef11b565bf17e49be99ac35e31ee54b80148af98 Mon Sep 17 00:00:00 2001 From: Lukas Joergensen Date: Tue, 23 Sep 2014 13:40:26 +0200 Subject: [PATCH 196/317] Expose new formats to the editor. --- Engine/source/terrain/terrData.cpp | 37 ++++++++++++++++++++++++++++++ Engine/source/terrain/terrData.h | 7 +++++- 2 files changed, 43 insertions(+), 1 deletion(-) diff --git a/Engine/source/terrain/terrData.cpp b/Engine/source/terrain/terrData.cpp index 1dd89d7b3..913232298 100644 --- a/Engine/source/terrain/terrData.cpp +++ b/Engine/source/terrain/terrData.cpp @@ -174,6 +174,18 @@ ConsoleFunction(getTerrainUnderWorldPoint, S32, 2, 4, "(Point3F x/y/z) Gets the } +typedef TerrainBlock::BaseTexFormat baseTexFormat; +DefineEnumType(baseTexFormat); + +ImplementEnumType(baseTexFormat, + "Description\n" + "@ingroup ?\n\n") +{ TerrainBlock::NONE, "NONE", "No cached terrain.\n" }, +{ TerrainBlock::DDS, "DDS", "Cache the terrain in a DDS format.\n" }, +{ TerrainBlock::PNG, "PNG", "Cache the terrain in a PNG format.\n" }, +{ TerrainBlock::JPG, "JPG", "Cache the terrain in a JPG format.\n" }, +EndImplementEnumType; + TerrainBlock::TerrainBlock() : mSquareSize( 1.0f ), mCastShadows( true ), @@ -270,6 +282,27 @@ bool TerrainBlock::_setBaseTexSize( void *obj, const char *index, const char *da return false; } +bool TerrainBlock::_setBaseTexFormat(void *obj, const char *index, const char *data) +{ + TerrainBlock *terrain = static_cast(obj); + + EngineEnumTable eTable = _baseTexFormat::_sEnumTable; + + for (U8 i = 0; i < eTable.getNumValues(); i++) + { + if (strcasecmp(eTable[i].mName, data) == 0) + { + terrain->mBaseTexFormat = (BaseTexFormat)eTable[i].mInt; + terrain->_updateMaterials(); + terrain->_updateLayerTexture(); + terrain->_updateBaseTexture(true); + break; + } + } + + return false; +} + bool TerrainBlock::_setLightMapSize( void *obj, const char *index, const char *data ) { TerrainBlock *terrain = static_cast(obj); @@ -1105,6 +1138,10 @@ void TerrainBlock::initPersistFields() &TerrainBlock::_setBaseTexSize, &defaultProtectedGetFn, "Size of base texture size per meter." ); + addProtectedField("baseTexFormat", TYPEID(), Offset(mBaseTexFormat, TerrainBlock), + &TerrainBlock::_setBaseTexFormat, &defaultProtectedGetFn, + ""); + addProtectedField( "lightMapSize", TypeS32, Offset( mLightMapSize, TerrainBlock ), &TerrainBlock::_setLightMapSize, &defaultProtectedGetFn, "Light map dimensions in pixels." ); diff --git a/Engine/source/terrain/terrData.h b/Engine/source/terrain/terrData.h index b3433dfb1..91618df73 100644 --- a/Engine/source/terrain/terrData.h +++ b/Engine/source/terrain/terrData.h @@ -74,6 +74,8 @@ protected: NextFreeMask = Parent::NextFreeMask << 6, }; +public: + enum BaseTexFormat { NONE, DDS, PNG, JPG @@ -94,6 +96,8 @@ protected: } }; +protected: + Box3F mBounds; /// @@ -235,7 +239,8 @@ protected: // Protected fields static bool _setTerrainFile( void *obj, const char *index, const char *data ); static bool _setSquareSize( void *obj, const char *index, const char *data ); - static bool _setBaseTexSize( void *obj, const char *index, const char *data ); + static bool _setBaseTexSize(void *obj, const char *index, const char *data); + static bool _setBaseTexFormat(void *obj, const char *index, const char *data); static bool _setLightMapSize( void *obj, const char *index, const char *data ); public: From 35f88a77b16a4760fd3842c543e58b742c4fd930 Mon Sep 17 00:00:00 2001 From: LukasPJ Date: Thu, 18 Sep 2014 21:49:25 +0200 Subject: [PATCH 197/317] Script integration for Ribbons --- Templates/Empty/game/art/ribbons/materials.cs | 48 +++++++++++ .../Empty/game/art/ribbons/ribbonExec.cs | 23 ++++++ Templates/Empty/game/art/ribbons/ribbons.cs | 44 ++++++++++ .../Empty/game/core/scripts/server/game.cs | 1 + Templates/Empty/game/scripts/server/game.cs | 1 + .../common/ribbons/basicRibbonShaderP.hlsl | 18 ++++ .../common/ribbons/basicRibbonShaderV.hlsl | 34 ++++++++ .../worldEditor/gui/objectBuilderGui.ed.gui | 8 ++ .../worldEditor/scripts/editors/creator.ed.cs | 1 + Templates/Full/game/art/ribbons/materials.cs | 77 ++++++++++++++++++ Templates/Full/game/art/ribbons/ribTex.png | Bin 0 -> 111995 bytes Templates/Full/game/art/ribbons/ribbonExec.cs | 23 ++++++ Templates/Full/game/art/ribbons/ribbons.cs | 63 ++++++++++++++ .../Full/game/core/scripts/server/game.cs | 1 + Templates/Full/game/scripts/server/game.cs | 1 + .../common/ribbons/basicRibbonShaderP.hlsl | 18 ++++ .../common/ribbons/basicRibbonShaderV.hlsl | 34 ++++++++ .../common/ribbons/texRibbonShaderP.hlsl | 20 +++++ .../common/ribbons/texRibbonShaderV.hlsl | 34 ++++++++ .../worldEditor/gui/objectBuilderGui.ed.gui | 8 ++ .../worldEditor/scripts/editors/creator.ed.cs | 1 + 21 files changed, 458 insertions(+) create mode 100644 Templates/Empty/game/art/ribbons/materials.cs create mode 100644 Templates/Empty/game/art/ribbons/ribbonExec.cs create mode 100644 Templates/Empty/game/art/ribbons/ribbons.cs create mode 100644 Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderP.hlsl create mode 100644 Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderV.hlsl create mode 100644 Templates/Full/game/art/ribbons/materials.cs create mode 100644 Templates/Full/game/art/ribbons/ribTex.png create mode 100644 Templates/Full/game/art/ribbons/ribbonExec.cs create mode 100644 Templates/Full/game/art/ribbons/ribbons.cs create mode 100644 Templates/Full/game/shaders/common/ribbons/basicRibbonShaderP.hlsl create mode 100644 Templates/Full/game/shaders/common/ribbons/basicRibbonShaderV.hlsl create mode 100644 Templates/Full/game/shaders/common/ribbons/texRibbonShaderP.hlsl create mode 100644 Templates/Full/game/shaders/common/ribbons/texRibbonShaderV.hlsl diff --git a/Templates/Empty/game/art/ribbons/materials.cs b/Templates/Empty/game/art/ribbons/materials.cs new file mode 100644 index 000000000..a8ee7b19c --- /dev/null +++ b/Templates/Empty/game/art/ribbons/materials.cs @@ -0,0 +1,48 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// This material should work fine for uniformly colored ribbons. + +//Basic ribbon shader///////////////////////////////////////////// + +new ShaderData( basicRibbonShader ) +{ + DXVertexShaderFile = "shaders/common/ribbons/basicRibbonShaderV.hlsl"; + DXPixelShaderFile = "shaders/common/ribbons/basicRibbonShaderP.hlsl"; + + pixVersion = 2.0; +}; + +singleton CustomMaterial( basicRibbonMat ) +{ + shader = basicRibbonShader; + version = 2.0; + + emissive[0] = true; + + doubleSided = true; + translucent = true; + BlendOp = AddAlpha; + translucentBlendOp = AddAlpha; + + preload = true; +}; \ No newline at end of file diff --git a/Templates/Empty/game/art/ribbons/ribbonExec.cs b/Templates/Empty/game/art/ribbons/ribbonExec.cs new file mode 100644 index 000000000..8193b1b8b --- /dev/null +++ b/Templates/Empty/game/art/ribbons/ribbonExec.cs @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +exec("./ribbons.cs"); \ No newline at end of file diff --git a/Templates/Empty/game/art/ribbons/ribbons.cs b/Templates/Empty/game/art/ribbons/ribbons.cs new file mode 100644 index 000000000..ce65e6fe8 --- /dev/null +++ b/Templates/Empty/game/art/ribbons/ribbons.cs @@ -0,0 +1,44 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +datablock RibbonNodeData(DefaultRibbonNodeData) +{ + timeMultiple = 1.0; +}; + +//ribbon data//////////////////////////////////////// + +datablock RibbonData(basicRibbon) +{ + size[0] = 0.5; + color[0] = "1.0 0.0 0.0 1.0"; + position[0] = 0.0; + + size[1] = 0.0; + color[1] = "1.0 0.0 0.0 0.0"; + position[1] = 1.0; + + RibbonLength = 40; + fadeAwayStep = 0.1; + UseFadeOut = true; + RibbonMaterial = basicRibbonMat; +}; \ No newline at end of file diff --git a/Templates/Empty/game/core/scripts/server/game.cs b/Templates/Empty/game/core/scripts/server/game.cs index d80dd4468..c135e6f99 100644 --- a/Templates/Empty/game/core/scripts/server/game.cs +++ b/Templates/Empty/game/core/scripts/server/game.cs @@ -34,6 +34,7 @@ function onServerCreated() // Load up any objects or datablocks saved to the editor managed scripts %datablockFiles = new ArrayObject(); + %datablockFiles.add( "art/ribbons/ribbonExec.cs" ); %datablockFiles.add( "art/particles/managedParticleData.cs" ); %datablockFiles.add( "art/particles/managedParticleEmitterData.cs" ); %datablockFiles.add( "art/decals/managedDecalData.cs" ); diff --git a/Templates/Empty/game/scripts/server/game.cs b/Templates/Empty/game/scripts/server/game.cs index 4826c0de2..d9529ca01 100644 --- a/Templates/Empty/game/scripts/server/game.cs +++ b/Templates/Empty/game/scripts/server/game.cs @@ -144,6 +144,7 @@ function onServerCreated() // Load up any objects or datablocks saved to the editor managed scripts %datablockFiles = new ArrayObject(); + %datablockFiles.add( "art/ribbons/ribbonExec.cs" ); %datablockFiles.add( "art/particles/managedParticleData.cs" ); %datablockFiles.add( "art/particles/managedParticleEmitterData.cs" ); %datablockFiles.add( "art/decals/managedDecalData.cs" ); diff --git a/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderP.hlsl b/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderP.hlsl new file mode 100644 index 000000000..7ce54e3aa --- /dev/null +++ b/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderP.hlsl @@ -0,0 +1,18 @@ +#define IN_HLSL +#include "../common/shdrConsts.h" + +struct v2f +{ + + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float4 color : COLOR0; +}; + +float4 main(v2f IN) : COLOR0 +{ + float fade = 1.0 - abs(IN.shiftdata.y - 0.5) * 2.0; + IN.color.xyz = IN.color.xyz + pow(fade, 4) / 10; + IN.color.a = IN.color.a * fade; + return IN.color; +} \ No newline at end of file diff --git a/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderV.hlsl b/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderV.hlsl new file mode 100644 index 000000000..5fd4ecbc0 --- /dev/null +++ b/Templates/Empty/game/shaders/common/ribbons/basicRibbonShaderV.hlsl @@ -0,0 +1,34 @@ +#define IN_HLSL +#include "../common/shdrConsts.h" + +struct a2v +{ + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float3 normal : NORMAL; + float4 position : POSITION; + float4 color : COLOR0; +}; + +struct v2f +{ + float4 hpos : POSITION; + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float4 color : COLOR0; +}; + +uniform float4x4 modelview; +uniform float3 eyePos; + +v2f main(a2v IN) +{ + v2f OUT; + + OUT.hpos = mul(modelview, IN.position); + OUT.color = IN.color; + OUT.texCoord = IN.texCoord; + OUT.shiftdata = IN.shiftdata; + + return OUT; +} \ No newline at end of file diff --git a/Templates/Empty/game/tools/worldEditor/gui/objectBuilderGui.ed.gui b/Templates/Empty/game/tools/worldEditor/gui/objectBuilderGui.ed.gui index 19c396a57..33cb5de75 100644 --- a/Templates/Empty/game/tools/worldEditor/gui/objectBuilderGui.ed.gui +++ b/Templates/Empty/game/tools/worldEditor/gui/objectBuilderGui.ed.gui @@ -862,6 +862,14 @@ function ObjectBuilderGui::buildParticleEmitterNode(%this) %this.process(); } +function ObjectBuilderGui::buildRibbonNode(%this) +{ + %this.objectClassName = "RibbonNode"; + %this.addField("dataBlock", "TypeDataBlock", "datablock", "RibbonNodeData"); + %this.addField("ribbon", "TypeDataBlock", "Ribbon data", "RibbonData"); + %this.process(); +} + function ObjectBuilderGui::buildParticleSimulation(%this) { %this.objectClassName = "ParticleSimulation"; diff --git a/Templates/Empty/game/tools/worldEditor/scripts/editors/creator.ed.cs b/Templates/Empty/game/tools/worldEditor/scripts/editors/creator.ed.cs index d63542d67..75d41eb53 100644 --- a/Templates/Empty/game/tools/worldEditor/scripts/editors/creator.ed.cs +++ b/Templates/Empty/game/tools/worldEditor/scripts/editors/creator.ed.cs @@ -46,6 +46,7 @@ function EWCreatorWindow::init( %this ) %this.registerMissionObject( "SFXEmitter", "Sound Emitter" ); %this.registerMissionObject( "Precipitation" ); %this.registerMissionObject( "ParticleEmitterNode", "Particle Emitter" ); + %this.registerMissionObject( "RibbonNode", "Ribbon" ); // Legacy features. Users should use Ground Cover and the Forest Editor. //%this.registerMissionObject( "fxShapeReplicator", "Shape Replicator" ); diff --git a/Templates/Full/game/art/ribbons/materials.cs b/Templates/Full/game/art/ribbons/materials.cs new file mode 100644 index 000000000..f9115356f --- /dev/null +++ b/Templates/Full/game/art/ribbons/materials.cs @@ -0,0 +1,77 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +// This material should work fine for uniformly colored ribbons. + +//Basic ribbon shader///////////////////////////////////////////// + +new ShaderData( basicRibbonShader ) +{ + DXVertexShaderFile = "shaders/common/ribbons/basicRibbonShaderV.hlsl"; + DXPixelShaderFile = "shaders/common/ribbons/basicRibbonShaderP.hlsl"; + + pixVersion = 2.0; +}; + +singleton CustomMaterial( basicRibbonMat ) +{ + shader = basicRibbonShader; + version = 2.0; + + emissive[0] = true; + + doubleSided = true; + translucent = true; + BlendOp = AddAlpha; + translucentBlendOp = AddAlpha; + + preload = true; +}; + +// This material can render a texture on top of a ribbon. + +//Texture ribbon shader///////////////////////////////////////////// + +new ShaderData( texRibbonShader ) +{ + DXVertexShaderFile = "shaders/common/ribbons/texRibbonShaderV.hlsl"; + DXPixelShaderFile = "shaders/common/ribbons/texRibbonShaderP.hlsl"; + + pixVersion = 2.0; +}; + +singleton CustomMaterial( texRibbonMat ) +{ + shader = texRibbonShader; + version = 2.0; + + emissive[0] = true; + + doubleSided = true; + translucent = true; + BlendOp = AddAlpha; + translucentBlendOp = AddAlpha; + + sampler["ribTex"] = "art/ribbons/ribTex.png"; + + preload = true; +}; \ No newline at end of file diff --git a/Templates/Full/game/art/ribbons/ribTex.png b/Templates/Full/game/art/ribbons/ribTex.png new file mode 100644 index 0000000000000000000000000000000000000000..82dffb96afab194b0960040ce5e4516dd4eb6c47 GIT binary patch literal 111995 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%0DrD`+a1mc6f)!GG8-qQDAIsWd*fd60e-aN*# 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 DLDPyXcFul~|6ee|{grcZqQcQ5|n`+hVV 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 b3IqNhkO7)w)nL>p00000NkvXXu0mjf36)ff literal 0 HcmV?d00001 diff --git a/Templates/Full/game/art/ribbons/ribbonExec.cs b/Templates/Full/game/art/ribbons/ribbonExec.cs new file mode 100644 index 000000000..8193b1b8b --- /dev/null +++ b/Templates/Full/game/art/ribbons/ribbonExec.cs @@ -0,0 +1,23 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +exec("./ribbons.cs"); \ No newline at end of file diff --git a/Templates/Full/game/art/ribbons/ribbons.cs b/Templates/Full/game/art/ribbons/ribbons.cs new file mode 100644 index 000000000..aef6204e4 --- /dev/null +++ b/Templates/Full/game/art/ribbons/ribbons.cs @@ -0,0 +1,63 @@ +//----------------------------------------------------------------------------- +// Copyright (c) 2012 GarageGames, LLC +// +// Permission is hereby granted, free of charge, to any person obtaining a copy +// of this software and associated documentation files (the "Software"), to +// deal in the Software without restriction, including without limitation the +// rights to use, copy, modify, merge, publish, distribute, sublicense, and/or +// sell copies of the Software, and to permit persons to whom the Software is +// furnished to do so, subject to the following conditions: +// +// The above copyright notice and this permission notice shall be included in +// all copies or substantial portions of the Software. +// +// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +// IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +// FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +// AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +// LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING +// FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS +// IN THE SOFTWARE. +//----------------------------------------------------------------------------- + +datablock RibbonNodeData(DefaultRibbonNodeData) +{ + timeMultiple = 1.0; +}; + +//ribbon data//////////////////////////////////////// + +datablock RibbonData(basicRibbon) +{ + size[0] = 0.5; + color[0] = "1.0 0.0 0.0 1.0"; + position[0] = 0.0; + + size[1] = 0.0; + color[1] = "1.0 0.0 0.0 0.0"; + position[1] = 1.0; + + RibbonLength = 40; + fadeAwayStep = 0.1; + UseFadeOut = true; + RibbonMaterial = basicRibbonMat; +}; + +datablock RibbonData(texRibbon) +{ + RibbonMaterial = texRibbonMat; + size[0] = 0.5; + color[0] = "1.0 1.0 1.0 1.0"; + position[0] = 0.0; + + size[1] = 0.5; + color[1] = "1.0 1.0 1.0 1.0"; + position[1] = 1.0; + + RibbonLength = 40; + fadeAwayStep = 0.1; + UseFadeOut = true; + tileScale = 1; + fixedTexCoords = true; + TexcoordsRelativeToDistance = true; +}; \ No newline at end of file diff --git a/Templates/Full/game/core/scripts/server/game.cs b/Templates/Full/game/core/scripts/server/game.cs index d80dd4468..c135e6f99 100644 --- a/Templates/Full/game/core/scripts/server/game.cs +++ b/Templates/Full/game/core/scripts/server/game.cs @@ -34,6 +34,7 @@ function onServerCreated() // Load up any objects or datablocks saved to the editor managed scripts %datablockFiles = new ArrayObject(); + %datablockFiles.add( "art/ribbons/ribbonExec.cs" ); %datablockFiles.add( "art/particles/managedParticleData.cs" ); %datablockFiles.add( "art/particles/managedParticleEmitterData.cs" ); %datablockFiles.add( "art/decals/managedDecalData.cs" ); diff --git a/Templates/Full/game/scripts/server/game.cs b/Templates/Full/game/scripts/server/game.cs index ace3ac2f8..42d329cd2 100644 --- a/Templates/Full/game/scripts/server/game.cs +++ b/Templates/Full/game/scripts/server/game.cs @@ -50,6 +50,7 @@ function onServerCreated() // Load up any objects or datablocks saved to the editor managed scripts %datablockFiles = new ArrayObject(); + %datablockFiles.add( "art/ribbons/ribbonExec.cs" ); %datablockFiles.add( "art/particles/managedParticleData.cs" ); %datablockFiles.add( "art/particles/managedParticleEmitterData.cs" ); %datablockFiles.add( "art/decals/managedDecalData.cs" ); diff --git a/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderP.hlsl b/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderP.hlsl new file mode 100644 index 000000000..7ce54e3aa --- /dev/null +++ b/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderP.hlsl @@ -0,0 +1,18 @@ +#define IN_HLSL +#include "../common/shdrConsts.h" + +struct v2f +{ + + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float4 color : COLOR0; +}; + +float4 main(v2f IN) : COLOR0 +{ + float fade = 1.0 - abs(IN.shiftdata.y - 0.5) * 2.0; + IN.color.xyz = IN.color.xyz + pow(fade, 4) / 10; + IN.color.a = IN.color.a * fade; + return IN.color; +} \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderV.hlsl b/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderV.hlsl new file mode 100644 index 000000000..5fd4ecbc0 --- /dev/null +++ b/Templates/Full/game/shaders/common/ribbons/basicRibbonShaderV.hlsl @@ -0,0 +1,34 @@ +#define IN_HLSL +#include "../common/shdrConsts.h" + +struct a2v +{ + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float3 normal : NORMAL; + float4 position : POSITION; + float4 color : COLOR0; +}; + +struct v2f +{ + float4 hpos : POSITION; + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float4 color : COLOR0; +}; + +uniform float4x4 modelview; +uniform float3 eyePos; + +v2f main(a2v IN) +{ + v2f OUT; + + OUT.hpos = mul(modelview, IN.position); + OUT.color = IN.color; + OUT.texCoord = IN.texCoord; + OUT.shiftdata = IN.shiftdata; + + return OUT; +} \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/ribbons/texRibbonShaderP.hlsl b/Templates/Full/game/shaders/common/ribbons/texRibbonShaderP.hlsl new file mode 100644 index 000000000..a93c6162b --- /dev/null +++ b/Templates/Full/game/shaders/common/ribbons/texRibbonShaderP.hlsl @@ -0,0 +1,20 @@ +#define IN_HLSL +#include "../common/shdrConsts.h" +#include "shaders/common/torque.hlsl" + +uniform sampler2D ribTex : register(S0); + +struct v2f +{ + + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float4 color : COLOR0; +}; + +float4 main(v2f IN) : COLOR0 +{ + float4 Tex = tex2D(ribTex,IN.texCoord); + Tex.a *= IN.color.a; + return hdrEncode(Tex); +} \ No newline at end of file diff --git a/Templates/Full/game/shaders/common/ribbons/texRibbonShaderV.hlsl b/Templates/Full/game/shaders/common/ribbons/texRibbonShaderV.hlsl new file mode 100644 index 000000000..5fd4ecbc0 --- /dev/null +++ b/Templates/Full/game/shaders/common/ribbons/texRibbonShaderV.hlsl @@ -0,0 +1,34 @@ +#define IN_HLSL +#include "../common/shdrConsts.h" + +struct a2v +{ + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float3 normal : NORMAL; + float4 position : POSITION; + float4 color : COLOR0; +}; + +struct v2f +{ + float4 hpos : POSITION; + float2 texCoord : TEXCOORD0; + float2 shiftdata : TEXCOORD1; + float4 color : COLOR0; +}; + +uniform float4x4 modelview; +uniform float3 eyePos; + +v2f main(a2v IN) +{ + v2f OUT; + + OUT.hpos = mul(modelview, IN.position); + OUT.color = IN.color; + OUT.texCoord = IN.texCoord; + OUT.shiftdata = IN.shiftdata; + + return OUT; +} \ No newline at end of file diff --git a/Templates/Full/game/tools/worldEditor/gui/objectBuilderGui.ed.gui b/Templates/Full/game/tools/worldEditor/gui/objectBuilderGui.ed.gui index 19c396a57..33cb5de75 100644 --- a/Templates/Full/game/tools/worldEditor/gui/objectBuilderGui.ed.gui +++ b/Templates/Full/game/tools/worldEditor/gui/objectBuilderGui.ed.gui @@ -862,6 +862,14 @@ function ObjectBuilderGui::buildParticleEmitterNode(%this) %this.process(); } +function ObjectBuilderGui::buildRibbonNode(%this) +{ + %this.objectClassName = "RibbonNode"; + %this.addField("dataBlock", "TypeDataBlock", "datablock", "RibbonNodeData"); + %this.addField("ribbon", "TypeDataBlock", "Ribbon data", "RibbonData"); + %this.process(); +} + function ObjectBuilderGui::buildParticleSimulation(%this) { %this.objectClassName = "ParticleSimulation"; diff --git a/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs b/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs index d63542d67..75d41eb53 100644 --- a/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs +++ b/Templates/Full/game/tools/worldEditor/scripts/editors/creator.ed.cs @@ -46,6 +46,7 @@ function EWCreatorWindow::init( %this ) %this.registerMissionObject( "SFXEmitter", "Sound Emitter" ); %this.registerMissionObject( "Precipitation" ); %this.registerMissionObject( "ParticleEmitterNode", "Particle Emitter" ); + %this.registerMissionObject( "RibbonNode", "Ribbon" ); // Legacy features. Users should use Ground Cover and the Forest Editor. //%this.registerMissionObject( "fxShapeReplicator", "Shape Replicator" ); From f5fb2fdf763e9b109f2e4a2a8eabdd976b29a622 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Wed, 24 Sep 2014 08:08:42 +1000 Subject: [PATCH 198/317] Improved code style in ribbon files. --- Engine/source/T3D/fx/ribbon.cpp | 44 +++++++++++-------- Engine/source/T3D/fx/ribbon.h | 67 ++++++++++++++++++----------- Engine/source/T3D/fx/ribbonNode.cpp | 2 +- Engine/source/T3D/fx/ribbonNode.h | 5 +-- 4 files changed, 68 insertions(+), 50 deletions(-) diff --git a/Engine/source/T3D/fx/ribbon.cpp b/Engine/source/T3D/fx/ribbon.cpp index d035c3491..49150711e 100644 --- a/Engine/source/T3D/fx/ribbon.cpp +++ b/Engine/source/T3D/fx/ribbon.cpp @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC +// Copyright (c) 2014 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to @@ -47,7 +47,7 @@ IMPLEMENT_CO_NETOBJECT_V1(Ribbon); // RibbonData::RibbonData() { - for (U8 i = 0; i < RIBBON_NUM_FIELDS; i++) { + for (U8 i = 0; i < NumFields; i++) { mSizes[i] = 0.0f; mColours[i].set(0.0f, 0.0f, 0.0f, 1.0f); mTimes[i] = -1.0f; @@ -70,32 +70,38 @@ void RibbonData::initPersistFields() { Parent::initPersistFields(); - addField("size", TypeF32, Offset(mSizes, RibbonData), RIBBON_NUM_FIELDS, + addGroup("Ribbon"); + + addField("size", TypeF32, Offset(mSizes, RibbonData), NumFields, "The size of the ribbon at the specified keyframe."); - addField("color", TypeColorF, Offset(mColours, RibbonData), RIBBON_NUM_FIELDS, + addField("color", TypeColorF, Offset(mColours, RibbonData), NumFields, "The colour of the ribbon at the specified keyframe."); - addField("position", TypeF32, Offset(mTimes, RibbonData), RIBBON_NUM_FIELDS, + addField("position", TypeF32, Offset(mTimes, RibbonData), NumFields, "The position of the keyframe along the lifetime of the ribbon."); - addField("RibbonLength", TypeS32, Offset(mRibbonLength, RibbonData), + + addField("ribbonLength", TypeS32, Offset(mRibbonLength, RibbonData), "The amount of segments the Ribbon can maximally have in length."); - addField("UseFadeOut", TypeBool, Offset(mUseFadeOut, RibbonData), - "If true, the ribbon will fade away after deletion."); - addField("RibbonMaterial", TypeString, Offset(mMatName, RibbonData), - "The material the ribbon uses for rendering."); - addField("fadeAwayStep", TypeF32, Offset(mFadeAwayStep, RibbonData), - "How much to fade the ribbon with each update, after deletion."); addField("segmentsPerUpdate", TypeS32, Offset(segmentsPerUpdate, RibbonData), "How many segments to add each update."); - addField("tileScale", TypeF32, Offset(mTileScale, RibbonData), + addField("skipAmount", TypeS32, Offset(mSegmentSkipAmount, RibbonData), + "The amount of segments to skip each update."); + + addField("useFadeOut", TypeBool, Offset(mUseFadeOut, RibbonData), + "If true, the ribbon will fade away after deletion."); + addField("fadeAwayStep", TypeF32, Offset(mFadeAwayStep, RibbonData), + "How much to fade the ribbon with each update, after deletion."); + addField("ribbonMaterial", TypeString, Offset(mMatName, RibbonData), + "The material the ribbon uses for rendering."); + addField("tileScale", TypeF32, Offset(mTileScale, RibbonData), "How much to scale each 'tile' with, where 1 means the material is stretched" "across the whole ribbon. (If TexcoordsRelativeToDistance is true, this is in meters.)"); addField("fixedTexcoords", TypeBool, Offset(mFixedTexcoords, RibbonData), "If true, this prevents 'floating' texture coordinates."); - addField("skipAmount", TypeS32, Offset(mSegmentSkipAmount, RibbonData), - "The amount of segments to skip each update."); - addField("TexcoordsRelativeToDistance", TypeBool, Offset(mTexcoordsRelativeToDistance, RibbonData), + addField("texcoordsRelativeToDistance", TypeBool, Offset(mTexcoordsRelativeToDistance, RibbonData), "If true, texture coordinates are scaled relative to distance, this prevents" "'stretched' textures."); + + endGroup("Ribbon"); } @@ -122,7 +128,7 @@ void RibbonData::packData(BitStream* stream) { Parent::packData(stream); - for (U8 i = 0; i < RIBBON_NUM_FIELDS; i++) { + for (U8 i = 0; i < NumFields; i++) { stream->write(mSizes[i]); stream->write(mColours[i]); stream->write(mTimes[i]); @@ -142,7 +148,7 @@ void RibbonData::unpackData(BitStream* stream) { Parent::unpackData(stream); - for (U8 i = 0; i < RIBBON_NUM_FIELDS; i++) { + for (U8 i = 0; i < NumFields; i++) { stream->read(&mSizes[i]); stream->read(&mColours[i]); stream->read(&mTimes[i]); @@ -558,7 +564,7 @@ void Ribbon::createBuffers(SceneRenderState *state, GFXVertexBufferHandlemSizes[0]; ColorF tColor = mDataBlock->mColours[0]; - for (U8 j = 0; j < RIBBON_NUM_FIELDS-1; j++) { + for (U8 j = 0; j < RibbonData::NumFields-1; j++) { F32 curPosition = mDataBlock->mTimes[j]; F32 curRadius = mDataBlock->mSizes[j]; diff --git a/Engine/source/T3D/fx/ribbon.h b/Engine/source/T3D/fx/ribbon.h index ce115d461..10ca8d40b 100644 --- a/Engine/source/T3D/fx/ribbon.h +++ b/Engine/source/T3D/fx/ribbon.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC +// Copyright (c) 2014 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to @@ -38,8 +38,6 @@ #include "materials/materialParameters.h" #include "math/util/matrixSet.h" -#define RIBBON_NUM_FIELDS 4 - //-------------------------------------------------------------------------- class RibbonData : public GameBaseData { @@ -50,19 +48,26 @@ protected: public: - U32 mRibbonLength; ///< The amount of segments that will make up the ribbon. - F32 mSizes[RIBBON_NUM_FIELDS]; ///< The radius for each keyframe. - ColorF mColours[RIBBON_NUM_FIELDS]; ///< The colour of the ribbon for each keyframe. - F32 mTimes[RIBBON_NUM_FIELDS]; ///< The relative time for each keyframe. - StringTableEntry mMatName; ///< The material for the ribbon. - bool mUseFadeOut; ///< If true, the ribbon will fade away after deletion. - F32 mFadeAwayStep; ///< How quickly the ribbons is faded away after deletion. - S32 segmentsPerUpdate; ///< Amount of segments to add each update. - F32 mTileScale; ///< A scalar to scale the texcoord. - bool mFixedTexcoords; ///< If true, texcoords will stay the same over the lifetime for each segment. - bool mTexcoordsRelativeToDistance; ///< If true, texcoords will not be stretched if the distance between 2 segments are long. + enum Constants + { + NumFields = 4 + }; + + F32 mSizes[NumFields]; ///< The radius for each keyframe. + ColorF mColours[NumFields]; ///< The colour of the ribbon for each keyframe. + F32 mTimes[NumFields]; ///< The relative time for each keyframe. + + U32 mRibbonLength; ///< The amount of segments that will make up the ribbon. + S32 segmentsPerUpdate; ///< Amount of segments to add each update. S32 mSegmentSkipAmount; ///< The amount of segments to skip each time segments are added. + bool mUseFadeOut; ///< If true, the ribbon will fade away after deletion. + F32 mFadeAwayStep; ///< How quickly the ribbons is faded away after deletion. + StringTableEntry mMatName; ///< The material for the ribbon. + F32 mTileScale; ///< A scalar to scale the texcoord. + bool mFixedTexcoords; ///< If true, texcoords will stay the same over the lifetime for each segment. + bool mTexcoordsRelativeToDistance; ///< If true, texcoords will not be stretched if the distance between 2 segments are long. + RibbonData(); void packData(BitStream*); @@ -77,21 +82,25 @@ public: class Ribbon : public GameBase { typedef GameBase Parent; + RibbonData* mDataBlock; + + bool mDeleteOnEnd; ///< If true, the ribbon should delete itself as soon as the last segment is deleted + bool mUseFadeOut; ///< If true, the ribbon will fade away upon deletion + F32 mFadeAwayStep; ///< How quickly the ribbons is faded away after deletion. + F32 mFadeOut; + F32 mTravelledDistance; ///< How far the ribbon has travelled in it's lifetime. + Vector mSegmentPoints; ///< The points in space where the ribbon has spawned segments. + U32 mSegmentOffset; + U32 mSegmentIdx; + + bool mUpdateBuffers; ///< If true, the vertex buffers need to be updated. BaseMatInstance *mRibbonMat; MaterialParameterHandle* mRadiusSC; MaterialParameterHandle* mRibbonProjSC; GFXPrimitiveBufferHandle primBuffer; GFXVertexBufferHandle verts; - bool mUpdateBuffers; ///< If true, the vertex buffers need to be updated. - bool mDeleteOnEnd; ///< If true, the ribbon should delete itself as soon as the last segment is deleted - bool mUseFadeOut; ///< If true, the ribbon will fade away upon deletion - F32 mFadeAwayStep; ///< How quickly the ribbons is faded away after deletion. - F32 mFadeOut; - U32 mSegmentOffset; - U32 mSegmentIdx; - F32 mTravelledDistance; ///< How far the ribbon has travelled in it's lifetime. protected: @@ -102,10 +111,11 @@ protected: // Rendering void prepRenderImage(SceneRenderState *state); + void setShaderParams(); ///Checks to see if ribbon is too long U32 checkRibbonDistance(S32 segments); - void setShaderParams(); + /// Construct the vertex and primitive buffers void createBuffers(SceneRenderState *state, GFXVertexBufferHandle &verts, GFXPrimitiveBufferHandle &pb, U32 segments); @@ -116,11 +126,16 @@ public: DECLARE_CONOBJECT(Ribbon); static void initPersistFields(); bool onNewDataBlock(GameBaseData*,bool); - void addSegmentPoint(Point3F &point, MatrixF &mat); ///< Used to add another segment to the ribbon. - void clearSegments() { mSegmentPoints.clear(); } ///< Delete all segments. - void deleteOnEnd(); ///< Delete the ribbon when all segments have been deleted. void onRemove(); + /// Used to add another segment to the ribbon. + void addSegmentPoint(Point3F &point, MatrixF &mat); + + /// Delete all segments. + void clearSegments() { mSegmentPoints.clear(); } + + /// Delete the ribbon when all segments have been deleted. + void deleteOnEnd(); }; #endif // _H_RIBBON diff --git a/Engine/source/T3D/fx/ribbonNode.cpp b/Engine/source/T3D/fx/ribbonNode.cpp index 0343fc5b3..2582fbe6f 100644 --- a/Engine/source/T3D/fx/ribbonNode.cpp +++ b/Engine/source/T3D/fx/ribbonNode.cpp @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC +// Copyright (c) 2014 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to diff --git a/Engine/source/T3D/fx/ribbonNode.h b/Engine/source/T3D/fx/ribbonNode.h index 62b1158e4..cdd86ef33 100644 --- a/Engine/source/T3D/fx/ribbonNode.h +++ b/Engine/source/T3D/fx/ribbonNode.h @@ -1,5 +1,5 @@ //----------------------------------------------------------------------------- -// Copyright (c) 2012 GarageGames, LLC +// Copyright (c) 2014 GarageGames, LLC // // Permission is hereby granted, free of charge, to any person obtaining a copy // of this software and associated documentation files (the "Software"), to @@ -37,12 +37,9 @@ class RibbonNodeData : public GameBaseData { typedef GameBaseData Parent; - //-------------------------------------- Console set variables public: F32 timeMultiple; - //-------------------------------------- load set variables - public: RibbonNodeData(); ~RibbonNodeData(); From 2a9e6ef5e62c27b48852fe27e2ce91341b3876e4 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Wed, 24 Sep 2014 08:27:03 +1000 Subject: [PATCH 199/317] Renamed default ribbon datablocks. --- Templates/Empty/game/art/ribbons/materials.cs | 6 +++--- Templates/Empty/game/art/ribbons/ribbons.cs | 6 +++--- Templates/Full/game/art/ribbons/materials.cs | 16 ++++++++-------- Templates/Full/game/art/ribbons/ribbons.cs | 8 ++++---- 4 files changed, 18 insertions(+), 18 deletions(-) diff --git a/Templates/Empty/game/art/ribbons/materials.cs b/Templates/Empty/game/art/ribbons/materials.cs index a8ee7b19c..d53ada9e2 100644 --- a/Templates/Empty/game/art/ribbons/materials.cs +++ b/Templates/Empty/game/art/ribbons/materials.cs @@ -24,7 +24,7 @@ //Basic ribbon shader///////////////////////////////////////////// -new ShaderData( basicRibbonShader ) +new ShaderData( BasicRibbonShader ) { DXVertexShaderFile = "shaders/common/ribbons/basicRibbonShaderV.hlsl"; DXPixelShaderFile = "shaders/common/ribbons/basicRibbonShaderP.hlsl"; @@ -32,9 +32,9 @@ new ShaderData( basicRibbonShader ) pixVersion = 2.0; }; -singleton CustomMaterial( basicRibbonMat ) +singleton CustomMaterial( BasicRibbonMat ) { - shader = basicRibbonShader; + shader = BasicRibbonShader; version = 2.0; emissive[0] = true; diff --git a/Templates/Empty/game/art/ribbons/ribbons.cs b/Templates/Empty/game/art/ribbons/ribbons.cs index ce65e6fe8..b2184b74c 100644 --- a/Templates/Empty/game/art/ribbons/ribbons.cs +++ b/Templates/Empty/game/art/ribbons/ribbons.cs @@ -27,7 +27,7 @@ datablock RibbonNodeData(DefaultRibbonNodeData) //ribbon data//////////////////////////////////////// -datablock RibbonData(basicRibbon) +datablock RibbonData(BasicRibbon) { size[0] = 0.5; color[0] = "1.0 0.0 0.0 1.0"; @@ -40,5 +40,5 @@ datablock RibbonData(basicRibbon) RibbonLength = 40; fadeAwayStep = 0.1; UseFadeOut = true; - RibbonMaterial = basicRibbonMat; -}; \ No newline at end of file + RibbonMaterial = BasicRibbonMat; +}; diff --git a/Templates/Full/game/art/ribbons/materials.cs b/Templates/Full/game/art/ribbons/materials.cs index f9115356f..dce3e9cac 100644 --- a/Templates/Full/game/art/ribbons/materials.cs +++ b/Templates/Full/game/art/ribbons/materials.cs @@ -24,7 +24,7 @@ //Basic ribbon shader///////////////////////////////////////////// -new ShaderData( basicRibbonShader ) +new ShaderData( BasicRibbonShader ) { DXVertexShaderFile = "shaders/common/ribbons/basicRibbonShaderV.hlsl"; DXPixelShaderFile = "shaders/common/ribbons/basicRibbonShaderP.hlsl"; @@ -32,9 +32,9 @@ new ShaderData( basicRibbonShader ) pixVersion = 2.0; }; -singleton CustomMaterial( basicRibbonMat ) +singleton CustomMaterial( BasicRibbonMat ) { - shader = basicRibbonShader; + shader = BasicRibbonShader; version = 2.0; emissive[0] = true; @@ -51,17 +51,17 @@ singleton CustomMaterial( basicRibbonMat ) //Texture ribbon shader///////////////////////////////////////////// -new ShaderData( texRibbonShader ) +new ShaderData( TexturedRibbonShader ) { - DXVertexShaderFile = "shaders/common/ribbons/texRibbonShaderV.hlsl"; - DXPixelShaderFile = "shaders/common/ribbons/texRibbonShaderP.hlsl"; + DXVertexShaderFile = "shaders/common/ribbons/TexturedRibbonShaderV.hlsl"; + DXPixelShaderFile = "shaders/common/ribbons/TexturedRibbonShaderP.hlsl"; pixVersion = 2.0; }; -singleton CustomMaterial( texRibbonMat ) +singleton CustomMaterial( TexturedRibbonMat ) { - shader = texRibbonShader; + shader = TexturedRibbonShader; version = 2.0; emissive[0] = true; diff --git a/Templates/Full/game/art/ribbons/ribbons.cs b/Templates/Full/game/art/ribbons/ribbons.cs index aef6204e4..8660ec4d2 100644 --- a/Templates/Full/game/art/ribbons/ribbons.cs +++ b/Templates/Full/game/art/ribbons/ribbons.cs @@ -27,7 +27,7 @@ datablock RibbonNodeData(DefaultRibbonNodeData) //ribbon data//////////////////////////////////////// -datablock RibbonData(basicRibbon) +datablock RibbonData(BasicRibbon) { size[0] = 0.5; color[0] = "1.0 0.0 0.0 1.0"; @@ -40,12 +40,12 @@ datablock RibbonData(basicRibbon) RibbonLength = 40; fadeAwayStep = 0.1; UseFadeOut = true; - RibbonMaterial = basicRibbonMat; + RibbonMaterial = BasicRibbonMat; }; -datablock RibbonData(texRibbon) +datablock RibbonData(TexturedRibbon) { - RibbonMaterial = texRibbonMat; + RibbonMaterial = TexturedRibbonMat; size[0] = 0.5; color[0] = "1.0 1.0 1.0 1.0"; position[0] = 0.0; From 9f47032522224419b7672be59d4ac5879fbebb00 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 21 Sep 2014 18:27:31 +1000 Subject: [PATCH 200/317] Allow return status to be specified using quitWithStatus. --- Engine/source/app/mainLoop.cpp | 5 +++++ Engine/source/app/mainLoop.h | 3 +++ Engine/source/cinterface/cinterface.cpp | 5 +++++ Engine/source/console/consoleFunctions.cpp | 20 ++++++++++++++++--- Engine/source/core/util/journal/process.cpp | 10 ++++++++-- Engine/source/core/util/journal/process.h | 6 +++++- Engine/source/main/main.cpp | 2 +- Engine/source/platform/platform.h | 2 +- .../platformWin32/winProcessControl.cpp | 2 +- Engine/source/platformWin32/winWindow.cpp | 3 ++- Engine/source/platformX86UNIX/x86UNIXMain.cpp | 3 ++- .../platformX86UNIX/x86UNIXProcessControl.cpp | 7 +------ .../windowManager/win32/winDispatch.cpp | 2 +- 13 files changed, 52 insertions(+), 18 deletions(-) diff --git a/Engine/source/app/mainLoop.cpp b/Engine/source/app/mainLoop.cpp index f89b4c435..2819d153d 100644 --- a/Engine/source/app/mainLoop.cpp +++ b/Engine/source/app/mainLoop.cpp @@ -623,6 +623,11 @@ bool StandardMainLoop::doMainLoop() return keepRunning; } +S32 StandardMainLoop::getReturnStatus() +{ + return Process::getReturnStatus(); +} + void StandardMainLoop::setRestart(bool restart ) { gRequiresRestart = restart; diff --git a/Engine/source/app/mainLoop.h b/Engine/source/app/mainLoop.h index 4c2b186bd..b7baad503 100644 --- a/Engine/source/app/mainLoop.h +++ b/Engine/source/app/mainLoop.h @@ -41,6 +41,9 @@ public: /// Shut down the core libraries and call registered shutdown fucntions. static void shutdown(); + /// Gets the return status code of the current process. + static S32 getReturnStatus(); + static void setRestart( bool restart ); static bool requiresRestart(); diff --git a/Engine/source/cinterface/cinterface.cpp b/Engine/source/cinterface/cinterface.cpp index 2d9a729d3..f87ffaea7 100644 --- a/Engine/source/cinterface/cinterface.cpp +++ b/Engine/source/cinterface/cinterface.cpp @@ -132,6 +132,11 @@ extern "C" { } + S32 torque_getreturnstatus() + { + return StandardMainLoop::getReturnStatus(); + } + // signal an engine shutdown (as with the quit(); console command) void torque_enginesignalshutdown() { diff --git a/Engine/source/console/consoleFunctions.cpp b/Engine/source/console/consoleFunctions.cpp index 2dea93f44..52ff68a00 100644 --- a/Engine/source/console/consoleFunctions.cpp +++ b/Engine/source/console/consoleFunctions.cpp @@ -1517,11 +1517,12 @@ ConsoleFunction( realQuit, void, 1, 1, "" ) //----------------------------------------------------------------------------- -DefineConsoleFunction( quitWithErrorMessage, void, ( const char* message ),, +DefineConsoleFunction( quitWithErrorMessage, void, ( const char* message, S32 status ), (0), "Display an error message box showing the given @a message and then shut down the engine and exit its process.\n" "This function cleanly uninitialized the engine and then exits back to the system with a process " "exit status indicating an error.\n\n" - "@param message The message to log to the console and show in an error message box.\n\n" + "@param message The message to log to the console and show in an error message box.\n" + "@param status The status code to return to the OS.\n\n" "@see quit\n\n" "@ingroup Platform" ) { @@ -1532,7 +1533,20 @@ DefineConsoleFunction( quitWithErrorMessage, void, ( const char* message ),, // as the script code should not be allowed to pretty much hard-crash the engine // and prevent proper shutdown. Changed this to use postQuitMessage. - Platform::postQuitMessage( -1 ); + Platform::postQuitMessage( status ); +} + +//----------------------------------------------------------------------------- + +DefineConsoleFunction( quitWithStatus, void, ( S32 status ), (0), + "Shut down the engine and exit its process.\n" + "This function cleanly uninitializes the engine and then exits back to the system with a given " + "return status code.\n\n" + "@param status The status code to return to the OS.\n\n" + "@see quitWithErrorMessage\n\n" + "@ingroup Platform" ) +{ + Platform::postQuitMessage(status); } //----------------------------------------------------------------------------- diff --git a/Engine/source/core/util/journal/process.cpp b/Engine/source/core/util/journal/process.cpp index a2520337d..3911cd0a1 100644 --- a/Engine/source/core/util/journal/process.cpp +++ b/Engine/source/core/util/journal/process.cpp @@ -43,15 +43,21 @@ static Process* _theOneProcess = NULL; ///< the one instance of the Process clas //----------------------------------------------------------------------------- -void Process::requestShutdown() +void Process::requestShutdown(S32 status) { Process::get()._RequestShutdown = true; + Process::get()._ReturnStatus = status; +} + +S32 Process::getReturnStatus() +{ + return Process::get()._ReturnStatus; } //----------------------------------------------------------------------------- Process::Process() -: _RequestShutdown( false ) +: _RequestShutdown( false ), _ReturnStatus( 0 ) { } diff --git a/Engine/source/core/util/journal/process.h b/Engine/source/core/util/journal/process.h index 4d21898d9..d5633558c 100644 --- a/Engine/source/core/util/journal/process.h +++ b/Engine/source/core/util/journal/process.h @@ -64,7 +64,7 @@ public: static bool processEvents(); /// Ask the processEvents() function to shutdown. - static void requestShutdown(); + static void requestShutdown(S32 status = 0); static void notifyInit(Delegate del, F32 order = PROCESS_DEFAULT_ORDER) @@ -149,6 +149,9 @@ public: /// Trigger the registered shutdown functions static bool shutdown(); + /// get the current return status code we've been asked to end with. + static S32 getReturnStatus(); + private: friend class StandardMainLoop; @@ -167,6 +170,7 @@ private: Signal _signalShutdown; bool _RequestShutdown; + S32 _ReturnStatus; }; /// Register a command line handling function. diff --git a/Engine/source/main/main.cpp b/Engine/source/main/main.cpp index 1638b5b33..9728f37ec 100644 --- a/Engine/source/main/main.cpp +++ b/Engine/source/main/main.cpp @@ -321,7 +321,7 @@ S32 TorqueMain(S32 argc, const char **argv) Platform::restartInstance(); // Return. - return 0; + return StandardMainLoop::getReturnStatus(); } #endif //TORQUE_SHARED diff --git a/Engine/source/platform/platform.h b/Engine/source/platform/platform.h index d390eef5c..81f6165c1 100644 --- a/Engine/source/platform/platform.h +++ b/Engine/source/platform/platform.h @@ -204,7 +204,7 @@ namespace Platform bool excludeOtherInstances(const char *string); bool checkOtherInstances(const char *string); void restartInstance(); - void postQuitMessage(const U32 in_quitVal); + void postQuitMessage(const S32 in_quitVal); void forceShutdown(S32 returnValue); // Debug diff --git a/Engine/source/platformWin32/winProcessControl.cpp b/Engine/source/platformWin32/winProcessControl.cpp index f20905428..69cab8ad8 100644 --- a/Engine/source/platformWin32/winProcessControl.cpp +++ b/Engine/source/platformWin32/winProcessControl.cpp @@ -23,7 +23,7 @@ #include "platformWin32/platformWin32.h" #include "core/strings/stringFunctions.h" -void Platform::postQuitMessage(const U32 in_quitVal) +void Platform::postQuitMessage(const S32 in_quitVal) { if (!Platform::getWebDeployment()) PostQuitMessage(in_quitVal); diff --git a/Engine/source/platformWin32/winWindow.cpp b/Engine/source/platformWin32/winWindow.cpp index 5c2bac019..75df7d431 100644 --- a/Engine/source/platformWin32/winWindow.cpp +++ b/Engine/source/platformWin32/winWindow.cpp @@ -375,6 +375,7 @@ extern "C" { bool torque_engineinit(S32 argc, const char **argv); S32 torque_enginetick(); + S32 torque_getreturnstatus(); bool torque_engineshutdown(); }; @@ -390,7 +391,7 @@ S32 TorqueMain(int argc, const char **argv) torque_engineshutdown(); - return 0; + return torque_getreturnstatus(); } diff --git a/Engine/source/platformX86UNIX/x86UNIXMain.cpp b/Engine/source/platformX86UNIX/x86UNIXMain.cpp index 4ffb262c9..e0f252e0d 100644 --- a/Engine/source/platformX86UNIX/x86UNIXMain.cpp +++ b/Engine/source/platformX86UNIX/x86UNIXMain.cpp @@ -121,6 +121,7 @@ extern "C" { bool torque_engineinit(int argc, const char **argv); int torque_enginetick(); + S32 torque_getreturnstatus(); bool torque_engineshutdown(); int torque_unixmain(int argc, const char **argv) @@ -135,7 +136,7 @@ extern "C" torque_engineshutdown(); - return 0; + return torque_getreturnstatus(); } } diff --git a/Engine/source/platformX86UNIX/x86UNIXProcessControl.cpp b/Engine/source/platformX86UNIX/x86UNIXProcessControl.cpp index 1930af2b5..ed0b2aac6 100644 --- a/Engine/source/platformX86UNIX/x86UNIXProcessControl.cpp +++ b/Engine/source/platformX86UNIX/x86UNIXProcessControl.cpp @@ -37,9 +37,6 @@ //----------------------------------------------------------------------------- // This is a mainly a debugging function for intercepting a nonzero exit code // and generating a core dump for a stack trace. -// Need an S64 here because postQuitMessage uses a U32, and -// forceshutdown uses an S32. So S64 is needed to -// accomodate them both static void CheckExitCode(S64 exitCode) { if (exitCode != 0) @@ -141,7 +138,7 @@ void ProcessControlInit() } //----------------------------------------------------------------------------- -void Platform::postQuitMessage(const U32 in_quitVal) +void Platform::postQuitMessage(const S32 in_quitVal) { // if we have a window send a quit event, otherwise just force shutdown #if 0 @@ -171,8 +168,6 @@ void Platform::debugBreak() //----------------------------------------------------------------------------- void Platform::forceShutdown(S32 returnValue) { - CheckExitCode(returnValue); - #if 0 // if a dedicated server is running, turn it off if (x86UNIXState->isDedicated() && Game->isRunning()) diff --git a/Engine/source/windowManager/win32/winDispatch.cpp b/Engine/source/windowManager/win32/winDispatch.cpp index 7038d0a29..15ebd4048 100644 --- a/Engine/source/windowManager/win32/winDispatch.cpp +++ b/Engine/source/windowManager/win32/winDispatch.cpp @@ -450,7 +450,7 @@ static bool _dispatch(HWND hWnd,UINT message,WPARAM wParam,WPARAM lParam) // Quit indicates that we're not going to receive anymore Win32 messages. // Therefore, it's appropriate to flag our event loop for exit as well, // since we won't be getting any more messages. - Process::requestShutdown(); + Process::requestShutdown((S32)wParam); break; } From f678a19be84fbf1126f06c36d5fdea3a4930a5d5 Mon Sep 17 00:00:00 2001 From: Azaezel Date: Tue, 23 Sep 2014 19:15:43 -0500 Subject: [PATCH 201/317] Testing using the regenerating turret https://github.com/Azaezel/Torque3D/tree/outpost_testbed_clean shows that tracking is precisely inverted using https://github.com/GarageGames/Torque3D/commit/8b1ff267f09381e51f851af0373588a004e8a376#diff-983ceaf4b3cbddd4701734aaa048a79e . reversion PR till someone has time to properly review whether that's fouled conversion on my part, or if turrets were accounting for bad math to start with. --- Engine/source/math/mQuat.cpp | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/Engine/source/math/mQuat.cpp b/Engine/source/math/mQuat.cpp index 7ffc4eaa3..c78252bc2 100644 --- a/Engine/source/math/mQuat.cpp +++ b/Engine/source/math/mQuat.cpp @@ -35,27 +35,27 @@ QuatF& QuatF::set( const EulerF & e ) F32 cx, sx; F32 cy, sy; F32 cz, sz; - mSinCos( e.x * 0.5f, sx, cx ); - mSinCos( e.y * 0.5f, sy, cy ); - mSinCos( e.z * 0.5f, sz, cz ); + mSinCos( -e.x * 0.5f, sx, cx ); + mSinCos( -e.y * 0.5f, sy, cy ); + mSinCos( -e.z * 0.5f, sz, cz ); - // Qyaw(z) = [ (0, 0, sin z/2), cos z/2 ] + // Qyaw(z) = [ (0, 0, sin z/2), cos z/2 ] // Qpitch(x) = [ (sin x/2, 0, 0), cos x/2 ] - // Qroll(y) = [ (0, sin y/2, 0), cos y/2 ] - // this = Qresult = Qyaw*Qpitch*Qroll ZXY + // Qroll(y) = [ (0, sin y/2, 0), cos y/2 ] + // this = Qresult = Qyaw*Qpitch*Qroll ZXY // // The code that folows is a simplification of: - // roll*=pitch; - // roll*=yaw; - // *this = roll; + // roll*=pitch; + // roll*=yaw; + // *this = roll; F32 cycz, sysz, sycz, cysz; cycz = cy*cz; sysz = sy*sz; sycz = sy*cz; cysz = cy*sz; - w = cycz*cx - sysz*sx; + w = cycz*cx + sysz*sx; x = cycz*sx + sysz*cx; - y = sycz*cx + cysz*sx; + y = sycz*cx - cysz*sx; z = cysz*cx - sycz*sx; return *this; From df37afac34b83c27514da925c89ce1fe89546614 Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Fri, 26 Sep 2014 00:10:14 +0200 Subject: [PATCH 202/317] Fix Torque 64 define. --- Engine/source/console/compiler.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Engine/source/console/compiler.cpp b/Engine/source/console/compiler.cpp index ca1282ca6..49125389f 100644 --- a/Engine/source/console/compiler.cpp +++ b/Engine/source/console/compiler.cpp @@ -65,8 +65,8 @@ namespace Compiler void evalSTEtoCode(StringTableEntry ste, U32 ip, U32 *ptr) { -#ifdef TORQUE_64 - *((U64*)(ptr) = (U64)ste; +#ifdef TORQUE_CPU_X64 + *(U64*)(ptr) = (U64)ste; #else *ptr = (U32)ste; #endif From 100823605360b4f2920de7fec29f5fcfa7b8d597 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Fri, 26 Sep 2014 13:11:29 +1000 Subject: [PATCH 203/317] Bump version numbers. --- Engine/source/app/version.h | 4 ++-- README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/Engine/source/app/version.h b/Engine/source/app/version.h index 03051c512..ab035793d 100644 --- a/Engine/source/app/version.h +++ b/Engine/source/app/version.h @@ -41,10 +41,10 @@ /// code version, the game name, and which type of game it is (TGB, TGE, TGEA, etc.). /// /// Version number is major * 1000 + minor * 100 + revision * 10. -#define TORQUE_GAME_ENGINE 3501 +#define TORQUE_GAME_ENGINE 3600 /// Human readable engine version string. -#define TORQUE_GAME_ENGINE_VERSION_STRING "3.5.1" +#define TORQUE_GAME_ENGINE_VERSION_STRING "3.6" /// Gets the engine version number. The version number is specified as a global in version.cc U32 getVersionNumber(); diff --git a/README.md b/README.md index a461b5db0..412e858ff 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -Torque 3D v3.5.1 +Torque 3D v3.6 ================ MIT Licensed Open Source version of [Torque 3D](http://www.garagegames.com/products/torque-3d) from [GarageGames](http://www.garagegames.com) @@ -21,7 +21,7 @@ Pre-compiled Version In addition to GitHub we also have a couple of pre-packaged files for you to download if you would prefer to not compile the code yourself: -* [Complete Torque 3D 3.5.1 zip package](http://mit.garagegames.com/Torque3D-3-5-1.zip) with updated TorqueScript documentation, the *Project Manager*, and compiled versions of the templates. +* [Complete Torque 3D 3.6 zip package](http://mit.garagegames.com/Torque3D-3-6.zip) with updated TorqueScript documentation, the *Project Manager*, and compiled versions of the templates. * [Torque 3D Project Manager v2.1](http://mit.garagegames.com/T3DProjectManager-2-1.zip) on its own for use in your T3D forks. If you're looking for an older release see the [Torque 3D Archive](https://github.com/GarageGames/Torque3D/wiki/Torque-3D-Archive) From b18bcfb709ff23d2a581e74e11178f55af02ca5e Mon Sep 17 00:00:00 2001 From: LuisAntonRebollo Date: Sun, 28 Sep 2014 20:27:29 +0200 Subject: [PATCH 204/317] Fix compiler warnings with CMAKE and TORQUE_DISABLE_MEMORY_MANAGER. --- Tools/CMake/torque3d.cmake | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 7f4ead92d..2fb9b3191 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -339,10 +339,6 @@ if(TORQUE_HYDRA) include( "modules/module_hydra.cmake" ) endif() -if(TORQUE_DISABLE_MEMORY_MANAGER) - addDef(TORQUE_DISABLE_MEMORY_MANAGER) -endif() - if(TORQUE_DEDICATED) addDef(TORQUE_DEDICATED) endif() @@ -456,11 +452,10 @@ finishExecutable() ############################################################################### ############################################################################### +message(STATUS "writing ${projectSrcDir}/torqueConfig.h") +CONFIGURE_FILE("${cmakeDir}/torqueConfig.h.in" "${projectSrcDir}/torqueConfig.h") + # configure the relevant files only once -if(NOT EXISTS "${projectSrcDir}/torqueConfig.h") - message(STATUS "writing ${projectSrcDir}/torqueConfig.h") - CONFIGURE_FILE("${cmakeDir}/torqueConfig.h.in" "${projectSrcDir}/torqueConfig.h") -endif() if(NOT EXISTS "${projectSrcDir}/torque.ico") CONFIGURE_FILE("${cmakeDir}/torque.ico" "${projectSrcDir}/torque.ico" COPYONLY) endif() From ff67ac3e80a6f0838044bfe4b9fbfa12923935ed Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 28 Sep 2014 16:08:26 +1000 Subject: [PATCH 205/317] Fixed some spacing. --- .../source/component/test/simComponentTest.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/Engine/source/component/test/simComponentTest.cpp b/Engine/source/component/test/simComponentTest.cpp index d7d135a08..b4c8b1509 100644 --- a/Engine/source/component/test/simComponentTest.cpp +++ b/Engine/source/component/test/simComponentTest.cpp @@ -91,9 +91,9 @@ public: << "Pointer to dependent interface is NULL"; if( mpU32 ) { - EXPECT_TRUE( *(*mpU32) & ( 1 << 24 )) + EXPECT_TRUE( *(*mpU32) & ( 1 << 24 ) ) << "Pointer to interface data is bogus."; - EXPECT_TRUE( *(*mpU32) != *mMyId) + EXPECT_TRUE( *(*mpU32) != *mMyId ) << "Two of me have the same ID, bad!"; } } @@ -114,20 +114,20 @@ TEST(SimComponent, Composition) CachedInterfaceExampleComponent *componentB = new CachedInterfaceExampleComponent(); // Register sub-components - EXPECT_TRUE( componentA->registerObject()) + EXPECT_TRUE( componentA->registerObject() ) << "Failed to register componentA"; - EXPECT_TRUE( componentB->registerObject()) + EXPECT_TRUE( componentB->registerObject() ) << "Failed to register componentB"; // Add the components - EXPECT_TRUE( testComponent->addComponent( componentA )) + EXPECT_TRUE( testComponent->addComponent( componentA ) ) << "Failed to add component a to testComponent"; - EXPECT_TRUE( testComponent->addComponent( componentB )) + EXPECT_TRUE( testComponent->addComponent( componentB ) ) << "Failed to add component b to testComponent"; - EXPECT_EQ( componentA->getOwner(), testComponent) + EXPECT_EQ( componentA->getOwner(), testComponent ) << "testComponent did not properly set the mOwner field of componentA to NULL."; - EXPECT_EQ( componentB->getOwner(), testComponent) + EXPECT_EQ( componentB->getOwner(), testComponent ) << "testComponent did not properly set the mOwner field of componentB to NULL."; // Register the object with the simulation, kicking off the interface registration From b01e5668b42e0d7d0fd9a9c6c92f7e6a47cfc0f1 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 28 Sep 2014 16:15:52 +1000 Subject: [PATCH 206/317] Removed dead unit test code from main loop. --- Engine/source/platformWin32/winWindow.cpp | 9 --------- 1 file changed, 9 deletions(-) diff --git a/Engine/source/platformWin32/winWindow.cpp b/Engine/source/platformWin32/winWindow.cpp index 5c2bac019..8af7079b2 100644 --- a/Engine/source/platformWin32/winWindow.cpp +++ b/Engine/source/platformWin32/winWindow.cpp @@ -311,18 +311,10 @@ S32 main(S32 argc, const char **argv) //-------------------------------------- -#include "unit/test.h" #include "app/mainLoop.h" S32 PASCAL WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR lpszCmdLine, S32) { -#if 0 - // Run a unit test. - StandardMainLoop::initCore(); - UnitTesting::TestRun tr; - tr.test("Platform", true); -#else - Vector argv( __FILE__, __LINE__ ); char moduleName[256]; @@ -366,7 +358,6 @@ S32 PASCAL WinMain( HINSTANCE hInstance, HINSTANCE, LPSTR lpszCmdLine, S32) dFree(argv[j]); return retVal; -#endif } #else //TORQUE_SHARED From d02cc94eddbbde7f5accc2345f11d6b29a820eae Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 28 Sep 2014 16:16:23 +1000 Subject: [PATCH 207/317] Allow test runs to be non-verbose. --- Engine/source/testing/unitTesting.cpp | 21 ++++++++++++++------- 1 file changed, 14 insertions(+), 7 deletions(-) diff --git a/Engine/source/testing/unitTesting.cpp b/Engine/source/testing/unitTesting.cpp index bf4a2bc0a..ce92475d0 100644 --- a/Engine/source/testing/unitTesting.cpp +++ b/Engine/source/testing/unitTesting.cpp @@ -36,8 +36,9 @@ class TorqueUnitTestListener : public ::testing::EmptyTestEventListener // Called before a test starts. virtual void OnTestStart( const ::testing::TestInfo& testInfo ) { - Con::printf("> Starting Test '%s.%s'", - testInfo.test_case_name(), testInfo.name()); + if( mVerbose ) + Con::printf("> Starting Test '%s.%s'", + testInfo.test_case_name(), testInfo.name()); } // Called after a failed assertion or a SUCCEED() invocation. @@ -45,13 +46,13 @@ class TorqueUnitTestListener : public ::testing::EmptyTestEventListener { if ( testPartResult.failed() ) { - Con::warnf(">> Failed with '%s' in '%s' at (line:%d)", + Con::warnf(">> Failed with '%s' in '%s' at (line:%d)\n", testPartResult.summary(), testPartResult.file_name(), testPartResult.line_number() ); } - else + else if( mVerbose ) { Con::printf(">> Passed with '%s' in '%s' at (line:%d)", testPartResult.summary(), @@ -64,9 +65,15 @@ class TorqueUnitTestListener : public ::testing::EmptyTestEventListener // Called after a test ends. virtual void OnTestEnd( const ::testing::TestInfo& testInfo ) { - Con::printf("> Ending Test '%s.%s'\n", - testInfo.test_case_name(), testInfo.name()); + if( mVerbose ) + Con::printf("> Ending Test '%s.%s'\n", + testInfo.test_case_name(), testInfo.name()); } + + bool mVerbose; + +public: + TorqueUnitTestListener( bool verbose ) : mVerbose( verbose ) {} }; DefineConsoleFunction( runAllUnitTests, int, (const char* testSpecs), (""), @@ -113,7 +120,7 @@ DefineConsoleFunction( runAllUnitTests, int, (const char* testSpecs), (""), } // Add the Torque unit test listener. - listeners.Append( new TorqueUnitTestListener ); + listeners.Append( new TorqueUnitTestListener(false) ); // Perform googletest run. Con::printf( "\nUnit Tests Starting...\n" ); From 178484ca6ab30fab0361ab813a7fa62fdf303a5f Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 28 Sep 2014 16:30:16 +1000 Subject: [PATCH 208/317] Moved zip tests and commented them out :(. --- Engine/source/core/util/zip/{unitTests => test}/zipTest.h | 0 .../source/core/util/zip/{unitTests => test}/zipTestMisc.cpp | 3 ++- .../source/core/util/zip/{unitTests => test}/zipTestRead.cpp | 3 ++- .../source/core/util/zip/{unitTests => test}/zipTestWrite.cpp | 3 ++- Tools/CMake/torque3d.cmake | 2 +- Tools/projectGenerator/modules/core.inc | 2 +- 6 files changed, 8 insertions(+), 5 deletions(-) rename Engine/source/core/util/zip/{unitTests => test}/zipTest.h (100%) rename Engine/source/core/util/zip/{unitTests => test}/zipTestMisc.cpp (99%) rename Engine/source/core/util/zip/{unitTests => test}/zipTestRead.cpp (99%) rename Engine/source/core/util/zip/{unitTests => test}/zipTestWrite.cpp (99%) diff --git a/Engine/source/core/util/zip/unitTests/zipTest.h b/Engine/source/core/util/zip/test/zipTest.h similarity index 100% rename from Engine/source/core/util/zip/unitTests/zipTest.h rename to Engine/source/core/util/zip/test/zipTest.h diff --git a/Engine/source/core/util/zip/unitTests/zipTestMisc.cpp b/Engine/source/core/util/zip/test/zipTestMisc.cpp similarity index 99% rename from Engine/source/core/util/zip/unitTests/zipTestMisc.cpp rename to Engine/source/core/util/zip/test/zipTestMisc.cpp index be5392c30..eda383e00 100644 --- a/Engine/source/core/util/zip/unitTests/zipTestMisc.cpp +++ b/Engine/source/core/util/zip/test/zipTestMisc.cpp @@ -19,7 +19,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- - +/* #include "core/crc.h" #include "core/strings/stringFunctions.h" #include "core/util/zip/zipArchive.h" @@ -194,3 +194,4 @@ private: return ret; } }; +*/ \ No newline at end of file diff --git a/Engine/source/core/util/zip/unitTests/zipTestRead.cpp b/Engine/source/core/util/zip/test/zipTestRead.cpp similarity index 99% rename from Engine/source/core/util/zip/unitTests/zipTestRead.cpp rename to Engine/source/core/util/zip/test/zipTestRead.cpp index 45be733d4..20df7c6b0 100644 --- a/Engine/source/core/util/zip/unitTests/zipTestRead.cpp +++ b/Engine/source/core/util/zip/test/zipTestRead.cpp @@ -19,7 +19,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- - +/* #include "platform/platform.h" #include "unit/test.h" @@ -250,3 +250,4 @@ private: return ret; } }; +*/ \ No newline at end of file diff --git a/Engine/source/core/util/zip/unitTests/zipTestWrite.cpp b/Engine/source/core/util/zip/test/zipTestWrite.cpp similarity index 99% rename from Engine/source/core/util/zip/unitTests/zipTestWrite.cpp rename to Engine/source/core/util/zip/test/zipTestWrite.cpp index cdfaa8778..889cf6948 100644 --- a/Engine/source/core/util/zip/unitTests/zipTestWrite.cpp +++ b/Engine/source/core/util/zip/test/zipTestWrite.cpp @@ -19,7 +19,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- - +/* #include "core/strings/stringFunctions.h" #include "core/util/zip/zipArchive.h" #include "core/util/zip/unitTests/zipTest.h" @@ -242,3 +242,4 @@ bail: return ret; } }; +*/ \ No newline at end of file diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 4c6869610..f2b3f2b33 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -166,7 +166,7 @@ addPath("${srcDir}/core/util/test") addPath("${srcDir}/core/util/journal") addPath("${srcDir}/core/util/journal/test") addPath("${srcDir}/core/util/zip") -addPath("${srcDir}/core/util/zip/unitTests") +addPath("${srcDir}/core/util/zip/test") addPath("${srcDir}/core/util/zip/compressors") addPath("${srcDir}/i18n") addPath("${srcDir}/sim") diff --git a/Tools/projectGenerator/modules/core.inc b/Tools/projectGenerator/modules/core.inc index d730cc1a2..cd6ac7c3f 100644 --- a/Tools/projectGenerator/modules/core.inc +++ b/Tools/projectGenerator/modules/core.inc @@ -44,7 +44,7 @@ addEngineSrcDir('core/util/test'); addEngineSrcDir('core/util/journal'); addEngineSrcDir('core/util/journal/test'); addEngineSrcDir('core/util/zip'); -addEngineSrcDir('core/util/zip/unitTests'); +addEngineSrcDir('core/util/zip/test'); addEngineSrcDir('core/util/zip/compressors'); addEngineSrcDir('i18n'); addEngineSrcDir('sim'); From 87c4c5c790ccd3ef0f58b81adf7b75fb9a058693 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 28 Sep 2014 19:05:53 +1000 Subject: [PATCH 209/317] Removed old test framework from project generators. --- Tools/CMake/torque3d.cmake | 2 -- Tools/projectGenerator/modules/core.inc | 2 -- 2 files changed, 4 deletions(-) diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index f2b3f2b33..dfcd17aa1 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -170,8 +170,6 @@ addPath("${srcDir}/core/util/zip/test") addPath("${srcDir}/core/util/zip/compressors") addPath("${srcDir}/i18n") addPath("${srcDir}/sim") -#addPath("${srcDir}/unit/tests") -addPath("${srcDir}/unit") addPath("${srcDir}/util") addPath("${srcDir}/windowManager") addPath("${srcDir}/windowManager/torque") diff --git a/Tools/projectGenerator/modules/core.inc b/Tools/projectGenerator/modules/core.inc index cd6ac7c3f..1071ea8bb 100644 --- a/Tools/projectGenerator/modules/core.inc +++ b/Tools/projectGenerator/modules/core.inc @@ -48,8 +48,6 @@ addEngineSrcDir('core/util/zip/test'); addEngineSrcDir('core/util/zip/compressors'); addEngineSrcDir('i18n'); addEngineSrcDir('sim'); -addEngineSrcDir('unit/tests'); -addEngineSrcDir('unit'); addEngineSrcDir('util'); addEngineSrcDir('windowManager'); addEngineSrcDir('windowManager/torque'); From b173e5571c3be2d435967e93c56570ecbf76c931 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Sun, 28 Sep 2014 19:08:43 +1000 Subject: [PATCH 210/317] Moved async packet queue test and commented it out. --- .../test/asyncPacketQueueTest.cpp} | 3 ++- Tools/CMake/torque3d.cmake | 1 + Tools/projectGenerator/modules/core.inc | 1 + 3 files changed, 4 insertions(+), 1 deletion(-) rename Engine/source/platform/{test/testAsyncPacketQueue.cpp => async/test/asyncPacketQueueTest.cpp} (99%) diff --git a/Engine/source/platform/test/testAsyncPacketQueue.cpp b/Engine/source/platform/async/test/asyncPacketQueueTest.cpp similarity index 99% rename from Engine/source/platform/test/testAsyncPacketQueue.cpp rename to Engine/source/platform/async/test/asyncPacketQueueTest.cpp index 7c2d72333..f928b4da2 100644 --- a/Engine/source/platform/test/testAsyncPacketQueue.cpp +++ b/Engine/source/platform/async/test/asyncPacketQueueTest.cpp @@ -19,7 +19,7 @@ // FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS // IN THE SOFTWARE. //----------------------------------------------------------------------------- - +/* #include "unit/test.h" #include "platform/async/asyncPacketQueue.h" #include "console/console.h" @@ -149,3 +149,4 @@ CreateUnitTest( TestAsyncPacketQueue, "Platform/AsyncPacketQueue" ) }; #endif // !TORQUE_SHIPPING +*/ \ No newline at end of file diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index dfcd17aa1..53885861d 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -186,6 +186,7 @@ endif() addPath("${srcDir}/platform/test") addPath("${srcDir}/platform/threads") addPath("${srcDir}/platform/async") +addPath("${srcDir}/platform/async/test") addPath("${srcDir}/platform/input") addPath("${srcDir}/platform/output") addPath("${srcDir}/app") diff --git a/Tools/projectGenerator/modules/core.inc b/Tools/projectGenerator/modules/core.inc index 1071ea8bb..3860705f0 100644 --- a/Tools/projectGenerator/modules/core.inc +++ b/Tools/projectGenerator/modules/core.inc @@ -72,6 +72,7 @@ switch( T3D_Generator::$platform ) addEngineSrcDir('platform/threads'); addEngineSrcDir('platform/threads/test'); addEngineSrcDir('platform/async'); +addEngineSrcDir('platform/async/test'); addEngineSrcDir('platform/input'); addEngineSrcDir('platform/output'); addEngineSrcDir('app'); From fae1bad96c277f0f5fe9dbd6f00b7441999837e0 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Mon, 29 Sep 2014 11:00:43 +1000 Subject: [PATCH 211/317] All unit tests that run now pass. --- .../component/test/simComponentTest.cpp | 28 ++++++++----------- .../platform/test/platformTimerTest.cpp | 2 +- .../threads/test/threadSafeRefCountTest.cpp | 10 +++---- Engine/source/testing/unitTesting.cpp | 1 + 4 files changed, 19 insertions(+), 22 deletions(-) diff --git a/Engine/source/component/test/simComponentTest.cpp b/Engine/source/component/test/simComponentTest.cpp index b4c8b1509..407e3adce 100644 --- a/Engine/source/component/test/simComponentTest.cpp +++ b/Engine/source/component/test/simComponentTest.cpp @@ -47,10 +47,10 @@ public: public: ////////////////////////////////////////////////////////////////////////// - virtual void registerInterfaces( const SimComponent *owner ) + virtual void registerInterfaces( SimComponent *owner ) { // Register a cached interface for this - registerCachedInterface( NULL, "aU32", this, &mMyId ); + owner->registerCachedInterface( NULL, "aU32", this, &mMyId ); } ////////////////////////////////////////////////////////////////////////// @@ -66,7 +66,7 @@ public: ComponentInterfaceList list; // Enumerate the interfaces on the owner, only ignore interfaces that this object owns - if( !_getOwner()->getInterfaces( &list, NULL, "aU32", this, true ) ) + if( !owner->getInterfaces( &list, NULL, "aU32", this, true ) ) return false; // Sanity check before just assigning all willy-nilly @@ -131,23 +131,19 @@ TEST(SimComponent, Composition) << "testComponent did not properly set the mOwner field of componentB to NULL."; // Register the object with the simulation, kicking off the interface registration - const bool registered = testComponent->registerObject(); - EXPECT_TRUE( registered ) + ASSERT_TRUE( testComponent->registerObject() ) << "Failed to register testComponent"; - // Interface tests - if( registered ) { - { - SCOPED_TRACE("componentA"); - componentA->unit_test(); - } - { - SCOPED_TRACE("componentB"); - componentB->unit_test(); - } - testComponent->deleteObject(); + SCOPED_TRACE("componentA"); + componentA->unit_test(); } + { + SCOPED_TRACE("componentB"); + componentB->unit_test(); + } + + testComponent->deleteObject(); }; #endif \ No newline at end of file diff --git a/Engine/source/platform/test/platformTimerTest.cpp b/Engine/source/platform/test/platformTimerTest.cpp index dddf58830..c8f4db09f 100644 --- a/Engine/source/platform/test/platformTimerTest.cpp +++ b/Engine/source/platform/test/platformTimerTest.cpp @@ -40,7 +40,7 @@ TEST(Platform, Sleep) U32 start = Platform::getRealMilliseconds(); Platform::sleep(500); U32 end = Platform::getRealMilliseconds(); - EXPECT_GE(end - start, 500) + EXPECT_GE(end - start, 500-10) // account for clock resolution << "We didn't sleep at least as long as we requested!"; }; diff --git a/Engine/source/platform/threads/test/threadSafeRefCountTest.cpp b/Engine/source/platform/threads/test/threadSafeRefCountTest.cpp index 698637422..16390dd57 100644 --- a/Engine/source/platform/threads/test/threadSafeRefCountTest.cpp +++ b/Engine/source/platform/threads/test/threadSafeRefCountTest.cpp @@ -105,7 +105,7 @@ TEST(ThreadSafeRefCount, Concurrent) }; mRef = new TestObject; - EXPECT_EQ(mRef->getRefCount(), 2); // increments of 2 + EXPECT_EQ(2, mRef->getRefCount()); // increments of 2 Vector threads; threads.setSize(NUM_THREADS); @@ -122,8 +122,8 @@ TEST(ThreadSafeRefCount, Concurrent) for (U32 i = 0; i < NUM_THREADS; i++) threads[i]->join(); - Con::printf("REF: %i", mRef->getRefCount()); - EXPECT_EQ(mRef->getRefCount(), 2 + ((NUM_ADD_REFS_PER_THREAD + NUM_EXTRA_REFS_PER_THREAD) * NUM_THREADS * 2)); + EXPECT_EQ(2 + ((1 + NUM_ADD_REFS_PER_THREAD + NUM_EXTRA_REFS_PER_THREAD) * NUM_THREADS * 2), + mRef->getRefCount()); // Run phase 2: release references. for (U32 i = 0; i < NUM_THREADS; i++) @@ -136,7 +136,7 @@ TEST(ThreadSafeRefCount, Concurrent) delete threads[i]; } - EXPECT_EQ(mRef->getRefCount(), 2); // increments of two + EXPECT_EQ(2, mRef->getRefCount()); // increments of two mRef = NULL; } @@ -148,7 +148,7 @@ TEST(ThreadSafeRefCount, Tagging) TestObjectRef ref; EXPECT_FALSE(ref.isTagged()); - EXPECT_TRUE(bool(ref)); + EXPECT_FALSE(bool(ref)); EXPECT_FALSE(bool(ref.ptr())); EXPECT_TRUE(ref.trySetFromTo(ref, NULL)); diff --git a/Engine/source/testing/unitTesting.cpp b/Engine/source/testing/unitTesting.cpp index ce92475d0..2724bf637 100644 --- a/Engine/source/testing/unitTesting.cpp +++ b/Engine/source/testing/unitTesting.cpp @@ -86,6 +86,7 @@ DefineConsoleFunction( runAllUnitTests, int, (const char* testSpecs), (""), "@param testSpecs A space-sepatated list of filters for test cases. " "See https://code.google.com/p/googletest/wiki/AdvancedGuide#Running_a_Subset_of_the_Tests " + "and http://stackoverflow.com/a/14021997/945863 " "for a description of the flag format.") { S32 testArgc = 0; From 6cc59a97cc7fc9855ae73f139a1170d89c1ff61e Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Mon, 29 Sep 2014 11:05:55 +1000 Subject: [PATCH 212/317] Added CMake flag for unit tests. --- Tools/CMake/torque3d.cmake | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/Tools/CMake/torque3d.cmake b/Tools/CMake/torque3d.cmake index 53885861d..92fae8f6b 100644 --- a/Tools/CMake/torque3d.cmake +++ b/Tools/CMake/torque3d.cmake @@ -60,6 +60,8 @@ option(TORQUE_EXTENDED_MOVE "Extended move support" OFF) mark_as_advanced(TORQUE_EXTENDED_MOVE) option(TORQUE_NAVIGATION "Enable Navigation module" OFF) #mark_as_advanced(TORQUE_NAVIGATION) +option(TORQUE_TESTING "Enable unit test module" OFF) +mark_as_advanced(TORQUE_TESTING) if(WIN32) option(TORQUE_OPENGL "Allow OpenGL render" OFF) #mark_as_advanced(TORQUE_OPENGL) @@ -326,6 +328,10 @@ else() addPath("${srcDir}/T3D/gameBase/std") endif() +if(TORQUE_TESTING) + include( "modules/module_testing.cmake" ) +endif() + if(TORQUE_NAVIGATION) include( "modules/module_navigation.cmake" ) endif() From bedde94a9f0898639c256b702f2f98326b305648 Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Mon, 29 Sep 2014 14:31:07 +1000 Subject: [PATCH 213/317] Removed all local type definitions for GCC. --- .../core/util/journal/test/journalTest.cpp | 121 ++++++++++-------- Engine/source/core/util/test/tVectorTest.cpp | 61 +++++---- .../platform/threads/test/threadPoolTest.cpp | 6 +- .../threads/test/threadSafeDequeTest.cpp | 111 ++++++++-------- .../threads/test/threadSafeRefCountTest.cpp | 63 ++++----- 5 files changed, 194 insertions(+), 168 deletions(-) diff --git a/Engine/source/core/util/journal/test/journalTest.cpp b/Engine/source/core/util/journal/test/journalTest.cpp index f44a6f157..fc638f11c 100644 --- a/Engine/source/core/util/journal/test/journalTest.cpp +++ b/Engine/source/core/util/journal/test/journalTest.cpp @@ -25,22 +25,78 @@ #include "core/util/journal/journaledSignal.h" #include "core/util/safeDelete.h" -TEST(Journal, BasicAPI) +FIXTURE(Journal) { +public: + // Used for basic API test. struct receiver { - U32 lastTriggerValue; - void triggerReceiver(U16 msg) + U16 lastTriggerValue; + void trigger(U16 msg) { lastTriggerValue = msg; } - } rec; + }; + // Used for non-basic test. + typedef JournaledSignal EventA; + typedef JournaledSignal EventB; + typedef JournaledSignal EventC; + + // Root, non-dynamic signal receiver. + struct multiReceiver { + U32 recvA, recvB, recvC; + + EventA *dynamicA; + EventB *dynamicB; + EventC *dynamicC; + + void receiverRoot(U8 msg) + { + if(msg==1) + { + dynamicA = new EventA(); + dynamicA->notify(this, &multiReceiver::receiverA); + } + + if(msg==2) + { + dynamicB = new EventB(); + dynamicB->notify(this, &multiReceiver::receiverB); + } + + if(msg==3) + { + dynamicC = new EventC(); + dynamicC->notify(this, &multiReceiver::receiverC); + } + } + + void receiverA(U32, U16 d) + { + recvA += d; + } + + void receiverB(U8, S8 d) + { + recvB += d; + } + + void receiverC(U32, S32 d) + { + recvC += d; + } + }; +}; + +TEST_FIX(Journal, BasicAPI) +{ + receiver rec; rec.lastTriggerValue = 0; // Set up a journaled signal to test with. JournaledSignal testEvent; - testEvent.notify(&rec, &receiver::triggerReceiver); + testEvent.notify(&rec, &receiver::trigger); // Initialize journal recording and fire off some events... Journal::Record("test.jrn"); @@ -71,63 +127,16 @@ TEST(Journal, BasicAPI) << "Should encounter last journaled value (18)."; } -TEST(Journal, DynamicSignals) +TEST_FIX(Journal, DynamicSignals) { - typedef JournaledSignal EventA; - typedef JournaledSignal EventB; - typedef JournaledSignal EventC; - - // Root, non-dynamic signal receiver. - struct receiver { - U32 recvA, recvB, recvC; - - EventA *dynamicA; - EventB *dynamicB; - EventC *dynamicC; - - void receiverRoot(U8 msg) - { - if(msg==1) - { - dynamicA = new EventA(); - dynamicA->notify(this, &receiver::receiverA); - } - - if(msg==2) - { - dynamicB = new EventB(); - dynamicB->notify(this, &receiver::receiverB); - } - - if(msg==3) - { - dynamicC = new EventC(); - dynamicC->notify(this, &receiver::receiverC); - } - } - - void receiverA(U32, U16 d) - { - recvA += d; - } - - void receiverB(U8, S8 d) - { - recvB += d; - } - - void receiverC(U32, S32 d) - { - recvC += d; - } - } rec; + multiReceiver rec; // Reset our state values. rec.recvA = rec.recvB = rec.recvC = 0; // Set up a signal to start with. JournaledSignal testEvent; - testEvent.notify(&rec, &receiver::receiverRoot); + testEvent.notify(&rec, &multiReceiver::receiverRoot); // Initialize journal recording and fire off some events... Journal::Record("test.jrn"); @@ -177,4 +186,4 @@ TEST(Journal, DynamicSignals) EXPECT_EQ(rec.recvC, 2) << "recvC wasn't 2 - something broken in journal?"; } -#endif \ No newline at end of file +#endif diff --git a/Engine/source/core/util/test/tVectorTest.cpp b/Engine/source/core/util/test/tVectorTest.cpp index 0aaf2f29e..3a3aeec13 100644 --- a/Engine/source/core/util/test/tVectorTest.cpp +++ b/Engine/source/core/util/test/tVectorTest.cpp @@ -25,22 +25,40 @@ #include "core/util/tVector.h" // Define some test data used below. -static const S32 ints[] = {0, 10, 2, 3, 14, 4, 12, 6, 16, 7, 8, 1, 11, 5, 13, 9, 15}; -static const U32 length = sizeof(ints) / sizeof(S32); -static S32 QSORT_CALLBACK sortInts(const S32* a, const S32* b) +FIXTURE(Vector) { - S32 av = *a; - S32 bv = *b; - - if (av < bv) - return -1; - else if (av > bv) - return 1; - else - return 0; -} +public: + struct Dtor + { + bool* ptr; + Dtor() {} // Needed for vector increment. + Dtor(bool* ptr): ptr(ptr) {} + ~Dtor() + { + *ptr = true; + } + }; -TEST(Vector, Allocation) + static const S32 ints[]; + static const U32 length; + static S32 QSORT_CALLBACK sortInts(const S32* a, const S32* b) + { + S32 av = *a; + S32 bv = *b; + + if (av < bv) + return -1; + else if (av > bv) + return 1; + else + return 0; + } +}; + +const S32 VectorFixture::ints[] = {0, 10, 2, 3, 14, 4, 12, 6, 16, 7, 8, 1, 11, 5, 13, 9, 15}; +const U32 VectorFixture::length = sizeof(VectorFixture::ints) / sizeof(S32); + +TEST_FIX(Vector, Allocation) { Vector *vector = new Vector; for (S32 i = 0; i < 1000; i++) @@ -57,19 +75,8 @@ TEST(Vector, Allocation) delete vector; } -TEST(Vector, Deallocation) +TEST_FIX(Vector, Deallocation) { - struct Dtor - { - bool* ptr; - Dtor() {} // Needed for vector increment. - Dtor(bool* ptr): ptr(ptr) {} - ~Dtor() - { - *ptr = true; - } - }; - bool dtorVals[10]; Vector v; @@ -101,7 +108,7 @@ TEST(Vector, Deallocation) << "Element " << i << "'s destructor was not called"; } -TEST(Vector, Sorting) +TEST_FIX(Vector, Sorting) { Vector v; diff --git a/Engine/source/platform/threads/test/threadPoolTest.cpp b/Engine/source/platform/threads/test/threadPoolTest.cpp index 95eacd920..cfb5c5dd4 100644 --- a/Engine/source/platform/threads/test/threadPoolTest.cpp +++ b/Engine/source/platform/threads/test/threadPoolTest.cpp @@ -26,8 +26,9 @@ #include "console/console.h" #include "core/util/tVector.h" -TEST(ThreadPool, BasicAPI) +FIXTURE(ThreadPool) { +public: // Represents a single unit of work. In this test we just set an element in // a result vector. struct TestItem : public ThreadPool::WorkItem @@ -43,7 +44,10 @@ TEST(ThreadPool, BasicAPI) mResults[mIndex] = mIndex; } }; +}; +TEST_FIX(ThreadPool, BasicAPI) +{ // Construct the vector of results from the work items. const U32 numItems = 100; Vector results(__FILE__, __LINE__); diff --git a/Engine/source/platform/threads/test/threadSafeDequeTest.cpp b/Engine/source/platform/threads/test/threadSafeDequeTest.cpp index 183c31d3f..54df89ad4 100644 --- a/Engine/source/platform/threads/test/threadSafeDequeTest.cpp +++ b/Engine/source/platform/threads/test/threadSafeDequeTest.cpp @@ -28,57 +28,10 @@ #include "core/util/tVector.h" #include "console/console.h" -// Test deque without concurrency. -TEST(ThreadSafeDeque, PopFront) +FIXTURE(ThreadSafeDeque) { - ThreadSafeDeque deque; - String str = "teststring"; - - for(U32 i = 0; i < str.length(); i++) - deque.pushBack(str[i]); - - EXPECT_FALSE(deque.isEmpty()); - - char ch; - for(U32 i = 0; i < str.length(); i++) - { - EXPECT_TRUE(deque.tryPopFront(ch)); - EXPECT_EQ(str[i], ch); - } - - ASSERT_TRUE(deque.isEmpty()); -} - -TEST(ThreadSafeDeque, PopBack) -{ - ThreadSafeDeque deque; - String str = "teststring"; - - const char* p1 = str.c_str() + 4; - const char* p2 = p1 + 1; - while(*p2) - { - deque.pushFront(*p1); - deque.pushBack(*p2); - --p1; - ++p2; - } - - char ch; - for(S32 i = str.length()-1; i >= 0; i--) - { - EXPECT_TRUE(deque.tryPopBack(ch)); - EXPECT_EQ(str[i], ch); - } - - ASSERT_TRUE(deque.isEmpty()); -} - -// Test deque in a concurrent setting. -TEST(ThreadSafeDeque, Concurrent1) -{ - const U32 NumValues = 100; - +public: + // Used by the concurrent test. struct Value : public ThreadSafeRefCount { U32 mIndex; @@ -121,9 +74,6 @@ TEST(ThreadSafeDeque, Concurrent1) } }; - Deque mDeque; - Vector mValues; - struct ProducerThread : public Thread { Vector& mValues; @@ -163,6 +113,61 @@ TEST(ThreadSafeDeque, Concurrent1) } } }; +}; + +// Test deque without concurrency. +TEST_FIX(ThreadSafeDeque, PopFront) +{ + ThreadSafeDeque deque; + String str = "teststring"; + + for(U32 i = 0; i < str.length(); i++) + deque.pushBack(str[i]); + + EXPECT_FALSE(deque.isEmpty()); + + char ch; + for(U32 i = 0; i < str.length(); i++) + { + EXPECT_TRUE(deque.tryPopFront(ch)); + EXPECT_EQ(str[i], ch); + } + + ASSERT_TRUE(deque.isEmpty()); +} + +TEST_FIX(ThreadSafeDeque, PopBack) +{ + ThreadSafeDeque deque; + String str = "teststring"; + + const char* p1 = str.c_str() + 4; + const char* p2 = p1 + 1; + while(*p2) + { + deque.pushFront(*p1); + deque.pushBack(*p2); + --p1; + ++p2; + } + + char ch; + for(S32 i = str.length()-1; i >= 0; i--) + { + EXPECT_TRUE(deque.tryPopBack(ch)); + EXPECT_EQ(str[i], ch); + } + + ASSERT_TRUE(deque.isEmpty()); +} + +// Test deque in a concurrent setting. +TEST_FIX(ThreadSafeDeque, Concurrent1) +{ + const U32 NumValues = 100; + + Deque mDeque; + Vector mValues; mValues.setSize(NumValues); diff --git a/Engine/source/platform/threads/test/threadSafeRefCountTest.cpp b/Engine/source/platform/threads/test/threadSafeRefCountTest.cpp index 16390dd57..28c6bf0a7 100644 --- a/Engine/source/platform/threads/test/threadSafeRefCountTest.cpp +++ b/Engine/source/platform/threads/test/threadSafeRefCountTest.cpp @@ -27,42 +27,23 @@ #include "core/util/tVector.h" #include "console/console.h" -TEST(ThreadSafeRefCount, Serial) +FIXTURE(ThreadSafeRefCount) { - struct TestObject : public ThreadSafeRefCount +public: + struct TestObjectDtor : public ThreadSafeRefCount { bool &flag; - TestObject(bool &f) : flag(f) + TestObjectDtor(bool &f) : flag(f) { flag = false; } - ~TestObject() + ~TestObjectDtor() { flag = true; } }; - typedef ThreadSafeRef TestObjectRef; + typedef ThreadSafeRef TestObjectDtorRef; - bool deleted = false; - TestObjectRef ref1 = new TestObject(deleted); - ASSERT_FALSE(deleted); - EXPECT_FALSE(ref1->isShared()); - EXPECT_TRUE(ref1 != NULL); - - TestObjectRef ref2 = ref1; - EXPECT_TRUE(ref1->isShared()); - EXPECT_TRUE(ref2->isShared()); - EXPECT_EQ(ref1, ref2); - - ref1 = NULL; - EXPECT_FALSE(ref2->isShared()); - - ref2 = NULL; - ASSERT_TRUE(deleted); -} - -TEST(ThreadSafeRefCount, Concurrent) -{ enum { NUM_ADD_REFS_PER_THREAD = 10, @@ -72,7 +53,6 @@ TEST(ThreadSafeRefCount, Concurrent) class TestObject : public ThreadSafeRefCount {}; typedef ThreadSafeRef TestObjectRef; - TestObjectRef mRef; class TestThread : public Thread { @@ -104,7 +84,31 @@ TEST(ThreadSafeRefCount, Concurrent) } }; - mRef = new TestObject; +}; + +TEST_FIX(ThreadSafeRefCount, Serial) +{ + bool deleted = false; + TestObjectDtorRef ref1 = new TestObjectDtor(deleted); + ASSERT_FALSE(deleted); + EXPECT_FALSE(ref1->isShared()); + EXPECT_TRUE(ref1 != NULL); + + TestObjectDtorRef ref2 = ref1; + EXPECT_TRUE(ref1->isShared()); + EXPECT_TRUE(ref2->isShared()); + EXPECT_EQ(ref1, ref2); + + ref1 = NULL; + EXPECT_FALSE(ref2->isShared()); + + ref2 = NULL; + ASSERT_TRUE(deleted); +} + +TEST_FIX(ThreadSafeRefCount, Concurrent) +{ + TestObjectRef mRef = new TestObject; EXPECT_EQ(2, mRef->getRefCount()); // increments of 2 Vector threads; @@ -141,11 +145,8 @@ TEST(ThreadSafeRefCount, Concurrent) mRef = NULL; } -TEST(ThreadSafeRefCount, Tagging) +TEST_FIX(ThreadSafeRefCount, Tagging) { - struct TestObject : public ThreadSafeRefCount {}; - typedef ThreadSafeRef TestObjectRef; - TestObjectRef ref; EXPECT_FALSE(ref.isTagged()); EXPECT_FALSE(bool(ref)); From 599ad823b0353bdcbee373ea9c41bcb685e9587b Mon Sep 17 00:00:00 2001 From: Daniel Buckmaster Date: Thu, 2 Oct 2014 10:51:39 +1000 Subject: [PATCH 214/317] Added Outpost testing level by Azaezel. --- .../art/shapes/trees/defaulttree/materials.cs | 8 + .../Full/game/art/terrains/Outpost_0.ter | Bin 0 -> 3145786 bytes Templates/Full/game/art/terrains/materials.cs | 150 +- Templates/Full/game/levels/Outpost.forest | Bin 0 -> 29500 bytes Templates/Full/game/levels/Outpost.mis | 1295 +++++++++++++++++ .../Full/game/levels/Outpost.postfxpreset.cs | 45 + .../Full/game/levels/Outpost_preview.png | Bin 0 -> 209178 bytes 7 files changed, 1450 insertions(+), 48 deletions(-) create mode 100644 Templates/Full/game/art/terrains/Outpost_0.ter create mode 100644 Templates/Full/game/levels/Outpost.forest create mode 100644 Templates/Full/game/levels/Outpost.mis create mode 100644 Templates/Full/game/levels/Outpost.postfxpreset.cs create mode 100644 Templates/Full/game/levels/Outpost_preview.png diff --git a/Templates/Full/game/art/shapes/trees/defaulttree/materials.cs b/Templates/Full/game/art/shapes/trees/defaulttree/materials.cs index ca5adadcd..f82946ccb 100644 --- a/Templates/Full/game/art/shapes/trees/defaulttree/materials.cs +++ b/Templates/Full/game/art/shapes/trees/defaulttree/materials.cs @@ -72,3 +72,11 @@ singleton Material(defaultTree_fronds_material) alphaRef = "114"; translucent = "1"; }; + +singleton Material(defaulttree_ColorEffectR27G177B88_material) +{ + mapTo = "ColorEffectR27G177B88-material"; + diffuseColor[0] = "0.105882 0.694118 0.345098 1"; + specularPower[0] = "10"; + translucentBlendOp = "None"; +}; diff --git a/Templates/Full/game/art/terrains/Outpost_0.ter b/Templates/Full/game/art/terrains/Outpost_0.ter new file mode 100644 index 0000000000000000000000000000000000000000..5b39c9e700e850104fd01be7e1a2a785737dc1d5 GIT binary patch literal 3145786 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

reW31F|O3s$Iq#9b#roz#q7n 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 PB-k>ro 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�N*rwa;2}KCRJ#|PWkn-iA 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

x?>y1r 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

+92b~fKj)lx~_zLVnAdQlv9UbgM)!>@PV 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℘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

kOuus%0N=hrj= zc>y}ORlBLnBGg3pla63ht+V2GQS=`#{4VO+RXhqe;VHLS5gxH7ND){cq=Ny~wI?rZjw1B=;YN`*QW>-%|JzGY% zh0}OL(xCjv1<;r)qq}pMnEpQl)qkG5(!S)?R&$^3g_WOk`MFT`H$8W?rvH>?ooLE? z9ze`8nKer@QdY1!DE3#Bw3n6lHclZ;y-5qInEwA3nz;J^bf8+ubo!BC@;DH4B1*t4 z&~!O5_wV3t0{nk3lliX!_BZ$1k@t@R>81Vmf&&clMtKvx>E1jq#+&I)fDsJjryac3 z@PyXn3FH%K@3z(`2US7uTwWxq{f>xbP6ud79^y4HFAbWdoNk)28y~DoN6k`YJj|o` zz272kMWlpPlyvTchF(KF2vyuOCGu`MU12{R364dak9Zi71l|z_EBFiV!V+4W9@>O0g-#~ z8=~GhFt;(p{k@3_I&tr-bGLGSiXtk5sk-|$(WVfqkm`A_QkQ&%7&{5~R0C9lufWt5 zb^U%kmGON>jyp|@Pea_uH6(pa^ zd(rIUWo&bu`(z6HrKyhJxT`gq?~P`U)zLt`j5@OVHRM?<@$T{xBZQ#%9|6^WB%1%4 z41>JbYeD`kypw%I{0CT<_d4v~4DPRDtx;vbKu}0d|CC{>Kop8h4>-~^_g0=J;lJkL zj^^EJf~lq&wxbTP9(+eWvvlB%`q^9q*Z)S#KLN@gg%AAZV*RarmbdRPD57q54?{w6 zr6oZ-NJ<7F+*J`i;QL|#SBTUN`%j_&pW?avo;dOZp5!?W!UQx?Z!`F|hTZ^Q65~!K z%KaD*K!3JjVBRdgzZv8|1HX9aaH|lpYjgbe!*OdZ;~edO3jQ|$i=&A0hH%yu{m%%j z$@3p`06h{9kXmaTU?>crgTrvEc}`Vc493$jf^8JsVI1m`ws9bf zeQzR+V=|jEK$Cg>(c~a{^E~D(%i8@e#tb=ASU&Cp(3B6`K zXQn=Rjzpm+;Yl&DqRz0@y5M+>*xLMU9-UGM9`aH$0x zX?0MzKz`<~a}s8U2UNui+7ouT6mRhRB0di}?_ON3tX*ySQ4m5WEu zJ9RxVTsQ(Qo{apvx$v@c1zR7U%G?`exuOdP^a4%mDa_Ypxz1H1AO%3#e%1JNAo73L ztg9_lEr(E#Ccq=r1EVZl=yg7q=UCK&6Tizf)(%BLA0MCx=>N`W zX2ZCnz9*k1?_?G-2$f-oVgS+rq%`?la{+hcH@vFyCyM^%&rJqn(X{TutQn<=6k2lU ztK-i+)(A!Z>i=RhUzANhi4rXwN`eC*E%@^ii1P*+fX1L>PvYy3T=sl0x%pAx{|q$U~~BAgmdJCf92!73j1GWPs%u5qJsNeCXl00ANL~s zya2zedPScFq+oAj*ArOHolw~CSY$nK?92=KEgN8X8Ds+gMox?Gt;dk%>lLS)uRwiz z{O$t3g0OpFHv0oi!Fu=v`#ae0=~WHCg9xtZRMUtQze^ucb%%Nj*pwh;GSoXjSs?3Z z^zYdQaj{qBm5*^^T(D@)=9ag?lF#U<-TX%w& zXSu7d@^KDD`4W}n^82>|BhG~7q|g6AauYuUn93Zkufc9F+dJI<6tw?_pY3!?*eRIl z1#&*-CcVos+RyP*O{XeUtgcb@Oz(3|Hbo&&T%)Ka59@=b`<({a*9SWN7@~{;!{qf} z4hH6cHA>y!KjK~XA%Yp;{7j?46yd*o&B_Dl zv2p& zi+Vrx`z_4+{0^&e9~fFR5vywGj!-@S5KUJ4zg%pkI0_9}le?k*t4cz7*{9=wkONF- z-5vv`_k{yIx%K484hF%s3Gofxr=TC0vwJDXqHQVXVS)2{>pS0|G_T-eDH)=8m_z}d%SZnR z7yVym9XL&9?l^bFanSZ0QNd-9|1w20Tdt@O@Yy^>iJMuE6xlY2vPK5pd`ge!> zm!ms!UasNXcwse5vJwol4#&Q++ung2*Wx=>Uq$_Nm^}(EvVr|biuzCv{?Q8L@54UyVPERO z2eOf0NQlxO1`p70yvcr+b~%BYJnOe`hmYtJpiXE_$aMI9Q8gj34Cq<_WX(ie?%}Mv zNc67>XOZCKlbwmsRD)WVYfGwBRj#Ufe5b8r0G~VMcq!}q zPvFrhloyX#{AG2yBDza}r6DR{W5C^b~Ku&y{>g1jR|Dfva z9>h}r6{ia|g;&=3FE~$EKER+}?lXX`PMrv5{tfX@Is84wvz>4{z3=Dy4fMB3gX(-} zC~R{OV!Q7B=N9MCZyN^-4-2YLQ=XaZW-^tJ2!F zO>+X2?_Uo>orZ%XWp4`8)xJLYimq^(@$9#}YnzDv&)~_5hpzLSQ`3u*i?7LUG4$kCU> znQ@0MR6lS`2H^C68`nxfs#mk1??2hSA};p(8i4WuS^1k%xB;8s2NVYw%Q2ooK42;Q zN|k}}InyQ30QzuT*K$N7aRJnTuZ-u2t)ds8IsnPLmXJt45!Y!_{!a)~y+>So1$4W} z$7MKzej^zSAv-9oPW*=d#lSrI1e7rlGtkdUb6;!vT>~&*{eo5Ft4Lp1uPomid{|#0 z`$}2Af1`hVl)W3g@>wwUI7lmH>=;bo3>@GrE5lK~6YTdB#r;$bwIw4i%fF}zp2W4( z&~?Cgo~v2g^IXCCsE{blwN{m_20CFyzAC}B{UZERv!<&PF%0GYS%V62lP-$6dB&>n zx|ZaEdUNHdc2ds26)sn|1D<}GW2C8nnOVD}eu({4Vts4RO7{sFPeqnVmj@SIDfIA`d)N_r1&BDesWC z#ruv-fpmb5WD`o0&krLPpjdq(TD{F5R7RpFZoV0G7rYvg7XDF{%z^laUacR_gBD=@ zi+B>A+iAtT`giM_=@M9NBdvoJb^u65jNLY4B&0&1W*Q`5HWZ*)>l;_D);}J zi}e{fVzzt4g?A_X9Bzdh?$ ztm?^N<(x9Znpd=u|5tNGY-CSjZ%@YmivG{ASv}z?9{tT`@xSs4njo)!2%0PC$MDZ~ z7;NJ8l#kY?>AefVDP`j)6G=~j11y9A=qlYwzTu>E16~FRZEk?}LG_IO{CZz^(B21; z|KF2~!~!e>64d{N|2;V;jT|yc?^Ayfn`LKJzc!&PEqH#NyZ%q~y*o_eDGbU-bL95$ z&NuN6Vo>6TaTSOWcI8g1&y1i%O#gTg_7|GED&y*f6~T{{n$@5%nSt7_nm3B5Q90fA ztkeUzlNS?-CuFVaKwjb(x?d!QhiB%xP=#Iu8K|3V_nk{86Nq1g`?E5cF>~ptx9|J- zST%9u1%Ce~JZTrla0$m$97l|_C0ArkR=QVN6;p-ZVh!8O$}x_;Ye}3^0XFg)I8n`M zjJ=3oCKF43No=85GsZp{6k*K<)O5ff zPy`g6Uf?&AvGNw@b=2$cBd~uO`~NlTuR5|D`fKWXW-#j&GS}7c+%<8lvE2WIiOEJ2 zvrh)OXFZhxC<`zh{Ey~*kAq98SAhDpX|tXILH)pRUzMKg@LTMFhsk~UtZhF(-ws1q z!TFK~Fp7w}t7COdIJ-4CzvWDBCov<;#+g}t{MJ4W&v>f3>p(|j*9f;?x|3zZ1$_TF0vx?kXE z6h;#%%IBBS1=LkB5iB?%6BV+;b?cK0Q0>VtIBf3GN9je*p=APka=oc1n0o!F>TeIn z?E-rlf?kpv9^^YC>%ez3^Y;@M|F2`sQq2D+E9qIHjEi{xHCIku={+LItVEPrUEV+y zf1jv-Dw%%u%=?ns|1Uw!rMT#Rr{{kPkhBm8UB%V=soHMp zbkB%he})|YMk)qYxos_R|4y9zk0P=W*UNF(0rU@68&>s-z)q5*`9F`Vu$aqEyvy%Z z#3PUlN5F9$fnUOFG{>Mn8bD{5P7{a!HF)2C^q+;5G!#euMPe|E^t95Ca1oUvd`(kS zZ%Iv5vfdJ^`BMiuWtVi7`3yk3vI1A1a<{4mP!EPr(Rf#KCeD%pP~T-~F0T;7l_1w& zmMFiv!+Wd1Xvp3*eXLR_TOlq`14Y30)^{tUM@i81LiGfc_HW&GgTA?SF7G4U^(Pg` z8MYp76C3^YIBx#m^8Lbmx%5?2s4ZBR`4cQc^LM^-+cGfm-<%@&3bEsV!+%ZN)$}gS z@R|)XQifz5d!@b5e1{Y8fYa~|n~!jT$0wW$5Zgzn-qF(gt(TBrn_#X3o57?WQerBr zd3NRhqv2v*Ial>KGxGV%Gi(#=i-D5r`jwY;Bnxah9%z07ZhZsv&%lewpwbaEG%fbMs*9-NOD$l&?;m9?OZ3{KIGJaz&cc>|1tb?2<^?jnAdPD<-!2c_(Uf+OK>ey4Bl`1!gU^8pwX>UkIwl{b- zoPGTmcG8`gUsda`68A=+`R_)%odAY61CvXU%P$Td+iW~JnSK19?CO~{X-;$Iz7Ny{ zP7N>sIRe}93{6QNmvzxmO@5~de4#m86I4|Br|LMJRPmD5O?AS} z4t7gf&Bde4tgY!lv*b+g4RN^zb*=l``2tlN_zT-+`~d11e3+9&Ie z4YMgsqBjg+Cami>vOA$fzE$CW!(oKG$bzNd`m4*Hs=vho_>N(jrU4#ud`C#T-EEo? zbb#+3<8HhJ7f;OHY18e^{}*Hcxm@Nz-Fkfv@G5tAX;$G%fp{!fEuiUDLC;_@zAD7C zO8L*oostOkAs+g}9r%bJ{T~8rcd+ebfA;fuJNvAxzk2--azb{$&e_?$&jgLNcH|H2eQ-#ak>#Bf8l!MZYE;hLhb%WD(H4boF@9upzW*|6=m`Ka%a=4fdZPzQ5~*N2ZL-4Enzq`4U@hm_P;;fRvFQ3cx+@ zs&|gx``z2iUu^Q$dW+xyeZlq`Ab2*goB_zh?vbeaDM9zlR1B2D=cif&=?s0@gD$85 zjZpauq4=x5AOjwNQdA_?e*z52C-^mxlUH;U=wF@BPUwEy-6jCDLo)SioB@TcN&1 z+GE3izu%sw6kDWhuK6JPTkLH-;NSg!h19V-0QmWSq5ox$hxHW*M*mw~^shKxT@Ye8 zPReeqcWQhP^{J-|c=I`k*=kc+C=TILdzoW9>p=bvlRdt9y&+E$%U=x_-2UIQ~ zIAQTPoFu4B1bhGZ(#gN{FTqEc>Ed9B<9zH|3~xF!8~r^RDo{%|0Xd?!fBw z5uM!h+gU}9pdda7OT!BFA96^GcwZ54!5W7E3PK=zJQ*c z2&Y2Aun@Y!WaMjcfcp5dTkz4C_^~poaSkFz_4M6J9^*5oW@^E3r*`RD4SnEon29yIIY6LWe=HOD>fG4UW zoI$^@%BeQ!^Y5am8h0qmUurAIrW`?0r=p4nq-IU^n9THm{D8U+{t>7QP|rX)3ooM% zgpqrA9^N3Gp*EUpKNRt=Kz_|&-$T~_PwLgA{9EjwhZwp%Y^W~Br~zE*T{0);-RnXg zKppO%#Qe7)v?!-N%0q=jPw&8b9-1C!^U|m&Rfov+&Tfs3c3! z?mN@1qB!oq|IPbnxF7s%P5|`PoGC{hHY4R}!dh zP`v|6#?twe4-?ueXJEa2b@!{2Uv~a3wTt}|;`-OD!eI2jpMB8OqOI<;VgHw$OXXcU zIrM+ob^F;3vl_%zS_;=gSg0~l+HSb1(!&2RbbzF=)N~wk<-m*~+~yel2`fE<55}fX z73Qk!$Qg>kXL|(CL^6(88Lqq@yyASW7&-sN5HE2|)IBLP@1QtGr6%#jyR288KpT10 zqFG^AvMwB91r2ezeybfzjPf6fr~WxmM7@86K23=J2jjk54(=Z$*8ZFAB-nWb=lq?B z)a232f~<}4@OK3L`+@z#UEDv;izen@M81EMw+A#o<(>8ZA?tsK=syWqpApp0;~0SO zKRtRt(#SC4|GPK=!~jn77pJ^G$rkK_1H^da!1ngwcP&)@8oc7$DEW=?8nzC23kFeF zI25g*FMflz@PX1q{LjG@62Jhmk)5c)?>FMFD#BM%(uMFK@8?I})lyc@`8W&4v6}V- zU7MnJ)FPgK9rVuwmZl<;ejmg+4!&&Ws4YT0k&k)+@tx|uRUe|d6XTQWG9hJ2TO*En zBUW-L0G4CYHCcpAlzv}sfch|m_jxAOXz$`R;-lPa@}eL6zXi5yVp}VazbzFQona_i z|39Q!u`~NB&q6ai3t}wQIbX${TfxVFbplYoz0>TwKd(T3u-G&Ids8=Jr3po zO#L_X*Mz0(Ts;?JMgQuDyc|STPo&vw>MHpiulKC(LiS32Ho_bLnv|pt%G#E2tQUe? z3mqTObE!v_KRe-8z}aw~JqY#)GFO5+DyTx_GFpMWiou>ydQIsL#yNzFGswgAhq2Ws zo|kjD6f1*NKdl5Wf&N+-^0B_8C;xcIt+5ZnGH?c)^H5OFT*eOJ6n714^bI&4kC=WO7ARleIE$5JnRfU05|#&2qF%onVIUlT_! zM+aE!sstuG1z-p$+l#oSE6Q^_Ft;%bKv@1J&r}m!*Zi6eta?#AXLo+H4_hyyRKIJA z{@0uuRTPK^yp1AI&nW}q5#?EPl}UYpRW&^fAc=DcT*HBO1~1?lR@lp|tq+|m-P{Ew zVKADS)1K_aWVDK3LB`{B)H{z$^BTQ#6O#9r@6WpaRe=@xmHLgquGTOBb!zTL9w5j7 zgzZ5ufaU&U<9|@?mp@=3jBL3>e~bF}yGZv>knR#$U8^{=s}3 zlh}AG82KHM=t5q11|9>$sSj63V=EZJ;(dG$Gh7P%h8mEah$WRa4k(} z&W!&27g>>6_yC#_#lMUKkUk`c@c-8%RRk&zkc*0d*UV+QuG)us5&4rrJ6Npl)_^(E-%&VgW~GzI(hFW+a!JTo9W<-vhkc>-gMqaMzX% zWbcLlRUDIlg}XRAxqjBJiqE-H#-l;T3E?4_Ed_CsyfQ9aMNnWe!3e_9tdzC7%{7_?4d8XI?I;wJ*)V{^ zk7t8+Bm>}gPC9}1e}yO_ly{ngb*vOeMXpE9M(7UK3}wBW`~?21=InX2=jT8>IsdY- za+D$lejDWJOr$XZ-T!AQ{Le=`pzI>j3-{u&dBoL4K;y=^?Je#f0ZKfO^`8_u7(Z`z`cVY5)1ifh0p0xdDnC00&hs7Q>2e71KRI?na<^ zu+lWhDa}Vxjn)2bR$29ti(N-5W~pA5QlXTS`7h77$SDB#!_)GruX60m!9b+(He>BK z-0uMf^rZ%3Ai0VG?5naB9bg4=Et+GY0vUrsa2E9|lKvlpYH`Ie7vJ^&EcEwlKn;p%36N|qaZbu5tY556nk{kYtr z@)WSTC(*f>Tp7pDf^mRN9o6cPi})dFcxd>;u!P)Kg<%VQK+!MJH{_kwgyBuRhlMc1 zj~v6+L=8fWPDqBGpPH^LH8Sx}xm z`ht+%E2AiE zUihyHOPv98Mr`Jcd<8G*3FFBaegSX7NTU1v#QgV&1CQV_`W?hO>0;dk65H@D-DZ83 zN8EgVLjEeK{V&oZJ{eK9WihkD1uB5RZ9wa8AapN>(|%X8v8V-d0nUay%q1hBd8hIK z42321;rBa(@@-rmKnP0$xXlN7Q5-d zdYM}PhoK43MZ#Fm<@(Ena;_daHp1H_x<|49svemQvhN|_%lEW^#l%X z)8Gd}INQw0y~^qSigNd(fFCF8bOGdvjsB`R40;^a)b&Dwv_F@T*Qle#yo zh6j9uqOg#hLo`{5K{!3$CuXb+KIKQv$v{`2cwra7|IK7)7NK~I=;>wvKqH7_pq*Ci@1 zz>$p?b{d^*0j`R-xxOMpFOv6AH|n4G|1VHI5sbOGW6U3LuYAPaFdlthm9m4l;yc5_ z8gUj%!(VmfrpF6dlCPV?tkpYV1NUeM6@g{Yorc5ntaGAO{>A6rJrDa))qfSD3C%`S zTr!ANMZJF(u*&{KTqONJKE5+Kh|N2m9`w)4%2AG(vN>_)Fk<7cao8Pm{e9AsInVDE z_6m5f5K~w8nxVB1B=R1QMnBzE_bvBUf% z^(4QkI#S7vYFDFGl1NF;%x~E2==`vN3h;yXsT!NXE9?c!(xBk0(yub;|2D|p4qxJj z4)=#qdoheX)&E0a0li@WQD`%*PzZedFBR}BW(wf{T^Ic;Z}Dv9UkbppqyO0Pzwkdc z`de24)Boe*zs3JCVBjL6`DMIg|CRGk@A@0|PtpHir~bbQ`digd9@dOJoGC+p z&9yHCVrM08NW({V?x{NX1q}T)&C&b@>PxIDfO(Fo_?Zt?Q0SQ5Mvpkdo-E__y72b} zsay8QDQdn+G8n*h;x_q-4)OUi>sfBDoA<%!E!>~kxEi8}_vaGlf6d1N_^Ps1{b7P* zVE{{r_xGX7-QjAFq_1W;)iPVjX>~xuN)T}vW+LR@g(JdP(&aqO&zmS76oV72B~dLwVl0s1zIUY+Z77e#y{A(zBlXJ#@08ofiT>E293_1Fxk0Hzlq% z^p^tA(P;pEiMWTNW)F96fX_gE^#r!ee-QnBhriHY&Ol`VtcSZ|T;+!jqYs?{*RO)_ zwyEPx`~db_2ioLZN5%nez$Q-QB&hj$exttk8Ie*PLZW)>Kw~Ly{{%H4Q{?GVd@qR`k{w%I1u=)b$~Zj4`G58QGbJxKxSkZ`7k10F{})Hcuh|!a@;eDgA+Wyw`mCdxn!i-{ z0Wk>uEItZ=p5vIy^{e}pYAu#IXY3?cbZ^dl1Fni!II7`c|8V`RhDG)T{qvA_Qtyu? zs2?-&RBS}ozr~!da@-$7$d7%`-FDnn17AUBI*;CTh`a6=>ID~ZZFh&2mPS7)fu2>H z^Ag41OhTL5OAa6vbwdqcOVeO^n>gRXfAvmG&0bsApDHNO@3F>dHnOJuPCx)#IIA9Be2r}r1Qe;VZfoBvOM*S|mB4#El!dVjzH!pQrZ4^SN7IdTBupne#i zO(DpDH!wR6fgF6!0!Mfrr$8FEwEV9}Nh2fR2Iue!eCtgn`j_%w67)~$V*mJFD4G7` z&I1tc-D8dX4aeY8-qWWpitbHZ)q^U6wjh6FqHSsDrHK3sz<{#R0U>_Gc~E&BC=~6o zx$^aC7M$|>a`lx5F_d9aU8<^6tShv-uj=S$cgNqRRR}|*BFjmVm@Q@C{8&$hKE#bn zL6R%s&%rzt`ODYW5NwZPogd;NJt@Dcz4*lOfWdrUvllwV!hdxxd^Ml|Jm=#7J4D9P z{^iW~HE&hd`w_NKP}{Rzda?8W$B3?jvHvaDih1$XRWL68TmD~eew&Q22;7qs;Mth~ zaRAjJY~;6s{d{~^;7k~+%}|aZa<$r(-@vmI?7?+^RJUeT zJX(zYE5||6uvI^*D~@Xa`=I{UA$oWP1u#3jKLdAsZioJ=n<>RQ@(SlJ7h68Z*xmvC zKSMi^7O|6O-_QF$4yLLe;Wu7MF3PQr7wm<79Cy8)Bgv|T!X)rMvh_Dim-%OD8L)M))LD1)joiB$RBZAq1XYLQzlP4UlvLLyDhG2a! z5V#*}-iJid?fDy3G&doVX$7`M@!fu4i(CQP)DK9JyW;j%MC{7`KZ*X`+%qcTAI9%n zKY__$lxC&QrH|}9;w$@z;k(iNhWZSe7Vv?4{n}&`D&Qb2Nvu`OZSq(bVk-idDT_j& zsJ|Il)RVteZ(6@Yy=JeZqDC$UUWoi)et!PX;~p0x`Y*#uU(3b+jfnnRJVt*h0P5M- ziKE&J^y&-ViU;^CAc*|-NY1~xF7}^CjINr27%=WDmjf`*|ABwS|5EvZwH`a{T$Q@yh6PHI;dZktpZv9{}A_ga$S|jkV*KSUO*Sny>p{; zx8~eW;`&l$`@PVl;J@-s<5_k?}Hy50WbeXK~SZk^ppgw;@!$i4;WxAT=}_CRPGU*?}SZ7bM?MM{2o8_AaVZ})Ie%( zh4^goLgZsZdte|Bk0b!1+}cFQ?#0J%-VAo z4TA43f}>v!O~!Ru7oHT&o~&o>F#MPIFBk7!6_HiJf1CL;1lRgFR??Xu+BYuhlbZSm zF-CIY|9l`{abkv2M8H)*!>f&P2Z@NL^h_n#k82)0q}e;Y4AdXPUiS%55o8h}JZ z{mF0$q;>g$mm&*AmW+HYvQ%X0$V!nF*~+n%;p^8TUyUpPFL*BUAu7O1Y7DxQ<1dB6 zpBmi1M_+;j4*iob{pUu+ALK`WAnMlyfF7)#(#~6>uQw*nuEqO(1Gl}<^CetUrl5zi zN~(*>0zCTt;7^(Z*9hgeJ}dKExc=TiEzS@6r$iTy7k-N?;~aO|DXu5YMM=oLn1-L` z;5_8!BQvpMGGe|5{Ok@_S}6BzR*>Zl&Q51m;)S5bCDh)cZ+ith|Z2k7srAUnmve`PI7z+GNI!AVQ~pWP_nyegj1=@Tb zF@R}A*Z#Chj@wO>Irp5a^rOWZaSF3^>`vkCfKCwSoJt`b-CH^bq&&vDJ)CBL-@-RL!rz($V! zGC05>_+DIB21zf^!Ouz;>qPNu)6q27o(cHEhATLTCUyXU|l2_rasmBtp~6Hxz?l! z902z=5Id{aLpRQNRaEMXM5;IFM|E><6gIGQuI7Ve?m zb}3<6`N02D;9nJvtT=%3D$U^l>W4U(y`JPUC5wr<{ffxl9GM4U8J(-j<^ju(WxfBE z*)PDooR-M!D#*B#cW+&jG(X!|glgtCGi5^^^UuPUuE3pca_pq9hylnCAZ0f_d_97r zDf~YGgIEhc(=-mh(^6OVvL>;37WmXPuAsFX$MIZ+O<esBPDGvQF5&Ku* z4*8N?TLO9?)_{-oLqQmae?YaMBdH)6z&VQIda4FDxW@gsG;~JjryQ}dJntk}+A7q! z+o5UT4R3RGeF`sH2OCo7pS$D`wT2a9RVz>S`CZii-YD0@z!{tRXPH^WWE)*%@Q}ze z7qO?+7QKz9tS#%uSk{~Excu+Z-7hn4`by;b+vBhsj@m!lTSz^huzwrazYDMaUKjbF zik0g({J%)_FE_uB|G)A#tKDB7gdM0BP*|^cJ{7nx^pB1IFGl7E|I0;Ij;s+`JF)>= zy~sK|t_l+<1vkjX-(2Il7qV9$z)y-$k&qPpe@HwOACH1Z0`m&%;g3W_<3LSC%)vfC z)gA6@;;(w>sdr9#hn*2r&i%t0z78B4<03>=hpUR;dP7)scoB5r^xSRg2YDIfKMwy= zM~K5*lbXJ8pZibUEA&sST%9W}xvT6SSU@O@ARnuIUCz#E*5a*1)af|`HHiG1vA%cU z3=H8MM1%X&m|I}@Ka%gM52MBZAAkW(od2(en_S@6EU04fKGE?>R(ZeLO=xf3^`4CX zpRND$MrBIHQIV5R=SShuJn27@QT4Z!w8xY|65$IY<_zX zxvR_NE0eFSFA-7~{?gC=OZitXboD-!i=Z#hG{nXJHWzIk>#Dj4ST*5V)>+dk)}vK? z&-WJ)D`~3gKzM_Eg7OBco2p+epo#&1)85EcC_bWRUkc|D_CG>Z%?>sy#;?q(UK@qA zFEJ$Qq~}; zlef954s*S&<9WxSRkz@sQbVT#bRxPQ%+Z=2EB?3INOcOj6PCf8)cEznXFn1AUyA1cBfWe#5&i$lwuQKUJ3L@N(f+Z3`hONR;4kv_ zzWT4M{Xw{ZetR2y;S64YTR8q6!UIAAy1(#0383VR(G1e?H&=PSrRWh|$V*hfn~;;LzvQ5PLUhna%p|;oH|l$$r{QGGTl4NzFR$%Q z7x5P)Ud}S+~S-btHOX>`zn%pCNrCt%5)~Z?_>(HRO)gr{69pD&C>GgIuJV!)1^oZuT z{-V(L2ZR2avM`0!ToV^n+b!(x&zj%UDZs6q|GyrJK{e-F@M{(#n3i}23^MiGSG@gr z|H=QY575){^Ks*U>Hn5-w>iGS`hV3I>;RD!w_7(Kp|+|3=X0&utb2W_k4~Mg!4SxLp zE3a*@6RbB-CC3QY`R5(*zZCedEPn=8(Y&l5<%#v*!8g<1<$LPm)2I*ncgH{bIqz&G zca4sX^&PUK(MSUm~w(gvx1CfJyMo<49e^b z)~Iz{Nq54s!#+MirMnuQ6NcB@F~LdXfCj?NtGQZ_A}Bs@1$4S^xmWjbx1L1%-;7^( zBrGyR#J2FUWGi~XBNoD{&eCHt2X!q4VF2l1B*$QD!(bWtU?bak-4VoDjaYkMMZ;7- z|37g8ECdUzCrnCm!3RKp^~{^k_O=x-SVn~T2GMf|J4eIfq_s@CLH*v7VnuUCU7nm{0D z;YW^NNQn@K@cBmYf2j)QCzt@@EhL`Wi4J~`H8nRYc_lDi3UnLvXJzxd@X--vzbQPR zet-v50;@EIPl~m=00db;6PQ3;F3^&{Rd)ctuYV5^cmQWz9Kc*|u@wQE9Xy+JqRzhR zz$mY~R+BSM1CW9k;?PNTZCL{vqwi$PK~^UV*RgVt7Q0&XdYjneFqrZ+SKR<2%gW?3 z^>+piqQ{TIyiYw~X{x&v$KkNo}parl1@ z;x7j8HKkc|n%9H;TfzPl)LlFbNdY2O;)>|bl{JsEcnHR&8TVpbQd%lFW>w|!(QFR7 zc42=6j!gmB!s>c6EF~(x99O3Ko1b6Rdvfh03cmyM*~?wE0F)d8lXxHQ z(->_*&JF1c?f9OiHcsX@z5{W0@SJB^)1{xt31IVmld;m@cX8fso@=R7{f85^tD>Sl zT&fuL3n8fWzi}j^x#B+Hny-q^kqM{F0q&LWISzX`0%=jBCUV3RqAWG#b?2e*97hEV zMY%}#lQ=TM|1%to zgs?v0zx-vj$X0hG%8~DH0+GNpR#BT(I}a68wN~p`84nS$$bDW2Tx^CuK89?g^8Tvv zi*)`u_4u(4e_hD4oBF?$to!%){Z{e$JKX-u`1(8N|5Jzm9e4qx3H%QF%MBn0;6*+z zkRv$G_v|M}9XB|F$Nz8q0Qcw&sQkb00T35R3=>F?1K=g}{_-yFFG1a4CK$pap6@7= z1m?j=+M)edrwdRKe16%{08+pJ?nhi9cd-}rp8}S4B2umaN@{{m3D(b7h>voB7pYuE zG#-eld3CqJxIc+!)zKw}W7dn@iO}Eg1n*CsR>f#O?y;2Ihd%yGU)#vF@jctBz~}E^ zieGchF6P5(zSlcN?-1AIgRsZ*}#rrYzSM^^F*i1Rz)yw2AQW4v!Tajh{e+8+m+y9fOANy;Yne*@be6adY{deTK zvq?M`9!LMd{(tI&w;hy`(?F;#9YF5C6=0b8lOtTes=P2cZSroU3*)tLs{;{#5czjdoxhuH2OK~QU@52)oIx}aE+SX8c?JE?nn$2af_f6q z<1bbPRR0K2s3^E74|+#*`(B(A>&`or|9hjeYO-E)u)PjZe?`!$L?9a=H$YX#0c={Y zx&jDYj0qSUXwI{?b({PF>inpjz{en)dH`A7vI6KMc)0`3mPrt$#_ z|HDA0xbyMyavrtCo|dcLRpJGjU6atM;5)#$$-I{q>~Ss@+JET2x7qnT7Lx6m9fui8PV;_9QiUFLz{(Io2*J>u&p&mN?jE{h9^%) zA&ZUvYgkElz=>{!r2;8m0sZ}2zF_6P2YOzBPj6%uoX zXT|C4wsx!;W#IxL;RncmbmiKt1W!^Hsv_4|8{W|ncWf2}@L%q~lrW!s=pa>jF6I6Q z6Jcsj*bJ~=b;+uzirr&PTI#v*Fjkcc;D3Mcaye1N(Li_qbmY|4;kSaTqHl!*pfBD1 zCgS~zp+lcGRpCv?5fF_Fe=3h+(BqfE1f&k^z@dK(+*bwQP1ouFI=KBe$h{x6U^A5h zYsm-vh$65br@$4|fIH4N@DJL+Wq$Lr;|1}+`z*x#ukev0GC4H@|G*r!p+U^1>c17$ zedXB0V&H#%w2I8m18_4!{Q_4LI}Zj^8xl2@#p9qZc`u-K&8k{{ z7i;w*j?hR}e)agNNiItLjJvmGAptL3Bt`aM}K>V+|Tgwa0hpDQ+x4L@{ zbxM=&@^S8t{t_ObDGi5EY7%j0R=`s@1^l=eo|4tPHmKeelppD0|Jdk1maM<|{^kAp z;4%8w#lKLE)xIq3Ehp=GVsaOk$UWP76b6(XjL1imM*9WSRTM<6V_MIXBBHXW|~>8zDXH5r`9#G zI7dPHcA|**uKw>y|G&BaRa-D0Htgenkl(%HJTvOi*b3E5^)h4m7>WwiKj5Dg{;%L2 zu4nV}9oFmcC*H|^_DHz^O+da4-d=&%?d1q8=bjtHwe%`3kmFnd!-;AdfHbAJkIS*< zH3nDuvhvS{H~xx>7s~znF1&CP`bIwdAU)uN@_h{8+}GneQCI48VEN1Vb>2cRQYY+B zok}#3N8QL66hPshj%Vd{xXoLr7(;o-+g+c@FjTJixDk$m|6lU{I-nBfW52GWOs#k7 zaCi3YRoB~p8<_ABdVG0u!5(vA_R}wKJ<-fhtZ08w#jQy$1yMPh5yi_nKO2-5ip+!w z*sLGR@hi?Z#P0_BcMCjLm0(Z@u%Aowx17JCb8Tb6@97SwR}-fOBYs2vJ?O3b+4d1r zZAZah3uloNKzpD~D6j6rvA19b;Ya>r7n#)aF7j7LwK^c95ND`EWScY-}>IN(>ASR$* zfHu`t-SGAjC!S)hxdAF^vg@-w?cQQ&9RB z!x6X8U-<~#YyNUw7A^&PEF`D9Qa-q19iF`xTrmc=wg=a{X4~cljn#Kr^^|_r-#C)Z zRM4>y`pXlcZa{0n{;kBj$H}OM@ZL22$NF~J`_;UjlrCbw#P94ReqO-c)sLfIi*x)E zJTC*@&J^@=O3rnzsmR4(0gX5(13B)CS)r|u{zd!%ci;i?ZQUUMav0@qH5@&ft3mUd zbq^QEx0sgd(0{m&e&jkG&Gp?1r+x(-rnzA}4{`e)BU6?H=F=4pdo#2&f4h)pQYX>8 z+@JMO8aq3sZUEI_qwyC{$LF+wyK@2mYn}QpYGBUrD`W}gqSN(cwU95er^|{=gc;3- zC$8rTIfL87bJ2fcSc9Vf&a7M`TozEV|9m#zPc1eeZweq!aiPJZwe(4-S_#(bvx z?8G&99t9ww>z<#B8oHwR>nc5-v15Jy)#rCKd|-s@z%vju9?DnaVFR;Z0ZU;4KcfSP z0bB&pwOs)HkC6x14(hMuH#8GC=5eo}{pbeU;0^2f=^8%P!Vz|Y_2;PxOd6RE%|8)c z0?(r>Y;x#7itYoN^H&^)g0Mdq(SK^9|2xj_|07DqCurqOStZ{H_~WJbKOd-(dkFTc zB1&E4juGP?B2rTw*L2Q;Tz@Ta`U@Sbn#8)o>F&~_>|A%kfAxhqgE}W&$htqO?{*I& z2*nDTs3H|nIjn))*Pn3)HK%zil_>Gh=gRRaG4QXu;aOSB-=XGDYq@oInZb%a4K_BB zuRkIB?@t^k*S{(jtn-i1UlV~e1K?%emueQTfk1No8;<(*ZcoGfxZiuy2Pib&1A6(W z{qzoiz5;NF==TUJw|N2O;$O>=l%^nkT>62Iu)q18dDWdy?g5(ztpEMT#sho?U{eLG zGmup(}PVByr;~9x3 zv>DgLNay_=K;MUk#Q!hhRe7HCoei8X%eCDM4$ztFyeprZlS3-Qr0o6dad}q$^gNsL zOz-nv7P!6K$yL4;4)YDMz!-Fm#>5`4a4jYbzf6?5%EgCGh%!lDzWQ%b zzBGA@^sTCY7{6(Cepcl-6$D?%^ZMw&-Sy+!=QKrK))3Tzxs1Rfn{`wUbb2`3N(h+^GuN_<)qr^1mL%u?$kqOB;%J$B@-y_3LEu*# z@T(}!wd;617r6dE)o}F}!sC~Rn!ZAsN*TH3vdrDq6i~V&RRF7@wug)5(tl0?To(s3?#(AsC{gxf%PlSqok8^)5 z^akATQD`JQAUCYH2CKC?>PiRL0>hA->m0{j6}o%TVTAslaV7ZZU(9*_Zc)Rqmn%*6 zda9mmL;SD#)_JMh_h0}IL(-EC7)OpNF)U{USba7u8D~b5ygRTqj3EY8UEWv37VB9v z)ip#b>S6N2+qqM}LW^#X3n?}0$069y0@jLPx9G*ZKke;d_VWSRkX%l~tjfyvzFWnl z{#)kXoY^x2UMewyIW7;df>r8I^zRg`o>jn?0c4wF=q0)?;#U-O^A;tbx;Pnq_;bSp zqy?xFuocSt2Pgtv>ATmJn!pZxMDh4Tsso48t4E#x7s3IyxXM7Q0o;dQZwvS??2kb$ zn8@#qfJ3NX@GQ1T)D;e(%Agxl13TjCALLCyWB38JKskcjXar|K{~a)cMW_vHuaIdXWF=!`6#U z@&6DIRQ0nrfh8&y`vC29U%1j+o#As?dwbl6mA%r$=Nw!& z#o;}b+1?D~fePVeeE`yT;(84UzZF;~b8r>^$HfC9c^A=a6Id69uy1X7$2GaaUu1oG zz_q-dwPhd*Wl7e=o1p3sAnO#nCw%q3ibnv!}EEtzlLF z6%^jbwu47M6OqeTz9_&1l$%$_o*?QA@8xRt)p@_JUmE>PqEb2jz6+rEW@7wZpvF;V z*8hob{S+MuPx1esfv-+cM}3r^?{PT+uHe(Nu~&fE))aD8V= zbAL8>asLP~XR?c^W^!~CeFiU*jHMZu+BhB5L$EX$8gN8|DW`JbNa1^H>*BY-S&ma*EkZIZ?E~jQe>@4 zCy4$L=(7Ltd;9sD)vUMEc-|-xpn?$jeEB05`U$Zdwp(8EhJ*lgO@?KqF0Cmth^3&r-{U-8J zYcdIrPyl`tgU2r+ocs+|o*u*$Q()z*h?JzYs>=ETYpdq)sxN;CJ{>u^|6pyI4<@xD z8qI^B^E}TXci=j-5cMJ0NIbb8u5%SNBqMjdV*keAS68=csqSwAG3^{)X#vk;Jyo;< zC?}xaYD?LJ&8(H@ofEJSS;eNrnY~dRr7A8#4frKu4?g-6XzJJS)g{9dm<0b}6+UpZ%hc?Fj7)k32F*&zKy@Oubo-`%l?59r_1 z1S~HF!^lG%pBC>yHhK+Jr_b*|u>M=LfIZ|B_TV7gfa_o(>|q?Q(2-Xy&)%fL^>-By z)Iqe1_2e*Sk~8g#R^9}ptQZUbH6b^gBkQBToM>{R+XOpl|KkJe;=AC#=0gh|R9B)( zFsW&(2u+D*s+myNov&sq*UA)D&5p49a$G~%SUZ!E6H@n(9D(eX@=UF{#>D_ufCSgW z(h_aoT_;lJ;tkBk47@we%JR(HSA#p?Dqs{4Nu`=tUH{$Bw1tplL-05bfSKi~|T>fiQ* zxbpa`=58mC54)dP=DL{}(d|dIO%uVEml>0iNe3Q}Q&i;x*7uQxRVU z7b6|FyWsXl&!zr6I={mI*WWEI#zQ+RU+(6c>1ZN>H; zUv+j&dMH=M6sMZ0(m}q2Iow0zxxc0oP4DDADKjE&qvIw1z7{q)0Q4^o`rn81eb2hm z2CXwU*F{pUj!Y<@b%}V!v9@d__76eldy`ij0q2)0lnGTswYZ;fEsul;^a!xo*Bs-^ z>QsPS@Si+f0a=+9n1RSYDcAO&>|0B&)T^lazp^P)D!nl$S8;XLhF0z!s$X#z-e*gW zKy`kS7o@ny+VTU}a$hj6G|^31*m+jSbxzs*kag`HA{lknD@8O{5$B(E{TbvG-S1cj zx3i|60$Z=}aTa&?5fEk@JU}t#7hw7%P)5CfRR5>CepU4=##fJEsq_nZG|z2c@E6PY zyESkK#q65`%;I;D{1mJAMLP6c;o~BY{jdHZyLuK@;BRO7O8aty?V8)C^X_-A;Jyxr zfxQ4`*L2t~514xNr~-c;ENwoY{Yib%MB~DL&Fk$Ai*HXPsT@F6&g5$Wv@Zst7GQh% zf87eXJaHw<40~*2x+Pay3pAh$t3z>lDPtpGZ7yWM|-+j(${^6+KnH~0* z;`!c&RdgY{Gs2<&a#WceoX3Ac!*KuQ{~y-=1Z>N34gZFto}o#pNJ>(YDTxrR3MEpK zA(A0w3M)gx$`~n1SjiA6vdAh*MH!YU6%~mjBFT`73W<`^`}>{8eRV&@TL16=z3+By z&vZZccwOgt?EA4F6E^d7&O%@Ifz#~NJj}??Pe3qtdLGBe?4e~T%YA1)*`csjEwkQP z*yiy4i>bH@-``rjiCoKv(GxDu;eQRfTKHSOWO|X zZ(}RJr)GIq?5UH->_MA(q@#!mOeD)_TJd{y9Qii!fF06oy(S=bYx?URzvXD3dh|P! z@9$I$48(&UkvjH1#?OcG^#7S+1~DP{AMk_=LGTVKf3Ggxd$uq8Gtd7io%-H`bF40| zNZ&y@Kz}j}#-JKZOvjj{9SjEld&4=L^;@p&YSfBZ`2Ej;n-38&y&e{m_4UjA|J^vB z70}6lgI9f>&`Pa5aB>PZ{V}-Got(L=u#sni1|7i$d-xvBDw9pCsM=Ao7*1{9?H}Of z3s|dTu^(5GlW{ouQiZ}lur|v$RuJ#8Yxu4(f=1Yi--joL>8O~6dkOpfqqT#i>>cyiBDXK z>+AFbXCHn64lm4|5%E7d|LFN%2dW3g+tbO;4o=GI0vcYDFws6wc6>M;9mx3^*Ps~O z0oFeXraPNJ?SEuiEw{6io`qq2!{=Ydt9kaLd(t!L4BeCOU#jREe37TIAm^d@9|!}e zo8q1)6#kv{Fb@CXOMIGLv1Fa$8V|x#=J5%t!0tMxdR*&1u21^UxqQy!h;%d|*P$M2 z$1bVjp%Biqo^BpL^QdKTkrBLZJz|ElQG6f4{*A_leSiu^GgjgN=NK{3d(s*tO?kytym*tuH8kKL|dF z|2rjbIcwgYxuvkO8^GM}@vi2l3UhUUQK0>Op#I$`0Y>+Sp$v>CY9BiN3~)ikAoTl} zxi#|q-va&Lf=7G+UO9{BTXxn;I-vauuCL~|RUpYP{N>+kc+KoslWzSzy&t>y`mQz? zl!BS>i1ls+?wp36e=W#6H2IwN-}lJ+&S%ql^7tP)04e~dfCBafaH4Kg(7!QSjy-}J za9!(hbxR)gxXu6AOXwgp!It2MIfG`p#q7YVVO)Jdh~NO`3t8{>H0WgofW2;4Z0YSL z{%6zbvY$r#^9n%d;a0$j{+rOR<$+Up#=GDeU3mR{VV}Qm!T;ycTI~CC1L%Jao_bTf z*3HCu-X)X&QBeMR7{K{-^EnIbzkurgE12ke2Oa%}@!x-?jL^yC`oDu-U{|Q$$qaFt z(mwQAY5@+O3IeJT4dTi_2E%(1taYZ+3KW=%WdAkBmo}4q5a|CDo>bI4IOp#(KDQHp zov`a9z|Z-8C6?1k8OFGe=WuSP8Cm;bn`>g5MgO0$$_ruC#$>J4iBST){r}Hd zO>eQntb3Gsjzg1plUvtQ0eS#e=Nx>sV1)8O#MJ3cJk>d zd)fS@yE%s7q2%@dlK#I8Ao2pN2~^iKiuolLetWu>L|nBk5yuO#JJ%8wxD7AlK@e#o zioiP{^mlXzD2v}+mkhy!@Y)X{2k&sS_#@~RbTpWJGPr(8((q#{;2^lbZ7BMEbBFVK zu0Sg|1Ln|_Jj0#v;D2PkkaK?Rb!OMosc89|imQ{`cLMk9P4?k!^tGP?eq z19vzSwZP8)Z}5mGh?x&TySSccsJ;EnkZ--k|F4iH*eh=sT+3}|l7P2~(8l^b%n_*j<@u^m0x96kKJ&9$%nDx~V%d#Hi{{(wE18)5> zmeYIb4*uir~n66ESj@9-Dyjqy z9s>TK3NjwW{@Dq}{5$W)_Y&TCFe0^t$>8=dc11sw{a!GD%Q$)_ymwx67qDKBzdQMN z#s#!5&UG>?_fqbC15tG|bcW}Sp{Xl$KfaG)=PZH&Ead&Z!mGT%UYf?vyPTC%9k2Qu ze7M)JZiAEk)ptAr?71MVg$af4^8d|+J7U9H!$q!V&pyq2{R}*>#@aXr#&>VX*%07w|PAF5kAvF_Q!g)Q%bIqJNfOZpEF4-CfiGw#I+;|Lb!8e^dgVz?o^8q5yjT8T#)5rtP-Xu?LJm z{LeZF9?Gk?fIAo&?7|toglDy5V060`{iEK^S%6liJL|@2w`IV$8sMBgsw4Uz%72yx zVCU+{P}1|a0xl+3eg+o4#OHZ{Ptb$cY>Gd!19nRPU;RIL-HouibIA2L9NoG+>i;K1 z^CyDuL(AL-1Goy*zm%`5$OycVD*qv6M)1E0DdTT0mF-se{Y-zKa^!{Vf#!WE3Q-62 zq6@*o8h!|Ak!XA7Ew0N&RoeJL(SqvBrNKJk^Rv>!j_~YZt?ntk^FT18^FG z{(os!ZWH3??a-~nf0d59$uldH*6=czLpBM~eC!86-|JFjwhb%UIhLi!Zdn2YcoW__ zHt9`IrnNYO)%8Q_t$zg8JfgPOuxBshDyjLj;oJrG|IAvQ!{-?Z|863tm~*)R78}|PR5l%{{}Fj4%n_M@Kx?ixF4O%TxaJF18GpH~2k3^;i;A^{8V%)ZCEmxYg*Th$r_wI}VlSfMKuMm>aY`VzkOr)e%w zxipP;SJZ$#sKDQcT*1b0fg_o>cM=TX0xjN+7mC(Nn_;ubahT3ox?BXbXJDgWJn3})(Fo#V{KAB4A zvr+iZcP6Fuij*aPI`7>2_{{&Wk#urB9YLCmVPd+G$%ht_RAx@2U!9>NMb2QK+47WfFz zVJz5L9rZeL|8If0j40v#+xbWRU+wn>&VBU!jjF%!{;m3R;*TD$vHwM&VwU}vae&Bs z%PRocOx^7NnM&|Fh~;frW%L{Dq{n3fjmQ4nSA%aZgqgHXHfPtvJ>1p-|?yitW%>_XK=LTw_Q?>yYK8f zRfqIvuzG5t)h$ZCh)U!`pv`r77UnqJjlG+MxBUqgZgXKZ z)>l(rsVnbk0372fqH^=t^95Li1K~9tsRXb$RFA|0y1@zBvu+xte8*p4?X$5cc6WRo zF8eaO{90CT*`jyhrJe9N{|45af$hH$W%ui^IwRb`+Y+HiV0_{U}blJ0sM|V zUBZ25vJ<9&0ApYbBXi8*-`w&RcyIsnwbX)@qVvG7AiI%+&(RV-;(zb)D!~B4|Nn&B z7xRd3*;6Z%@4qT30e{B*>=yAqruWn)q~mE>DNQ*%Qc0fM$4vjo=g_d_B;=2Dn!8aB5KC|6aUXvl03SjD z7zhWrnQXtC`PrYC|9znU7}SB8Apc5Y{e`(%O2+z|ll|U-9600sW`^F&_5OF_d9%5m z#@faHB34OF&WgEzXYlE70i~b7`+qf|zrAd;dB-u`IA&zn2Vo_)b0_R^TP$;XEVKPP z_vVaLrknP980^wylV|Wu6IsDdLA;-HYF758oa=UQuLi{5N}~aM3DdM{)R@!b98aPK zOobJE$rQ}WT&c!fSCyLR_Rx&zjZ+iK!<@cOj~ok=y^YUkpDAlw`{4f@(Xz&L1lT_t z&+jl^bu{m(9)9`tVE+?2t=+C<=0+{W20DqS2>h>sHQyf#<`j63L-;*<)KrBZl%k9H zJUsOw>}~s&+PB2M`x!m_2G0Hd7TcyUlgxF2e98xY37eq zp$qvQAo78U0kj6&JK>>sPnm+3f%v_d(svzSy~z~3gxkBo5st=x--n!n3UupTQ@j+s ze}~GwXJ7#TLIs$NqCYL^4d0+Rd{15C&m2GU)8j{eUqfEPf`tB$k+E!W`84H|w`5P25AoIPI{%-9{*S_!@_Bz*&z~dFQ|tlP6uw<6Mbdu4QZD67 zW*QSW=b|%BNQ&thtdJcFSFko-W=|RIbR9fF2GmUU+8+g#P#qhxny!M4m*ooH$?LDD z#``GF>fcc;?tr~KmW%!Wn@=zL+xg$>ziYw&ZD;S7rfb5s_)&W8i;(a{6!L=*!WMFDEB?w;`d8<)IVV5&lJ7ES$!7dAH?(B$g>V$ z4!IHK&OB!4n77ADoX<1fz*@YI_xd`Ve--C`XJY(KS;tO=zJMKg0eLMa@wfQDUGWO` z>l7G+JbyIr;TeARN42O`^ikn$tT!v3YJxil@%tIrBRi_z4IYdFWy~Lbo_Ooa>C36> z#$D%Ng+HJV*?VLqzC+%)w)Dxu`wDNyi!#S$zm!My2Wb60XuX{8&dB{81h1I#8Uy{R zU@KJsmgeHjFXL@Z*b?J^CZYcV{=0@pmSyM27|Oveen*A$Q^CE=`gB zcR89(*M$Dg{yh!G-wr+4UI3W_P!Hd_CJdkk-!l~;@Bh~aRqFE9fNOCW*m4~2z#%b-?+0f8qi89C)L|dGx=X>uv>n zQ(k{(m|2GZQ^37@@mw#Xlf#i@tyiJ*@7Kis|3jyTr@;S5VE_-7xgX3Q%GdoI4{_Tw zbb)x6y7ook^x#ba_!@QMgJa-Y?;kLrJ(dG|iR}W%` zPC<{Io0K|j*b+RYW#Fux%vAuYrr3WYtXV_=4kQx4Pm1%|*S!+RwT#Ex*Yjrl6>FD{ zhW#|*IE<_Ur^=pyZhaZnV-N`cIB0I&v>jPTgYgmde*oXYBhvuvtZr|$IE-#SmD2$h z!cBh8y{}!!HL`n1lVpR;RJ)y6z!-4)eHg$p{PrK1Kf0l~fDXL75E0lLU%e@PgIZHF z*q%thX|Mn%2VDUNxH@_M7x4FK@Png?7aT-gK`kl`3-IHAh5;-B@82Mf|4cd}5yt*6G zS#?cW^=B5%5!hsV{xxQwRW2%BWM0W(^#Rxa-$X_K zi5g_4(=hbXci@cc(1L2R<}U*eDi@s#^ZJO+3;UzxpM_1o0ao=8@r(GpdGxpX@5bc) z$IM^-|Mp~tn+I;yU!ecizV6H|=pT7+dG9}dlL43kXeE180A!57M}`7m#-F$y*k6DK z72WKsfwz?t11N?AWNLt2l&pG>sk~yh5&ot8T+ZPdFR3163WDsxYy!0d6@>NtZCC%_ z_{&&!uz)OI&`3l+Tfpc3o_lSivrR*osrcU!mRg7RXO+7#fXK(oG?6_1-wrlyn5{zImp2}ua_{wPSbrN@P$t#B1a3U<= zO!SE8IXIeW!1nO}g9@Fxcm>DtDO~}_vH}`lHP*sqr?P8@CS76(``jKMcN4K_LNtE} z2xWhg?Lp!)SiOB&e@?G+X1(3j%sd;2*J=N8dC7R*ozqoZ<6mKIzvg?)JfDLqFh1u+ zcBNwaa9BWX)PQni36|z4L}lNh1o~HJFI41p183evYkgx&Y<~(n}=~C|1G%LIs$UV*qyhv-aQ0fAjeBl>pzuu)l>3getI-xIjb&)?>%@HFw1` zKM2I_kQDdJ@CN&`W9_>*5`-9C0{^FQt-o(~KbUZ?D=eoPkC#A#qGz_7PWQ zBA&|KXxNt!u|9&Fz0yqn{fs$v6Y=n$+;Tj{Uv_`=mVfiVSLgw?gxa>6DE;lp8FcpZ zH7MYH@rcdrxE~~Z5{5S&_47-(+)s%eniE}xRikfxM7kPhfc{tV-t2!h2F#wx%CnRG zdUl8NcuJ)_E4!?^7M-xNsjwoS@`%L7JHgYBWOp9GC*L{ESXoQl;Bz>{+nl`@u*>6k z)+dvSWgh21&he#*!R`-3D~*oz8QdiLMvMmkA4dsr0_1Dt^(}$b{y=1D9q;@PIAj@C z#{bXrfZ-`JARlmkyEOs!SX<3IE8vxP!qYH1 z(Go;G6STUXtn^30=o$1L{SwUno{oF#&;Tmq$?plCs{oh@&;oT|7SItT;B2Y`df@l> z;AaX8gKttg2Qh zD@SlNPsne--$Rr4Uktm9Ng6ZRmkY4qi&<$4QA}sESD(gy_QtN(XO(=znwrd78b&1O zR*r$_0OQzcpRs;+LXEwWs9sGx$EUE|WjUv9Iir6^Rp}3d@$;1Q{qIB-xP|{^wSQ+2 z<+h)J(O+{z%Hn~qNxpK)Sa(VM|EnDOtOC$V_L6!)_PD4=`8wgY5&t4yr!1JiBkH=H zcCDf)pKkN^vT%d#LEOxz4`n|e-#2gYSJqKX=Zi=9m{1VZ0x}-3p5IErb2n{a1+oBR z`O8_0<{4xWgZODJ;&$wt`g8zj$8i*Aa}U^wGxs73PzAt_2y>sjNmMkf=)1o zx>&Epp#5vu^9dmT3t;OzN%1d*y|RisYPRK>PA@UbWZyJ3ssU@RF}EDUiadwCekVKl zWfa4;Fvgu><8|4MyK+U;{%7Lz-^Z@+!>jj2cd+8e$yFV&pW|Vr2hr(dE!IAi*YZ@S z)P)C_iIyqQ-SO)$BQDVoJT&Y1HP-YZ*zMP>_+=c%2$m9+Rmm8Sr5=ngc>$hV8$9X* zSo3P$JM+BN_?}n3^Z5S>nEe(zAm6vw8N*uG^;p(CYY6-=2Awuyc~^tzi^2H?FzR=C zeAMy3nW_P@dI0T-W@7%qJ z{!ieyG2CYT-%$KU=ds)bU$~Mp6kUKb^v}lt>;@c}fL8z6VXi5EH_sjMA8P~}^K}Tv z;oRRkfS7b+N1)3&`}PPK!F!nqUyS*GAAp8l1*g_#diWY5kL%$8WzYdCQHf+Uz!?B> zIIqTeb?e~_M!}u^`zXkFD_pNT?B@usR&_YmMzC!$SLHQy>WA@CuAw%+16BGvQL(<9 zDt^1xKgID3Kc6QO@CvthyoBHXDzSkD+-rLx{YTT)&YFI6K%W3Toz7$*h*!AUbBPMA z;2LZ$C=cq_z>9XCVk17k(-lriyvJ211C4C^SFx<0vEHn8G^$+=p5ts|`>gu;H>L{6 zz1Uf1xnIOi>jLsce0FaXk;#mVUR|>B45J;VOB8Oq*r&yK2J!GOthnKae|*|MpKb29Q5O3os%en*YYS&d2x78~lT;(&7^Lk4I(-LOjk# z{!s$|qs~BWAXtFU_zNmQwtIf#-eq`BGiU3ghB(1d@4qtKL`IVJc(4zjS^0tgBN93e z<(dyk96z9PKr?87{rZtr6H~)pK$ZLm&3`R^|L&lBi}WnAiDOxJ=9pTAUjWbi5sNS8 ze$UrRtbH*a&tWJJz1i6>@L3iU6)@ZXI~e3__W4Nsfh&_GxD2jv6Yu2iqT9$N*u7{T ztFj%c(+^m=smu@gFzK*Af{SJF#q58k7Tpvd_87G2^T72%WGUED)D^xs#{pJiQ-9*U zIO$>z*{iO0vjQ$>#h=8AJ{)^jKc|tZerNiB=Kp_+-3$%?eSG&%c@HbVpAAW4-Jt|) zux_?2v1#%DSA53rz^G5R;D6KyypmJ|^@BGPCy*By9mwkfS%$!BuqPrCR^S}KyENxd z-NURur^<)#{}h-T+DtzCKfM2Y&DDfgmE<6?A|a z=-;(al6D3GD-my>w7655U=np%^TNv(ASFy@%bF37CU3GG(@2 zfUkB0e56w0hUC+~%X%_D#V*KM_X)dBKhGI@8Fug*Uv`$7oLIucC>#T^>UO>Q51;&N zcx*XP-zml|Q#Zj&xtdehxkq9d9!F#SkXZjJcI?(F=yv!z8Tz#<^;M zgZOzMOkjWb!2bMx=oUS|8vn-JR=;c=)Pfr1_U+8~U2}(jS0#>6k$Aucw1CA_1Wf0Z z{te%-BmS_Q65u?XTj=k52Q1)@gp+;o{-Z`-FF8a1JpMNX9rTztVmGbRng%LP;9L#o z99{$Z+wH$)QhKc|(ZB16Wp2T`s>?q61#9>Y*sr2AG40UEQvEjVw5jmRVOWZ;Ski{5 zq8q^a4_Px~*j%(}Zn~L^GeO%9mw_FW_8IorHW$53J zPk2MB{I~MYdBxGoU_Vwyb#Tr;Mr)FH9GPxL%EQ;qW4ZZ#Y5@O@1N>DzfcS6j-nSsG z>eP1l)6Uq-Punr;FsANR;{Pp>)-A#&_8>8WD^(@D3#bleP;7|#gvSv;M^Lz0;*JG1MtH!E;B{R*v%|5@UGa@?=M|sk72g8uooTrkk8Cy5MSjJqu7sB? z<{iFUeDL0`P7?l1?dq8gH8VE$VjY`z>I{Qvc!_?HR8#eVDeR)XzDSM84xaq^5a z_=w`9|NqW?)}R18>|CRdatl%Hvs2x_ng5GY?%%U` z{x47iFo&K#pX2SX(uckB-<8NxXp&ZjmHWfC@Z4*t}4tTNHR3S3xL;|#uP0yg4&dia?73L|W&<{crJEUVch=3EpP%5;YFBz{Dz(w4^?ugvWn8H&pVb^y&&G@F zkH>*qRr?1I(%3GP1^kM2Jp z*E1-{V)|1lZ%?g%G8qQZFEu))X8xWGz^N>e4G{VM(G4g+Sem9%&HmT-KZu|E!UpPc)Zxp@fPHhv z-rR3D(0*s~1uKyEUje_r8Vq1JbbvkipU0j&Y8P0*cJu@IhS!+RYmI?%{EH0vNAURX zAp_xNq7XOmbv^O%n2eX@$j4kfegDk=H~zmr=W-X$>~HAE3;AqD|E>8Sg5TDYSc=}? zp&U&?f>W@h-9Uxo@&E0jRao>Be0)aXv#eD+bK0wYAZJqVV(GiziNt8q81{+(RKmDG4OM9)7d@hWow|49^J2;T?7ZJha& zWi#sgJHzN;R)bz{C00qPRO$X5{(px5S+;vg{P*|&#sU6U@Bb_VU|m|1B5qy**eUf< z6no7F@TWW=;{wH?bZGec@EL!|s|DF_;ydou#IX33Fb5Z~n?zKg0!CzB)B z;`cH{!M{ZtTg@ZAM;yKjvjCQ%WxUThu>S9vv|}DkO!q;q{2j!7E(25b9}WdCt)j-y{2YFl@3Ca>Ru=cce9Fa;&snSI;c5{b}YCQK6@3nHN^_f#@B5@w09B~&8|Cx$o3kAHuW+} zrde65k_Nvc-iy=k?8J60KAF={CgL&K1MNLtb!ozzpQ(HKnHB#PpJgiV#;o#uH}vL7 z;Wow{9KarjMbujT4B~%>#U|pGC$@vrgk0D}k7gu8;*l&GvJz`2x18LraIS-9tl;)a< z``uws<~Ur7&T$JKc%FTY0sEiBuX+=U`Y{pywV=OUj%%|^Vxm#R$eN*~pUJAe1HV^= z`UyPNC-789@b}&DUGr|vWc{~c*U18$dr%Dxp)7mWNp0&`xyG>O@k*1qx_46O*_jKjP8fR$a$CqDw-*OS}= zCwZF3@EWSakHmOS$HRCH^j}SuQmHLoi#|#m1z2$Nl(pae#nE zuyS_#(BHFi&93HJvsZF@ZprTLq4s9IQq1iN-Pif=kquCi2jnNKJ%t~fbq6p9;PHh2 zvr#R6!lKq9+8p@bl?o)g1Kvjb{}B-R#gqZCtoT=A|CPY-4CnXBVSjCKeiy!KgXbP4 zmHx6Q{eO@(_(!q*g?FHiuU3u$?3pqGc0~i&7XKgPRqqEKt%Xxn|t);xRRRx zQ2)2Z|K^}(BeWFzWp1J0t>$@ zE9fV7;S5%eliJ5)8Q)=#ev1-k7l6<4*v5hXMwhN(Z+E~F)`SUvkKLNgnzWg_P>uVS#@|1X2#lY=nKwA zLAqCH64B>*dBXp<*@=_6;$vCq|H5Z}Fy#RlL%R^{Y?U@hT~|~(%z|VRG-Yc5&h^Qp1%>V z=&S*&2`=O3iQxZ!)a0+jLQMse24X8ap_uN0|7v!fJ<9jRf9#3nd=cAUwrC$7(Vpip z(?IRQPH5Jvv`rk~Ot@=%m`VAf&#`qU75;*~>I(<$MqO5Sva|bR%SL1WXX7caO!@c5 z0-`5QOcsl}IJ>VKx0uTsw;TFutmsc@0LGT}rLt*g&O@;$ZZoWYBi^YTpf;!=qL+pP znA5)=G%kgAUy<9@8yc}o4(0w?20^I*X8-B^d;G+Ev#+n++TJ6pW-18(5_mrj{C8%J zo>@%zkq1m-uc-*wP3SG|{|S$^`^NUX;?5}oU{!aQ6m5@Qv({qR>G$z;$OQ6nfUyZX z?f7~f8pnBHSmyWVk-mFE{6KyEeW&~Na#%CytjDg&`xW`kwTOAZeG(cD z0HN;%H=+vh<>dc6Mf_vFqVwH6elAFV%>}>w`iSdcHN5L-JuJY?K(Rljk-wAJ*?6w} z!{D5;e<#)2v$h`Vs0@s3F=zA@&SS}*^#`TCKg-H2#OI#{_RpmXa3wYU1yuJ{q382X z1(o@&JU>PMb3w>|qqOM#?!$Grr$q-==&>o*aWuYlYtHs5eD3o&1HC|7S6Q3-5L&vdN#f4J2g zuEF?mSEQ`ClarQbUf_Q0(R%E+n(XDQJJ3?j=9{ctBLTg*D)t4g&Kh0Kig^>C{xR_W z4t9$(D&zp!EGjGeM)2RUth4DvJC?y@cTS%FIf*kr%=wnTy~|E1#bm!WaFVgurB$h? zZ^`Iq`2Tio`LzW42Ls6ZtY`zvV(0a+%(vE?Sx+ADVlZPqw(dh%fPGeH=d5*fY>W7B z)OAI7*L?ppxxltOAe*cf%76^OO2NlKrpN$TfcNxUQAMonVd($u@j*{Vi9R1(`UePX zy!sik^k$K>{~a7)d%XD_!ST@ZRq}Vj$B(a__~~y|K>Bhy$p}d$fwg;iwUV;^kNpR6cyA90kJp8gQT*D@;pc-&&v;XG90A4_$iyl>G zs;puEeaFsxfi>*}uYv5gYhk;`qBmA4Tu+Yj8(69VC}rKr6`hC;Y=J#_o_$&e+kGq7 z<4c&~?x}-K#8!Ia^Lxa+A9EQanj!ihLM$jE+-A6MB65`F#zpi$-|cp5Iw#wF#acCoCaX~>!+-SuKjjlm2md1yX6N1! z3H@&ctz!!C5!jo(ljdI=YoX4+GgsX?>Sk5%o)pFHKxa{0A7xWv=&oj9RpbBlczg?Z zgtG(1|I=ZP9aAkqbFjW1kGD68T+vFdio`LS^M7Xz$rmo-_am`YyP#Pv0c{`0M>-pu zS`FR)Lsrn_f@krxUdPv3PlwW@!H-c`nHBhcRe6W|@<_7Mutz^Y727%0bjcMqi0XPrUdicnN&p3dYfmPWH9##3hI%@o0SlmN-trpz7Ik91T zX4mBZs{fhyAO8P$Wby?APy=`uyK5xYD#DmFIFjsjRoRROJd=wIs0LVXzL+?7r~uV@ zje~eSC;ePP)|v74yJ74@!K2~$`ep*!b1$C_1vsu4j=Q{g1b>(wsx&Ir{d=`LoZ_o3Yuz+XS*AK!1ZiXvd#3SX!G20`uc^k7+YQwSZbfqHq zE>_#Rt)8%yrn$VW57CjHK=&Vj&*qHEo8STc`73g`MzGSxvmfUZnJUFvZdlj~J?C;( zh?yG``Bc^~u7u(53BSLDck?Fp-5h0oolG4KOQ^52kx%e#i8?76KwbqfYHHS$s;J%8 zvU+B_i)T|+tcm)Ha}bZrbYS^GHbreZj46(iG2n~`s0qX|Aw?970*nS1o!=fkiav#N zY$&P>M%kO_Z?FKBfK#bJ?t%AxGyQgk!vX$7Ccp>8>SY1?^QFs{1;IVa(ib56-M`H5 z+m3#HKT|chg35t!_*%|yE6Mp=2M_pzUIH8W{YUBw?FKlDeuMV(9f2x75YBNu*m!kP z#YOx}!ThW@npo*ZhcUqEN9WVF!MP#_WYZW)Bg3pISUs3eI-9&hq+?yy1W8wuBtSP-S$uRv#Wd%KYOwEo00EYShyUk zH3?tq2D(IC2Vef8peEkfK%zsx7w(7tJA?@U-;x_whm8LAaI0>qzB@iqV81-(I3fgA z{53?ws1Cjv?KX2H>$nrS|0Sp0l*IHu|DN%~>BI%{|IH@<*uB?d9vXlQU?pC4hT;Eb z{I3D}R!VC&YWqs6{}C5Z-OsWC1K}&8hebpz_J-g6PZnPYWg)aM<6--tM>XPWKeA)& zfK;mZYjWcka3-yis|7=-nchVi&XHY_vuw|p++)4xFqFAKfBj^0hwPb$9Lg4ojkx+ITgTe zY-?bVD`5{igV?RmD=*!m7qnvS)kdpaiQRt+bm)Y2*-UJ83?9fe#E;I$d%vOJ!Gag? zhRYWoi3j;G_GSTg-)??Y@o?%TCZNw_E_C&x9eH2t$TfKiJ6yVOA~D`$u;5kb&bkLZ z{LkR;yI}=06aJS6f33=DhGN@+6?z7Up{Htv;6QNX32gRDAkPB$$+sL!K%e*E1kaJB zFcR-o-RojvTPJ}9`FgmlC$5Tc{%8ndi2SYb6x;CgaI|836gV@ZG9qYHp_fPS{2>}Myw<^6wA?GljgN{&m@Z|1UP{=TRmhn?dthpS!8qvKEk zkGa47uoHIragy8P3HwFGiKz#X9`Wm(+w2IOd ze94^0)n$HTZfzZE0<7(CO>cna^aebFpU(2>gO5A~@1`89^mv~C8qU&P$@bmED+cH3 z0kVeQZ>42;{S$b1uO>ao%n~)Fh%BuuER9xGjXih(yQmGj$4OlF+#Ce3SCSdIUbd{g7`+d0V&T(0?SJ+@ys5Ke7XzTXY)N*4o8~ zc@}Fnj0rENcA+*5ygRz`)4Xr{=wyC;Rcu-XY@3)K(|xQ@vQLVY%}cRgQ8gVMQ`B9} zf)e|!r~U#R(;ROzv%bS7n+I*z?_ce;L3TaFi2s-oNv4E8z=%$7gw;yeac1p5y8aCxb-&_cVB-HUG|$IDqdh@!qWM zQvKf-Tk5pG?NUBTWRrXg>y~%_0Q#?C=S{^<3}hwrV=dTg;zoAFG3<;=g+F1nUS-`3 zW+nB$%Jqxer&hW280PviVn0LczVuQ&lV%GM1qVSRS5#Wd0dREC+V~gtxU2?l%WJ z`x(ERlO37Z>+$LJ#Sef>oQK^p1F#F0=UkYjwE%4s1E^87ebEZogVWr8hXL$|o$FF` zF@A#if~}I$RE^hJge|!XjA~34>2#uxSES191JcC34)hcsj0gO4!QNo)by%KPct_u` z4xJ8H5${IaFH`h8O2ap-%=v|HfNq_L!@rJR+X4J9M;x~@zTDCHeSN{pnPh_fLDzHZ z0GcPwR{TGOXtlBGZdkZ$@sWnIVxvA=zw=|x!+g%eH1KL1`1KHL{+7f5oQ~TO6*#)$ z{Piq6{rZ22E2z)G6#0>YT{#dnTmclS-)bn|L9y>RQM~|>hHpiM&Q-;hc0`UKb6z|_qW;_0_gI3gru7Ia;bb=?G%+ZBdW*;Kt&%nlh#+zvdGrE$` z6PbJ}4uj!UHzp3Gg5*47S5NdmWP+k?3FpUgt7c$P_$}pYZM?Z_3%Tm9cta|0Ym7>)9rvJ0m@qK-M)a zask%DZlaUvXSsXXGtmCLRz77%IbLB0z6y&&DR8d6lWq0lw&noSljoby4=_5A&kPS$ zz}&%nWOz1AViCx>KB*X$`3zRd3{Xuw^t06X@*ju!AP{rEov#lObC z|MT&6@#jPgKB0HOM|}6^{Pf$xVvkSgD)ctb@_gz8{79|??u|tFo#>NC`7^PscH8GpCqI1(;U4>YKR z{f|z|1@M4U*y!JgRB81$puB}*dnYU1{0}>Z*=M^0_PaW9&o9v-W5UM$Sgg0OMOEPj zH?h~hFRTU6y_tCQT&{6t*7G6os@CNAAD7EqGJBXnt-0Z ztI8aLsyx3(DE+&n8WTh8E*C5}VWO3)R*FWX3 zBk9-R`3l%U0rzyy?nTM(=z_H|6R!u~PeV~U81KGR(Goh7OvO)F#Qlr$cB~zU2)=zL zjWtzf&iDIR%WKgV%N5ST_FTx!y}i(8%hH*(ChF@6_$3pFitm8u+zrk-lC?AoL{^Wo zFXeK6pOKK@3B16;pi@im|3hq8PyD(jpT=_pP{>P{mA(f`6D)9 zZh%?u<^^UvpeIZz@(+B~!2go{2d8lDq8C9hf{YD_{qf$s#rdzVrCVM?2YLbRz*#rL zu|R`(_G@_u)-0S2k2`_ue-LMRCw%`k;M*L|*+VGX7o!0kLp*3t=G0bWeHG_2{G8z9 z^uB#y2FIsPz8A0tJuYDd^@c~@%ekFFHfJGTa9dvGI?j<<{8mkjOi_oySVNKj-lVIj z9_9W1FIl6Z^w+`KHeuZ#&ki|{z2eMTr)&)e!PTH&!KeR}`1$r&zWtN-tYthdX`$wG zMFq4|a+kvY^;xEZT;ox&Bfi%U>v|nLzc)(46^WI1X1`f!+nBvt8INu`%;{y`b3d+S zTTs0+3?RDuIu%)sNrh=NYwT69|6^kQ>p=eotl#rc#s>50ME^-#iD_5{@qZN=40XYd zp48z_!1CBZC+hm`ni6qav$pJRnoXZ~0=oT87x48V-xu@0W!N-x0ao+roMjeD{|f&4 znP)5qqSeUR$IL>S2~`nqD_{2<_25|tb(uh>{AMwM>3G9dhrOOt0WwvfWIs5kt$cxX zjZ82*!4|?cs$u>2!5$kCI05DQd|1HMWULQPHEg4Z38)9WN_W2L95dhqvvS8f#dA2` z$X7Y$vcs~s78!-7x178e^o$VHLCj^SAH@p{{fC+d<_QOdtgD2AOa9mwr7!*q>Zvp_6usC z8nJqYVY5rI`p+Y}HJ*327L{lhkZx~QdP7hy-viP~Z*}+#8GE$G-ir9>inD}k>2)VU7fl&=$y^0G+Wj8x^+cBrg>Qvu&w@| zW%K8=2vz!J6M_8t{C34Wn5)^0y>A4nHQ!ru>mIyP0r+pPzcr{7`A#6NFq%BDbp_Q?KRbXG zBAM~qb3lF-fXG+wN+1ShrV>x$<^d7kNVc-MUX+mO1B7*+ult zKNy9qF4+K8U?f~@J%Q{BXh#7%0y!~o3K!;V)Sc+KZ>D3!-<>(1O##U8e*{;;zHu^}b6^0z z|ETScYCk_yR0HJm`hypA-Etly@BeJhu!ow;HRvUxW4`}T%pVZ@Cve0hLVpV;kbMtE z;Ju#1=7{-m{`L8zTH)&4J3Tp_<)c!5#lER`tv!D}p3- z_?zV)*xwNBKL*cVZ@(w2?P}JF-Tekq`96ZPI-9HY2eJP{Ih#GW>c+p-F`Sz$2Y3kk z77-OE`Z~vGb}r6uhhFp09j9SD=6NKK}yToC}tPb$2$W`MvkRCi=rER06I^J5es)fgN`!>YG#aeuBZz z;2k>g@*LJi%(qyB*Y+XnW-4pzCBEKBDw5N!N~00z`(KEScV6-6lrQospU-O9`8hkV zJO1N^N&kP3&#;O9Y33wlvu88EKfJ7Ls!9ImJU;sp7|rsO3-CQWzzpwi`5u0lY{FSV z`{5VbEBFxXqVodma90ywN-t{-_AutK$N_?1#2oa$LWXUt24co)cF&k*`Y}w*Xz&X7 zn01QQGCH%M9)4?MUi~QW_N0^@XoqsM*lwemZ3x-=52aZ?cJsHl?bA&4d5(O(SBu|( z70kldmkk8&zX4;I%>VzzWB$qGt$pu_+J8PBazhUfTt6luK;BPooAa03f%+#VY^CWBkeym_0_QYxUH}%PWUXNv4jJ=8uW>#MRh`l|Q`;2An zRfUaS%IcrOHFCA9i&tQ+?7|h^J^9ya*%|g{UG}UQiJm_Z`Tzd{{Y(1)a)8jJ7IDp^ z53t!NWx3*J;DzU(b*I{gb(bB%1olXL#R`FZMSz)qo9F~!H9!^-i2T2-iXe+1_3f{Pi*MpiPC< zdA8PIy)&cQfdA*gFSR_EVs(z@**3!zCZ|>MB*^&&`oYG+nyjbhR1dW0QOCj`_CpQ* zh1{eOh36OUS-6lKt?uMzRVw(AN~}dxU$4YU*P-^hH-5=n^zwb^B7ZS`#)n~npTr)G z#`fNVeQF2)Y{&}S9Tu?@HE_RUv7f_&pNyuvH@@AWSoeS6<9q;%*bS8ETzC;o;tJI4 zi$O;#);r=kTGieO%V>wXKBxfq!f(yqwv%x-Cs(U)y!ipF=v{byYQH_%0eZZDKE%+I zS^q};_u!pX!Yf^eU-b!kvi*In;D4W}*<#}MPWf34{zo-HmK7ie_=?!GEZ}`C^GsO4 z8{qsDIDnH0tM*7jBzcq0$;&YEBhs{cM zJA4euae$l2Byi4`838A=&mt?gQqcy~yZ3mPkER&FSzOab?3n0}Z0+eH*4P4kwq>lK z?YZ5#(Z_K8u4fvCp1(akU&9g@h0zaC+bE6AIGVjb059`xklg$~^P8;LHseoi-W;pQ z-p^1r|5HCquPg9Bo140Zv*Mg!dj!Xvz`x-E9g96|pT6WpO}8+Bjd+O8W>qzatbzYk z85Z^7nI~i}g+Wur1z%-EmUHpE%eii?TFoegb@4piz;aU_0``(YI-K9sg0tf&{;Owr6t^D6Dv3(@3h<%ToQHX7g-m9Bs16N48@dATzjKNS zMgCVEu%Iq>KRUme4_=LUaQOaaSnY+aZibg;tyNQ00y_g#BQmg#ES3eVfhkx}jJG^eBp4d6GJ_foRAKY%yC3%4vyY@jQ)at0pY-tdpB;3F@x9u~5e)mc<{3X(1! zyiw-p%so5ftYe3$`!D6bab=_CBY(C2|M|@;9D0BF|5pDk2KztbYe{0BuHs_WaCPwC z4E(GwRUm(Ktj>B?%L1~PK=|U;T^jYWe@{dNBE}ze{2B85Q}j2>#17aVq4ileg8MC zld(wA-~K`Sx>S`r=gkejtxE+%+;5)p54j4SMu0HuvI$|K`6rax4-_5tivzJ9vp%_ z??y(!Y3K^Kf|WDir#rA7PXOgF2DL8+<*ZhZTCPAhr_^<4O=p-D-g3TT{?GVt)%yjo zkRDqd`o-sfjvdHFKNw`P7fmT5()OZz9~^%x9kbXc^OFV;^L zpMz<^0^WiHyn<(HZD8a9J_Gu{#OIrrG8~J@*LLPi3(z<|Pu2%8RCx36^1lC+oNw)w zGNwHsDF7FO>%R9EoZZG82csus*$nw;$MGqz@#u8FX56DGOsqMmk#U8SI0Ic_0CvGK zVyOCH4&dmV|7w^p$mvlFh;l6_9=;w#tcb_2sqLJ9{ZwoY`J2u-<#Kvp2!p7o;43p?LH$8^C-NdnA`C+{kJ({utSFR7*WNr0vl2p{Af!QZ~)9^I`%W_|04PsQ`(Kp&n|(l zB`vlYcj5ns{vSP0jhGgqb?%nDZENw4$(it86F)CGoMvtP!|qG!0dol$zpdVE3aGeMR@X8lDFT7Oo2O51xA7N z&w=?{=Co{&-8yO8XlO+$kw4oXfCO)3H(?z$JGl z26`R*aWS=EP0)*PMzMMh^=fAB>YLxWiMUq<-hXtriwQd!`e(E4MeF}w@&CWCY5X_1 zT~z#i%(G?3B36yu@iV?V2f`WvJ3(g^cqQ>aQ=*!1HOy6z6J#*~wE$}tiopHA{=n!g z#=kZ8N6$-hOtb51w7(VD?-c5j$m{6L;Z*0AaLxK$--5)7@)ZU7u7U-qK1TmDeiZ#4 zz54y|{yQbKZGc@;!C8eJScRo3W(CLq#eR{$5|~{?-2Qj$Sa|=_z-HBN5yIRzz3%7m zaTdc5?CaZzs8P%eY6t4~!v}c~zHtKr+ty@^`59j|1*i8BU|=8^SK z|5sr}@4=QgC|pj==t_M3y$ULjnYD)8taW8dquniDhw|4_uO5)`5I{>!j0zQ@;pKdJfR z|4iQB`|K3w`z(PqJK-;50KepN0n85ki7zt))dHLn;H+S)178HA>28Ki;`B);7qS{4^Q9{Sq{arnJ4w>RWo&iirVue}Zqhc_I>XE(pnsXyi*^Z`8wb4HCE4&%HA&OgN8 zLpdUzIF$bzTd{UN?&p1nVQ0*^H`lHgw#NC)?eO}K;I;Ni^%Pl0C1)_nu-@gYJVEUK z1|t6*SwnkK+gA?ne_fe8{udA(s!N`K2NVJ)`rHWmM<1N~&{M1y{UT{8yYqQO!fQA) zBVobz?)wDq*A5X@#%#x~s)Eg|59>XYmE8e9t2=w(3e+%T{P*FZM~B%jur216Rb($m z=X-m3>q-6%o8J*HQiL1fNWklS{U_!7jX@b0h5tW< zj=tCP>73*1Cu_^6{u}HjYQDm|)WZygIL`wjYTh`Ze}+3w{L%Y2J4E&WG4%HS_+<8c zcY1`KSIz&j-^;Gp<9zHldcFoH*q3|R8PNEyomM-e@0!40im%aDY;UE3if{W3}JF2Je6c?7@D27tE>6{tzjK!A#6~nU<6Qqwekt zv+lCKcp3VK_HQkSht;0{O-tH(h5h+2V>|D&ha%RWVZT2=1^MF@ZmBLI=f5RS^BNEI+jznQlkckJxV%D8J5k zL48vF5A@G`|9E~gZDZC`%>Q|ty*N2>zuSq*n~~Q9j<*YO|58P3!G1INHWrp+4LDuh zDJphUvzPc_2L8I!|4JH(8{ds7E|9S`6evid8-9?T#)fUo1RHiL_vv{AWXt@uQU+^Sx&TmM5u>X&fY1;7E=Gef)S)+p(>o@Eaz(y$oa0c2$Z#+Ib(FOjO zRN%LbZP*3C`iAp3+bz?3cjA##4r;)%>hW1TWLUd#Zu1=vDsk>BaSe9n_PTJ7#xQ`^ zTm!E|^uh1J)w45>bqKffZp=lzhhs3;)LRDdx_#j(eZZ#t{rpc%CG5lFdxA;!UTDKL zK8W{GmvpiaWc=+BxqCV%6*Js6FZLfZ;@dc#CVb0o}hItKww5<-V+hr}6vWXDwTYTFkDgi8VYBn`(bc zYhkV-({=!QtMUEu=x;{&^&{uv<$jIVAK7s=a<<&;r-MPU*6@8Z%Z(*l?R72;pcA*X z1XHZ|mD}43ZvoE}S#6QSmUZ|F24HvFn0arfZ|8jUAnNDb+{pT=ME2H7@P9T|+fG*f zU}R^fGuf2g(*Wis%WJ@zX_{L#mv9w~84YFCjOJ1H^tKbz*D#;8Wd2oW_a6y1TtW2z zVbqP+;3o6I-xXka=(hh2jl&X~ac#tPHTE|80NT%N7cf2Y?c0H*_KCR&-0y=%uBP6f z?E8y2KcS8^;huJfH`);0;eExwW*pn%L3sD-{M#}BwbPfuCMyTb4u~o7>Hu;8C%ic& z{i`rHKIyp{D;yqWu#=evb*v`58l&z>_cov;OZ$4B))< z$=dP^;nUYaW2nIy*(v9Vnk80JFIF?&ANFC!oV9Xgk{1|tf9gZ3|97(%u3&X`O6Y$O z2)-+9wK88$V~vjQ_MY62FO>je{!Osc_N3?xYcRvR2aoT}<6EEtn89TSxoT9DybnrD zLj$u~_hqc)J?z{w*q1wi|FhXckFyIu#vV1q8ag9labamR-{WBbec;6p61#eq^E@-f zfL6c&%(IAzczN_M>HVwhnBlQ?t;b)pxIbU-xp0S)Ucbm6&lfo(G6S!%lK_kW6oKPv z_BGRf72!*wf5rghUs)_*A2^zo3t2WmHP&W6yDy6pM7Cedz6|uwNBdR$&)VVxi2jH3 zx;44F*1N=1fRfXRLqFV!cN)siBF>KWf3L6~ozUA0cHV}HfBQNVV*%Ep{CoUD4P6DU zq4EBhIr1CToh!LQPI$5-zdE;>O%H+lsM&^%B`W}SSR<9Z7>C| zr-yVXj!fG!WR zB75WI>`lh~M^vVa#IjE$qPvn_`8(mSI-kwsp!8i1a1_WA-P3;>HmpAYJP z3UbHsIllW+l>Q%c_-{Wzs{;c6*W=Z1?Au&T+bu$e$@3M%nM@o;SkSPX@sobC&cCD{=(7$GoHHUud^O-&OX` zoIvcXrR=qxx@hHtOsg$t#Xbwx$=fp^>g5K2FgJnu-f}hnxg-}uxtQ~7@1X3kyMWP} zEaI=G68V&NQrI4D^*gzpKX8UWM;(c|b@x)&ei>`1EjgtJq5#x_Q$_~B@1XzYGG$8O zf3M{G-^D64mtq`iZV}H|mG#sbhIcLN?n$tJKCAvWl&>1#{=ux|6H(ZEl64=pED--~ z^7*VIvXh9hy7ei>zcJ~FG4WOp&WhecI9js9PfEVx-_eWB{qC9Kz^CE`9*(`)4Q%@Z zd-OFP%DjaB`3YxPhcA2FM|a!k^*t1u-w*shzv!fd{`(j0fxlM{J+9=O#Cu>gW_fht z${dxhk(y0RE$Ym9?ulk=y@<+;(HLhIIH7qd`*{`CqbhjO65rxd@{5L&W#$~+H}N~? z;Yoc>4xiQif33!g{-LUCYgZ@FU$4f_0oAdfo)xRZ&*V&8%30}4&HWH&;lyOzew?Fo zv63f%vSvp_m$*ti-sy0WZ{P&9|I-l_;0t&s+cJR2oqiGjIX^Ep*usZNA^sW<(V48y zdecudl2Zw!sGf@lG~yFDLFm}z$#p{mxDoC$ggn1dDKGG8uzwutzZrsKnE3lBQ2;Xl zuZI0O`KE31{`DoR^Q`%fa9d+h=5d-YUlYV}N^RhOe%_Dz?;Wg9d$iaQ!s)(7@yl~m zL-ozP{|xn2bDai!BnaODpT8@}e>qB0^m*;fZSCO^b_?nNrc{6dIJLv+Z7cBzKVpT9 zVizBS1y~9Le}X95D0Ipn3wFb+x)#>?E-R-pUf}UW0eZrf?}nc^dm*MpuZB~_M7^xu z&#aJa+Fwb3UDSR*UAOgJL$d?jPYDJqe{?O_yq0Q2@PQo zGP{@*WK|986@73hvJ^*s+m1PxU!xI;~ zy)w^kzt#%;t~P3=sWnM^vwD{DT6cq#RSG|*^Utk#VJFaWF#68^tcfos70KS0))I3w2m!7V@YY%6&V;{{G_k7+aoDF(7V-msmAYxC-MIW*hh z8j#lN^pX*HKT*~NI4T{?$bW)stv=kH&(n-|U!CVF%sp@R9-_bIVvO_@`&rfv9I}fDKItg3`2kgO-bq-cViTZ+zx!1*8{%56z^9R-a?W1fp zg`U4%6xVT{h|Mv4a2o0bP@uVGz1F#vWFWniY*^v5@PSpDK zfh*ia{pmSQ&A zKADfh2F+hJkFpCaR>pb)-<@C(Ih*}i1w*)(T_dxO7H{_|2#1+ zd_|)HW^&5y&Op(*g6w{K2HXb+cnBrnF(UtQIOAtH83Ol|z<={T4ko4?5&f+1K{k;t z!=k|d2CR`^tzaqwlMEAp2s6jrCT;`>zihum{lo`2O}& zbuN3WbhH8APsXBm1EpQ(k=Jzze|N>ty8^HBe3-xiJWpxtdnvvOZb%f8G1qrxw8{q+0o>lD47y21t8B^^iw&}xG4{evY36D-+} zs2c29{9gnsdW$}bV^QI6;CgicYxl)piJm?yQsm#+;-#qnE5s_4P9FMl?1I&P(}?xk zPwn0mYrP&1;3lv$W|s}a3aAE{%{Lw`=PfLvNNkpKCGwnG!V(`ycF3+|{lWiM*q-YA zuN+vvGl<_DdvP*WsU!bs#O+JTnZ6Z!UaoLH_eF)6iUZ>9i}PZyU&3A%|_dK1+DikInptvD-cHX6cuvX)wL{cnNqPQ}Oh9lUE=bZYWi?geKj)1miclnp?b>lg1^ zd;rHm>}nCddCCYo5>M_BcD{%GeAI>8CUmoFi!r`4!MXGB`z{3|ob7UX&ij}7Hv$3l z{*CBH&i~fvFG58Zo9Jh2ukVqlaQEO@^@O9eA~s>HcSn}UAM6o*v_>N#>trgA{ul_Jdysb6PtgLzU%ZY0In*1y|lYN6~VK40rC;EqO{vaOnOJvQNGyg`Kj5`FDbp96n z_w4S2rHzia|J(nM_Y%IIG5yRN+?oNHS(#^8p5y$~*kBD&8z=)1uktfrqFy|U16U;6^m?fJWJQ9b-YZK#;v1WwSHFK4{!opne@N9?rPzmuo!N;#Bc z5S+3XM{j=K0PE|QO)O$>e264#sOT1kqek{fP5vtS$)mH`s}{(QwyOV z5PAMinsA-`o#>WPpibC{W;~}EuI0ghdke}()W`z;vwlQTGnwK4r?8mU(cvFKmFvM( zJQ9|s7rtH5ui&Dc99N<9Z$!T)mVyEe|I8KBMR4@9L zSG)yms#5qRXg>zNI1K;G8LQucAT_~?ld($oVyWiidl^4yfpX*|!EW%+h)Z2W-@!An z%O|lKkHz90$8UBe>cuJ@RP;~2ZXz?`bRJoU%9RhuUKoU(I}QvnV{L!O@={anoIw!@P%co(pXo&GffWVnoL z;CwH0dcTE{S-0raVe1y(V`tCaQpxaE%4x6%@iN|n6LO3BBo+B8=F#$KkLt`mue56 zV>pfj*E-_ucV?%b&rUCS_?QcLWDoYf3UFk%XWqa4zODGnqkk5ij^1|B>1zV``vl6H zy}qwxkF>|4tNd4oA(tuIzz$tag~Hcd-%r?2AAsZU(w}Qa;eXimq4?awe$OgdjK4TH zBC<8q0n7r(I%6+HOQ;Bf9L=5&9d|epfM5Xs0ej4yi@7U)pPs9*6Mr)+tM@PZ+hNBV zDgCVKAYNlo^Q0X555zN`K+gPp`sOa7!`>Wn=f;7X*JC3)agOx=?G@V)KP$T4{4e~E zoP&I%H$#7Y!TcooZM}c-|79%do4L;rUSw1SX850Vc+2CzI&pK}UvRy%`8-}-XZJ)^ zfHQnOZX`G0YN9^Ieg4jU%=|Ftt3B9Yb;B-yf&W?FXqJ&?=70GAYQ*M=%;$_p?!T+S z=zsSV{deNKcyGo3!QgL8tfIABP2mB~pwK&u8SSTWU7gozm#Y3~2>;?3L5}C`tgV~5 z<#sqO2FO585x@a%9Nwn#tz$l)P$rFFsQQ@;qT{PM4`SYCc;(L#9v#zpxL$S412_ z+i`X3Zg-J9Kl1)E{QnIGumK*h z8uXr@y#AP>G8~I%26lh;y>TY}iN(H?5xgVbfk9n0$K4jP-6+>Tp*!4J8=@LbTK6!D^uK$G{A=e29nT`%WV z$KsEEUs#^`f310p+0=KkqOCF*&U*Y8YQr-{6FDZ~c}z+_Utr}=!~d9D^ijIf&cCov z>XSuxaYl~E9{#`z99;N!@>EU&S)A2=4pz?@b&NtARsRlG<&*{kAD~&;{int14^-Q#cANG0X5UDr8!~J)Ahu4GlqMTkSRK56whW zQD{u0q6QdW9zVZa!p`vS%O*5cGuRdUlS4RH=+Le3umB|dIjQkW;oQF$6!1J&a+w8S z9l(WL&t61G`l9>}WiO6R=sycp?sHax%34GvHnBE{1khUl0Q}+*&S>Odw&WaS zD#{5-NjZjF%pkDhpb>{Te>Gt|Rnpl}^RVW46;m?Ae(u@T>3$a^ z+_$&u*{QniSkNQy{eOYgdIzt1Ix9I4Kg&)pIqSx50Auj~?;`i+B6eRp_M>_KwPD~D zu>ji_ZNh6e$7cmPz}G~_zAXF#&i4_j&l@R|+sKxAEv`#zV`Tu>vsRoP(VvwN9AFH) z{sWjq0e)ml5JMIBc5)scg?)^LgG@>u^K0ZjnhP|ApKtQcwZK`=b*-#1bgQiN{G&SB zod5msc~0OQ==%ryPouZ)QabGa!1s^g0^{I?TOzU$pe&@VEee7&uYzxMw! z3<({0+xUv;9}FOi0c6h-JqyDBU(e^TLvbGeogQSA$Nnuw0P?+iWdav%;eHqK`}tgZ zao<@!C42eW-=M};`0oUH^Sz=UU38;0_s^Uzs<5}G;)2%TFs2e_c*W{!9Kb)Nfn3UBU_CYshXpxpuLQ(}42|-Xq z5X2QEMG!<%N(m7}x;38kb%x;rHNe)oCy@DAwz`>yNbT(dju&d$8= ziRZqbu8aQo5!%y0Jjt_ISJe<6f=^lU{R$&)xvdVrM`&SqP&q=qA61dT zuh{?VyXSO)1U=xK?Tp!3MQdMy(meVWx9=Ii{n~#}#Yd5#-nSoA zZmhx?SIuS(i}P2+Ut`A(KV&z&&CYoqbXU}0Sml@)%8Us7{*gYQ?5w|{L=^w`7nYBo z@xu2f`?ofD`vM$2Gm&=c39f#r!hhBAS7-ZkPV?Ucg3H>g$}i^e_YqmG(SJTb4m8IL zAO>KOK#BlR1ex+al=(3k3{f1}QrWe;*Sb?5y^hNe5 z_Pj0k`#$(w9@Kdje3JEE9p0Y;c@!z|6hF(!wF-fR(T2*3B`5%2P?lQ<-bp^wuh5=* zQLCs6qdOAxGjO*98dWboRWX&aqG%RJG6RFS<6gW@HkbF|8m~AWP{7#^9_PJNfxnON z)yLo~YC(2%#U6MzV)pQd!D&UoDUR?4I(Sm>Uoik>U>WsbIqJeIS^hOw@EuHOH8Y`@ zE?IDt$EG@;$^pHON9A`Iz<92_F03U7Oym+VaU1cC{|L@6c6oh3@_az44?E$j)TaQKM7+Nev zCjLqm51W6pd7J>bmm+Dt#DDZI6(eNne}8{$h;&+jX=lAdTKJ$W{g2&L)7 z3M}M~R&mwai66L5%wG{WS$Fow>6lEMp%1~f1xTG6v1yR%uYQ+0w71F1N?`|1NegRsMM%`=9(|;^C_76@w*Ewtx$c z<(&b?55obra1O1Wix@yFc)vy~cB3*eIm{t{?+SXrp~P}O;OT0!0}=rW(_1^B>EqsTiylm z|1akG2OfTW?`IErm^0y<)QR9x?P6K7stU45<6DU;p#SE$J^=FUzveD)V{e z|F->m2H<@FK5ifq{|zO)e_r-~>HjaH!>fXu;<^Ia?K|Jq-M32MLRM5o{ zJQjO}$g6rttOJ~@ew@XY*y8$QSzF7w&&0WIfb%6+P|7sf{+_P+H6Imymm$H^Pcs?>*>mjH8B&tDOK)-PT7ui9kR)mD-Ii@M-ah`c zD;`_uFJGW_G*`5?Wo%g=H{}8LQpF9))(*(jZphv)$PnpY7DcIU(z*DnQ0NRZG#WlpEw%`pHJVS~4g;D4r%_bD zD&IE1QeFLn}Bxs%l`J?3ABX3e8v5ZKU(( z1_2A8EnC0&iZBJ$EtGy-5C)~bf~qfk+iBg3#aEQ$hPR^nMmQP)>sY5RIJ!-ej^4jntf3Ru93&pi$9Ki-`5^kfRAqq@$&s0qJBxX{Wl=+ zEb#Ovd;wP3Aczduj(7fd9^2st2jBu{;3s$8+@)lu#R8t-GsLoryZbW^AiIh5e#KE( zhT0R{kvt_Cu(~A2mLSG+hO1hI#v$~tMHWc`vPiNs@__$upzF7xAK4JleinSidMM0+ zzpTL1v=vTs2#@a%UuW(NHFZ-CMrKZ~!5Nt_98Sa&^1US(G)J5c(vs_H8@$}-xXaoO{4Fb7SUlL5@$Ts;0e>6&-k zje}Ir{{?O}f>~?lY^0Tul&^r3*6m(>(JnJHd%?f|t~X@%f7yS1M`1sE!U5C&E%HC; z4CK+@d;sRjFa(jVc-;B_ts0y6%bH~JS=}B1Wa+(ssHU4dL+W)a8^$vXtAj5ru`2kg zB$F7K6^&-^hxtmBTW*=(R(00A|2LT1-Qb;~zh^W177e2AZym7#G(a|21{JD+?$YgB zpxw9St9k;+4xlcS_4#>yek*R!)SLVHU%Y z-F=mvV9v$9)E7!fADR7U_^;?sp9SiDeun-Q+b;`H;3L$DSFK0Y`G1MMpnT4husN$C z{CB)<$)o>+y$CGw#drQ)#|(~RZ9ZbH%V2TOiRC?oi_N@=#($Bst_Z;64r_iz`sO?6>}cDrx*5uO8BExSMp&jlnLM}nb27(vLimmay6LU zw*jkb610ONNZ?u^y~RhW)~0fT%>p|o^b=jQHY0;~(ot(azQUcMU#Yu2gPftkE@#-d zKs9E%5O#r>&?)q$Y3$<~FxRNqeb_W7IMi)|?*AGZXHKx?7ASQ9ukvPe!wulsRs@!6be`()DV5>5uVZy9i8Qtt+tTpv z3bAi+a^7N;kA0Q-$;*1h!lC!#_gIW|;A>cyJn*H_NtK=W65~}6Ul|HkdDk)x%Yo0u zUb&`-jaKF4X*zhF^U zorB+as2bQZH+}(m*TV$%@=oWSl_D`SnA*h#Jjp#2#QI@=fY-S8;{4z8(;tT;JPu39 z#^VW|TfM)in9JDyf5Y}Lkn4CCyMG=!NTj6NZ)R#g7sV6sA<=z9xhD0p-^bWPPV!#7 z89}XtwAh}M3sM}v&6gnI?U?6~jq}haQ?g&)g;fs5x<8*@Eo)s3*Nq_mHoCX%i|^>V zj~yol@H=*ZmDG1x!rq$?f(2P#(_F6iBr?FpVL2Gi`+enDY6qwPH@;^9@DT&0z(YlV zivj$NEK;_aqRnr(TEP#qgQXkXf}5x_nrh!FMnI82%KGid>1hTNuLlcwm)-X^C#ozv zuPD1cH}7EZJ#s*OpXCuy$ES}t^&=eq zpK$hDumAS~7vzsq4ua1`oQVB-Dk!A>xyLW~mbIQ4os4%ydGf80pLJmh-b*Uqzq0?2lW+LHw12ak2)qAq zIed!VQ=FM)C>s8o@89QC$Z`?jzZigQ06xFLA^>FnSDn`Y{eyb(o&)IKtc!~zydPHC z_jyoX7W{OfG;T<8G@RDb6}!+{{6ttozC2k zV@-xLEFV-HK+>ooGWTP4h$>Tk0hY^;UmKY%R$#iD{#OqUX@2Z|b~7Pp!3{wPj&C3KrnE(+iR zvd4O3$cog7>r^Jdax67lSx1e?s6OGi$LhbpeR=86Fq9#v|J(5E>HmI*Lw|cO%MsYX z`}^v@{{3W;QskkKUp1&zjY{ZmwRXa+gtx%^8J87C-1Wcu0_0|AoBt;g{Z$dF4m;P! z{ixG}Yyfd!e-ZxTN!~9tD{p-RuW_=ka^A1U zRp7PAzm#a-hQZgI&HoHC{U8xuYtXaDUi9XcJlNmzW!aQblO@Z;fZ;T+*CJdX`u4;@A(ucF$9JhMZb%pE|NtuDyiI-!?2 zYZv&8Zd`HN*b~GeOu%OU2^NpCAYEpn>rcR0e*(!SqvI+od@}mu_n^o+m$|@oFNDXo<%4Ay`Ns7Fsl*2?<=ES@k6SgAh8 zWmqBgZXJVkUjd%)M%t?{%}ruJqv2a=!TwyVjcR_V+IKOYJ?)rClGq!(R^2|vz!97A zDTR4Yb@})UO@0Nsv?A@hqjNW4w1fwAM+5%~-ai6=`XnMc=kgjw3kds{@V}WbhHv?~ z=5Yx>{hd1Dsy6Sd24r^E?CS?AI#AUE4F9u%_+}@Nx8O-M^-TOf84-^s;bz}Q569A2 z6MuhB`bYc|b%)+jR^6`>KL6gZlEtjr2`nR4t>7X259#={{H*oM%v~jRaYGPqJevQb zXheh1Ba^|$8=AVTv{ZF>-v?rCg&C-`n|i#g zcGX_h5l*q|%NUEz;BI!?BCKV@kj>qR#OVjcj6ERaLUsoFq?xz2~Uhg=|kF=kg4{Watze;0Y!0t?FkGUAuOqy9wiKfyd7glR2@ zO-SM(t>-SrIKa=^YmV6tuQ8dKEtmf_b>O!ib zsC;;vqJ{M4o0z8 zlpolfRcwgH-;AB|K5U>S)`Ir@%@oJotdH`8_}`xup2-}|=j&8HL!O3~?1yZz$2mnc z&=eMMA}eBzD@_IN#^?r%@c}9d-~*(|bmGPiVt)#wX*>mLm}OX1q*M?>fAj6Ig|I$UIfBQ_ohN zaXaO*0yO2hs;jFeUU8(b>f+X7<(fJUpq@AS{VSkL#hABwe1`^+9LZA7p}%@bXGN1$ z$FZHD*zeexZ^fhqTi$_H&UCo02#eF)Is4to_h}0QuFEfye^HYr| zsjrZnKO$SVgmxes4up;&hi`>qLHcar0^#D}(&4yprEnF-+u=9(Ujf)aID8H1ybTGw z#8ut;9to^Ior{pc{a|9nVA)&9T6~Y|4Z{J}voHHGC*{F~MBx8w(0?R;B9Gc z{BIh_wH|x(8SGgpz@!(z{*RHE>(LsY=Ki`N^?r{1lRLZ0DZK-ay8u7i&l<@txgz!# ze!CPot&?ND$JK*G5j(RuQD3;&2<2_$gBvHLLgH3<#kY7{WOGrc*{+PwU~TGot1i8k zr(j<#i~B9m>`fl3y{|gPU5PK4%!;qZzpx)JrrOV{TC0Ba$~kF%~Sda?uv=~bP0 zIZR*<=&wGr8sGB2CH(wXatBQle;6!R#R63=k`3T_tN_J%JkS3WhoNdeihoengYt!uLO#&lmpRh>m7um7A#g_OD^# zYQ$Xj1l6Xw9#zr!g+66vF2?5I3ny1J;c zcvdV{Swl~7^3@?Mzsobs&-c9K9OgsCX2T*oSQtWPG0DED2>Tx|CUzmJ*eOd7~oO5-ztjbUg=hp0{G3=zx z>>|Z){TKcVb%ug2gZU~BFpYh^kUjq^n%GfJLoy_$YWk~#p6Z=ws8;TWF#Il@%5lul zD(39~%<4G2>Q8Jg3qb#Y#7ne84m6FQEng!c3k*mV5)N>}yd8gCJlg-)cSVt@&x^@@ zUtP|sru={mosE2w9Fpf^(LDxW85h+P^d^S^wqzp9cP$4Q2}K zV3vDn?p=uuALw-V8CZ*^@~k)3fRA7XE!~j!R+0am`OOSu@y~D`SpdxbKgjJUX)czv zw+iXK(|J#1dm4)LibJE=gQR*k=6Cdgx5>46o*LV4U;`L|JUt(snN#;M-qyLqogPIF zC6CR4#8DJLdpN{6K4&@a=_^PCC>~^88d6*ArMgQ8=1#CjUu_%tBL1=rCv z*I{p7!1xhA`&FX(ilOBXV2!tM{;o5p;#GI~E}T?%Go9g`%!9gvD;wloY*IM%TgZk{ z=umsn2d;%43dbOY4fl(N%YgXrg&TyMg+B_nXSC&eOMVg;E*yRWnSCpC3W>bg>16AP zA^IJO@+bPmBCLrYvjUky=QuTE(7h{zf=OZzP#dKa_nL=#L0iDDfn>3KNEPD7=#ih` zNB#y6@)jc55)mQr2J&$bQu7G+pP#?~gcX>FexN$tsq zBB?gGhjnb2!WrttNl@+93drev;ByEc${Av{x5F`h!uqNjM5@r&3W@x#!~Ys?4I5yg zQI-9kNbjK_+8B8FR3!RB^nT@PZfETe;r&r$!ZpVT@5DR|15g*Jyy*PTaSoLsQ~*Y< zicw)a0B68|_4*nQ9<+c{6bE^eVF9oXJL>d3iE}*!#Me-tg}y`s^yaHE0O|V53Q*6& z#mw9+kbjCB(|N8)fpstdhrJIXXvfi`q^H$&2)l$V3)E4I=%&g17yG-Tx#teG~B>x$!VmVgGl* z7P$cL_Zj3+0rdWEAi%}gtenj@VDu7j`xpox6HXD%5YCEaDF=J%37BO@{+}V720oBD zoQVDSa9CX68gXi784wqcO+Z!w`3B50pssJT;S^)hWd{(a@)@7f3Pe-h-+S>9;Fa0! z>i?!F0M&ti0si|eUWn&-_JaGK3*=A_@+XiSSNZ^Ak6 z&rVp%UOMCO-`oFtgZ8HXOA{Q*3Gd4oz~{uLftmZ#DD-y~{B8ozSl zl=svcuB9q}qw(4O$SK{c%6L$xc@YUFp z^6Y+muy`f2+ge7r>c04_1y%Tw<^GC8ec7B<`R0H=JnB-sM4V-2{@ zUmfQzHgSE*5?zcG@^>P8%vkV$Fv#75APg9*tdvZgM$#7-z32 zUbyUZb*soJ9D^VAMsyyeYkSV!G9=OkY>-*lho!Lqw1Aro<5PZMEzAQD`12>;LlR9E_K*52F76`oDGjHE)9ULJ;>S{J+C)O&GFXL^ognQFsCT>gs#I0DLZn z_Xc?V-*Wsc^20}b$kYFds|WB5=QuOi9~HX7weMnID#u6l!j-2eZle8l6OI?u{PVGY zLjNr8Z_l%rSFw&$Sr_I0s_wUHH5WyqXN&j$CqR?Cjm7sG`rHn%cq-`75B!&JPj%O1 zQ?{YHY|8tQ{l5}g|4T@G^?of!q*-OGI(3{Epe?Uaf4FLV9|5-Pk7>!?kw2+B9P%CP zIe9_SW8l?X+_Ld$rRSfoZ==K{{)MG!I_GAu7K4^N@XJi%#*c74v&;am~V zo0)~bk;bJs_uqivXF}28wBelS_9epQ(DQ4C8-ey+!ac)%!~Mep!(WF7@wHF56F;pT zt`L46Nu3xQz)_fkX=B@Yb`YDuJ{Z9FNSkiR-dEA0!lAQ#-emUwOQ7B_U}#0+?f*h+ z97^<9UHn*8*vpO4GDm=82ctvi{SCOU^;prL!+y}6Gc}L(la2E%d&e?IlVTZ^M34>1 zIF8vol%?b&Qe=}<7ZU5(IEOPgmQ&ErvGTT@#l|qIO2iBlh#?4k>=@bw~?e-b~;AtFBx@;lY*Jju9#1y1#w9>oXXJAvgQ z5+EzABME+^i~NnOMdFM7VSP2(57ssQBAERLb`WI%{^arjW`VHdn4Pac|DKF~!~>2& z-=B+~zZ{fa!B=GtER2u9n+bQA#czLv0sO&?p20GohsIcd*YS$mWJ1(YK)$*qMxN#)9(cK>b|dr^3&%rwZ|~QGlQ1 zWe2<)0kKoaua(`s{XV`z6qT$tK zPnCnGTSeGp?D>=6{~{!Vdb(K$2=!U22J%WyXUcRXGHUT&Bg3Ch5d9!=kHF0PCvYJdI-D^;q&*b>bwS^-n$;Y2$Z@&@k(~S&V&lqqh|c8zS*v`u|OE-)zpN z&-w~De}M~zO9A%#XOa9sHgL;)2>L^a11Jj6+hYbeYmhOr8QiJY>=!u(&>yX%hg-Fd z*uOfmb^}<+>AZ)xfymB3i7}P$v)NfIky(YS&5T61)pCGwt?F4%PAm-Dr==gQ!N%B1=tskp}isxfREEO&%VusXFlh_ ze{q0-7X>|n{}{`s8F8z7JDxAMLi8?gRF|HS~T5}(DgN;?noKP>NG z6+m=;ea9f#^iqNg%Gfx|X_YTr^*v-?(oPe@^!w`y*Pu`K5rCHar*}xj&;Di&wy<-3 zpXbh;Z{h#z;F$V6OOmHRgN#C&t3u;3tbB8weNXvjitrJZiv`&5b=N=Q|1~)E<*?S2 zM*^r8i!$Kf<>%_+@+oN0jK3WNreufl{R;M}Zq^i5zbTsQQ)rJzL85QbM86=LsxRN? zk|S_FCKXbr5mMoY*h4VKw9J*NeM{>RpU;Jt^E&q~OO~o`Y-D!A?5#Rz>l@I-Qit<} zONJ|kYoYPC3V#~z3hoaNj|qRfgN!d5rOi} zZsGeLw6ZoKg61F}v0Q+H-(gj^Uqk_DWTBw<=ZYI4T>P)pL^t zQs;IuXJI02KzX;l@#NM)Ys*LE))k`ne&o~pI}5Ec`cO25R zW9}0BaD<4S-7X&TD0;YJ0+)j6qw)U_WDMomG_3p!LH>1k3x2~+zY)f;+VKT>174UFz;)N8Cq_!7M(j4RYlcUpe{C?{=_6KC5H2IbaLzpl|cWYSW}L~Bqr{%0o?fy zEYqbyq;Y7R`_bd?gc5`MX+i$Qy0G(FV@n7~~se!CNU z=|k*r@4x}d!pHMt0|*nTvy)iQ$>?SMiQxW}{auGyQWXqUUMkPYZw==hjXh~O{PN%U zKbn0iU-J;BO@8W7q%%xFF)ri4kyUWsi=5?boborhGv)aA!U{5s*qjkqM5Z!3i_ra- zgZvwLShYX(#~TbgP_ASf@ZY#nHS8nB(Hj)`cZ0K}N-=(@L!4s9gS_tt8GsnK@`zQ1 zKo(=2!%fJVMfVWS@ZUTDid{6$pgb}St07|<0P@kACtiBub@Xv{1<*s44duTVCy2#P z`zTi{e}MWuD&9XI>@6c2ToT6_3}bHacjv%zVVOm=Ta5evW&y?&ER(_ahF%ALN}ep` zex|~{l(pX%x!fO2mpw+jMwOb^f;8$BHy>Fe*4EE)fNrc=Pq%7go#n!^`5q$e4eI!m0=&A&d#q$l#$jvg$I5lWk0boA0Lm7| zqM3`i6$40)H(nLM_hAd3j4bbguBb;>kfSYUUFhE!9^m8uRp+iYI+Ac-by}@PtLgu$ zh}(s~>_LB>jWJpH#2K7x)t=hVy-bBi*1>X}&Q*h)hdk-atGi>9o`9uQ>-{KJ<~qc{ zEd(|1#6E^StO8Q*9YzK0of)~SgJ{G*b64^lZsmk!gducCHk=M62mecB@&5qy?}(n? zCp;uP+9CgBzK#!n!~c7PKLP!#Vk5|bpWtsK@D_ZCKciVJVk|+zC?e};p3g<z+ z1N&oLK0PWf{jDyy2FmD~?^{vMyTZ2S}PSjfY&E@m$}e^S=GBoW`Wu=LhL#;bE@arl^I z{7q)xGC=+U*>@AOaS)5ZIjjJx2>CE9F&3_9oggn^CD?~{yaAL{pU*)sqPj4_BG}>; zcb5P!z;$x@Ri7{lKm0X(0)K+?%R$+NjAg|4ZGi3n>2f9x!4nP=HL#Bv{DW7?9XslsbmJjn?xeUs@jB=lPj@NGH6UJd_XiT?=+-Ux1qjsS6 zPm3B6H8AQ+9>d`Q>%sqo*dEK_x#`ZU7lHnlqElhfdXKf8%-+2We=7rHo`5t<%^n;; zjNV`P^&iDV1mA(z{}ul~92>)E7=|jv zb_V@b2jo*eryHoFxUS_~$z{BGInacvqsLoc+_7l)-@0ht1t9)%=4l-~e*@3H#~LsK z>{kY5H)hK+C6qT+1H>uKTs+Q+li$?5hF-q=C@)ow`>#5%#s(}7P;mhJ-5Hd&G0$r1lx|PkQat`WIbizP;c63}-utAvY~j@ngy!OAAZ0h=8C|tl~HC zI1N+Qf5o#!da+GI4Ybop#50Sx)yOp@3#=c$1FTi;>jWYz=5Gk=KO+MDh5ySP?-}Q8 zj`9!mf+314j097rA%`OIUo*0py|jt@KMpP>Lt>T#`#T_=JF=In!|hUqj=&pg;{EN8 zpKb*{(<1cTNJv~xZcbuz>~$MC<@wPjyI@ONgAVdfOd9w?3H-)&!ID7^|1C;W-44c|HOlK!0E3=|TJt!{5~9&$=|~ znXCY+5@>zB3#0QZ%R_806R%9lYTxD)jyra~hPhNOoRHsQO%*A-mfaTI|9|zE5a$<* zxD5yUgPl9wG5fyp_}|1?j7uZ+3*rOFi7)pt;s9dN|4!iPv}kVC^H5!{E-?L%oc^!; z9%b_>`%n3Q@&G6%P?3Rp7Q_H|0Qr>zI060sW=tHPej@f+?l=usbdJ3-k-b(M+3;xW zSt9?e&W@^xj{-#&B6SaeHW{7V9SwImLoL;3um~zrLbVUFqQC5BUS`5c6{ETkhLaH4 z*BI%!4clsZr0uIPfcoL);CyFn{{yl5e;sf8?}<&IakzT8EU!uvzJvF6b7(1lIoxGj z_W%&1l`BN-d>0?iTsl$lV` zs?|dENOxWyG6OAWMa1~B)6(m;&0;^vr99I3l1!wn9j)nDdsFY_wvb*wG}CmCnp24{GF2rjlIdL>>NRr+a8bWxG$48;5GiCPi$ zUDU9sUQwS%b%<&o)iLS|xWI2w=b}=gb<_p@r@}@~L?X zA0*23Ews#E(AUFg@&%oiAIC1M0n)#PC+T(Y{w+9VbsqZqHt{88c~uEG!ZUD%tk?)r zhLaI-`jCqR@b>?M*#70i{viVWyMz86iDmQM>@D;EK?d+HNY?<(N|o+j!|$&?V+Ea` zEGL@3VSgGVip83xLGq|itTKnKhq^46VgcvDBXtk_oqhI;ll9^N(iZx_0h)4B-^2!x zlQ@twc$SnE)Q>%H@jZ(FQQgJDSODt4|3(t$yBrRGf)kt))=&ZqLVY|ZUD=s~KoH9| z>&O2;=M1%D7q;bDZ*;n8?D5^~{**8nRZ{TiKZ$u+!0ZV5H=y-zaCsA|qVOFyfx)gy zf}y`E<*GvdyVwdVpikxIZlhhln;_!b&@!^JPvB)U+z-Ag4sb8?qR4-tzfRy3PG5lk zid>YYEdAei3zCOHS>^VB>1c;J$-*Ji!~;#;=a36S9;^{yd49_5hy@SsVk;233Fpo@ z&StTUp3zvnqTrK+iL!hD+u|MI|0?+2H0HfG^RN7mX2=5TQaOh^^18p`j+Kc$1FRng zZ|llwkj+S!l|1;A`rDfpLAAI&zbiOR0TQzoaKbO(kgY|ibUHVXHGw=0W z>=rQO(U_m%=+D3aQWKl|BGoy1)6?x#bV@7$O~H`)AksN(aIyfr;$i^$aouZ}Va=^N zM5q(bEp#Yvhc`^VKkhG>fB6c;mTb0;-;>0CE={X8-tgdW4ga-n#y33Iuut^*zmN5Z z?8}&zy{LK&;&_&$V_7=t^rw1(89AlN$k|N*LY(IcRnc=Js}R}C%kba2P1$}6EC4qr zUXK>6Q6v2S^82@f3w*{Yx9T6Q_>FP`Kj7iL1M1!S8J6b3;K5w<&C@YY67!}^DP=Jh zb7%4r=V=Ois181z)NqcyNDOt&9fkBz_rS^gr8=qJM09Z2k z--%qdYTdHVe!*kNJ?_1j9b#wIDteFOTy{XKuguyiPw!c-uoN0xO*FZtV7)SX`Xbo} z@h-CGS?u@6PRc7IL=mBn5=D@Zds6LZV*q=&w_Tw9PQLCzx7*M7lXJO-m0N_JV+L!c zo=`pDg{lhi7CxbuTx6)K_9{NKsKbe;UDV!lWQ&v}FU0yFmE)DxDM39DQ^X`ehkpqB z{Vi}_-2;!p1rCDi``p(f{Qoe|)Dd|%HUZ-YTikc$5H7}RFrH|`?qvE`N18kl{c!ZL zsI}<&Bci%TwTo&R)iCM<9xbDKN6n1d8TAl4M-}oCCSxzS9-SMFwk!8@5Vn*P{#l1L z9!gcov+S<2VDeJ@L@}U$5%l`1M36Qijt^# zdyD*2Pk8D6vinQ(PlMEXl#!B$YLlhnyX1n;9e)UUa~IhuZAo6neeCRw^ey^<=s@|F zM$;waGti+9RVWHmcjGQQ;3w8vUEEcLvNS7KfHi#z4p0UiK(YVxsUEQ(?LV4`kpf8U zD)>%RC02HR?c8BJR2QxrxYL&3d<5=Tf8b?k&#IwV82!C9dHjRHccH)RaT~DsZ-w1& z;rnX-zlfht1XBi}`}bgFIxu7HowuYBCpV602-E%_3S9*w0{jo+xr73SqW_Bl$j7SR zZQ_g?`pa5w9&XeB1N@Jy9;gUI*#peJV7>rD7qcvTK44X3q`gU-^xfN3$JK`Al?f5d zavsd2))V;Y-oS@C1v}h7oJREn7)vzCUf86%Nf(DrHsOP#1RUT&{P#Wr@ggxX&%=cAfu-N~Z7}>7XY-nxX4~-J*8m7I zFl+@q{(A)WxgM$ssB>tMUI9Bu!C6cJ>L&sBakXKkqRX4sa9=>?LIIJggWg@ch4s zm9rT5f0O++56S!qUdmR;k{QUYJF!pm8DEkI{3o8(q{Lh0M^3+r4YoS50Pits@vH(i z+Wcf`3H#T&9KY6Jd^wO^)(O?sNXDw9M0TojLy)hL%V|_8Su1rBKg}9hEtL^``sd)P za$+=86Z3QD&z#Hpsq5=uR`eu)A?d2x8|pQn>>GV+39|88jA7*I&n_hB3VD@wd%b-gHKhR_0_yq%ws8k zIfGp>g?Aaw-uQxj(SkjpIKo22_o*XnRP0SO{*#Uc?8X<@n>)9_$ zvDnN*>-!eF@Gy{}Cwrg`Tv5Gx-{Yb9aK(Yzs0B(WDn!1aK8)V()rurhx5Of7l6i>g zvkHFd!kE-q^lxD?P%qIWlbJOfA)Uq43q@wAa9m;+i;C5U)IC3+`4*GyEOZnuRc=BH4&f23fj$%$`qWk{>`fnyG$D;jJMR@@~ zSN%k@{dXtsU+AyV8pc$E)p{}B_peyT;}Q7pH8`sa<7IoG6|dtY`Jw1yb%~Si9~t-W zv0vYXcV>gLx^Y5c-^*fpk{+{JmG&`!Rx; zLqjjKPYeGd@!wwUqls29+mp}-TVM-(6Q=tL+F(6=07JMZMeb=16d@oBSAxICye~w^z-13M}jq<9VwKqIbt8dtaz$*M>^ z_25$+rDsc?0X&HRmbd@!dH`mZ@pBv4P`vy#eqgZ{56WlHDXbchMdA4_53=AqhF)(T zIMrVaSe>ucXt@Kb<8zMrvPu)%*gq?n!v)N}s$@P`Woj|FqV?IpPTS!aiFKLw`0v@H zYDQSMhhN#F6P&i!o4K8#1{}+EhfqQAi zUi^anuz_=)9@gIoxiJG|SA5n##Al_a(ogPi-tg0mJjm{}qHnShkhlDtkkDxScq8pK=bB zp*xcMR_!g*|HS~r*DaDuTFOlL;|i{3H&=WaW~>+yRfW6F|FqiK2~iTykHY-U=?GT%MkmP9WWXu zr(RJ5u@?7$Gk(VC;$jfT!ZFnuY8keJxiAOSl0C)wTmkS(f4v6`{Y*tN`*Zs5^{m z5USc-H(0@ZP~%#J{{JwNpm>6qn$ljGu8`xNjKLiuS2~4UxW3MrS1{r zgw;KY)c+51GKB@^NB0asI{U8<^*s7325=*%b`A2@s^zL~;S?SL{+s{bEVA}`VX@Vt z)p=ayWP&mSW$9C8Kt&ZwPgh-9AG_~SJ!JW<~ewuqT zob&kanZib{K#^pMBc6&3Y6l8f*Pmiw!8`na0H{14DYBL!d8L?8Rcac|n)OEVv|zm& zqJyjQ{})Ik;k~$#Dzm7Yz*xS{MxJdz0wy4mp*WV*X6)%9&X%L9R$su;;_!CgW@dYU zKPQn2$zr~v14r5DLSz9JCkC)PalhNB{hbFX&>HEm3@LLfCKbNw@>r94@mVX_D}N#L z6+Nl?yOu|#TzsF6r;N!+Tk8GzA87#)(i_9_E8?-=zWd+(_iyzYLttMG(05@<&l$9b zYBs7;vtqnel~Ylls@r6i9eLkmfsiZ~PN+Vkaw$wh6vOnEe`Q+AH?xYpX*`DK1ZmDJ)<)M;oe$~%>HwTL_8ii0n)Bi+wxzc|`Y9A7iBdk!!Y z6ObZbfLaT<$K&J*=7Isdix==Kh|m3vmD&S>o`bpFW}inf*RtD* zgGPg~F+2;g&hq7`Gy5w3Y9{Y!J{a}LR1EC^WZpQ(p%)?@*0A=wc~2kB;^osGB+5^G z@;J_`>U7IzZ`M@x-B9IppLb-Z+j5XMz+#RdO%q}B%?e{uw5d8xz65_#)tEfYl`_PY zAC?|_M+&a{Ci{3F(rYQ_YXrNcJ-ehP@hCr{F?z{+Nus;oF zD8sxPyrC7mvkpwO90;96ZzQeqFG?53wZQ5LNn3cqG{*LtH*d7k=Ix-Tz&5|1vJ(KL_Za zEc_7hrI&~rFpXb%e!_hrzv4|*0eBL-Rd)Z#m_KE)2R(nJ?_1v=RluYKQgew>D-V#Kt2Ik1hnSH0MtD+ikLuYBeI~H z9r6VG=KwSCCy(LC+>eL^EDTdm2m@G!p8qwxt{bB(*1F-G`R`zU>s?;yb$jQf21z%nf|9vN$QpOK6CO4`(3?}RO{$|D|k?q*YfDSeK)X-uj4#< ziAd`$FpSHfUJ{tBJnOQhCw5Xdh?TUM3B^)*tx9p##~j{j?ju>4 zVSVJ+|HaE9U*n&f&BaG?sM@2v{DJ# ze!ow%SB8Np+1Ul^V%C^?9yOc=ASoW81Kh`A&~+vlIEqzOjlB?8u>u=FA2`5Z7loC8 ztL)8*yb1olhSjq!@1+=khsY#d4LVB)Ry4?Qhjtl3wjxNxD#(ErNQB--ITX70m0CsT~s`4d& zpzK$wJ}#fZd^bb=nInsu7XA-_)6L?Pu0d*A-jVbHSrNn{u5vOmg85~cZ&gug#N2-f z1N)eD_?Yi48Eu%2Pn^tbip8}C^CGXQYAhrmM`sr{U{#AA#AhjERF&Z>A_mf#t679YRm_IE%H@twybh1f@6J6Q^-M=-y|60WTm*rK3T)bau zDoZ>RzD}0kU+DbXuwk!vzUv?GXsg=fY|v~HJM(MMU-@f6%%87dcYoZURsL1Q5bFtE z9oxSm-HNj7im>mV1@-d5^~3;_`yW*Q5fd;j;MsuUk&FSz51<~RG4U)w8kAUoV$Cmr zB8pYNfOPN2E~pF_%*MJM$G5i}&bE-Tg6I3)x}GC0RCyvo|IEk~)r>66PAJ2!sm(c( zp0D^5Spig6{!`{zy)WKjG~j=uk$uO|=am0n7IxDLMl=crpq>b$n5BU(7PkwYn2$Nz zwtqW<``zL576s4-E8}~3NlM0B|5Z~<_P0R)Hy^H~wfZ8Qahjjm0K@>iJ{@FR$+A6* z)2AxA(t_1hT>7wL0Db)*UjaxuzDa09{>4ZpUn@|0@}k_3Ok6*5jmCVN_#98=)bnuj5c8(HLgNuM$j!riLPM%d|A?;naLkV|^7rV=S)4jR4XDO3fpdL1 zIx{%b9Im+xZ6P5RrPsKE&U~8W?|x)t2-LT(d;dmQi^>zu8Urvr-Yor=0iYg_)-B89 zcjO5&6t)#X&~^-|NxpJ?pD^0IYVuu1x$>**9ME53;=GvsX;_7XuLf zFGbEyq+5D2pbN6k zUUTwN_0nE}b>;&36NXNs9a}X@dG8eu*$DPg4dk*;XwNe%`PgmQk#^aUW6yG3C7do( zo>w*DdgQI#%{8S*=9cDq-g5}s5FC96`BRFYW<{c2|C{6E2Z`uNo|+?DR{C-#7{H|5or_HXuoW$MUoATGFzd6Zvp50Af?g=nPx zbKG%L?4qihqFyoske}*~FxpAfZ}F1MN2@Xf0=4uDo{!fALJ*Xq`efX#F8pcmBfbnSmz2nip zCOH5liS~aAKT~?=^S?`#pYzzC_7c0cmZ-HKnDO~&{4=olPi7syft?Oyg#yiARx)3~ z?!laYU-`Ej>th_CQm7)Qsi@QcUtq-x5dEJI2ArFH{v`W9$2}%sTtKw|%_<DJsa?zYJ%r9M1q0?bjKEkRQ_UUtI{}_G!1&%$t79+Zi>kWc=Wzft{t^#oC^SXTp!Kb%}yy2%|2VLIt{dsJxQ3iU zoi=H3MOkDYdLW^_j71 zvR$zRs-CQ@w!^{ysb~h9(fCq=n9V`IV^m6y10idGlhRj8A*0fR(Fus8QznJ1CU-c8 zYgpGN;Orx@C$J>`god$$3NsnmmqWnoDCAo;v{%{rT5$efLpG;jK2<$m8Ff|@!&X=M zx3bTl$H$`H2#NSKRaCgjS>MGOo5#8A?~wmPKJk60gH#9o%OL*?fdYzuOUtYzilsap z0x=aVHA`#`eqNA!s>!Y$%KqMsu96G%e+N#|5h)}M?sK%3y4-CMEU+NB5VN;~YqDsEwS2br;$c?Uk0U%&T!gAttY+WM zM22amm0|E6(xVy~9hKdv6h9xD6C3J0Dt1&GeXuIK^i7yw3Ak4-I74E5Lr3uyEW<-J z5NlI2YyodkcjiU(c-8*TNq@c!#0!cOoF{W|9rnXHE*@}b^l<77eM?T!UMl-$C$gYF z?DTR>TI^m$!6tQC)F{vARpqmraHb3hqGb9h+TUybX7}G4UtQvR=6|NECovu?wk%Qu<9+_B^y9t$>i%_q zy+5D#=RE-GhhrSzO_)F#r~em)vB?6U{C`yd%7+H5>i>#h6&Elz5Lg8iAE0Ug$`4dT zpyH6_LsdO$t4S?BU~C`}KTjF@k-e&ZoUK`*H<2I8E-3`MmuBXq5lX5&$6D!>W~+)?&*kSAx6fWm*t z;Rmr_SgZ^NRk*d>cVGK1U;*meA>ZvpW^1ssb12vGb7ClZQPE^Dp0#gyhY_6U5xmPc zoDXs5Z;_MVy04S@iQ;j)Ge4hTA(7vtonza*-Kigk)+{Drd3Vwb6;~kdzqCTrXd|^+ zSwDpTE0Hr(dFO7-x-vSI`=vVG;%LQK&+E-cC#ZH!$-F)mpHh{$f`h8%reEMo00PO&e;=%Wz%3o0BH>lwdnWN?Z#$q{@kD!XE*6mYqe*xFf zCz(A&pPGW1P#@bF>>O2$ug>a~VeN{r?s?rQRsFw5vBF+J8(Im=8h}r~CNrKF1|&j7i1Vb#sT+I|dY-A8wHRVP(V zOVzc?jqFLzT$r6P0XQiePE6<|`(ZSHQI@=rJ7k|-#2%ZLt8L3}KZze!(S6;~We0NJ zJ0KTokX!N`8csS^T$aASK%KQlD z)#p~%ZCU@V67*4I=3U|}Ga>(9;e55hRyrQ!7uK)i>yJc3{EQB2-3^5MC(%r=kd3BV zw2Dr4kyS88wgQ#=q7PQ50O`xgI)bTF=`8m zTTBirm4{{2Va@Ba@{L&k7JOO<@c4~cg+cIkOCPSx>BX@K-h`&6%&W#n} zh1dcHQd_zWwWg|)5m=N6Q(?cN{qHiPXI!_Rop}7$62mSQHVvJByxS+TW)F<7E}`tT zPnc!jxki5f2X%kr0KWI1F#vs4-OwWFyYc`OcD4VX#p^ z4QZD}o#jjnpbo3q(jkWG$aHp?QlFozFMLr}q%Obvf=^h%{9ht=Mc%yU++97*{fN~( z1IJMokYXU}I17M!0`&m@z5XAG|7QR9(Oo`o&RfWR9=Lgdg#}^=hLt~Yr?Ze1s%P&t z@WI@z4SB>zf!d1BzMlao3w|0iG?-aY=Uny8tH*uSVz<>|)MgIk@u&(DR!7}B%vw{o z_uBF5_8`4-v$W%1M-sivD1*$d!cR;$7YC4bK8~}bN;{HnvH%+PdyjSyTWHpgZD7C( zUO${UZ^CDn0rj86+Lo0^Hl7!XSRut~D!$*YrZvwsRP$doq2ENNm*Dl!@=#<)UB?%6 zRfC*`xwYdeIz`j@%&SCm=4AzoA^l%NQr5wGqpr4++=g}(PAeEvylsZWaj z2;ILT9N=vtbb1rhvz00#xk02J_<|3_Bx5&K^QvNEEV9R< z>!rc_T!0{w&*Clq8~H6(K%Mo>ejtSPk%iLCl%?^%YylPt={y&w@sqk*`_7Ijilx1BNvf{BKR9e<`%8+~`z~6M3z!=kfrQMy{&9e=YL4RP(1U zme@wl0?-cG`(?cKe}wb?Z$L(ekk=L2H>24HcZmS7uGP{qY##o`a$%M49^q=$;aUt} z1E;4adqTYl)o;Eer)LQ7AZc6$DftyK!K%pjjbj1g`_YR!g)K&K7q^Uxu>;{SiHS51R3f!eW zo%H_qVA#_87Z72#g@{LWoma=%4dBpr{LqJpgggs6-JmyJLSoay;Y9eUlk!N!2-Bw~ zAwKI%jtv~3ck)*J)~b!T2&=U+cvW#lh}8yLj19#Te0Xn|{;F(G)l(>gv@Xc{sJ?}) z2$#6W-DoRo9oJPIL*u#ns%T4#c~QpEM8*W>*?f@QoHs#rt17cP#b9VTVTq4mn|TNi z!F9OeIriKkEJj=4v8qus$mJHcpfj?fRyy(jS^Vxi*k6!+SsyLMWL!tqU;2(LDwJZS26S-2S*IBUw&)LUNh@{)dMUT!3MltKs-Rc0d)sZO<>DWQl^0A2UxuzJ=aj>GIbYJe5k5YA8~dj zld7sjC+%`2>o}Sn+MKf!$NH3JuNQYrpfGHp@I3}lm2=mWGbamxtXX0J%{UP+aniHl zf5`=|RN(9mK!aBG_(PmA`Sc`DrBfxrW04i)P#%!)2izK)n>+xj0?;O2|1XTaBOMx; zs;678Jn3tyfUhX)`-iOMvXu)PWE~FhUlF^b?=hYJoY*fofxS76s%0d5yn5WsaZ*|= zK$3elI<{hc6x-O)&4z5PgoviP&G+*>8E_q($O+TNT)wvbd6l4l#G1@=p8EAYX~OO$1L5{B&=F{}rR( zB?@o|F}nMR)p-eCKZZEre`4~nzdmA3%>FO^|1sWQy%V2?9m)!jiTx_Smd6?GRG$mr zGc5jJ-+c|?0RO}B3_x{;tk%F?ScSzEc>UkL3zy{sP(??}Ofav!XAtK3R}BK&joNSP z-0(GF;eTYbfxIC$3rRqr1N`+k&e{8{omlo$tbGR7 zJ^n5a>4=(|6j&NQ;8UWT3BA?{r5G08uJ&b z_a`5~K;j3c@IJf907}bFRu=Uj&czWdL1!7KkR7MupBaBq54_Y|uQZg5%<);Y;Mts? zrm*A6NQS!Xr`gO^8j$vVPWU%ivdwF~2(%pw+IHaQC3%O0q2tWrH2$_3fB6zj@p(?u zD_9fCV%b-mS{#qcJSu|5aqRQ@u#--l>A{?2RrFME60=q};nT};{ZBAMifm8JET!e- zKFLWe%Ut$EN7=+G=K$dvBYDPwj=zAB3*Z4mz~AcFE+4_awTAcY%^ZkP7?-ca>1xS( z3_!9-gEtKS11T!>mmNy`LivJgIo;~K)t}QPkGsiC%NbBii2hQ2uvI(gG`lhtC+tlZ zGc_4Mvoas{QLRQDW)7j#tK+P&|0H_;Wpw_8M5cv6{gmNU_^LBe%{DuhYmMyGx5*rS zjA38Xk~5haUZ6;}bJz{`V-1&u;1@n+5qB)sGlLkdiExf@@oMYYIO4U$6#B($Ws3b! zH&)fC6%!1>{l&@6BWLzFWt>@Eb~_FJOqehBdnE0*tj}1irdBHAkYt`Se znb}b-(9<{5Pa+f1{LRSmACKQ(=)az?3yJ(2L)5=n{;Pui%JdJS{neFL*zarA$mcKJ zU-P19KUI|!_6PWH(XkK4{CnK@->YL;io@pbv0vZIfcY2g?=k70WYlF0)|G$qI{;Y}HvU!RNlgCl`m6KMpEi!dvnK zYttM4tgZoy5><4OYymckxszCh-5*#0)K8`pr$ODop2WJQ{u4J@rKdTY?N}M{v8kMX zb^Erc*3-zl7_KirpB#r4`5ybK1?No+Ks5kbpj%df1FM>cIvpgWQs@rKC z%CfdTs;b%Rs{-AR|JKcJ2)m>&cc#%Z{vi$^9xeu;3?aqnDQasb|Ce>K9e1e;>n}NZ z`xyH%gdTC#)za8a&dV0;R~o+~b=7U_B5^1QiFHv#>@7T}2jH<=89R7(E+z`bkc)XN z$9qbr`4oAeipk2Jy@G!f8RuZd&kCfU4{;h!qn*l1*qh^RR&V zJq=dM2CiL|ku`amy;&OWrmmIw_|18)|0~}0I=MHssTH~fn}2I;|8emVJL<~4n%Wt7aR+|>fRiwS$?*bpo_kaI>e_cP* z=8XZ!s-Rpiq4{07sYQ@PG60_oXdRx!0&F~Jjqp(^0si~E5g!-gqeIqmuk!Hx;nvw0 zo>d(1_#f09us)Kq@MYvWPr@2zvR9k3dL>!oT}@GJpV)SIk+H0YKTAGf-=?gqCf z5p~}dbgv55mxcqVyJ~)Tfc*cj#_RuP_iu#mZ(RYcAE0vomHjsY>;FpPwH{`d6=%0K z(CqBIPvLmrS z18AQe&9wjsRs;)nQM%QXAd0PI_+`ecU|(6jmf)ua>8p?#E|8203jgpv>SL?OHnT7M z23G2!@f%~Ei*z@R@ICxsJba)J659J`tk63-!iZfsGt7F07) zH5S&fI?9gu5vz%!vZu3BipVzmtgu}9k!{gksNs6WSkftSI zESBA=uP^a`&ju_y&|3ynBiKg?C`KSRs4iQ8=K(%GP&NS@>Kv3B^e@4u*X8r;#%9Pv8PE+2V?`Er71=4L*D^or=Spa(TT>Nn; z=UY*)7XLbm|9=tN1+C&85#R;b1_k6xh^D2ax&iTk@4PAb>N13{uKR-xOUy)%S$|>yV|YX{3z5Yw`AK`In`e zo@Pj!*a^zchc9$M<~@t{moWAMma_e1=w5Ky8mT~qr&xhPZkDvT?iC5(>j9W9|Nry#Z|*^wychuLs(YsA%I_8fICWq2K%kjhDi6zguGoKFTXOch>J6!XlzBrQZ{xulD^Q=l?hyLpDS2N!3 zZ^&;I|K$m2gngzR44@zT`a90BtpC~AO_h-aU6IiXz%$>!&SGShC!-1u^58tqx|PNw zRG4cIQyXw2yQe=ALYUWs2$?ltLS}ydAu?w^IaGflVNZZPe}XY{u%~v!T3UqlzQ|{c z;jgOm$+@t!rFTA#%-ps4xpIQzVgiatt^||r#N5o`I(C3Ds*fXo#APfL+d2Qr*i#04 z6Xwpx>^H4*n4(y4sZ9lc$T490`ZM=uF9<}275w(IJP#8 zxiS_|CmzYv^IAUa(qPsLj=w*_9+p?ySE^Uk=1HWOb#7ONqpz@HvC`HNb1f%F)sK|3 zJj$(HBW7H-=yE*b(E95$k6$wH(^(bOHxb)bB}7G0TNKp>cH}8yI*Q|oABT99LY+$DG*WFtu~e0;vuH z>%ph1=)K9E%}qT78X^k@U=LV;bU1;`dyG9_m^-Wh|BxM^4i9zxd6m@@ws`rk$_Bpc zuB5eWPV$ngubNfR@ws@u6OipAb+h;Oe~Z62Ymr!h*#g8~d^|upqu(fU|Vlw=RjUDh_C&y!YY?~IQH0C!M^`!dWGujE{HdngdGJ!0 zZ$o|vt32cF|JM08FZy(D?*BI2b~^jD1uOp=domYzm>l+Z1N2unm-Dc{hr$0?cIpk- z;6`FBhLXpx+P|v)rz-BH80zh*`ajbA8>Cwt#aGPu=OE=5XlQGART@^eDe`9- zsDA=EtE}pSXlQfs2z-WYDv3^TnOXDrp9kGPGx}eKdn~}L0eRTD#bD>P!PLQ^|7NiN z4hWjn#Y)SAl%6<*bI6AcpzJJmr}njmyzbM`JXiC(3uK06g>%$GPy7YlM0A=c;w`H zW{@g9)#%gk{c)`Q*0J_wbXaw*t3mae+C;N8CeOVIR`2FSwp(@k#_)g|aDZ1`hMSLa zPk~<`hG;hpizG+{FGxyH#pE!ANBMm5jHLYbAJ*&$Sq7_|k7NW_+Xwy8s<mpw`VySmz~5@bisONNcobANfUQc zw{oG)DArH4+BU+>lrO4ksyo;LibhZcWqDz4#y$a?90s~w4?PCosZO2W&fzbK^i%i9 z30VDy6Y(#v|A)*}MP5+=4kg|H7CZ7Ur}?kHhyBX&8|%hsEdN6Pe=~sI(si&eC(@)Qe>aeG zvmOlpn=_jUDOUhqQ<6I_$3qdu1-J)UfFI*}%nNK)*A#i}E9v{1E4$eT;*%TW8Kvig zi#g@W-%~extA3}9e`)s?0q8k^tO1HuZOI*F`Kiag7M99Q&8 zM`l9#zv{HNmEEk!Ayq3C0}$?5^)S=@XR;zqk$HKL^=Co-&1h&Vu`_In*@^ez48B@r zIAwufn0BOWK-Kn$1Njx9ScOkC7G0e+t>zejY^gr}uM=l$2=Y;V9Tlf!+Rb9LF=aHJ z;Hna!qYD2sAvJTb!_?a_73g`0>mSE{co9qV8rJ^#n9F47j-VHJBjNy>kOR<<%pFw% z%umeOQ0ybBDN~g>|CYbp11^M+*SQ@Ne9i55=@HNK6YT)YxK~X!Z-c-8dti|d;sY$| z&yZWlZvXcl0FV9lo{C?$**1TRzFv-J48nQ;t^p%>Q2#gW-!sn0IugF7tL*+38Gej+ zS6o4G53$(1t;dkBHl%oLbr#Og446JVnw?UQ6)f%WKRp?331Y9fYAmOy==!(Ae^~(j zVIS<^yiJ0C_5{gWfbwDhJc$i{3#F zbYrKgCZxsETJ3N7h=y^#*02K-uvW#~`F(*idyQ57nb#|VrW5B&yL<)bJq536jI3E1 zI)J8mjR>ntJX?)rc_7$P2Pu#NZ_+AovpwhVW!R}Qk)-)&zBjV*vx1z@a_r%curSPG zZcnj;9%uhpl>pUJdkHz19%*rrPutWAsLJj+mh-q&_@5Il*K6OoM>+0Ie4iM*Z+?D6% z``M=f{W36o41w52K0db~<9WOa1^8bsew!MXb8 zC-@v|ZcFrF-^V2@4B&4hnz~{sch};#k7C;t?(0mbx3cQmrEsr2#w`BC?wY_%+l+t1 z^C_@`A6Z3JS+c5GX1lX!3t_-+w8Iqar1ogIr|Ixp({ zBtuGNNHidlLWYv5KBdSIAyTGDnNn29kTE4fhLE8ok%*$AR4N%t2>;jX{p{5~C%(Vy z`nRsr8TQ_5uf5iLJ@@@g_rp2qhN}OK`2TmO>5+p=ZYCFSWzzXOE%p2Do%>{g{^tC% zTK~tv{?e$Qq5pp|0Am3Em+o)izu4cItt;n6EvNj}mGg8mM=io1O)RC=#?9^k*?>&o zn2k)pm_TVBVAnuo1rAI+AQS{L7NBl`8j%lk#pV}pPF2X>T>E;gq3x&>UCHNtiK})Q z4D>LrfOEOtVAZQFr)HF~V=LUx=jZ6N>B*W_3E27Zs$W{?c^5Y74C*%4Fhk-J`iz|@ z*aYp-)38FffPhoT3#_6pU{Ac5uG|-oaE~nD+W*15TM0YAYw~yM<#0c`t?Yq{nQN-T zX}6>Ix->Rg3mDngJo1O$j&sG;V9C4{>rO_p4QE9;yI?R}-2MeWWzX~}+kvl5spGR| zcmuxFXQ1}8#QASU7x@CLgqL5>N|i$A7^{hJN|^^Gmnz&BsZ9XFLv(HHhtvuIh00BlpZfU5sH zr~>F&&G+x{%IYVg3oxGnh?TTye_`;2vVQ(=?869*d(D`c_#c0d z>c8;*qwYW7K{`3bclAirdelFR`VK9f*@tYxPxKZ+2rpim9@9Gk49^teHN1zY7~Z zgR@h>aR*~r$BJ-P@m)N6InTR}6*Y=av6v{q!C1cgsIfD;{T*Llg8t+1zWd@e9>eD@ zDE@}?b}y^RY=?ag2XYL1V>R6DNLIzi3|beSGlX}Ujt!~EE04s7Iu9&YdBxt8hY)1cMO8VXG%gmZH0~h6d|2AOd zU7-I5AZyh2|5UsIOT7&kzaR1IqwrGOgWT7% zyK60RumY}#HECwD_u;Ou$urmEb-c^@eTwtwlm$Dr)X}nA%6^IY>_?@9EJhkN-=7oJ zslt0*fWJPM%*oyydl#6`wVZ=N#QO(vHk|Nt31>;2zZRTZEB>p{G4Tgpv9%Dh;b3nh zMl*I}yuUQ==YJj!Z*^{cwiyK|%K-9S02%gQKnCn$^4yo=acA>xR1LU@v(yBXZ;q$m zg4oINxvdR9w}uS_56DLZ{yQFEULYn2n-x$+C@KSXq&{F@R@YL_NnN;P8#cQg_9BL~ zS;-1=9WTIv<={==;2ZEnr?Y}pgL7J(Jr>4aqQf&fZEgc8?n~Fi*(R!6+`%e8n#kmf z*pOEA9&gK}=t^`S9gLN^5cQXlcqVhOTibFEx5K8~i9LCZ_xYT)uqNf03y9hkZ?ueh zMOsJu7q*5ShSd-4wbJ%C!~aZq#i|l}_n&0#Jjt4IlLM&!mni^g#cspTy^g!mp13ox zuJ%;d$M3I*bzMQOe+C%)6t-{(eJ5s9Pvuh`iI#2sG~KFpVRdF&sZ^SmH1gU zP@SIZJ^0keU{5dPTnu0jPRmfA^79k}aH>)h{PIh&r1lSm0>~T?Vlk^aBG935t9Dpa z{ap@z*T%zMLWEbH?dM?>FY(hG@QP2U`}#iRS}GAKl>t}@&;aXcom2~arIy&tqqsUp zbLKmN_viBNQO(?npR#HumA~xly_L=SKXuJQ)6DGZdhkE5iXUotCB$pR|1q4&Q+d}v z$&*|T_xp!QoDb6n&=>T-zF-*s|A%ON?Z`Q~nyE5NSp&`BGk3H98Q8$}sRGalz!9K; z9N-v!ItUvq1IVI4;U)i@3uFu+L;Ud6owfW!!tIqg|9?H6R{4azuB5DsKfL+>`d>a! zS}ibRX66|DySjfyGu4~S`u;Qg52fGm|D)qSBLBZ)?bNafT><&6h%cAQwq1$biT=(4 zt4C$k_uPj!!`Rw${f~gvsiPwPuTNW6|5qvXngsr<0Pr9=sKLZyt_A7MRXNf3Dt;eC zJZcc%gBhF&1300S|9=)yyX&d`o5UJhO6IN>@7Ipi(GRX1Jbx_f!zpup@Mq1bcS%0@ zSngx}uJu^+MZ{sBW$jo$+nv|;!0+wHni>z=FW_^XgSC8)*y=lE0bT-e?*k#aa>Wnk zJd`V5#(Gfa;3_-~bq?%7irOQ&Y>R~acB`02e3<)YE_TD%d29A@8MZeR#QT8i?O8$l za#oEWg`&T#t1{TK7b~j?R<%8zSa+=OK;r$w+3w-{&FtkIysiCt=4xsPRPoY}7|(ls z!0!t{F7HyS*-F>BoBy|hH)@a7`*GHfU2ivIW$c8%0dK>4Bj=~9YG5_RTvlimj;0;x z@5H0G>C=nezVQA_u+DWs_@hAg)4{Qei0Yq1F8dVl`#Ao36gIpCj6i14DdqXk-vs^p z^^sE$`Stb>0vm!&oXq>2#(q!d|5FlUF#6DtNI)HG z4z^Bs=>**6xosIe3aSt}5icg=*faM6yy`Y^#R@ZLx!WHqA6r5U=7)se<*+H$6L+-3 z`0$kZbLxxu{|;yAF;-qwBoAZlIW5u2rq0bWD!DA(@_5V0ECspUBUu=Ewmu z7GNx(dD_ejWK{wY5l}_w5LT6ufPLXpwXiaKu*M$Y>TCwrX$Ye`l`Gnc>v<4Soo!(N zD_ME3QI~r=+~h>=((T~@Uy&sn&-s`AhF;zXR_-XSfgXRRc>NYE?LF4=B>p;pd$xMf z9HRc6sW+<3gol;XeHStjdVldQk8?&hp z+#$ttI^eH2XHzk-3ryq&j>M~~N^)H9P)D+^`fZMFA$MyzENFDYhayf@e*cC0@GTsq4c*p?WYYKPra^ipX z0EA^e5(Z#*LCiSW2L`)qVgb?BVntx6;^vt^Rv+Ljp0%KMw~ zCi)+SwQK^eZI2H+9`tawjy!9aKEJbbX zeMfw%Ysf|q$6DW=_N#uMwFoju<5Zq&vsMRiW?N$m?Kx6={F;RRq2Olcu{d=YSEU5> zwU0OoTqd`bNOW1g>0=Tn@O&<-_UkXIfZ z!QUsa_pj)vR8{c`{K%*1#G6Icng0J0qW|mg{ww4C9{^IH09thgxh}!4?+L=64;P4f zJ~8?%em;*au)inI%KQHP!TyN;2l}6m-IqBu%Ek2e2KQqV{f#rUNpXa8N-hK?yXD^N z9AfAv!$Df{cvJYue#8uF6PqXkIsOJ`)gLOo{YmV?c{x99tnM5Du5T?y$19)%g zy_d(H?3nKRW;|*QVKb0x7yN%!myIIbjg1WTl9-o})m%C);&aYm=zx08XYl)mq6S>W zCx}Y?N#?&1+~`d3zBhRg&s?{3<~niCP9`p3H-!CunZCd1uY%&+@KWO!q4ASP{gGq> zHq8a(>i}Hb52-n_M7j1k4MlBV-&v24MaFzNk3dMo#0O;vKklO*t!V zb63-t&YtkOl9Jzv4SWhGe*_MFA@@i<*7zFM_iXM-C(XHeFQ{}8eav%tR3^NLYw#(5 zn}C(*1;+eNTzU{Vxg!`b4>jlc%z0X0P#H`-mI|Y5uuU(gTtETw;(9#44fna-oGNKe zO8($FkR~#uc4=+Jkwo-Av)h^KdeoB0`=-Mc^SZ0{0IOl0-<${*T1R#P*yDBsR?5i* z_6RouCGDwl#>Had=~KYgr_fP(lx-}v{iCpoF%$dpT;<={*vs}rOwFDiLxpu?*g$ZNpF7z{hIy+E8&; zBiLDv+EaT0j0>nsq=mMN%05k});z^e!Iz>>=?%{Bm$3E?+^^9MAGzwl|F60BPjgPs zheho`24Fls{;~7{&ZiIH0cHRVFBpT0=Z9%NXMN7wVAjVH7+?#0LnF9v!+SCYup1WJ ziclGVF@PqlWcvY(42qBDv1*bN&I2fP5GpnZ&9L{*WJwd0lMxCtF3pUV!V{Ji~o!vs}UYTFIkx%h8ar%R?Z@sXA z{jt(2)E^2ymymH>%$b@}^bC6+!F_5+u^L)E*sGhmZsr^9P#MLZUdJjeCz?~0^VgjE zJr%}dHsx62zV~2XuOt_63XdJedUBR}-E^iKVIVp^g?zv17Xa1CF?9@*XZ5jO4*su%0AKWkL=QuoBGax&Ss3GbcQGZ*x?3w}O7$pExR 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�SH5^Ymdp91s+2|zoxW2;ynt5V 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
2C+-qmzOrlE1dQR1a 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

;x7%%`bm!QDzHR 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�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%0A){0li5OBS88_iCq9$NGniWHg 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%0A3{a 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

  • D|NAzf&&Qu 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%0Dw*PS5 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%0Dl1gf5HRM2Po~f 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%0DJP0rz~gX% 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

    FoNVhx9<_$cN^+XJMe!s{5%o*|1@$c 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

    R0R9gt4FBcg&ee= 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 z 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